The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
@ 2026-07-06  9:42 Simon Schippers
  2026-07-06 10:11 ` Brett A C Sheffield
  0 siblings, 1 reply; 5+ messages in thread
From: Simon Schippers @ 2026-07-06  9:42 UTC (permalink / raw)
  To: Willem de Bruijn, Jason Wang, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Michael S . Tsirkin, netdev
  Cc: Simon Horman, Jonathan Corbet, Shuah Khan, Andrew Lunn,
	Tim Gebauer, Brett Sheffield, linux-doc, linux-kernel,
	Simon Schippers

Commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop
when a qdisc is present") did not show a relevant performance regression
in my testing but on Brett Sheffield's librecast testbed it shows a
significant performance drop in a IPv6 multicast testcase. The regression
can be pinpointed when multiple iperf3 UDP threads are sending. For 8
threads the performance dropped from 13.5 Gbit/s to 9.13 Gbit/s. This is
the reason why this patch makes the qdisc backpressure behavior opt-in.

One option to accomplish the opt-in would be to set the default qdisc to
noqueue at init. However this may also break userspace as users might
have chosen a custom qdisc even though most of the qdiscs did nothing
for tun/tap in the past due to missing backpressure...

This is the reason why in this patch, the flag IFF_BACKPRESSURE is
introduced instead which is required to enable the backpressure logic.
This means the stopping logic in tun_net_xmit() and the waking logic in
__tun_wake_queue() are skipped if the flag is disabled.

To avoid a possible stall due to disabling IFF_BACKPRESSURE, the new
helper tun_force_wake_queue() is implemented. The helper safely wakes the
respective netdev queue and resets cons_cnt while the consumer_lock and
the producer_lock of the ring are held. The helper is run in tun_attach()
when a queue (re)attaches, in tun_set_iff() for attached tfiles, and
in tun_queue_resize().

The documentation in tuntap.rst is updated accordingly.

Fixes: 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present")
Reported-by: Brett Sheffield <brett@librecast.net>
Closes: https://lore.kernel.org/netdev/akVnoOYQOrt8k-Gu@karahi.librecast.net/T/#u
Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de>
---
V1 -> V2:
- Sashiko: Ensure detached queues are woken on re-attach by calling the
  new tun_force_wake_queue() helper from tun_attach(), and reuse it
  across the existing wake paths.
- Specify the failing test case in the commit message.
---
 Documentation/networking/tuntap.rst | 17 ++++++++++
 drivers/net/tun.c                   | 49 ++++++++++++++++++-----------
 include/uapi/linux/if_tun.h         |  1 +
 tools/include/uapi/linux/if_tun.h   |  1 +
 4 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/Documentation/networking/tuntap.rst b/Documentation/networking/tuntap.rst
index 4d7087f727be..599264825dd2 100644
--- a/Documentation/networking/tuntap.rst
+++ b/Documentation/networking/tuntap.rst
@@ -206,6 +206,23 @@ enable is true we enable it, otherwise we disable it::
       return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
   }
 
+3.4 qdisc backpressure
+----------------------
+
+Starting with Linux 7.2, IFF_BACKPRESSURE can be set to enable qdisc
+backpressure. Without it, TX drops occur when the internal ring buffer is
+full. With it, the kernel stops the TX queue instead, letting the qdisc
+hold packets. Drops only occur as a rare race. This can benefit protocols
+like TCP that react to drops. Backpressure requires a qdisc to be
+attached and has no effect with noqueue.
+
+The TUN/TAP ring buffer size can be reduced alongside this flag to
+further shift buffering into the qdisc and reduce bufferbloat, but comes
+at possible performance cost.
+
+When running multiple network streams in parallel, the flag may reduce
+performance due to the extra overhead of the backpressure mechanism.
+
 Universal TUN/TAP device driver Frequently Asked Question
 =========================================================
 
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index ffbe6f13fb1f..62e5eab4e345 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -98,7 +98,8 @@ static void tun_default_link_ksettings(struct net_device *dev,
 #define TUN_FASYNC	IFF_ATTACH_QUEUE
 
 #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
-		      IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS)
+		      IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS | \
+		      IFF_BACKPRESSURE)
 
 #define GOODCOPY_LEN 128
 
@@ -694,6 +695,20 @@ static void tun_detach_all(struct net_device *dev)
 		module_put(THIS_MODULE);
 }
 
+static void tun_force_wake_queue(struct tun_struct *tun,
+				 struct tun_file *tfile)
+{
+	/* Ensure that the producer can not stop the
+	 * queue concurrently by taking locks.
+	 */
+	spin_lock_bh(&tfile->tx_ring.consumer_lock);
+	spin_lock(&tfile->tx_ring.producer_lock);
+	netif_wake_subqueue(tun->dev, tfile->queue_index);
+	tfile->cons_cnt = 0;
+	spin_unlock(&tfile->tx_ring.producer_lock);
+	spin_unlock_bh(&tfile->tx_ring.consumer_lock);
+}
+
 static int tun_attach(struct tun_struct *tun, struct file *file,
 		      bool skip_filter, bool napi, bool napi_frags,
 		      bool publish_tun)
@@ -737,11 +752,9 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
 		goto out;
 	}
 
-	spin_lock(&tfile->tx_ring.consumer_lock);
-	tfile->cons_cnt = 0;
-	spin_unlock(&tfile->tx_ring.consumer_lock);
 	tfile->queue_index = tun->numqueues;
 	tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
+	tun_force_wake_queue(tun, tfile);
 
 	if (tfile->detached) {
 		/* Re-attach detached tfile, updating XDP queue_index */
@@ -1077,7 +1090,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	spin_lock(&tfile->tx_ring.producer_lock);
 	ret = __ptr_ring_produce(&tfile->tx_ring, skb);
-	if (!qdisc_txq_has_no_queue(queue) &&
+	if ((tun->flags & IFF_BACKPRESSURE) &&
+	    !qdisc_txq_has_no_queue(queue) &&
 	    __ptr_ring_check_produce(&tfile->tx_ring) == -ENOSPC) {
 		netif_tx_stop_queue(queue);
 		/* Paired with smp_mb() in __tun_wake_queue() */
@@ -2151,8 +2165,12 @@ static ssize_t tun_put_user(struct tun_struct *tun,
 static void __tun_wake_queue(struct tun_struct *tun,
 			     struct tun_file *tfile, int consumed)
 {
-	struct netdev_queue *txq = netdev_get_tx_queue(tun->dev,
-						tfile->queue_index);
+	struct netdev_queue *txq;
+
+	if (!(tun->flags & IFF_BACKPRESSURE))
+		return;
+
+	txq = netdev_get_tx_queue(tun->dev, tfile->queue_index);
 
 	/* Paired with smp_mb__after_atomic() in tun_net_xmit() */
 	smp_mb();
@@ -2764,7 +2782,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 	struct tun_struct *tun;
 	struct tun_file *tfile = file->private_data;
 	struct net_device *dev;
-	int err;
+	int err, i;
 
 	if (tfile->detached)
 		return -EINVAL;
@@ -2894,7 +2912,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
 	 * xoff state.
 	 */
 	if (netif_running(tun->dev))
-		netif_tx_wake_all_queues(tun->dev);
+		for (i = 0; i < tun->numqueues; i++)
+			tun_force_wake_queue(tun, rtnl_dereference(tun->tfiles[i]));
 
 	strscpy(ifr->ifr_name, tun->dev->name);
 	return 0;
@@ -3690,15 +3709,9 @@ static int tun_queue_resize(struct tun_struct *tun)
 					  dev->tx_queue_len, GFP_KERNEL,
 					  tun_ptr_free);
 
-	if (!ret) {
-		for (i = 0; i < tun->numqueues; i++) {
-			tfile = rtnl_dereference(tun->tfiles[i]);
-			spin_lock(&tfile->tx_ring.consumer_lock);
-			netif_wake_subqueue(tun->dev, tfile->queue_index);
-			tfile->cons_cnt = 0;
-			spin_unlock(&tfile->tx_ring.consumer_lock);
-		}
-	}
+	if (!ret)
+		for (i = 0; i < tun->numqueues; i++)
+			tun_force_wake_queue(tun, rtnl_dereference(tun->tfiles[i]));
 
 	kfree(rings);
 	return ret;
diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h
index 79d53c7a1ebd..73a77141315c 100644
--- a/include/uapi/linux/if_tun.h
+++ b/include/uapi/linux/if_tun.h
@@ -69,6 +69,7 @@
 #define IFF_NAPI_FRAGS	0x0020
 /* Used in TUNSETIFF to bring up tun/tap without carrier */
 #define IFF_NO_CARRIER	0x0040
+#define IFF_BACKPRESSURE	0x0080
 #define IFF_NO_PI	0x1000
 /* This flag has no real effect */
 #define IFF_ONE_QUEUE	0x2000
diff --git a/tools/include/uapi/linux/if_tun.h b/tools/include/uapi/linux/if_tun.h
index 2ec07de1d73b..97b670f5bc0a 100644
--- a/tools/include/uapi/linux/if_tun.h
+++ b/tools/include/uapi/linux/if_tun.h
@@ -67,6 +67,7 @@
 #define IFF_TAP		0x0002
 #define IFF_NAPI	0x0010
 #define IFF_NAPI_FRAGS	0x0020
+#define IFF_BACKPRESSURE	0x0080
 #define IFF_NO_PI	0x1000
 /* This flag has no real effect */
 #define IFF_ONE_QUEUE	0x2000
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
  2026-07-06  9:42 [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE Simon Schippers
@ 2026-07-06 10:11 ` Brett A C Sheffield
  2026-07-06 13:23   ` Michael S. Tsirkin
  0 siblings, 1 reply; 5+ messages in thread
From: Brett A C Sheffield @ 2026-07-06 10:11 UTC (permalink / raw)
  To: Simon Schippers
  Cc: Willem de Bruijn, Jason Wang, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Michael S . Tsirkin, netdev,
	Simon Horman, Jonathan Corbet, Shuah Khan, Andrew Lunn,
	Tim Gebauer, linux-doc, linux-kernel

On 2026-07-06 11:42, Simon Schippers wrote:
> Commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop
> when a qdisc is present") did not show a relevant performance regression
> in my testing but on Brett Sheffield's librecast testbed it shows a
> significant performance drop in a IPv6 multicast testcase. The regression
> can be pinpointed when multiple iperf3 UDP threads are sending. For 8
> threads the performance dropped from 13.5 Gbit/s to 9.13 Gbit/s. This is
> the reason why this patch makes the qdisc backpressure behavior opt-in.

Your v1 commit message was correct.  The iperf3 tests were TCP, not UDP.

The original failing test that alerted me to the problem was IPv6 multicast
(UDP), but the reproducer tests I provided stats for in the regression report
were TCP "To eliminate my code and any multicast weirdness" and also to verify
that this also affected TCP.

Sorry for the confusion. The command lines used are in the regression report.

I've tested the v2 patch (with IPv6 multicast), and verified the
previously failing test passes.

Tested-by: Brett A C Sheffield <bacs@librecast.net>

Cheers,


Brett
-- 
Brett Sheffield (he/him)
Librecast - Decentralising the Internet with Multicast
https://librecast.net/
https://blog.brettsheffield.com/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
  2026-07-06 10:11 ` Brett A C Sheffield
