有同事报告一个错误:lookup: cannot allocate memory,并请求如何解决。

lookup

项目中使用的 eBPF 库是 cilium/ebpf。查看一下 BPF map lookup 的源代码,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// map.go

func (m *Map) Lookup(key, valueOut interface{}) error {
    valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize)
    if err := m.lookup(key, valuePtr, 0); err != nil {
        return err
    }

    return m.unmarshalValue(valueOut, valueBytes)
}

func (m *Map) lookup(key interface{}, valueOut sys.Pointer, flags MapLookupFlags) error {
    keyPtr, err := m.marshalKey(key)
    if err != nil {
        return fmt.Errorf("can't marshal key: %w", err)
    }

    attr := sys.MapLookupElemAttr{
        MapFd: m.fd.Uint(),
        Key:   keyPtr,
        Value: valueOut,
        Flags: uint64(flags),
    }

    if err = sys.MapLookupElem(&attr); err != nil {
        return fmt.Errorf("lookup: %w", wrapMapError(err))
    }
    return nil
}

// internal/sys/types.go

func MapLookupElem(attr *MapLookupElemAttr) error {
    _, err := BPF(BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
    return err
}

好家伙,看样子这个错误不是 cilium/ebpf 报的。那就扒开内核的遮羞布,看看 BPF 这个系统调用的源代码吧。

cannot allocate memory

BPF 这个系统的返回值是一个叫做 error no. 的整数,我们就看看 cannot allocate memory 对应哪个 error no. 吧。

1
2
3
4
# errno -l
...
ENOMEM 12 Cannot allocate memory
...

好吧,我们就看看 BPF map lookup 的内核源代码中会不会返回 ENOMEM

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// kernel/bpf/syscall.c

SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
|-->case BPF_MAP_LOOKUP_ELEM:
        err = map_lookup_elem(&attr);
        break;
    |-->key = __bpf_copy_key(ukey, map->key_size);
        if (IS_ERR(key)) {
            err = PTR_ERR(key);
            goto err_put;
        }
        |-->if (key_size)
                return memdup_user(ukey, key_size);
            |-->p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
                if (!p)
                    return ERR_PTR(-ENOMEM);

由此可知,BPF map lookup 确实需要动态分配内存,用于复制 key/value

如何解决

从用户态应用程序而言,该问题无解,因为导致 lookup: cannot allocate memory 的原因是内核动态分配内存失败了。

P.S. 那位同事查了下 free -g,的确是系统内存耗光了。

那要如何解决呢?需要从系统资源分配与利用的角度出发,限制用户应用程序的内存使用,预留一定比例的内存给内核处理突发事件。(未验证)

小结

诚如侯捷所说:源码面前,了无秘密;就包括本次的错误报告。