mlx4: Fix transmit flow control and concurrency
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 22 Jun 2017 20:16:31 +0000 (16:16 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 20 Jul 2017 12:19:46 +0000 (08:19 -0400)
We had no way to put backpressure on the network stack, and if you sent
packets fast enough, you'd overflow the NIC's TX queues, and get a "CQ
overrun".

Also, I noticed we had no protection for concurrency.  If we had two
concurrent transmitters, they would fight for slots in the ring.  The worst
I could come up with was one flow could get another's payload (consider
TSO'd packets, but someone else's packet replaces one of your frags).
That'd be another nightmare to debug.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/drivers/net/mlx4/en_netdev.c
kern/drivers/net/mlx4/en_tx.c
kern/drivers/net/mlx4/main.c
kern/drivers/net/mlx4/mlx4_en.h

index 984a117..55b34ad 100644 (file)
@@ -1675,7 +1675,7 @@ int mlx4_en_start_port(struct ether *dev)
 #if 0 // AKAROS_PORT
                tx_ring->tx_queue = netdev_get_tx_queue(dev, i);
 #else
-               tx_ring->tx_queue = 0; // TODO
+               tx_ring->tx_queue = 0; /* TODO multi-queue support. */
 #endif
 
                /* Arm CQ for TX completions */
index 58b1a75..10a9543 100644 (file)
@@ -219,6 +219,7 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
                netif_set_xps_queue(priv->dev, &ring->affinity_mask,
                                    ring->queue_index);
 #endif
+       poke_init(&ring->poker, __mlx4_xmit_poke);
 
        return err;
 }
@@ -490,6 +491,15 @@ static bool mlx4_en_process_tx_cq(struct ether *dev,
                netif_tx_wake_queue(ring->tx_queue);
                ring->wake_queue++;
        }
+#else
+       struct mlx4_poke_args args;
+
+       if (txbbs_skipped > 0) {
+               args.edev = dev;
+               args.priv = priv;
+               args.ring = ring;
+               poke(&ring->poker, &args);
+       }
 #endif
        return done < budget;
 }
@@ -728,10 +738,9 @@ static size_t get_lso_hdr_size(struct block *block)
        return block->transport_header_end;
 }
 
-netdev_tx_t mlx4_send_packet(struct block *block, struct ether *dev)
+netdev_tx_t mlx4_send_packet(struct block *block, struct mlx4_en_priv *priv,
+                             struct mlx4_en_tx_ring *ring)
 {
-       struct mlx4_en_priv *priv = netdev_priv(dev);
-       struct mlx4_en_tx_ring *ring;
        struct mlx4_en_tx_desc *tx_desc;
        struct mlx4_wqe_data_seg *data;
        struct mlx4_en_tx_info *tx_info;
@@ -752,8 +761,6 @@ netdev_tx_t mlx4_send_packet(struct block *block, struct ether *dev)
        if (!priv->port_up)
                goto tx_drop;
 
-       ring = priv->tx_ring[0]; /* TODO multi-queue support */
-
        lso_header_size = get_lso_hdr_size(block);
        for (i_frag = 0; i_frag < block->nr_extra_bufs; i_frag++) {
                const struct extra_bdata *ebd;
@@ -919,6 +926,8 @@ netdev_tx_t mlx4_send_packet(struct block *block, struct ether *dev)
        if (unlikely(bounce))
                tx_desc = mlx4_en_bounce_to_desc(priv, ring, index, desc_size);
 
+       /* Flow control is handled by the mlx4_transmit function in main.c */
+
        real_size = (real_size / 16) & 0x3f; /* Clear fence bit. */
 
        tx_desc->ctrl.vlan_tag = 0;
@@ -1274,3 +1283,31 @@ tx_drop:
 }
 #endif
 
+void __mlx4_xmit_poke(void *args)
+{
+       struct ether *edev = ((struct mlx4_poke_args*)args)->edev;
+       struct mlx4_en_priv *priv = ((struct mlx4_poke_args*)args)->priv;
+       struct mlx4_en_tx_ring *ring = ((struct mlx4_poke_args*)args)->ring;
+       struct block *block;
+
+       while (!mlx4_en_ring_is_full(ring)) {
+               block = qget(edev->oq);
+               if (!block)
+                       break;
+               mlx4_send_packet(block, priv, ring);
+       }
+}
+
+void mlx4_transmit(struct ether *edev)
+{
+       struct mlx4_en_priv *priv = netdev_priv(edev);
+       struct mlx4_en_tx_ring *ring;
+       struct mlx4_poke_args args;
+
+       /* TODO multi-queue support. */
+       ring = priv->tx_ring[0];
+       args.edev = edev;
+       args.priv = priv;
+       args.ring = ring;
+       poke(&ring->poker, &args);
+}
index 3cf3c8c..4121966 100644 (file)
@@ -3736,6 +3736,7 @@ end:
 #endif
 }
 
