[dpdk-dev,v3,2/3] net/e1000: e1000 igb support to free consumed buffers
diff mbox

Message ID 20170120160109.1088-3-bmcfall@redhat.com
State Superseded, archived
Headers show

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/Intel compilation fail Compilation issues

Commit Message

Billy McFall Jan. 20, 2017, 4:01 p.m. UTC
Add support to the e1000 igb driver for the new API to force free
consumed buffers on Tx ring. e1000 igb driver does not implement a
tx_rs_thresh to free mbufs, it frees a slot in the ring as needed, so
a new function needed to be written.

Signed-off-by: Billy McFall <bmcfall@redhat.com>
---
 drivers/net/e1000/e1000_ethdev.h |   2 +
 drivers/net/e1000/igb_ethdev.c   |   1 +
 drivers/net/e1000/igb_rxtx.c     | 126 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 129 insertions(+)

Comments

Lu, Wenzhuo Jan. 22, 2017, 3:47 a.m. UTC | #1
Hi Billy,

> -----Original Message-----
> From: Billy McFall [mailto:bmcfall@redhat.com]
> Sent: Saturday, January 21, 2017 12:01 AM
> To: thomas.monjalon@6wind.com; Lu, Wenzhuo
> Cc: dev@dpdk.org; Billy McFall
> Subject: [PATCH v3 2/3] net/e1000: e1000 igb support to free consumed
> buffers
> 
> Add support to the e1000 igb driver for the new API to force free consumed
> buffers on Tx ring. e1000 igb driver does not implement a tx_rs_thresh to free
> mbufs, it frees a slot in the ring as needed, so a new function needed to be
> written.
> 
> Signed-off-by: Billy McFall <bmcfall@redhat.com>
> ---
>  drivers/net/e1000/e1000_ethdev.h |   2 +
>  drivers/net/e1000/igb_ethdev.c   |   1 +
>  drivers/net/e1000/igb_rxtx.c     | 126
> +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 129 insertions(+)
> 
> diff --git a/drivers/net/e1000/e1000_ethdev.h
> b/drivers/net/e1000/e1000_ethdev.h
> index 81a6dbb..39b2f43 100644
> --- a/drivers/net/e1000/e1000_ethdev.h
> +++ b/drivers/net/e1000/e1000_ethdev.h
> @@ -315,6 +315,8 @@ int eth_igb_tx_queue_setup(struct rte_eth_dev *dev,
> uint16_t tx_queue_id,
>  		uint16_t nb_tx_desc, unsigned int socket_id,
>  		const struct rte_eth_txconf *tx_conf);
> 
> +int eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt);
> +
>  int eth_igb_rx_init(struct rte_eth_dev *dev);
> 
>  void eth_igb_tx_init(struct rte_eth_dev *dev); diff --git
> a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c index
> 8843dd1..12a1a30 100644
> --- a/drivers/net/e1000/igb_ethdev.c
> +++ b/drivers/net/e1000/igb_ethdev.c
> @@ -408,6 +408,7 @@ static const struct eth_dev_ops eth_igb_ops = {
>  	.rx_descriptor_done   = eth_igb_rx_descriptor_done,
>  	.tx_queue_setup       = eth_igb_tx_queue_setup,
>  	.tx_queue_release     = eth_igb_tx_queue_release,
> +	.tx_done_cleanup      = eth_igb_tx_done_cleanup,
>  	.dev_led_on           = eth_igb_led_on,
>  	.dev_led_off          = eth_igb_led_off,
>  	.flow_ctrl_get        = eth_igb_flow_ctrl_get,
> diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c index
> 45f3f24..00cd2aa 100644
> --- a/drivers/net/e1000/igb_rxtx.c
> +++ b/drivers/net/e1000/igb_rxtx.c
> @@ -1277,6 +1277,132 @@ eth_igb_tx_queue_release(void *txq)
>  	igb_tx_queue_release(txq);
>  }
> 
> +static int
> +igb_tx_done_cleanup(struct igb_tx_queue *txq, uint32_t free_cnt) {
> +	struct igb_tx_entry *sw_ring;
> +	volatile union e1000_adv_tx_desc *txr;
> +	uint16_t tx_first; /* First segment analyzed. */
> +	uint16_t tx_id;    /* Current segment being processed. */
> +	uint16_t tx_last;  /* Last segment in the current packet. */
> +	uint16_t tx_next;  /* First segment of the next packet. */
> +	int count;
> +
> +	if (txq != NULL) {
> +		count = 0;
> +		sw_ring = txq->sw_ring;
> +		txr = txq->tx_ring;
> +
> +		/*
> +		 * tx_tail is the last sent packet on the sw_ring. Goto the end
> +		 * of that packet (the last segment in the packet chain) and
> +		 * then the next segment will be the start of the oldest
> segment
> +		 * in the sw_ring. This is the first packet that will be
> +		 * attempted to be freed.
> +		 */
> +
> +		/* Get last segment in most recently added packet. */
> +		tx_first = sw_ring[txq->tx_tail].last_id;
> +
> +		/* Get the next segment, which is the oldest segment in ring.
> */
> +		tx_first = sw_ring[tx_first].next_id;
> +
> +		/* Set the current index to the first. */
> +		tx_id = tx_first;
> +
> +		/*
> +		 * Loop through each packet. For each packet, verify that an
> +		 * mbuf exists and that the last segment is free. If so, free
> +		 * it and move on.
> +		 */
> +		while (1) {
> +			tx_last = sw_ring[tx_id].last_id;
> +
> +			if (sw_ring[tx_last].mbuf) {
> +				if (txr[tx_last].wb.status &
> +						E1000_TXD_STAT_DD) {
> +					/*
> +					 * Increment the number of packets
> +					 * freed.
> +					 */
> +					count++;
> +
> +					/* Get the start of the next packet. */
> +					tx_next = sw_ring[tx_last].next_id;
> +
> +					/*
> +					 * Loop through all segments in a
> +					 * packet.
> +					 */
> +					do {
> +
> 	rte_pktmbuf_free_seg(sw_ring[tx_id].mbuf);
> +						sw_ring[tx_id].mbuf = NULL;
> +						sw_ring[tx_id].last_id = tx_id;
> +
> +						/* Move to next segemnt. */
> +						tx_id = sw_ring[tx_id].next_id;
> +
> +					} while (tx_id != tx_next);
> +
> +					if (unlikely(count == (int)free_cnt))
> +						break;
> +				} else
> +					/*
> +					 * mbuf still in use, nothing left to
> +					 * free.
> +					 */
> +					break;
> +			} else {
> +				/*
> +				 * There are multiple reasons to be here:
> +				 * 1) All the packets on the ring have been
> +				 *    freed - tx_id is equal to tx_first
> +				 *    and some packets have been freed.
> +				 *    - Done, exit
> +				 * 2) Interfaces has not sent a rings worth of
> +				 *    packets yet, so the segment after tail is
> +				 *    still empty. Or a previous call to this
> +				 *    function freed some of the segments but
> +				 *    not all so there is a hole in the list.
> +				 *    Hopefully this is a rare case.
> +				 *    - Walk the list and find the next mbuf. If
> +				 *      there isn't one, then done.
> +				 */
> +				if (likely((tx_id == tx_first) && (count != 0)))
> +					break;
> +
> +				/*
> +				 * Walk the list and find the next mbuf, if any.
> +				 */
> +				do {
> +					/* Move to next segemnt. */
> +					tx_id = sw_ring[tx_id].next_id;
> +
> +					if (sw_ring[tx_id].mbuf)
> +						break;
> +
> +				} while (tx_id != tx_first);
> +
> +				/*
> +				 * Determine why previous loop bailed. If
> there
> +				 * is not an mbuf, done.
> +				 */
> +				if (sw_ring[tx_id].mbuf == NULL)
> +					break;
> +			}
> +		}
> +	} else
> +		count = -ENODEV;
> +
> +	return count;
> +}
> +
> +int
> +eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt) {
> +	return igb_tx_done_cleanup(txq, free_cnt); }
> +
>  static void
>  igb_reset_tx_queue_stat(struct igb_tx_queue *txq)  {
> --
> 2.9.3
I think the code is good. But I don't understand the whole idea. An API means APP need call it. When should APP call This API? Does APP need to check after sending every packet? Or just call it periodically?
Billy McFall Jan. 23, 2017, 1:49 p.m. UTC | #2
There are two scenarios the APP calls this API (there may be others, but
the API was designed with these two in mind):

1) APP receives a packet that needs to be flooded. Instead of making a copy
of the packet (whether it is the whole packet, just the header or a clone
buffer is used, as done today), the APP increments the reference count,
adjusts the header and then sends the packet to one of the destination
ports. The APP then polls this API until the reference count is
decremented, and forwards to the next destination port with any header
modifications needed.

