Merge tag 'mlx5-fixes-2019-07-11' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / net / ethernet / mellanox / mlx5 / core / en_rx.c
index 8a5f9411cac6cc399335972540770a0f504a553f..ac6e586d403d8cb6bffdb44fa475efa60b0833c7 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/tcp.h>
+#include <linux/indirect_call_wrapper.h>
 #include <net/ip6_checksum.h>
 #include <net/page_pool.h>
 #include <net/inet_ecn.h>
@@ -46,6 +47,7 @@
 #include "en_accel/tls_rxtx.h"
 #include "lib/clock.h"
 #include "en/xdp.h"
+#include "en/xsk/rx.h"
 
 static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config)
 {
@@ -234,8 +236,8 @@ static inline bool mlx5e_rx_cache_get(struct mlx5e_rq *rq,
        return true;
 }
 
-static inline int mlx5e_page_alloc_mapped(struct mlx5e_rq *rq,
-                                         struct mlx5e_dma_info *dma_info)
+static inline int mlx5e_page_alloc_pool(struct mlx5e_rq *rq,
+                                       struct mlx5e_dma_info *dma_info)
 {
        if (mlx5e_rx_cache_get(rq, dma_info))
                return 0;
@@ -247,7 +249,7 @@ static inline int mlx5e_page_alloc_mapped(struct mlx5e_rq *rq,
        dma_info->addr = dma_map_page(rq->pdev, dma_info->page, 0,
                                      PAGE_SIZE, rq->buff.map_dir);
        if (unlikely(dma_mapping_error(rq->pdev, dma_info->addr))) {
-               put_page(dma_info->page);
+               page_pool_recycle_direct(rq->page_pool, dma_info->page);
                dma_info->page = NULL;
                return -ENOMEM;
        }
@@ -255,13 +257,23 @@ static inline int mlx5e_page_alloc_mapped(struct mlx5e_rq *rq,
        return 0;
 }
 
+static inline int mlx5e_page_alloc(struct mlx5e_rq *rq,
+                                  struct mlx5e_dma_info *dma_info)
+{
+       if (rq->umem)
+               return mlx5e_xsk_page_alloc_umem(rq, dma_info);
+       else
+               return mlx5e_page_alloc_pool(rq, dma_info);
+}
+
 void mlx5e_page_dma_unmap(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info)
 {
        dma_unmap_page(rq->pdev, dma_info->addr, PAGE_SIZE, rq->buff.map_dir);
 }
 
-void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info,
-                       bool recycle)
+void mlx5e_page_release_dynamic(struct mlx5e_rq *rq,
+                               struct mlx5e_dma_info *dma_info,
+                               bool recycle)
 {
        if (likely(recycle)) {
                if (mlx5e_rx_cache_put(rq, dma_info))
@@ -271,10 +283,25 @@ void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info,
                page_pool_recycle_direct(rq->page_pool, dma_info->page);
        } else {
                mlx5e_page_dma_unmap(rq, dma_info);
+               page_pool_release_page(rq->page_pool, dma_info->page);
                put_page(dma_info->page);
        }
 }
 
+static inline void mlx5e_page_release(struct mlx5e_rq *rq,
+                                     struct mlx5e_dma_info *dma_info,
+                                     bool recycle)
+{
+       if (rq->umem)
+               /* The `recycle` parameter is ignored, and the page is always
+                * put into the Reuse Ring, because there is no way to return
+                * the page to the userspace when the interface goes down.
+                */
+               mlx5e_xsk_page_release(rq, dma_info);
+       else
+               mlx5e_page_release_dynamic(rq, dma_info, recycle);
+}
+
 static inline int mlx5e_get_rx_frag(struct mlx5e_rq *rq,
                                    struct mlx5e_wqe_frag_info *frag)
 {
@@ -286,7 +313,7 @@ static inline int mlx5e_get_rx_frag(struct mlx5e_rq *rq,
                 * offset) should just use the new one without replenishing again
                 * by themselves.
                 */
-               err = mlx5e_page_alloc_mapped(rq, frag->di);
+               err = mlx5e_page_alloc(rq, frag->di);
 
        return err;
 }
@@ -352,6 +379,13 @@ static int mlx5e_alloc_rx_wqes(struct mlx5e_rq *rq, u16 ix, u8 wqe_bulk)
        int err;
        int i;
 
+       if (rq->umem) {
+               int pages_desired = wqe_bulk << rq->wqe.info.log_num_frags;
+
+               if (unlikely(!mlx5e_xsk_pages_enough_umem(rq, pages_desired)))
+                       return -ENOMEM;
+       }
+
        for (i = 0; i < wqe_bulk; i++) {
                struct mlx5e_rx_wqe_cyc *wqe = mlx5_wq_cyc_get_wqe(wq, ix + i);
 
@@ -399,11 +433,17 @@ mlx5e_copy_skb_header(struct device *pdev, struct sk_buff *skb,
 static void
 mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, bool recycle)
 {
-       const bool no_xdp_xmit =
-               bitmap_empty(wi->xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE);
+       bool no_xdp_xmit;
        struct mlx5e_dma_info *dma_info = wi->umr.dma_info;
        int i;
 
+       /* A common case for AF_XDP. */
+       if (bitmap_full(wi->xdp_xmit_bitmap, MLX5_MPWRQ_PAGES_PER_WQE))
+               return;
+
+       no_xdp_xmit = bitmap_empty(wi->xdp_xmit_bitmap,
+                                  MLX5_MPWRQ_PAGES_PER_WQE);
+
        for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++)
                if (no_xdp_xmit || !test_bit(i, wi->xdp_xmit_bitmap))
                        mlx5e_page_release(rq, &dma_info[i], recycle);
@@ -425,11 +465,6 @@ static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq, u8 n)
        mlx5_wq_ll_update_db_record(wq);
 }
 
-static inline u16 mlx5e_icosq_wrap_cnt(struct mlx5e_icosq *sq)
-{
-       return mlx5_wq_cyc_get_ctr_wrap_cnt(&sq->wq, sq->pc);
-}
-
 static inline void mlx5e_fill_icosq_frag_edge(struct mlx5e_icosq *sq,
                                              struct mlx5_wq_cyc *wq,
                                              u16 pi, u16 nnops)
@@ -457,6 +492,12 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
        int err;
        int i;
 
+       if (rq->umem &&
+           unlikely(!mlx5e_xsk_pages_enough_umem(rq, MLX5_MPWRQ_PAGES_PER_WQE))) {
+               err = -ENOMEM;
+               goto err;
+       }
+
        pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
        contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
        if (unlikely(contig_wqebbs_room < MLX5E_UMR_WQEBBS)) {
@@ -465,12 +506,10 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
        }
 
        umr_wqe = mlx5_wq_cyc_get_wqe(wq, pi);
-       if (unlikely(mlx5e_icosq_wrap_cnt(sq) < 2))
-               memcpy(umr_wqe, &rq->mpwqe.umr_wqe,
-                      offsetof(struct mlx5e_umr_wqe, inline_mtts));
+       memcpy(umr_wqe, &rq->mpwqe.umr_wqe, offsetof(struct mlx5e_umr_wqe, inline_mtts));
 
        for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) {
-               err = mlx5e_page_alloc_mapped(rq, dma_info);
+               err = mlx5e_page_alloc(rq, dma_info);
                if (unlikely(err))
                        goto err_unmap;
                umr_wqe->inline_mtts[i].ptag = cpu_to_be64(dma_info->addr | MLX5_EN_WR);
@@ -485,6 +524,7 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
        umr_wqe->uctrl.xlt_offset = cpu_to_be16(xlt_offset);
 
        sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR;
+       sq->db.ico_wqe[pi].umr.rq = rq;
        sq->pc += MLX5E_UMR_WQEBBS;
 
        sq->doorbell_cseg = &umr_wqe->ctrl;
@@ -496,6 +536,8 @@ err_unmap:
                dma_info--;
                mlx5e_page_release(rq, dma_info, true);
        }
