Forráskód Böngészése

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 <lk4d4math@gmail.com> (github: LK4D4)
Alexandr Morozov 11 éve
szülő
commit
ef94ac7d2f

+ 43 - 71
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)
-
-	if ip == nil {
-		next, err := getNextIp(address)
-		if err != nil {
-			return nil, err
-		}
-		return next, nil
+	key := network.String()
+	allocated, ok := allocatedIPs[key]
+	if !ok {
+		allocated = newAllocatedMap()
+		allocatedIPs[key] = allocated
 	}
 
-	if err := registerIP(address, ip); err != nil {
-		return nil, err
+	if ip == nil {
+		return allocated.getNextIP(network)
 	}
-	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()
-	}
-}

+ 17 - 0
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()
+	}
+}