2) An APP like a packet generation completes a run and then wants to reset
for another run. It can call this API to free and mbufs that have been
cache by the driver so it can start another run with the full set of mbufs.
Maybe it is switching interfaces that are on another core and want to
reclaim any mbufs that have been cached.

So this API is intended to be called on an as need basis, not on every
packet. When either a particular mbuf is desired or the mbufs for a
particular interface need to be released.

Hope this helps,
Billy McFall

On Sat, Jan 21, 2017 at 10:47 PM, Lu, Wenzhuo <wenzhuo.lu@intel.com> wrote:

> Hi Billy,
>
> > -----Original Message-----
> > From: Billy McFall [mailto:bmcfall@redhat.com]
> > Sent: Saturday, January 21, 2017 12:01 AM
> > To: thomas.monjalon@6wind.com; Lu, Wenzhuo
> > Cc: dev@dpdk.org; Billy McFall
> > Subject: [PATCH v3 2/3] net/e1000: e1000 igb support to free consumed
> > buffers
> >
> > Add support to the e1000 igb driver for the new API to force free
> consumed
> > buffers on Tx ring. e1000 igb driver does not implement a tx_rs_thresh
> to free
> > mbufs, it frees a slot in the ring as needed, so a new function needed
> to be
> > written.
> >
> > Signed-off-by: Billy McFall <bmcfall@redhat.com>
> > ---
> >  drivers/net/e1000/e1000_ethdev.h |   2 +
> >  drivers/net/e1000/igb_ethdev.c   |   1 +
> >  drivers/net/e1000/igb_rxtx.c     | 126
> > +++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 129 insertions(+)
> >
> > diff --git a/drivers/net/e1000/e1000_ethdev.h
> > b/drivers/net/e1000/e1000_ethdev.h
> > index 81a6dbb..39b2f43 100644
> > --- a/drivers/net/e1000/e1000_ethdev.h
> > +++ b/drivers/net/e1000/e1000_ethdev.h
> > @@ -315,6 +315,8 @@ int eth_igb_tx_queue_setup(struct rte_eth_dev *dev,
> > uint16_t tx_queue_id,
> >               uint16_t nb_tx_desc, unsigned int socket_id,
> >               const struct rte_eth_txconf *tx_conf);
> >
> > +int eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt);
> > +
> >  int eth_igb_rx_init(struct rte_eth_dev *dev);
> >
> >  void eth_igb_tx_init(struct rte_eth_dev *dev); diff --git
> > a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c index
> > 8843dd1..12a1a30 100644
> > --- a/drivers/net/e1000/igb_ethdev.c
> > +++ b/drivers/net/e1000/igb_ethdev.c
> > @@ -408,6 +408,7 @@ static const struct eth_dev_ops eth_igb_ops = {
> >       .rx_descriptor_done   = eth_igb_rx_descriptor_done,
> >       .tx_queue_setup       = eth_igb_tx_queue_setup,
> >       .tx_queue_release     = eth_igb_tx_queue_release,
> > +     .tx_done_cleanup      = eth_igb_tx_done_cleanup,
> >       .dev_led_on           = eth_igb_led_on,
> >       .dev_led_off          = eth_igb_led_off,
> >       .flow_ctrl_get        = eth_igb_flow_ctrl_get,
> > diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c
> index
> > 45f3f24..00cd2aa 100644
> > --- a/drivers/net/e1000/igb_rxtx.c
> > +++ b/drivers/net/e1000/igb_rxtx.c
> > @@ -1277,6 +1277,132 @@ eth_igb_tx_queue_release(void *txq)
> >       igb_tx_queue_release(txq);
> >  }
> >
> > +static int
> > +igb_tx_done_cleanup(struct igb_tx_queue *txq, uint32_t free_cnt) {
> > +     struct igb_tx_entry *sw_ring;
> > +     volatile union e1000_adv_tx_desc *txr;
> > +     uint16_t tx_first; /* First segment analyzed. */
> > +     uint16_t tx_id;    /* Current segment being processed. */
> > +     uint16_t tx_last;  /* Last segment in the current packet. */
> > +     uint16_t tx_next;  /* First segment of the next packet. */
> > +     int count;
> > +
> > +     if (txq != NULL) {
> > +             count = 0;
> > +             sw_ring = txq->sw_ring;
> > +             txr = txq->tx_ring;
> > +
> > +             /*
> > +              * tx_tail is the last sent packet on the sw_ring. Goto
> the end
> > +              * of that packet (the last segment in the packet chain)
> and
> > +              * then the next segment will be the start of the oldest
> > segment
> > +              * in the sw_ring. This is the first packet that will be
> > +              * attempted to be freed.
> > +              */
> > +
> > +             /* Get last segment in most recently added packet. */
> > +             tx_first = sw_ring[txq->tx_tail].last_id;
> > +
> > +             /* Get the next segment, which is the oldest segment in
> ring.
> > */
> > +             tx_first = sw_ring[tx_first].next_id;
> > +
> > +             /* Set the current index to the first. */
> > +             tx_id = tx_first;
> > +
> > +             /*
> > +              * Loop through each packet. For each packet, verify that
> an
> > +              * mbuf exists and that the last segment is free. If so,
> free
> > +              * it and move on.
> > +              */
> > +             while (1) {
> > +                     tx_last = sw_ring[tx_id].last_id;
> > +
> > +                     if (sw_ring[tx_last].mbuf) {
> > +                             if (txr[tx_last].wb.status &
> > +                                             E1000_TXD_STAT_DD) {
> > +                                     /*
> > +                                      * Increment the number of packets
> > +                                      * freed.
> > +                                      */
> > +                                     count++;
> > +
> > +                                     /* Get the start of the next
> packet. */
> > +                                     tx_next = sw_ring[tx_last].next_id;
> > +
> > +                                     /*
> > +                                      * Loop through all segments in a
> > +                                      * packet.
> > +                                      */
> > +                                     do {
> > +
> >       rte_pktmbuf_free_seg(sw_ring[tx_id].mbuf);
> > +                                             sw_ring[tx_id].mbuf = NULL;
> > +                                             sw_ring[tx_id].last_id =
> tx_id;
> > +
> > +                                             /* Move to next segemnt. */
> > +                                             tx_id =
> sw_ring[tx_id].next_id;
> > +
> > +                                     } while (tx_id != tx_next);
> > +
> > +                                     if (unlikely(count ==
> (int)free_cnt))
> > +                                             break;
> > +                             } else
> > +                                     /*
> > +                                      * mbuf still in use, nothing left
> to
> > +                                      * free.
> > +                                      */
> > +                                     break;
> > +                     } else {
> > +                             /*
> > +                              * There are multiple reasons to be here:
> > +                              * 1) All the packets on the ring have been
> > +                              *    freed - tx_id is equal to tx_first
> > +                              *    and some packets have been freed.
> > +                              *    - Done, exit
> > +                              * 2) Interfaces has not sent a rings
> worth of
> > +                              *    packets yet, so the segment after
> tail is
> > +                              *    still empty. Or a previous call to
> this
> > +                              *    function freed some of the segments
> but
> > +                              *    not all so there is a hole in the
> list.
> > +                              *    Hopefully this is a rare case.
> > +                              *    - Walk the list and find the next
> mbuf. If
> > +                              *      there isn't one, then done.
> > +                              */
> > +                             if (likely((tx_id == tx_first) && (count
> != 0)))
> > +                                     break;
> > +
> > +                             /*
> > +                              * Walk the list and find the next mbuf,
> if any.
> > +                              */
> > +                             do {
> > +                                     /* Move to next segemnt. */
> > +                                     tx_id = sw_ring[tx_id].next_id;
> > +
> > +                                     if (sw_ring[tx_id].mbuf)
> > +                                             break;
> > +
> > +                             } while (tx_id != tx_first);
> > +
> > +                             /*
> > +                              * Determine why previous loop bailed. If
> > there
> > +                              * is not an mbuf, done.
> > +                              */
> > +                             if (sw_ring[tx_id].mbuf == NULL)
> > +                                     break;
> > +                     }
> > +             }
> > +     } else
> > +             count = -ENODEV;
> > +
> > +     return count;
> > +}
> > +
> > +int
> > +eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt) {
> > +     return igb_tx_done_cleanup(txq, free_cnt); }
> > +
> >  static void
> >  igb_reset_tx_queue_stat(struct igb_tx_queue *txq)  {
> > --
> > 2.9.3
> I think the code is good. But I don't understand the whole idea. An API
> means APP need call it. When should APP call This API? Does APP need to
> check after sending every packet? Or just call it periodically?
>
>
Lu, Wenzhuo Jan. 24, 2017, 12:42 a.m. UTC | #3
Hi Billy,
Thanks for your explanation. It makes a picture to me ☺


Best regards
Wenzhuo Lu

From: Billy McFall [mailto:bmcfall@redhat.com]

Sent: Monday, January 23, 2017 9:49 PM
To: Lu, Wenzhuo
Cc: thomas.monjalon@6wind.com; dev@dpdk.org
Subject: Re: [PATCH v3 2/3] net/e1000: e1000 igb support to free consumed buffers

There are two scenarios the APP calls this API (there may be others, but the API was designed with these two in mind):

1) APP receives a packet that needs to be flooded. Instead of making a copy of the packet (whether it is the whole packet, just the header or a clone buffer is used, as done today), the APP increments the reference count, adjusts the header and then sends the packet to one of the destination ports. The APP then polls this API until the reference count is decremented, and forwards to the next destination port with any header modifications needed.

