Add rx/tx support. Signed-off-by: Jacek Siuda <j...@semihalf.com> Signed-off-by: Tomasz Duszynski <t...@semihalf.com> --- drivers/net/mrvl/mrvl_ethdev.c | 370 ++++++++++++++++++++++++++++++++++++++++- drivers/net/mrvl/mrvl_ethdev.h | 11 ++ 2 files changed, 380 insertions(+), 1 deletion(-)
diff --git a/drivers/net/mrvl/mrvl_ethdev.c b/drivers/net/mrvl/mrvl_ethdev.c index 1e730c6..c87d48e 100644 --- a/drivers/net/mrvl/mrvl_ethdev.c +++ b/drivers/net/mrvl/mrvl_ethdev.c @@ -70,6 +70,9 @@ #define MRVL_MUSDK_HIFS_MAX 9 #define MRVL_MAC_ADDRS_MAX 1 +/* prefetch shift */ +#define MRVL_MUSDK_PREFETCH_SHIFT 2 + #define MRVL_MATCH_LEN 16 #define MRVL_PKT_EFFEC_OFFS (MRVL_PKT_OFFS + MV_MH_SIZE) /* Maximum allowable packet size */ @@ -100,8 +103,29 @@ static int used_bpools[PP2_NUM_PKT_PROC] = { MRVL_MUSDK_BPOOLS_RESERVED }; +struct pp2_bpool *mrvl_port_to_bpool_lookup[RTE_MAX_ETHPORTS]; +int mrvl_port_bpool_size[PP2_NUM_PKT_PROC][PP2_BPOOL_NUM_POOLS][RTE_MAX_LCORE]; uint64_t cookie_addr_high = MRVL_COOKIE_ADDR_INVALID; +/* + * To use buffer harvesting based on loopback port shadow queue structure + * was introduced for buffers information bookkeeping. + * + * Before sending the packet, related buffer information (pp2_buff_inf) is + * stored in shadow queue. After packet is transmitted no longer used + * packet buffer is released back to it's original hardware pool, + * on condition it originated from interface. + * In case it was generated by application itself i.e: mbuf->port field is + * 0xff then its released to software mempool. + */ +struct mrvl_shadow_txq { + int head; /* write index - used when sending buffers */ + int tail; /* read index - used when releasing buffers */ + u16 size; /* queue occupied size */ + u16 num_to_release; /* number of buffers sent, that can be released */ + struct buff_release_entry ent[MRVL_PP2_TX_SHADOWQ_SIZE]; /* q entries */ +}; + struct mrvl_rxq { struct mrvl_priv *priv; struct rte_mempool *mp; @@ -115,8 +139,31 @@ struct mrvl_txq { int port_id; }; +/* + * Every tx queue should have dedicated shadow tx queue. + * + * Ports assigned by DPDK might not start at zero or be continuous so + * as a workaround define shadow queues for each possible port so that + * we eventually fit somewhere. + */ +struct mrvl_shadow_txq shadow_txqs[RTE_MAX_ETHPORTS][RTE_MAX_LCORE]; + /** Number of ports configured. */ int mrvl_ports_nb; +static int mrvl_lcore_first; +static int mrvl_lcore_last; + +static inline int +mrvl_get_bpool_size(int pp2_id, int pool_id) +{ + int i; + int size = 0; + + for (i = mrvl_lcore_first; i <= mrvl_lcore_last; i++) + size += mrvl_port_bpool_size[pp2_id][pool_id][i]; + + return size; +} static inline int mrvl_reserve_bit(int *bitmap, int max) @@ -326,6 +373,33 @@ mrvl_flush_rx_queues(struct rte_eth_dev *dev) } /** + * Flush transmit shadow queues. + * + * @param dev + * Pointer to Ethernet device structure. + */ +static void +mrvl_flush_tx_shadow_queues(struct rte_eth_dev *dev) +{ + int i; + + RTE_LOG(INFO, PMD, "Flushing tx shadow queues\n"); + for (i = 0; i < RTE_MAX_LCORE; i++) { + struct mrvl_shadow_txq *sq = + &shadow_txqs[dev->data->port_id][i]; + + while (sq->tail != sq->head) { + uint64_t addr = cookie_addr_high | + sq->ent[sq->tail].buff.cookie; + rte_pktmbuf_free((struct rte_mbuf *)addr); + sq->tail = (sq->tail + 1) & MRVL_PP2_TX_SHADOWQ_MASK; + } + + memset(sq, 0, sizeof(*sq)); + } +} + +/** * Flush hardware bpool (buffer-pool). * * @param dev @@ -371,6 +445,7 @@ mrvl_dev_stop(struct rte_eth_dev *dev) mrvl_dev_set_link_down(dev); mrvl_flush_rx_queues(dev); + mrvl_flush_tx_shadow_queues(dev); if (priv->qos_tbl) pp2_cls_qos_tbl_deinit(priv->qos_tbl); pp2_ppio_deinit(priv->ppio); @@ -543,6 +618,7 @@ mrvl_fill_bpool(struct mrvl_rxq *rxq, int num) } pp2_bpool_put_buffs(hif, entries, (uint16_t *)&i); + mrvl_port_bpool_size[bpool->pp2_id][bpool->id][core_id] += i; if (i != num) goto out; @@ -619,6 +695,7 @@ mrvl_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc, rxq->mp = mp; rxq->queue_id = idx; rxq->port_id = dev->data->port_id; + mrvl_port_to_bpool_lookup[rxq->port_id] = priv->bpool; tc = priv->rxq_map[rxq->queue_id].tc, inq = priv->rxq_map[rxq->queue_id].inq; @@ -750,6 +827,276 @@ static const struct eth_dev_ops mrvl_ops = { }; /** + * DPDK callback for receive. + * + * @param rxq + * Generic pointer to the receive queue. + * @param rx_pkts + * Array to store received packets. + * @param nb_pkts + * Maximum number of packets in array. + * + * @return + * Number of packets successfully received. + */ +static uint16_t +mrvl_rx_pkt_burst(void *rxq, struct rte_mbuf **rx_pkts, uint16_t nb_pkts) +{ + struct mrvl_rxq *q = rxq; + struct pp2_ppio_desc descs[nb_pkts]; + struct pp2_bpool *bpool; + int i, ret, rx_done = 0; + int num; + unsigned int core_id = rte_lcore_id(); + + if (unlikely(!q->priv->ppio)) + return 0; + + bpool = q->priv->bpool; + + ret = pp2_ppio_recv(q->priv->ppio, q->priv->rxq_map[q->queue_id].tc, + q->priv->rxq_map[q->queue_id].inq, descs, &nb_pkts); + if (unlikely(ret < 0)) { + RTE_LOG(ERR, PMD, "Failed to receive packets\n"); + return 0; + } + mrvl_port_bpool_size[bpool->pp2_id][bpool->id][core_id] -= nb_pkts; + + for (i = 0; i < nb_pkts; i++) { + struct rte_mbuf *mbuf; + enum pp2_inq_desc_status status; + uint64_t addr; + + if (likely(nb_pkts - i > MRVL_MUSDK_PREFETCH_SHIFT)) { + struct pp2_ppio_desc *pref_desc; + u64 pref_addr; + + pref_desc = &descs[i + MRVL_MUSDK_PREFETCH_SHIFT]; + pref_addr = cookie_addr_high | + pp2_ppio_inq_desc_get_cookie(pref_desc); + rte_mbuf_prefetch_part1((struct rte_mbuf *)(pref_addr)); + rte_mbuf_prefetch_part2((struct rte_mbuf *)(pref_addr)); + } + + addr = cookie_addr_high | + pp2_ppio_inq_desc_get_cookie(&descs[i]); + mbuf = (struct rte_mbuf *)addr; + rte_pktmbuf_reset(mbuf); + + /* drop packet in case of mac, overrun or resource error */ + status = pp2_ppio_inq_desc_get_l2_pkt_error(&descs[i]); + if (unlikely(status != PP2_DESC_ERR_OK)) { + struct pp2_buff_inf binf = { + .addr = rte_mbuf_data_dma_addr_default(mbuf), + .cookie = (pp2_cookie_t)(uint64_t)mbuf, + }; + + pp2_bpool_put_buff(hifs[core_id], bpool, &binf); + mrvl_port_bpool_size + [bpool->pp2_id][bpool->id][core_id]++; + continue; + } + + mbuf->data_off += MRVL_PKT_EFFEC_OFFS; + mbuf->pkt_len = pp2_ppio_inq_desc_get_pkt_len(&descs[i]); + mbuf->data_len = mbuf->pkt_len; + mbuf->port = q->port_id; + + rx_pkts[rx_done++] = mbuf; + } + + if (rte_spinlock_trylock(&q->priv->lock) == 1) { + num = mrvl_get_bpool_size(bpool->pp2_id, bpool->id); + + if (unlikely(num <= q->priv->bpool_min_size || + (!rx_done && num < q->priv->bpool_init_size))) { + ret = mrvl_fill_bpool(q, MRVL_BURST_SIZE); + if (ret) + RTE_LOG(ERR, PMD, "Failed to fill bpool\n"); + } else if (unlikely(num > q->priv->bpool_max_size)) { + int i; + int pkt_to_remove = num - q->priv->bpool_init_size; + struct rte_mbuf *mbuf; + struct pp2_buff_inf buff; + + RTE_LOG(DEBUG, PMD, + "\nport-%d:%d: bpool %d oversize - remove %d buffers (pool size: %d -> %d)\n", + bpool->pp2_id, q->priv->ppio->port_id, + bpool->id, pkt_to_remove, num, + q->priv->bpool_init_size); + + for (i = 0; i < pkt_to_remove; i++) { + pp2_bpool_get_buff(hifs[core_id], bpool, &buff); + mbuf = (struct rte_mbuf *) + (cookie_addr_high | buff.cookie); + rte_pktmbuf_free(mbuf); + } + mrvl_port_bpool_size + [bpool->pp2_id][bpool->id][core_id] -= + pkt_to_remove; + } + rte_spinlock_unlock(&q->priv->lock); + } + + return rx_done; +} + +/** + * Release already sent buffers to bpool (buffer-pool). + * + * @param ppio + * Pointer to the port structure. + * @param hif + * Pointer to the MUSDK hardware interface. + * @param sq + * Pointer to the shadow queue. + * @param qid + * Queue id number. + * @param force + * Force releasing packets. + */ +static inline void +mrvl_free_sent_buffers(struct pp2_ppio *ppio, struct pp2_hif *hif, + struct mrvl_shadow_txq *sq, int qid, int force) +{ + struct buff_release_entry *entry; + uint16_t nb_done = 0, num = 0, skip_bufs = 0; + int i, core_id = rte_lcore_id(); + + pp2_ppio_get_num_outq_done(ppio, hif, qid, &nb_done); + + sq->num_to_release += nb_done; + + if (likely(!force && + sq->num_to_release < MRVL_PP2_BUF_RELEASE_BURST_SIZE)) + return; + + nb_done = sq->num_to_release; + sq->num_to_release = 0; + + for (i = 0; i < nb_done; i++) { + entry = &sq->ent[sq->tail + num]; + if (unlikely(!entry->buff.addr)) { + RTE_LOG(ERR, PMD, + "Shadow memory @%d: cookie(%lx), pa(%lx)!\n", + sq->tail, (u64)entry->buff.cookie, + (u64)entry->buff.addr); + skip_bufs = 1; + goto skip; + } + + if (unlikely(!entry->bpool)) { + struct rte_mbuf *mbuf; + + mbuf = (struct rte_mbuf *) + (cookie_addr_high | entry->buff.cookie); + rte_pktmbuf_free(mbuf); + skip_bufs = 1; + goto skip; + } + + mrvl_port_bpool_size + [entry->bpool->pp2_id][entry->bpool->id][core_id]++; + num++; + if (unlikely(sq->tail + num == MRVL_PP2_TX_SHADOWQ_SIZE)) + goto skip; + continue; +skip: + if (likely(num)) + pp2_bpool_put_buffs(hif, &sq->ent[sq->tail], &num); + num += skip_bufs; + sq->tail = (sq->tail + num) & MRVL_PP2_TX_SHADOWQ_MASK; + sq->size -= num; + num = 0; + } + + if (likely(num)) { + pp2_bpool_put_buffs(hif, &sq->ent[sq->tail], &num); + sq->tail = (sq->tail + num) & MRVL_PP2_TX_SHADOWQ_MASK; + sq->size -= num; + } +} + +/** + * DPDK callback for transmit. + * + * @param txq + * Generic pointer transmit queue. + * @param tx_pkts + * Packets to transmit. + * @param nb_pkts + * Number of packets in array. + * + * @return + * Number of packets successfully transmitted. + */ +static uint16_t +mrvl_tx_pkt_burst(void *txq, struct rte_mbuf **tx_pkts, uint16_t nb_pkts) +{ + struct mrvl_txq *q = txq; + struct mrvl_shadow_txq *sq = &shadow_txqs[q->port_id][rte_lcore_id()]; + struct pp2_hif *hif = hifs[rte_lcore_id()]; + struct pp2_ppio_desc descs[nb_pkts]; + int i; + uint16_t num, sq_free_size; + + if (unlikely(!q->priv->ppio)) + return 0; + + if (sq->size) + mrvl_free_sent_buffers(q->priv->ppio, hif, sq, q->queue_id, 0); + + sq_free_size = MRVL_PP2_TX_SHADOWQ_SIZE - sq->size - 1; + if (unlikely(nb_pkts > sq_free_size)) { + RTE_LOG(DEBUG, PMD, + "No room in shadow queue for %d packets! %d packets will be sent.\n", + nb_pkts, sq_free_size); + nb_pkts = sq_free_size; + } + + for (i = 0; i < nb_pkts; i++) { + struct rte_mbuf *mbuf = tx_pkts[i]; + + if (likely(nb_pkts - i > MRVL_MUSDK_PREFETCH_SHIFT)) { + struct rte_mbuf *pref_pkt_hdr; + + pref_pkt_hdr = tx_pkts[i + MRVL_MUSDK_PREFETCH_SHIFT]; + rte_mbuf_prefetch_part1(pref_pkt_hdr); + rte_mbuf_prefetch_part2(pref_pkt_hdr); + } + + sq->ent[sq->head].buff.cookie = (pp2_cookie_t)(uint64_t)mbuf; + sq->ent[sq->head].buff.addr = + rte_mbuf_data_dma_addr_default(mbuf); + sq->ent[sq->head].bpool = + (unlikely(mbuf->port == 0xff || mbuf->refcnt > 1)) ? + NULL : mrvl_port_to_bpool_lookup[mbuf->port]; + sq->head = (sq->head + 1) & MRVL_PP2_TX_SHADOWQ_MASK; + sq->size++; + + pp2_ppio_outq_desc_reset(&descs[i]); + pp2_ppio_outq_desc_set_phys_addr(&descs[i], + rte_pktmbuf_mtophys(mbuf)); + pp2_ppio_outq_desc_set_pkt_offset(&descs[i], 0); + pp2_ppio_outq_desc_set_pkt_len(&descs[i], + rte_pktmbuf_pkt_len(mbuf)); + } + + num = nb_pkts; + pp2_ppio_send(q->priv->ppio, hif, q->queue_id, descs, &nb_pkts); + /* number of packets that were not sent */ + if (unlikely(num > nb_pkts)) { + for (i = nb_pkts; i < num; i++) { + sq->head = (MRVL_PP2_TX_SHADOWQ_SIZE + sq->head - 1) & + MRVL_PP2_TX_SHADOWQ_MASK; + } + sq->size -= num - nb_pkts; + } + + return nb_pkts; +} + +/** * Initialize packet processor. * * @return @@ -821,6 +1168,7 @@ mrvl_priv_create(const char *dev_name) goto out_clear_bpool_bit; priv->ppio_params.type = PP2_PPIO_T_NIC; + rte_spinlock_init(&priv->lock); return priv; out_clear_bpool_bit: @@ -875,6 +1223,8 @@ mrvl_eth_dev_create(struct rte_vdev_device *vdev, const char *name) memcpy(eth_dev->data->mac_addrs[0].addr_bytes, req.ifr_addr.sa_data, ETHER_ADDR_LEN); + eth_dev->rx_pkt_burst = mrvl_rx_pkt_burst; + eth_dev->tx_pkt_burst = mrvl_tx_pkt_burst; eth_dev->data->dev_private = priv; eth_dev->device = &vdev->device; eth_dev->dev_ops = &mrvl_ops; @@ -984,6 +1334,15 @@ mrvl_deinit_hifs(void) } } +static void mrvl_set_first_last_cores(int core_id) +{ + if (core_id < mrvl_lcore_first) + mrvl_lcore_first = core_id; + + if (core_id > mrvl_lcore_last) + mrvl_lcore_last = core_id; +} + /** * DPDK callback to register the virtual device. * @@ -999,7 +1358,7 @@ rte_pmd_mrvl_probe(struct rte_vdev_device *vdev) struct rte_kvargs *kvlist; const char *ifnames[PP2_NUM_ETH_PPIO * PP2_NUM_PKT_PROC]; int ret = -EINVAL; - uint32_t i, ifnum, cfgnum; + uint32_t i, ifnum, cfgnum, core_id; const char *params; params = rte_vdev_device_args(vdev); @@ -1053,6 +1412,15 @@ rte_pmd_mrvl_probe(struct rte_vdev_device *vdev) rte_kvargs_free(kvlist); + memset(mrvl_port_bpool_size, 0, sizeof(mrvl_port_bpool_size)); + + mrvl_lcore_first = RTE_MAX_LCORE; + mrvl_lcore_last = 0; + + RTE_LCORE_FOREACH(core_id) { + mrvl_set_first_last_cores(core_id); + } + return 0; out_cleanup: for (; i > 0; i--) diff --git a/drivers/net/mrvl/mrvl_ethdev.h b/drivers/net/mrvl/mrvl_ethdev.h index 626cfaa..da33d05 100644 --- a/drivers/net/mrvl/mrvl_ethdev.h +++ b/drivers/net/mrvl/mrvl_ethdev.h @@ -33,6 +33,7 @@ #ifndef _MRVL_ETHDEV_H_ #define _MRVL_ETHDEV_H_ +#include <rte_spinlock.h> #include <drivers/mv_pp2_cls.h> #include <drivers/mv_pp2_ppio.h> @@ -69,10 +70,20 @@ /** Packet offset inside RX buffer. */ #define MRVL_PKT_OFFS 64 +/** Maximum number of descriptors in shadow queue. Must be power of 2 */ +#define MRVL_PP2_TX_SHADOWQ_SIZE MRVL_PP2_TXD_MAX + +/** Shadow queue size mask (since shadow queue size is power of 2) */ +#define MRVL_PP2_TX_SHADOWQ_MASK (MRVL_PP2_TX_SHADOWQ_SIZE - 1) + +/** Minimum number of sent buffers to release from shadow queue to BM */ +#define MRVL_PP2_BUF_RELEASE_BURST_SIZE 64 + struct mrvl_priv { /* Hot fields, used in fast path. */ struct pp2_bpool *bpool; /**< BPool pointer */ struct pp2_ppio *ppio; /**< Port handler pointer */ + rte_spinlock_t lock; /**< Spinlock for checking bpool status */ uint16_t bpool_max_size; /**< BPool maximum size */ uint16_t bpool_min_size; /**< BPool minimum size */ uint16_t bpool_init_size; /**< Configured BPool size */ -- 2.7.4