mirror of
https://github.com/torvalds/linux.git
synced 2026-08-02 05:12:01 +02:00
Merge branch 'amd-xgbe-tx-resilience-improvements-for-link-down-handling'
Raju Rangoju says: ==================== amd-xgbe: TX resilience improvements for link-down handling This series enhances the AMD 10GbE driver's TX queue handling during link-down events to improve resilience, prevent resource leaks, and enable fast failover in link aggregation configurations. The three patches form a complete link-down handling solution: 1. Patch 1: Fast detection (know quickly when link goes down) 2. Patch 2: Quick response (stop TX immediately, skip waits) 3. Patch 3: Clean recovery (reclaim abandoned resources) ==================== Link: https://patch.msgid.link/20260319163251.1808611-1-Raju.Rangoju@amd.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
9d9f21d314
|
|
@ -330,6 +330,10 @@
|
|||
#define MAC_ISR_SMI_WIDTH 1
|
||||
#define MAC_ISR_TSIS_INDEX 12
|
||||
#define MAC_ISR_TSIS_WIDTH 1
|
||||
#define MAC_ISR_LS_INDEX 24
|
||||
#define MAC_ISR_LS_WIDTH 2
|
||||
#define MAC_ISR_LSI_INDEX 0
|
||||
#define MAC_ISR_LSI_WIDTH 1
|
||||
#define MAC_MACA1HR_AE_INDEX 31
|
||||
#define MAC_MACA1HR_AE_WIDTH 1
|
||||
#define MAC_MDIOIER_SNGLCOMPIE_INDEX 12
|
||||
|
|
|
|||
|
|
@ -3186,7 +3186,16 @@ static void xgbe_txq_prepare_tx_stop(struct xgbe_prv_data *pdata,
|
|||
/* The Tx engine cannot be stopped if it is actively processing
|
||||
* packets. Wait for the Tx queue to empty the Tx fifo. Don't
|
||||
* wait forever though...
|
||||
*
|
||||
* Optimization: Skip the wait when link is down. Hardware won't
|
||||
* complete TX processing, so waiting serves no purpose and only
|
||||
* delays interface shutdown. Descriptors will be reclaimed via
|
||||
* the force-cleanup path in tx_poll.
|
||||
*/
|
||||
|
||||
if (!pdata->phy.link)
|
||||
return;
|
||||
|
||||
tx_timeout = jiffies + (XGBE_DMA_STOP_TIMEOUT * HZ);
|
||||
while (time_before(jiffies, tx_timeout)) {
|
||||
tx_status = XGMAC_MTL_IOREAD(pdata, queue, MTL_Q_TQDR);
|
||||
|
|
@ -3267,28 +3276,83 @@ static void xgbe_enable_tx(struct xgbe_prv_data *pdata)
|
|||
XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* xgbe_wait_for_dma_tx_complete - Wait for DMA to complete pending TX
|
||||
* @pdata: driver private data
|
||||
*
|
||||
* Wait for the DMA TX channels to complete all pending descriptors.
|
||||
* This ensures no frames are in-flight before we disable the transmitter.
|
||||
* If link is down, return immediately as TX will never complete.
|
||||
*
|
||||
* Return: 0 on success, -ETIMEDOUT on timeout
|
||||
*/
|
||||
static int xgbe_wait_for_dma_tx_complete(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_channel *channel;
|
||||
struct xgbe_ring *ring;
|
||||
unsigned long timeout;
|
||||
unsigned int i;
|
||||
bool complete;
|
||||
|
||||
/* If link is down, TX will never complete - skip waiting */
|
||||
if (!pdata->phy.link)
|
||||
return 0;
|
||||
|
||||
timeout = jiffies + (XGBE_DMA_STOP_TIMEOUT * HZ);
|
||||
|
||||
do {
|
||||
complete = true;
|
||||
|
||||
for (i = 0; i < pdata->channel_count; i++) {
|
||||
channel = pdata->channel[i];
|
||||
ring = channel->tx_ring;
|
||||
if (!ring)
|
||||
continue;
|
||||
|
||||
/* Check if DMA has processed all descriptors */
|
||||
if (ring->dirty != ring->cur) {
|
||||
complete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (complete)
|
||||
return 0;
|
||||
|
||||
usleep_range(100, 200);
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
netif_warn(pdata, drv, pdata->netdev,
|
||||
"timeout waiting for DMA TX to complete\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void xgbe_disable_tx(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Prepare for Tx DMA channel stop */
|
||||
for (i = 0; i < pdata->tx_q_count; i++)
|
||||
xgbe_prepare_tx_stop(pdata, i);
|
||||
/* Step 1: Wait for DMA to complete pending descriptors */
|
||||
xgbe_wait_for_dma_tx_complete(pdata);
|
||||
|
||||
/* Disable MAC Tx */
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0);
|
||||
|
||||
/* Disable each Tx queue */
|
||||
for (i = 0; i < pdata->tx_q_count; i++)
|
||||
XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN, 0);
|
||||
|
||||
/* Disable each Tx DMA channel */
|
||||
/* Step 2: Disable each Tx DMA channel to stop
|
||||
* processing new descriptors
|
||||
*/
|
||||
for (i = 0; i < pdata->channel_count; i++) {
|
||||
if (!pdata->channel[i]->tx_ring)
|
||||
break;
|
||||
|
||||
XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_TCR, ST, 0);
|
||||
}
|
||||
|
||||
/* Step 3: Wait for MTL TX queues to drain */
|
||||
for (i = 0; i < pdata->tx_q_count; i++)
|
||||
xgbe_prepare_tx_stop(pdata, i);
|
||||
|
||||
/* Step 4: Disable MTL TX queues */
|
||||
for (i = 0; i < pdata->tx_q_count; i++)
|
||||
XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_TQOMR, TXQEN, 0);
|
||||
|
||||
/* Step 5: Disable MAC TX last */
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_TCR, TE, 0);
|
||||
}
|
||||
|
||||
static void xgbe_prepare_rx_stop(struct xgbe_prv_data *pdata,
|
||||
|
|
|
|||
|
|
@ -607,11 +607,33 @@ static void xgbe_service_timer(struct timer_list *t)
|
|||
struct xgbe_prv_data *pdata = timer_container_of(pdata, t,
|
||||
service_timer);
|
||||
struct xgbe_channel *channel;
|
||||
unsigned int poll_interval;
|
||||
unsigned int i;
|
||||
|
||||
queue_work(pdata->dev_workqueue, &pdata->service_work);
|
||||
|
||||
mod_timer(&pdata->service_timer, jiffies + HZ);
|
||||
/* Adaptive link status polling for fast failure detection:
|
||||
*
|
||||
* - When carrier is UP: poll every 100ms for rapid link-down detection
|
||||
* Enables sub-second response to link failures, minimizing traffic
|
||||
* loss.
|
||||
*
|
||||
* - When carrier is DOWN: poll every 1s to conserve CPU resources
|
||||
* Link-up events are less time-critical.
|
||||
*
|
||||
* The 100ms active polling interval balances responsiveness with
|
||||
* efficiency:
|
||||
* - Provides ~100-200ms link-down detection (10x faster than 1s
|
||||
* polling)
|
||||
* - Minimal CPU overhead (1% vs 0.1% with 1s polling)
|
||||
* - Enables fast failover in link aggregation deployments
|
||||
*/
|
||||
if (netif_running(pdata->netdev) && netif_carrier_ok(pdata->netdev))
|
||||
poll_interval = msecs_to_jiffies(100); /* 100ms when up */
|
||||
else
|
||||
poll_interval = HZ; /* 1 second when down */
|
||||
|
||||
mod_timer(&pdata->service_timer, jiffies + poll_interval);
|
||||
|
||||
if (!pdata->tx_usecs)
|
||||
return;
|
||||
|
|
@ -2147,6 +2169,7 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
|
|||
struct net_device *netdev = pdata->netdev;
|
||||
struct netdev_queue *txq;
|
||||
int processed = 0;
|
||||
int force_cleanup;
|
||||
unsigned int tx_packets = 0, tx_bytes = 0;
|
||||
unsigned int cur;
|
||||
|
||||
|
|
@ -2163,13 +2186,41 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
|
|||
|
||||
txq = netdev_get_tx_queue(netdev, channel->queue_index);
|
||||
|
||||
/* Smart descriptor cleanup during link-down conditions.
|
||||
*
|
||||
* When link is down, hardware stops processing TX descriptors (OWN bit
|
||||
* remains set). Enable intelligent cleanup to reclaim these abandoned
|
||||
* descriptors and maintain TX queue health.
|
||||
*
|
||||
* This cleanup mechanism enables:
|
||||
* - Continuous TX queue availability for new packets when link recovers
|
||||
* - Clean resource management (skbs, DMA mappings, descriptors)
|
||||
* - Fast failover in link aggregation scenarios
|
||||
*/
|
||||
force_cleanup = !pdata->phy.link;
|
||||
|
||||
while ((processed < XGBE_TX_DESC_MAX_PROC) &&
|
||||
(ring->dirty != cur)) {
|
||||
rdata = XGBE_GET_DESC_DATA(ring, ring->dirty);
|
||||
rdesc = rdata->rdesc;
|
||||
|
||||
if (!hw_if->tx_complete(rdesc))
|
||||
break;
|
||||
if (!hw_if->tx_complete(rdesc)) {
|
||||
if (!force_cleanup)
|
||||
break;
|
||||
/* Link-down descriptor cleanup: reclaim abandoned
|
||||
* resources.
|
||||
*
|
||||
* Hardware has stopped processing this descriptor, so
|
||||
* perform intelligent cleanup to free skbs and reclaim
|
||||
* descriptors for future use when link recovers.
|
||||
*
|
||||
* These are not counted as successful transmissions
|
||||
* since packets never reached the wire.
|
||||
*/
|
||||
netif_dbg(pdata, tx_err, netdev,
|
||||
"force-freeing stuck TX desc %u (link down)\n",
|
||||
ring->dirty);
|
||||
}
|
||||
|
||||
/* Make sure descriptor fields are read after reading the OWN
|
||||
* bit */
|
||||
|
|
@ -2178,9 +2229,13 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
|
|||
if (netif_msg_tx_done(pdata))
|
||||
xgbe_dump_tx_desc(pdata, ring, ring->dirty, 1, 0);
|
||||
|
||||
if (hw_if->is_last_desc(rdesc)) {
|
||||
tx_packets += rdata->tx.packets;
|
||||
tx_bytes += rdata->tx.bytes;
|
||||
/* Only count packets actually transmitted (not force-cleaned)
|
||||
*/
|
||||
if (!force_cleanup || hw_if->is_last_desc(rdesc)) {
|
||||
if (hw_if->is_last_desc(rdesc)) {
|
||||
tx_packets += rdata->tx.packets;
|
||||
tx_bytes += rdata->tx.bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the SKB and reset the descriptor for re-use */
|
||||
|
|
|
|||
|
|
@ -1047,11 +1047,29 @@ static void xgbe_phy_adjust_link(struct xgbe_prv_data *pdata)
|
|||
if (pdata->phy_link != pdata->phy.link) {
|
||||
new_state = 1;
|
||||
pdata->phy_link = pdata->phy.link;
|
||||
|
||||
/* Link is coming up - wake TX queues */
|
||||
netif_tx_wake_all_queues(pdata->netdev);
|
||||
}
|
||||
} else if (pdata->phy_link) {
|
||||
new_state = 1;
|
||||
pdata->phy_link = 0;
|
||||
pdata->phy_speed = SPEED_UNKNOWN;
|
||||
|
||||
/* Proactive TX queue management on link-down.
|
||||
*
|
||||
* Immediately stop TX queues to enable clean link-down
|
||||
* handling:
|
||||
* - Prevents queueing packets that can't be transmitted
|
||||
* - Allows orderly descriptor cleanup by NAPI poll
|
||||
* - Enables rapid failover in link aggregation configurations
|
||||
*
|
||||
* Note: We do NOT call netdev_tx_reset_queue() here because
|
||||
* NAPI poll may still be running and would trigger BQL
|
||||
* assertion. BQL state is cleaned up naturally during
|
||||
* descriptor reclamation.
|
||||
*/
|
||||
netif_tx_stop_all_queues(pdata->netdev);
|
||||
}
|
||||
|
||||
if (new_state && netif_msg_link(pdata))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user