+
+err:
        rq->stats->buff_alloc_err++;
 
        return err;
@@ -542,11 +584,10 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq)
        return !!err;
 }
 
-static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq)
+void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
 {
        struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq);
        struct mlx5_cqe64 *cqe;
-       u8  completed_umr = 0;
        u16 sqcc;
        int i;
 
@@ -587,7 +628,7 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq)
 
                        if (likely(wi->opcode == MLX5_OPCODE_UMR)) {
                                sqcc += MLX5E_UMR_WQEBBS;
-                               completed_umr++;
+                               wi->umr.rq->mpwqe.umr_completed++;
                        } else if (likely(wi->opcode == MLX5_OPCODE_NOP)) {
                                sqcc++;
                        } else {
@@ -603,24 +644,25 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq)
        sq->cc = sqcc;
 
        mlx5_cqwq_update_db_record(&cq->wq);
-
-       if (likely(completed_umr)) {
-               mlx5e_post_rx_mpwqe(rq, completed_umr);
-               rq->mpwqe.umr_in_progress -= completed_umr;
-       }
 }
 
 bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)
 {
        struct mlx5e_icosq *sq = &rq->channel->icosq;
        struct mlx5_wq_ll *wq = &rq->mpwqe.wq;
+       u8  umr_completed = rq->mpwqe.umr_completed;
+       int alloc_err = 0;
        u8  missing, i;
        u16 head;
 
        if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state)))
                return false;
 
-       mlx5e_poll_ico_cq(&sq->cq, rq);
+       if (umr_completed) {
+               mlx5e_post_rx_mpwqe(rq, umr_completed);
+               rq->mpwqe.umr_in_progress -= umr_completed;
+               rq->mpwqe.umr_completed = 0;
+       }
 
        missing = mlx5_wq_ll_missing(wq) - rq->mpwqe.umr_in_progress;
 
