src/dpdk/drivers/net/txgbe/txgbe_rxtx.c
static inline uint16_t
txgbe_recv_pkts_lro(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts,
		    bool bulk_alloc)
{
	struct txgbe_rx_queue *rxq = rx_queue;
	struct rte_eth_dev *dev = &rte_eth_devices[rxq->port_id];
	volatile struct txgbe_rx_desc *rx_ring = rxq->rx_ring;
	struct txgbe_rx_entry *sw_ring = rxq->sw_ring;
	struct txgbe_scattered_rx_entry *sw_sc_ring = rxq->sw_sc_ring;
	uint16_t rx_id = rxq->rx_tail;
	uint16_t nb_rx = 0;
	uint16_t nb_hold = rxq->nb_rx_hold;
	uint16_t prev_id = rxq->rx_tail;

	while (nb_rx < nb_pkts) {
		bool eop;
		struct txgbe_rx_entry *rxe;
		struct txgbe_scattered_rx_entry *sc_entry;
		struct txgbe_scattered_rx_entry *next_sc_entry = NULL;
		struct txgbe_rx_entry *next_rxe = NULL;
		struct rte_mbuf *first_seg;
		struct rte_mbuf *rxm;
		struct rte_mbuf *nmb = NULL;
		struct txgbe_rx_desc rxd;
		uint16_t data_len;
		uint16_t next_id;
		volatile struct txgbe_rx_desc *rxdp;
		uint32_t staterr;

next_desc:
		/*
		 * The code in this whole file uses the volatile pointer to
		 * ensure the read ordering of the status and the rest of the
		 * descriptor fields (on the compiler level only!!!). This is so
		 * UGLY - why not to just use the compiler barrier instead? DPDK
		 * even has the rte_compiler_barrier() for that.
		 *
		 * But most importantly this is just wrong because this doesn't
		 * ensure memory ordering in a general case at all. For
		 * instance, DPDK is supposed to work on Power CPUs where
		 * compiler barrier may just not be enough!
		 *
		 * I tried to write only this function properly to have a
		 * starting point (as a part of an LRO/RSC series) but the
		 * compiler cursed at me when I tried to cast away the
		 * "volatile" from rx_ring (yes, it's volatile too!!!). So, I'm
		 * keeping it the way it is for now.
		 *
		 * The code in this file is broken in so many other places and
		 * will just not work on a big endian CPU anyway therefore the
		 * lines below will have to be revisited together with the rest
		 * of the txgbe PMD.
		 *
		 * TODO:
		 *    - Get rid of "volatile" and let the compiler do its job.
		 *    - Use the proper memory barrier (rte_rmb()) to ensure the
		 *      memory ordering below.
		 */
		rxdp = &rx_ring[rx_id];
		staterr = rte_le_to_cpu_32(rxdp->qw1.lo.status);

		if (!(staterr & TXGBE_RXD_STAT_DD))
			break;

		rxd = *rxdp;

		PMD_RX_LOG(DEBUG, "port_id=%u queue_id=%u rx_id=%u "
				  "staterr=0x%x data_len=%u",
			   rxq->port_id, rxq->queue_id, rx_id, staterr,
			   rte_le_to_cpu_16(rxd.qw1.hi.len));

		if (!bulk_alloc) {
			nmb = rte_mbuf_raw_alloc(rxq->mb_pool);
			if (nmb == NULL) {
				PMD_RX_LOG(DEBUG, "RX mbuf alloc failed "
						  "port_id=%u queue_id=%u",
					   rxq->port_id, rxq->queue_id);

				dev->data->rx_mbuf_alloc_failed++;
				break;
			}
		} else if (nb_hold > rxq->rx_free_thresh) {
			uint16_t next_rdt = rxq->rx_free_trigger;

			if (!txgbe_rx_alloc_bufs(rxq, false)) {
				rte_wmb();
				txgbe_set32_relaxed(rxq->rdt_reg_addr,
							    next_rdt);
				nb_hold -= rxq->rx_free_thresh;
			} else {
				PMD_RX_LOG(DEBUG, "RX bulk alloc failed "
						  "port_id=%u queue_id=%u",
					   rxq->port_id, rxq->queue_id);

				dev->data->rx_mbuf_alloc_failed++;
				break;
			}
		}

		nb_hold++;
		rxe = &sw_ring[rx_id];
		eop = staterr & TXGBE_RXD_STAT_EOP;

		next_id = rx_id + 1;
		if (next_id == rxq->nb_rx_desc)
			next_id = 0;

		/* Prefetch next mbuf while processing current one. */
		rte_txgbe_prefetch(sw_ring[next_id].mbuf);

		/*
		 * When next RX descriptor is on a cache-line boundary,
		 * prefetch the next 4 RX descriptors and the next 4 pointers
		 * to mbufs.
		 */
		if ((next_id & 0x3) == 0) {
			rte_txgbe_prefetch(&rx_ring[next_id]);
			rte_txgbe_prefetch(&sw_ring[next_id]);
		}

		rxm = rxe->mbuf;

		if (!bulk_alloc) {
			__le64 dma =
			  rte_cpu_to_le_64(rte_mbuf_data_iova_default(nmb));
			/*
			 * Update RX descriptor with the physical address of the
			 * new data buffer of the new allocated mbuf.
			 */
			rxe->mbuf = nmb;

			rxm->data_off = RTE_PKTMBUF_HEADROOM;
			TXGBE_RXD_HDRADDR(rxdp, 0);
			TXGBE_RXD_PKTADDR(rxdp, dma);
		} else {
			rxe->mbuf = NULL;
		}

		/*
		 * Set data length & data buffer address of mbuf.
		 */
		data_len = rte_le_to_cpu_16(rxd.qw1.hi.len);
		rxm->data_len = data_len;

		if (!eop) {
			uint16_t nextp_id;
			/*
			 * Get next descriptor index:
			 *  - For RSC it's in the NEXTP field.
			 *  - For a scattered packet - it's just a following
			 *    descriptor.
			 */
			if (TXGBE_RXD_RSCCNT(rxd.qw0.dw0))
				nextp_id = TXGBE_RXD_NEXTP(staterr);
			else
				nextp_id = next_id;

			next_sc_entry = &sw_sc_ring[nextp_id];
			next_rxe = &sw_ring[nextp_id];
			rte_txgbe_prefetch(next_rxe);
		}

		sc_entry = &sw_sc_ring[rx_id];
		first_seg = sc_entry->fbuf;
		sc_entry->fbuf = NULL;

		/*
		 * If this is the first buffer of the received packet,
		 * set the pointer to the first mbuf of the packet and
		 * initialize its context.
		 * Otherwise, update the total length and the number of segments
		 * of the current scattered packet, and update the pointer to
		 * the last mbuf of the current packet.
		 */
		if (first_seg == NULL) {
			first_seg = rxm;
			first_seg->pkt_len = data_len;
			first_seg->nb_segs = 1;
		} else {
			first_seg->pkt_len += data_len;
			first_seg->nb_segs++;
		}

		prev_id = rx_id;
		rx_id = next_id;

		/*
		 * If this is not the last buffer of the received packet, update
		 * the pointer to the first mbuf at the NEXTP entry in the
		 * sw_sc_ring and continue to parse the RX ring.
		 */
		if (!eop && next_rxe) {
			rxm->next = next_rxe->mbuf;
			next_sc_entry->fbuf = first_seg;
			goto next_desc;
		}

		/* Initialize the first mbuf of the returned packet */
		txgbe_fill_cluster_head_buf(first_seg, &rxd, rxq, staterr);

		/*
		 * Deal with the case, when HW CRC srip is disabled.
		 * That can't happen when LRO is enabled, but still could
		 * happen for scattered RX mode.
		 */
		first_seg->pkt_len -= rxq->crc_len;
		if (unlikely(rxm->data_len <= rxq->crc_len)) {
			struct rte_mbuf *lp;

			for (lp = first_seg; lp->next != rxm; lp = lp->next)
				;

			first_seg->nb_segs--;
			lp->data_len -= rxq->crc_len - rxm->data_len;
			lp->next = NULL;
			rte_pktmbuf_free_seg(rxm);
		} else {
			rxm->data_len -= rxq->crc_len;
		}

		/* Prefetch data of first segment, if configured to do so. */
		rte_packet_prefetch((char *)first_seg->buf_addr +
			first_seg->data_off);

		/*
		 * Store the mbuf address into the next entry of the array
		 * of returned packets.
		 */
		rx_pkts[nb_rx++] = first_seg;
	}

	/*
	 * Record index of the next RX descriptor to probe.
	 */
	rxq->rx_tail = rx_id;

	/*
	 * If the number of free RX descriptors is greater than the RX free
	 * threshold of the queue, advance the Receive Descriptor Tail (RDT)
	 * register.
	 * Update the RDT with the value of the last processed RX descriptor
	 * minus 1, to guarantee that the RDT register is never equal to the
	 * RDH register, which creates a "full" ring situation from the
	 * hardware point of view...
	 */
	if (!bulk_alloc && nb_hold > rxq->rx_free_thresh) {
		PMD_RX_LOG(DEBUG, "port_id=%u queue_id=%u rx_tail=%u "
			   "nb_hold=%u nb_rx=%u",
			   rxq->port_id, rxq->queue_id, rx_id, nb_hold, nb_rx);

		rte_wmb();
		txgbe_set32_relaxed(rxq->rdt_reg_addr, prev_id);
		nb_hold = 0;
	}

	rxq->nb_rx_hold = nb_hold;
	return nb_rx;
}
/**
 * txgbe_recv_pkts_lro - receive handler for and LRO case.
 *
 * @rx_queue Rx queue handle
 * @rx_pkts table of received packets
 * @nb_pkts size of rx_pkts table
 * @bulk_alloc if TRUE bulk allocation is used for a HW ring refilling
 *
 * Handles the Rx HW ring completions when RSC feature is configured. Uses an
 * additional ring of txgbe_rsc_entry's that will hold the relevant RSC info.
 *
 * We use the same logic as in Linux and in FreeBSD txgbe drivers:
 * 1) When non-EOP RSC completion arrives:
 *    a) Update the HEAD of the current RSC aggregation cluster with the new
 *       segment's data length.
 *    b) Set the "next" pointer of the current segment to point to the segment
 *       at the NEXTP index.
 *    c) Pass the HEAD of RSC aggregation cluster on to the next NEXTP entry
 *       in the sw_rsc_ring.
 * 2) When EOP arrives we just update the cluster's total length and offload
 *    flags and deliver the cluster up to the upper layers. In our case - put it
 *    in the rx_pkts table.
 *
 * Returns the number of received packets/clusters (according to the "bulk
 * receive" interface).
 */
