1159d2131STariq Toukan /* 2159d2131STariq Toukan * Copyright (c) 2018, Mellanox Technologies. All rights reserved. 3159d2131STariq Toukan * 4159d2131STariq Toukan * This software is available to you under a choice of one of two 5159d2131STariq Toukan * licenses. You may choose to be licensed under the terms of the GNU 6159d2131STariq Toukan * General Public License (GPL) Version 2, available from the file 7159d2131STariq Toukan * COPYING in the main directory of this source tree, or the 8159d2131STariq Toukan * OpenIB.org BSD license below: 9159d2131STariq Toukan * 10159d2131STariq Toukan * Redistribution and use in source and binary forms, with or 11159d2131STariq Toukan * without modification, are permitted provided that the following 12159d2131STariq Toukan * conditions are met: 13159d2131STariq Toukan * 14159d2131STariq Toukan * - Redistributions of source code must retain the above 15159d2131STariq Toukan * copyright notice, this list of conditions and the following 16159d2131STariq Toukan * disclaimer. 17159d2131STariq Toukan * 18159d2131STariq Toukan * - Redistributions in binary form must reproduce the above 19159d2131STariq Toukan * copyright notice, this list of conditions and the following 20159d2131STariq Toukan * disclaimer in the documentation and/or other materials 21159d2131STariq Toukan * provided with the distribution. 22159d2131STariq Toukan * 23159d2131STariq Toukan * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24159d2131STariq Toukan * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25159d2131STariq Toukan * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26159d2131STariq Toukan * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27159d2131STariq Toukan * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28159d2131STariq Toukan * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29159d2131STariq Toukan * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30159d2131STariq Toukan * SOFTWARE. 31159d2131STariq Toukan */ 32159d2131STariq Toukan 33159d2131STariq Toukan #include <linux/bpf_trace.h> 34159d2131STariq Toukan #include "en/xdp.h" 35159d2131STariq Toukan 36c94e4f11STariq Toukan static inline bool 37c94e4f11STariq Toukan mlx5e_xmit_xdp_buff(struct mlx5e_xdpsq *sq, struct mlx5e_dma_info *di, 38c94e4f11STariq Toukan struct xdp_buff *xdp) 39c94e4f11STariq Toukan { 40c94e4f11STariq Toukan struct mlx5e_xdp_info xdpi; 41c94e4f11STariq Toukan 42c94e4f11STariq Toukan xdpi.xdpf = convert_to_xdp_frame(xdp); 43c94e4f11STariq Toukan if (unlikely(!xdpi.xdpf)) 44c94e4f11STariq Toukan return false; 45c94e4f11STariq Toukan xdpi.dma_addr = di->addr + (xdpi.xdpf->data - (void *)xdpi.xdpf); 46c94e4f11STariq Toukan dma_sync_single_for_device(sq->pdev, xdpi.dma_addr, 47c94e4f11STariq Toukan xdpi.xdpf->len, PCI_DMA_TODEVICE); 48c94e4f11STariq Toukan xdpi.di = *di; 49c94e4f11STariq Toukan 50c94e4f11STariq Toukan return mlx5e_xmit_xdp_frame(sq, &xdpi); 51c94e4f11STariq Toukan } 52c94e4f11STariq Toukan 53159d2131STariq Toukan /* returns true if packet was consumed by xdp */ 54159d2131STariq Toukan bool mlx5e_xdp_handle(struct mlx5e_rq *rq, struct mlx5e_dma_info *di, 55159d2131STariq Toukan void *va, u16 *rx_headroom, u32 *len) 56159d2131STariq Toukan { 57159d2131STariq Toukan struct bpf_prog *prog = READ_ONCE(rq->xdp_prog); 58159d2131STariq Toukan struct xdp_buff xdp; 59159d2131STariq Toukan u32 act; 60159d2131STariq Toukan int err; 61159d2131STariq Toukan 62159d2131STariq Toukan if (!prog) 63159d2131STariq Toukan return false; 64159d2131STariq Toukan 65159d2131STariq Toukan xdp.data = va + *rx_headroom; 66159d2131STariq Toukan xdp_set_data_meta_invalid(&xdp); 67159d2131STariq Toukan xdp.data_end = xdp.data + *len; 68159d2131STariq Toukan xdp.data_hard_start = va; 69159d2131STariq Toukan xdp.rxq = &rq->xdp_rxq; 70159d2131STariq Toukan 71159d2131STariq Toukan act = bpf_prog_run_xdp(prog, &xdp); 72159d2131STariq Toukan switch (act) { 73159d2131STariq Toukan case XDP_PASS: 74159d2131STariq Toukan *rx_headroom = xdp.data - xdp.data_hard_start; 75159d2131STariq Toukan *len = xdp.data_end - xdp.data; 76159d2131STariq Toukan return false; 77159d2131STariq Toukan case XDP_TX: 78c94e4f11STariq Toukan if (unlikely(!mlx5e_xmit_xdp_buff(&rq->xdpsq, di, &xdp))) 79c94e4f11STariq Toukan goto xdp_abort; 80c94e4f11STariq Toukan __set_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags); /* non-atomic */ 81159d2131STariq Toukan return true; 82159d2131STariq Toukan case XDP_REDIRECT: 83159d2131STariq Toukan /* When XDP enabled then page-refcnt==1 here */ 84159d2131STariq Toukan err = xdp_do_redirect(rq->netdev, &xdp, prog); 85c94e4f11STariq Toukan if (unlikely(err)) 86c94e4f11STariq Toukan goto xdp_abort; 87159d2131STariq Toukan __set_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags); 88159d2131STariq Toukan rq->xdpsq.db.redirect_flush = true; 89159d2131STariq Toukan mlx5e_page_dma_unmap(rq, di); 9086690b4bSTariq Toukan rq->stats->xdp_redirect++; 91159d2131STariq Toukan return true; 92159d2131STariq Toukan default: 93159d2131STariq Toukan bpf_warn_invalid_xdp_action(act); 94159d2131STariq Toukan case XDP_ABORTED: 95c94e4f11STariq Toukan xdp_abort: 96159d2131STariq Toukan trace_xdp_exception(rq->netdev, prog, act); 97159d2131STariq Toukan case XDP_DROP: 98159d2131STariq Toukan rq->stats->xdp_drop++; 99159d2131STariq Toukan return true; 100159d2131STariq Toukan } 101159d2131STariq Toukan } 102159d2131STariq Toukan 103c94e4f11STariq Toukan bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xdp_info *xdpi) 104159d2131STariq Toukan { 105159d2131STariq Toukan struct mlx5_wq_cyc *wq = &sq->wq; 106159d2131STariq Toukan u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc); 107159d2131STariq Toukan struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); 108159d2131STariq Toukan 109c94e4f11STariq Toukan struct mlx5e_rq *rq = container_of(sq, struct mlx5e_rq, xdpsq); 110c94e4f11STariq Toukan 111159d2131STariq Toukan struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; 112159d2131STariq Toukan struct mlx5_wqe_eth_seg *eseg = &wqe->eth; 113c94e4f11STariq Toukan struct mlx5_wqe_data_seg *dseg = wqe->data; 114159d2131STariq Toukan 115c94e4f11STariq Toukan struct xdp_frame *xdpf = xdpi->xdpf; 116c94e4f11STariq Toukan dma_addr_t dma_addr = xdpi->dma_addr; 117c94e4f11STariq Toukan unsigned int dma_len = xdpf->len; 118159d2131STariq Toukan 119159d2131STariq Toukan struct mlx5e_rq_stats *stats = rq->stats; 120159d2131STariq Toukan 121159d2131STariq Toukan prefetchw(wqe); 122159d2131STariq Toukan 123c94e4f11STariq Toukan if (unlikely(dma_len < MLX5E_XDP_MIN_INLINE || sq->hw_mtu < dma_len)) { 124159d2131STariq Toukan stats->xdp_drop++; 125159d2131STariq Toukan return false; 126159d2131STariq Toukan } 127159d2131STariq Toukan 128159d2131STariq Toukan if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1))) { 129159d2131STariq Toukan if (sq->db.doorbell) { 130159d2131STariq Toukan /* SQ is full, ring doorbell */ 131159d2131STariq Toukan mlx5e_xmit_xdp_doorbell(sq); 132159d2131STariq Toukan sq->db.doorbell = false; 133159d2131STariq Toukan } 134159d2131STariq Toukan stats->xdp_tx_full++; 135159d2131STariq Toukan return false; 136159d2131STariq Toukan } 137159d2131STariq Toukan 138159d2131STariq Toukan cseg->fm_ce_se = 0; 139159d2131STariq Toukan 140159d2131STariq Toukan /* copy the inline part if required */ 141159d2131STariq Toukan if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) { 142c94e4f11STariq Toukan memcpy(eseg->inline_hdr.start, xdpf->data, MLX5E_XDP_MIN_INLINE); 143159d2131STariq Toukan eseg->inline_hdr.sz = cpu_to_be16(MLX5E_XDP_MIN_INLINE); 144159d2131STariq Toukan dma_len -= MLX5E_XDP_MIN_INLINE; 145159d2131STariq Toukan dma_addr += MLX5E_XDP_MIN_INLINE; 146159d2131STariq Toukan dseg++; 147159d2131STariq Toukan } 148159d2131STariq Toukan 149159d2131STariq Toukan /* write the dma part */ 150159d2131STariq Toukan dseg->addr = cpu_to_be64(dma_addr); 151159d2131STariq Toukan dseg->byte_count = cpu_to_be32(dma_len); 152159d2131STariq Toukan 153159d2131STariq Toukan cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_SEND); 154159d2131STariq Toukan 155159d2131STariq Toukan /* move page to reference to sq responsibility, 156159d2131STariq Toukan * and mark so it's not put back in page-cache. 157159d2131STariq Toukan */ 158c94e4f11STariq Toukan sq->db.xdpi[pi] = *xdpi; 159159d2131STariq Toukan sq->pc++; 160159d2131STariq Toukan 161159d2131STariq Toukan sq->db.doorbell = true; 162159d2131STariq Toukan 163159d2131STariq Toukan stats->xdp_tx++; 164159d2131STariq Toukan return true; 165159d2131STariq Toukan } 166159d2131STariq Toukan 167159d2131STariq Toukan bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) 168159d2131STariq Toukan { 169159d2131STariq Toukan struct mlx5e_xdpsq *sq; 170159d2131STariq Toukan struct mlx5_cqe64 *cqe; 171159d2131STariq Toukan struct mlx5e_rq *rq; 172159d2131STariq Toukan u16 sqcc; 173159d2131STariq Toukan int i; 174159d2131STariq Toukan 175159d2131STariq Toukan sq = container_of(cq, struct mlx5e_xdpsq, cq); 176159d2131STariq Toukan 177159d2131STariq Toukan if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) 178159d2131STariq Toukan return false; 179159d2131STariq Toukan 180159d2131STariq Toukan cqe = mlx5_cqwq_get_cqe(&cq->wq); 181159d2131STariq Toukan if (!cqe) 182159d2131STariq Toukan return false; 183159d2131STariq Toukan 184159d2131STariq Toukan rq = container_of(sq, struct mlx5e_rq, xdpsq); 185159d2131STariq Toukan 186159d2131STariq Toukan /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), 187159d2131STariq Toukan * otherwise a cq overrun may occur 188159d2131STariq Toukan */ 189159d2131STariq Toukan sqcc = sq->cc; 190159d2131STariq Toukan 191159d2131STariq Toukan i = 0; 192159d2131STariq Toukan do { 193159d2131STariq Toukan u16 wqe_counter; 194159d2131STariq Toukan bool last_wqe; 195159d2131STariq Toukan 196159d2131STariq Toukan mlx5_cqwq_pop(&cq->wq); 197159d2131STariq Toukan 198159d2131STariq Toukan wqe_counter = be16_to_cpu(cqe->wqe_counter); 199159d2131STariq Toukan 200159d2131STariq Toukan do { 201c94e4f11STariq Toukan struct mlx5e_xdp_info *xdpi; 202159d2131STariq Toukan u16 ci; 203159d2131STariq Toukan 204159d2131STariq Toukan last_wqe = (sqcc == wqe_counter); 205159d2131STariq Toukan 206159d2131STariq Toukan ci = mlx5_wq_cyc_ctr2ix(&sq->wq, sqcc); 207c94e4f11STariq Toukan xdpi = &sq->db.xdpi[ci]; 208159d2131STariq Toukan 209159d2131STariq Toukan sqcc++; 210159d2131STariq Toukan /* Recycle RX page */ 211c94e4f11STariq Toukan mlx5e_page_release(rq, &xdpi->di, true); 212159d2131STariq Toukan } while (!last_wqe); 213159d2131STariq Toukan } while ((++i < MLX5E_TX_CQ_POLL_BUDGET) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); 214159d2131STariq Toukan 215159d2131STariq Toukan rq->stats->xdp_tx_cqe += i; 216159d2131STariq Toukan 217159d2131STariq Toukan mlx5_cqwq_update_db_record(&cq->wq); 218159d2131STariq Toukan 219159d2131STariq Toukan /* ensure cq space is freed before enabling more cqes */ 220159d2131STariq Toukan wmb(); 221159d2131STariq Toukan 222159d2131STariq Toukan sq->cc = sqcc; 223159d2131STariq Toukan return (i == MLX5E_TX_CQ_POLL_BUDGET); 224159d2131STariq Toukan } 225159d2131STariq Toukan 226159d2131STariq Toukan void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq) 227159d2131STariq Toukan { 228159d2131STariq Toukan struct mlx5e_rq *rq = container_of(sq, struct mlx5e_rq, xdpsq); 229c94e4f11STariq Toukan struct mlx5e_xdp_info *xdpi; 230159d2131STariq Toukan u16 ci; 231159d2131STariq Toukan 232159d2131STariq Toukan while (sq->cc != sq->pc) { 233159d2131STariq Toukan ci = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->cc); 234c94e4f11STariq Toukan xdpi = &sq->db.xdpi[ci]; 235159d2131STariq Toukan sq->cc++; 236159d2131STariq Toukan 237c94e4f11STariq Toukan mlx5e_page_release(rq, &xdpi->di, false); 238159d2131STariq Toukan } 239159d2131STariq Toukan } 240159d2131STariq Toukan 241