@ 2026-07-06 13:23   ` Michael S. Tsirkin
  2026-07-06 15:33     ` Simon Schippers
  0 siblings, 1 reply; 5+ messages in thread
From: Michael S. Tsirkin @ 2026-07-06 13:23 UTC (permalink / raw)
  To: Brett A C Sheffield
  Cc: Simon Schippers, Willem de Bruijn, Jason Wang, David S . Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, Simon Horman,
	Jonathan Corbet, Shuah Khan, Andrew Lunn, Tim Gebauer, linux-doc,
	linux-kernel

On Mon, Jul 06, 2026 at 12:11:15PM +0200, Brett A C Sheffield wrote:
> On 2026-07-06 11:42, Simon Schippers wrote:
> > Commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop
> > when a qdisc is present") did not show a relevant performance regression
> > in my testing but on Brett Sheffield's librecast testbed it shows a
> > significant performance drop in a IPv6 multicast testcase. The regression
> > can be pinpointed when multiple iperf3 UDP threads are sending. For 8
> > threads the performance dropped from 13.5 Gbit/s to 9.13 Gbit/s. This is
> > the reason why this patch makes the qdisc backpressure behavior opt-in.
> 
> Your v1 commit message was correct.  The iperf3 tests were TCP, not UDP.
> 
> The original failing test that alerted me to the problem was IPv6 multicast
> (UDP), but the reproducer tests I provided stats for in the regression report
> were TCP "To eliminate my code and any multicast weirdness" and also to verify
> that this also affected TCP.
> 
> Sorry for the confusion. The command lines used are in the regression report.
> 
> I've tested the v2 patch (with IPv6 multicast), and verified the
> previously failing test passes.
> 
> Tested-by: Brett A C Sheffield <bacs@librecast.net>
> 
> Cheers,


