Skip to content

Commit 299b6d4

Browse files
author
Xun Gu
committed
basic04-pinning-maps: Implementation Assignment2, map unpinning, programe unloading
Signed-off-by: Xun Gu <[email protected]>
1 parent 004feab commit 299b6d4

File tree

4 files changed

+255
-72
lines changed

4 files changed

+255
-72
lines changed

basic-solutions/xdp_loader.c

Lines changed: 137 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ static const struct option_wrapper long_options[] = {
4545
{{"force", no_argument, NULL, 'F' },
4646
"Force install, replacing existing program on interface"},
4747

48-
{{"unload", no_argument, NULL, 'U' },
49-
"Unload XDP program instead of loading"},
48+
{{"unload", required_argument, NULL, 'U' },
49+
"Unload XDP program <id> instead of loading", "<id>"},
5050

5151
{{"reuse-maps", no_argument, NULL, 'M' },
5252
"Reuse pinned maps"},
@@ -70,21 +70,95 @@ static const struct option_wrapper long_options[] = {
7070
const char *pin_basedir = "/sys/fs/bpf";
7171
const char *map_name = "xdp_stats_map";
7272

73-
/* Pinning maps under /sys/fs/bpf in subdir */
73+
74+
/* Load BPF and XDP program with map reuse using libxdp */
75+
struct xdp_program *load_bpf_and_xdp_attach_reuse_maps(struct config *cfg, bool *map_reused)
76+
{
77+
struct xdp_program *prog;
78+
struct bpf_map *map;
79+
char map_path[PATH_MAX];
80+
int len, pinned_map_fd;
81+
int err;
82+
83+
if (!cfg || !cfg->filename[0] || !cfg->progname[0] || !map_reused) {
84+
fprintf(stderr, "ERR: invalid arguments\n");
85+
return NULL;
86+
}
87+
88+
*map_reused = false;
89+
90+
/* 1) Create XDP program through libxdp */
91+
DECLARE_LIBXDP_OPTS(xdp_program_opts, xdp_opts,
92+
.prog_name = cfg->progname,
93+
.open_filename = cfg->filename);
94+
95+
prog = xdp_program__create(&xdp_opts);
96+
if (!prog) {
97+
err = errno;
98+
fprintf(stderr, "ERR: xdp_program__create: %s\n", strerror(err));
99+
goto out;
100+
}
101+
102+
/* 2) Get BPF object from xdp_program and reuse the specific map
103+
* At this point: BPF object and maps have not been loaded into the kernel
104+
*/
105+
if (cfg->reuse_maps) {
106+
map = bpf_object__find_map_by_name(xdp_program__bpf_obj(prog), map_name);
107+
if (!map) {
108+
fprintf(stderr, "ERR: Map %s not found!\n", map_name);
109+
goto out;
110+
}
111+
112+
len = snprintf(map_path, PATH_MAX, "%s/%s", cfg->pin_dir, map_name);
113+
if (len < 0 || len >= PATH_MAX) {
114+
fprintf(stderr, "ERR: map path too long\n");
115+
goto out;
116+
}
117+
118+
pinned_map_fd = bpf_obj_get(map_path);
119+
if (pinned_map_fd >= 0) {
120+
err = bpf_map__reuse_fd(map, pinned_map_fd);
121+
if (err) {
122+
close(pinned_map_fd);
123+
fprintf(stderr, "ERR: bpf_map__reuse_fd: %s\n", strerror(-err));
124+
goto out;
125+
}
126+
*map_reused = true;
127+
if (verbose)
128+
printf(" - Reusing pinned map: %s\n", map_path);
129+
}
130+
}
131+
132+
/* 3) Attach XDP program to interface
133+
* BPF object will be loaded into the kernel as part of XDP attachment
134+
*/
135+
err = xdp_program__attach(prog, cfg->ifindex, cfg->attach_mode, 0);
136+
if (err) {
137+
fprintf(stderr, "ERR: xdp_program__attach: %s\n", strerror(-err));
138+
goto out;
139+
}
140+
141+
return prog;
142+
143+
out:
144+
xdp_program__close(prog);
145+
return NULL;
146+
}
147+
148+
/* Pinning maps under /sys/fs/bpf */
74149
int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg)
75150
{
76151
char map_filename[PATH_MAX];
77152
int err, len;
78153

79-
len = snprintf(map_filename, PATH_MAX, "%s/%s/%s",
80-
cfg->pin_dir, cfg->ifname, map_name);
154+
len = snprintf(map_filename, PATH_MAX, "%s/%s", cfg->pin_dir, map_name);
81155
if (len < 0) {
82156
fprintf(stderr, "ERR: creating map_name\n");
83157
return EXIT_FAIL_OPTION;
84158
}
85159

86160
/* Existing/previous XDP prog might not have cleaned up */
87-
if (access(map_filename, F_OK ) != -1 ) {
161+
if (access(map_filename, F_OK) != -1 ) {
88162
if (verbose)
89163
printf(" - Unpinning (remove) prev maps in %s/\n",
90164
cfg->pin_dir);
@@ -109,10 +183,36 @@ int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg)
109183
return 0;
110184
}
111185

186+
/* Unpinning map under /sys/fs/bpf */
187+
void unpin_map(struct config *cfg)
188+
{
189+
char map_path[PATH_MAX];
190+
int len;
191+
192+
len = snprintf(map_path, PATH_MAX, "%s/%s", cfg->pin_dir, map_name);
193+
if (len < 0) {
194+
fprintf(stderr, "ERR: creating map filename for unpin\n");
195+
return;
196+
}
197+
198+
/* If the map file exists, unpin it */
199+
if (access(map_path, F_OK) == 0) {
200+
if (verbose)
201+
printf(" - Unpinning map %s\n", map_path);
202+
203+
/* Use unlink to remove the pinned map file */
204+
if (unlink(map_path)) {
205+
fprintf(stderr, "ERR: Failed to unpin map %s: %s\n",
206+
map_path, strerror(errno));
207+
}
208+
}
209+
}
210+
112211
int main(int argc, char **argv)
113212
{
114213
struct xdp_program *program;
115214
int err, len;
215+
bool map_reused = false;
116216

117217
struct config cfg = {
118218
.attach_mode = XDP_MODE_NATIVE,
@@ -130,12 +230,6 @@ int main(int argc, char **argv)
130230
usage(argv[0], __doc__, long_options, (argc == 1));
131231
return EXIT_FAIL_OPTION;
132232
}
133-
if (cfg.do_unload) {
134-
if (!cfg.reuse_maps) {
135-
/* TODO: Miss unpin of maps on unload */
136-
}
137-
/* return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); */
138-
}
139233

140234
/* Initialize the pin_dir configuration */
141235
len = snprintf(cfg.pin_dir, 512, "%s/%s", pin_basedir, cfg.ifname);
@@ -144,10 +238,30 @@ int main(int argc, char **argv)
144238
return EXIT_FAIL_OPTION;
145239
}
146240

241+
if (cfg.do_unload) {
242+
if (!cfg.reuse_maps) {
243+
unpin_map(&cfg);
244+
}
245+
246+
/* unload the program */
247+
err = do_unload(&cfg);
248+
if (err) {
249+
char errmsg[1024];
250+
libxdp_strerror(err, errmsg, sizeof(errmsg));
251+
fprintf(stderr, "Couldn't unload XDP program: %s\n", errmsg);
252+
return err;
253+
}
254+
255+
printf("Success: Unloaded XDP program\n");
256+
return EXIT_OK;
257+
}
147258

148-
program = load_bpf_and_xdp_attach(&cfg);
149-
if (!program)
150-
return EXIT_FAIL_BPF;
259+
/* Try to reuse existing pinned maps before loading */
260+
program = load_bpf_and_xdp_attach_reuse_maps(&cfg, &map_reused);
261+
if (!program) {
262+
err = EXIT_FAIL_BPF;
263+
goto out;
264+
}
151265

152266
if (verbose) {
153267
printf("Success: Loaded BPF-object(%s) and used program(%s)\n",
@@ -156,14 +270,18 @@ int main(int argc, char **argv)
156270
cfg.ifname, cfg.ifindex);
157271
}
158272

159-
/* Use the --dev name as subdir for exporting/pinning maps */
160-
if (!cfg.reuse_maps) {
273+
if (cfg.reuse_maps && !map_reused) {
274+
/* Use the --dev name as subdir for exporting/pinning maps */
161275
err = pin_maps_in_bpf_object(xdp_program__bpf_obj(program), &cfg);
162276
if (err) {
163277
fprintf(stderr, "ERR: pinning maps\n");
164-
return err;
278+
goto out;
165279
}
166280
}
167281

168-
return EXIT_OK;
282+
err = EXIT_OK;
283+
284+
out:
285+
xdp_program__close(program);
286+
return err;
169287
}

basic04-pinning-maps/README.org

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,18 +113,50 @@ reloading via =xdp_loader=, else it will be watching the wrong FD.
113113

114114
*** Reusing maps with libbpf
115115

116-
The libbpf library can *reuse and replace* a map with an existing map file
117-
descriptor, via the libbpf API call: =bpf_map__reuse_fd()=. But you cannot
118-
use =bpf_prog_load()= for this; instead you have to code it yourself, as you
119-
need a step in-between =bpf_object__open()= and =bpf_object__load=. The
120-
basic steps needed looks like:
116+
Sometimes you want multiple XDP programs to share the same map
117+
(for example, a stats map pinned in /sys/fs/bpf/…).
118+
The libbpf API provides a way to *reuse and replace* the map inside
119+
your BPF object with an already existing pinned map, using:
120+
121+
=bpf_map__reuse_fd()=
122+
123+
This call must be made *after* the object has been opened
124+
with =bpf_object__open()= but *before* it is loaded
125+
with =bpf_object__load()=.
126+
127+
When using the higher-level XDP program APIs:
128+
129+
- =xdp_program__create()= → internally calls =bpf_object__open()=
130+
- =xdp_program__attach()= → internally calls =bpf_object__load()=
131+
132+
Therefore, you need to inject the =bpf_map__reuse_fd()= step in between,
133+
by getting the underlying =bpf_object= from the XDP program.
134+
135+
Here is a minimal example:
121136

122137
#+begin_src C
123-
int pinned_map_fd = bpf_obj_get("/sys/fs/bpf/veth0/xdp_stats_map");
124-
struct bpf_object *obj = bpf_object__open(cfg.filename);
125-
struct bpf_map *map = bpf_object__find_map_by_name(obj, "xdp_stats_map");
126-
bpf_map__reuse_fd(map, pinned_map_fd);
127-
bpf_object__load(obj);
138+
struct xdp_program *prog;
139+
struct bpf_object *bpf_obj;
140+
struct bpf_map *map;
141+
int pinned_map_fd;
142+
143+
/* 1. Create program (opens bpf_object) */
144+
prog = xdp_program__create(&xdp_opts);
145+
146+
/* 2. Access the underlying bpf_object */
147+
bpf_obj = xdp_program__bpf_obj(prog);
148+
149+
/* 3. Look up the map in the object */
150+
map = bpf_object__find_map_by_name(bpf_obj, "xdp_stats_map");
151+
152+
/* 4. Get FD of the pinned map */
153+
pinned_map_fd = bpf_obj_get("/sys/fs/bpf/veth0/xdp_stats_map");
154+
155+
/* 5. Reuse pinned FD instead of creating a new map */
156+
bpf_map__reuse_fd(map, pinned_map_fd);
157+
158+
/* 6. Now attach program (this will load the object) */
159+
xdp_program__attach(prog, ifindex, attach_mode, 0);
128160
#+end_src
129161

130162
(Hint: see [[#assignment2-xdp_loaderc-reuse-pinned-map][Assignment 2]])
@@ -147,9 +179,7 @@ BPF program ID.
147179

148180
** Assignment 2: (xdp_loader.c) reuse pinned map
149181

150-
As mentioned above, libbpf can reuse and replace a map with an existing map,
151-
it just requires writing your own =bpf_prog_load()= (or
152-
=bpf_prog_load_xattr=).
182+
As mentioned above, libbpf can reuse and replace a map with an existing map.
153183

154184
The *assignment* is to check in [[file:xdp_loader.c][xdp_loader]] if there already is a pinned
155185
version of the map "xdp_stats_map" and use libbpf =bpf_map__reuse_fd()= API

0 commit comments

Comments
 (0)