2) An APP like a packet generation completes a run and then wants to reset for another run. It can call this API to free and mbufs that have been cache by the driver so it can start another run with the full set of mbufs. Maybe it is switching interfaces that are on another core and want to reclaim any mbufs that have been cached.

So this API is intended to be called on an as need basis, not on every packet. When either a particular mbuf is desired or the mbufs for a particular interface need to be released.

Hope this helps,
Billy McFall

On Sat, Jan 21, 2017 at 10:47 PM, Lu, Wenzhuo <wenzhuo.lu@intel.com<mailto:wenzhuo.lu@intel.com>> wrote:
Hi Billy,

> -----Original Message-----

> From: Billy McFall [mailto:bmcfall@redhat.com<mailto:bmcfall@redhat.com>]

> Sent: Saturday, January 21, 2017 12:01 AM

> To: thomas.monjalon@6wind.com<mailto:thomas.monjalon@6wind.com>; Lu, Wenzhuo

> Cc: dev@dpdk.org<mailto:dev@dpdk.org>; Billy McFall

> Subject: [PATCH v3 2/3] net/e1000: e1000 igb support to free consumed

> buffers

>

> Add support to the e1000 igb driver for the new API to force free consumed

> buffers on Tx ring. e1000 igb driver does not implement a tx_rs_thresh to free