Just to clarify, it's more of a work-around, not a fix.

It's not really great to have a flag that says "change something
opaque in the internals of the device, it affects performance
in some way, we can't predict how".

So maybe we really should revert for now, and work on something
more coherent for the next linux.


> 
> Brett
> -- 
> Brett Sheffield (he/him)
> Librecast - Decentralising the Internet with Multicast
> https://librecast.net/
> https://blog.brettsheffield.com/


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
  2026-07-06 13:23   ` Michael S. Tsirkin
@ 2026-07-06 15:33     ` Simon Schippers
  2026-07-06 17:10       ` Brett A C Sheffield
  0 siblings, 1 reply; 5+ messages in thread
From: Simon Schippers @ 2026-07-06 15:33 UTC (permalink / raw)
  To: Michael S. Tsirkin, Brett A C Sheffield
  Cc: Willem de Bruijn, Jason Wang, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netdev, Simon Horman,
	Jonathan Corbet, Shuah Khan, Andrew Lunn, Tim Gebauer, linux-doc,
	linux-kernel

On 7/6/26 15:23, Michael S. Tsirkin wrote:
> On Mon, Jul 06, 2026 at 12:11:15PM +0200, Brett A C Sheffield wrote:
>> On 2026-07-06 11:42, Simon Schippers wrote:
>>> Commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop
>>> when a qdisc is present") did not show a relevant performance regression
>>> in my testing but on Brett Sheffield's librecast testbed it shows a
>>> significant performance drop in a IPv6 multicast testcase. The regression
>>> can be pinpointed when multiple iperf3 UDP threads are sending. For 8
>>> threads the performance dropped from 13.5 Gbit/s to 9.13 Gbit/s. This is
>>> the reason why this patch makes the qdisc backpressure behavior opt-in.
>>
>> Your v1 commit message was correct.  The iperf3 tests were TCP, not UDP.
>>
>> The original failing test that alerted me to the problem was IPv6 multicast
>> (UDP), but the reproducer tests I provided stats for in the regression report
>> were TCP "To eliminate my code and any multicast weirdness" and also to verify
>> that this also affected TCP.
>>
>> Sorry for the confusion. The command lines used are in the regression report.
>>
>> I've tested the v2 patch (with IPv6 multicast), and verified the
>> previously failing test passes.
>>
>> Tested-by: Brett A C Sheffield <bacs@librecast.net>
>>
>> Cheers,
> 
> 
> Just to clarify, it's more of a work-around, not a fix.
> 
> It's not really great to have a flag that says "change something
> opaque in the internals of the device, it affects performance
> in some way, we can't predict how".

Yes, I agree, but in the end I am just using netif_tx_stop_queue() and
netif_tx_wake_queue()...

> 
> So maybe we really should revert for now, and work on something
> more coherent for the next linux.

But even if we could perfectly fix the performance issues, maybe users
even users rely on the dropping behavior. From Brett [1]:

"In our multicast use case data is sent by multiple threads to multiple
groups simultaneously, this just breaks things to the extent that a 
<2 second test times out after 5 minutes."

We are *not* factor 5min * 60sec/min / 2s = 150 times slower than without
the patchset. My theory is that the sender sends a fixed amount of data
of which most is dropped without backpressure, which is much faster then
the real processing, and so the test *relies* on the tail-dropping to
work.

@Brett can you maybe support this theory?

Thank you both very much! :)

[1] Link: https://lore.kernel.org/netdev/akVnoOYQOrt8k-Gu@karahi.librecast.net/

> 
> 
>>
>> Brett
>> -- 
>> Brett Sheffield (he/him)
>> Librecast - Decentralising the Internet with Multicast
>> https://librecast.net/
>> https://blog.brettsheffield.com/
> 

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
  2026-07-06 15:33     ` Simon Schippers
