+/** DPDK callback for S/G 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_sg_pkt_burst(void *txq, struct rte_mbuf **tx_pkts,
+ uint16_t nb_pkts)
+{
+ struct mrvl_txq *q = txq;
+ struct mrvl_shadow_txq *sq;
+ struct pp2_hif *hif;
+ struct pp2_ppio_desc descs[nb_pkts * PP2_PPIO_DESC_NUM_FRAGS];
+ struct pp2_ppio_sg_pkts pkts;
+ uint8_t frags[nb_pkts];
+ unsigned int core_id = rte_lcore_id();
+ int i, j, ret, bytes_sent = 0;
+ int tail, tail_first;
+ uint16_t num, sq_free_size;
+ uint16_t nb_segs, total_descs = 0;
+ uint64_t addr;
+
+ hif = mrvl_get_hif(q->priv, core_id);
+ sq = &q->shadow_txqs[core_id];
+ pkts.frags = frags;
+ pkts.num = 0;
+
+ if (unlikely(!q->priv->ppio || !hif))
+ return 0;
+
+ if (sq->size)
+ mrvl_free_sent_buffers(q->priv->ppio, hif, core_id,
+ sq, q->queue_id, 0);
+
+ /* Save shadow queue free size */
+ sq_free_size = MRVL_PP2_TX_SHADOWQ_SIZE - sq->size - 1;
+
+ tail = 0;
+ for (i = 0; i < nb_pkts; i++) {
+ struct rte_mbuf *mbuf = tx_pkts[i];
+ struct rte_mbuf *seg = NULL;
+ int gen_l3_cksum, gen_l4_cksum;
+ enum pp2_outq_l3_type l3_type;
+ enum pp2_outq_l4_type l4_type;
+
+ nb_segs = mbuf->nb_segs;
+ tail_first = tail;
+ total_descs += nb_segs;
+
+ /*
+ * Check if total_descs does not exceed
+ * shadow queue free size
+ */
+ if (unlikely(total_descs > sq_free_size)) {
+ total_descs -= nb_segs;
+ RTE_LOG(DEBUG, PMD,
+ "No room in shadow queue for %d packets! "
+ "%d packets will be sent.\n",
+ nb_pkts, i);
+ break;
+ }
+
+ /* Check if nb_segs does not exceed the max nb of desc per
+ * fragmented packet
+ */
+ if (nb_segs > PP2_PPIO_DESC_NUM_FRAGS) {
+ total_descs -= nb_segs;
+ RTE_LOG(ERR, PMD,
+ "Too many segments. Packet won't be sent.\n");
+ break;
+ }
+
+ 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);
+ }
+
+ pkts.frags[pkts.num] = nb_segs;
+ pkts.num++;
+
+ seg = mbuf;
+ for (j = 0; j < nb_segs - 1; j++) {
+ /* For the subsequent segments, set shadow queue
+ * buffer to NULL
+ */
+ mrvl_fill_shadowq(sq, NULL);
+ mrvl_fill_desc(&descs[tail], seg);
+
+ tail++;
+ seg = seg->next;
+ }
+ /* Put first mbuf info in last shadow queue entry */
+ mrvl_fill_shadowq(sq, mbuf);
+ /* Update descriptor with last segment */
+ mrvl_fill_desc(&descs[tail++], seg);
+
+ bytes_sent += rte_pktmbuf_pkt_len(mbuf);
+ /* In case unsupported ol_flags were passed
+ * do not update descriptor offload information
+ */
+ ret = mrvl_prepare_proto_info(mbuf->ol_flags, mbuf->packet_type,
+ &l3_type, &l4_type, &gen_l3_cksum,
+ &gen_l4_cksum);
+ if (unlikely(ret))
+ continue;
+
+ pp2_ppio_outq_desc_set_proto_info(&descs[tail_first], l3_type,
+ l4_type, mbuf->l2_len,
+ mbuf->l2_len + mbuf->l3_len,
+ gen_l3_cksum, gen_l4_cksum);
+ }
+
+ num = total_descs;
+ pp2_ppio_send_sg(q->priv->ppio, hif, q->queue_id, descs,
+ &total_descs, &pkts);
+ /* number of packets that were not sent */
+ if (unlikely(num > total_descs)) {
+ for (i = total_descs; i < num; i++) {
+ sq->head = (MRVL_PP2_TX_SHADOWQ_SIZE + sq->head - 1) &
+ MRVL_PP2_TX_SHADOWQ_MASK;
+
+ addr = sq->ent[sq->head].buff.cookie;
+ if (addr)
+ bytes_sent -=
+ rte_pktmbuf_pkt_len((struct rte_mbuf *)
+ (cookie_addr_high | addr));
+ }
+ sq->size -= num - total_descs;
+ nb_pkts = pkts.num;
+ }
+
+ q->bytes_sent += bytes_sent;
+
+ return nb_pkts;
+}
+