From ef94ac7d2fd42a09c99567b0393fb48b9d782a9e Mon Sep 17 00:00:00 2001 From: Alexandr Morozov Date: Tue, 20 May 2014 18:26:10 +0400 Subject: [PATCH] Ip allocator refactoring We don't need ordered set anymore, also some cleanings and simple benchmark. Docker-DCO-1.1-Signed-off-by: Alexandr Morozov (github: LK4D4) --- daemon/networkdriver/ipallocator/allocator.go | 114 +++++++----------- .../ipallocator/allocator_test.go | 17 +++ 2 files changed, 60 insertions(+), 71 deletions(-) diff --git a/daemon/networkdriver/ipallocator/allocator.go b/daemon/networkdriver/ipallocator/allocator.go index 4bcce65174..f154b0bd49 100644 --- a/daemon/networkdriver/ipallocator/allocator.go +++ b/daemon/networkdriver/ipallocator/allocator.go @@ -4,19 +4,18 @@ import ( "encoding/binary" "errors" "github.com/dotcloud/docker/daemon/networkdriver" - "github.com/dotcloud/docker/pkg/collections" "net" "sync" - "sync/atomic" ) +// allocatedMap is thread-unsafe set of allocated IP type allocatedMap struct { - *collections.OrderedIntSet + p map[int32]struct{} last int32 } func newAllocatedMap() *allocatedMap { - return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()} + return &allocatedMap{p: make(map[int32]struct{})} } type networkSet map[string]*allocatedMap @@ -35,70 +34,65 @@ var ( // will return the next available ip if the ip provided is nil. If the // ip provided is not nil it will validate that the provided ip is available // for use or return an error -func RequestIP(address *net.IPNet, ip *net.IP) (*net.IP, error) { +func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { lock.Lock() defer lock.Unlock() - - checkAddress(address) + key := network.String() + allocated, ok := allocatedIPs[key] + if !ok { + allocated = newAllocatedMap() + allocatedIPs[key] = allocated + } if ip == nil { - next, err := getNextIp(address) - if err != nil { - return nil, err - } - return next, nil + return allocated.getNextIP(network) } - - if err := registerIP(address, ip); err != nil { - return nil, err - } - return ip, nil + return allocated.checkIP(network, ip) } // ReleaseIP adds the provided ip back into the pool of // available ips to be returned for use. -func ReleaseIP(address *net.IPNet, ip *net.IP) error { +func ReleaseIP(network *net.IPNet, ip *net.IP) error { lock.Lock() defer lock.Unlock() - - checkAddress(address) - - var ( - allocated = allocatedIPs[address.String()] - pos = getPosition(address, ip) - ) - - allocated.Remove(int(pos)) - + if allocated, exists := allocatedIPs[network.String()]; exists { + pos := getPosition(network, ip) + delete(allocated.p, pos) + } return nil } // convert the ip into the position in the subnet. Only // position are saved in the set -func getPosition(address *net.IPNet, ip *net.IP) int32 { - var ( - first, _ = networkdriver.NetworkRange(address) - base = ipToInt(&first) - i = ipToInt(ip) - ) - return i - base +func getPosition(network *net.IPNet, ip *net.IP) int32 { + first, _ := networkdriver.NetworkRange(network) + return ipToInt(ip) - ipToInt(&first) +} + +func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { + pos := getPosition(network, ip) + if _, ok := allocated.p[pos]; ok { + return nil, ErrIPAlreadyAllocated + } + allocated.p[pos] = struct{}{} + allocated.last = pos + return ip, nil } // return an available ip if one is currently available. If not, // return the next available ip for the nextwork -func getNextIp(address *net.IPNet) (*net.IP, error) { +func (allocated *allocatedMap) getNextIP(network *net.IPNet) (*net.IP, error) { var ( - ownIP = ipToInt(&address.IP) - allocated = allocatedIPs[address.String()] - first, _ = networkdriver.NetworkRange(address) - base = ipToInt(&first) - size = int(networkdriver.NetworkSize(address.Mask)) - max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address - pos = atomic.LoadInt32(&allocated.last) + ownIP = ipToInt(&network.IP) + first, _ = networkdriver.NetworkRange(network) + base = ipToInt(&first) + size = int(networkdriver.NetworkSize(network.Mask)) + max = int32(size - 2) // size -1 for the broadcast network, -1 for the gateway network + pos = allocated.last ) var ( - firstNetIP = address.IP.To4().Mask(address.Mask) + firstNetIP = network.IP.To4().Mask(network.Mask) firstAsInt = ipToInt(&firstNetIP) + 1 ) @@ -109,31 +103,16 @@ func getNextIp(address *net.IPNet) (*net.IP, error) { if next == ownIP || next == firstAsInt { continue } - - if !allocated.Exists(int(pos)) { - ip := intToIP(next) - allocated.Push(int(pos)) - atomic.StoreInt32(&allocated.last, pos) - return ip, nil + if _, ok := allocated.p[pos]; ok { + continue } + allocated.p[pos] = struct{}{} + allocated.last = pos + return intToIP(next), nil } return nil, ErrNoAvailableIPs } -func registerIP(address *net.IPNet, ip *net.IP) error { - var ( - allocated = allocatedIPs[address.String()] - pos = getPosition(address, ip) - ) - - if allocated.Exists(int(pos)) { - return ErrIPAlreadyAllocated - } - atomic.StoreInt32(&allocated.last, pos) - - return nil -} - // Converts a 4 bytes IP into a 32 bit integer func ipToInt(ip *net.IP) int32 { return int32(binary.BigEndian.Uint32(ip.To4())) @@ -146,10 +125,3 @@ func intToIP(n int32) *net.IP { ip := net.IP(b) return &ip } - -func checkAddress(address *net.IPNet) { - key := address.String() - if _, exists := allocatedIPs[key]; !exists { - allocatedIPs[key] = newAllocatedMap() - } -} diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/daemon/networkdriver/ipallocator/allocator_test.go index 2b63d9a03c..6897a0a44b 100644 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ b/daemon/networkdriver/ipallocator/allocator_test.go @@ -324,3 +324,20 @@ func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) { t.Fatalf("Expected IP %s, got %s", ip1, ip2) } } + +func BenchmarkRequestIP(b *testing.B) { + network := &net.IPNet{ + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for j := 0; j < 253; j++ { + _, err := RequestIP(network, nil) + if err != nil { + b.Fatal(err) + } + } + reset() + } +}