@ 2026-07-06 17:10       ` Brett A C Sheffield
  0 siblings, 0 replies; 5+ messages in thread
From: Brett A C Sheffield @ 2026-07-06 17:10 UTC (permalink / raw)
  To: Simon Schippers
  Cc: Michael S. Tsirkin, Willem de Bruijn, Jason Wang,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	netdev, Simon Horman, Jonathan Corbet, Shuah Khan, Andrew Lunn,
	Tim Gebauer, linux-doc, linux-kernel

On 2026-07-06 17:33, Simon Schippers wrote:
> But even if we could perfectly fix the performance issues, maybe users
> even users rely on the dropping behavior. From Brett [1]:
> 
> "In our multicast use case data is sent by multiple threads to multiple
> groups simultaneously, this just breaks things to the extent that a 
> <2 second test times out after 5 minutes."
> 
> We are *not* factor 5min * 60sec/min / 2s = 150 times slower than without
> the patchset.

I didn't mean to suggest 150x slower. It would have been more correct if I'd
simply said "the test normally takes <2s but fails to complete with the
patchset". The 5min timeout was irrelevant detail.

The iperf3 tests give a much better picture of the performance impact.

I thought a simple TCP test with a familar tool might be easier than explaining
the ways in which we're torturing multicast ;-)

> My theory is that the sender sends a fixed amount of data
> of which most is dropped without backpressure, which is much faster then
> the real processing, and so the test *relies* on the tail-dropping to
> work.
> 
> @Brett can you maybe support this theory?

The test synchronizes two blobs of data. The amount of data that needs syncing
is fixed, but the amount sent will vary as it is encoded with RaptorQ.

The test sends on several multicast groups simultaneously. Each group is a
stream of RaptorQ encoded symbols and the receiver listens on that group until
is has enough symbols to decode. In practice, on a local tap interface, the
packet loss is normally zero, so the amount of data sent is more or less fixed.



-- 
Brett Sheffield (he/him)
Librecast - Decentralising the Internet with Multicast
https://librecast.net/
https://blog.brettsheffield.com/

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-06 17:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06  9:42 [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE Simon Schippers
2026-07-06 10:11 ` Brett A C Sheffield
2026-07-06 13:23   ` Michael S. Tsirkin
2026-07-06 15:33     ` Simon Schippers
2026-07-06 17:10       ` Brett A C Sheffield

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox