I am using a multi-producer single-consumer implementation on the user side to handle incoming data from a eBPF map from an XDP hook.
However, in order to do this, I need to limit the number of cores the XDP hook can use to send information to the user-space by one.
Is there a way we can do this?
I am using a BPF_MAP_TYPE_PERF_EVENT_ARRAY, and I hook the kern code via bpf_prog_load_xattr and bpf_set_link_xdp_fd.
Here is the function that loads the hook.
static int do_attach(int idx, int fd, const char *name, __u32 xdp_flags)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
int err;
err = bpf_set_link_xdp_fd(idx, fd, xdp_flags);
if (err < 0) {
printf("ERROR: failed to attach program to %s\n", name);
return err;
}
err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
if (err) {
printf("can't get prog info - %s\n", strerror(errno));
return err;
}
prog_id = info.id;
return err;
}
And here is where it gets used.
int main(int argc, char **argv)
{
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
struct bpf_prog_load_attr prog_load_attr = {
.prog_type = BPF_PROG_TYPE_XDP,
};
int prog_fd, map_fd;
struct bpf_object *obj;
struct bpf_map *map;
char filename[256];
int ret, err, i;
int numcpus = bpf_num_possible_cpus();
struct config cfg = {
.xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE,
.ifindex = -1,
};
strncpy(cfg.filename, default_filename, sizeof(cfg.filename));
/* Cmdline options can change these */
parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
/* Required option */
if (cfg.ifindex == -1) {
fprintf(stderr, "ERR: required option --dev missing\n");
usage(argv[0], __doc__, long_options, (argc == 1));
return EXIT_FAIL_OPTION;
}
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
perror("setrlimit(RLIMIT_MEMLOCK)");
return 1;
}
snprintf(filename, sizeof(filename), "xdp_sample_pkts_kern.o");
prog_load_attr.file = filename;
if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
return 1;
if (!prog_fd) {
printf("load_bpf_file: %s\n", strerror(errno));
return 1;
}
map = bpf_map__next(NULL, obj);
if (!map) {
printf("finding a map in obj file failed\n");
return 1;
}
map_fd = bpf_map__fd(map);
err = do_attach(cfg.ifindex, prog_fd, cfg.ifname, cfg.xdp_flags);
if (err)
return err;
if (signal(SIGINT, sig_handler) ||
signal(SIGHUP, sig_handler) ||
signal(SIGTERM, sig_handler)) {
perror("signal");
return 1;
}
test_bpf_perf_event(map_fd, numcpus);
for (i = 0; i < numcpus; i++)
if (perf_event_mmap_header(pmu_fds[i], &headers[i]) < 0)
return 1;
pd = pcap_open_dead(DLT_EN10MB, 65535);
if (!pd)
goto out;
pdumper = pcap_dump_open(pd, cfg.filename);
if (!pdumper)
goto out;
ret = perf_event_poller_multi(pmu_fds, headers, numcpus,
print_bpf_output, &done);
pcap_dump_close(pdumper);
pcap_close(pd);
out:
do_detach(cfg.ifindex, cfg.ifname);
printf("\n%u packet samples stored in %s\n", pcap_pkts, cfg.filename);
return ret;
}