summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/veth.c278
1 files changed, 188 insertions, 90 deletions
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 9b0a7b9ab1b8..e3202af72df5 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -42,20 +42,24 @@ struct pcpu_vstats {
struct u64_stats_sync syncp;
};
-struct veth_priv {
+struct veth_rq {
struct napi_struct xdp_napi;
struct net_device *dev;
struct bpf_prog __rcu *xdp_prog;
- struct bpf_prog *_xdp_prog;
- struct net_device __rcu *peer;
- atomic64_t dropped;
struct xdp_mem_info xdp_mem;
- unsigned requested_headroom;
bool rx_notify_masked;
struct ptr_ring xdp_ring;
struct xdp_rxq_info xdp_rxq;
};
+struct veth_priv {
+ struct net_device __rcu *peer;
+ atomic64_t dropped;
+ struct bpf_prog *_xdp_prog;
+ struct veth_rq *rq;
+ unsigned int requested_headroom;
+};
+
/*
* ethtool interface
*/
@@ -144,19 +148,19 @@ static void veth_ptr_free(void *ptr)
kfree_skb(ptr);
}
-static void __veth_xdp_flush(struct veth_priv *priv)
+static void __veth_xdp_flush(struct veth_rq *rq)
{
/* Write ptr_ring before reading rx_notify_masked */
smp_mb();
- if (!priv->rx_notify_masked) {
- priv->rx_notify_masked = true;
- napi_schedule(&priv->xdp_napi);
+ if (!rq->rx_notify_masked) {
+ rq->rx_notify_masked = true;
+ napi_schedule(&rq->xdp_napi);
}
}
-static int veth_xdp_rx(struct veth_priv *priv, struct sk_buff *skb)
+static int veth_xdp_rx(struct veth_rq *rq, struct sk_buff *skb)
{
- if (unlikely(ptr_ring_produce(&priv->xdp_ring, skb))) {
+ if (unlikely(ptr_ring_produce(&rq->xdp_ring, skb))) {
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -164,21 +168,22 @@ static int veth_xdp_rx(struct veth_priv *priv, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
-static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb, bool xdp)
+static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb,
+ struct veth_rq *rq, bool xdp)
{
- struct veth_priv *priv = netdev_priv(dev);
-
return __dev_forward_skb(dev, skb) ?: xdp ?
- veth_xdp_rx(priv, skb) :
+ veth_xdp_rx(rq, skb) :
netif_rx(skb);
}
static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
+ struct veth_rq *rq = NULL;
struct net_device *rcv;
int length = skb->len;
bool rcv_xdp = false;
+ int rxq;
rcu_read_lock();
rcv = rcu_dereference(priv->peer);
@@ -188,9 +193,15 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
}
rcv_priv = netdev_priv(rcv);
- rcv_xdp = rcu_access_pointer(rcv_priv->xdp_prog);
+ rxq = skb_get_queue_mapping(skb);
+ if (rxq < rcv->real_num_rx_queues) {
+ rq = &rcv_priv->rq[rxq];
+ rcv_xdp = rcu_access_pointer(rq->xdp_prog);
+ if (rcv_xdp)
+ skb_record_rx_queue(skb, rxq);
+ }
- if (likely(veth_forward_skb(rcv, skb, rcv_xdp) == NET_RX_SUCCESS)) {
+ if (likely(veth_forward_skb(rcv, skb, rq, rcv_xdp) == NET_RX_SUCCESS)) {
struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);
u64_stats_update_begin(&stats->syncp);
@@ -203,7 +214,7 @@ drop:
}
if (rcv_xdp)
- __veth_xdp_flush(rcv_priv);
+ __veth_xdp_flush(rq);
rcu_read_unlock();
@@ -278,12 +289,18 @@ static struct sk_buff *veth_build_skb(void *head, int headroom, int len,
return skb;
}
+static int veth_select_rxq(struct net_device *dev)
+{
+ return smp_processor_id() % dev->real_num_rx_queues;
+}
+
static int veth_xdp_xmit(struct net_device *dev, int n,
struct xdp_frame **frames, u32 flags)
{
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
struct net_device *rcv;
unsigned int max_len;
+ struct veth_rq *rq;
int i, drops = 0;
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
@@ -294,30 +311,31 @@ static int veth_xdp_xmit(struct net_device *dev, int n,
return -ENXIO;
rcv_priv = netdev_priv(rcv);
+ rq = &rcv_priv->rq[veth_select_rxq(rcv)];
/* Non-NULL xdp_prog ensures that xdp_ring is initialized on receive
* side. This means an XDP program is loaded on the peer and the peer
* device is up.
*/
- if (!rcu_access_pointer(rcv_priv->xdp_prog))
+ if (!rcu_access_pointer(rq->xdp_prog))
return -ENXIO;
max_len = rcv->mtu + rcv->hard_header_len + VLAN_HLEN;
- spin_lock(&rcv_priv->xdp_ring.producer_lock);
+ spin_lock(&rq->xdp_ring.producer_lock);
for (i = 0; i < n; i++) {
struct xdp_frame *frame = frames[i];
void *ptr = veth_xdp_to_ptr(frame);
if (unlikely(frame->len > max_len ||
- __ptr_ring_produce(&rcv_priv->xdp_ring, ptr))) {
+ __ptr_ring_produce(&rq->xdp_ring, ptr))) {
xdp_return_frame_rx_napi(frame);
drops++;
}
}
- spin_unlock(&rcv_priv->xdp_ring.producer_lock);
+ spin_unlock(&rq->xdp_ring.producer_lock);
if (flags & XDP_XMIT_FLUSH)
- __veth_xdp_flush(rcv_priv);
+ __veth_xdp_flush(rq);
return n - drops;
}
@@ -326,6 +344,7 @@ static void veth_xdp_flush(struct net_device *dev)
{
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
struct net_device *rcv;
+ struct veth_rq *rq;
rcu_read_lock();
rcv = rcu_dereference(priv->peer);
@@ -333,11 +352,12 @@ static void veth_xdp_flush(struct net_device *dev)
goto out;
rcv_priv = netdev_priv(rcv);
+ rq = &rcv_priv->rq[veth_select_rxq(rcv)];
/* xdp_ring is initialized on receive side? */
- if (unlikely(!rcu_access_pointer(rcv_priv->xdp_prog)))
+ if (unlikely(!rcu_access_pointer(rq->xdp_prog)))
goto out;
- __veth_xdp_flush(rcv_priv);
+ __veth_xdp_flush(rq);
out:
rcu_read_unlock();
}
@@ -352,7 +372,7 @@ static int veth_xdp_tx(struct net_device *dev, struct xdp_buff *xdp)
return veth_xdp_xmit(dev, 1, &frame, 0);
}
-static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
+static struct sk_buff *veth_xdp_rcv_one(struct veth_rq *rq,
struct xdp_frame *frame,
unsigned int *xdp_xmit)
{
@@ -365,7 +385,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
struct sk_buff *skb;
rcu_read_lock();
- xdp_prog = rcu_dereference(priv->xdp_prog);
+ xdp_prog = rcu_dereference(rq->xdp_prog);
if (likely(xdp_prog)) {
struct xdp_buff xdp;
u32 act;
@@ -374,7 +394,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
xdp.data = frame->data;
xdp.data_end = frame->data + frame->len;
xdp.data_meta = frame->data - frame->metasize;
- xdp.rxq = &priv->xdp_rxq;
+ xdp.rxq = &rq->xdp_rxq;
act = bpf_prog_run_xdp(xdp_prog, &xdp);
@@ -387,8 +407,8 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
orig_frame = *frame;
xdp.data_hard_start = head;
xdp.rxq->mem = frame->mem;
- if (unlikely(veth_xdp_tx(priv->dev, &xdp) < 0)) {
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ if (unlikely(veth_xdp_tx(rq->dev, &xdp) < 0)) {
+ trace_xdp_exception(rq->dev, xdp_prog, act);
frame = &orig_frame;
goto err_xdp;
}
@@ -399,7 +419,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
orig_frame = *frame;
xdp.data_hard_start = head;
xdp.rxq->mem = frame->mem;
- if (xdp_do_redirect(priv->dev, &xdp, xdp_prog)) {
+ if (xdp_do_redirect(rq->dev, &xdp, xdp_prog)) {
frame = &orig_frame;
goto err_xdp;
}
@@ -409,7 +429,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ trace_xdp_exception(rq->dev, xdp_prog, act);
case XDP_DROP:
goto err_xdp;
}
@@ -424,7 +444,7 @@ static struct sk_buff *veth_xdp_rcv_one(struct veth_priv *priv,
}
xdp_scrub_frame(frame);
- skb->protocol = eth_type_trans(skb, priv->dev);
+ skb->protocol = eth_type_trans(skb, rq->dev);
err:
return skb;
err_xdp:
@@ -434,8 +454,7 @@ xdp_xmit:
return NULL;
}
-static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
- struct sk_buff *skb,
+static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, struct sk_buff *skb,
unsigned int *xdp_xmit)
{
u32 pktlen, headroom, act, metalen;
@@ -445,7 +464,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
struct xdp_buff xdp;
rcu_read_lock();
- xdp_prog = rcu_dereference(priv->xdp_prog);
+ xdp_prog = rcu_dereference(rq->xdp_prog);
if (unlikely(!xdp_prog)) {
rcu_read_unlock();
goto out;
@@ -499,7 +518,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
xdp.data = skb_mac_header(skb);
xdp.data_end = xdp.data + pktlen;
xdp.data_meta = xdp.data;
- xdp.rxq = &priv->xdp_rxq;
+ xdp.rxq = &rq->xdp_rxq;
orig_data = xdp.data;
orig_data_end = xdp.data_end;
@@ -511,9 +530,9 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
case XDP_TX:
get_page(virt_to_page(xdp.data));
consume_skb(skb);
- xdp.rxq->mem = priv->xdp_mem;
- if (unlikely(veth_xdp_tx(priv->dev, &xdp) < 0)) {
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ xdp.rxq->mem = rq->xdp_mem;
+ if (unlikely(veth_xdp_tx(rq->dev, &xdp) < 0)) {
+ trace_xdp_exception(rq->dev, xdp_prog, act);
goto err_xdp;
}
*xdp_xmit |= VETH_XDP_TX;
@@ -522,8 +541,8 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
case XDP_REDIRECT:
get_page(virt_to_page(xdp.data));
consume_skb(skb);
- xdp.rxq->mem = priv->xdp_mem;
- if (xdp_do_redirect(priv->dev, &xdp, xdp_prog))
+ xdp.rxq->mem = rq->xdp_mem;
+ if (xdp_do_redirect(rq->dev, &xdp, xdp_prog))
goto err_xdp;
*xdp_xmit |= VETH_XDP_REDIR;
rcu_read_unlock();
@@ -531,7 +550,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
default:
bpf_warn_invalid_xdp_action(act);
case XDP_ABORTED:
- trace_xdp_exception(priv->dev, xdp_prog, act);
+ trace_xdp_exception(rq->dev, xdp_prog, act);
case XDP_DROP:
goto drop;
}
@@ -547,7 +566,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_priv *priv,
off = xdp.data_end - orig_data_end;
if (off != 0)
__skb_put(skb, off);
- skb->protocol = eth_type_trans(skb, priv->dev);
+ skb->protocol = eth_type_trans(skb, rq->dev);
metalen = xdp.data - xdp.data_meta;
if (metalen)
@@ -565,27 +584,26 @@ xdp_xmit:
return NULL;
}
-static int veth_xdp_rcv(struct veth_priv *priv, int budget,
- unsigned int *xdp_xmit)
+static int veth_xdp_rcv(struct veth_rq *rq, int budget, unsigned int *xdp_xmit)
{
int i, done = 0;
for (i = 0; i < budget; i++) {
- void *ptr = __ptr_ring_consume(&priv->xdp_ring);
+ void *ptr = __ptr_ring_consume(&rq->xdp_ring);
struct sk_buff *skb;
if (!ptr)
break;
if (veth_is_xdp_frame(ptr)) {
- skb = veth_xdp_rcv_one(priv, veth_ptr_to_xdp(ptr),
+ skb = veth_xdp_rcv_one(rq, veth_ptr_to_xdp(ptr),
xdp_xmit);
} else {
- skb = veth_xdp_rcv_skb(priv, ptr, xdp_xmit);
+ skb = veth_xdp_rcv_skb(rq, ptr, xdp_xmit);
}
if (skb)
- napi_gro_receive(&priv->xdp_napi, skb);
+ napi_gro_receive(&rq->xdp_napi, skb);
done++;
}
@@ -595,25 +613,25 @@ static int veth_xdp_rcv(struct veth_priv *priv, int budget,
static int veth_poll(struct napi_struct *napi, int budget)
{
- struct veth_priv *priv =
- container_of(napi, struct veth_priv, xdp_napi);
+ struct veth_rq *rq =
+ container_of(napi, struct veth_rq, xdp_napi);
unsigned int xdp_xmit = 0;
int done;
xdp_set_return_frame_no_direct();
- done = veth_xdp_rcv(priv, budget, &xdp_xmit);
+ done = veth_xdp_rcv(rq, budget, &xdp_xmit);
if (done < budget && napi_complete_done(napi, done)) {
/* Write rx_notify_masked before reading ptr_ring */
- smp_store_mb(priv->rx_notify_masked, false);
- if (unlikely(!__ptr_ring_empty(&priv->xdp_ring))) {
- priv->rx_notify_masked = true;
- napi_schedule(&priv->xdp_napi);
+ smp_store_mb(rq->rx_notify_masked, false);
+ if (unlikely(!__ptr_ring_empty(&rq->xdp_ring))) {
+ rq->rx_notify_masked = true;
+ napi_schedule(&rq->xdp_napi);
}
}
if (xdp_xmit & VETH_XDP_TX)
- veth_xdp_flush(priv->dev);
+ veth_xdp_flush(rq->dev);
if (xdp_xmit & VETH_XDP_REDIR)
xdp_do_flush_map();
xdp_clear_return_frame_no_direct();
@@ -624,56 +642,90 @@ static int veth_poll(struct napi_struct *napi, int budget)
static int veth_napi_add(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
- int err;
+ int err, i;
- err = ptr_ring_init(&priv->xdp_ring, VETH_RING_SIZE, GFP_KERNEL);
- if (err)
- return err;
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ err = ptr_ring_init(&rq->xdp_ring, VETH_RING_SIZE, GFP_KERNEL);
+ if (err)
+ goto err_xdp_ring;
+ }
- netif_napi_add(dev, &priv->xdp_napi, veth_poll, NAPI_POLL_WEIGHT);
- napi_enable(&priv->xdp_napi);
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ netif_napi_add(dev, &rq->xdp_napi, veth_poll, NAPI_POLL_WEIGHT);
+ napi_enable(&rq->xdp_napi);
+ }
return 0;
+err_xdp_ring:
+ for (i--; i >= 0; i--)
+ ptr_ring_cleanup(&priv->rq[i].xdp_ring, veth_ptr_free);
+
+ return err;
}
static void veth_napi_del(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
+ int i;
- napi_disable(&priv->xdp_napi);
- netif_napi_del(&priv->xdp_napi);
- priv->rx_notify_masked = false;
- ptr_ring_cleanup(&priv->xdp_ring, veth_ptr_free);
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ napi_disable(&rq->xdp_napi);
+ napi_hash_del(&rq->xdp_napi);
+ }
+ synchronize_net();
+
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ netif_napi_del(&rq->xdp_napi);
+ rq->rx_notify_masked = false;
+ ptr_ring_cleanup(&rq->xdp_ring, veth_ptr_free);
+ }
}
static int veth_enable_xdp(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
- int err;
+ int err, i;
- if (!xdp_rxq_info_is_reg(&priv->xdp_rxq)) {
- err = xdp_rxq_info_reg(&priv->xdp_rxq, dev, 0);
- if (err < 0)
- return err;
+ if (!xdp_rxq_info_is_reg(&priv->rq[0].xdp_rxq)) {
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
- err = xdp_rxq_info_reg_mem_model(&priv->xdp_rxq,
- MEM_TYPE_PAGE_SHARED, NULL);
- if (err < 0)
- goto err;
+ err = xdp_rxq_info_reg(&rq->xdp_rxq, dev, i);
+ if (err < 0)
+ goto err_rxq_reg;
+
+ err = xdp_rxq_info_reg_mem_model(&rq->xdp_rxq,
+ MEM_TYPE_PAGE_SHARED,
+ NULL);
+ if (err < 0)
+ goto err_reg_mem;
+
+ /* Save original mem info as it can be overwritten */
+ rq->xdp_mem = rq->xdp_rxq.mem;
+ }
err = veth_napi_add(dev);
if (err)
- goto err;
-
- /* Save original mem info as it can be overwritten */
- priv->xdp_mem = priv->xdp_rxq.mem;
+ goto err_rxq_reg;
}
- rcu_assign_pointer(priv->xdp_prog, priv->_xdp_prog);
+ for (i = 0; i < dev->real_num_rx_queues; i++)
+ rcu_assign_pointer(priv->rq[i].xdp_prog, priv->_xdp_prog);
return 0;
-err:
- xdp_rxq_info_unreg(&priv->xdp_rxq);
+err_reg_mem:
+ xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq);
+err_rxq_reg:
+ for (i--; i >= 0; i--)
+ xdp_rxq_info_unreg(&priv->rq[i].xdp_rxq);
return err;
}
@@ -681,11 +733,17 @@ err:
static void veth_disable_xdp(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
+ int i;
- rcu_assign_pointer(priv->xdp_prog, NULL);
+ for (i = 0; i < dev->real_num_rx_queues; i++)
+ rcu_assign_pointer(priv->rq[i].xdp_prog, NULL);
veth_napi_del(dev);
- priv->xdp_rxq.mem = priv->xdp_mem;
- xdp_rxq_info_unreg(&priv->xdp_rxq);
+ for (i = 0; i < dev->real_num_rx_queues; i++) {
+ struct veth_rq *rq = &priv->rq[i];
+
+ rq->xdp_rxq.mem = rq->xdp_mem;
+ xdp_rxq_info_unreg(&rq->xdp_rxq);
+ }
}
static int veth_open(struct net_device *dev)
@@ -842,6 +900,12 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
goto err;
}
+ if (dev->real_num_rx_queues < peer->real_num_tx_queues) {
+ NL_SET_ERR_MSG_MOD(extack, "XDP expects number of rx queues not less than peer tx queues");
+ err = -ENOSPC;
+ goto err;
+ }
+
if (dev->flags & IFF_UP) {
err = veth_enable_xdp(dev);
if (err) {
@@ -976,13 +1040,31 @@ static int veth_validate(struct nlattr *tb[], struct nlattr *data[],
return 0;
}
+static int veth_alloc_queues(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+
+ priv->rq = kcalloc(dev->num_rx_queues, sizeof(*priv->rq), GFP_KERNEL);
+ if (!priv->rq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void veth_free_queues(struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+
+ kfree(priv->rq);
+}
+
static struct rtnl_link_ops veth_link_ops;
static int veth_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
- int err;
+ int err, i;
struct net_device *peer;
struct veth_priv *priv;
char ifname[IFNAMSIZ];
@@ -1035,6 +1117,12 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
return PTR_ERR(peer);
}
+ err = veth_alloc_queues(peer);
+ if (err) {
+ put_net(net);
+ goto err_peer_alloc_queues;
+ }
+
if (!ifmp || !tbp[IFLA_ADDRESS])
eth_hw_addr_random(peer);
@@ -1063,6 +1151,10 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
* should be re-allocated
*/
+ err = veth_alloc_queues(dev);
+ if (err)
+ goto err_alloc_queues;
+
if (tb[IFLA_ADDRESS] == NULL)
eth_hw_addr_random(dev);
@@ -1082,22 +1174,28 @@ static int veth_newlink(struct net *src_net, struct net_device *dev,
*/
priv = netdev_priv(dev);
- priv->dev = dev;
+ for (i = 0; i < dev->real_num_rx_queues; i++)
+ priv->rq[i].dev = dev;
rcu_assign_pointer(priv->peer, peer);
priv = netdev_priv(peer);
- priv->dev = peer;
+ for (i = 0; i < peer->real_num_rx_queues; i++)
+ priv->rq[i].dev = peer;
rcu_assign_pointer(priv->peer, dev);
return 0;
err_register_dev:
+ veth_free_queues(dev);
+err_alloc_queues:
/* nothing to do */
err_configure_peer:
unregister_netdevice(peer);
return err;
err_register_peer:
+ veth_free_queues(peer);
+err_peer_alloc_queues:
free_netdev(peer);
return err;
}