Factor out the check and change logic from dev_change_xdp_fd(), and export them for the following commit. Signed-off-by: Toshiaki Makita <toshiaki.makita1@xxxxxxxxx> --- include/linux/netdevice.h | 4 ++ net/core/dev.c | 111 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3207e0b..c338a73 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3707,6 +3707,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_check_xdp(struct net_device *dev, struct netlink_ext_ack *extack, + bool do_install, u32 *prog_id_p, u32 flags); +int dev_change_xdp(struct net_device *dev, struct netlink_ext_ack *extack, + struct bpf_prog *prod, u32 flags); 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 8bc3dce..9965675 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -8317,23 +8317,24 @@ static void dev_xdp_uninstall(struct net_device *dev) } /** - * dev_change_xdp_fd - set or clear a bpf program for a device rx path + * dev_check_xdp - check if xdp prog can be [un]installed * @dev: device * @extack: netlink extended ack - * @fd: new program fd or negative value to clear + * @install: flag to install or uninstall + * @prog_id_p: pointer to a storage for program id * @flags: xdp-related flags * - * Set or clear a bpf program for a device + * Check if xdp prog can be [un]installed + * If a program is already loaded, store the prog id to prog_id_p */ -int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, - int fd, u32 flags) +int dev_check_xdp(struct net_device *dev, struct netlink_ext_ack *extack, + bool install, u32 *prog_id_p, u32 flags) { const struct net_device_ops *ops = dev->netdev_ops; enum bpf_netdev_command query; - struct bpf_prog *prog = NULL; bpf_op_t bpf_op, bpf_chk; bool offload; - int err; + u32 prog_id; ASSERT_RTNL(); @@ -8350,28 +8351,64 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, if (bpf_op == bpf_chk) bpf_chk = generic_xdp_install; - if (fd >= 0) { - u32 prog_id; - + if (install) { if (!offload && __dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG)) { NL_SET_ERR_MSG(extack, "native and generic XDP can't be active at the same time"); return -EEXIST; } prog_id = __dev_xdp_query(dev, bpf_op, query); + if (prog_id_p) + *prog_id_p = prog_id; if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && prog_id) { NL_SET_ERR_MSG(extack, "XDP program already attached"); return -EBUSY; } + } else { + prog_id = __dev_xdp_query(dev, bpf_op, query); + if (prog_id_p) + *prog_id_p = prog_id; + if (!prog_id) + return -ENOENT; + } - prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP, - bpf_op == ops->ndo_bpf); - if (IS_ERR(prog)) - return PTR_ERR(prog); + return 0; +} +EXPORT_SYMBOL_GPL(dev_check_xdp); + +/** + * dev_change_xdp - set or clear a bpf program for a device rx path + * @dev: device + * @extack: netlink extended ack + * @prog: bpf progam + * @flags: xdp-related flags + * + * Set or clear a bpf program for a device. + * Caller must call dev_check_xdp before calling this function to + * check if xdp prog can be [un]installed. + */ +int dev_change_xdp(struct net_device *dev, struct netlink_ext_ack *extack, + struct bpf_prog *prog, u32 flags) +{ + const struct net_device_ops *ops = dev->netdev_ops; + enum bpf_netdev_command query; + bpf_op_t bpf_op; + bool offload; + + ASSERT_RTNL(); + + offload = flags & XDP_FLAGS_HW_MODE; + query = offload ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG; + + bpf_op = ops->ndo_bpf; + if (!bpf_op || (flags & XDP_FLAGS_SKB_MODE)) + bpf_op = generic_xdp_install; + + if (prog) { + u32 prog_id = __dev_xdp_query(dev, bpf_op, query); if (!offload && bpf_prog_is_dev_bound(prog->aux)) { NL_SET_ERR_MSG(extack, "using device-bound program without HW_MODE flag is not supported"); - bpf_prog_put(prog); return -EINVAL; } @@ -8379,13 +8416,47 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, bpf_prog_put(prog); return 0; } - } else { - if (!__dev_xdp_query(dev, bpf_op, query)) - return 0; } - err = dev_xdp_install(dev, bpf_op, extack, flags, prog); - if (err < 0 && prog) + return dev_xdp_install(dev, bpf_op, extack, flags, prog); +} +EXPORT_SYMBOL_GPL(dev_change_xdp); + +/** + * dev_change_xdp_fd - set or clear a bpf program for a device rx path + * @dev: device + * @extack: netlink extended ack + * @fd: new program fd or negative value to clear + * @flags: xdp-related flags + * + * Set or clear a bpf program for a device + */ +int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, + int fd, u32 flags) +{ + struct bpf_prog *prog = NULL; + bool install = fd >= 0; + int err; + + err = dev_check_xdp(dev, extack, install, NULL, flags); + if (err) { + if (!install && err == -ENOENT) + err = 0; + return err; + } + + if (install) { + bool attach_drv; + + attach_drv = dev->netdev_ops->ndo_bpf && + !(flags & XDP_FLAGS_SKB_MODE); + prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP, attach_drv); + if (IS_ERR(prog)) + return PTR_ERR(prog); + } + + err = dev_change_xdp(dev, extack, prog, flags); + if (err && prog) bpf_prog_put(prog); return err; -- 1.8.3.1