As UMH runs under RTNL, it cannot attach XDP from userspace. Thus the kernel, xdp_flow module, installs the XDP program. NOTE: As an RFC, XDP-related logic is emulating dev_change_xdp_fd(). I'm thinking I should factor out the logic from dev_change_xdp_fd() and export it instead. Signed-off-by: Toshiaki Makita <toshiaki.makita1@xxxxxxxxx> --- include/linux/netdevice.h | 4 +++ net/core/dev.c | 11 ++++--- net/xdp_flow/xdp_flow_kern_mod.c | 63 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8829295..c99e022 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3678,6 +3678,10 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq, int *ret); typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf); +int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, + struct netlink_ext_ack *extack, u32 flags, + struct bpf_prog *prog); +int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp); int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, int fd, u32 flags); u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op, diff --git a/net/core/dev.c b/net/core/dev.c index fc676b2..a45d2e4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5145,7 +5145,7 @@ static void __netif_receive_skb_list(struct list_head *head) memalloc_noreclaim_restore(noreclaim_flag); } -static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) +int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) { struct bpf_prog *old = rtnl_dereference(dev->xdp_prog); struct bpf_prog *new = xdp->prog; @@ -5177,6 +5177,7 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) return ret; } +EXPORT_SYMBOL_GPL(generic_xdp_install); static int netif_receive_skb_internal(struct sk_buff *skb) { @@ -8001,10 +8002,11 @@ u32 __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op, return xdp.prog_id; } +EXPORT_SYMBOL_GPL(__dev_xdp_query); -static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, - struct netlink_ext_ack *extack, u32 flags, - struct bpf_prog *prog) +int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, + struct netlink_ext_ack *extack, u32 flags, + struct bpf_prog *prog) { struct netdev_bpf xdp; @@ -8019,6 +8021,7 @@ static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, return bpf_op(dev, &xdp); } +EXPORT_SYMBOL_GPL(dev_xdp_install); static void dev_xdp_uninstall(struct net_device *dev) { diff --git a/net/xdp_flow/xdp_flow_kern_mod.c b/net/xdp_flow/xdp_flow_kern_mod.c index 823ab65..9cf527d 100644 --- a/net/xdp_flow/xdp_flow_kern_mod.c +++ b/net/xdp_flow/xdp_flow_kern_mod.c @@ -116,10 +116,26 @@ static int xdp_flow_setup_block_cb(enum tc_setup_type type, void *type_data, static int xdp_flow_setup_bind(struct net_device *dev, struct netlink_ext_ack *extack) { + enum bpf_prog_type attach_type = BPF_PROG_TYPE_XDP; struct mbox_request *req; + bpf_op_t bpf_op, bpf_chk; + struct bpf_prog *prog; u32 id = 0; int err; + bpf_op = bpf_chk = dev->netdev_ops->ndo_bpf; + if (!bpf_op) + bpf_op = generic_xdp_install; + else + bpf_chk = generic_xdp_install; + + /* TODO: These checks should be unified with net core */ + if (__dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) + return -EEXIST; + + if (__dev_xdp_query(dev, bpf_op, XDP_QUERY_PROG)) + return -EBUSY; + req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; @@ -129,21 +145,56 @@ static int xdp_flow_setup_bind(struct net_device *dev, /* Load bpf in UMH and get prog id */ err = transact_umh(req, &id); + if (err) + goto out; + + prog = bpf_prog_get_by_id(id); + if (IS_ERR(prog)) { + err = PTR_ERR(prog); + goto err_umh; + } + + if (!bpf_prog_get_ok(prog, &attach_type, false)) { + err = -EINVAL; + goto err_prog; + } - /* TODO: id will be used to attach bpf prog to XDP - * As we have rtnl_lock, UMH cannot attach prog to XDP - */ + /* As we have rtnl_lock, install XDP in kernel */ + err = dev_xdp_install(dev, bpf_op, extack, 0, prog); + if (err) + goto err_prog; + /* TODO: Should get prog once more and save it for later check */ +out: kfree(req); return err; +err_prog: + bpf_prog_put(prog); +err_umh: + req->cmd = XDP_FLOW_CMD_UNLOAD; + transact_umh(req, NULL); + + goto out; } static int xdp_flow_setup_unbind(struct net_device *dev, struct netlink_ext_ack *extack) { struct mbox_request *req; - int err; + int err, ret = 0; + bpf_op_t bpf_op; + + bpf_op = dev->netdev_ops->ndo_bpf; + if (!bpf_op) + bpf_op = generic_xdp_install; + + /* TODO: Should check if prog is not changed */ + err = dev_xdp_install(dev, bpf_op, extack, 0, NULL); + if (err) { + pr_warn("Failed to uninstall XDP prog: %d\n", err); + ret = err; + } req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) @@ -153,10 +204,12 @@ static int xdp_flow_setup_unbind(struct net_device *dev, req->ifindex = dev->ifindex; err = transact_umh(req, NULL); + if (err) + ret = err; kfree(req); - return err; + return ret; } static int xdp_flow_setup(struct net_device *dev, bool do_bind, -- 1.8.3.1