Skip to content

Commit 9456f82

Browse files
tanstafelNipaLocal
authored and
NipaLocal
committed
net: ks8851: Fix TX stall caused by TX buffer overrun
There is a bug in the ks8851 Ethernet driver that more data is written to the hardware TX buffer than actually available. This is caused by wrong accounting of the free TX buffer space. The driver maintains a tx_space variable that represents the TX buffer space that is deemed to be free. The ks8851_start_xmit_spi() function adds an SKB to a queue if tx_space is large enough and reduces tx_space by the amount of buffer space it will later need in the TX buffer and then schedules a work item. If there is not enough space then the TX queue is stopped. The worker function ks8851_tx_work() dequeues all the SKBs and writes the data into the hardware TX buffer. The last packet will trigger an interrupt after it was send. Here it is assumed that all data fits into the TX buffer. In the interrupt routine (which runs asynchronously because it is a threaded interrupt) tx_space is updated with the current value from the hardware. Also the TX queue is woken up again. Now it could happen that after data was sent to the hardware and before handling the TX interrupt new data is queued in ks8851_start_xmit_spi() when the TX buffer space had still some space left. When the interrupt is actually handled tx_space is updated from the hardware but now we already have new SKBs queued that have not been written to the hardware TX buffer yet. Since tx_space has been overwritten by the value from the hardware the space is not accounted for. Now we have more data queued then buffer space available in the hardware and ks8851_tx_work() will potentially overrun the hardware TX buffer. In many cases it will still work because often the buffer is written out fast enough so that no overrun occurs but for example if the peer throttles us via flow control then an overrun may happen. This can be fixed in different ways. The most simple way would be to set tx_space to 0 before writing data to the hardware TX buffer preventing the queuing of more SKBs until the TX interrupt has been handled. I have choosen a slightly more efficient (and still rather simple) way and track the amount of data that is already queued and not yet written to the hardware. When new SKBs are to be queued the already queued amount of data is honoured when checking free TX buffer space. I tested this with a setup of two linked KS8851 running iperf3 between the two in bidirectional mode. Before the fix I got a stall after some minutes. With the fix I saw now issues anymore after hours. Cc: "David S. Miller" <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Jakub Kicinski <[email protected]> Cc: Paolo Abeni <[email protected]> Cc: [email protected] Cc: [email protected] # 5.10+ Signed-off-by: Ronald Wahl <[email protected]> Signed-off-by: NipaLocal <nipa@local>
1 parent 8aa3681 commit 9456f82

File tree

3 files changed

+37
-26
lines changed

3 files changed

+37
-26
lines changed

drivers/net/ethernet/micrel/ks8851.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ struct ks8851_net {
399399
struct work_struct rxctrl_work;
400400

401401
struct sk_buff_head txq;
402+
unsigned int queued_len;
402403

403404
struct eeprom_93cx6 eeprom;
404405
struct regulator *vdd_reg;

drivers/net/ethernet/micrel/ks8851_common.c

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -362,16 +362,17 @@ static irqreturn_t ks8851_irq(int irq, void *_ks)
362362
handled |= IRQ_RXPSI;
363363

364364
if (status & IRQ_TXI) {
365-
handled |= IRQ_TXI;
366-
367-
/* no lock here, tx queue should have been stopped */
365+
unsigned short tx_space = ks8851_rdreg16(ks, KS_TXMIR);
366+
netif_dbg(ks, intr, ks->netdev,
367+
"%s: txspace %d\n", __func__, tx_space);
368368

369-
/* update our idea of how much tx space is available to the
370-
* system */
371-
ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR);
369+
spin_lock(&ks->statelock);
370+
ks->tx_space = tx_space;
371+
if (netif_queue_stopped(ks->netdev))
372+
netif_wake_queue(ks->netdev);
373+
spin_unlock(&ks->statelock);
372374

373-
netif_dbg(ks, intr, ks->netdev,
374-
"%s: txspace %d\n", __func__, ks->tx_space);
375+
handled |= IRQ_TXI;
375376
}
376377

377378
if (status & IRQ_RXI)
@@ -414,9 +415,6 @@ static irqreturn_t ks8851_irq(int irq, void *_ks)
414415
if (status & IRQ_LCI)
415416
mii_check_link(&ks->mii);
416417

