diff options
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 57 |
1 files changed, 38 insertions, 19 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 96cf83da0d66..fca407b4a6ea 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -6852,6 +6852,32 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down) | |||
6852 | } | 6852 | } |
6853 | EXPORT_SYMBOL(dev_change_proto_down); | 6853 | EXPORT_SYMBOL(dev_change_proto_down); |
6854 | 6854 | ||
6855 | bool __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op) | ||
6856 | { | ||
6857 | struct netdev_xdp xdp; | ||
6858 | |||
6859 | memset(&xdp, 0, sizeof(xdp)); | ||
6860 | xdp.command = XDP_QUERY_PROG; | ||
6861 | |||
6862 | /* Query must always succeed. */ | ||
6863 | WARN_ON(xdp_op(dev, &xdp) < 0); | ||
6864 | return xdp.prog_attached; | ||
6865 | } | ||
6866 | |||
6867 | static int dev_xdp_install(struct net_device *dev, xdp_op_t xdp_op, | ||
6868 | struct netlink_ext_ack *extack, | ||
6869 | struct bpf_prog *prog) | ||
6870 | { | ||
6871 | struct netdev_xdp xdp; | ||
6872 | |||
6873 | memset(&xdp, 0, sizeof(xdp)); | ||
6874 | xdp.command = XDP_SETUP_PROG; | ||
6875 | xdp.extack = extack; | ||
6876 | xdp.prog = prog; | ||
6877 | |||
6878 | return xdp_op(dev, &xdp); | ||
6879 | } | ||
6880 | |||
6855 | /** | 6881 | /** |
6856 | * dev_change_xdp_fd - set or clear a bpf program for a device rx path | 6882 | * dev_change_xdp_fd - set or clear a bpf program for a device rx path |
6857 | * @dev: device | 6883 | * @dev: device |
@@ -6864,41 +6890,34 @@ EXPORT_SYMBOL(dev_change_proto_down); | |||
6864 | int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, | 6890 | int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, |
6865 | int fd, u32 flags) | 6891 | int fd, u32 flags) |
6866 | { | 6892 | { |
6867 | int (*xdp_op)(struct net_device *dev, struct netdev_xdp *xdp); | ||
6868 | const struct net_device_ops *ops = dev->netdev_ops; | 6893 | const struct net_device_ops *ops = dev->netdev_ops; |
6869 | struct bpf_prog *prog = NULL; | 6894 | struct bpf_prog *prog = NULL; |
6870 | struct netdev_xdp xdp; | 6895 | xdp_op_t xdp_op, xdp_chk; |
6871 | int err; | 6896 | int err; |
6872 | 6897 | ||
6873 | ASSERT_RTNL(); | 6898 | ASSERT_RTNL(); |
6874 | 6899 | ||
6875 | xdp_op = ops->ndo_xdp; | 6900 | xdp_op = xdp_chk = ops->ndo_xdp; |
6901 | if (!xdp_op && (flags & XDP_FLAGS_DRV_MODE)) | ||
6902 | return -EOPNOTSUPP; | ||
6876 | if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE)) | 6903 | if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE)) |
6877 | xdp_op = generic_xdp_install; | 6904 | xdp_op = generic_xdp_install; |
6905 | if (xdp_op == xdp_chk) | ||
6906 | xdp_chk = generic_xdp_install; | ||
6878 | 6907 | ||
6879 | if (fd >= 0) { | 6908 | if (fd >= 0) { |
6880 | if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) { | 6909 | if (xdp_chk && __dev_xdp_attached(dev, xdp_chk)) |
6881 | memset(&xdp, 0, sizeof(xdp)); | 6910 | return -EEXIST; |
6882 | xdp.command = XDP_QUERY_PROG; | 6911 | if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && |
6883 | 6912 | __dev_xdp_attached(dev, xdp_op)) | |
6884 | err = xdp_op(dev, &xdp); | 6913 | return -EBUSY; |
6885 | if (err < 0) | ||
6886 | return err; | ||
6887 | if (xdp.prog_attached) | ||
6888 | return -EBUSY; | ||
6889 | } | ||
6890 | 6914 | ||
6891 | prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); | 6915 | prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); |
6892 | if (IS_ERR(prog)) | 6916 | if (IS_ERR(prog)) |
6893 | return PTR_ERR(prog); | 6917 | return PTR_ERR(prog); |
6894 | } | 6918 | } |
6895 | 6919 | ||
6896 | memset(&xdp, 0, sizeof(xdp)); | 6920 | err = dev_xdp_install(dev, xdp_op, extack, prog); |
6897 | xdp.command = XDP_SETUP_PROG; | ||
6898 | xdp.extack = extack; | ||
6899 | xdp.prog = prog; | ||
6900 | |||
6901 | err = xdp_op(dev, &xdp); | ||
6902 | if (err < 0 && prog) | 6921 | if (err < 0 && prog) |
6903 | bpf_prog_put(prog); | 6922 | bpf_prog_put(prog); |
6904 | 6923 | ||