mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next v4 1/2] pppoe: pass bound packets directly to generic PPP
@ 2026-11-03 11:17 Qingfang Deng
  2026-11-03 11:17 ` [PATCH net-next v4 2/2] pppoe: queue unbound packets directly Qingfang Deng
  0 siblings, 1 reply; 2+ messages in thread
From: Qingfang Deng @ 2026-11-03 11:17 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Asim Viladi Oglu Manizada,
	Eric Woudstra, Felix Fietkau, netdev, linux-kernel

Bound PPPoE sockets pass received frames to the generic PPP layer. They
currently do so through __sk_receive_skb(), which takes the socket BH
lock and serializes calls to ppp_input().

The lock originally prevented ppp_input() from racing with
ppp_unregister_channel(). Commit ec4215683e47 ("ppp: defer channel free
to an RCU grace period to fix pppol2tp RX UAF") now keeps the generic
PPP channel alive until in-flight RCU readers have completed, so bound
packets can be passed directly to ppp_input() from pppoe_rcv().

The direct bound receive path also skips the socket filter check in
__sk_receive_skb(). This does not affect normal pppd PPPoE operation:
pppd uses the kernel PPP channel and does not attach a receive filter to
a socket. Instead, it uses the generic PPP pass and active filters,
installed through PPPIOCSPASS and PPPIOCSACTIVE, unaffected by this
change. Set skb->dev to NULL to match __sk_receive_skb()'s other
behavior.

Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
A local receive-path benchmark sends 1492-byte IPv4 packets through
multiqueue TAP -> PPPoE -> PPP in a 16-vCPU x86-64 QEMU KVM guest.
The guest runs on 16 E-cores. Senders are pinned to distinct guest CPUs.
CONFIG_PARAVIRT_SPINLOCKS and CONFIG_X86_NATIVE_CPU are enabled.
This synchronous TAP receive path does not use GRO.
The generic PPP receive lock (ppp->rlock) is retained in both
variants. Median throughput over 12 samples per cell across four
boots, expressed as IP-layer Gbps:

  Senders       Before       After     Change
        1        19.08       19.63     +2.89%
        2        31.52       33.77     +7.15%
        4        53.67       63.48    +18.28%
        8        39.79       51.50    +29.42%
       16        49.40       61.33    +24.15%

Repeating the test with a separate benchmark-only change that removes
the generic PPP receive lock:

  Senders       Before       After     Change
        1        19.45       19.89     +2.23%
        2        33.29       34.92     +4.90%
        4        61.72       67.01     +8.57%
        8        44.46      123.76   +178.35%
       16        54.62      220.48   +303.69%

Eight and sixteen senders perform worse than four whenever at least
one receive lock remains. With both locks removed, throughput
rises from 67.01 Gbps with four senders to 123.76 Gbps with eight
and 220.48 Gbps with sixteen.

A separate matched GRO-off/on test uses threaded TAP NAPI and UDP
packets with a valid checksum. Senders and NAPI threads share the same
1/2/4/8/16 guest-CPU sets. UDP GRO forwarding is enabled in both modes;
only rx-gro changes. Medians use the same sample and boot counts.
With the generic PPP receive lock retained, in IP-layer Gbps:

                   GRO off                GRO on
  Senders     Before      After      Before      After
        1       13.44       13.42       14.35       14.15
        2       21.11       20.81       25.80       25.26
        4       42.18       40.89       45.31       44.76
        8       48.09       59.12       74.96       75.73
       16       46.84       54.99      142.00      142.27

With the benchmark-only generic PPP receive lock removal:

                   GRO off                GRO on
  Senders     Before      After      Before      After
        1       13.47       13.41       14.20       14.32
        2       20.63       19.86       25.58       25.21
        4       41.15       40.21       41.91       44.02
        8       50.61       70.97       76.39       74.38
       16       48.69      122.52      141.55      142.89

GRO coalesces 3.33-5.07 input packets per PPP receive skb across
measured samples. Packet and byte counters account for every datagram,
with zero TAP drops or PPP receive errors. The threaded-NAPI results
are a separate comparison from the synchronous TAP results above.

GPT Codex assisted in the benchmarks.
---
v4: repost after net-next is open, and set skb->dev = NULL before input.
    attach a benchmark with TAP's NAPI path.

v3:
https://lore.kernel.org/netdev/20261030045943.2093-1-qingfang.deng@linux.dev/
---
 drivers/net/ppp/pppoe.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 67c28ac763ef..5d5187e4976b 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -415,6 +415,12 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	if (!po)
 		goto drop;
 
+	skb->dev = NULL;
+
+	if (likely(po->sk.sk_state & PPPOX_BOUND)) {
+		ppp_input(&po->chan, skb);
+		return NET_RX_SUCCESS;
+	}
 	return __sk_receive_skb(&po->sk, skb, 0, 1, false);
 
 drop:
-- 
2.34.1


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

* [PATCH net-next v4 2/2] pppoe: queue unbound packets directly
  2026-11-03 11:17 [PATCH net-next v4 1/2] pppoe: pass bound packets directly to generic PPP Qingfang Deng
@ 2026-11-03 11:17 ` Qingfang Deng
  0 siblings, 0 replies; 2+ messages in thread
