* [dpdk-dev] net/e1000: fix segfault on tx done clean up
@ 2020-07-16 8:53 Jeff Guo
2020-07-16 9:15 ` Zhao1, Wei
0 siblings, 1 reply; 4+ messages in thread
From: Jeff Guo @ 2020-07-16 8:53 UTC (permalink / raw)
To: wei.zhao1; +Cc: bmcfall, dev, jia.guo
As tx mbuf is not set for some advanced descriptors, if there is no
mbuf checking before rte_pktmbuf_free_seg() function be called on
the process of tx done clean up, that will cause a segfault. So add
a NULL pointer check to fix it.
Bugzilla ID: 501
Fixes: 8d907d2b79f7 (net/igb: free consumed Tx buffers on demand)
Signed-off-by: Jeff Guo <jia.guo@intel.com>
---
drivers/net/e1000/igb_rxtx.c | 170 +++++++++++++++++------------------
1 file changed, 82 insertions(+), 88 deletions(-)
diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c
index 5717cdb70..115a723e4 100644
--- a/drivers/net/e1000/igb_rxtx.c
+++ b/drivers/net/e1000/igb_rxtx.c
@@ -1295,113 +1295,107 @@ igb_tx_done_cleanup(struct igb_tx_queue *txq, uint32_t free_cnt)
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;
+ int count = 0;
- if (txq != NULL) {
- count = 0;
- sw_ring = txq->sw_ring;
- txr = txq->tx_ring;
+ if (!txq)
+ return = -ENODEV;
- /*
- * 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.
- */
+ sw_ring = txq->sw_ring;
+ txr = txq->tx_ring;
- /* Get last segment in most recently added packet. */
- tx_first = sw_ring[txq->tx_tail].last_id;
+ /* 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 the next segment, which is the oldest segment in ring. */
- tx_first = sw_ring[tx_first].next_id;
+ /* Get last segment in most recently added packet. */
+ tx_first = sw_ring[txq->tx_tail].last_id;
- /* Set the current index to the first. */
- tx_id = tx_first;
+ /* Get the next segment, which is the oldest segment in ring. */
+ tx_first = sw_ring[tx_first].next_id;
- /*
- * 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);
+ /* 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 {
+ if (sw_ring[tx_id].mbuf) {
+ 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;
+ /* Move to next segemnt. */
+ tx_id = sw_ring[tx_id].next_id;
- } while (tx_id != tx_next);
+ } while (tx_id != tx_next);
- if (unlikely(count == (int)free_cnt))
- break;
- } else
- /*
- * mbuf still in use, nothing left to
- * free.
- */
+ if (unlikely(count == (int)free_cnt))
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.
+ /* mbuf still in use, nothing left to
+ * free.
*/
- if (likely((tx_id == tx_first) && (count != 0)))
- break;
+ 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;
+ /* 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;
+ if (sw_ring[tx_id].mbuf)
+ break;
- } while (tx_id != tx_first);
+ } 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;
- }
+ /* Determine why previous loop bailed. If there
+ * is not an mbuf, done.
+ */
+ if (!sw_ring[tx_id].mbuf)
+ break;
}
- } else
- count = -ENODEV;
+ }
return count;
}
--
2.20.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [dpdk-dev] net/e1000: fix segfault on tx done clean up
2020-07-16 8:53 [dpdk-dev] net/e1000: fix segfault on tx done clean up Jeff Guo
@ 2020-07-16 9:15 ` Zhao1, Wei
2020-07-21 5:40 ` Zhang, Qi Z
0 siblings, 1 reply; 4+ messages in thread
From: Zhao1, Wei @ 2020-07-16 9:15 UTC (permalink / raw)
To: Guo, Jia; +Cc: bmcfall, dev
Reviewed-by: Wei Zhao <wei.zhao1@intel.com>
> -----Original Message-----
> From: Guo, Jia <jia.guo@intel.com>
> Sent: Thursday, July 16, 2020 4:54 PM
> To: Zhao1, Wei <wei.zhao1@intel.com>
> Cc: bmcfall@redhat.com; dev@dpdk.org; Guo, Jia <jia.guo@intel.com>
> Subject: [dpdk-dev] net/e1000: fix segfault on tx done clean up
>
> As tx mbuf is not set for some advanced descriptors, if there is no mbuf
> checking before rte_pktmbuf_free_seg() function be called on the process of tx
> done clean up, that will cause a segfault. So add a NULL pointer check to fix it.
>
> Bugzilla ID: 501
> Fixes: 8d907d2b79f7 (net/igb: free consumed Tx buffers on demand)
>
> Signed-off-by: Jeff Guo <jia.guo@intel.com>
> ---
> drivers/net/e1000/igb_rxtx.c | 170 +++++++++++++++++------------------
> 1 file changed, 82 insertions(+), 88 deletions(-)
>
> diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c index
> 5717cdb70..115a723e4 100644
> --- a/drivers/net/e1000/igb_rxtx.c
> +++ b/drivers/net/e1000/igb_rxtx.c
> @@ -1295,113 +1295,107 @@ igb_tx_done_cleanup(struct igb_tx_queue *txq,
> uint32_t free_cnt)
> 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;
> + int count = 0;
>
> - if (txq != NULL) {
> - count = 0;
> - sw_ring = txq->sw_ring;
> - txr = txq->tx_ring;
> + if (!txq)
> + return = -ENODEV;
>
> - /*
> - * 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.
> - */
> + sw_ring = txq->sw_ring;
> + txr = txq->tx_ring;
>
> - /* Get last segment in most recently added packet. */
> - tx_first = sw_ring[txq->tx_tail].last_id;
> + /* 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 the next segment, which is the oldest segment in ring. */
> - tx_first = sw_ring[tx_first].next_id;
> + /* Get last segment in most recently added packet. */
> + tx_first = sw_ring[txq->tx_tail].last_id;
>
> - /* Set the current index to the first. */
> - tx_id = tx_first;
> + /* Get the next segment, which is the oldest segment in ring. */
> + tx_first = sw_ring[tx_first].next_id;
>
> - /*
> - * 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);
> + /* 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 {
> + if (sw_ring[tx_id].mbuf) {
> + 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;
> + /* Move to next segemnt. */
> + tx_id = sw_ring[tx_id].next_id;
>
> - } while (tx_id != tx_next);
> + } while (tx_id != tx_next);
>
> - if (unlikely(count == (int)free_cnt))
> - break;
> - } else
> - /*
> - * mbuf still in use, nothing left to
> - * free.
> - */
> + if (unlikely(count == (int)free_cnt))
> 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.
> + /* mbuf still in use, nothing left to
> + * free.
> */
> - if (likely((tx_id == tx_first) && (count != 0)))
> - break;
> + 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;
> + /* 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;
> + if (sw_ring[tx_id].mbuf)
> + break;
>
> - } while (tx_id != tx_first);
> + } 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;
> - }
> + /* Determine why previous loop bailed. If there
> + * is not an mbuf, done.
> + */
> + if (!sw_ring[tx_id].mbuf)
> + break;
> }
> - } else
> - count = -ENODEV;
> + }
>
> return count;
> }
> --
> 2.20.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [dpdk-dev] net/e1000: fix segfault on tx done clean up
2020-07-16 9:15 ` Zhao1, Wei
@ 2020-07-21 5:40 ` Zhang, Qi Z
0 siblings, 0 replies; 4+ messages in thread
From: Zhang, Qi Z @ 2020-07-21 5:40 UTC (permalink / raw)
To: Zhao1, Wei, Guo, Jia; +Cc: bmcfall, dev
> -----Original Message-----
> From: dev <dev-bounces@dpdk.org> On Behalf Of Zhao1, Wei
> Sent: Thursday, July 16, 2020 5:16 PM
> To: Guo, Jia <jia.guo@intel.com>
> Cc: bmcfall@redhat.com; dev@dpdk.org
> Subject: Re: [dpdk-dev] net/e1000: fix segfault on tx done clean up
>
>
> Reviewed-by: Wei Zhao <wei.zhao1@intel.com>
>
> > -----Original Message-----
> > From: Guo, Jia <jia.guo@intel.com>
> > Sent: Thursday, July 16, 2020 4:54 PM
> > To: Zhao1, Wei <wei.zhao1@intel.com>
> > Cc: bmcfall@redhat.com; dev@dpdk.org; Guo, Jia <jia.guo@intel.com>
> > Subject: [dpdk-dev] net/e1000: fix segfault on tx done clean up
> >
> > As tx mbuf is not set for some advanced descriptors, if there is no
> > mbuf checking before rte_pktmbuf_free_seg() function be called on the
> > process of tx done clean up, that will cause a segfault. So add a NULL pointer
> check to fix it.
> >
> > Bugzilla ID: 501
> > Fixes: 8d907d2b79f7 (net/igb: free consumed Tx buffers on demand)
> >
> > Signed-off-by: Jeff Guo <jia.guo@intel.com>
Applied to dpdk-next-net-intel.
Thanks
Qi
^ permalink raw reply [flat|nested] 4+ messages in thread
* [dpdk-dev] net/e1000: fix segfault on tx done clean up
@ 2020-07-16 8:50 Jeff Guo
0 siblings, 0 replies; 4+ messages in thread
From: Jeff Guo @ 2020-07-16 8:50 UTC (permalink / raw)
To: wei.zhao1; +Cc: bmcfall, dev, jia.guo
As tx mbuf is not set for some advanced descriptors, if there is no
mbuf checking before rte_pktmbuf_free_seg() function be called on
the process of tx done clean up, that will casue a segfault. So add
a NULL pointer check to fix it.
Bugzilla ID: 501
Fixes: 8d907d2b79f7 (net/igb: free consumed Tx buffers on demand)
Signed-off-by: Jeff Guo <jia.guo@intel.com>
---
drivers/net/e1000/igb_rxtx.c | 170 +++++++++++++++++------------------
1 file changed, 82 insertions(+), 88 deletions(-)
diff --git a/drivers/net/e1000/igb_rxtx.c b/drivers/net/e1000/igb_rxtx.c
index 5717cdb70..115a723e4 100644
--- a/drivers/net/e1000/igb_rxtx.c
+++ b/drivers/net/e1000/igb_rxtx.c
@@ -1295,113 +1295,107 @@ igb_tx_done_cleanup(struct igb_tx_queue *txq, uint32_t free_cnt)
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;
+ int count = 0;
- if (txq != NULL) {
- count = 0;
- sw_ring = txq->sw_ring;
- txr = txq->tx_ring;
+ if (!txq)
+ return = -ENODEV;
- /*
- * 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.
- */
+ sw_ring = txq->sw_ring;
+ txr = txq->tx_ring;
- /* Get last segment in most recently added packet. */
- tx_first = sw_ring[txq->tx_tail].last_id;
+ /* 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 the next segment, which is the oldest segment in ring. */
- tx_first = sw_ring[tx_first].next_id;
+ /* Get last segment in most recently added packet. */
+ tx_first = sw_ring[txq->tx_tail].last_id;
- /* Set the current index to the first. */
- tx_id = tx_first;
+ /* Get the next segment, which is the oldest segment in ring. */
+ tx_first = sw_ring[tx_first].next_id;
- /*
- * 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);
+ /* 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 {
+ if (sw_ring[tx_id].mbuf) {
+ 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;
+ /* Move to next segemnt. */
+ tx_id = sw_ring[tx_id].next_id;
- } while (tx_id != tx_next);
+ } while (tx_id != tx_next);
- if (unlikely(count == (int)free_cnt))
- break;
- } else
- /*
- * mbuf still in use, nothing left to
- * free.
- */
+ if (unlikely(count == (int)free_cnt))
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.
+ /* mbuf still in use, nothing left to
+ * free.
*/
- if (likely((tx_id == tx_first) && (count != 0)))
- break;
+ 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;
+ /* 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;
+ if (sw_ring[tx_id].mbuf)
+ break;
- } while (tx_id != tx_first);
+ } 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;
- }
+ /* Determine why previous loop bailed. If there
+ * is not an mbuf, done.
+ */
+ if (!sw_ring[tx_id].mbuf)
+ break;
}
- } else
- count = -ENODEV;
+ }
return count;
}
--
2.20.1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-07-21 5:40 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-16 8:53 [dpdk-dev] net/e1000: fix segfault on tx done clean up Jeff Guo
2020-07-16 9:15 ` Zhao1, Wei
2020-07-21 5:40 ` Zhang, Qi Z
-- strict thread matches above, loose matches on Subject: below --
2020-07-16 8:50 Jeff Guo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).