417-
if (status & IRQ_TXI)
418-
netif_wake_queue(ks->netdev);
419-
420418
return IRQ_HANDLED;
421419
}
422420

@@ -500,6 +498,7 @@ static int ks8851_net_open(struct net_device *dev)
500498
ks8851_wrreg16(ks, KS_ISR, ks->rc_ier);
501499
ks8851_wrreg16(ks, KS_IER, ks->rc_ier);
502500

501+
ks->queued_len = 0;
503502
netif_start_queue(ks->netdev);
504503

505504
netif_dbg(ks, ifup, ks->netdev, "network device up\n");

drivers/net/ethernet/micrel/ks8851_spi.c

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,18 @@ static void ks8851_wrfifo_spi(struct ks8851_net *ks, struct sk_buff *txp,
286286
netdev_err(ks->netdev, "%s: spi_sync() failed\n", __func__);
287287
}
288288

289+
/**
290+
* calc_txlen - calculate size of message to send packet
291+
* @len: Length of data
292+
*
293+
* Returns the size of the TXFIFO message needed to send
294+
* this packet.
295+
*/
296+
static unsigned int calc_txlen(unsigned int len)
297+
{
298+
return ALIGN(len + 4, 4);
299+
}
300+
289301
/**
290302
* ks8851_rx_skb_spi - receive skbuff
291303
* @ks: The device state
@@ -310,6 +322,8 @@ static void ks8851_tx_work(struct work_struct *work)
310322
unsigned long flags;
311323
struct sk_buff *txb;
312324
bool last;
325+
unsigned short tx_space;
326+
unsigned int dequeued_len = 0;
313327

314328
kss = container_of(work, struct ks8851_net_spi, tx_work);
315329
ks = &kss->ks8851;
@@ -320,6 +334,7 @@ static void ks8851_tx_work(struct work_struct *work)
320334
while (!last) {
321335
txb = skb_dequeue(&ks->txq);
322336
last = skb_queue_empty(&ks->txq);
337+
dequeued_len += calc_txlen(txb->len);
323338

324339
if (txb) {
325340
ks8851_wrreg16_spi(ks, KS_RXQCR,
@@ -332,6 +347,13 @@ static void ks8851_tx_work(struct work_struct *work)
332347
}
333348
}
334349

350+
tx_space = ks8851_rdreg16_spi(ks, KS_TXMIR);
351+
352+
spin_lock(&ks->statelock);
353+
ks->queued_len -= dequeued_len;
354+
ks->tx_space = tx_space;
355+
spin_unlock(&ks->statelock);
356+
335357
ks8851_unlock_spi(ks, &flags);
336358
}
337359

@@ -346,18 +368,6 @@ static void ks8851_flush_tx_work_spi(struct ks8851_net *ks)
346368
flush_work(&kss->tx_work);
347369
}
348370

349-
/**
350-
* calc_txlen - calculate size of message to send packet
351-
* @len: Length of data
352-
*
353-
* Returns the size of the TXFIFO message needed to send
354-
* this packet.
355-
*/
356-
static unsigned int calc_txlen(unsigned int len)
357-
{
358-
return ALIGN(len + 4, 4);
359-
}
360-
361371
/**
362372
* ks8851_start_xmit_spi - transmit packet using SPI
363373
* @skb: The buffer to transmit
@@ -386,16 +396,17 @@ static netdev_tx_t ks8851_start_xmit_spi(struct sk_buff *skb,
386396

387397
spin_lock(&ks->statelock);
388398

389-
if (needed > ks->tx_space) {
399+
if (ks->queued_len + needed > ks->tx_space) {
390400
netif_stop_queue(dev);
391401
ret = NETDEV_TX_BUSY;
392402
} else {
393-
ks->tx_space -= needed;
403+
ks->queued_len += needed;
394404
skb_queue_tail(&ks->txq, skb);
395405
}
396406

397407
spin_unlock(&ks->statelock);
398-
schedule_work(&kss->tx_work);
408+
if (ret == NETDEV_TX_OK)
409+
schedule_work(&kss->tx_work);
399410

400411
return ret;
401412
}

0 commit comments

Comments
 (0)