1

Let's suppose there are two programs (User program and Kernel program).

User program made bpf map by api bpf_create_map_name() and it returns fd. With this fd, I can access the map by syscalls (e.g., bpf_map_update(fd, ..)). But I can do this only in the user space programs because the fd is valid only to user program(=user process), then How can I access to this map in the bpf program (located in the kernel space)?

I was heard that I can pin the map in the fs via libbpf's bpf_obj_pin(fd, file path) and can get this map via libbpf's bpf_obj_get(file path) but the problem is bpf_obj_get is only available in the user space because this is system call.

I saw a similar discussion before (Accessing BPF maps from kernel space). but this was not clear to me. To access bpf map via bpf_map_lookup_elem(fd, ..) in the kernel space, I have to know map's fd in advance. But as I mentioned before, the map's fd is not valid in the kernel.

I am using libbpf not BCC.

Dusol
  • 47
  • 8

2 Answers2

2

You should probably look at libbpf's function bpf_map__reuse_fd(), which allows to reuse a file descriptor pointing to an existing map for a BPF program.

Here is an example using this function: we first retrieve a pointer to the map to replace in the struct bpf_object by calling bpf_object__find_map_by_name(), and then tell it to reuse the existing fd.

Qeole
  • 8,284
  • 1
  • 24
  • 52
  • But can I use that functions when I make bpf_program? I thought that functions can be used in the user space. I was finding adequate 'bpf helper function' which do such roles. – Dusol May 08 '22 at 00:54
  • 1
    My goal is to find and use map which is made by another bpf program. The bpf programs locate in kernel space. – Dusol May 08 '22 at 04:02
  • 1
    Sorry I'm afraid I don't understand your question. In your eBPF program, you always use the map the same way - it doesn't matter if the map exists or not. Then it's up to libbpf to make sure that your program uses the right map: Either by creating it before loading the program, or by reusing the fd of an existing, pinned map created before, if you use `bpf_map__reuse_fd()`. If you meant something else, please clarify your question. – Qeole May 08 '22 at 12:53
  • Okay, let's suppose two example xdpsock_kern.c and xdp1_kern.c (https://github.com/torvalds/linux/tree/master/samples/bpf). These are all bpf program which will be attached to two different trace points. xdpsock (bpf program) has 'xsks_map' map and xdp1 (bpf program) has 'rxcnt' map. I want to access to 'rxcnt' map when xdpsock is executed. In short, I want to lookup rxcnt map in the xdpsock program. Thank you for your concern. btw, I thought I can use libbpf functions (e.g., bpf_map__reuse_fd) in the user space program (e.g., xdpsock_user.c and xdp1_user.c) not in the kernel space program. – Dusol May 09 '22 at 03:55
  • If my questions is still not clear, feel free to let me know – Dusol May 09 '22 at 03:58
  • 2
    Yes, `bpf_map__reuse_fd()` is user space only. But it's in user space that you tell your programs what maps to use. Your particular example wouldn't work, because the maps aren't the same (should be same type and size for both programs). Say the maps were identical in both `*_kern.c` files; in that case, you would keep both BPF source files just as is. And then in user space with libbpf: 1) Create a `struct bpf_object` for each prog, 2) load the first one, which will make libbpf create the map it needs, 3) retrieve fd and call `bpf_map__reuse_fd()` to set it for the 2nd prog, 4) load 2nd prog – Qeole May 09 '22 at 08:49
  • Ahh I got it. I understood your explanation. let me try your proposal. – Dusol May 09 '22 at 13:06
  • I tried in several ways but I think something goes wrong. there are two bpf program (prog1 and prog2 and they have same map named "map1" which has same structure). 1) bpf_prog_load(prog1, Type, obj, progfd) to prepare bpf_object. 2) I already loaded. 3) map = bpf_object__find_map_by_name(obj, "map1"), map_fd = bpf_object__find_map_fd_by_name(obj, "map1") and bpf_map__reuse_fd(map, map_fd). 4) bpf_prog_load(prog2, Type, obj2, progfd2). But when I checked, the map ids of "map1" after load each program was different. I think they are not sharing "map1". May I ask some advice? – Dusol May 10 '22 at 09:46
  • `map = bpf_object__find_map_by_name(obj, "map1")` should be `map = bpf_object__find_map_by_name(obj_for_prog2, "map1")`, no? If you're using the same bpf_object everywhere, you're getting the fd used for the map of one bpf_object, and then re-assigning it to the same map? It should be something like: `fd = `, then `map = – Qeole May 10 '22 at 14:31
  • 1) `bpf_prog_load("prog1.o", TYPE, obj1, ..)` 2) `map_fd = bpf_object__find_map_fd_by_name(obj1, "map1)` 2) `bpf_prog_load("prog2.o", TYPE, obj2,..)` 3) `map = bpf_object__find_map_by_name(obj2, "map1")` 4) `bpf_map__reuse_fd(map, map_fd)` -> after these, the map1 id from both programs were same. But when the contents of map1 are updated in prog1 and I checked them in prog2, but the update is not reflected. I am debugging now and I think something is going wrong. – Dusol May 11 '22 at 05:39
  • The reason why it doesn't work I guess is that I loaded prog2 before `bpf_map__reuse_fd(map, map_fd)`. I think the "map1" fds ( one from obj1 and another from obj2 ) can be different, but I thought it would work because the ids were same. I think I have to use combination of `obj1 = bpf_ object__open_file("prog1.o")` and `bpf_object__load(obj1)` instead of `bpf_prog_load`. BTW, really Sorry for the long question. – Dusol May 11 '22 at 06:23
  • Correct, you need to change the fd in the `struct bpf_object` for prog2 _before_ loading it, because it's when you load it that the reference to the map is passed to the kernel. If you don't, libbpf will just recreate a new map for the second program. I don't remember all the functions in libbpf's API, but yeah, I think it's correct also that you have to split between opening the object file and loading, so that you can insert the fd update between those two steps. – Qeole May 11 '22 at 09:24
1

The only way to dynamically add maps to an existing eBPF program is to use a map-in-map type like BPF_MAP_TYPE_ARRAY_OF_MAPS or BPF_MAP_TYPE_HASH_OF_MAPS. To use it we define a inner and outer map type. The userspace program/loader creates the outer map and loads the program with a reference to the outer map. After that we can modify this outer map from userspace to add one or more inner maps to it at any time. Please take a look at samples in the kernel: test_map_in_map_user.c and test_map_in_map_kern.c.

To explain further and to clarify some things:

  • eBPF programs don't create maps, the ELF files contain map definitions which are read by the userspace program/loader which then creates them. eBPF programs can't create maps as of this moment.
  • When the loader creates a map, the kernel returns a file descriptor. When a eBPF program is loaded into the kernel, the loader will dynamically rewrite the program and inject this file descriptor into it. The kernel verifies the program using the loaded map, after which the actual memory address of the map is placed in the program instead of the file descriptor so the generated machine code can access the map directly by memory address. This verification and conversion step is the reason programs can't dynamically "get" map references.
  • Map-in-map types do work dynamically. That is because the verifier validates the inner map definition when the program is loaded to make sure the code is valid. Then when the userspace updates the map-in-map, the actual memory address of the map is stored which can be accesses by the eBPF program. But even so, you need to know how the type/structure of the inner map before loading the first program.
Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21