Skip to content

Commit c620552

Browse files
committed
add Map.Drain to iterate and remove entries
Signed-off-by: Simone Magnani <[email protected]>
1 parent 2996c5a commit c620552

File tree

4 files changed

+268
-189
lines changed

4 files changed

+268
-189
lines changed

examples/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ Please see our [guide on what makes a good example](https://ebpf-go.dev/contribu
2121
* [tcp_close](tcprtt/) - Log RTT of IPv4 TCP connections using eBPF CO-RE helpers.
2222
* XDP - Attach a program to a network interface to process incoming packets.
2323
* [xdp](xdp/) - Print packet counts by IPv4 source address.
24-
* Add your use case(s) here!
2524

2625
## How to run
2726

map.go

Lines changed: 117 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,14 +1272,20 @@ func batchCount(keys, values any) (int, error) {
12721272
//
12731273
// It's not possible to guarantee that all keys in a map will be
12741274
// returned if there are concurrent modifications to the map.
1275-
//
1276-
// For value-only maps (Queue and Stack), multiple iterators will
1277-
// never lookup the same entry, as it is removed from the buffer by design
1278-
// (pop operation).
1279-
func (m *Map) Iterate() MapIterator {
1275+
func (m *Map) Iterate() *MapIterator {
12801276
return newMapIterator(m)
12811277
}
12821278

1279+
// Drain traverses a map while also removing entries.
1280+
//
1281+
// It's safe to create multiple drainers at the same time,
1282+
// but their respective outputs will differ.
1283+
func (m *Map) Drain() *MapIterator {
1284+
it := newMapIterator(m)
1285+
it.drain = true
1286+
return it
1287+
}
1288+
12831289
// Close the Map's underlying file descriptor, which could unload the
12841290
// Map from the kernel if it is not pinned or in use by a loaded Program.
12851291
func (m *Map) Close() error {
@@ -1529,103 +1535,57 @@ func marshalMap(m *Map, length int) ([]byte, error) {
15291535
return buf, nil
15301536
}
15311537

1532-
// MapIterator is the interface defining methods to iterate
1533-
// through the content of the target map.
1534-
//
1535-
// See Map.Iterate.
1536-
type MapIterator interface {
1537-
// Err returns any encountered error.
1538-
//
1539-
// The method must be called after Next returns nil.
1540-
//
1541-
// For key-value maps, returns ErrIterationAborted if
1542-
// it wasn't possible to do a full iteration.
1543-
Err() error
1544-
1545-
// Next decodes the next key and value.
1546-
//
1547-
// In case of a value-only map (Queue and Stack), the key
1548-
// parameter is set to nil in case of no errors.
1549-
//
1550-
// Returns false if there are no more entries. You must check
1551-
// the result of Err afterwards.
1552-
Next(keyOut, valueOut interface{}) bool
1553-
}
1554-
1555-
// keyValueMapIterator iterates a Map defined by key-value pairs.
1556-
// (all except Queue and Stack)
1538+
// MapIterator iterates a Map.
15571539
//
15581540
// See Map.Iterate.
1559-
type keyValueMapIterator struct {
1541+
type MapIterator struct {
15601542
target *Map
15611543
// Temporary storage to avoid allocations in Next(). This is any instead
15621544
// of []byte to avoid allocations.
15631545
cursor any
15641546
count, maxEntries uint32
1565-
done bool
1566-
err error
1567-
}
1568-
1569-
// keylessMapIterator iterates a Map defined with only values.
1570-
// (Queue and Stack)
1571-
//
1572-
// See Map.Iterate.
1573-
type keylessMapIterator struct {
1574-
target *Map
1575-
err error
1547+
done, drain bool
1548+
// Used in old kernels while Draining a map and LookupAndDelete is not supported
1549+
fallback bool
1550+
err error
15761551
}
15771552

1578-
// newMapIterator return the correct MapIterator implementation
1579-
// based on the underlying map type.
1580-
func newMapIterator(target *Map) MapIterator {
1581-
if target.typ.isQueueStack() {
1582-
return &keylessMapIterator{
1583-
target: target,
1584-
}
1585-
}
1586-
return &keyValueMapIterator{
1553+
func newMapIterator(target *Map) *MapIterator {
1554+
return &MapIterator{
15871555
target: target,
15881556
maxEntries: target.maxEntries,
15891557
}
15901558
}
15911559

1592-
// next decodes the next value from a map of type Queue or Stack.
1560+
// Next decodes the next key and value. If the iterator is created
1561+
// through the Map.Drain API, the key and value are also removed.
15931562
//
1594-
// Returns false if there are no more entries. You must check
1595-
// the result of Err afterwards.
1596-
func (mi *keylessMapIterator) next(_, valueOut interface{}) bool {
1597-
// Check whether there was a previous error
1598-
if mi.err != nil {
1599-
return false
1600-
}
1601-
1602-
err := mi.target.LookupAndDelete(nil, valueOut)
1603-
if errors.Is(err, ErrKeyNotExist) {
1604-
// For these maps this error indicates that the map is empty.
1605-
// Return false instead of the error.
1606-
return false
1607-
}
1608-
if err != nil {
1609-
// Whether any other error rather than ErrKeyNotExist occurred
1610-
mi.err = fmt.Errorf("look up next value: %w", err)
1611-
return false
1612-
}
1613-
return true
1614-
}
1615-
1616-
// next decodes the next key and value from a map with key-value pair
1617-
// (all except Queue and Stack).
1563+
// Iterating a hash map from which keys are being deleted is not
1564+
// safe. You may see the same key multiple times. Iteration may
1565+
// also abort with an error, see IsIterationAborted.
1566+
//
1567+
// Iterating a queue/stack map returns ErrIterationAborted, as only
1568+
// Map.Drain is supported.
16181569
//
16191570
// Returns false if there are no more entries. You must check
16201571
// the result of Err afterwards.
1621-
func (mi *keyValueMapIterator) next(keyOut, valueOut interface{}) bool {
1572+
//
1573+
// See Map.Get for further caveats around valueOut.
1574+
func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
16221575
if mi.err != nil || mi.done {
16231576
return false
16241577
}
1578+
if mi.drain {
1579+
return mi.nextDrain(keyOut, valueOut)
1580+
}
1581+
return mi.nextIterate(keyOut, valueOut)
1582+
}
16251583

1626-
// For array-like maps NextKey returns nil only after maxEntries
1627-
// iterations.
1628-
for mi.count <= mi.maxEntries {
1584+
func (mi *MapIterator) nextIterate(keyOut, valueOut interface{}) bool {
1585+
// For array-like maps NextKey returns nil only after maxEntries iterations.
1586+
// For maps with keySize equal to 0 (Queue/Stack) we return ErrIterationAborted,
1587+
// since NextKey returns an error. In this case, Map.Drain should be used instead.
1588+
for mi.target.keySize != 0 && mi.count <= mi.maxEntries {
16291589
if mi.cursor == nil {
16301590
// Pass nil interface to NextKey to make sure the Map's first key
16311591
// is returned. If we pass an uninitialized []byte instead, it'll see a
@@ -1677,41 +1637,91 @@ func (mi *keyValueMapIterator) next(keyOut, valueOut interface{}) bool {
16771637
return false
16781638
}
16791639

1680-
// Next decodes the next key and value.
1681-
//
1682-
// Iterating a hash map from which keys are being deleted is not
1683-
// safe. You may see the same key multiple times. Iteration may
1684-
// also abort with an error, see IsIterationAborted.
1685-
//
1686-
// Returns false if there are no more entries. You must check
1687-
// the result of Err afterwards.
1688-
//
1689-
// See Map.Get for further caveats around valueOut.
1690-
func (mi *keyValueMapIterator) Next(keyOut, valueOut interface{}) bool {
1691-
return mi.next(keyOut, valueOut)
1692-
}
1640+
func (mi *MapIterator) nextDrain(keyOut, valueOut interface{}) bool {
1641+
// Check for maps without a key (e.g., Queue/Stack). In this case, when there is no
1642+
// more data, ErrKeyNotExist arise, but we gently stop the retrieval with no error.
1643+
if mi.target.keySize == 0 {
1644+
mi.err = mi.target.LookupAndDelete(keyOut, valueOut)
1645+
if errors.Is(mi.err, ErrKeyNotExist) {
1646+
mi.done = true
1647+
mi.err = nil
1648+
return false
1649+
} else if mi.err != nil {
1650+
return false
1651+
}
1652+
mi.count++
1653+
return true
1654+
}
16931655

1694-
// Next decodes the next value in the Queue/Stack, ignoring the keyOut parameter.
1695-
//
1696-
// Returns false if there are no more entries (ErrKeyNotExist).
1697-
// You must check the result of Err afterwards.
1698-
func (mi *keylessMapIterator) Next(keyOut, valueOut interface{}) bool {
1699-
return mi.next(keyOut, valueOut)
1656+
// Here we allocate only once data for retrieving the next key in the map.
1657+
if mi.cursor == nil {
1658+
mi.cursor = make([]byte, mi.target.keySize)
1659+
}
1660+
1661+
// Always retrieve first key in the map. This should ensure that
1662+
// the whole map is traversed, despite concurrent insertion.
1663+
// The expected ordering might differ:
1664+
// - initial keys in map: `a -> b -> c`
1665+
// - call MapIterator.Next and retrieve key `a`
1666+
// - insert key `d` in map
1667+
// - retrieve all the remaining keys `d -> b -> c`
1668+
mi.err = mi.target.NextKey(nil, mi.cursor)
1669+
if errors.Is(mi.err, ErrKeyNotExist) {
1670+
mi.done = true
1671+
mi.err = nil
1672+
return false
1673+
} else if mi.err != nil {
1674+
mi.err = fmt.Errorf("get next key: %w", mi.err)
1675+
return false
1676+
}
1677+
1678+
// falling back to sequential Lookup -> Delete in case LookupAndDelete
1679+
// is not supported (e.g., kernel < 5.14).
1680+
if mi.fallback {
1681+
mi.count++
1682+
mi.err = mi.target.Lookup(mi.cursor, valueOut)
1683+
if errors.Is(mi.err, ErrKeyNotExist) {
1684+
// Same as in MapIterator.nextIterate.
1685+
return mi.nextDrain(keyOut, valueOut)
1686+
} else if mi.err != nil {
1687+
mi.err = fmt.Errorf("look up next key: %w", mi.err)
1688+
return false
1689+
}
1690+
mi.err = mi.target.Delete(mi.cursor)
1691+
} else {
1692+
// Check if LookupAndDelete is supported and not invalid args, otherwise fallback
1693+
mi.err = mi.target.LookupAndDelete(mi.cursor, valueOut)
1694+
if mi.err != nil && errors.Is(mi.err, ErrNotSupported) || errors.Is(mi.err, unix.EINVAL) {
1695+
mi.fallback = true
1696+
return mi.nextDrain(keyOut, valueOut)
1697+
}
1698+
mi.count++
1699+
}
1700+
1701+
if errors.Is(mi.err, ErrKeyNotExist) {
1702+
// Same as in MapIterator.nextIterate.
1703+
return mi.nextDrain(keyOut, valueOut)
1704+
} else if mi.err != nil {
1705+
mi.err = fmt.Errorf("delete next key: %w", mi.err)
1706+
return false
1707+
}
1708+
1709+
buf := mi.cursor.([]byte)
1710+
if ptr, ok := keyOut.(unsafe.Pointer); ok {
1711+
copy(unsafe.Slice((*byte)(ptr), len(buf)), buf)
1712+
} else {
1713+
mi.err = sysenc.Unmarshal(keyOut, buf)
1714+
}
1715+
1716+
return mi.err == nil
17001717
}
17011718

17021719
// Err returns any encountered error.
17031720
//
17041721
// The method must be called after Next returns nil.
17051722
//
17061723
// Returns ErrIterationAborted if it wasn't possible to do a full iteration.
1707-
func (mi *keyValueMapIterator) Err() error {
1708-
return mi.err
1709-
}
1710-
1711-
// Err returns any encountered error.
1712-
//
1713-
// The method must be called after Next returns nil.
1714-
func (mi *keylessMapIterator) Err() error {
1724+
func (mi *MapIterator) Err() error {
17151725
return mi.err
17161726
}
17171727

0 commit comments

Comments
 (0)