]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net/mlx5e: Use synchronize_rcu to sync with NAPI
authorMaxim Mikityanskiy <maximmi@mellanox.com>
Thu, 11 Jun 2020 11:25:19 +0000 (14:25 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 26 Sep 2020 16:05:28 +0000 (18:05 +0200)
[ Upstream commit 9c25a22dfb00270372224721fed646965420323a ]

As described in the previous commit, napi_synchronize doesn't quite fit
the purpose when we just need to wait until the currently running NAPI
quits. Its implementation waits until NAPI is not running by polling and
waiting for 1ms in between. In cases where we need to deactivate one
queue (e.g., recovery flows) or where we deactivate them one-by-one
(deactivate channel flow), we may get stuck in napi_synchronize forever
if other queues keep NAPI active, causing a soft lockup. Depending on
kernel configuration (CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC), it may result
in a kernel panic.

To fix the issue, use synchronize_rcu to wait for NAPI to quit, and wrap
the whole NAPI in rcu_read_lock.

Fixes: acc6c5953af1 ("net/mlx5e: Split open/close channels to stages")
Signed-off-by: Maxim Mikityanskiy <maximmi@mellanox.com>
Reviewed-by: Tariq Toukan <tariqt@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c

index a33a1f762c70db7d2722984b159e436410fa3e45..40db27bf790bb287aa5a9f63a3e83ee91266a18a 100644 (file)
@@ -31,7 +31,6 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
 {
        struct xdp_buff *xdp = wi->umr.dma_info[page_idx].xsk;
        u32 cqe_bcnt32 = cqe_bcnt;
-       bool consumed;
 
        /* Check packet size. Note LRO doesn't use linear SKB */
        if (unlikely(cqe_bcnt > rq->hw_mtu)) {
@@ -51,10 +50,6 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
        xsk_buff_dma_sync_for_cpu(xdp);
        prefetch(xdp->data);
 
-       rcu_read_lock();
-       consumed = mlx5e_xdp_handle(rq, NULL, &cqe_bcnt32, xdp);
-       rcu_read_unlock();
-
        /* Possible flows:
         * - XDP_REDIRECT to XSKMAP:
         *   The page is owned by the userspace from now.
@@ -70,7 +65,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq,
         * allocated first from the Reuse Ring, so it has enough space.
         */
 
-       if (likely(consumed)) {
+       if (likely(mlx5e_xdp_handle(rq, NULL, &cqe_bcnt32, xdp))) {
                if (likely(__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)))
                        __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */
                return NULL; /* page/packet was consumed by XDP */
@@ -88,7 +83,6 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
                                              u32 cqe_bcnt)
 {
        struct xdp_buff *xdp = wi->di->xsk;
-       bool consumed;
 
        /* wi->offset is not used in this function, because xdp->data and the
         * DMA address point directly to the necessary place. Furthermore, the
@@ -107,11 +101,7 @@ struct sk_buff *mlx5e_xsk_skb_from_cqe_linear(struct mlx5e_rq *rq,
                return NULL;
        }
 
-       rcu_read_lock();
-       consumed = mlx5e_xdp_handle(rq, NULL, &cqe_bcnt, xdp);
-       rcu_read_unlock();
-
-       if (likely(consumed))
+       if (likely(mlx5e_xdp_handle(rq, NULL, &cqe_bcnt, xdp)))
                return NULL; /* page/packet was consumed by XDP */
 
        /* XDP_PASS: copy the data from the UMEM to a new SKB. The frame reuse
index 2c80205dc939d359e21bb9b460759e32c42d60f2..3081cd74d651bf2f3c425bb1f3e5f73acca1e23d 100644 (file)
@@ -143,8 +143,7 @@ err_free_cparam:
 void mlx5e_close_xsk(struct mlx5e_channel *c)
 {
        clear_bit(MLX5E_CHANNEL_STATE_XSK, c->state);
-       napi_synchronize(&c->napi);
-       synchronize_rcu(); /* Sync with the XSK wakeup. */
+       synchronize_rcu(); /* Sync with the XSK wakeup and with NAPI. */
 
        mlx5e_close_rq(&c->xskrq);
        mlx5e_close_cq(&c->xskrq.cq);
index 3e1434fb166ec499af4dc794ccfdb0b1c348cbf1..cccf65fc116eef8a8986e3f7696474565dfd2f83 100644 (file)
@@ -911,7 +911,7 @@ void mlx5e_activate_rq(struct mlx5e_rq *rq)
 void mlx5e_deactivate_rq(struct mlx5e_rq *rq)
 {
        clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state);
-       napi_synchronize(&rq->channel->napi); /* prevent mlx5e_post_rx_wqes */
+       synchronize_rcu(); /* Sync with NAPI to prevent mlx5e_post_rx_wqes. */
 }
 
 void mlx5e_close_rq(struct mlx5e_rq *rq)
@@ -1356,12 +1356,10 @@ void mlx5e_tx_disable_queue(struct netdev_queue *txq)
 
 static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq)
 {
-       struct mlx5e_channel *c = sq->channel;
        struct mlx5_wq_cyc *wq = &sq->wq;
 
        clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
-       /* prevent netif_tx_wake_queue */
-       napi_synchronize(&c->napi);
+       synchronize_rcu(); /* Sync with NAPI to prevent netif_tx_wake_queue. */
 
        mlx5e_tx_disable_queue(sq->txq);
 
@@ -1436,10 +1434,8 @@ void mlx5e_activate_icosq(struct mlx5e_icosq *icosq)
 
 void mlx5e_deactivate_icosq(struct mlx5e_icosq *icosq)
 {
-       struct mlx5e_channel *c = icosq->channel;
-
        clear_bit(MLX5E_SQ_STATE_ENABLED, &icosq->state);
-       napi_synchronize(&c->napi);
+       synchronize_rcu(); /* Sync with NAPI. */
 }
 
 void mlx5e_close_icosq(struct mlx5e_icosq *sq)
@@ -1517,7 +1513,7 @@ void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq)
        struct mlx5e_channel *c = sq->channel;
 
        clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
-       napi_synchronize(&c->napi);
+       synchronize_rcu(); /* Sync with NAPI. */
 
        mlx5e_destroy_sq(c->mdev, sq->sqn);
        mlx5e_free_xdpsq_descs(sq);
index dbb1c63239672f4e3f6d74a32b5f835526d4a143..409fecbcc5d2b14b9158f4f9f8180d523fa6ac6f 100644 (file)
@@ -1072,7 +1072,6 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
        struct xdp_buff xdp;
        struct sk_buff *skb;
        void *va, *data;
-       bool consumed;
        u32 frag_size;
 
        va             = page_address(di->page) + wi->offset;
@@ -1084,11 +1083,8 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
        prefetchw(va); /* xdp_frame data area */
        prefetch(data);
 
-       rcu_read_lock();
        mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt, &xdp);
-       consumed = mlx5e_xdp_handle(rq, di, &cqe_bcnt, &xdp);
-       rcu_read_unlock();
-       if (consumed)
+       if (mlx5e_xdp_handle(rq, di, &cqe_bcnt, &xdp))
                return NULL; /* page/packet was consumed by XDP */
 
        rx_headroom = xdp.data - xdp.data_hard_start;
@@ -1369,7 +1365,6 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
        struct sk_buff *skb;
        void *va, *data;
        u32 frag_size;
-       bool consumed;
 
        /* Check packet size. Note LRO doesn't use linear SKB */
        if (unlikely(cqe_bcnt > rq->hw_mtu)) {
@@ -1386,11 +1381,8 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
        prefetchw(va); /* xdp_frame data area */
        prefetch(data);
 
-       rcu_read_lock();
        mlx5e_fill_xdp_buff(rq, va, rx_headroom, cqe_bcnt32, &xdp);
-       consumed = mlx5e_xdp_handle(rq, di, &cqe_bcnt32, &xdp);
-       rcu_read_unlock();
-       if (consumed) {
+       if (mlx5e_xdp_handle(rq, di, &cqe_bcnt32, &xdp)) {
                if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags))
                        __set_bit(page_idx, wi->xdp_xmit_bitmap); /* non-atomic */
                return NULL; /* page/packet was consumed by XDP */
index 8480278f2ee209dc02b4d50d89d577487beca6f9..954a2f0513d671e2b1ee785a4feabe7a1e959161 100644 (file)
@@ -121,13 +121,17 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
        struct mlx5e_xdpsq *xsksq = &c->xsksq;
        struct mlx5e_rq *xskrq = &c->xskrq;
        struct mlx5e_rq *rq = &c->rq;
-       bool xsk_open = test_bit(MLX5E_CHANNEL_STATE_XSK, c->state);
        bool aff_change = false;
        bool busy_xsk = false;
        bool busy = false;
        int work_done = 0;
+       bool xsk_open;
        int i;
 
+       rcu_read_lock();
+
+       xsk_open = test_bit(MLX5E_CHANNEL_STATE_XSK, c->state);
+
        ch_stats->poll++;
 
        for (i = 0; i < c->num_tc; i++)
@@ -167,8 +171,10 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
        busy |= busy_xsk;
 
        if (busy) {
-               if (likely(mlx5e_channel_no_affinity_change(c)))
-                       return budget;
+               if (likely(mlx5e_channel_no_affinity_change(c))) {
+                       work_done = budget;
+                       goto out;
+               }
                ch_stats->aff_change++;
                aff_change = true;
                if (budget && work_done == budget)
@@ -176,7 +182,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
        }
 
        if (unlikely(!napi_complete_done(napi, work_done)))
-               return work_done;
+               goto out;
 
        ch_stats->arm++;
 
@@ -203,6 +209,9 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
                ch_stats->force_irq++;
        }
 
+out:
+       rcu_read_unlock();
+
        return work_done;
 }