Commit d74a32ac authored by David S. Miller's avatar David S. Miller

Merge branch 'xdp-netlink-ext-ack'

Jakub Kicinski says:

====================
xdp: use netlink extended ACK reporting

This series is an attempt to make XDP more user friendly by
enabling exploiting the recently added netlink extended ACK
reporting to carry messages to user space.

David Ahern's iproute2 ext ack patches for ip link are sufficient
to show the errors like this:

Error: nfp: MTU too large w/ XDP enabled

Where the message is coming directly from the driver.  There could
still be a bit of a leap for a complete novice from the message
above to the right settings, but it's a big improvement over the
standard "Invalid argument" message.

v1/non-rfc:
 - add a separate macro in patch 1;
 - add KBUILD_MODNAME as part of the message (Daniel);
 - don't print the error to logs in patch 1.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 73333626 9861ce03
...@@ -818,7 +818,8 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, ...@@ -818,7 +818,8 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries,
unsigned int n); unsigned int n);
struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn);
int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new,
struct netlink_ext_ack *extack);
bool nfp_net_link_changed_read_clear(struct nfp_net *nn); bool nfp_net_link_changed_read_clear(struct nfp_net *nn);
int nfp_net_refresh_eth_port(struct nfp_net *nn); int nfp_net_refresh_eth_port(struct nfp_net *nn);
......
...@@ -2524,24 +2524,27 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) ...@@ -2524,24 +2524,27 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn)
return new; return new;
} }
static int nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp) static int
nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp,
struct netlink_ext_ack *extack)
{ {
/* XDP-enabled tests */ /* XDP-enabled tests */
if (!dp->xdp_prog) if (!dp->xdp_prog)
return 0; return 0;
if (dp->fl_bufsz > PAGE_SIZE) { if (dp->fl_bufsz > PAGE_SIZE) {
nn_warn(nn, "MTU too large w/ XDP enabled\n"); NL_MOD_TRY_SET_ERR_MSG(extack, "MTU too large w/ XDP enabled");
return -EINVAL; return -EINVAL;
} }
if (dp->num_tx_rings > nn->max_tx_rings) { if (dp->num_tx_rings > nn->max_tx_rings) {
nn_warn(nn, "Insufficient number of TX rings w/ XDP enabled\n"); NL_MOD_TRY_SET_ERR_MSG(extack, "Insufficient number of TX rings w/ XDP enabled");
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp) int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp,
struct netlink_ext_ack *extack)
{ {
int r, err; int r, err;
...@@ -2553,7 +2556,7 @@ int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp) ...@@ -2553,7 +2556,7 @@ int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp)
dp->num_r_vecs = max(dp->num_rx_rings, dp->num_stack_tx_rings); dp->num_r_vecs = max(dp->num_rx_rings, dp->num_stack_tx_rings);
err = nfp_net_check_config(nn, dp); err = nfp_net_check_config(nn, dp, extack);
if (err) if (err)
goto exit_free_dp; goto exit_free_dp;
...@@ -2628,7 +2631,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) ...@@ -2628,7 +2631,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
dp->mtu = new_mtu; dp->mtu = new_mtu;
return nfp_net_ring_reconfig(nn, dp); return nfp_net_ring_reconfig(nn, dp, NULL);
} }
static void nfp_net_stat64(struct net_device *netdev, static void nfp_net_stat64(struct net_device *netdev,
...@@ -2944,9 +2947,10 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) ...@@ -2944,9 +2947,10 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog)
return ret; return ret;
} }
static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) static int nfp_net_xdp_setup(struct nfp_net *nn, struct netdev_xdp *xdp)
{ {
struct bpf_prog *old_prog = nn->dp.xdp_prog; struct bpf_prog *old_prog = nn->dp.xdp_prog;
struct bpf_prog *prog = xdp->prog;
struct nfp_net_dp *dp; struct nfp_net_dp *dp;
int err; int err;
...@@ -2969,7 +2973,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) ...@@ -2969,7 +2973,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
dp->rx_dma_off = prog ? XDP_PACKET_HEADROOM - nn->dp.rx_offset : 0; dp->rx_dma_off = prog ? XDP_PACKET_HEADROOM - nn->dp.rx_offset : 0;
/* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */
err = nfp_net_ring_reconfig(nn, dp); err = nfp_net_ring_reconfig(nn, dp, xdp->extack);
if (err) if (err)
return err; return err;
...@@ -2987,7 +2991,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp) ...@@ -2987,7 +2991,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp)
switch (xdp->command) { switch (xdp->command) {
case XDP_SETUP_PROG: case XDP_SETUP_PROG:
return nfp_net_xdp_setup(nn, xdp->prog); return nfp_net_xdp_setup(nn, xdp);
case XDP_QUERY_PROG: case XDP_QUERY_PROG:
xdp->prog_attached = !!nn->dp.xdp_prog; xdp->prog_attached = !!nn->dp.xdp_prog;
return 0; return 0;
......
...@@ -309,7 +309,7 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) ...@@ -309,7 +309,7 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt)
dp->rxd_cnt = rxd_cnt; dp->rxd_cnt = rxd_cnt;
dp->txd_cnt = txd_cnt; dp->txd_cnt = txd_cnt;
return nfp_net_ring_reconfig(nn, dp); return nfp_net_ring_reconfig(nn, dp, NULL);
} }
static int nfp_net_set_ringparam(struct net_device *netdev, static int nfp_net_set_ringparam(struct net_device *netdev,
...@@ -880,7 +880,7 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, ...@@ -880,7 +880,7 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx,
if (dp->xdp_prog) if (dp->xdp_prog)
dp->num_tx_rings += total_rx; dp->num_tx_rings += total_rx;
return nfp_net_ring_reconfig(nn, dp); return nfp_net_ring_reconfig(nn, dp, NULL);
} }
static int nfp_net_set_channels(struct net_device *netdev, static int nfp_net_set_channels(struct net_device *netdev,
......
...@@ -1878,7 +1878,8 @@ static int virtnet_reset(struct virtnet_info *vi, int curr_qp, int xdp_qp) ...@@ -1878,7 +1878,8 @@ static int virtnet_reset(struct virtnet_info *vi, int curr_qp, int xdp_qp)
return ret; return ret;
} }
static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog) static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog,
struct netlink_ext_ack *extack)
{ {
unsigned long int max_sz = PAGE_SIZE - sizeof(struct padded_vnet_hdr); unsigned long int max_sz = PAGE_SIZE - sizeof(struct padded_vnet_hdr);
struct virtnet_info *vi = netdev_priv(dev); struct virtnet_info *vi = netdev_priv(dev);
...@@ -1890,16 +1891,17 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog) ...@@ -1890,16 +1891,17 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) ||
virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) ||
virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO)) { virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO)) {
netdev_warn(dev, "can't set XDP while host is implementing LRO, disable LRO first\n"); NL_SET_ERR_MSG(extack, "can't set XDP while host is implementing LRO, disable LRO first");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (vi->mergeable_rx_bufs && !vi->any_header_sg) { if (vi->mergeable_rx_bufs && !vi->any_header_sg) {
netdev_warn(dev, "XDP expects header/data in single page, any_header_sg required\n"); NL_SET_ERR_MSG(extack, "XDP expects header/data in single page, any_header_sg required");
return -EINVAL; return -EINVAL;
} }
if (dev->mtu > max_sz) { if (dev->mtu > max_sz) {
NL_SET_ERR_MSG(extack, "MTU too large to enable XDP");
netdev_warn(dev, "XDP requires MTU less than %lu\n", max_sz); netdev_warn(dev, "XDP requires MTU less than %lu\n", max_sz);
return -EINVAL; return -EINVAL;
} }
...@@ -1910,6 +1912,7 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog) ...@@ -1910,6 +1912,7 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog)
/* XDP requires extra queues for XDP_TX */ /* XDP requires extra queues for XDP_TX */
if (curr_qp + xdp_qp > vi->max_queue_pairs) { if (curr_qp + xdp_qp > vi->max_queue_pairs) {
NL_SET_ERR_MSG(extack, "Too few free TX rings available");
netdev_warn(dev, "request %i queues but max is %i\n", netdev_warn(dev, "request %i queues but max is %i\n",
curr_qp + xdp_qp, vi->max_queue_pairs); curr_qp + xdp_qp, vi->max_queue_pairs);
return -ENOMEM; return -ENOMEM;
...@@ -1971,7 +1974,7 @@ static int virtnet_xdp(struct net_device *dev, struct netdev_xdp *xdp) ...@@ -1971,7 +1974,7 @@ static int virtnet_xdp(struct net_device *dev, struct netdev_xdp *xdp)
{ {
switch (xdp->command) { switch (xdp->command) {
case XDP_SETUP_PROG: case XDP_SETUP_PROG:
return virtnet_xdp_set(dev, xdp->prog); return virtnet_xdp_set(dev, xdp->prog, xdp->extack);
case XDP_QUERY_PROG: case XDP_QUERY_PROG:
xdp->prog_attached = virtnet_xdp_query(dev); xdp->prog_attached = virtnet_xdp_query(dev);
return 0; return 0;
......
...@@ -813,11 +813,16 @@ enum xdp_netdev_command { ...@@ -813,11 +813,16 @@ enum xdp_netdev_command {
XDP_QUERY_PROG, XDP_QUERY_PROG,
}; };
struct netlink_ext_ack;
struct netdev_xdp { struct netdev_xdp {
enum xdp_netdev_command command; enum xdp_netdev_command command;
union { union {
/* XDP_SETUP_PROG */ /* XDP_SETUP_PROG */
struct bpf_prog *prog; struct {
struct bpf_prog *prog;
struct netlink_ext_ack *extack;
};
/* XDP_QUERY_PROG */ /* XDP_QUERY_PROG */
bool prog_attached; bool prog_attached;
}; };
...@@ -3291,7 +3296,8 @@ int dev_get_phys_port_id(struct net_device *dev, ...@@ -3291,7 +3296,8 @@ 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, u32 flags); int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
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);
......
...@@ -92,6 +92,14 @@ struct netlink_ext_ack { ...@@ -92,6 +92,14 @@ struct netlink_ext_ack {
(extack)->_msg = _msg; \ (extack)->_msg = _msg; \
} while (0) } while (0)
#define NL_MOD_TRY_SET_ERR_MSG(extack, msg) do { \
static const char _msg[] = KBUILD_MODNAME ": " msg; \
struct netlink_ext_ack *_extack = (extack); \
\
if (_extack) \
_extack->_msg = _msg; \
} while (0)
extern void netlink_kernel_release(struct sock *sk); extern void netlink_kernel_release(struct sock *sk);
extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
......
...@@ -6854,12 +6854,14 @@ EXPORT_SYMBOL(dev_change_proto_down); ...@@ -6854,12 +6854,14 @@ 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
* @extact: netlink extended ack
* @fd: new program fd or negative value to clear * @fd: new program fd or negative value to clear
* @flags: xdp-related flags * @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, u32 flags) int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
int fd, u32 flags)
{ {
int (*xdp_op)(struct net_device *dev, struct netdev_xdp *xdp); 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;
...@@ -6892,6 +6894,7 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) ...@@ -6892,6 +6894,7 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags)
memset(&xdp, 0, sizeof(xdp)); memset(&xdp, 0, sizeof(xdp));
xdp.command = XDP_SETUP_PROG; xdp.command = XDP_SETUP_PROG;
xdp.extack = extack;
xdp.prog = prog; xdp.prog = prog;
err = xdp_op(dev, &xdp); err = xdp_op(dev, &xdp);
......
...@@ -1919,6 +1919,7 @@ static int do_set_master(struct net_device *dev, int ifindex) ...@@ -1919,6 +1919,7 @@ static int do_set_master(struct net_device *dev, int ifindex)
#define DO_SETLINK_NOTIFY 0x03 #define DO_SETLINK_NOTIFY 0x03
static int do_setlink(const struct sk_buff *skb, static int do_setlink(const struct sk_buff *skb,
struct net_device *dev, struct ifinfomsg *ifm, struct net_device *dev, struct ifinfomsg *ifm,
struct netlink_ext_ack *extack,
struct nlattr **tb, char *ifname, int status) struct nlattr **tb, char *ifname, int status)
{ {
const struct net_device_ops *ops = dev->netdev_ops; const struct net_device_ops *ops = dev->netdev_ops;
...@@ -2201,7 +2202,7 @@ static int do_setlink(const struct sk_buff *skb, ...@@ -2201,7 +2202,7 @@ static int do_setlink(const struct sk_buff *skb,
} }
if (xdp[IFLA_XDP_FD]) { if (xdp[IFLA_XDP_FD]) {
err = dev_change_xdp_fd(dev, err = dev_change_xdp_fd(dev, extack,
nla_get_s32(xdp[IFLA_XDP_FD]), nla_get_s32(xdp[IFLA_XDP_FD]),
xdp_flags); xdp_flags);
if (err) if (err)
...@@ -2261,7 +2262,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2261,7 +2262,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
if (err < 0) if (err < 0)
goto errout; goto errout;
err = do_setlink(skb, dev, ifm, tb, ifname, 0); err = do_setlink(skb, dev, ifm, extack, tb, ifname, 0);
errout: errout:
return err; return err;
} }
...@@ -2423,6 +2424,7 @@ EXPORT_SYMBOL(rtnl_create_link); ...@@ -2423,6 +2424,7 @@ EXPORT_SYMBOL(rtnl_create_link);
static int rtnl_group_changelink(const struct sk_buff *skb, static int rtnl_group_changelink(const struct sk_buff *skb,
struct net *net, int group, struct net *net, int group,
struct ifinfomsg *ifm, struct ifinfomsg *ifm,
struct netlink_ext_ack *extack,
struct nlattr **tb) struct nlattr **tb)
{ {
struct net_device *dev, *aux; struct net_device *dev, *aux;
...@@ -2430,7 +2432,7 @@ static int rtnl_group_changelink(const struct sk_buff *skb, ...@@ -2430,7 +2432,7 @@ static int rtnl_group_changelink(const struct sk_buff *skb,
for_each_netdev_safe(net, dev, aux) { for_each_netdev_safe(net, dev, aux) {
if (dev->group == group) { if (dev->group == group) {
err = do_setlink(skb, dev, ifm, tb, NULL, 0); err = do_setlink(skb, dev, ifm, extack, tb, NULL, 0);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -2576,14 +2578,15 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2576,14 +2578,15 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
status |= DO_SETLINK_NOTIFY; status |= DO_SETLINK_NOTIFY;
} }
return do_setlink(skb, dev, ifm, tb, ifname, status); return do_setlink(skb, dev, ifm, extack, tb, ifname,
status);
} }
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
if (ifm->ifi_index == 0 && tb[IFLA_GROUP]) if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
return rtnl_group_changelink(skb, net, return rtnl_group_changelink(skb, net,
nla_get_u32(tb[IFLA_GROUP]), nla_get_u32(tb[IFLA_GROUP]),
ifm, tb); ifm, extack, tb);
return -ENODEV; return -ENODEV;
} }
......
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