> mbufs, it frees a slot in the ring as needed, so a new function needed to be

> written.

>

> Signed-off-by: Billy McFall <bmcfall@redhat.com<mailto:bmcfall@redhat.com>>

> ---

>  drivers/net/e1000/e1000_ethdev.h |   2 +

>  drivers/net/e1000/igb_ethdev.c   |   1 +

>  drivers/net/e1000/igb_rxtx.c     | 126

> +++++++++++++++++++++++++++++++++++++++

>  3 files changed, 129 insertions(+)

>

> diff --git a/drivers/net/e1000/e1000_ethdev.h

> b/drivers/net/e1000/e1000_ethdev.h

> index 81a6dbb..39b2f43 100644

> --- a/drivers/net/e1000/e1000_ethdev.h

> +++ b/drivers/net/e1000/e1000_ethdev.h

> @@ -315,6 +315,8 @@ int eth_igb_tx_queue_setup(struct rte_eth_dev *dev,

> uint16_t tx_queue_id,

>               uint16_t nb_tx_desc, unsigned int socket_id,

>               const struct rte_eth_txconf *tx_conf);

>

> +int eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt);

> +

>  int eth_igb_rx_init(struct rte_eth_dev *dev);

>

>  void eth_igb_tx_init(struct rte_eth_dev *dev); diff --git