+#if 0 // AKAROS_PORT
 static void mlx4_shutdown(struct pci_device *pdev)
 {
        struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev);
@@ -3746,6 +3747,12 @@ static void mlx4_shutdown(struct pci_device *pdev)
                mlx4_unload_one(pdev);
        qunlock(&persist->interface_state_mutex);
 }
+#else
+static void mlx4_shutdown(struct ether *edev)
+{
+       panic("Not implemented");
+}
+#endif
 
 #if 0 // AKAROS_PORT
 static const struct pci_error_handlers mlx4_err_handler = {
@@ -3854,7 +3861,6 @@ module_exit(mlx4_cleanup);
 
 extern int mlx4_en_init(void);
 extern int mlx4_en_open(struct ether *dev);
-extern netdev_tx_t mlx4_send_packet(struct block *block, struct ether *dev);
 
 static const struct pci_device_id *search_pci_table(struct pci_device *needle)
 {
@@ -3870,31 +3876,21 @@ static const struct pci_device_id *search_pci_table(struct pci_device *needle)
        return NULL;
 }
 
-static void ether_attach(struct ether *edev)
+static void mlx4_attach(struct ether *edev)
 {
        mlx4_en_open(edev);
 }
 
-static void ether_transmit(struct ether *edev)
-{
-       struct block *block;
+/* The organization of this driver is a fucking catastrophe */
+extern void mlx4_transmit(struct ether *edev);
 
-       while ((block = qget(edev->oq)))
-               mlx4_send_packet(block, edev);
-}
-
-static long ether_ifstat(struct ether *edev, void *a, long n, uint32_t offset)
+static long mlx4_ifstat(struct ether *edev, void *a, long n, uint32_t offset)
 {
        printk("edev %p a %p n %d offset %u\n", edev, a, n, offset);
        return 0;
 }
 
-static long ether_ctl(struct ether *edev, void *buf, long n)
-{
-       panic("Not implemented");
-}
-
-static void ether_shutdown(struct ether *edev)
+static long mlx4_ctl(struct ether *edev, void *buf, long n)
 {
        panic("Not implemented");
 }
@@ -3966,11 +3962,11 @@ static int mlx4_pnp(struct ether *edev)
        edev->tbdf = MKBUS(BusPCI, pdev->bus, pdev->dev, pdev->func);
        edev->mbps = 10000; // FIXME
 
-       edev->attach = ether_attach;
-       edev->transmit = ether_transmit;
-       edev->ifstat = ether_ifstat;
-       edev->ctl = ether_ctl;
-       edev->shutdown = ether_shutdown;
+       edev->attach = mlx4_attach;
+       edev->transmit = mlx4_transmit;
+       edev->ifstat = mlx4_ifstat;
+       edev->ctl = mlx4_ctl;
+       edev->shutdown = mlx4_shutdown;
 
        edev->arg = edev;
        edev->promiscuous = NULL;
index e53b70e..bc5d87f 100644 (file)
@@ -257,6 +257,7 @@ struct mlx4_en_tx_ring {
        unsigned long           xmit_more;
        struct mlx4_bf          bf;
        unsigned long           queue_stopped;
+       struct poke_tracker     poker;
 
        /* Following part should be mostly read */
        cpumask_t               affinity_mask;
@@ -282,6 +283,14 @@ struct mlx4_en_tx_ring {
        int                     hwtstamp_tx_type;
 } ____cacheline_aligned_in_smp;
 
+struct mlx4_poke_args {
+       struct ether                    *edev;
+       struct mlx4_en_priv             *priv;
+       struct mlx4_en_tx_ring  *ring;
+};
+
+void __mlx4_xmit_poke(void *args);
+
 struct mlx4_en_rx_desc {
        /* actual number of entries depends on rx ring stride */
        struct mlx4_wqe_data_seg data[0];
@@ -615,6 +624,13 @@ static inline struct mlx4_cqe *mlx4_en_get_cqe(void *buf, int idx, int cqe_sz)
        return buf + idx * cqe_sz;
 }
 
+static inline bool mlx4_en_ring_is_full(struct mlx4_en_tx_ring *ring)
+{
+       /* Check available TXBBs And 2K spare for prefetch */
+       return (int)(ring->prod - ring->cons) >
+              ring->size - HEADROOM - MAX_DESC_TXBBS;
+}
+
 #ifdef CONFIG_NET_RX_BUSY_POLL
 static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
 {