Skip to content

Commit 81b4d19

Browse files
idoschksacilotto
authored andcommitted
mlxsw: pci: Recycle received packet upon allocation failure
BugLink: https://bugs.launchpad.net/bugs/1952785 commit 7596357 upstream. When the driver fails to allocate a new Rx buffer, it passes an empty Rx descriptor (contains zero address and size) to the device and marks it as invalid by setting the skb pointer in the descriptor's metadata to NULL. After processing enough Rx descriptors, the driver will try to process the invalid descriptor, but will return immediately seeing that the skb pointer is NULL. Since the driver no longer passes new Rx descriptors to the device, the Rx queue will eventually become full and the device will start to drop packets. Fix this by recycling the received packet if allocation of the new packet failed. This means that allocation is no longer performed at the end of the Rx routine, but at the start, before tearing down the DMA mapping of the received packet. Remove the comment about the descriptor being zeroed as it is no longer correct. This is OK because we either use the descriptor as-is (when recycling) or overwrite its address and size fields with that of the newly allocated Rx buffer. The issue was discovered when a process ("perf") consumed too much memory and put the system under memory pressure. It can be reproduced by injecting slab allocation failures [1]. After the fix, the Rx queue no longer comes to a halt. [1] # echo 10 > /sys/kernel/debug/failslab/times # echo 1000 > /sys/kernel/debug/failslab/interval # echo 100 > /sys/kernel/debug/failslab/probability FAULT_INJECTION: forcing a failure. name failslab, interval 1000, probability 100, space 0, times 8 [...] Call Trace: <IRQ> dump_stack_lvl+0x34/0x44 should_fail.cold+0x32/0x37 should_failslab+0x5/0x10 kmem_cache_alloc_node+0x23/0x190 __alloc_skb+0x1f9/0x280 __netdev_alloc_skb+0x3a/0x150 mlxsw_pci_rdq_skb_alloc+0x24/0x90 mlxsw_pci_cq_tasklet+0x3dc/0x1200 tasklet_action_common.constprop.0+0x9f/0x100 __do_softirq+0xb5/0x252 irq_exit_rcu+0x7a/0xa0 common_interrupt+0x83/0xa0 </IRQ> asm_common_interrupt+0x1e/0x40 RIP: 0010:cpuidle_enter_state+0xc8/0x340 [...] mlxsw_spectrum2 0000:06:00.0: Failed to alloc skb for RDQ Fixes: eda6500 ("mlxsw: Add PCI bus implementation") Signed-off-by: Ido Schimmel <[email protected]> Reviewed-by: Petr Machata <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Kamal Mostafa <[email protected]> Signed-off-by: Stefan Bader <[email protected]>
1 parent 2ecc397 commit 81b4d19

File tree

1 file changed

+12
-13
lines changed
  • drivers/net/ethernet/mellanox/mlxsw

1 file changed

+12
-13
lines changed

drivers/net/ethernet/mellanox/mlxsw/pci.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,10 @@ static int mlxsw_pci_rdq_skb_alloc(struct mlxsw_pci *mlxsw_pci,
353353
struct sk_buff *skb;
354354
int err;
355355

356-
elem_info->u.rdq.skb = NULL;
357356
skb = netdev_alloc_skb_ip_align(NULL, buf_len);
358357
if (!skb)
359358
return -ENOMEM;
360359

361-
/* Assume that wqe was previously zeroed. */
362-
363360
err = mlxsw_pci_wqe_frag_map(mlxsw_pci, wqe, 0, skb->data,
364361
buf_len, DMA_FROM_DEVICE);
365362
if (err)
@@ -548,21 +545,26 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
548545
struct pci_dev *pdev = mlxsw_pci->pdev;
549546
struct mlxsw_pci_queue_elem_info *elem_info;
550547
struct mlxsw_rx_info rx_info = {};
551-
char *wqe;
548+
char wqe[MLXSW_PCI_WQE_SIZE];
552549
struct sk_buff *skb;
553550
u16 byte_count;
554551
int err;
555552

556553
elem_info = mlxsw_pci_queue_elem_info_consumer_get(q);
557-
skb = elem_info->u.sdq.skb;
558-
if (!skb)
559-
return;
560-
wqe = elem_info->elem;
561-
mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, 0, DMA_FROM_DEVICE);
554+
skb = elem_info->u.rdq.skb;
555+
memcpy(wqe, elem_info->elem, MLXSW_PCI_WQE_SIZE);
562556

563557
if (q->consumer_counter++ != consumer_counter_limit)
564558
dev_dbg_ratelimited(&pdev->dev, "Consumer counter does not match limit in RDQ\n");
565559

560+
err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
561+
if (err) {
562+
dev_err_ratelimited(&pdev->dev, "Failed to alloc skb for RDQ\n");
563+
goto out;
564+
}
565+
566+
mlxsw_pci_wqe_frag_unmap(mlxsw_pci, wqe, 0, DMA_FROM_DEVICE);
567+
566568
if (mlxsw_pci_cqe_lag_get(cqe_v, cqe)) {
567569
rx_info.is_lag = true;
568570
rx_info.u.lag_id = mlxsw_pci_cqe_lag_id_get(cqe_v, cqe);
@@ -594,10 +596,7 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
594596
skb_put(skb, byte_count);
595597
mlxsw_core_skb_receive(mlxsw_pci->core, skb, &rx_info);
596598

597-
memset(wqe, 0, q->elem_size);
598-
err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
599-
if (err)
600-
dev_dbg_ratelimited(&pdev->dev, "Failed to alloc skb for RDQ\n");
599+
out:
601600
/* Everything is set up, ring doorbell to pass elem to HW */
602601
q->producer_counter++;
603602
mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);

0 commit comments

Comments
 (0)