> a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c index

> 8843dd1..12a1a30 100644

> --- a/drivers/net/e1000/igb_ethdev.c

> +++ b/drivers/net/e1000/igb_ethdev.c

> @@ -408,6 +408,7 @@ static const struct eth_dev_ops eth_igb_ops = {

>       .rx_descriptor_done   = eth_igb_rx_descriptor_done,

>       .tx_queue_setup       = eth_igb_tx_queue_setup,

>       .tx_queue_release     = eth_igb_tx_queue_release,

> +     .tx_done_cleanup      = eth_igb_tx_done_cleanup,

>       .dev_led_on           = eth_igb_led_on,

>       .dev_led_off          = eth_igb_led_off,

>       .flow_ctrl_get        = eth_igb_flow_ctrl_get,

> diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c index

> 45f3f24..00cd2aa 100644

> --- a/drivers/net/e1000/igb_rxtx.c

> +++ b/drivers/net/e1000/igb_rxtx.c

> @@ -1277,6 +1277,132 @@ eth_igb_tx_queue_release(void *txq)

>       igb_tx_queue_release(txq);

>  }

>

> +static int

> +igb_tx_done_cleanup(struct igb_tx_queue *txq, uint32_t free_cnt) {

> +     struct igb_tx_entry *sw_ring;

> +     volatile union e1000_adv_tx_desc *txr;

> +     uint16_t tx_first; /* First segment analyzed. */

> +     uint16_t tx_id;    /* Current segment being processed. */

> +     uint16_t tx_last;  /* Last segment in the current packet. */

> +     uint16_t tx_next;  /* First segment of the next packet. */

> +     int count;

> +

> +     if (txq != NULL) {

> +             count = 0;

> +             sw_ring = txq->sw_ring;

> +             txr = txq->tx_ring;

> +

> +             /*

> +              * tx_tail is the last sent packet on the sw_ring. Goto the end

> +              * of that packet (the last segment in the packet chain) and

> +              * then the next segment will be the start of the oldest

> segment

> +              * in the sw_ring. This is the first packet that will be

> +              * attempted to be freed.

> +              */

> +

> +             /* Get last segment in most recently added packet. */

> +             tx_first = sw_ring[txq->tx_tail].last_id;

> +

> +             /* Get the next segment, which is the oldest segment in ring.

> */

> +             tx_first = sw_ring[tx_first].next_id;

> +

> +             /* Set the current index to the first. */

> +             tx_id = tx_first;

> +

> +             /*

> +              * Loop through each packet. For each packet, verify that an

> +              * mbuf exists and that the last segment is free. If so, free

> +              * it and move on.

> +              */

> +             while (1) {

> +                     tx_last = sw_ring[tx_id].last_id;

> +

> +                     if (sw_ring[tx_last].mbuf) {

> +                             if (txr[tx_last].wb.status &

> +                                             E1000_TXD_STAT_DD) {

> +                                     /*

> +                                      * Increment the number of packets

> +                                      * freed.

> +                                      */

> +                                     count++;

> +

> +                                     /* Get the start of the next packet. */

> +                                     tx_next = sw_ring[tx_last].next_id;

> +

> +                                     /*

> +                                      * Loop through all segments in a

> +                                      * packet.

> +                                      */

> +                                     do {

> +

>       rte_pktmbuf_free_seg(sw_ring[tx_id].mbuf);

> +                                             sw_ring[tx_id].mbuf = NULL;

> +                                             sw_ring[tx_id].last_id = tx_id;

> +

> +                                             /* Move to next segemnt. */

> +                                             tx_id = sw_ring[tx_id].next_id;

> +

> +                                     } while (tx_id != tx_next);

> +

> +                                     if (unlikely(count == (int)free_cnt))

> +                                             break;

> +                             } else

> +                                     /*

> +                                      * mbuf still in use, nothing left to

> +                                      * free.

> +                                      */

> +                                     break;

> +                     } else {

> +                             /*

> +                              * There are multiple reasons to be here:

> +                              * 1) All the packets on the ring have been

> +                              *    freed - tx_id is equal to tx_first

> +                              *    and some packets have been freed.

> +                              *    - Done, exit

> +                              * 2) Interfaces has not sent a rings worth of

> +                              *    packets yet, so the segment after tail is

> +                              *    still empty. Or a previous call to this

> +                              *    function freed some of the segments but

> +                              *    not all so there is a hole in the list.

> +                              *    Hopefully this is a rare case.

> +                              *    - Walk the list and find the next mbuf. If

> +                              *      there isn't one, then done.

> +                              */

> +                             if (likely((tx_id == tx_first) && (count != 0)))

> +                                     break;

> +

> +                             /*

> +                              * Walk the list and find the next mbuf, if any.

> +                              */

> +                             do {

> +                                     /* Move to next segemnt. */

> +                                     tx_id = sw_ring[tx_id].next_id;

> +

> +                                     if (sw_ring[tx_id].mbuf)

> +                                             break;

> +

> +                             } while (tx_id != tx_first);

> +

> +                             /*

> +                              * Determine why previous loop bailed. If

> there

> +                              * is not an mbuf, done.

> +                              */

> +                             if (sw_ring[tx_id].mbuf == NULL)

> +                                     break;

> +                     }

> +             }

> +     } else

> +             count = -ENODEV;

> +

> +     return count;

> +}

