libbpf: proper XSKMAP cleanup

The bpf_map_update_elem() function, when used on an XSKMAP, will fail
if not a valid AF_XDP socket is passed as value. Therefore, this is
function cannot be used to clear the XSKMAP. Instead, the
bpf_map_delete_elem() function should be used for that.

This patch also simplifies the code by breaking up
xsk_update_bpf_maps() into three smaller functions.

Reported-by: William Tu <u9012063@gmail.com>
Fixes: 1cad078842 ("libbpf: add support for using AF_XDP sockets")
Signed-off-by: Björn Töpel <bjorn.topel@intel.com>
Tested-by: William Tu <u9012063@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Björn Töpel 2019-04-30 14:45:36 +02:00 committed by Alexei Starovoitov
parent 0e6741f092
commit 5750902a6e

View File

@ -387,21 +387,17 @@ static void xsk_delete_bpf_maps(struct xsk_socket *xsk)
{ {
close(xsk->qidconf_map_fd); close(xsk->qidconf_map_fd);
close(xsk->xsks_map_fd); close(xsk->xsks_map_fd);
xsk->qidconf_map_fd = -1;
xsk->xsks_map_fd = -1;
} }
static int xsk_update_bpf_maps(struct xsk_socket *xsk, int qidconf_value, static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
int xsks_value)
{ {
bool qidconf_map_updated = false, xsks_map_updated = false; __u32 i, *map_ids, num_maps, prog_len = sizeof(struct bpf_prog_info);
__u32 map_len = sizeof(struct bpf_map_info);
struct bpf_prog_info prog_info = {}; struct bpf_prog_info prog_info = {};
__u32 prog_len = sizeof(prog_info);
struct bpf_map_info map_info; struct bpf_map_info map_info;
__u32 map_len = sizeof(map_info); int fd, err;
__u32 *map_ids;
int reset_value = 0;
__u32 num_maps;
unsigned int i;
int err;
err = bpf_obj_get_info_by_fd(xsk->prog_fd, &prog_info, &prog_len); err = bpf_obj_get_info_by_fd(xsk->prog_fd, &prog_info, &prog_len);
if (err) if (err)
@ -422,66 +418,71 @@ static int xsk_update_bpf_maps(struct xsk_socket *xsk, int qidconf_value,
goto out_map_ids; goto out_map_ids;
for (i = 0; i < prog_info.nr_map_ids; i++) { for (i = 0; i < prog_info.nr_map_ids; i++) {
int fd; if (xsk->qidconf_map_fd != -1 && xsk->xsks_map_fd != -1)
break;
fd = bpf_map_get_fd_by_id(map_ids[i]); fd = bpf_map_get_fd_by_id(map_ids[i]);
if (fd < 0) { if (fd < 0)
err = -errno; continue;
goto out_maps;
}
err = bpf_obj_get_info_by_fd(fd, &map_info, &map_len); err = bpf_obj_get_info_by_fd(fd, &map_info, &map_len);
if (err) if (err) {
goto out_maps; close(fd);
continue;
if (!strcmp(map_info.name, "qidconf_map")) {
err = bpf_map_update_elem(fd, &xsk->queue_id,
&qidconf_value, 0);
if (err)
goto out_maps;
qidconf_map_updated = true;
xsk->qidconf_map_fd = fd;
} else if (!strcmp(map_info.name, "xsks_map")) {
err = bpf_map_update_elem(fd, &xsk->queue_id,
&xsks_value, 0);
if (err)
goto out_maps;
xsks_map_updated = true;
xsk->xsks_map_fd = fd;
} }
if (qidconf_map_updated && xsks_map_updated) if (!strcmp(map_info.name, "qidconf_map")) {
break; xsk->qidconf_map_fd = fd;
} continue;
}
if (!(qidconf_map_updated && xsks_map_updated)) { if (!strcmp(map_info.name, "xsks_map")) {
err = -ENOENT; xsk->xsks_map_fd = fd;
goto out_maps; continue;
}
close(fd);
} }
err = 0; err = 0;
goto out_success; if (xsk->qidconf_map_fd < 0 || xsk->xsks_map_fd < 0) {
err = -ENOENT;
xsk_delete_bpf_maps(xsk);
}
out_maps:
if (qidconf_map_updated)
(void)bpf_map_update_elem(xsk->qidconf_map_fd, &xsk->queue_id,
&reset_value, 0);
if (xsks_map_updated)
(void)bpf_map_update_elem(xsk->xsks_map_fd, &xsk->queue_id,
&reset_value, 0);
out_success:
if (qidconf_map_updated)
close(xsk->qidconf_map_fd);
if (xsks_map_updated)
close(xsk->xsks_map_fd);
out_map_ids: out_map_ids:
free(map_ids); free(map_ids);
return err; return err;
} }
static void xsk_clear_bpf_maps(struct xsk_socket *xsk)
{
int qid = false;
(void)bpf_map_update_elem(xsk->qidconf_map_fd, &xsk->queue_id, &qid, 0);
(void)bpf_map_delete_elem(xsk->xsks_map_fd, &xsk->queue_id);
}
static int xsk_set_bpf_maps(struct xsk_socket *xsk)
{
int qid = true, fd = xsk->fd, err;
err = bpf_map_update_elem(xsk->qidconf_map_fd, &xsk->queue_id, &qid, 0);
if (err)
goto out;
err = bpf_map_update_elem(xsk->xsks_map_fd, &xsk->queue_id, &fd, 0);
if (err)
goto out;
return 0;
out:
xsk_clear_bpf_maps(xsk);
return err;
}
static int xsk_setup_xdp_prog(struct xsk_socket *xsk) static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
{ {
bool prog_attached = false;
__u32 prog_id = 0; __u32 prog_id = 0;
int err; int err;
@ -491,7 +492,6 @@ static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
return err; return err;
if (!prog_id) { if (!prog_id) {
prog_attached = true;
err = xsk_create_bpf_maps(xsk); err = xsk_create_bpf_maps(xsk);
if (err) if (err)
return err; return err;
@ -501,20 +501,21 @@ static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
goto out_maps; goto out_maps;
} else { } else {
xsk->prog_fd = bpf_prog_get_fd_by_id(prog_id); xsk->prog_fd = bpf_prog_get_fd_by_id(prog_id);
err = xsk_lookup_bpf_maps(xsk);
if (err)
goto out_load;
} }
err = xsk_update_bpf_maps(xsk, true, xsk->fd); err = xsk_set_bpf_maps(xsk);
if (err) if (err)
goto out_load; goto out_load;
return 0; return 0;
out_load: out_load:
if (prog_attached) close(xsk->prog_fd);
close(xsk->prog_fd);
out_maps: out_maps:
if (prog_attached) xsk_delete_bpf_maps(xsk);
xsk_delete_bpf_maps(xsk);
return err; return err;
} }
@ -642,6 +643,9 @@ int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
goto out_mmap_tx; goto out_mmap_tx;
} }
xsk->qidconf_map_fd = -1;
xsk->xsks_map_fd = -1;
if (!(xsk->config.libbpf_flags & XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)) { if (!(xsk->config.libbpf_flags & XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)) {
err = xsk_setup_xdp_prog(xsk); err = xsk_setup_xdp_prog(xsk);
if (err) if (err)
@ -706,7 +710,8 @@ void xsk_socket__delete(struct xsk_socket *xsk)
if (!xsk) if (!xsk)
return; return;
(void)xsk_update_bpf_maps(xsk, 0, 0); xsk_clear_bpf_maps(xsk);
xsk_delete_bpf_maps(xsk);
optlen = sizeof(off); optlen = sizeof(off);
err = getsockopt(xsk->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen); err = getsockopt(xsk->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen);