+static int
+nicvf_qset_sq_alloc(struct rte_eth_dev *dev, struct nicvf *nic,
+ struct nicvf_txq *sq, uint16_t qidx, uint32_t desc_cnt)
+{
+ const struct rte_memzone *rz;
+ uint32_t ring_size = SND_QUEUE_SZ_MAX * sizeof(union sq_entry_t);
+
+ rz = rte_eth_dma_zone_reserve(dev, "sq",
+ nicvf_netdev_qidx(nic, qidx), ring_size,
+ NICVF_SQ_BASE_ALIGN_BYTES, nic->node);
+ if (rz == NULL) {
+ PMD_INIT_LOG(ERR, "Failed allocate mem for sq hw ring");
+ return -ENOMEM;
+ }
+
+ memset(rz->addr, 0, ring_size);
+
+ sq->phys = rz->iova;
+ sq->desc = rz->addr;
+ sq->qlen_mask = desc_cnt - 1;
+
+ return 0;
+}
+
+static int
+nicvf_qset_rbdr_alloc(struct rte_eth_dev *dev, struct nicvf *nic,
+ uint32_t desc_cnt, uint32_t buffsz)
+{
+ struct nicvf_rbdr *rbdr;
+ const struct rte_memzone *rz;
+ uint32_t ring_size;
+
+ assert(nic->rbdr == NULL);
+ rbdr = rte_zmalloc_socket("rbdr", sizeof(struct nicvf_rbdr),
+ RTE_CACHE_LINE_SIZE, nic->node);
+ if (rbdr == NULL) {
+ PMD_INIT_LOG(ERR, "Failed to allocate mem for rbdr");
+ return -ENOMEM;
+ }
+
+ ring_size = sizeof(struct rbdr_entry_t) * RBDR_QUEUE_SZ_MAX;
+ rz = rte_eth_dma_zone_reserve(dev, "rbdr",
+ nicvf_netdev_qidx(nic, 0), ring_size,
+ NICVF_RBDR_BASE_ALIGN_BYTES, nic->node);
+ if (rz == NULL) {
+ PMD_INIT_LOG(ERR, "Failed to allocate mem for rbdr desc ring");
+ return -ENOMEM;
+ }
+
+ memset(rz->addr, 0, ring_size);
+
+ rbdr->phys = rz->iova;
+ rbdr->tail = 0;
+ rbdr->next_tail = 0;
+ rbdr->desc = rz->addr;
+ rbdr->buffsz = buffsz;
+ rbdr->qlen_mask = desc_cnt - 1;
+ rbdr->rbdr_status =
+ nicvf_qset_base(nic, 0) + NIC_QSET_RBDR_0_1_STATUS0;
+ rbdr->rbdr_door =
+ nicvf_qset_base(nic, 0) + NIC_QSET_RBDR_0_1_DOOR;
+
+ nic->rbdr = rbdr;
+ return 0;
+}
+
+static void
+nicvf_rbdr_release_mbuf(struct rte_eth_dev *dev, struct nicvf *nic,
+ nicvf_iova_addr_t phy)
+{
+ uint16_t qidx;
+ void *obj;
+ struct nicvf_rxq *rxq;
+ uint16_t rx_start, rx_end;
+
+ /* Get queue ranges for this VF */
+ nicvf_rx_range(dev, nic, &rx_start, &rx_end);
+
+ for (qidx = rx_start; qidx <= rx_end; qidx++) {
+ rxq = dev->data->rx_queues[qidx];
+ if (rxq->precharge_cnt) {
+ obj = (void *)nicvf_mbuff_phy2virt(phy,
+ rxq->mbuf_phys_off);
+ rte_mempool_put(rxq->pool, obj);
+ rxq->precharge_cnt--;
+ break;
+ }
+ }
+}
+
+static inline void
+nicvf_rbdr_release_mbufs(struct rte_eth_dev *dev, struct nicvf *nic)
+{
+ uint32_t qlen_mask, head;
+ struct rbdr_entry_t *entry;
+ struct nicvf_rbdr *rbdr = nic->rbdr;
+
+ qlen_mask = rbdr->qlen_mask;
+ head = rbdr->head;
+ while (head != rbdr->tail) {
+ entry = rbdr->desc + head;
+ nicvf_rbdr_release_mbuf(dev, nic, entry->full_addr);
+ head++;
+ head = head & qlen_mask;
+ }
+}
+
+static inline void
+nicvf_tx_queue_release_mbufs(struct nicvf_txq *txq)
+{
+ uint32_t head;
+
+ head = txq->head;
+ while (head != txq->tail) {
+ if (txq->txbuffs[head]) {
+ rte_pktmbuf_free_seg(txq->txbuffs[head]);
+ txq->txbuffs[head] = NULL;
+ }
+ head++;
+ head = head & txq->qlen_mask;
+ }
+}
+
+static void
+nicvf_tx_queue_reset(struct nicvf_txq *txq)
+{
+ uint32_t txq_desc_cnt = txq->qlen_mask + 1;
+
+ memset(txq->desc, 0, sizeof(union sq_entry_t) * txq_desc_cnt);
+ memset(txq->txbuffs, 0, sizeof(struct rte_mbuf *) * txq_desc_cnt);
+ txq->tail = 0;
+ txq->head = 0;
+ txq->xmit_bufs = 0;
+}
+
+static inline int
+nicvf_vf_start_tx_queue(struct rte_eth_dev *dev, struct nicvf *nic,
+ uint16_t qidx)
+{
+ struct nicvf_txq *txq;
+ int ret;
+
+ assert(qidx < MAX_SND_QUEUES_PER_QS);
+
+ if (dev->data->tx_queue_state[nicvf_netdev_qidx(nic, qidx)] ==
+ RTE_ETH_QUEUE_STATE_STARTED)
+ return 0;
+
+ txq = dev->data->tx_queues[nicvf_netdev_qidx(nic, qidx)];
+ txq->pool = NULL;
+ ret = nicvf_qset_sq_config(nic, qidx, txq);
+ if (ret) {
+ PMD_INIT_LOG(ERR, "Failed to configure sq VF%d %d %d",
+ nic->vf_id, qidx, ret);
+ goto config_sq_error;
+ }
+
+ dev->data->tx_queue_state[nicvf_netdev_qidx(nic, qidx)] =
+ RTE_ETH_QUEUE_STATE_STARTED;
+ return ret;
+
+config_sq_error:
+ nicvf_qset_sq_reclaim(nic, qidx);
+ return ret;
+}
+
+static inline int
+nicvf_vf_stop_tx_queue(struct rte_eth_dev *dev, struct nicvf *nic,
+ uint16_t qidx)
+{
+ struct nicvf_txq *txq;
+ int ret;
+
+ assert(qidx < MAX_SND_QUEUES_PER_QS);
+
+ if (dev->data->tx_queue_state[nicvf_netdev_qidx(nic, qidx)] ==
+ RTE_ETH_QUEUE_STATE_STOPPED)
+ return 0;
+
+ ret = nicvf_qset_sq_reclaim(nic, qidx);
+ if (ret)
+ PMD_INIT_LOG(ERR, "Failed to reclaim sq VF%d %d %d",
+ nic->vf_id, qidx, ret);
+
+ txq = dev->data->tx_queues[nicvf_netdev_qidx(nic, qidx)];
+ nicvf_tx_queue_release_mbufs(txq);
+ nicvf_tx_queue_reset(txq);
+
+ dev->data->tx_queue_state[nicvf_netdev_qidx(nic, qidx)] =
+ RTE_ETH_QUEUE_STATE_STOPPED;
+ return ret;
+}
+
+static inline int
+nicvf_configure_cpi(struct rte_eth_dev *dev)
+{
+ struct nicvf *nic = nicvf_pmd_priv(dev);
+ uint16_t qidx, qcnt;
+ int ret;
+
+ /* Count started rx queues */
+ for (qidx = qcnt = 0; qidx < dev->data->nb_rx_queues; qidx++)
+ if (dev->data->rx_queue_state[qidx] ==
+ RTE_ETH_QUEUE_STATE_STARTED)
+ qcnt++;
+
+ nic->cpi_alg = CPI_ALG_NONE;
+ ret = nicvf_mbox_config_cpi(nic, qcnt);
+ if (ret)
+ PMD_INIT_LOG(ERR, "Failed to configure CPI %d", ret);
+
+ return ret;
+}
+
+static inline int
+nicvf_configure_rss(struct rte_eth_dev *dev)
+{
+ struct nicvf *nic = nicvf_pmd_priv(dev);
+ uint64_t rsshf;
+ int ret = -EINVAL;
+
+ rsshf = nicvf_rss_ethdev_to_nic(nic,
+ dev->data->dev_conf.rx_adv_conf.rss_conf.rss_hf);
+ PMD_DRV_LOG(INFO, "mode=%d rx_queues=%d loopback=%d rsshf=0x%" PRIx64,
+ dev->data->dev_conf.rxmode.mq_mode,
+ dev->data->nb_rx_queues,
+ dev->data->dev_conf.lpbk_mode, rsshf);
+
+ if (dev->data->dev_conf.rxmode.mq_mode == ETH_MQ_RX_NONE)
+ ret = nicvf_rss_term(nic);
+ else if (dev->data->dev_conf.rxmode.mq_mode == ETH_MQ_RX_RSS)
+ ret = nicvf_rss_config(nic, dev->data->nb_rx_queues, rsshf);
+ if (ret)
+ PMD_INIT_LOG(ERR, "Failed to configure RSS %d", ret);
+
+ return ret;
+}
+
+static int
+nicvf_configure_rss_reta(struct rte_eth_dev *dev)
+{
+ struct nicvf *nic = nicvf_pmd_priv(dev);
+ unsigned int idx, qmap_size;
+ uint8_t qmap[RTE_MAX_QUEUES_PER_PORT];
+ uint8_t default_reta[NIC_MAX_RSS_IDR_TBL_SIZE];
+
+ if (nic->cpi_alg != CPI_ALG_NONE)
+ return -EINVAL;
+
+ /* Prepare queue map */
+ for (idx = 0, qmap_size = 0; idx < dev->data->nb_rx_queues; idx++) {
+ if (dev->data->rx_queue_state[idx] ==
+ RTE_ETH_QUEUE_STATE_STARTED)
+ qmap[qmap_size++] = idx;
+ }
+
+ /* Update default RSS RETA */
+ for (idx = 0; idx < NIC_MAX_RSS_IDR_TBL_SIZE; idx++)
+ default_reta[idx] = qmap[idx % qmap_size];
+
+ return nicvf_rss_reta_update(nic, default_reta,
+ NIC_MAX_RSS_IDR_TBL_SIZE);
+}
+
+static void
+nicvf_dev_tx_queue_release(void *sq)
+{
+ struct nicvf_txq *txq;
+
+ PMD_INIT_FUNC_TRACE();
+
+ txq = (struct nicvf_txq *)sq;
+ if (txq) {
+ if (txq->txbuffs != NULL) {
+ nicvf_tx_queue_release_mbufs(txq);
+ rte_free(txq->txbuffs);
+ txq->txbuffs = NULL;
+ }
+ rte_free(txq);
+ }
+}
+
+static void
+nicvf_set_tx_function(struct rte_eth_dev *dev)
+{
+ struct nicvf_txq *txq = NULL;
+ size_t i;
+ bool multiseg = false;
+
+ for (i = 0; i < dev->data->nb_tx_queues; i++) {
+ txq = dev->data->tx_queues[i];
+ if (txq->offloads & DEV_TX_OFFLOAD_MULTI_SEGS) {
+ multiseg = true;
+ break;
+ }
+ }
+
+ /* Use a simple Tx queue (no offloads, no multi segs) if possible */
+ if (multiseg) {
+ PMD_DRV_LOG(DEBUG, "Using multi-segment tx callback");
+ dev->tx_pkt_burst = nicvf_xmit_pkts_multiseg;
+ } else {
+ PMD_DRV_LOG(DEBUG, "Using single-segment tx callback");
+ dev->tx_pkt_burst = nicvf_xmit_pkts;
+ }
+
+ if (!txq)
+ return;
+
+ if (txq->pool_free == nicvf_single_pool_free_xmited_buffers)
+ PMD_DRV_LOG(DEBUG, "Using single-mempool tx free method");
+ else
+ PMD_DRV_LOG(DEBUG, "Using multi-mempool tx free method");
+}
+
+static void
+nicvf_set_rx_function(struct rte_eth_dev *dev)
+{
+ struct nicvf *nic = nicvf_pmd_priv(dev);
+
+ const eth_rx_burst_t rx_burst_func[2][2][2] = {
+ /* [NORMAL/SCATTER] [CKSUM/NO_CKSUM] [VLAN_STRIP/NO_VLAN_STRIP] */
+ [0][0][0] = nicvf_recv_pkts_no_offload,
+ [0][0][1] = nicvf_recv_pkts_vlan_strip,
+ [0][1][0] = nicvf_recv_pkts_cksum,
+ [0][1][1] = nicvf_recv_pkts_cksum_vlan_strip,
+ [1][0][0] = nicvf_recv_pkts_multiseg_no_offload,
+ [1][0][1] = nicvf_recv_pkts_multiseg_vlan_strip,
+ [1][1][0] = nicvf_recv_pkts_multiseg_cksum,
+ [1][1][1] = nicvf_recv_pkts_multiseg_cksum_vlan_strip,
+ };
+
+ dev->rx_pkt_burst =
+ rx_burst_func[dev->data->scattered_rx]
+ [nic->offload_cksum][nic->vlan_strip];
+}
+
+static int
+nicvf_dev_tx_queue_setup(struct rte_eth_dev *dev, uint16_t qidx,
+ uint16_t nb_desc, unsigned int socket_id,
+ const struct rte_eth_txconf *tx_conf)
+{
+ uint16_t tx_free_thresh;
+ bool is_single_pool;
+ struct nicvf_txq *txq;
+ struct nicvf *nic = nicvf_pmd_priv(dev);
+ uint64_t offloads;
+
+ PMD_INIT_FUNC_TRACE();
+
+ if (qidx >= MAX_SND_QUEUES_PER_QS)
+ nic = nic->snicvf[qidx / MAX_SND_QUEUES_PER_QS - 1];
+
+ qidx = qidx % MAX_SND_QUEUES_PER_QS;
+
+ /* Socket id check */
+ if (socket_id != (unsigned int)SOCKET_ID_ANY && socket_id != nic->node)
+ PMD_DRV_LOG(WARNING, "socket_id expected %d, configured %d",
+ socket_id, nic->node);
+
+ /* Tx deferred start is not supported */
+ if (tx_conf->tx_deferred_start) {
+ PMD_INIT_LOG(ERR, "Tx deferred start not supported");
+ return -EINVAL;
+ }
+
+ /* Roundup nb_desc to available qsize and validate max number of desc */
+ nb_desc = nicvf_qsize_sq_roundup(nb_desc);
+ if (nb_desc == 0) {
+ PMD_INIT_LOG(ERR, "Value of nb_desc beyond available sq qsize");
+ return -EINVAL;
+ }
+
+ /* Validate tx_free_thresh */
+ tx_free_thresh = (uint16_t)((tx_conf->tx_free_thresh) ?
+ tx_conf->tx_free_thresh :
+ NICVF_DEFAULT_TX_FREE_THRESH);
+
+ if (tx_free_thresh > (nb_desc) ||
+ tx_free_thresh > NICVF_MAX_TX_FREE_THRESH) {
+ PMD_INIT_LOG(ERR,
+ "tx_free_thresh must be less than the number of TX "
+ "descriptors. (tx_free_thresh=%u port=%d "
+ "queue=%d)", (unsigned int)tx_free_thresh,
+ (int)dev->data->port_id, (int)qidx);
+ return -EINVAL;
+ }
+
+ /* Free memory prior to re-allocation if needed. */
+ if (dev->data->tx_queues[nicvf_netdev_qidx(nic, qidx)] != NULL) {
+ PMD_TX_LOG(DEBUG, "Freeing memory prior to re-allocation %d",
+ nicvf_netdev_qidx(nic, qidx));
+ nicvf_dev_tx_queue_release(
+ dev->data->tx_queues[nicvf_netdev_qidx(nic, qidx)]);
+ dev->data->tx_queues[nicvf_netdev_qidx(nic, qidx)] = NULL;
+ }
+
+ /* Allocating tx queue data structure */
+ txq = rte_zmalloc_socket("ethdev TX queue", sizeof(struct nicvf_txq),
+ RTE_CACHE_LINE_SIZE, nic->node);
+ if (txq == NULL) {
+ PMD_INIT_LOG(ERR, "Failed to allocate txq=%d",
+ nicvf_netdev_qidx(nic, qidx));
+ return -ENOMEM;
+ }
+
+ txq->nic = nic;
+ txq->queue_id = qidx;
+ txq->tx_free_thresh = tx_free_thresh;
+ txq->sq_head = nicvf_qset_base(nic, qidx) + NIC_QSET_SQ_0_7_HEAD;
+ txq->sq_door = nicvf_qset_base(nic, qidx) + NIC_QSET_SQ_0_7_DOOR;
+ offloads = tx_conf->offloads | dev->data->dev_conf.txmode.offloads;
+ txq->offloads = offloads;
+
+ is_single_pool = !!(offloads & DEV_TX_OFFLOAD_MBUF_FAST_FREE);
+
+ /* Choose optimum free threshold value for multipool case */
+ if (!is_single_pool) {
+ txq->tx_free_thresh = (uint16_t)
+ (tx_conf->tx_free_thresh == NICVF_DEFAULT_TX_FREE_THRESH ?
+ NICVF_TX_FREE_MPOOL_THRESH :
+ tx_conf->tx_free_thresh);
+ txq->pool_free = nicvf_multi_pool_free_xmited_buffers;
+ } else {
+ txq->pool_free = nicvf_single_pool_free_xmited_buffers;
+ }
+
+ /* Allocate software ring */
+ txq->txbuffs = rte_zmalloc_socket("txq->txbuffs",
+ nb_desc * sizeof(struct rte_mbuf *),
+ RTE_CACHE_LINE_SIZE, nic->node);
+
+ if (txq->txbuffs == NULL) {
+ nicvf_dev_tx_queue_release(txq);
+ return -ENOMEM;
+ }
+
+ if (nicvf_qset_sq_alloc(dev, nic, txq, qidx, nb_desc)) {
+ PMD_INIT_LOG(ERR, "Failed to allocate mem for sq %d", qidx);
+ nicvf_dev_tx_queue_release(txq);
+ return -ENOMEM;
+ }
+
+ nicvf_tx_queue_reset(txq);
+
+ PMD_INIT_LOG(DEBUG, "[%d] txq=%p nb_desc=%d desc=%p"
+ " phys=0x%" PRIx64 " offloads=0x%" PRIx64,
+ nicvf_netdev_qidx(nic, qidx), txq, nb_desc, txq->desc,
+ txq->phys, txq->offloads);
+
+ dev->data->tx_queues[nicvf_netdev_qidx(nic, qidx)] = txq;
+ dev->data->tx_queue_state[nicvf_netdev_qidx(nic, qidx)] =
+ RTE_ETH_QUEUE_STATE_STOPPED;
+ return 0;
+}
+
+static inline void
+nicvf_rx_queue_release_mbufs(struct rte_eth_dev *dev, struct nicvf_rxq *rxq)
+{
+ uint32_t rxq_cnt;
+ uint32_t nb_pkts, released_pkts = 0;
+ uint32_t refill_cnt = 0;
+ struct rte_mbuf *rx_pkts[NICVF_MAX_RX_FREE_THRESH];
+
+ if (dev->rx_pkt_burst == NULL)
+ return;
+
+ while ((rxq_cnt = nicvf_dev_rx_queue_count(dev,
+ nicvf_netdev_qidx(rxq->nic, rxq->queue_id)))) {
+ nb_pkts = dev->rx_pkt_burst(rxq, rx_pkts,
+ NICVF_MAX_RX_FREE_THRESH);
+ PMD_DRV_LOG(INFO, "nb_pkts=%d rxq_cnt=%d", nb_pkts, rxq_cnt);
+ while (nb_pkts) {
+ rte_pktmbuf_free_seg(rx_pkts[--nb_pkts]);
+ released_pkts++;
+ }
+ }
+
+
+ refill_cnt += nicvf_dev_rbdr_refill(dev,
+ nicvf_netdev_qidx(rxq->nic, rxq->queue_id));
+
+ PMD_DRV_LOG(INFO, "free_cnt=%d refill_cnt=%d",
+ released_pkts, refill_cnt);
+}
+