From: Qingfang Deng @ 2026-11-03 11:17 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Asim Viladi Oglu Manizada,
	Eric Woudstra, Felix Fietkau, netdev, linux-kernel

Queue unbound packets directly with sock_queue_rcv_skb() and remove
the unused backlog callback.

Previously, __sk_receive_skb() serialized receive processing with
PPPIOCGCHAN through the socket lock. Receivers either finished before
the ioctl acquired ownership or ran from the backlog after PPPOX_BOUND
was set.

The new lockless path relies on synchronize_net() from commit
92c1bf630abf ("pppox: drain queued packets on channel handoff").
After setting PPPOX_BOUND, PPPIOCGCHAN waits for receivers that saw
the old state to finish queueing before draining sk_receive_queue
into PPP. This prevents packets from being stranded after handoff.
SOCK_RCU_FREE protects the socket lifetime during teardown.

Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
v4: no changes

v3:
https://lore.kernel.org/netdev/20261030045943.2093-2-qingfang.deng@linux.dev/
---
 drivers/net/ppp/pppoe.c | 40 ++++++----------------------------------
 1 file changed, 6 insertions(+), 34 deletions(-)

diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 5d5187e4976b..51445fddb4d3 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -340,35 +340,7 @@ static struct notifier_block pppoe_notifier = {
 
 /************************************************************************
  *
- * Do the real work of receiving a PPPoE Session frame.
- *
- ***********************************************************************/
-static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
-{
-	struct pppox_sock *po = pppox_sk(sk);
-
-	/* Backlog receive. Semantics of backlog rcv preclude any code from
-	 * executing in lock_sock()/release_sock() bounds; meaning sk->sk_state
-	 * can't change.
-	 */
-
-	if (sk->sk_state & PPPOX_BOUND) {
-		ppp_input(&po->chan, skb);
-	} else {
-		if (sock_queue_rcv_skb(sk, skb))
-			goto abort_kfree;
-	}
-
-	return NET_RX_SUCCESS;
-
-abort_kfree:
-	kfree_skb(skb);
-	return NET_RX_DROP;
-}
-
-/************************************************************************
- *
- * Receive wrapper called in BH context.
+ * Receive a PPPoE Session frame.
  *
  ***********************************************************************/
 static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
@@ -417,11 +389,12 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
 
 	skb->dev = NULL;
 
-	if (likely(po->sk.sk_state & PPPOX_BOUND)) {
+	if (likely(po->sk.sk_state & PPPOX_BOUND))
 		ppp_input(&po->chan, skb);
-		return NET_RX_SUCCESS;
-	}
-	return __sk_receive_skb(&po->sk, skb, 0, 1, false);
+	else if (sock_queue_rcv_skb(&po->sk, skb))
+		goto drop;
+
+	return NET_RX_SUCCESS;
 
 drop:
 	kfree_skb(skb);
@@ -528,7 +501,6 @@ static int pppoe_create(struct net *net, struct socket *sock, int kern)
 	sock->state	= SS_UNCONNECTED;
 	sock->ops	= &pppoe_ops;
 
-	sk->sk_backlog_rcv	= pppoe_rcv_core;
 	sk->sk_destruct		= pppoe_destruct;
 	sk->sk_state		= PPPOX_NONE;
 	sk->sk_type		= SOCK_STREAM;
-- 
2.34.1


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

end of thread, other threads:[~2026-11-03 11:17 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03 11:17 [PATCH net-next v4 1/2] pppoe: pass bound packets directly to generic PPP Qingfang Deng
2026-11-03 11:17 ` [PATCH net-next v4 2/2] pppoe: queue unbound packets directly Qingfang Deng

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

all inboxes | Powered by JetHome®