Same story as the RX refactor: pull TX LTB alloc/free out of open/close
into helpers and wire them in this patch.

  ibmveth_alloc_tx_resources()
  ibmveth_free_tx_resources()

They wrap the existing per-queue allocate_tx_ltb() / free_tx_ltb()
primitives. alloc_tx_resources() allocates every TX queue and unwinds
partial failure itself; free_tx_resources() walks real_num_tx_queues.
The helpers remove dependence on shared open/close loop indices and
match the RX helper structure. TX was already multi-queue capable via
ethtool -L.

Also tighten TX LTB lifetime: free_tx_ltb() keys off tx_ltb_ptr[]
presence (not a dma==0 sentinel) and clears tx_ltb_dma[] after unmap;
allocate_tx_ltb() clears tx_ltb_dma[] after DMA-map failure.

Move TX LTB allocation to the end of open(), after LAN registration,
RX pools, RX interrupt setup, and the initial replenish kick. A late
alloc_tx_resources() failure jumps to out_cleanup_rx_interrupts and
must not call free_tx_resources() again: alloc already freed any
partial TX LTBs. start_xmit() bails if tx_ltb_ptr[] is gone so RX can
be live while TX LTB alloc still runs (and so close/failed-reopen with
IFF_UP set cannot UAF).

After LAN registration, open-fail teardown frees the logical LAN before
tearing down RX pool DMA (intentional safer order than leaving the LAN
registered while unmapping RX memory).

close() quiesces TX with netif_tx_disable() (stop_all_queues does not
wait for in-flight ndo_start_xmit), then frees LTBs after
h_free_logical_lan() via free_tx_resources() - required because direct
close() callers bypass synchronize_net().

Signed-off-by: Mingming Cao <[email protected]>
Reviewed-by: Dave Marquardt <[email protected]>
Tested-by: Shaik Abdulla <[email protected]>
---

Changes in v5:
- Quiesce TX with netif_tx_disable before free (stop_all_queues does not
  wait for in-flight xmit); free LTBs after h_free_logical_lan - direct
  close() callers bypass synchronize_net()
- Guard start_xmit if tx_ltb_ptr gone so open can leave RX live while TX
  LTB alloc still runs (also covers close/failed-reopen with IFF_UP set)
- Drop fake mid-open TX-leak / Fixes: motivation; reword as helper
  extraction matching RX (shared loop-index independence)
- Free TX LTB by pointer presence (drop dma==0 sentinel; dma_mapping_error
  already cleared the slot on map failure)
- Document intentional open-fail LAN-first unwind (free_lan before RX
  pool/DMA teardown) rather than leaving it silent in a TX-only refactor
- Drop drive-by blank-line cosmetics (header / start_xmit)

Changes in v4:
- Introduce the TX resource helpers in the same patch that wires their
  first open/close callers.
- Do not free TX LTBs again after a failed alloc_tx_resources();
  harden free_tx_ltb() against unset slots.
- Move TX allocation after RX IRQ setup / replenish kick so open()
  failure unwind no longer depends on a shared loop index (also fixes
  a mid-open TX LTB leak).

 drivers/net/ethernet/ibm/ibmveth.c | 98 +++++++++++++++++++++++-------
 1 file changed, 75 insertions(+), 23 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c 
b/drivers/net/ethernet/ibm/ibmveth.c
index 99eeb6ef51bf..b39e8c53cbfd 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -1183,8 +1183,12 @@ static int ibmveth_rxq_harvest_buffer(struct 
ibmveth_adapter *adapter,
 
 static void ibmveth_free_tx_ltb(struct ibmveth_adapter *adapter, int idx)
 {
+       if (!adapter->tx_ltb_ptr[idx])
+               return;
+
        dma_unmap_single(&adapter->vdev->dev, adapter->tx_ltb_dma[idx],
                         adapter->tx_ltb_size, DMA_TO_DEVICE);
+       adapter->tx_ltb_dma[idx] = 0;
        kfree(adapter->tx_ltb_ptr[idx]);
        adapter->tx_ltb_ptr[idx] = NULL;
 }
@@ -1207,12 +1211,54 @@ static int ibmveth_allocate_tx_ltb(struct 
ibmveth_adapter *adapter, int idx)
                           "unable to DMA map tx long term buffer\n");
                kfree(adapter->tx_ltb_ptr[idx]);
                adapter->tx_ltb_ptr[idx] = NULL;
+               adapter->tx_ltb_dma[idx] = 0;
                return -ENOMEM;
        }
 
        return 0;
 }
 
