Skip to content

Commit b3c40d8

Browse files
author
Xun Gu
committed
basic-solutions: Implement basic04 Assignment 2
Signed-off-by: Xun Gu <xugu@redhat.com>
1 parent 77b90a7 commit b3c40d8

File tree

4 files changed

+211
-71
lines changed

4 files changed

+211
-71
lines changed

basic-solutions/xdp_loader.c

Lines changed: 140 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +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"},
50-
51-
{{"reuse-maps", no_argument, NULL, 'M' },
52-
"Reuse pinned maps"},
48+
{{"unload", required_argument, NULL, 'U' },
49+
"Unload XDP program <id> instead of loading", "<id>"},
5350

5451
{{"quiet", no_argument, NULL, 'q' },
5552
"Quiet mode (no output)"},
@@ -70,14 +67,94 @@ static const struct option_wrapper long_options[] = {
7067
const char *pin_basedir = "/sys/fs/bpf";
7168
const char *map_name = "xdp_stats_map";
7269

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

150+
len = snprintf(pin_dir, PATH_MAX, "%s/%s", pin_basedir, subdir);
151+
if (len < 0) {
152+
fprintf(stderr, "ERR: creating pin dirname\n");
153+
return EXIT_FAIL_OPTION;
154+
}
155+
79156
len = snprintf(map_filename, PATH_MAX, "%s/%s/%s",
80-
cfg->pin_dir, cfg->ifname, map_name);
157+
pin_basedir, subdir, map_name);
81158
if (len < 0) {
82159
fprintf(stderr, "ERR: creating map_name\n");
83160
return EXIT_FAIL_OPTION;
@@ -87,22 +164,22 @@ int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg)
87164
if (access(map_filename, F_OK) != -1 ) {
88165
if (verbose)
89166
printf(" - Unpinning (remove) prev maps in %s/\n",
90-
cfg->pin_dir);
167+
pin_dir);
91168

92169
/* Basically calls unlink(3) on map_filename */
93-
err = bpf_object__unpin_maps(bpf_obj, cfg->pin_dir);
170+
err = bpf_object__unpin_maps(bpf_obj, pin_dir);
94171
if (err) {
95-
fprintf(stderr, "ERR: UNpinning maps in %s\n", cfg->pin_dir);
172+
fprintf(stderr, "ERR: UNpinning maps in %s\n", pin_dir);
96173
return EXIT_FAIL_BPF;
97174
}
98175
}
99176
if (verbose)
100-
printf(" - Pinning maps in %s/\n", cfg->pin_dir);
177+
printf(" - Pinning maps in %s/\n", pin_dir);
101178

102179
/* This will pin all maps in our bpf_object */
103-
err = bpf_object__pin_maps(bpf_obj, cfg->pin_dir);
180+
err = bpf_object__pin_maps(bpf_obj, pin_dir);
104181
if (err) {
105-
fprintf(stderr, "ERR: Pinning maps in %s\n", cfg->pin_dir);
182+
fprintf(stderr, "ERR: Pinning maps in %s\n", pin_dir);
106183
return EXIT_FAIL_BPF;
107184
}
108185

@@ -111,8 +188,10 @@ int pin_maps_in_bpf_object(struct bpf_object *bpf_obj, struct config *cfg)
111188

112189
int main(int argc, char **argv)
113190
{
114-
struct xdp_program *program;
115191
int err, len;
192+
struct xdp_program *program;
193+
bool map_reused = false;
194+
char path[PATH_MAX];
116195

117196
struct config cfg = {
118197
.attach_mode = XDP_MODE_NATIVE,
@@ -131,23 +210,52 @@ int main(int argc, char **argv)
131210
return EXIT_FAIL_OPTION;
132211
}
133212
if (cfg.do_unload) {
134-
if (!cfg.reuse_maps) {
135-
/* TODO: Miss unpin of maps on unload */
213+
/* unpin the maps */
214+
len = snprintf(path, PATH_MAX, "%s/%s/%s", pin_basedir,
215+
cfg.ifname, map_name);
216+
if (len < 0) {
217+
fprintf(stderr, "ERR: creating map filename for unload\n");
218+
return EXIT_FAIL_OPTION;
219+
}
220+
221+
/* If the map file exists, unpin it */
222+
if (access(path, F_OK) == 0) {
223+
if (verbose)
224+
printf(" - Unpinning map %s\n", path);
225+
226+
/* Use unlink to remove the pinned map file */
227+
err = unlink(path);
228+
if (err) {
229+
fprintf(stderr, "ERR: Failed to unpin map %s: %s\n",
230+
path, strerror(errno));
231+
}
136232
}
137-
/* return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0); */
233+
234+
/* unload the program */
235+
err = do_unload(&cfg);
236+
if (err) {
237+
char errmsg[1024];
238+
libxdp_strerror(err, errmsg, sizeof(errmsg));
239+
fprintf(stderr, "Couldn't unload XDP program: %s\n", errmsg);
240+
return err;
241+
}
242+
243+
printf("Success: Unloaded XDP program\n");
244+
return EXIT_OK;
138245
}
139246

140-
/* Initialize the pin_dir configuration */
141-
len = snprintf(cfg.pin_dir, 512, "%s/%s", pin_basedir, cfg.ifname);
247+
/* Try to reuse existing pinned maps before loading */
248+
len = snprintf(path, PATH_MAX, "%s/%s", pin_basedir, cfg.ifname);
142249
if (len < 0) {
143250
fprintf(stderr, "ERR: creating pin dirname\n");
144251
return EXIT_FAIL_OPTION;
145252
}
146253

147-
148-
program = load_bpf_and_xdp_attach(&cfg);
149-
if (!program)
150-
return EXIT_FAIL_BPF;
254+
program = load_bpf_and_xdp_attach_reuse_maps(&cfg, path, &map_reused);
255+
if (!program) {
256+
err = EXIT_FAIL_BPF;
257+
goto out;
258+
}
151259

152260
if (verbose) {
153261
printf("Success: Loaded BPF-object(%s) and used program(%s)\n",
@@ -156,14 +264,18 @@ int main(int argc, char **argv)
156264
cfg.ifname, cfg.ifindex);
157265
}
158266

159-
/* Use the --dev name as subdir for exporting/pinning maps */
160-
if (!cfg.reuse_maps) {
161-
err = pin_maps_in_bpf_object(xdp_program__bpf_obj(program), &cfg);
267+
if (!map_reused) {
268+
/* Use the --dev name as subdir for exporting/pinning maps */
269+
err = pin_maps_in_bpf_object(xdp_program__bpf_obj(program), cfg.ifname);
162270
if (err) {
163271
fprintf(stderr, "ERR: pinning maps\n");
164-
return err;
272+
goto out;
165273
}
166274
}
167275

168-
return EXIT_OK;
276+
err = EXIT_OK;
277+
278+
out:
279+
xdp_program__close(program);
280+
return err;
169281
}

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)