From: Toshiaki Makita <makita.toshi...@lab.ntt.co.jp>

Use percpu temporary storage to avoid per-packet spinlock.
This is different from dequeue in that multiple veth devices can be
redirect target in one napi loop so allocate percpu storage in veth
private structure.

Signed-off-by: Toshiaki Makita <makita.toshi...@lab.ntt.co.jp>
---
 drivers/net/veth.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 1592119e3873..5978d76f2c00 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -38,12 +38,18 @@ struct pcpu_vstats {
        struct u64_stats_sync   syncp;
 };
 
+struct xdp_queue {
+       void *q[VETH_XDP_QUEUE_SIZE];
+       unsigned int len;
+};
+
 struct veth_priv {
        struct napi_struct      xdp_napi;
        struct net_device       *dev;
        struct bpf_prog __rcu   *xdp_prog;
        struct net_device __rcu *peer;
        atomic64_t              dropped;
+       struct xdp_queue __percpu *xdp_produce_q;
        struct xdp_mem_info     xdp_mem;
        unsigned                requested_headroom;
        bool                    rx_notify_masked;
@@ -147,8 +153,48 @@ static void veth_ptr_free(void *ptr)
        }
 }
 
+static void veth_xdp_cleanup_queues(struct veth_priv *priv)
+{
+       int cpu;
+
+       for_each_possible_cpu(cpu) {
+               struct xdp_queue *q = per_cpu_ptr(priv->xdp_produce_q, cpu);
+               int i;
+
+               for (i = 0; i < q->len; i++)
+                       veth_ptr_free(q->q[i]);
+
+               q->len = 0;
+       }
+}
+
+static bool veth_xdp_flush_queue(struct veth_priv *priv)
+{
+       struct xdp_queue *q = this_cpu_ptr(priv->xdp_produce_q);
+       int i;
+
+       if (unlikely(!q->len))
+               return false;
+
+       spin_lock(&priv->xdp_ring.producer_lock);
+       for (i = 0; i < q->len; i++) {
+               void *ptr = q->q[i];
+
+               if (unlikely(__ptr_ring_produce(&priv->xdp_ring, ptr)))
+                       veth_ptr_free(ptr);
+       }
+       spin_unlock(&priv->xdp_ring.producer_lock);
+
+       q->len = 0;
+
+       return true;
+}
+
 static void __veth_xdp_flush(struct veth_priv *priv)
 {
+       if (unlikely(!veth_xdp_flush_queue(priv)))
+               return;
+
        /* Write ptr_ring before reading rx_notify_masked */
        smp_mb();
        if (!priv->rx_notify_masked) {
@@ -159,9 +205,13 @@ static void __veth_xdp_flush(struct veth_priv *priv)
 
 static int veth_xdp_enqueue(struct veth_priv *priv, void *ptr)
 {
-       if (unlikely(ptr_ring_produce(&priv->xdp_ring, ptr)))
+       struct xdp_queue *q = this_cpu_ptr(priv->xdp_produce_q);
+
+       if (unlikely(q->len >= VETH_XDP_QUEUE_SIZE))
                return -ENOSPC;
 
+       q->q[q->len++] = ptr;
+
        return 0;
 }
 
@@ -644,6 +694,7 @@ static void veth_napi_del(struct net_device *dev)
 
        napi_disable(&priv->xdp_napi);
        netif_napi_del(&priv->xdp_napi);
+       veth_xdp_cleanup_queues(priv);
        ptr_ring_cleanup(&priv->xdp_ring, veth_ptr_free);
 }
 
@@ -711,15 +762,28 @@ static int is_valid_veth_mtu(int mtu)
 
 static int veth_dev_init(struct net_device *dev)
 {
+       struct veth_priv *priv = netdev_priv(dev);
+
        dev->vstats = netdev_alloc_pcpu_stats(struct pcpu_vstats);
        if (!dev->vstats)
                return -ENOMEM;
+
+       priv->xdp_produce_q = __alloc_percpu(sizeof(*priv->xdp_produce_q),
+                                            sizeof (void *));
+       if (!priv->xdp_produce_q) {
+               free_percpu(dev->vstats);
+               return -ENOMEM;
+       }
+
        return 0;
 }
 
 static void veth_dev_free(struct net_device *dev)
 {
+       struct veth_priv *priv = netdev_priv(dev);
+
        free_percpu(dev->vstats);
+       free_percpu(priv->xdp_produce_q);
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
-- 
2.14.3

Reply via email to