+/**
+ * ibmveth_alloc_tx_resources - Allocate TX resources for all queues
+ * @adapter: ibmveth adapter structure
+ *
+ * Allocates TX Long Term Buffers (LTBs) for all TX queues.
+ *
+ * Return: 0 on success, -ENOMEM on failure
+ */
+static int ibmveth_alloc_tx_resources(struct ibmveth_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       int i;
+
+       for (i = 0; i < netdev->real_num_tx_queues; i++) {
+               if (ibmveth_allocate_tx_ltb(adapter, i))
+                       goto err_free_ltbs;
+       }
+
+       return 0;
+
+err_free_ltbs:
+       while (--i >= 0)
+               ibmveth_free_tx_ltb(adapter, i);
+       return -ENOMEM;
+}
+
+/**
+ * ibmveth_free_tx_resources - Free TX resources for all queues
+ * @adapter: ibmveth adapter structure
+ *
+ * Frees TX Long Term Buffers (LTBs) for all TX queues.
+ */
+static void ibmveth_free_tx_resources(struct ibmveth_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       int i;
+
+       for (i = 0; i < netdev->real_num_tx_queues; i++)
+               ibmveth_free_tx_ltb(adapter, i);
+}
+
 static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
         union ibmveth_buf_desc rxq_desc, u64 mac_address)
 {
@@ -1263,12 +1309,6 @@ static int ibmveth_open(struct net_device *netdev)
        if (rc)
                goto out_free_filter_list;
 
-       rc = -ENOMEM;
-       for (i = 0; i < netdev->real_num_tx_queues; i++) {
-               if (ibmveth_allocate_tx_ltb(adapter, i))
-                       goto out_free_tx_ltb;
-       }
-
        mac_address = ether_addr_to_u64(netdev->dev_addr);
 
        rxq_desc.fields.flags_len = IBMVETH_BUF_VALID |
@@ -1290,24 +1330,24 @@ static int ibmveth_open(struct net_device *netdev)
                                     rxq_desc.desc,
                                     mac_address);
                rc = -ENONET;
-               goto out_free_tx_ltb;
+               goto out_free_queue_mem;
        }
 
        rc = ibmveth_alloc_buffer_pools(adapter);
        if (rc)
-               goto out_free_tx_ltb;
+               goto out_unregister_lan;
 
        rc = ibmveth_setup_rx_interrupts(adapter);
-       if (rc) {
-               do {
-                       lpar_rc = 
h_free_logical_lan(adapter->vdev->unit_address);
-               } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
-               goto out_free_buffer_pools;
-       }
+       if (rc)
+               goto out_unregister_lan;
 
        netdev_dbg(netdev, "initial replenish cycle\n");
        ibmveth_schedule_rx_queue(adapter, 0);
 
+       rc = ibmveth_alloc_tx_resources(adapter);
+       if (rc)
+               goto out_cleanup_rx_interrupts;
+
        netif_tx_start_all_queues(netdev);
 
        adapter->opened = true;
@@ -1315,11 +1355,14 @@ static int ibmveth_open(struct net_device *netdev)
 
        return 0;
 
-out_free_buffer_pools:
+out_cleanup_rx_interrupts:
+       ibmveth_cleanup_rx_interrupts(adapter);
+out_unregister_lan:
+       do {
+               lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
+       } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
        ibmveth_free_buffer_pools(adapter);
-out_free_tx_ltb:
-       while (--i >= 0)
-               ibmveth_free_tx_ltb(adapter, i);
+out_free_queue_mem:
        ibmveth_cleanup_rx_resources(adapter);
 out_free_filter_list:
        ibmveth_free_filter_list(adapter);
@@ -1331,7 +1374,6 @@ static int ibmveth_close(struct net_device *netdev)
 {
        struct ibmveth_adapter *adapter = netdev_priv(netdev);
        long lpar_rc;
-       int i;
 
        /* Gate on opened, not IFF_UP: pool_store/change_mtu close+open can
         * leave IFF_UP set after a failed reopen.
@@ -1343,7 +1385,10 @@ static int ibmveth_close(struct net_device *netdev)
 
        netdev_dbg(netdev, "close starting\n");
 
-       netif_tx_stop_all_queues(netdev);
+       /* Disable and wait for in-flight ndo_start_xmit (stop_all_queues
+        * alone does not). Direct close() callers bypass synchronize_net().
+        */
+       netif_tx_disable(netdev);
 
        ibmveth_cleanup_rx_interrupts(adapter);
        /* Wait for softirq/poll that already passed shutdown checks. */
@@ -1359,13 +1404,14 @@ static int ibmveth_close(struct net_device *netdev)
                           "h_free_logical_lan failed with %lx, continuing\n",
                           lpar_rc);
        }
+       /* Free TX LTBs after quiesce and after H_FREE_LOGICAL_LAN so xmit
+        * cannot touch unmapped bounce buffers while the LAN is live.
+        */
+       ibmveth_free_tx_resources(adapter);
        ibmveth_free_buffer_pools(adapter);
        ibmveth_cleanup_rx_resources(adapter);
        ibmveth_free_filter_list(adapter);
 
-       for (i = 0; i < netdev->real_num_tx_queues; i++)
-               ibmveth_free_tx_ltb(adapter, i);
-
        netdev_dbg(netdev, "close complete\n");
 
        return 0;
@@ -1789,6 +1835,12 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff 
*skb,
        int i, queue_num = skb_get_queue_mapping(skb);
        unsigned long mss = 0;
 
+       /* Close / failed reopen can free LTBs while IFF_UP is still set. */
+       if (unlikely(!adapter->tx_ltb_ptr[queue_num])) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+
        if (ibmveth_is_packet_unsupported(skb, netdev))
                goto out;
        /* veth can't checksum offload UDP */
-- 
2.50.1 (Apple Git-155)


Reply via email to