> +

> +int

> +eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt) {

> +     return igb_tx_done_cleanup(txq, free_cnt); }

> +

>  static void

>  igb_reset_tx_queue_stat(struct igb_tx_queue *txq)  {

> --

> 2.9.3

I think the code is good. But I don't understand the whole idea. An API means APP need call it. When should APP call This API? Does APP need to check after sending every packet? Or just call it periodically?

Patch
diff mbox

diff --git a/drivers/net/e1000/e1000_ethdev.h b/drivers/net/e1000/e1000_ethdev.h
index 81a6dbb..39b2f43 100644
--- a/drivers/net/e1000/e1000_ethdev.h
+++ b/drivers/net/e1000/e1000_ethdev.h
@@ -315,6 +315,8 @@  int eth_igb_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id,
 		uint16_t nb_tx_desc, unsigned int socket_id,
 		const struct rte_eth_txconf *tx_conf);
 
+int eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt);
+
 int eth_igb_rx_init(struct rte_eth_dev *dev);
 
 void eth_igb_tx_init(struct rte_eth_dev *dev);
diff --git a/drivers/net/e1000/igb_ethdev.c b/drivers/net/e1000/igb_ethdev.c
index 8843dd1..12a1a30 100644
--- a/drivers/net/e1000/igb_ethdev.c
+++ b/drivers/net/e1000/igb_ethdev.c
@@ -408,6 +408,7 @@  static const struct eth_dev_ops eth_igb_ops = {
 	.rx_descriptor_done   = eth_igb_rx_descriptor_done,
 	.tx_queue_setup       = eth_igb_tx_queue_setup,
 	.tx_queue_release     = eth_igb_tx_queue_release,
+	.tx_done_cleanup      = eth_igb_tx_done_cleanup,
 	.dev_led_on           = eth_igb_led_on,
 	.dev_led_off          = eth_igb_led_off,
 	.flow_ctrl_get        = eth_igb_flow_ctrl_get,
diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c
index 45f3f24..00cd2aa 100644
--- a/drivers/net/e1000/igb_rxtx.c
+++ b/drivers/net/e1000/igb_rxtx.c
@@ -1277,6 +1277,132 @@  eth_igb_tx_queue_release(void *txq)
 	igb_tx_queue_release(txq);
 }
 
