-
Notifications
You must be signed in to change notification settings - Fork 881
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix for Duplicate IP issues #2105
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,6 +108,12 @@ func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) { | |
bitSel >>= 1 | ||
bits++ | ||
} | ||
// Check if the loop exited because it could not | ||
// find any available bit int block starting from | ||
// "from". Return invalid pos in that case. | ||
if bitSel == 0 { | ||
return invalidPos, invalidPos, ErrNoBitAvailable | ||
} | ||
return bits / 8, bits % 8, nil | ||
} | ||
|
||
|
@@ -313,14 +319,14 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial | |
curr := uint64(0) | ||
h.Lock() | ||
store = h.store | ||
h.Unlock() | ||
if store != nil { | ||
h.Unlock() // The lock is acquired in the GetObject | ||
if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I hate the sequence too. But because of the way we handle differently for swarmkit and native, its kind of needed like this. I will work on the refactor next. |
||
return ret, err | ||
} | ||
h.Lock() // Acquire the lock back | ||
} | ||
|
||
h.Lock() | ||
logrus.Debugf("Received set for ordinal %v, start %v, end %v, any %t, release %t, serial:%v curr:%d \n", ordinal, start, end, any, release, serial, h.curr) | ||
if serial { | ||
curr = h.curr | ||
} | ||
|
@@ -346,7 +352,6 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial | |
|
||
// Create a private copy of h and work on it | ||
nh := h.getCopy() | ||
h.Unlock() | ||
|
||
nh.head = pushReservation(bytePos, bitPos, nh.head, release) | ||
if release { | ||
|
@@ -355,22 +360,25 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial | |
nh.unselected-- | ||
} | ||
|
||
// Attempt to write private copy to store | ||
if err := nh.writeToStore(); err != nil { | ||
if _, ok := err.(types.RetryError); !ok { | ||
return ret, fmt.Errorf("internal failure while setting the bit: %v", err) | ||
if h.store != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to differentiate between a store logic and a non store logic codepath. I need to unlock before going to write store it will lead to deadlock. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it regarding the logic to differentiate store/non-store codepaths. For the locks, it looks like we are calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep thats right. All the more reason not to have a lock around the operation right ? I retained the logic needed for store code path the way it was. |
||
h.Unlock() | ||
// Attempt to write private copy to store | ||
if err := nh.writeToStore(); err != nil { | ||
if _, ok := err.(types.RetryError); !ok { | ||
return ret, fmt.Errorf("internal failure while setting the bit: %v", err) | ||
} | ||
// Retry | ||
continue | ||
} | ||
// Retry | ||
continue | ||
h.Lock() | ||
} | ||
|
||
// Previous atomic push was succesfull. Save private copy to local copy | ||
h.Lock() | ||
defer h.Unlock() | ||
h.unselected = nh.unselected | ||
h.head = nh.head | ||
h.dbExists = nh.dbExists | ||
h.dbIndex = nh.dbIndex | ||
h.Unlock() | ||
return ret, nil | ||
} | ||
} | ||
|
@@ -498,24 +506,40 @@ func (h *Handle) UnmarshalJSON(data []byte) error { | |
func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) { | ||
// Find sequence which contains the start bit | ||
byteStart, bitStart := ordinalToPos(start) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as a reminder for myself, byteStart and bitStart here are relative to the first byte of the block where the ordinal start falls into |
||
current, _, _, inBlockBytePos := findSequence(head, byteStart) | ||
|
||
current, _, precBlocks, inBlockBytePos := findSequence(head, byteStart) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. current is the pointer to the block where byteStart fall into |
||
// Derive the this sequence offsets | ||
byteOffset := byteStart - inBlockBytePos | ||
bitOffset := inBlockBytePos*8 + bitStart | ||
var firstOffset uint64 | ||
if current == head { | ||
firstOffset = byteOffset | ||
} | ||
for current != nil { | ||
if current.block != blockMAX { | ||
// If the current block is not full, check if there is any bit | ||
// from the current bit in the current block. If not, before proceeding to the | ||
// next block node, make sure we check for available bit in the next | ||
// instance of the same block. Due to RLE same block signature will be | ||
// compressed. | ||
retry: | ||
bytePos, bitPos, err := current.getAvailableBit(bitOffset) | ||
if err != nil && precBlocks == current.count-1 { | ||
// This is the last instance in the same block node, | ||
// so move to the next block. | ||
goto next | ||
} | ||
if err != nil { | ||
// There are some more instances of the same block, so add the offset | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is only possible in the case of serial allocation, where the start can be with in the block itself |
||
// and be optimistic that you will find the available bit in the next | ||
// instance of the same block. | ||
bitOffset = 0 | ||
byteOffset += blockBytes | ||
precBlocks++ | ||
goto retry | ||
} | ||
return byteOffset + bytePos, bitPos, err | ||
} | ||
// Moving to next block: Reset bit offset. | ||
next: | ||
bitOffset = 0 | ||
byteOffset += (current.count * blockBytes) - firstOffset | ||
firstOffset = 0 | ||
byteOffset += (current.count * blockBytes) - (precBlocks * blockBytes) | ||
precBlocks = 0 | ||
current = current.next | ||
} | ||
return invalidPos, invalidPos, ErrNoBitAvailable | ||
|
@@ -526,19 +550,20 @@ func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) { | |
// This can be further optimized to check from start till curr in case of a rollover | ||
func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) { | ||
var bytePos, bitPos uint64 | ||
var err error | ||
if curr != 0 && curr > start { | ||
bytePos, bitPos, _ = getFirstAvailable(head, curr) | ||
bytePos, bitPos, err = getFirstAvailable(head, curr) | ||
ret := posToOrdinal(bytePos, bitPos) | ||
if end < ret { | ||
if end < ret || err != nil { | ||
goto begin | ||
} | ||
return bytePos, bitPos, nil | ||
} | ||
|
||
begin: | ||
bytePos, bitPos, _ = getFirstAvailable(head, start) | ||
bytePos, bitPos, err = getFirstAvailable(head, start) | ||
ret := posToOrdinal(bytePos, bitPos) | ||
if end < ret { | ||
if end < ret || err != nil { | ||
return invalidPos, invalidPos, ErrNoBitAvailable | ||
} | ||
return bytePos, bitPos, nil | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears
store.GetObject
will modifyh
(through theSetValue
andSetIndex
calls). If so, shouldn't we keeph
locked while that is happening? Also, it appears the lock acquired inGetObject
is a datastore lock rather than the object's lock. Ish.Unlock()
needed and correct before theGetObject
call invoked onh
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way the store logic works in libnetwork is , the in memory data structure gets overloaded by the store data. So there need not be lock going into the GetObjec