@@ -634,7 +676,9 @@ bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)
        head = rq->mpwqe.actual_wq_head;
        i = missing;
        do {
-               if (unlikely(mlx5e_alloc_rx_mpwqe(rq, head)))
+               alloc_err = mlx5e_alloc_rx_mpwqe(rq, head);
+
+               if (unlikely(alloc_err))
                        break;
                head = mlx5_wq_ll_get_wqe_next_ix(wq, head);
        } while (--i);
@@ -648,6 +692,12 @@ bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)
        rq->mpwqe.umr_in_progress += rq->mpwqe.umr_last_bulk;
        rq->mpwqe.actual_wq_head   = head;
 
+       /* If XSK Fill Ring doesn't have enough frames, busy poll by
+        * rescheduling the NAPI poll.
+        */
+       if (unlikely(alloc_err == -ENOMEM && rq->umem))
+               return true;
+
        return false;
 }
 
@@ -1021,7 +1071,7 @@ mlx5e_skb_from_cqe_linear(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe,
        }
 
        rcu_read_lock();
-       consumed = mlx5e_xdp_handle(rq, di, va, &rx_headroom, &cqe_bcnt);
+       consumed = mlx5e_xdp_handle(rq, di, va, &rx_headroom, &cqe_bcnt, false);
        rcu_read_unlock();
        if (consumed)
                return NULL; /* page/packet was consumed by XDP */
@@ -1097,7 +1147,10 @@ void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
        wi       = get_frag(rq, ci);
        cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
 
-       skb = rq->wqe.skb_from_cqe(rq, cqe, wi, cqe_bcnt);
+       skb = INDIRECT_CALL_2(rq->wqe.skb_from_cqe,
+                             mlx5e_skb_from_cqe_linear,
+                             mlx5e_skb_from_cqe_nonlinear,
+                             rq, cqe, wi, cqe_bcnt);
        if (!skb) {
                /* probably for XDP */
                if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) {
@@ -1235,7 +1288,7 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
        prefetch(data);
 
        rcu_read_lock();
-       consumed = mlx5e_xdp_handle(rq, di, va, &rx_headroom, &cqe_bcnt32);
+       consumed = mlx5e_xdp_handle(rq, di, va, &rx_headroom, &cqe_bcnt32, false);
        rcu_read_unlock();
        if (consumed) {
                if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags))
@@ -1284,8 +1337,10 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
 
        cqe_bcnt = mpwrq_get_cqe_byte_cnt(cqe);
 
-       skb = rq->mpwqe.skb_from_cqe_mpwrq(rq, wi, cqe_bcnt, head_offset,
-                                          page_idx);
+       skb = INDIRECT_CALL_2(rq->mpwqe.skb_from_cqe_mpwrq,
+                             mlx5e_skb_from_cqe_mpwrq_linear,
+                             mlx5e_skb_from_cqe_mpwrq_nonlinear,
+                             rq, wi, cqe_bcnt, head_offset, page_idx);
        if (!skb)
                goto mpwrq_cqe_out;
 
@@ -1332,7 +1387,8 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
 
                mlx5_cqwq_pop(cqwq);
 
-               rq->handle_rx_cqe(rq, cqe);
+               INDIRECT_CALL_2(rq->handle_rx_cqe, mlx5e_handle_rx_cqe_mpwrq,
+                               mlx5e_handle_rx_cqe, rq, cqe);
        } while ((++work_done < budget) && (cqe = mlx5_cqwq_get_cqe(cqwq)));
 
 out:
@@ -1442,7 +1498,10 @@ void mlx5i_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
        wi       = get_frag(rq, ci);
        cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
 
-       skb = rq->wqe.skb_from_cqe(rq, cqe, wi, cqe_bcnt);
+       skb = INDIRECT_CALL_2(rq->wqe.skb_from_cqe,
+                             mlx5e_skb_from_cqe_linear,
+                             mlx5e_skb_from_cqe_nonlinear,
+                             rq, cqe, wi, cqe_bcnt);
        if (!skb)
                goto wq_free_wqe;
 
@@ -1474,7 +1533,10 @@ void mlx5e_ipsec_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
        wi       = get_frag(rq, ci);
        cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
 
-       skb = rq->wqe.skb_from_cqe(rq, cqe, wi, cqe_bcnt);
+       skb = INDIRECT_CALL_2(rq->wqe.skb_from_cqe,
+                             mlx5e_skb_from_cqe_linear,
+                             mlx5e_skb_from_cqe_nonlinear,
+                             rq, cqe, wi, cqe_bcnt);
        if (unlikely(!skb)) {
                /* a DROP, save the page-reuse checks */
                mlx5e_free_rx_wqe(rq, wi, true);