+static int
+igb_tx_done_cleanup(struct igb_tx_queue *txq, uint32_t free_cnt)
+{
+	struct igb_tx_entry *sw_ring;
+	volatile union e1000_adv_tx_desc *txr;
+	uint16_t tx_first; /* First segment analyzed. */
+	uint16_t tx_id;    /* Current segment being processed. */
+	uint16_t tx_last;  /* Last segment in the current packet. */
+	uint16_t tx_next;  /* First segment of the next packet. */
+	int count;
+
+	if (txq != NULL) {
+		count = 0;
+		sw_ring = txq->sw_ring;
+		txr = txq->tx_ring;
+
+		/*
+		 * tx_tail is the last sent packet on the sw_ring. Goto the end
+		 * of that packet (the last segment in the packet chain) and
+		 * then the next segment will be the start of the oldest segment
+		 * in the sw_ring. This is the first packet that will be
+		 * attempted to be freed.
+		 */
+
+		/* Get last segment in most recently added packet. */
+		tx_first = sw_ring[txq->tx_tail].last_id;
+
+		/* Get the next segment, which is the oldest segment in ring. */
+		tx_first = sw_ring[tx_first].next_id;
+
+		/* Set the current index to the first. */
+		tx_id = tx_first;
+
+		/*
+		 * Loop through each packet. For each packet, verify that an
+		 * mbuf exists and that the last segment is free. If so, free
+		 * it and move on.
+		 */
+		while (1) {
+			tx_last = sw_ring[tx_id].last_id;
+
+			if (sw_ring[tx_last].mbuf) {
+				if (txr[tx_last].wb.status &
+						E1000_TXD_STAT_DD) {
+					/*
+					 * Increment the number of packets
+					 * freed.
+					 */
+					count++;
+
+					/* Get the start of the next packet. */
+					tx_next = sw_ring[tx_last].next_id;
+
+					/*
+					 * Loop through all segments in a
+					 * packet.
+					 */
+					do {
+						rte_pktmbuf_free_seg(sw_ring[tx_id].mbuf);
+						sw_ring[tx_id].mbuf = NULL;
+						sw_ring[tx_id].last_id = tx_id;
+
+						/* Move to next segemnt. */
+						tx_id = sw_ring[tx_id].next_id;
+
+					} while (tx_id != tx_next);
+
+					if (unlikely(count == (int)free_cnt))
+						break;
+				} else
+					/*
+					 * mbuf still in use, nothing left to
+					 * free.
+					 */
+					break;
+			} else {
+				/*
+				 * There are multiple reasons to be here:
+				 * 1) All the packets on the ring have been
+				 *    freed - tx_id is equal to tx_first
+				 *    and some packets have been freed.
+				 *    - Done, exit
+				 * 2) Interfaces has not sent a rings worth of
+				 *    packets yet, so the segment after tail is
+				 *    still empty. Or a previous call to this
+				 *    function freed some of the segments but
+				 *    not all so there is a hole in the list.
+				 *    Hopefully this is a rare case.
+				 *    - Walk the list and find the next mbuf. If
+				 *      there isn't one, then done.
+				 */
+				if (likely((tx_id == tx_first) && (count != 0)))
+					break;
+
+				/*
+				 * Walk the list and find the next mbuf, if any.
+				 */
+				do {
+					/* Move to next segemnt. */
+					tx_id = sw_ring[tx_id].next_id;
+
+					if (sw_ring[tx_id].mbuf)
+						break;
+
+				} while (tx_id != tx_first);
+
+				/*
+				 * Determine why previous loop bailed. If there
+				 * is not an mbuf, done.
+				 */
+				if (sw_ring[tx_id].mbuf == NULL)
+					break;
+			}
+		}
+	} else
+		count = -ENODEV;
+
+	return count;
+}
+
+int
+eth_igb_tx_done_cleanup(void *txq, uint32_t free_cnt)
+{
+	return igb_tx_done_cleanup(txq, free_cnt);
+}
+
 static void
 igb_reset_tx_queue_stat(struct igb_tx_queue *txq)
 {