Commit ab4ac5a9 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-mana-fix-null-dereferences-during-teardown-after-attach-failure'

Dipayaan Roy says:

====================
net: mana: Fix NULL dereferences during teardown after attach failure

When mana_attach() fails (e.g. during queue allocation), the error
cleanup frees apc->tx_qp and apc->rxqs and sets them to NULL. Multiple
subsequent teardown paths can then dereference these NULL pointers,
causing kernel panics.

Patch 1 adds NULL guards in the low-level teardown functions
(mana_fence_rqs, mana_destroy_vport, mana_dealloc_queues) so they are
safe to call regardless of queue initialization state. This covers all
callers: mana_remove(), mana_change_mtu() recovery, and internal error
paths in mana_alloc_queues().

Patch 2 adds an early exit in mana_detach() for already-detached ports,
making it safe for non-close callers. This allows the queue reset
handler to safely retry mana_attach() without redundant teardown.
====================

Link: https://patch.msgid.link/20260525081129.1230035-1-dipayanroy@linux.microsoft.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 3e200099 5b05aa36
Loading
Loading
Loading
Loading
+47 −29
Original line number Diff line number Diff line
@@ -1727,6 +1727,9 @@ static void mana_fence_rqs(struct mana_port_context *apc)
	struct mana_rxq *rxq;
	int err;

	if (!apc->rxqs)
		return;

	for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
		rxq = apc->rxqs[rxq_idx];
		err = mana_fence_rq(apc, rxq);
@@ -2858,6 +2861,8 @@ static void mana_destroy_vport(struct mana_port_context *apc)
	struct mana_rxq *rxq;
	u32 rxq_idx;

	if (apc->rxqs) {

		for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
			rxq = apc->rxqs[rxq_idx];
			if (!rxq)
@@ -2866,6 +2871,7 @@ static void mana_destroy_vport(struct mana_port_context *apc)
			mana_destroy_rxq(apc, rxq, true);
			apc->rxqs[rxq_idx] = NULL;
		}
	}

	mana_destroy_txq(apc);
	mana_uncfg_vport(apc);
@@ -3269,6 +3275,7 @@ static int mana_dealloc_queues(struct net_device *ndev)
	if (apc->port_is_up)
		return -EINVAL;

	if (apc->rxqs)
		mana_chn_setxdp(apc, NULL);

	if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
@@ -3287,6 +3294,7 @@ static int mana_dealloc_queues(struct net_device *ndev)
	 * number of queues.
	 */

	if (apc->tx_qp) {
		for (i = 0; i < apc->num_queues; i++) {
			txq = &apc->tx_qp[i].txq;
			tsleep = 1000;
@@ -3296,10 +3304,12 @@ static int mana_dealloc_queues(struct net_device *ndev)
				tsleep <<= 1;
			}
			if (atomic_read(&txq->pending_sends)) {
			err = pcie_flr(to_pci_dev(gd->gdma_context->dev));
				err =
				    pcie_flr(to_pci_dev(gd->gdma_context->dev));
				if (err) {
					netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
					   err, atomic_read(&txq->pending_sends),
						   err,
					    atomic_read(&txq->pending_sends),
					    txq->gdma_txq_id);
				}
				break;
@@ -3314,6 +3324,8 @@ static int mana_dealloc_queues(struct net_device *ndev)
			}
			atomic_set(&txq->pending_sends, 0);
		}
	}

	/* We're 100% sure the queues can no longer be woken up, because
	 * we're sure now mana_poll_tx_cq() can't be running.
	 */
@@ -3338,6 +3350,12 @@ int mana_detach(struct net_device *ndev, bool from_close)

	ASSERT_RTNL();

	/* If already detached (indicates detach succeeded but attach failed
	 * previously). Now skip mana detach and just retry mana_attach.
	 */
	if (!from_close && !netif_device_present(ndev))
		return 0;

	apc->port_st_save = apc->port_is_up;
	apc->port_is_up = false;