ibmveth_change_mtu(), veth_pool_store(), ibmveth_set_csum_offload()
and ibmveth_set_tso() call close() and open() directly. If open() fails,
NAPI is left disabled while IFF_UP stays set, so the next close()
(ifdown, unregister or another reconfiguration) calls napi_disable()
again and waits forever with RTNL held. Networking and shutdown
hang; only a reboot recovers. ethtool -L in that state also wakes
queues that have no TX buffer and dereferences NULL in
ibmveth_start_xmit().

Any open() failure on those paths triggers it, for example an
allocation failure on an MTU change to jumbo frames.

Track a successful open in adapter->opened. close() returns early
when it is clear, and set_channels() checks it instead of IFF_UP.

The open() error-path leaks are fixed separately in net by
commit af0524bf4ce1 ("ibmveth: h_free logical LAN on open-fail after
register") and commit 84bec0bf0352 ("ibmveth: fix TX LTB and filter
unwind on open-fail"); this patch does not depend on them.

Tested on a POWER10 LPAR with ibmveth_open() forced to fail:
'ip link set dev eth1 mtu 9000' fails, then 'ip link set dev eth1 down'
returns at once and 'ip link set dev eth1 up' recovers the interface.

Fixes: 860f242eb534 ("[PATCH] ibmveth change buffer pools dynamically")
Signed-off-by: Mingming Cao <[email protected]>
---
 drivers/net/ethernet/ibm/ibmveth.c | 24 +++++++++++++++++-------
 drivers/net/ethernet/ibm/ibmveth.h |  2 ++
 2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c 
b/drivers/net/ethernet/ibm/ibmveth.c
index aa2300e97081..00cee2916920 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -738,6 +738,7 @@ static int ibmveth_open(struct net_device *netdev)
 
        netif_tx_start_all_queues(netdev);
 
+       adapter->opened = true;
        netdev_dbg(netdev, "open complete\n");
 
        return 0;
@@ -779,6 +780,14 @@ static int ibmveth_close(struct net_device *netdev)
        long lpar_rc;
        int i;
 
+       /* change_mtu, pool sysfs, set_csum and set_tso call close() and
+        * open() directly. If that open() fails, IFF_UP stays set and
+        * NAPI is disabled; a second close() would hang in napi_disable().
+        */
+       if (!adapter->opened)
+               return 0;
+       adapter->opened = false;
+
        netdev_dbg(netdev, "close starting\n");
 
        napi_disable(&adapter->napi);
@@ -830,10 +839,10 @@ static int ibmveth_close(struct net_device *netdev)
  *
  * @w: pointer to work_struct embedded in adapter structure
  *
- * Context: This routine acquires rtnl_mutex and disables its NAPI through
- *          ibmveth_close. It can't be called directly in a context that has
- *          already acquired rtnl_mutex or disabled its NAPI, or directly from
- *          a poll routine.
+ * Context: This routine acquires rtnl_mutex and, if the device is open,
+ *          disables its NAPI through ibmveth_close. It can't be called
+ *          directly in a context that has already acquired rtnl_mutex or
+ *          disabled its NAPI, or directly from a poll routine.
  *
  * Return: void
  */
@@ -1127,10 +1136,11 @@ static int ibmveth_set_channels(struct net_device 
*netdev,
                     goal = channels->tx_count;
        int rc, i;
 
-       /* If ndo_open has not been called yet then don't allocate, just set
-        * desired netdev_queue's and return
+       /* If the device is not open (including a failed close/open with
+        * IFF_UP still set) then don't allocate, just set desired
+        * netdev_queue's and return
         */
-       if (!(netdev->flags & IFF_UP))
+       if (!adapter->opened)
                return netif_set_real_num_tx_queues(netdev, goal);
 
        /* We have IBMVETH_MAX_QUEUES netdev_queue's allocated
diff --git a/drivers/net/ethernet/ibm/ibmveth.h 
b/drivers/net/ethernet/ibm/ibmveth.h
index d87713668ed3..3f2240823f6a 100644
--- a/drivers/net/ethernet/ibm/ibmveth.h
+++ b/drivers/net/ethernet/ibm/ibmveth.h
@@ -172,6 +172,8 @@ struct ibmveth_adapter {
        int rx_csum;
        int large_send;
        bool is_active_trunk;
+       /* Set by a successful ibmveth_open(), cleared by ibmveth_close(). */
+       bool opened;
        unsigned int rx_buffers_per_hcall;
 
        u64 fw_ipv6_csum_support;
-- 
2.39.3 (Apple Git-146)


Reply via email to