Missing support for iterating over per-CPU maps
HouzuoGuo opened this issue · comments
Describe the bug
(Disclaimer: I'm new to eBPF)
When iterating over a `BPF_MAP_TYPE_LRU_PERCPU_HASH`, the iterator `Map.Iterate(...)` yields nothing.
How to reproduce
(Partial snippet)
struct {
__uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
// __uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 128);
} src_data_len SEC(".maps");
static __always_inline int parse_addr(struct xdp_md *ctx, __u32 *out_src,
__u16 *out_len) {
...
SEC("xdp")
int count_packets(struct xdp_md *ctx) {
...
__u32 src_ip;
__u16 src_len;
__u64 new_len = 0;
if (!parse_addr(ctx, &src_ip, &src_len)) {
return XDP_PASS;
}
__u64 *existing_len = bpf_map_lookup_elem(&src_data_len, &src_ip);
new_len += src_len;
if (existing_len) {
new_len += *existing_len;
}
bpf_map_update_elem(&src_data_len, &src_ip, &new_len, BPF_ANY);
...
for {
select {
case <-ticker.C:
var totalPacketCount, totalPacketLen []uint64
var srcIP []byte
var ipTrafficLen uint64
if err := objs.PktCount.Lookup(uint32(0), &totalPacketCount); err != nil {
log.Fatal(err)
}
if err = objs.PktSize.Lookup(uint32(0), &totalPacketLen); err != nil {
log.Fatal(err)
}
srcTrafficIter := objs.SrcDataLen.Iterate()
for srcTrafficIter.Next(&srcIP, &ipTrafficLen) {
// Yields nothing from BPF_MAP_TYPE_LRU_PERCPU_HASH.
srcIPAddr := net.IPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3])
log.Printf("source IP %s: %v bytes of traffic", srcIPAddr, ipTrafficLen)
}
for cpu := range totalPacketCount {
log.Printf("cpu %d: Seen %d packets in %d bytes", cpu, totalPacketCount[cpu], totalPacketLen[cpu])
}
case <-sigInt:
return
}
}
### Version information
github.com/cilium/ebpf v0.12.3
You're not checking srcTrafficIter.Err()
. Otherwise you'd see the following error:
look up next key: per-cpu value requires a slice or a pointer to slice
Here is a diff to one of the examples which shows how to use it:
diff --git a/examples/xdp/bpf_bpfeb.o b/examples/xdp/bpf_bpfeb.o
index f90c8b99..9ab3a59f 100644
Binary files a/examples/xdp/bpf_bpfeb.o and b/examples/xdp/bpf_bpfeb.o differ
diff --git a/examples/xdp/bpf_bpfel.o b/examples/xdp/bpf_bpfel.o
index 35dba4b8..e708d827 100644
Binary files a/examples/xdp/bpf_bpfel.o and b/examples/xdp/bpf_bpfel.o differ
diff --git a/examples/xdp/main.go b/examples/xdp/main.go
index 123513b1..99796157 100644
--- a/examples/xdp/main.go
+++ b/examples/xdp/main.go
@@ -72,10 +72,10 @@ func formatMapContents(m *ebpf.Map) (string, error) {
var (
sb strings.Builder
key netip.Addr
- val uint32
+ val = make([]uint32, ebpf.MustPossibleCPU())
)
iter := m.Iterate()
- for iter.Next(&key, &val) {
+ for iter.Next(&key, val) {
sourceIP := key // IPv4 source address in network byte order.
packetCount := val
sb.WriteString(fmt.Sprintf("\t%s => %d\n", sourceIP, packetCount))
diff --git a/examples/xdp/xdp.c b/examples/xdp/xdp.c
index 2f36c161..ac414b66 100644
--- a/examples/xdp/xdp.c
+++ b/examples/xdp/xdp.c
@@ -9,7 +9,7 @@ char __license[] SEC("license") = "Dual MIT/GPL";
/* Define an LRU hash map for storing packet count by source IPv4 address */
struct {
- __uint(type, BPF_MAP_TYPE_LRU_HASH);
+ __uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
__uint(max_entries, MAX_MAP_ENTRIES);
__type(key, __u32); // source IPv4 address
__type(value, __u32); // packet count
Output:
2024/01/08 12:15:10 Attached XDP program to iface "lo" (index 1)
2024/01/08 12:15:10 Press Ctrl-C to exit and remove the program
2024/01/08 12:15:11 Map contents:
2024/01/08 12:15:12 Map contents:
2024/01/08 12:15:13 Map contents:
127.0.0.1 => [0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0]
2024/01/08 12:15:14 Map contents:
127.0.0.1 => [2 2 4 0 5 0 0 0 0 0 0 0 0 2 0 0]
2024/01/08 12:15:15 Map contents:
127.0.0.1 => [2 2 4 0 5 0 0 0 0 0 0 0 0 2 0 0]
That's very cool, thank you!