Merge pull request #2287 from weaveworks/donate-cidr-aligned
Donate CIDR-aligned ranges instead of just slicing at the mid-point.
This commit is contained in:
commit
ef5f80a05a
5 files changed
+117
-40
No files matched your search
@@ -220,7 +220,7 @@ func (alloc *Allocator) pause() func() {
|
||||
}
|
||||
|
||||
func TestCancel(t *testing.T) {
|
||||
const cidr = "10.0.1.7/26"
|
||||
const cidr = "10.0.4.0/26"
|
||||
router := gossip.NewTestRouter(0.0)
|
||||
|
||||
alloc1, subnet := makeAllocator("01:00:00:02:00:00", cidr, 2)
|
||||
@@ -281,7 +281,7 @@ func TestCancel(t *testing.T) {
|
||||
|
||||
func TestCancelOnDied(t *testing.T) {
|
||||
const (
|
||||
cidr = "10.0.1.7/26"
|
||||
cidr = "10.0.4.0/26"
|
||||
container1 = "abcdef"
|
||||
)
|
||||
|
||||
@@ -334,7 +334,7 @@ func TestGossipShutdown(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNoFrag(t *testing.T) {
|
||||
const cidr = "10.0.1.7/22"
|
||||
const cidr = "10.0.4.0/22"
|
||||
resultChan := make(chan int)
|
||||
for i := 0; i < 100; i++ {
|
||||
allocs, router, subnet := makeNetworkOfAllocators(3, cidr)
|
||||
@@ -343,13 +343,13 @@ func TestNoFrag(t *testing.T) {
|
||||
allocs[2].actionChan <- func() {
|
||||
resultChan <- len(allocs[2].ring.Entries)
|
||||
}
|
||||
require.True(t, <-resultChan < 5, "excessive ring fragmentation")
|
||||
require.True(t, <-resultChan < 6, "excessive ring fragmentation")
|
||||
stopNetworkOfAllocators(allocs, router)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransfer(t *testing.T) {
|
||||
const cidr = "10.0.1.7/22"
|
||||
const cidr = "10.0.4.0/22"
|
||||
allocs, router, subnet := makeNetworkOfAllocators(3, cidr)
|
||||
defer stopNetworkOfAllocators(allocs, router)
|
||||
alloc0 := allocs[0]
|
||||
@@ -389,7 +389,7 @@ func TestTransfer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFakeRouterSimple(t *testing.T) {
|
||||
const cidr = "10.0.1.7/22"
|
||||
const cidr = "10.0.4.0/22"
|
||||
allocs, router, subnet := makeNetworkOfAllocators(2, cidr)
|
||||
defer stopNetworkOfAllocators(allocs, router)
|
||||
|
||||
@@ -407,7 +407,7 @@ func TestAllocatorFuzz(t *testing.T) {
|
||||
nodes = 5
|
||||
maxAddresses = 1000
|
||||
concurrency = 5
|
||||
cidr = "10.0.1.7/22"
|
||||
cidr = "10.0.4.0/22"
|
||||
)
|
||||
allocs, router, subnet := makeNetworkOfAllocators(nodes, cidr)
|
||||
defer stopNetworkOfAllocators(allocs, router)
|
||||
|
||||
+8
-2
@@ -122,9 +122,12 @@ func (s *Space) biggestFreeRange(r address.Range) (biggest address.Range) {
|
||||
biggestSize := address.Count(0)
|
||||
s.walkFree(r, func(chunk address.Range) bool {
|
||||
if size := chunk.Size(); size >= biggestSize {
|
||||
chunk = chunk.BiggestCIDRRange()
|
||||
if size = chunk.Size(); size >= biggestSize {
|
||||
biggest = chunk
|
||||
biggestSize = size
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
return
|
||||
@@ -137,9 +140,12 @@ func (s *Space) Donate(r address.Range) (address.Range, bool) {
|
||||
return address.Range{}, false
|
||||
}
|
||||
|
||||
// Donate half of that biggest free range. Note size/2 rounds down, so
|
||||
// the resulting donation size rounds up, and in particular can't be empty.
|
||||
// Donate no more than half what we have available
|
||||
if biggest.Size() > s.NumFreeAddressesInRange(r)/2 {
|
||||
// Note size/2 rounds down, so the resulting donation size
|
||||
// rounds up, and in particular can't be empty.
|
||||
biggest.Start = address.Add(biggest.Start, address.Offset(biggest.Size()/2))
|
||||
}
|
||||
|
||||
s.ours = subtract(s.ours, biggest.Start, biggest.End)
|
||||
s.free = subtract(s.free, biggest.Start, biggest.End)
|
||||
|
||||
+32
-31
@@ -68,7 +68,7 @@ func TestLowlevel(t *testing.T) {
|
||||
wt.AssertErrorInterface(t, (*error)(nil), s.Free(100), "double free")
|
||||
|
||||
r, ok := s.Donate(address.NewRange(0, 1000))
|
||||
require.True(t, ok && r.Start == 125 && r.Size() == 25, "donate")
|
||||
require.Equal(t, address.NewRange(0xa0, 0x20), r, "donate")
|
||||
|
||||
// test Donate when addresses are scarce
|
||||
s = New()
|
||||
@@ -78,7 +78,7 @@ func TestLowlevel(t *testing.T) {
|
||||
require.NoError(t, s.Claim(0))
|
||||
require.NoError(t, s.Claim(2))
|
||||
r, ok = s.Donate(address.NewRange(0, 1000))
|
||||
require.True(t, ok && r.Start == 1 && r.End == 2, "donate")
|
||||
require.Equal(t, address.NewRange(1, 1), r, "donate")
|
||||
r, ok = s.Donate(address.NewRange(0, 1000))
|
||||
require.True(t, !ok, "donate should fail")
|
||||
}
|
||||
@@ -124,20 +124,21 @@ func TestSpaceAllocate(t *testing.T) {
|
||||
|
||||
func TestSpaceFree(t *testing.T) {
|
||||
const (
|
||||
testAddr1 = "10.0.3.4"
|
||||
testAddr1 = "10.0.3.16"
|
||||
testAddrx = "10.0.3.19"
|
||||
testAddry = "10.0.9.19"
|
||||
containerID = "deadbeef"
|
||||
size = 16
|
||||
)
|
||||
|
||||
entireRange := address.NewRange(ip(testAddr1), 20)
|
||||
space := makeSpace(ip(testAddr1), 20)
|
||||
entireRange := address.NewRange(ip(testAddr1), size)
|
||||
space := makeSpace(ip(testAddr1), size)
|
||||
|
||||
// Check we are prepared to give up the entire space
|
||||
r := space.biggestFreeRange(entireRange)
|
||||
require.True(t, r.Start == ip(testAddr1) && r.Size() == 20, "Wrong space")
|
||||
require.Equal(t, address.NewRange(ip(testAddr1), size), r, "Wrong space")
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
for i := 0; i < size; i++ {
|
||||
ok, _ := space.Allocate(entireRange)
|
||||
require.True(t, ok, "Failed to get address")
|
||||
}
|
||||
@@ -149,44 +150,44 @@ func TestSpaceFree(t *testing.T) {
|
||||
require.True(t, r.Size() == 0, "Wrong space")
|
||||
|
||||
// Free in the middle
|
||||
require.NoError(t, space.Free(ip("10.0.3.13")))
|
||||
r = space.biggestFreeRange(entireRange)
|
||||
require.True(t, r.Start == ip("10.0.3.13") && r.Size() == 1, "Wrong space")
|
||||
|
||||
// Free one at the end
|
||||
require.NoError(t, space.Free(ip("10.0.3.23")))
|
||||
r = space.biggestFreeRange(entireRange)
|
||||
require.True(t, r.Start == ip("10.0.3.23") && r.Size() == 1, "Wrong space")
|
||||
|
||||
// Free one at the end
|
||||
require.NoError(t, space.Free(ip("10.0.3.31")))
|
||||
r = space.biggestFreeRange(entireRange)
|
||||
require.True(t, r.Start == ip("10.0.3.31") && r.Size() == 1, "Wrong space")
|
||||
|
||||
// Now free a few at the end
|
||||
require.NoError(t, space.Free(ip("10.0.3.22")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.21")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.30")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.29")))
|
||||
|
||||
require.Equal(t, address.Count(4), space.NumFreeAddresses())
|
||||
|
||||
// Now get the biggest free space; should be 3.21
|
||||
// Now get the biggest free space; should be 3.30
|
||||
r = space.biggestFreeRange(entireRange)
|
||||
require.True(t, r.Start == ip("10.0.3.21") && r.Size() == 3, "Wrong space")
|
||||
require.Equal(t, address.NewRange(ip("10.0.3.30"), 2), r, "Wrong space")
|
||||
|
||||
// Now free a few in the middle
|
||||
require.NoError(t, space.Free(ip("10.0.3.12")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.11")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.10")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.24")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.22")))
|
||||
require.NoError(t, space.Free(ip("10.0.3.21")))
|
||||
|
||||
require.Equal(t, address.Count(7), space.NumFreeAddresses())
|
||||
|
||||
// Now get the biggest free space; should be 3.21
|
||||
// Now get the biggest free space; should be 3.30
|
||||
r = space.biggestFreeRange(entireRange)
|
||||
require.True(t, r.Start == ip("10.0.3.10") && r.Size() == 4, "Wrong space")
|
||||
require.Equal(t, address.NewRange(ip("10.0.3.30"), 2), r, "Wrong space")
|
||||
|
||||
require.Equal(t, []address.Range{{Start: ip("10.0.3.4"), End: ip("10.0.3.24")}}, space.OwnedRanges())
|
||||
require.Equal(t, []address.Range{{Start: ip("10.0.3.16"), End: ip("10.0.3.32")}}, space.OwnedRanges())
|
||||
}
|
||||
|
||||
func TestDonateSimple(t *testing.T) {
|
||||
const (
|
||||
testAddr1 = "10.0.1.0"
|
||||
testAddr2 = "10.0.1.32"
|
||||
size = 48
|
||||
size = 64
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -199,7 +200,7 @@ func TestDonateSimple(t *testing.T) {
|
||||
r, ok := ps1.Donate(address.NewRange(ip(testAddr1), size))
|
||||
numGivenUp := r.Size()
|
||||
require.True(t, ok, "Donate result")
|
||||
require.Equal(t, "10.0.1.24", r.Start.String(), "Invalid start")
|
||||
require.Equal(t, "10.0.1.32", r.Start.String(), "Invalid start")
|
||||
require.Equal(t, address.Count(size/2), numGivenUp)
|
||||
require.Equal(t, address.Count(size/2), ps1.NumFreeAddresses())
|
||||
|
||||
@@ -241,13 +242,13 @@ func TestDonateHard(t *testing.T) {
|
||||
// Now split
|
||||
newRange, ok := spaceset.Donate(address.NewRange(start, size))
|
||||
require.True(t, ok, "GiveUpSpace result")
|
||||
require.Equal(t, ip("10.0.1.23"), newRange.Start)
|
||||
require.Equal(t, address.Count(24), newRange.Size())
|
||||
require.Equal(t, address.Count(23), spaceset.NumFreeAddresses())
|
||||
require.Equal(t, address.NewRange(ip("10.0.1.16"), 16), newRange, "Wrong space")
|
||||
require.Equal(t, address.Count(31), spaceset.NumFreeAddresses())
|
||||
|
||||
//Space set should now have 2 spaces
|
||||
expected := New()
|
||||
expected.Add(start, 23)
|
||||
expected.ours = add(nil, ip("10.0.1.47"), ip("10.0.1.48"))
|
||||
//Space set should now have 3 spaces
|
||||
expected := &Space{
|
||||
ours: add(nil, ip("10.0.1.47"), ip("10.0.1.48")),
|
||||
free: add(add(nil, ip("10.0.1.0"), ip("10.0.1.16")), ip("10.0.1.32"), ip("10.0.1.47")),
|
||||
}
|
||||
require.Equal(t, expected, spaceset)
|
||||
}
|
||||
@@ -35,6 +35,35 @@ func (r Range) AsCIDRString() string {
|
||||
return CIDR{Addr: r.Start, PrefixLen: prefixLen}.String()
|
||||
}
|
||||
|
||||
// return the highest bit set in v
|
||||
// algorithm from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
func msb(v Count) Count {
|
||||
v |= v >> 1
|
||||
v |= v >> 2
|
||||
v |= v >> 4
|
||||
v |= v >> 8
|
||||
v |= v >> 16
|
||||
// Cast to 64-bit to avoid overflow when original v >= 0x80000000
|
||||
return Count((uint64(v) + 1) / 2)
|
||||
}
|
||||
|
||||
func (r Range) BiggestCIDRRange() Range {
|
||||
sizeMsb := Offset(msb(r.Size()))
|
||||
maskedSize := Offset(r.Size()) & (sizeMsb - 1)
|
||||
maskedStart := Offset(r.Start) & (sizeMsb - 1)
|
||||
if maskedStart == 0 {
|
||||
return NewRange(r.Start, sizeMsb)
|
||||
}
|
||||
if sizeMsb-maskedStart <= maskedSize {
|
||||
return NewRange(Add(r.Start, sizeMsb-maskedStart), sizeMsb)
|
||||
}
|
||||
size := sizeMsb / 2
|
||||
if size >= maskedStart {
|
||||
return NewRange(Add(r.Start, size-maskedStart), size)
|
||||
}
|
||||
return NewRange(Add(r.Start, sizeMsb-maskedStart), size)
|
||||
}
|
||||
|
||||
func MakeCIDR(subnet CIDR, addr Address) CIDR {
|
||||
return CIDR{Addr: addr, PrefixLen: subnet.PrefixLen}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func isPower2(x Count) bool {
|
||||
if x == 0 {
|
||||
return false
|
||||
}
|
||||
for ; x > 1; x /= 2 {
|
||||
if x&1 != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestBiggestPow2AlignedRange(t *testing.T) {
|
||||
require.Equal(t, NewRange(0, 1), NewRange(0, 1).BiggestCIDRRange())
|
||||
require.Equal(t, NewRange(1, 1), NewRange(1, 2).BiggestCIDRRange())
|
||||
require.Equal(t, NewRange(2, 2), NewRange(1, 3).BiggestCIDRRange())
|
||||
require.Equal(t, NewRange(0, 0x40000000), NewRange(0, 0x7fffffff).BiggestCIDRRange())
|
||||
require.Equal(t, NewRange(0xfffffffe, 1), NewRange(0xfffffffe, 1).BiggestCIDRRange())
|
||||
prop := func(start Address, size Offset) bool {
|
||||
if size > Offset(0xffffffff)-Offset(start) { // out of range
|
||||
return true
|
||||
}
|
||||
r := NewRange(start, size)
|
||||
result := r.BiggestCIDRRange()
|
||||
return r.Contains(result.Start) &&
|
||||
r.Contains(result.End-1) &&
|
||||
isPower2(result.Size()) &&
|
||||
result.Size() > r.Size()/4 &&
|
||||
Count(result.Start)%result.Size() == 0
|
||||
}
|
||||
require.NoError(t, quick.Check(prop, &quick.Config{MaxCount: 1000000}))
|
||||
}
|
||||
Reference in new issue
Block a user