Commit 85de8576 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

bpf, xdp: allow to pass flags to dev_change_xdp_fd

Add an IFLA_XDP_FLAGS attribute that can be passed for setting up
XDP along with IFLA_XDP_FD, which eventually allows user space to
implement typical add/replace/delete logic for programs. Right now,
calling into dev_change_xdp_fd() will always replace previous programs.

When passed XDP_FLAGS_UPDATE_IF_NOEXIST, we can handle this more
graceful when requested by returning -EBUSY in case we try to
attach a new program, but we find that another one is already
attached. This will be used by upcoming front-end for iproute2 as
well.
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0f29f05b
...@@ -3253,7 +3253,7 @@ int dev_get_phys_port_id(struct net_device *dev, ...@@ -3253,7 +3253,7 @@ int dev_get_phys_port_id(struct net_device *dev,
int dev_get_phys_port_name(struct net_device *dev, int dev_get_phys_port_name(struct net_device *dev,
char *name, size_t len); char *name, size_t len);
int dev_change_proto_down(struct net_device *dev, bool proto_down); int dev_change_proto_down(struct net_device *dev, bool proto_down);
int dev_change_xdp_fd(struct net_device *dev, int fd); int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags);
struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev); struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev);
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret); struct netdev_queue *txq, int *ret);
......
...@@ -876,10 +876,14 @@ enum { ...@@ -876,10 +876,14 @@ enum {
/* XDP section */ /* XDP section */
#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0)
#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST)
enum { enum {
IFLA_XDP_UNSPEC, IFLA_XDP_UNSPEC,
IFLA_XDP_FD, IFLA_XDP_FD,
IFLA_XDP_ATTACHED, IFLA_XDP_ATTACHED,
IFLA_XDP_FLAGS,
__IFLA_XDP_MAX, __IFLA_XDP_MAX,
}; };
......
...@@ -6692,26 +6692,42 @@ EXPORT_SYMBOL(dev_change_proto_down); ...@@ -6692,26 +6692,42 @@ EXPORT_SYMBOL(dev_change_proto_down);
* 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
* @fd: new program fd or negative value to clear * @fd: new program fd or negative value to clear
* @flags: xdp-related flags
* *
* Set or clear a bpf program for a device * Set or clear a bpf program for a device
*/ */
int dev_change_xdp_fd(struct net_device *dev, int fd) int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags)
{ {
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 = {}; struct netdev_xdp xdp;
int err; int err;
ASSERT_RTNL();
if (!ops->ndo_xdp) if (!ops->ndo_xdp)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (fd >= 0) { if (fd >= 0) {
if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) {
memset(&xdp, 0, sizeof(xdp));
xdp.command = XDP_QUERY_PROG;
err = ops->ndo_xdp(dev, &xdp);
if (err < 0)
return err;
if (xdp.prog_attached)
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.command = XDP_SETUP_PROG;
xdp.prog = prog; xdp.prog = prog;
err = ops->ndo_xdp(dev, &xdp); err = ops->ndo_xdp(dev, &xdp);
if (err < 0 && prog) if (err < 0 && prog)
bpf_prog_put(prog); bpf_prog_put(prog);
......
...@@ -1505,6 +1505,7 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = { ...@@ -1505,6 +1505,7 @@ static const struct nla_policy ifla_port_policy[IFLA_PORT_MAX+1] = {
static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = {
[IFLA_XDP_FD] = { .type = NLA_S32 }, [IFLA_XDP_FD] = { .type = NLA_S32 },
[IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, [IFLA_XDP_ATTACHED] = { .type = NLA_U8 },
[IFLA_XDP_FLAGS] = { .type = NLA_U32 },
}; };
static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla) static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla)
...@@ -2164,6 +2165,7 @@ static int do_setlink(const struct sk_buff *skb, ...@@ -2164,6 +2165,7 @@ static int do_setlink(const struct sk_buff *skb,
if (tb[IFLA_XDP]) { if (tb[IFLA_XDP]) {
struct nlattr *xdp[IFLA_XDP_MAX + 1]; struct nlattr *xdp[IFLA_XDP_MAX + 1];
u32 xdp_flags = 0;
err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP], err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP],
ifla_xdp_policy); ifla_xdp_policy);
...@@ -2174,9 +2176,19 @@ static int do_setlink(const struct sk_buff *skb, ...@@ -2174,9 +2176,19 @@ static int do_setlink(const struct sk_buff *skb,
err = -EINVAL; err = -EINVAL;
goto errout; goto errout;
} }
if (xdp[IFLA_XDP_FLAGS]) {
xdp_flags = nla_get_u32(xdp[IFLA_XDP_FLAGS]);
if (xdp_flags & ~XDP_FLAGS_MASK) {
err = -EINVAL;
goto errout;
}
}
if (xdp[IFLA_XDP_FD]) { if (xdp[IFLA_XDP_FD]) {
err = dev_change_xdp_fd(dev, err = dev_change_xdp_fd(dev,
nla_get_s32(xdp[IFLA_XDP_FD])); nla_get_s32(xdp[IFLA_XDP_FD]),
xdp_flags);
if (err) if (err)
goto errout; goto errout;
status |= DO_SETLINK_NOTIFY; status |= DO_SETLINK_NOTIFY;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment