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: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
0f29f05bd7
commit
85de8576a0
|
@ -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 {
|
||||||
|
|
||||||
/* 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);
|
||||||
* 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] = {
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue