dma_addr_t      addr;
 };
 
+/* XDP packets can be transmitted in different ways. On completion, we need to
+ * distinguish between them to clean up things in a proper way.
+ */
+enum mlx5e_xdp_xmit_mode {
+       /* An xdp_frame was transmitted due to either XDP_REDIRECT from another
+        * device or XDP_TX from an XSK RQ. The frame has to be unmapped and
+        * returned.
+        */
+       MLX5E_XDP_XMIT_MODE_FRAME,
+
+       /* The xdp_frame was created in place as a result of XDP_TX from a
+        * regular RQ. No DMA remapping happened, and the page belongs to us.
+        */
+       MLX5E_XDP_XMIT_MODE_PAGE,
+
+       /* No xdp_frame was created at all, the transmit happened from a UMEM
+        * page. The UMEM Completion Ring producer pointer has to be increased.
+        */
+       MLX5E_XDP_XMIT_MODE_XSK,
+};
+
 struct mlx5e_xdp_info {
-       struct xdp_frame      *xdpf;
-       dma_addr_t            dma_addr;
-       struct mlx5e_dma_info di;
+       enum mlx5e_xdp_xmit_mode mode;
+       union {
+               struct {
+                       struct xdp_frame *xdpf;
+                       dma_addr_t dma_addr;
+               } frame;
+               struct {
+                       struct mlx5e_dma_info di;
+               } page;
+       };
+};
+
+struct mlx5e_xdp_xmit_data {
+       dma_addr_t  dma_addr;
+       void       *data;
+       u32         len;
 };
 
 struct mlx5e_xdp_info_fifo {
 };
 
 struct mlx5e_xdpsq;
-typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq*,
-                                       struct mlx5e_xdp_info*);
+typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq *,
+                                       struct mlx5e_xdp_xmit_data *,
+                                       struct mlx5e_xdp_info *);
+
 struct mlx5e_xdpsq {
        /* data path */
 
 
 mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_dma_info *di,
                    struct xdp_buff *xdp)
 {
+       struct mlx5e_xdp_xmit_data xdptxd;
        struct mlx5e_xdp_info xdpi;
+       struct xdp_frame *xdpf;
+       dma_addr_t dma_addr;
 
-       xdpi.xdpf = convert_to_xdp_frame(xdp);
-       if (unlikely(!xdpi.xdpf))
+       xdpf = convert_to_xdp_frame(xdp);
+       if (unlikely(!xdpf))
                return false;
-       xdpi.dma_addr = di->addr + (xdpi.xdpf->data - (void *)xdpi.xdpf);
-       dma_sync_single_for_device(sq->pdev, xdpi.dma_addr,
-                                  xdpi.xdpf->len, DMA_TO_DEVICE);
-       xdpi.di = *di;
 
-       return sq->xmit_xdp_frame(sq, &xdpi);
+       xdptxd.data = xdpf->data;
+       xdptxd.len  = xdpf->len;
+
+       xdpi.mode = MLX5E_XDP_XMIT_MODE_PAGE;
+
+       dma_addr = di->addr + (xdpf->data - (void *)xdpf);
+       dma_sync_single_for_device(sq->pdev, dma_addr, xdptxd.len, DMA_TO_DEVICE);
+
+       xdptxd.dma_addr = dma_addr;
+       xdpi.page.di = *di;
+
+       return sq->xmit_xdp_frame(sq, &xdptxd, &xdpi);
 }
 
 /* returns true if packet was consumed by xdp */
 }
 
 static bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq,
+                                      struct mlx5e_xdp_xmit_data *xdptxd,
                                       struct mlx5e_xdp_info *xdpi)
 {
        struct mlx5e_xdp_mpwqe *session = &sq->mpwqe;
        struct mlx5e_xdpsq_stats *stats = sq->stats;
 
-       struct xdp_frame *xdpf = xdpi->xdpf;
-
-       if (unlikely(sq->hw_mtu < xdpf->len)) {
+       if (unlikely(xdptxd->len > sq->hw_mtu)) {
                stats->err++;
                return false;
        }
                mlx5e_xdp_mpwqe_session_start(sq);
        }
 
-       mlx5e_xdp_mpwqe_add_dseg(sq, xdpi, stats);
+       mlx5e_xdp_mpwqe_add_dseg(sq, xdptxd, stats);
 
        if (unlikely(session->complete ||
                     session->ds_count == session->max_ds_count))
        return true;
 }
 
-static bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xdp_info *xdpi)
+static bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq,
+                                struct mlx5e_xdp_xmit_data *xdptxd,
+                                struct mlx5e_xdp_info *xdpi)
 {
        struct mlx5_wq_cyc       *wq   = &sq->wq;
        u16                       pi   = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
        struct mlx5_wqe_eth_seg  *eseg = &wqe->eth;
        struct mlx5_wqe_data_seg *dseg = wqe->data;
 
-       struct xdp_frame *xdpf = xdpi->xdpf;
-       dma_addr_t dma_addr  = xdpi->dma_addr;
-       unsigned int dma_len = xdpf->len;
+       dma_addr_t dma_addr = xdptxd->dma_addr;
+       u32 dma_len = xdptxd->len;
 
        struct mlx5e_xdpsq_stats *stats = sq->stats;
 
 
        /* copy the inline part if required */
        if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) {
-               memcpy(eseg->inline_hdr.start, xdpf->data, MLX5E_XDP_MIN_INLINE);
+               memcpy(eseg->inline_hdr.start, xdptxd->data, MLX5E_XDP_MIN_INLINE);
                eseg->inline_hdr.sz = cpu_to_be16(MLX5E_XDP_MIN_INLINE);
                dma_len  -= MLX5E_XDP_MIN_INLINE;
                dma_addr += MLX5E_XDP_MIN_INLINE;
        for (i = 0; i < wi->num_pkts; i++) {
                struct mlx5e_xdp_info xdpi = mlx5e_xdpi_fifo_pop(xdpi_fifo);
 
-               if (rq) {
-                       /* XDP_TX */
-                       mlx5e_page_release(rq, &xdpi.di, recycle);
-               } else {
+               switch (xdpi.mode) {
+               case MLX5E_XDP_XMIT_MODE_FRAME:
                        /* XDP_REDIRECT */
-                       dma_unmap_single(sq->pdev, xdpi.dma_addr,
-                                        xdpi.xdpf->len, DMA_TO_DEVICE);
-                       xdp_return_frame(xdpi.xdpf);
+                       dma_unmap_single(sq->pdev, xdpi.frame.dma_addr,
+                                        xdpi.frame.xdpf->len, DMA_TO_DEVICE);
+                       xdp_return_frame(xdpi.frame.xdpf);
+                       break;
+               case MLX5E_XDP_XMIT_MODE_PAGE:
+                       /* XDP_TX */
+                       mlx5e_page_release(rq, &xdpi.page.di, recycle);
+                       break;
+               default:
+                       WARN_ON_ONCE(true);
                }
        }
 }
 
        for (i = 0; i < n; i++) {
                struct xdp_frame *xdpf = frames[i];
+               struct mlx5e_xdp_xmit_data xdptxd;
                struct mlx5e_xdp_info xdpi;
 
-               xdpi.dma_addr = dma_map_single(sq->pdev, xdpf->data, xdpf->len,
-                                              DMA_TO_DEVICE);
-               if (unlikely(dma_mapping_error(sq->pdev, xdpi.dma_addr))) {
+               xdptxd.data = xdpf->data;
+               xdptxd.len = xdpf->len;
+               xdptxd.dma_addr = dma_map_single(sq->pdev, xdptxd.data,
+                                                xdptxd.len, DMA_TO_DEVICE);
+
+               if (unlikely(dma_mapping_error(sq->pdev, xdptxd.dma_addr))) {
                        xdp_return_frame_rx_napi(xdpf);
                        drops++;
                        continue;
                }
 
-               xdpi.xdpf = xdpf;
+               xdpi.mode           = MLX5E_XDP_XMIT_MODE_FRAME;
+               xdpi.frame.xdpf     = xdpf;
+               xdpi.frame.dma_addr = xdptxd.dma_addr;
 
-               if (unlikely(!sq->xmit_xdp_frame(sq, &xdpi))) {
-                       dma_unmap_single(sq->pdev, xdpi.dma_addr,
-                                        xdpf->len, DMA_TO_DEVICE);
+               if (unlikely(!sq->xmit_xdp_frame(sq, &xdptxd, &xdpi))) {
+                       dma_unmap_single(sq->pdev, xdptxd.dma_addr,
+                                        xdptxd.len, DMA_TO_DEVICE);
                        xdp_return_frame_rx_napi(xdpf);
                        drops++;
                }
 
 }
 
 static inline void
-mlx5e_xdp_mpwqe_add_dseg(struct mlx5e_xdpsq *sq, struct mlx5e_xdp_info *xdpi,
+mlx5e_xdp_mpwqe_add_dseg(struct mlx5e_xdpsq *sq,
+                        struct mlx5e_xdp_xmit_data *xdptxd,
                         struct mlx5e_xdpsq_stats *stats)
 {
        struct mlx5e_xdp_mpwqe *session = &sq->mpwqe;
-       dma_addr_t dma_addr    = xdpi->dma_addr;
-       struct xdp_frame *xdpf = xdpi->xdpf;
        struct mlx5_wqe_data_seg *dseg =
                (struct mlx5_wqe_data_seg *)session->wqe + session->ds_count;
-       u16 dma_len = xdpf->len;
+       u32 dma_len = xdptxd->len;
 
        session->pkt_count++;
 
                }
 
                inline_dseg->byte_count = cpu_to_be32(dma_len | MLX5_INLINE_SEG);
-               memcpy(inline_dseg->data, xdpf->data, dma_len);
+               memcpy(inline_dseg->data, xdptxd->data, dma_len);
 
                session->ds_count += ds_cnt;
                stats->inlnw++;
        }
 
 no_inline:
-       dseg->addr       = cpu_to_be64(dma_addr);
+       dseg->addr       = cpu_to_be64(xdptxd->dma_addr);
        dseg->byte_count = cpu_to_be32(dma_len);
        dseg->lkey       = sq->mkey_be;
        session->ds_count++;