Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Jakub Kiciński | 2176 | 94.49% | 3 | 75.00% |
Diana Wang | 127 | 5.51% | 1 | 25.00% |
Total | 2303 | 4 |
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2015-2019 Netronome Systems, Inc. */ #include "nfp_app.h" #include "nfp_net_dp.h" #include "nfp_net_xsk.h" /** * nfp_net_rx_alloc_one() - Allocate and map page frag for RX * @dp: NFP Net data path struct * @dma_addr: Pointer to storage for DMA address (output param) * * This function will allcate a new page frag, map it for DMA. * * Return: allocated page frag or NULL on failure. */ void *nfp_net_rx_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) { void *frag; if (!dp->xdp_prog) { frag = netdev_alloc_frag(dp->fl_bufsz); } else { struct page *page; page = alloc_page(GFP_KERNEL); frag = page ? page_address(page) : NULL; } if (!frag) { nn_dp_warn(dp, "Failed to alloc receive page frag\n"); return NULL; } *dma_addr = nfp_net_dma_map_rx(dp, frag); if (dma_mapping_error(dp->dev, *dma_addr)) { nfp_net_free_frag(frag, dp->xdp_prog); nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); return NULL; } return frag; } /** * nfp_net_tx_ring_init() - Fill in the boilerplate for a TX ring * @tx_ring: TX ring structure * @dp: NFP Net data path struct * @r_vec: IRQ vector servicing this ring * @idx: Ring index * @is_xdp: Is this an XDP TX ring? */ static void nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring, struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, unsigned int idx, bool is_xdp) { struct nfp_net *nn = r_vec->nfp_net; tx_ring->idx = idx; tx_ring->r_vec = r_vec; tx_ring->is_xdp = is_xdp; u64_stats_init(&tx_ring->r_vec->tx_sync); tx_ring->qcidx = tx_ring->idx * nn->stride_tx; tx_ring->txrwb = dp->txrwb ? &dp->txrwb[idx] : NULL; tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx); } /** * nfp_net_rx_ring_init() - Fill in the boilerplate for a RX ring * @rx_ring: RX ring structure * @r_vec: IRQ vector servicing this ring * @idx: Ring index */ static void nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring, struct nfp_net_r_vector *r_vec, unsigned int idx) { struct nfp_net *nn = r_vec->nfp_net; rx_ring->idx = idx; rx_ring->r_vec = r_vec; u64_stats_init(&rx_ring->r_vec->rx_sync); rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx; rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx); } /** * nfp_net_rx_ring_reset() - Reflect in SW state of freelist after disable * @rx_ring: RX ring structure * * Assumes that the device is stopped, must be idempotent. */ void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) { unsigned int wr_idx, last_idx; /* wr_p == rd_p means ring was never fed FL bufs. RX rings are always * kept at cnt - 1 FL bufs. */ if (rx_ring->wr_p == 0 && rx_ring->rd_p == 0) return; /* Move the empty entry to the end of the list */ wr_idx = D_IDX(rx_ring, rx_ring->wr_p); last_idx = rx_ring->cnt - 1; if (rx_ring->r_vec->xsk_pool) { rx_ring->xsk_rxbufs[wr_idx] = rx_ring->xsk_rxbufs[last_idx]; memset(&rx_ring->xsk_rxbufs[last_idx], 0, sizeof(*rx_ring->xsk_rxbufs)); } else { rx_ring->rxbufs[wr_idx] = rx_ring->rxbufs[last_idx]; memset(&rx_ring->rxbufs[last_idx], 0, sizeof(*rx_ring->rxbufs)); } memset(rx_ring->rxds, 0, rx_ring->size); rx_ring->wr_p = 0; rx_ring->rd_p = 0; } /** * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring * @dp: NFP Net data path struct * @rx_ring: RX ring to remove buffers from * * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1) * entries. After device is disabled nfp_net_rx_ring_reset() must be called * to restore required ring geometry. */ static void nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) { unsigned int i; if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) return; for (i = 0; i < rx_ring->cnt - 1; i++) { /* NULL skb can only happen when initial filling of the ring * fails to allocate enough buffers and calls here to free * already allocated ones. */ if (!rx_ring->rxbufs[i].frag) continue; nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr); nfp_net_free_frag(rx_ring->rxbufs[i].frag, dp->xdp_prog); rx_ring->rxbufs[i].dma_addr = 0; rx_ring->rxbufs[i].frag = NULL; } } /** * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW) * @dp: NFP Net data path struct * @rx_ring: RX ring to remove buffers from */ static int nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) { struct nfp_net_rx_buf *rxbufs; unsigned int i; if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) return 0; rxbufs = rx_ring->rxbufs; for (i = 0; i < rx_ring->cnt - 1; i++) { rxbufs[i].frag = nfp_net_rx_alloc_one(dp, &rxbufs[i].dma_addr); if (!rxbufs[i].frag) { nfp_net_rx_ring_bufs_free(dp, rx_ring); return -ENOMEM; } } return 0; } int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) { unsigned int r; dp->tx_rings = kcalloc(dp->num_tx_rings, sizeof(*dp->tx_rings), GFP_KERNEL); if (!dp->tx_rings) return -ENOMEM; if (dp->ctrl & NFP_NET_CFG_CTRL_TXRWB) { dp->txrwb = dma_alloc_coherent(dp->dev, dp->num_tx_rings * sizeof(u64), &dp->txrwb_dma, GFP_KERNEL); if (!dp->txrwb) goto err_free_rings; } for (r = 0; r < dp->num_tx_rings; r++) { int bias = 0; if (r >= dp->num_stack_tx_rings) bias = dp->num_stack_tx_rings; nfp_net_tx_ring_init(&dp->tx_rings[r], dp, &nn->r_vecs[r - bias], r, bias); if (nfp_net_tx_ring_alloc(dp, &dp->tx_rings[r])) goto err_free_prev; if (nfp_net_tx_ring_bufs_alloc(dp, &dp->tx_rings[r])) goto err_free_ring; } return 0; err_free_prev: while (r--) { nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); err_free_ring: nfp_net_tx_ring_free(dp, &dp->tx_rings[r]); } if (dp->txrwb) dma_free_coherent(dp->dev, dp->num_tx_rings * sizeof(u64), dp->txrwb, dp->txrwb_dma); err_free_rings: kfree(dp->tx_rings); return -ENOMEM; } void nfp_net_tx_rings_free(struct nfp_net_dp *dp) { unsigned int r; for (r = 0; r < dp->num_tx_rings; r++) { nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); nfp_net_tx_ring_free(dp, &dp->tx_rings[r]); } if (dp->txrwb) dma_free_coherent(dp->dev, dp->num_tx_rings * sizeof(u64), dp->txrwb, dp->txrwb_dma); kfree(dp->tx_rings); } /** * nfp_net_rx_ring_free() - Free resources allocated to a RX ring * @rx_ring: RX ring to free */ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net_dp *dp = &r_vec->nfp_net->dp; if (dp->netdev) xdp_rxq_info_unreg(&rx_ring->xdp_rxq); if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) kvfree(rx_ring->xsk_rxbufs); else kvfree(rx_ring->rxbufs); if (rx_ring->rxds) dma_free_coherent(dp->dev, rx_ring->size, rx_ring->rxds, rx_ring->dma); rx_ring->cnt = 0; rx_ring->rxbufs = NULL; rx_ring->xsk_rxbufs = NULL; rx_ring->rxds = NULL; rx_ring->dma = 0; rx_ring->size = 0; } /** * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring * @dp: NFP Net data path struct * @rx_ring: RX ring to allocate * * Return: 0 on success, negative errno otherwise. */ static int nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) { enum xdp_mem_type mem_type; size_t rxbuf_sw_desc_sz; int err; if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) { mem_type = MEM_TYPE_XSK_BUFF_POOL; rxbuf_sw_desc_sz = sizeof(*rx_ring->xsk_rxbufs); } else { mem_type = MEM_TYPE_PAGE_ORDER0; rxbuf_sw_desc_sz = sizeof(*rx_ring->rxbufs); } if (dp->netdev) { err = xdp_rxq_info_reg(&rx_ring->xdp_rxq, dp->netdev, rx_ring->idx, rx_ring->r_vec->napi.napi_id); if (err < 0) return err; err = xdp_rxq_info_reg_mem_model(&rx_ring->xdp_rxq, mem_type, NULL); if (err) goto err_alloc; } rx_ring->cnt = dp->rxd_cnt; rx_ring->size = array_size(rx_ring->cnt, sizeof(*rx_ring->rxds)); rx_ring->rxds = dma_alloc_coherent(dp->dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL | __GFP_NOWARN); if (!rx_ring->rxds) { netdev_warn(dp->netdev, "failed to allocate RX descriptor ring memory, requested descriptor count: %d, consider lowering descriptor count\n", rx_ring->cnt); goto err_alloc; } if (nfp_net_has_xsk_pool_slow(dp, rx_ring->idx)) { rx_ring->xsk_rxbufs = kvcalloc(rx_ring->cnt, rxbuf_sw_desc_sz, GFP_KERNEL); if (!rx_ring->xsk_rxbufs) goto err_alloc; } else { rx_ring->rxbufs = kvcalloc(rx_ring->cnt, rxbuf_sw_desc_sz, GFP_KERNEL); if (!rx_ring->rxbufs) goto err_alloc; } return 0; err_alloc: nfp_net_rx_ring_free(rx_ring); return -ENOMEM; } int nfp_net_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) { unsigned int r; dp->rx_rings = kcalloc(dp->num_rx_rings, sizeof(*dp->rx_rings), GFP_KERNEL); if (!dp->rx_rings) return -ENOMEM; for (r = 0; r < dp->num_rx_rings; r++) { nfp_net_rx_ring_init(&dp->rx_rings[r], &nn->r_vecs[r], r); if (nfp_net_rx_ring_alloc(dp, &dp->rx_rings[r])) goto err_free_prev; if (nfp_net_rx_ring_bufs_alloc(dp, &dp->rx_rings[r])) goto err_free_ring; } return 0; err_free_prev: while (r--) { nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); err_free_ring: nfp_net_rx_ring_free(&dp->rx_rings[r]); } kfree(dp->rx_rings); return -ENOMEM; } void nfp_net_rx_rings_free(struct nfp_net_dp *dp) { unsigned int r; for (r = 0; r < dp->num_rx_rings; r++) { nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); nfp_net_rx_ring_free(&dp->rx_rings[r]); } kfree(dp->rx_rings); } void nfp_net_rx_ring_hw_cfg_write(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, unsigned int idx) { /* Write the DMA address, size and MSI-X info to the device */ nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), rx_ring->dma); nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), ilog2(rx_ring->cnt)); nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), rx_ring->r_vec->irq_entry); } void nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring, unsigned int idx) { nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), tx_ring->dma); if (tx_ring->txrwb) { *tx_ring->txrwb = 0; nn_writeq(nn, NFP_NET_CFG_TXR_WB_ADDR(idx), nn->dp.txrwb_dma + idx * sizeof(u64)); } nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), ilog2(tx_ring->cnt)); nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), tx_ring->r_vec->irq_entry); } void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx) { nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(idx), 0); nn_writeb(nn, NFP_NET_CFG_RXR_SZ(idx), 0); nn_writeb(nn, NFP_NET_CFG_RXR_VEC(idx), 0); nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(idx), 0); nn_writeq(nn, NFP_NET_CFG_TXR_WB_ADDR(idx), 0); nn_writeb(nn, NFP_NET_CFG_TXR_SZ(idx), 0); nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), 0); } netdev_tx_t nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); return nn->dp.ops->xmit(skb, netdev); } bool __nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) { struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; return nn->dp.ops->ctrl_tx_one(nn, r_vec, skb, false); } bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb) { struct nfp_net_r_vector *r_vec = &nn->r_vecs[0]; bool ret; spin_lock_bh(&r_vec->lock); ret = nn->dp.ops->ctrl_tx_one(nn, r_vec, skb, false); spin_unlock_bh(&r_vec->lock); return ret; } bool nfp_net_vlan_strip(struct sk_buff *skb, const struct nfp_net_rx_desc *rxd, const struct nfp_meta_parsed *meta) { u16 tpid = 0, tci = 0; if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) { tpid = ETH_P_8021Q; tci = le16_to_cpu(rxd->rxd.vlan); } else if (meta->vlan.stripped) { if (meta->vlan.tpid == NFP_NET_VLAN_CTAG) tpid = ETH_P_8021Q; else if (meta->vlan.tpid == NFP_NET_VLAN_STAG) tpid = ETH_P_8021AD; else return false; tci = meta->vlan.tci; } if (tpid) __vlan_hwaccel_put_tag(skb, htons(tpid), tci); return true; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1