receive handler for and LRO case.
@rx_queue Rx queue handle
@rx_pkts table of received packets
@nb_pkts size of rx_pkts table
@bulk_alloc if TRUE bulk allocation is used for a HW ring refilling

Handles the Rx HW ring completions when RSC feature is configured. Uses an
additional ring of txgbe_rsc_entry's that will hold the relevant RSC info.

We use the same logic as in Linux and in FreeBSD txgbe drivers:
1) When non-EOP RSC completion arrives:
a) Update the HEAD of the current RSC aggregation cluster with the new
segment's data length.
b) Set the "next" pointer of the current segment to point to the segment
at the NEXTP index.
c) Pass the HEAD of RSC aggregation cluster on to the next NEXTP entry
in the sw_rsc_ring.
2) When EOP arrives we just update the cluster's total length and offload
flags and deliver the cluster up to the upper layers. In our case - put it
in the rx_pkts table.

Returns the number of received packets/clusters (according to the "bulk
receive" interface).
receive handler for and LRO case . @rx_queue Rx queue handle @rx_pkts table of received packets @nb_pkts size of rx_pkts table @bulk_alloc if TRUE bulk allocation is used for a HW ring refilling Handles the Rx HW ring completions when RSC feature is configured . Uses an additional ring of txgbe_rsc_entry ' s that will hold the relevant RSC info . We use the same logic as in Linux and in FreeBSD txgbe drivers : 1 ) When non - EOP RSC completion arrives : a ) Update the HEAD of the current RSC aggregation cluster with the new segment ' s data length . b ) Set the " next " pointer of the current segment to point to the segment at the NEXTP index . c ) Pass the HEAD of RSC aggregation cluster on to the next NEXTP entry in the sw_rsc_ring . 2 ) When EOP arrives we just update the cluster ' s total length and offload flags and deliver the cluster up to the upper layers . In our case - put it in the rx_pkts table . Returns the number of received packets / clusters ( according to the " bulk receive " interface ) .
static inline uint16_t
txgbe_recv_pkts_lro(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts,
		    bool bulk_alloc)
{
	struct txgbe_rx_queue *rxq = rx_queue;
	struct rte_eth_dev *dev = &rte_eth_devices[rxq->port_id];
	volatile struct txgbe_rx_desc *rx_ring = rxq->rx_ring;
	struct txgbe_rx_entry *sw_ring = rxq->sw_ring;
	struct txgbe_scattered_rx_entry *sw_sc_ring = rxq->sw_sc_ring;
	uint16_t rx_id = rxq->rx_tail;
	uint16_t nb_rx = 0;
	uint16_t nb_hold = rxq->nb_rx_hold;
	uint16_t prev_id = rxq->rx_tail;
	while (nb_rx < nb_pkts) {
		bool eop;
		struct txgbe_rx_entry *rxe;
		struct txgbe_scattered_rx_entry *sc_entry;
		struct txgbe_scattered_rx_entry *next_sc_entry = NULL;
		struct txgbe_rx_entry *next_rxe = NULL;
		struct rte_mbuf *first_seg;
		struct rte_mbuf *rxm;
		struct rte_mbuf *nmb = NULL;
		struct txgbe_rx_desc rxd;
		uint16_t data_len;
		uint16_t next_id;
		volatile struct txgbe_rx_desc *rxdp;
		uint32_t staterr;
next_desc:
		rxdp = &rx_ring[rx_id];
		staterr = rte_le_to_cpu_32(rxdp->qw1.lo.status);
		if (!(staterr & TXGBE_RXD_STAT_DD))
			break;
		rxd = *rxdp;
		PMD_RX_LOG(DEBUG, "port_id=%u queue_id=%u rx_id=%u "
				  "staterr=0x%x data_len=%u",
			   rxq->port_id, rxq->queue_id, rx_id, staterr,
			   rte_le_to_cpu_16(rxd.qw1.hi.len));
		if (!bulk_alloc) {
			nmb = rte_mbuf_raw_alloc(rxq->mb_pool);
			if (nmb == NULL) {
				PMD_RX_LOG(DEBUG, "RX mbuf alloc failed "
						  "port_id=%u queue_id=%u",
					   rxq->port_id, rxq->queue_id);
				dev->data->rx_mbuf_alloc_failed++;
				break;
			}
		} else if (nb_hold > rxq->rx_free_thresh) {
			uint16_t next_rdt = rxq->rx_free_trigger;
			if (!txgbe_rx_alloc_bufs(rxq, false)) {
				rte_wmb();
				txgbe_set32_relaxed(rxq->rdt_reg_addr,
							    next_rdt);
				nb_hold -= rxq->rx_free_thresh;
			} else {
				PMD_RX_LOG(DEBUG, "RX bulk alloc failed "
						  "port_id=%u queue_id=%u",
					   rxq->port_id, rxq->queue_id);
				dev->data->rx_mbuf_alloc_failed++;
				break;
			}
		}
		nb_hold++;
		rxe = &sw_ring[rx_id];
		eop = staterr & TXGBE_RXD_STAT_EOP;
		next_id = rx_id + 1;
		if (next_id == rxq->nb_rx_desc)
			next_id = 0;
		rte_txgbe_prefetch(sw_ring[next_id].mbuf);
		if ((next_id & 0x3) == 0) {
			rte_txgbe_prefetch(&rx_ring[next_id]);
			rte_txgbe_prefetch(&sw_ring[next_id]);
		}
		rxm = rxe->mbuf;
		if (!bulk_alloc) {
			__le64 dma =
			  rte_cpu_to_le_64(rte_mbuf_data_iova_default(nmb));
			rxe->mbuf = nmb;
			rxm->data_off = RTE_PKTMBUF_HEADROOM;
			TXGBE_RXD_HDRADDR(rxdp, 0);
			TXGBE_RXD_PKTADDR(rxdp, dma);
		} else {
			rxe->mbuf = NULL;
		}
		data_len = rte_le_to_cpu_16(rxd.qw1.hi.len);
		rxm->data_len = data_len;
		if (!eop) {
			uint16_t nextp_id;
			if (TXGBE_RXD_RSCCNT(rxd.qw0.dw0))
				nextp_id = TXGBE_RXD_NEXTP(staterr);
			else
				nextp_id = next_id;
			next_sc_entry = &sw_sc_ring[nextp_id];
			next_rxe = &sw_ring[nextp_id];
			rte_txgbe_prefetch(next_rxe);
		}
		sc_entry = &sw_sc_ring[rx_id];
		first_seg = sc_entry->fbuf;
		sc_entry->fbuf = NULL;
		if (first_seg == NULL) {
			first_seg = rxm;
			first_seg->pkt_len = data_len;
			first_seg->nb_segs = 1;
		} else {
			first_seg->pkt_len += data_len;
			first_seg->nb_segs++;
		}
		prev_id = rx_id;
		rx_id = next_id;
		if (!eop && next_rxe) {
			rxm->next = next_rxe->mbuf;
			next_sc_entry->fbuf = first_seg;
			goto next_desc;
		}
		txgbe_fill_cluster_head_buf(first_seg, &rxd, rxq, staterr);
		first_seg->pkt_len -= rxq->crc_len;
		if (unlikely(rxm->data_len <= rxq->crc_len)) {
			struct rte_mbuf *lp;
			for (lp = first_seg; lp->next != rxm; lp = lp->next)
				;
			first_seg->nb_segs--;
			lp->data_len -= rxq->crc_len - rxm->data_len;
			lp->next = NULL;
			rte_pktmbuf_free_seg(rxm);
		} else {
			rxm->data_len -= rxq->crc_len;
		}
		rte_packet_prefetch((char *)first_seg->buf_addr +
			first_seg->data_off);
		rx_pkts[nb_rx++] = first_seg;
	}
	rxq->rx_tail = rx_id;
	if (!bulk_alloc && nb_hold > rxq->rx_free_thresh) {
		PMD_RX_LOG(DEBUG, "port_id=%u queue_id=%u rx_tail=%u "
			   "nb_hold=%u nb_rx=%u",
			   rxq->port_id, rxq->queue_id, rx_id, nb_hold, nb_rx);
		rte_wmb();
		txgbe_set32_relaxed(rxq->rdt_reg_addr, prev_id);
		nb_hold = 0;
	}
	rxq->nb_rx_hold = nb_hold;
	return nb_rx;
}
static inline uint16_t txgbe_recv_pkts_lro ( void * rx_queue , struct rte_mbuf * * rx_pkts , uint16_t nb_pkts , bool bulk_alloc ) { struct txgbe_rx_queue * rxq = rx_queue ; struct rte_eth_dev * dev = & rte_eth_devices [ rxq -> port_id ] ; volatile struct txgbe_rx_desc * rx_ring = rxq -> rx_ring ; struct txgbe_rx_entry * sw_ring = rxq -> sw_ring ; struct txgbe_scattered_rx_entry * sw_sc_ring = rxq -> sw_sc_ring ; uint16_t rx_id = rxq -> rx_tail ; uint16_t nb_rx = 0 ; uint16_t nb_hold = rxq -> nb_rx_hold ; uint16_t prev_id = rxq -> rx_tail ; while ( nb_rx < nb_pkts ) { bool eop ; struct txgbe_rx_entry * rxe ; struct txgbe_scattered_rx_entry * sc_entry ; struct txgbe_scattered_rx_entry * next_sc_entry = NULL ; struct txgbe_rx_entry * next_rxe = NULL ; struct rte_mbuf * first_seg ; struct rte_mbuf * rxm ; struct rte_mbuf * nmb = NULL ; struct txgbe_rx_desc rxd ; uint16_t data_len ; uint16_t next_id ; volatile struct txgbe_rx_desc * rxdp ; uint32_t staterr ; next_desc : rxdp = & rx_ring [ rx_id ] ; staterr = rte_le_to_cpu_32 ( rxdp -> qw1 . lo . status ) ; if ( ! ( staterr & TXGBE_RXD_STAT_DD ) ) break ; rxd = * rxdp ; PMD_RX_LOG ( DEBUG , " " " " , rxq -> port_id , rxq -> queue_id , rx_id , staterr , rte_le_to_cpu_16 ( rxd . qw1 . hi . len ) ) ; if ( ! bulk_alloc ) { nmb = rte_mbuf_raw_alloc ( rxq -> mb_pool ) ; if ( nmb == NULL ) { PMD_RX_LOG ( DEBUG , " " " " , rxq -> port_id , rxq -> queue_id ) ; dev -> data -> rx_mbuf_alloc_failed ++ ; break ; } } else if ( nb_hold > rxq -> rx_free_thresh ) { uint16_t next_rdt = rxq -> rx_free_trigger ; if ( ! txgbe_rx_alloc_bufs ( rxq , false ) ) { rte_wmb ( ) ; txgbe_set32_relaxed ( rxq -> rdt_reg_addr , next_rdt ) ; nb_hold -= rxq -> rx_free_thresh ; } else { PMD_RX_LOG ( DEBUG , " " " " , rxq -> port_id , rxq -> queue_id ) ; dev -> data -> rx_mbuf_alloc_failed ++ ; break ; } } nb_hold ++ ; rxe = & sw_ring [ rx_id ] ; eop = staterr & TXGBE_RXD_STAT_EOP ; next_id = rx_id + 1 ; if ( next_id == rxq -> nb_rx_desc ) next_id = 0 ; rte_txgbe_prefetch ( sw_ring [ next_id ] . mbuf ) ; if ( ( next_id & 0x3 ) == 0 ) { rte_txgbe_prefetch ( & rx_ring [ next_id ] ) ; rte_txgbe_prefetch ( & sw_ring [ next_id ] ) ; } rxm = rxe -> mbuf ; if ( ! bulk_alloc ) { __le64 dma = rte_cpu_to_le_64 ( rte_mbuf_data_iova_default ( nmb ) ) ; rxe -> mbuf = nmb ; rxm -> data_off = RTE_PKTMBUF_HEADROOM ; TXGBE_RXD_HDRADDR ( rxdp , 0 ) ; TXGBE_RXD_PKTADDR ( rxdp , dma ) ; } else { rxe -> mbuf = NULL ; } data_len = rte_le_to_cpu_16 ( rxd . qw1 . hi . len ) ; rxm -> data_len = data_len ; if ( ! eop ) { uint16_t nextp_id ; if ( TXGBE_RXD_RSCCNT ( rxd . qw0 . dw0 ) ) nextp_id = TXGBE_RXD_NEXTP ( staterr ) ; else nextp_id = next_id ; next_sc_entry = & sw_sc_ring [ nextp_id ] ; next_rxe = & sw_ring [ nextp_id ] ; rte_txgbe_prefetch ( next_rxe ) ; } sc_entry = & sw_sc_ring [ rx_id ] ; first_seg = sc_entry -> fbuf ; sc_entry -> fbuf = NULL ; if ( first_seg == NULL ) { first_seg = rxm ; first_seg -> pkt_len = data_len ; first_seg -> nb_segs = 1 ; } else { first_seg -> pkt_len += data_len ; first_seg -> nb_segs ++ ; } prev_id = rx_id ; rx_id = next_id ; if ( ! eop && next_rxe ) { rxm -> next = next_rxe -> mbuf ; next_sc_entry -> fbuf = first_seg ; goto next_desc ; } txgbe_fill_cluster_head_buf ( first_seg , & rxd , rxq , staterr ) ; first_seg -> pkt_len -= rxq -> crc_len ; if ( unlikely ( rxm -> data_len <= rxq -> crc_len ) ) { struct rte_mbuf * lp ; for ( lp = first_seg ; lp -> next != rxm ; lp = lp -> next ) ; first_seg -> nb_segs -- ; lp -> data_len -= rxq -> crc_len - rxm -> data_len ; lp -> next = NULL ; rte_pktmbuf_free_seg ( rxm ) ; } else { rxm -> data_len -= rxq -> crc_len ; } rte_packet_prefetch ( ( char * ) first_seg -> buf_addr + first_seg -> data_off ) ; rx_pkts [ nb_rx ++ ] = first_seg ; } rxq -> rx_tail = rx_id ; if ( ! bulk_alloc && nb_hold > rxq -> rx_free_thresh ) { PMD_RX_LOG ( DEBUG , " " " " , rxq -> port_id , rxq -> queue_id , rx_id , nb_hold , nb_rx ) ; rte_wmb ( ) ; txgbe_set32_relaxed ( rxq -> rdt_reg_addr , prev_id ) ; nb_hold = 0 ; } rxq -> nb_rx_hold = nb_hold ; return nb_rx ; }
txgbe_recv_pkts_lro - receive handler for and LRO case.
txgbe_recv_pkts_lro - receive handler for and LRO case .
/*
		 * The code in this whole file uses the volatile pointer to
		 * ensure the read ordering of the status and the rest of the
		 * descriptor fields (on the compiler level only!!!). This is so
		 * UGLY - why not to just use the compiler barrier instead? DPDK
		 * even has the rte_compiler_barrier() for that.
		 *
		 * But most importantly this is just wrong because this doesn't
		 * ensure memory ordering in a general case at all. For
		 * instance, DPDK is supposed to work on Power CPUs where
		 * compiler barrier may just not be enough!
		 *
		 * I tried to write only this function properly to have a
		 * starting point (as a part of an LRO/RSC series) but the
		 * compiler cursed at me when I tried to cast away the
		 * "volatile" from rx_ring (yes, it's volatile too!!!). So, I'm
		 * keeping it the way it is for now.
		 *
		 * The code in this file is broken in so many other places and
		 * will just not work on a big endian CPU anyway therefore the
		 * lines below will have to be revisited together with the rest
		 * of the txgbe PMD.
		 *
		 * TODO:
		 *    - Get rid of "volatile" and let the compiler do its job.
		 *    - Use the proper memory barrier (rte_rmb()) to ensure the
		 *      memory ordering below.
		 */ /* Prefetch next mbuf while processing current one. */ /*
		 * When next RX descriptor is on a cache-line boundary,
		 * prefetch the next 4 RX descriptors and the next 4 pointers
		 * to mbufs.
		 */ /*
			 * Update RX descriptor with the physical address of the
			 * new data buffer of the new allocated mbuf.
			 */ /*
		 * Set data length & data buffer address of mbuf.
		 */ /*
			 * Get next descriptor index:
			 *  - For RSC it's in the NEXTP field.
			 *  - For a scattered packet - it's just a following
			 *    descriptor.
			 */ /*
		 * If this is the first buffer of the received packet,
		 * set the pointer to the first mbuf of the packet and
		 * initialize its context.
		 * Otherwise, update the total length and the number of segments
		 * of the current scattered packet, and update the pointer to
		 * the last mbuf of the current packet.
		 */ /*
		 * If this is not the last buffer of the received packet, update
		 * the pointer to the first mbuf at the NEXTP entry in the
		 * sw_sc_ring and continue to parse the RX ring.
		 */ /* Initialize the first mbuf of the returned packet */ /*
		 * Deal with the case, when HW CRC srip is disabled.
		 * That can't happen when LRO is enabled, but still could
		 * happen for scattered RX mode.
		 */ /* Prefetch data of first segment, if configured to do so. */ /*
		 * Store the mbuf address into the next entry of the array
		 * of returned packets.
		 */ /*
	 * Record index of the next RX descriptor to probe.
	 */ /*
	 * If the number of free RX descriptors is greater than the RX free
	 * threshold of the queue, advance the Receive Descriptor Tail (RDT)
	 * register.
	 * Update the RDT with the value of the last processed RX descriptor
	 * minus 1, to guarantee that the RDT register is never equal to the
	 * RDH register, which creates a "full" ring situation from the
	 * hardware point of view...
	 */