Repository: cilium/ebpf
Pull request #710 (2022-06-12 13:53:51)

Remove 4.4 from CI

lmb: Thanks, added names back to the testcases.

Patch:
diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml
index 7d534ba..2488fa2 100644
--- a/.semaphore/semaphore.yml
+++ b/.semaphore/semaphore.yml
@@ -65,6 +65,6 @@ blocks:
       - name: Run unit tests
         matrix:
           - env_var: KERNEL_VERSION
-            values: ["5.15", "5.10", "5.4", "4.19", "4.14", "4.9", "4.4"]
+            values: ["5.15", "5.10", "5.4", "4.19", "4.14", "4.9"]
         commands:
           - timeout -s KILL 600s ./run-tests.sh $KERNEL_VERSION
diff --git a/README.md b/README.md
index 69a6bb0..3e490de 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,8 @@ This library includes the following packages:
 
 * A version of Go that is [supported by
   upstream](https://golang.org/doc/devel/release.html#policy)
-* Linux >= 4.4. CI is run against LTS releases.
+* Linux >= 4.9. CI is run against kernel.org LTS releases. 4.4 should work but is
+  not tested against.
 
 ## Regenerating Testdata
 
diff --git a/map.go b/map.go
index ee0b1a0..51d3358 100644
--- a/map.go
+++ b/map.go
@@ -19,7 +19,6 @@ import (
 
 // Errors returned by Map and MapIterator methods.
 var (
-	errFirstKeyNotFound = errors.New("first key not found")
 	ErrKeyNotExist      = errors.New("key does not exist")
 	ErrKeyExist         = errors.New("key already exists")
 	ErrIterationAborted = errors.New("iteration aborted")
@@ -779,14 +778,14 @@ func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error {
 		// Kernels 4.4.131 and earlier return EFAULT instead of a pointer to the
 		// first map element when a nil key pointer is specified.
 		if key == nil && errors.Is(err, unix.EFAULT) {
-			var guessKey sys.Pointer
+			var guessKey []byte
 			guessKey, err = m.guessNonExistentKey()
 			if err != nil {
-				return fmt.Errorf("can't guess starting key: %w", err)
+				return err
 			}
 
 			// Retry the syscall with a valid non-existing key.
-			attr.Key = guessKey
+			attr.Key = sys.NewSlicePointer(guessKey)
 			if err = sys.MapGetNextKey(&attr); err == nil {
 				return nil
 			}
@@ -801,7 +800,7 @@ func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error {
 // guessNonExistentKey attempts to perform a map lookup that returns ENOENT.
 // This is necessary on kernels before 4.4.132, since those don't support
 // iterating maps from the start by providing an invalid key pointer.
-func (m *Map) guessNonExistentKey() (startKey sys.Pointer, err error) {
+func (m *Map) guessNonExistentKey() ([]byte, error) {
 	// Provide an invalid value pointer to prevent a copy on the kernel side.
 	valuePtr := sys.NewPointer(unsafe.Pointer(^uintptr(0)))
 	randKey := make([]byte, int(m.keySize))
@@ -833,11 +832,11 @@ func (m *Map) guessNonExistentKey() (startKey sys.Pointer, err error) {
 
 		err := m.lookup(randKey, valuePtr, 0)
 		if errors.Is(err, ErrKeyNotExist) {
-			return sys.NewSlicePointer(randKey), nil
+			return randKey, nil
 		}
 	}
 
-	return sys.Pointer{}, errFirstKeyNotFound
+	return nil, errors.New("couldn't find non-existing key")
 }
 
 // BatchLookup looks up many elements in a map at once.


Test patch:
diff --git a/map_test.go b/map_test.go
index 0c58ec9..ca6f2cc 100644
--- a/map_test.go
+++ b/map_test.go
@@ -1059,167 +1059,122 @@ func TestMapIterateHashKeyOneByteFull(t *testing.T) {
 	}
 	var key uint8
 	var value uint8
-	var keys []uint8
+	var keys int
 
 	entries := hash.Iterate()
 	for entries.Next(&key, &value) {
 		if key != value {
 			t.Fatalf("Expected key == value, got key %v value %v", key, value)
 		}
-		keys = append(keys, key)
-	}
-
-	if err := entries.Err(); err != nil {
-		if errors.Is(err, errFirstKeyNotFound) {
-			testutils.SkipOnOldKernel(t, "4.4.132", "map iterate first key nil")
-		}
-		t.Fatal(err)
-	}
-
-	if n := uint32(len(keys)); n != hash.MaxEntries() {
-		t.Fatalf("Expected to get %d keys, have %d", hash.MaxEntries(), n)
-	}
-}
-
-func TestMapIterateHashAllUnknownKey(t *testing.T) {
-	hash, err := NewMap(&MapSpec{
-		Type:       Hash,
-		KeySize:    4,
-		ValueSize:  4,
-		MaxEntries: 3,
-	})
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer hash.Close()
-
-	// magic numbers from guessFirstKey()
-	key1 := uint32(0)
-	key2 := uint32(0x55555555)
-	key3 := uint32(0xffffffff)
-
-	if err := hash.Put(key1, uint32(21)); err != nil {
-		t.Fatal(err)
-	}
-
-	if err := hash.Put(key2, uint32(42)); err != nil {
-		t.Fatal(err)
-	}
-
-	if err := hash.Put(key3, uint32(63)); err != nil {
-		t.Fatal(err)
-	}
-
-	var key uint32
-	var value uint32
-	var keys []uint32
-
-	entries := hash.Iterate()
-	for entries.Next(&key, &value) {
-		keys = append(keys, key)
+		keys++
 	}
 
 	if err := entries.Err(); err != nil {
 		t.Fatal(err)
 	}
 
-	if n := uint32(len(keys)); n != hash.MaxEntries() {
-		t.Fatalf("Expected to get %d keys, have %d", hash.MaxEntries(), n)
-	}
-
-	sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
-	if keys[0] != key1 {
-		t.Error("Expected index 0 all zero, got", keys[0])
-	}
-	if keys[1] != key2 {
-		t.Errorf("Expected index 1 to be %v, got %v", key2, keys[1])
-	}
-	if keys[2] != key3 {
-		t.Errorf("Expected index 2 to be %v, got %v", key3, keys[2])
+	if keys != int(hash.MaxEntries()) {
+		t.Fatalf("Expected to get %d keys, have %d", hash.MaxEntries(), keys)
 	}
 }
 
-func TestMapIterateKeyZero(t *testing.T) {
-	keyZeroTests := []struct {
-		name       string
-		mapType    MapType
-		maxEntries uint32
+func TestMapGuessNonExistentKey(t *testing.T) {
+	tests := []struct {
+		name    string
+		mapType MapType
+		keys    []uint32
 	}{
 		{
-			name:       "hash key zero",
-			mapType:    Hash,
-			maxEntries: 2,
+			"empty", Hash, []uint32{},
 		},
 		{
-			name:       "hash key zero only",
-			mapType:    Hash,
-			maxEntries: 1,
+			"all zero key", Hash, []uint32{0},
 		},
 		{
-			name:       "array max entries",
-			mapType:    Array,
-			maxEntries: 2,
+			"all ones key", Hash, []uint32{math.MaxUint32},
+		},
+		{
+			"alternating bits key", Hash, []uint32{0x5555_5555},
+		},
+		{
+			"all special patterns", Hash, []uint32{0, math.MaxUint32, 0x5555_5555},
+		},
+		{
+			"empty", Array, []uint32{},
+		},
+		{
+			"all zero key", Array, []uint32{0},
+		},
+		{
+			"full", Array, []uint32{0, 1},
 		},
 	}
 
-	key1 := uint32(0)
-	key2 := uint32(2 - 1)
+	for _, tt := range tests {
+		t.Run(fmt.Sprintf("%s: %s", tt.mapType, tt.name), func(t *testing.T) {
+			maxEntries := uint32(len(tt.keys))
+			if maxEntries == 0 {
+				maxEntries = 1
+			}
 
-	for _, tt := range keyZeroTests {
-		t.Run(tt.name, func(t *testing.T) {
-			hash, err := NewMap(&MapSpec{
+			m, err := NewMap(&MapSpec{
 				Type:       tt.mapType,
 				KeySize:    4,
 				ValueSize:  4,
-				MaxEntries: tt.maxEntries,
+				MaxEntries: maxEntries,
 			})
 			if err != nil {
 				t.Fatal(err)
 			}
-			defer hash.Close()
+			defer m.Close()
 
-			if err := hash.Put(key1, uint32(21)); err != nil {
-				t.Fatal(err)
-			}
-
-			if tt.maxEntries > 1 {
-				if err := hash.Put(key2, uint32(42)); err != nil {
+			for _, key := range tt.keys {
+				if err := m.Put(key, key); err != nil {
 					t.Fatal(err)
 				}
 			}
 
-			var key uint32
-			var value uint32
-			var keys []uint32
-
-			entries := hash.Iterate()
-			for entries.Next(&key, &value) {
-				keys = append(keys, key)
-			}
-
-			if err := entries.Err(); err != nil {
+			guess, err := m.guessNonExistentKey()
+			if err != nil {
 				t.Fatal(err)
 			}
 
-			if n := uint32(len(keys)); n != tt.maxEntries {
-				t.Fatalf("Expected to get %d keys, have %d", tt.maxEntries, n)
+			if len(guess) != int(m.keySize) {
+				t.Fatal("Guessed key has wrong size")
 			}
 
-			sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
-			if keys[0] != key1 {
-				t.Error("Expected index 0 all zero, got", keys[0])
-			}
-			if tt.maxEntries > 1 {
-				k := keys[1]
-				if tt.mapType == Array {
-					k = keys[len(keys)-1]
-				}
-				if k != key2 {
-					t.Errorf("Expected index 1 to be %v, got %v", key2, k)
-				}
+			var value uint32
+			if err := m.Lookup(guess, &value); !errors.Is(err, unix.ENOENT) {
+				t.Fatal("Doesn't return ENOENT:", err)
 			}
 		})
 	}
+
+	t.Run("Hash: full", func(t *testing.T) {
+		const n = math.MaxUint8 + 1
+
+		hash, err := NewMap(&MapSpec{
+			Type:       Hash,
+			KeySize:    1,
+			ValueSize:  1,
+			MaxEntries: n,
+		})
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer hash.Close()
+
+		for i := 0; i < n; i++ {
+			if err := hash.Put(uint8(i), uint8(i)); err != nil {
+				t.Fatal(err)
+			}
+		}
+
+		_, err = hash.guessNonExistentKey()
+		if err == nil {
+			t.Fatal("guessNonExistentKey doesn't return error on full hash table")
+		}
+	})
 }
 
 func TestNotExist(t *testing.T) {