|
@@ -6852,6 +6852,32 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(dev_change_proto_down);
|
|
EXPORT_SYMBOL(dev_change_proto_down);
|
|
|
|
|
|
|
|
+bool __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op)
|
|
|
|
+{
|
|
|
|
+ struct netdev_xdp xdp;
|
|
|
|
+
|
|
|
|
+ memset(&xdp, 0, sizeof(xdp));
|
|
|
|
+ xdp.command = XDP_QUERY_PROG;
|
|
|
|
+
|
|
|
|
+ /* Query must always succeed. */
|
|
|
|
+ WARN_ON(xdp_op(dev, &xdp) < 0);
|
|
|
|
+ return xdp.prog_attached;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int dev_xdp_install(struct net_device *dev, xdp_op_t xdp_op,
|
|
|
|
+ struct netlink_ext_ack *extack,
|
|
|
|
+ struct bpf_prog *prog)
|
|
|
|
+{
|
|
|
|
+ struct netdev_xdp xdp;
|
|
|
|
+
|
|
|
|
+ memset(&xdp, 0, sizeof(xdp));
|
|
|
|
+ xdp.command = XDP_SETUP_PROG;
|
|
|
|
+ xdp.extack = extack;
|
|
|
|
+ xdp.prog = prog;
|
|
|
|
+
|
|
|
|
+ return xdp_op(dev, &xdp);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* dev_change_xdp_fd - set or clear a bpf program for a device rx path
|
|
* dev_change_xdp_fd - set or clear a bpf program for a device rx path
|
|
* @dev: device
|
|
* @dev: device
|
|
@@ -6864,41 +6890,34 @@ EXPORT_SYMBOL(dev_change_proto_down);
|
|
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
|
|
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
|
|
int fd, u32 flags)
|
|
int fd, u32 flags)
|
|
{
|
|
{
|
|
- int (*xdp_op)(struct net_device *dev, struct netdev_xdp *xdp);
|
|
|
|
const struct net_device_ops *ops = dev->netdev_ops;
|
|
const struct net_device_ops *ops = dev->netdev_ops;
|
|
struct bpf_prog *prog = NULL;
|
|
struct bpf_prog *prog = NULL;
|
|
- struct netdev_xdp xdp;
|
|
|
|
|
|
+ xdp_op_t xdp_op, xdp_chk;
|
|
int err;
|
|
int err;
|
|
|
|
|
|
ASSERT_RTNL();
|
|
ASSERT_RTNL();
|
|
|
|
|
|
- xdp_op = ops->ndo_xdp;
|
|
|
|
|
|
+ xdp_op = xdp_chk = ops->ndo_xdp;
|
|
|
|
+ if (!xdp_op && (flags & XDP_FLAGS_DRV_MODE))
|
|
|
|
+ return -EOPNOTSUPP;
|
|
if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE))
|
|
if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE))
|
|
xdp_op = generic_xdp_install;
|
|
xdp_op = generic_xdp_install;
|
|
|
|
+ if (xdp_op == xdp_chk)
|
|
|
|
+ xdp_chk = generic_xdp_install;
|
|
|
|
|
|
if (fd >= 0) {
|
|
if (fd >= 0) {
|
|
- if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) {
|
|
|
|
- memset(&xdp, 0, sizeof(xdp));
|
|
|
|
- xdp.command = XDP_QUERY_PROG;
|
|
|
|
-
|
|
|
|
- err = xdp_op(dev, &xdp);
|
|
|
|
- if (err < 0)
|
|
|
|
- return err;
|
|
|
|
- if (xdp.prog_attached)
|
|
|
|
- return -EBUSY;
|
|
|
|
- }
|
|
|
|
|
|
+ if (xdp_chk && __dev_xdp_attached(dev, xdp_chk))
|
|
|
|
+ return -EEXIST;
|
|
|
|
+ if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) &&
|
|
|
|
+ __dev_xdp_attached(dev, xdp_op))
|
|
|
|
+ return -EBUSY;
|
|
|
|
|
|
prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP);
|
|
prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP);
|
|
if (IS_ERR(prog))
|
|
if (IS_ERR(prog))
|
|
return PTR_ERR(prog);
|
|
return PTR_ERR(prog);
|
|
}
|
|
}
|
|
|
|
|
|
- memset(&xdp, 0, sizeof(xdp));
|
|
|
|
- xdp.command = XDP_SETUP_PROG;
|
|
|
|
- xdp.extack = extack;
|
|
|
|
- xdp.prog = prog;
|
|
|
|
-
|
|
|
|
- err = xdp_op(dev, &xdp);
|
|
|
|
|
|
+ err = dev_xdp_install(dev, xdp_op, extack, prog);
|
|
if (err < 0 && prog)
|
|
if (err < 0 && prog)
|
|
bpf_prog_put(prog);
|
|
bpf_prog_put(prog);
|
|
|
|
|