From: Qingfang Deng Date: Fri, 6 Feb 2026 09:38:50 +0000 (+0800) Subject: kernel: backport pppoe improvements X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=316492b809a67403264b91a32abf23e967c64bbf;p=thirdparty%2Fopenwrt.git kernel: backport pppoe improvements Backport PPP patches accepted upstream. Manually rebased: - target/linux/ipq40xx/patches-6.12/999-atm-mpoa-intel-dsl-phy-support.patch Signed-off-by: Qingfang Deng Link: https://github.com/openwrt/openwrt/pull/21892 Signed-off-by: Hauke Mehrtens --- diff --git a/target/linux/generic/backport-6.12/621-v6.17-ppp-convert-to-percpu-netstats.patch b/target/linux/generic/backport-6.12/621-v6.17-ppp-convert-to-percpu-netstats.patch new file mode 100644 index 00000000000..874d2bfd8ac --- /dev/null +++ b/target/linux/generic/backport-6.12/621-v6.17-ppp-convert-to-percpu-netstats.patch @@ -0,0 +1,126 @@ +From 1a3e9b7a6b09e8ab3d2af019e4a392622685855e Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Tue, 10 Jun 2025 16:32:10 +0800 +Subject: [PATCH] ppp: convert to percpu netstats + +Convert to percpu netstats to avoid lock contention when reading them. + +Signed-off-by: Qingfang Deng +Link: https://patch.msgid.link/20250610083211.909015-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/ppp_generic.c | 52 +++++++++++++---------------------- + 1 file changed, 19 insertions(+), 33 deletions(-) + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -108,18 +108,6 @@ struct ppp_file { + #define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) + + /* +- * Data structure to hold primary network stats for which +- * we want to use 64 bit storage. Other network stats +- * are stored in dev->stats of the ppp strucute. +- */ +-struct ppp_link_stats { +- u64 rx_packets; +- u64 tx_packets; +- u64 rx_bytes; +- u64 tx_bytes; +-}; +- +-/* + * Data structure describing one ppp unit. + * A ppp unit corresponds to a ppp network interface device + * and represents a multilink bundle. +@@ -162,7 +150,6 @@ struct ppp { + struct bpf_prog *active_filter; /* filter for pkts to reset idle */ + #endif /* CONFIG_PPP_FILTER */ + struct net *ppp_net; /* the net we belong to */ +- struct ppp_link_stats stats64; /* 64 bit network stats */ + }; + + /* +@@ -1541,23 +1528,12 @@ ppp_net_siocdevprivate(struct net_device + static void + ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) + { +- struct ppp *ppp = netdev_priv(dev); +- +- ppp_recv_lock(ppp); +- stats64->rx_packets = ppp->stats64.rx_packets; +- stats64->rx_bytes = ppp->stats64.rx_bytes; +- ppp_recv_unlock(ppp); +- +- ppp_xmit_lock(ppp); +- stats64->tx_packets = ppp->stats64.tx_packets; +- stats64->tx_bytes = ppp->stats64.tx_bytes; +- ppp_xmit_unlock(ppp); +- + stats64->rx_errors = dev->stats.rx_errors; + stats64->tx_errors = dev->stats.tx_errors; + stats64->rx_dropped = dev->stats.rx_dropped; + stats64->tx_dropped = dev->stats.tx_dropped; + stats64->rx_length_errors = dev->stats.rx_length_errors; ++ dev_fetch_sw_netstats(stats64, dev->tstats); + } + + static int ppp_dev_init(struct net_device *dev) +@@ -1655,6 +1631,7 @@ static void ppp_setup(struct net_device + dev->type = ARPHRD_PPP; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->priv_destructor = ppp_dev_priv_destructor; ++ dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + netif_keep_dst(dev); + } + +@@ -1800,8 +1777,7 @@ ppp_send_frame(struct ppp *ppp, struct s + #endif /* CONFIG_PPP_FILTER */ + } + +- ++ppp->stats64.tx_packets; +- ppp->stats64.tx_bytes += skb->len - PPP_PROTO_LEN; ++ dev_sw_netstats_tx_add(ppp->dev, 1, skb->len - PPP_PROTO_LEN); + + switch (proto) { + case PPP_IP: +@@ -2479,8 +2455,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, + break; + } + +- ++ppp->stats64.rx_packets; +- ppp->stats64.rx_bytes += skb->len - 2; ++ dev_sw_netstats_rx_add(ppp->dev, skb->len - PPP_PROTO_LEN); + + npi = proto_to_npindex(proto); + if (npi < 0) { +@@ -3308,14 +3283,25 @@ static void + ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) + { + struct slcompress *vj = ppp->vj; ++ int cpu; + + memset(st, 0, sizeof(*st)); +- st->p.ppp_ipackets = ppp->stats64.rx_packets; ++ for_each_possible_cpu(cpu) { ++ struct pcpu_sw_netstats *p = per_cpu_ptr(ppp->dev->tstats, cpu); ++ u64 rx_packets, rx_bytes, tx_packets, tx_bytes; ++ ++ rx_packets = u64_stats_read(&p->rx_packets); ++ rx_bytes = u64_stats_read(&p->rx_bytes); ++ tx_packets = u64_stats_read(&p->tx_packets); ++ tx_bytes = u64_stats_read(&p->tx_bytes); ++ ++ st->p.ppp_ipackets += rx_packets; ++ st->p.ppp_ibytes += rx_bytes; ++ st->p.ppp_opackets += tx_packets; ++ st->p.ppp_obytes += tx_bytes; ++ } + st->p.ppp_ierrors = ppp->dev->stats.rx_errors; +- st->p.ppp_ibytes = ppp->stats64.rx_bytes; +- st->p.ppp_opackets = ppp->stats64.tx_packets; + st->p.ppp_oerrors = ppp->dev->stats.tx_errors; +- st->p.ppp_obytes = ppp->stats64.tx_bytes; + if (!vj) + return; + st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed; diff --git a/target/linux/generic/backport-6.12/622-v6.18-ppp-remove-rwlock-usage.patch b/target/linux/generic/backport-6.12/622-v6.18-ppp-remove-rwlock-usage.patch new file mode 100644 index 00000000000..f66a5222244 --- /dev/null +++ b/target/linux/generic/backport-6.12/622-v6.18-ppp-remove-rwlock-usage.patch @@ -0,0 +1,334 @@ +From b8844aab519a154808dbce15a132f3e8f1c34af6 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Fri, 22 Aug 2025 09:25:47 +0800 +Subject: [PATCH] ppp: remove rwlock usage + +In struct channel, the upl lock is implemented using rwlock_t, +protecting access to pch->ppp and pch->bridge. + +As previously discussed on the list, using rwlock in the network fast +path is not recommended. +This patch replaces the rwlock with a spinlock for writers, and uses RCU +for readers. + +- pch->ppp and pch->bridge are now declared as __rcu pointers. +- Readers use rcu_dereference_bh() under rcu_read_lock_bh(). +- Writers use spin_lock() to update. + +Signed-off-by: Qingfang Deng +Link: https://patch.msgid.link/20250822012548.6232-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/ppp_generic.c | 120 ++++++++++++++++++---------------- + 1 file changed, 63 insertions(+), 57 deletions(-) + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -173,11 +173,11 @@ struct channel { + struct ppp_channel *chan; /* public channel data structure */ + struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */ + spinlock_t downl; /* protects `chan', file.xq dequeue */ +- struct ppp *ppp; /* ppp unit we're connected to */ ++ struct ppp __rcu *ppp; /* ppp unit we're connected to */ + struct net *chan_net; /* the net channel belongs to */ + netns_tracker ns_tracker; + struct list_head clist; /* link in list of channels per unit */ +- rwlock_t upl; /* protects `ppp' and 'bridge' */ ++ spinlock_t upl; /* protects `ppp' and 'bridge' */ + struct channel __rcu *bridge; /* "bridged" ppp channel */ + #ifdef CONFIG_PPP_MULTILINK + u8 avail; /* flag used in multilink stuff */ +@@ -639,34 +639,34 @@ static struct bpf_prog *compat_ppp_get_f + */ + static int ppp_bridge_channels(struct channel *pch, struct channel *pchb) + { +- write_lock_bh(&pch->upl); +- if (pch->ppp || ++ spin_lock(&pch->upl); ++ if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || + rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) { +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + return -EALREADY; + } + refcount_inc(&pchb->file.refcnt); + rcu_assign_pointer(pch->bridge, pchb); +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + +- write_lock_bh(&pchb->upl); +- if (pchb->ppp || ++ spin_lock(&pchb->upl); ++ if (rcu_dereference_protected(pchb->ppp, lockdep_is_held(&pchb->upl)) || + rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl))) { +- write_unlock_bh(&pchb->upl); ++ spin_unlock(&pchb->upl); + goto err_unset; + } + refcount_inc(&pch->file.refcnt); + rcu_assign_pointer(pchb->bridge, pch); +- write_unlock_bh(&pchb->upl); ++ spin_unlock(&pchb->upl); + + return 0; + + err_unset: +- write_lock_bh(&pch->upl); ++ spin_lock(&pch->upl); + /* Re-read pch->bridge with upl held in case it was modified concurrently */ + pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); + RCU_INIT_POINTER(pch->bridge, NULL); +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + synchronize_rcu(); + + if (pchb) +@@ -680,25 +680,25 @@ static int ppp_unbridge_channels(struct + { + struct channel *pchb, *pchbb; + +- write_lock_bh(&pch->upl); ++ spin_lock(&pch->upl); + pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl)); + if (!pchb) { +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + return -EINVAL; + } + RCU_INIT_POINTER(pch->bridge, NULL); +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + + /* Only modify pchb if phcb->bridge points back to pch. + * If not, it implies that there has been a race unbridging (and possibly + * even rebridging) pchb. We should leave pchb alone to avoid either a + * refcount underflow, or breaking another established bridge instance. + */ +- write_lock_bh(&pchb->upl); ++ spin_lock(&pchb->upl); + pchbb = rcu_dereference_protected(pchb->bridge, lockdep_is_held(&pchb->upl)); + if (pchbb == pch) + RCU_INIT_POINTER(pchb->bridge, NULL); +- write_unlock_bh(&pchb->upl); ++ spin_unlock(&pchb->upl); + + synchronize_rcu(); + +@@ -2144,10 +2144,9 @@ static int ppp_mp_explode(struct ppp *pp + #endif /* CONFIG_PPP_MULTILINK */ + + /* Try to send data out on a channel */ +-static void __ppp_channel_push(struct channel *pch) ++static void __ppp_channel_push(struct channel *pch, struct ppp *ppp) + { + struct sk_buff *skb; +- struct ppp *ppp; + + spin_lock(&pch->downl); + if (pch->chan) { +@@ -2166,7 +2165,6 @@ static void __ppp_channel_push(struct ch + spin_unlock(&pch->downl); + /* see if there is anything from the attached unit to be sent */ + if (skb_queue_empty(&pch->file.xq)) { +- ppp = pch->ppp; + if (ppp) + __ppp_xmit_process(ppp, NULL); + } +@@ -2174,15 +2172,18 @@ static void __ppp_channel_push(struct ch + + static void ppp_channel_push(struct channel *pch) + { +- read_lock_bh(&pch->upl); +- if (pch->ppp) { +- (*this_cpu_ptr(pch->ppp->xmit_recursion))++; +- __ppp_channel_push(pch); +- (*this_cpu_ptr(pch->ppp->xmit_recursion))--; ++ struct ppp *ppp; ++ ++ rcu_read_lock_bh(); ++ ppp = rcu_dereference_bh(pch->ppp); ++ if (ppp) { ++ (*this_cpu_ptr(ppp->xmit_recursion))++; ++ __ppp_channel_push(pch, ppp); ++ (*this_cpu_ptr(ppp->xmit_recursion))--; + } else { +- __ppp_channel_push(pch); ++ __ppp_channel_push(pch, NULL); + } +- read_unlock_bh(&pch->upl); ++ rcu_read_unlock_bh(); + } + + /* +@@ -2284,6 +2285,7 @@ void + ppp_input(struct ppp_channel *chan, struct sk_buff *skb) + { + struct channel *pch = chan->ppp; ++ struct ppp *ppp; + int proto; + + if (!pch) { +@@ -2295,18 +2297,19 @@ ppp_input(struct ppp_channel *chan, stru + if (ppp_channel_bridge_input(pch, skb)) + return; + +- read_lock_bh(&pch->upl); ++ rcu_read_lock_bh(); ++ ppp = rcu_dereference_bh(pch->ppp); + if (!ppp_decompress_proto(skb)) { + kfree_skb(skb); +- if (pch->ppp) { +- ++pch->ppp->dev->stats.rx_length_errors; +- ppp_receive_error(pch->ppp); ++ if (ppp) { ++ ++ppp->dev->stats.rx_length_errors; ++ ppp_receive_error(ppp); + } + goto done; + } + + proto = PPP_PROTO(skb); +- if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { ++ if (!ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { + /* put it on the channel queue */ + skb_queue_tail(&pch->file.rq, skb); + /* drop old frames if queue too long */ +@@ -2315,11 +2318,11 @@ ppp_input(struct ppp_channel *chan, stru + kfree_skb(skb); + wake_up_interruptible(&pch->file.rwait); + } else { +- ppp_do_recv(pch->ppp, skb, pch); ++ ppp_do_recv(ppp, skb, pch); + } + + done: +- read_unlock_bh(&pch->upl); ++ rcu_read_unlock_bh(); + } + + /* Put a 0-length skb in the receive queue as an error indication */ +@@ -2328,20 +2331,22 @@ ppp_input_error(struct ppp_channel *chan + { + struct channel *pch = chan->ppp; + struct sk_buff *skb; ++ struct ppp *ppp; + + if (!pch) + return; + +- read_lock_bh(&pch->upl); +- if (pch->ppp) { ++ rcu_read_lock_bh(); ++ ppp = rcu_dereference_bh(pch->ppp); ++ if (ppp) { + skb = alloc_skb(0, GFP_ATOMIC); + if (skb) { + skb->len = 0; /* probably unnecessary */ + skb->cb[0] = code; +- ppp_do_recv(pch->ppp, skb, pch); ++ ppp_do_recv(ppp, skb, pch); + } + } +- read_unlock_bh(&pch->upl); ++ rcu_read_unlock_bh(); + } + + /* +@@ -2889,7 +2894,6 @@ int ppp_register_net_channel(struct net + + pn = ppp_pernet(net); + +- pch->ppp = NULL; + pch->chan = chan; + pch->chan_net = get_net_track(net, &pch->ns_tracker, GFP_KERNEL); + chan->ppp = pch; +@@ -2900,7 +2904,7 @@ int ppp_register_net_channel(struct net + #endif /* CONFIG_PPP_MULTILINK */ + init_rwsem(&pch->chan_sem); + spin_lock_init(&pch->downl); +- rwlock_init(&pch->upl); ++ spin_lock_init(&pch->upl); + + spin_lock_bh(&pn->all_channels_lock); + pch->file.index = ++pn->last_channel_index; +@@ -2929,13 +2933,15 @@ int ppp_channel_index(struct ppp_channel + int ppp_unit_number(struct ppp_channel *chan) + { + struct channel *pch = chan->ppp; ++ struct ppp *ppp; + int unit = -1; + + if (pch) { +- read_lock_bh(&pch->upl); +- if (pch->ppp) +- unit = pch->ppp->file.index; +- read_unlock_bh(&pch->upl); ++ rcu_read_lock(); ++ ppp = rcu_dereference(pch->ppp); ++ if (ppp) ++ unit = ppp->file.index; ++ rcu_read_unlock(); + } + return unit; + } +@@ -2947,12 +2953,14 @@ char *ppp_dev_name(struct ppp_channel *c + { + struct channel *pch = chan->ppp; + char *name = NULL; ++ struct ppp *ppp; + + if (pch) { +- read_lock_bh(&pch->upl); +- if (pch->ppp && pch->ppp->dev) +- name = pch->ppp->dev->name; +- read_unlock_bh(&pch->upl); ++ rcu_read_lock(); ++ ppp = rcu_dereference(pch->ppp); ++ if (ppp && ppp->dev) ++ name = ppp->dev->name; ++ rcu_read_unlock(); + } + return name; + } +@@ -3475,9 +3483,9 @@ ppp_connect_channel(struct channel *pch, + ppp = ppp_find_unit(pn, unit); + if (!ppp) + goto out; +- write_lock_bh(&pch->upl); ++ spin_lock(&pch->upl); + ret = -EINVAL; +- if (pch->ppp || ++ if (rcu_dereference_protected(pch->ppp, lockdep_is_held(&pch->upl)) || + rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl))) + goto outl; + +@@ -3502,13 +3510,13 @@ ppp_connect_channel(struct channel *pch, + ppp->dev->hard_header_len = hdrlen; + list_add_tail_rcu(&pch->clist, &ppp->channels); + ++ppp->n_channels; +- pch->ppp = ppp; ++ rcu_assign_pointer(pch->ppp, ppp); + refcount_inc(&ppp->file.refcnt); + ppp_unlock(ppp); + ret = 0; + + outl: +- write_unlock_bh(&pch->upl); ++ spin_unlock(&pch->upl); + out: + mutex_unlock(&pn->all_ppp_mutex); + return ret; +@@ -3523,10 +3531,9 @@ ppp_disconnect_channel(struct channel *p + struct ppp *ppp; + int err = -EINVAL; + +- write_lock_bh(&pch->upl); +- ppp = pch->ppp; +- pch->ppp = NULL; +- write_unlock_bh(&pch->upl); ++ spin_lock(&pch->upl); ++ ppp = rcu_replace_pointer(pch->ppp, NULL, lockdep_is_held(&pch->upl)); ++ spin_unlock(&pch->upl); + if (ppp) { + /* remove it from the ppp unit's list */ + ppp_lock(ppp); diff --git a/target/linux/generic/backport-6.12/623-v6.18-pppoe-remove-rwlock-usage.patch b/target/linux/generic/backport-6.12/623-v6.18-pppoe-remove-rwlock-usage.patch new file mode 100644 index 00000000000..619f1cbf77b --- /dev/null +++ b/target/linux/generic/backport-6.12/623-v6.18-pppoe-remove-rwlock-usage.patch @@ -0,0 +1,321 @@ +From 72cdc67e7fa74931b055df3a76852bab551f1a04 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Thu, 28 Aug 2025 09:20:16 +0800 +Subject: [PATCH] pppoe: remove rwlock usage + +Like ppp_generic.c, convert the PPPoE socket hash table to use RCU for +lookups and a spinlock for updates. This removes rwlock usage and allows +lockless readers on the fast path. + +- Mark hash table and list pointers as __rcu. +- Use spin_lock() to protect writers. +- Readers use rcu_dereference() under rcu_read_lock(). All known callers + of get_item() already hold the RCU read lock, so no additional locking + is needed. +- get_item() now uses refcount_inc_not_zero() instead of sock_hold() to + safely take a reference. This prevents crashes if a socket is already + in the process of being freed (sk_refcnt == 0). +- Set SOCK_RCU_FREE to defer socket freeing until after an RCU grace + period. +- Move skb_queue_purge() into sk_destruct callback to ensure purge + happens after an RCU grace period. + +Signed-off-by: Qingfang Deng +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20250828012018.15922-1-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/pppoe.c | 94 ++++++++++++++++++++++------------------ + include/linux/if_pppox.h | 2 +- + 2 files changed, 54 insertions(+), 42 deletions(-) + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -100,8 +100,8 @@ struct pppoe_net { + * as well, moreover in case of SMP less locking + * controversy here + */ +- struct pppox_sock *hash_table[PPPOE_HASH_SIZE]; +- rwlock_t hash_lock; ++ struct pppox_sock __rcu *hash_table[PPPOE_HASH_SIZE]; ++ spinlock_t hash_lock; + }; + + /* +@@ -162,13 +162,13 @@ static struct pppox_sock *__get_item(str + int hash = hash_item(sid, addr); + struct pppox_sock *ret; + +- ret = pn->hash_table[hash]; ++ ret = rcu_dereference(pn->hash_table[hash]); + while (ret) { + if (cmp_addr(&ret->pppoe_pa, sid, addr) && + ret->pppoe_ifindex == ifindex) + return ret; + +- ret = ret->next; ++ ret = rcu_dereference(ret->next); + } + + return NULL; +@@ -177,19 +177,20 @@ static struct pppox_sock *__get_item(str + static int __set_item(struct pppoe_net *pn, struct pppox_sock *po) + { + int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote); +- struct pppox_sock *ret; ++ struct pppox_sock *ret, *first; + +- ret = pn->hash_table[hash]; ++ first = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock)); ++ ret = first; + while (ret) { + if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) && + ret->pppoe_ifindex == po->pppoe_ifindex) + return -EALREADY; + +- ret = ret->next; ++ ret = rcu_dereference_protected(ret->next, lockdep_is_held(&pn->hash_lock)); + } + +- po->next = pn->hash_table[hash]; +- pn->hash_table[hash] = po; ++ RCU_INIT_POINTER(po->next, first); ++ rcu_assign_pointer(pn->hash_table[hash], po); + + return 0; + } +@@ -198,20 +199,24 @@ static void __delete_item(struct pppoe_n + char *addr, int ifindex) + { + int hash = hash_item(sid, addr); +- struct pppox_sock *ret, **src; ++ struct pppox_sock *ret, __rcu **src; + +- ret = pn->hash_table[hash]; ++ ret = rcu_dereference_protected(pn->hash_table[hash], lockdep_is_held(&pn->hash_lock)); + src = &pn->hash_table[hash]; + + while (ret) { + if (cmp_addr(&ret->pppoe_pa, sid, addr) && + ret->pppoe_ifindex == ifindex) { +- *src = ret->next; ++ struct pppox_sock *next; ++ ++ next = rcu_dereference_protected(ret->next, ++ lockdep_is_held(&pn->hash_lock)); ++ rcu_assign_pointer(*src, next); + break; + } + + src = &ret->next; +- ret = ret->next; ++ ret = rcu_dereference_protected(ret->next, lockdep_is_held(&pn->hash_lock)); + } + } + +@@ -225,11 +230,9 @@ static inline struct pppox_sock *get_ite + { + struct pppox_sock *po; + +- read_lock_bh(&pn->hash_lock); + po = __get_item(pn, sid, addr, ifindex); +- if (po) +- sock_hold(sk_pppox(po)); +- read_unlock_bh(&pn->hash_lock); ++ if (po && !refcount_inc_not_zero(&sk_pppox(po)->sk_refcnt)) ++ po = NULL; + + return po; + } +@@ -258,9 +261,9 @@ static inline struct pppox_sock *get_ite + static inline void delete_item(struct pppoe_net *pn, __be16 sid, + char *addr, int ifindex) + { +- write_lock_bh(&pn->hash_lock); ++ spin_lock(&pn->hash_lock); + __delete_item(pn, sid, addr, ifindex); +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + } + + /*************************************************************************** +@@ -276,14 +279,16 @@ static void pppoe_flush_dev(struct net_d + int i; + + pn = pppoe_pernet(dev_net(dev)); +- write_lock_bh(&pn->hash_lock); ++ spin_lock(&pn->hash_lock); + for (i = 0; i < PPPOE_HASH_SIZE; i++) { +- struct pppox_sock *po = pn->hash_table[i]; ++ struct pppox_sock *po = rcu_dereference_protected(pn->hash_table[i], ++ lockdep_is_held(&pn->hash_lock)); + struct sock *sk; + + while (po) { + while (po && po->pppoe_dev != dev) { +- po = po->next; ++ po = rcu_dereference_protected(po->next, ++ lockdep_is_held(&pn->hash_lock)); + } + + if (!po) +@@ -300,7 +305,7 @@ static void pppoe_flush_dev(struct net_d + */ + + sock_hold(sk); +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + lock_sock(sk); + + if (po->pppoe_dev == dev && +@@ -320,11 +325,12 @@ static void pppoe_flush_dev(struct net_d + */ + + BUG_ON(pppoe_pernet(dev_net(dev)) == NULL); +- write_lock_bh(&pn->hash_lock); +- po = pn->hash_table[i]; ++ spin_lock(&pn->hash_lock); ++ po = rcu_dereference_protected(pn->hash_table[i], ++ lockdep_is_held(&pn->hash_lock)); + } + } +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + } + + static int pppoe_device_event(struct notifier_block *this, +@@ -528,6 +534,11 @@ static struct proto pppoe_sk_proto __rea + .obj_size = sizeof(struct pppox_sock), + }; + ++static void pppoe_destruct(struct sock *sk) ++{ ++ skb_queue_purge(&sk->sk_receive_queue); ++} ++ + /*********************************************************************** + * + * Initialize a new struct sock. +@@ -542,11 +553,13 @@ static int pppoe_create(struct net *net, + return -ENOMEM; + + sock_init_data(sock, sk); ++ sock_set_flag(sk, SOCK_RCU_FREE); + + 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; + sk->sk_family = PF_PPPOX; +@@ -599,7 +612,6 @@ static int pppoe_release(struct socket * + sock_orphan(sk); + sock->sk = NULL; + +- skb_queue_purge(&sk->sk_receive_queue); + release_sock(sk); + sock_put(sk); + +@@ -681,9 +693,9 @@ static int pppoe_connect(struct socket * + &sp->sa_addr.pppoe, + sizeof(struct pppoe_addr)); + +- write_lock_bh(&pn->hash_lock); ++ spin_lock(&pn->hash_lock); + error = __set_item(pn, po); +- write_unlock_bh(&pn->hash_lock); ++ spin_unlock(&pn->hash_lock); + if (error < 0) + goto err_put; + +@@ -1052,11 +1064,11 @@ static inline struct pppox_sock *pppoe_g + int i; + + for (i = 0; i < PPPOE_HASH_SIZE; i++) { +- po = pn->hash_table[i]; ++ po = rcu_dereference(pn->hash_table[i]); + while (po) { + if (!pos--) + goto out; +- po = po->next; ++ po = rcu_dereference(po->next); + } + } + +@@ -1065,19 +1077,19 @@ out: + } + + static void *pppoe_seq_start(struct seq_file *seq, loff_t *pos) +- __acquires(pn->hash_lock) ++ __acquires(RCU) + { + struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq)); + loff_t l = *pos; + +- read_lock_bh(&pn->hash_lock); ++ rcu_read_lock(); + return l ? pppoe_get_idx(pn, --l) : SEQ_START_TOKEN; + } + + static void *pppoe_seq_next(struct seq_file *seq, void *v, loff_t *pos) + { + struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq)); +- struct pppox_sock *po; ++ struct pppox_sock *po, *next; + + ++*pos; + if (v == SEQ_START_TOKEN) { +@@ -1085,14 +1097,15 @@ static void *pppoe_seq_next(struct seq_f + goto out; + } + po = v; +- if (po->next) +- po = po->next; ++ next = rcu_dereference(po->next); ++ if (next) ++ po = next; + else { + int hash = hash_item(po->pppoe_pa.sid, po->pppoe_pa.remote); + + po = NULL; + while (++hash < PPPOE_HASH_SIZE) { +- po = pn->hash_table[hash]; ++ po = rcu_dereference(pn->hash_table[hash]); + if (po) + break; + } +@@ -1103,10 +1116,9 @@ out: + } + + static void pppoe_seq_stop(struct seq_file *seq, void *v) +- __releases(pn->hash_lock) ++ __releases(RCU) + { +- struct pppoe_net *pn = pppoe_pernet(seq_file_net(seq)); +- read_unlock_bh(&pn->hash_lock); ++ rcu_read_unlock(); + } + + static const struct seq_operations pppoe_seq_ops = { +@@ -1149,7 +1161,7 @@ static __net_init int pppoe_init_net(str + struct pppoe_net *pn = pppoe_pernet(net); + struct proc_dir_entry *pde; + +- rwlock_init(&pn->hash_lock); ++ spin_lock_init(&pn->hash_lock); + + pde = proc_create_net("pppoe", 0444, net->proc_net, + &pppoe_seq_ops, sizeof(struct seq_net_private)); +--- a/include/linux/if_pppox.h ++++ b/include/linux/if_pppox.h +@@ -43,7 +43,7 @@ struct pppox_sock { + /* struct sock must be the first member of pppox_sock */ + struct sock sk; + struct ppp_channel chan; +- struct pppox_sock *next; /* for hash table */ ++ struct pppox_sock __rcu *next; /* for hash table */ + union { + struct pppoe_opt pppoe; + struct pptp_opt pptp; diff --git a/target/linux/generic/backport-6.12/624-v6.18-pppoe-drop-sock-reference-counting-on-fast-path.patch b/target/linux/generic/backport-6.12/624-v6.18-pppoe-drop-sock-reference-counting-on-fast-path.patch new file mode 100644 index 00000000000..26376210428 --- /dev/null +++ b/target/linux/generic/backport-6.12/624-v6.18-pppoe-drop-sock-reference-counting-on-fast-path.patch @@ -0,0 +1,124 @@ +From 4f54dff818d7b5b1d84becd5d90bc46e6233c0d7 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Thu, 28 Aug 2025 09:20:17 +0800 +Subject: [PATCH] pppoe: drop sock reference counting on fast path + +Now that PPPoE sockets are freed via RCU (SOCK_RCU_FREE), it is no longer +necessary to take a reference count when looking up sockets on the receive +path. Readers are protected by RCU, so the socket memory remains valid +until after a grace period. + +Convert fast-path lookups to avoid refcounting: + - Replace get_item() and sk_receive_skb() in pppoe_rcv() with + __get_item() and __sk_receive_skb(). + - Rework get_item_by_addr() into __get_item_by_addr() (no refcount and + move RCU lock into pppoe_ioctl) + - Remove unnecessary sock_put() calls. + +This avoids cacheline bouncing from atomic reference counting and improves +performance on the receive fast path. + +Signed-off-by: Qingfang Deng +Reviewed-by: Eric Dumazet +Link: https://patch.msgid.link/20250828012018.15922-2-dqfext@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/ppp/pppoe.c | 35 +++++++++++++---------------------- + 1 file changed, 13 insertions(+), 22 deletions(-) + +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -237,8 +237,8 @@ static inline struct pppox_sock *get_ite + return po; + } + +-static inline struct pppox_sock *get_item_by_addr(struct net *net, +- struct sockaddr_pppox *sp) ++static inline struct pppox_sock *__get_item_by_addr(struct net *net, ++ struct sockaddr_pppox *sp) + { + struct net_device *dev; + struct pppoe_net *pn; +@@ -246,15 +246,13 @@ static inline struct pppox_sock *get_ite + + int ifindex; + +- rcu_read_lock(); + dev = dev_get_by_name_rcu(net, sp->sa_addr.pppoe.dev); + if (dev) { + ifindex = dev->ifindex; + pn = pppoe_pernet(net); +- pppox_sock = get_item(pn, sp->sa_addr.pppoe.sid, +- sp->sa_addr.pppoe.remote, ifindex); ++ pppox_sock = __get_item(pn, sp->sa_addr.pppoe.sid, ++ sp->sa_addr.pppoe.remote, ifindex); + } +- rcu_read_unlock(); + return pppox_sock; + } + +@@ -384,18 +382,16 @@ static int pppoe_rcv_core(struct sock *s + if (sk->sk_state & PPPOX_BOUND) { + ppp_input(&po->chan, skb); + } else if (sk->sk_state & PPPOX_RELAY) { +- relay_po = get_item_by_addr(sock_net(sk), +- &po->pppoe_relay); ++ relay_po = __get_item_by_addr(sock_net(sk), ++ &po->pppoe_relay); + if (relay_po == NULL) + goto abort_kfree; + + if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0) +- goto abort_put; ++ goto abort_kfree; + + if (!__pppoe_xmit(sk_pppox(relay_po), skb)) +- goto abort_put; +- +- sock_put(sk_pppox(relay_po)); ++ goto abort_kfree; + } else { + if (sock_queue_rcv_skb(sk, skb)) + goto abort_kfree; +@@ -403,9 +399,6 @@ static int pppoe_rcv_core(struct sock *s + + return NET_RX_SUCCESS; + +-abort_put: +- sock_put(sk_pppox(relay_po)); +- + abort_kfree: + kfree_skb(skb); + return NET_RX_DROP; +@@ -447,14 +440,11 @@ static int pppoe_rcv(struct sk_buff *skb + ph = pppoe_hdr(skb); + pn = pppoe_pernet(dev_net(dev)); + +- /* Note that get_item does a sock_hold(), so sk_pppox(po) +- * is known to be safe. +- */ +- po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); ++ po = __get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); + if (!po) + goto drop; + +- return sk_receive_skb(sk_pppox(po), skb, 0); ++ return __sk_receive_skb(sk_pppox(po), skb, 0, 1, false); + + drop: + kfree_skb(skb); +@@ -820,11 +810,12 @@ static int pppoe_ioctl(struct socket *so + + /* Check that the socket referenced by the address + actually exists. */ +- relay_po = get_item_by_addr(sock_net(sk), &po->pppoe_relay); ++ rcu_read_lock(); ++ relay_po = __get_item_by_addr(sock_net(sk), &po->pppoe_relay); ++ rcu_read_unlock(); + if (!relay_po) + break; + +- sock_put(sk_pppox(relay_po)); + sk->sk_state |= PPPOX_RELAY; + err = 0; + break; diff --git a/target/linux/generic/backport-6.12/625-v7.0-ppp-enable-TX-scatter-gather.patch b/target/linux/generic/backport-6.12/625-v7.0-ppp-enable-TX-scatter-gather.patch new file mode 100644 index 00000000000..5ea56f5f74f --- /dev/null +++ b/target/linux/generic/backport-6.12/625-v7.0-ppp-enable-TX-scatter-gather.patch @@ -0,0 +1,104 @@ +From 42fcb213e58a7da33d5d2d7517b4e521025c68c3 Mon Sep 17 00:00:00 2001 +From: Qingfang Deng +Date: Thu, 29 Jan 2026 09:29:02 +0800 +Subject: [PATCH] ppp: enable TX scatter-gather + +PPP channels using chan->direct_xmit prepend the PPP header to a skb and +call dev_queue_xmit() directly. In this mode the skb does not need to be +linear, but the PPP netdevice currently does not advertise +scatter-gather features, causing unnecessary linearization and +preventing GSO. + +Enable NETIF_F_SG and NETIF_F_FRAGLIST on PPP devices. In case a linear +buffer is required (PPP compression, multilink, and channels without +direct_xmit), call skb_linearize() explicitly. + +Signed-off-by: Qingfang Deng +Link: https://patch.msgid.link/20260129012902.941-1-dqfext@gmail.com +Signed-off-by: Paolo Abeni +--- + drivers/net/ppp/ppp_generic.c | 30 +++++++++++++++++++++++++----- + 1 file changed, 25 insertions(+), 5 deletions(-) + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -1632,6 +1632,8 @@ static void ppp_setup(struct net_device + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->priv_destructor = ppp_dev_priv_destructor; + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; ++ dev->features = NETIF_F_SG | NETIF_F_FRAGLIST; ++ dev->hw_features = dev->features; + netif_keep_dst(dev); + } + +@@ -1696,6 +1698,10 @@ pad_compress_skb(struct ppp *ppp, struct + ppp->xcomp->comp_extra + ppp->dev->hard_header_len; + int compressor_skb_size = ppp->dev->mtu + + ppp->xcomp->comp_extra + PPP_HDRLEN; ++ ++ if (skb_linearize(skb)) ++ return NULL; ++ + new_skb = alloc_skb(new_skb_size, GFP_ATOMIC); + if (!new_skb) { + if (net_ratelimit()) +@@ -1783,6 +1789,10 @@ ppp_send_frame(struct ppp *ppp, struct s + case PPP_IP: + if (!ppp->vj || (ppp->flags & SC_COMP_TCP) == 0) + break; ++ ++ if (skb_linearize(skb)) ++ goto drop; ++ + /* try to do VJ TCP header compression */ + new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2, + GFP_ATOMIC); +@@ -1880,19 +1890,26 @@ ppp_push(struct ppp *ppp) + } + + if ((ppp->flags & SC_MULTILINK) == 0) { ++ struct ppp_channel *chan; + /* not doing multilink: send it down the first channel */ + list = list->next; + pch = list_entry(list, struct channel, clist); + + spin_lock(&pch->downl); +- if (pch->chan) { +- if (pch->chan->ops->start_xmit(pch->chan, skb)) +- ppp->xmit_pending = NULL; +- } else { +- /* channel got unregistered */ ++ chan = pch->chan; ++ if (unlikely(!chan || (!chan->direct_xmit && skb_linearize(skb)))) { ++ /* channel got unregistered, or it requires a linear ++ * skb but linearization failed ++ */ + kfree_skb(skb); + ppp->xmit_pending = NULL; ++ goto out; + } ++ ++ if (chan->ops->start_xmit(chan, skb)) ++ ppp->xmit_pending = NULL; ++ ++out: + spin_unlock(&pch->downl); + return; + } +@@ -1977,6 +1994,8 @@ static int ppp_mp_explode(struct ppp *pp + return 0; /* can't take now, leave it in xmit_pending */ + + /* Do protocol field compression */ ++ if (skb_linearize(skb)) ++ goto err_linearize; + p = skb->data; + len = skb->len; + if (*p == 0 && mp_protocol_compress) { +@@ -2135,6 +2154,7 @@ static int ppp_mp_explode(struct ppp *pp + + noskb: + spin_unlock(&pch->downl); ++ err_linearize: + if (ppp->debug & 1) + netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); + ++ppp->dev->stats.tx_errors; diff --git a/target/linux/generic/backport-6.12/625-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch b/target/linux/generic/backport-6.12/626-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch similarity index 100% rename from target/linux/generic/backport-6.12/625-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch rename to target/linux/generic/backport-6.12/626-v6.16-net-bridge-locally-receive-all-multicast-packets-if-.patch diff --git a/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch b/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch index 11d0f03fbaa..2aa55f84b99 100644 --- a/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch +++ b/target/linux/generic/pending-6.12/650-net-pppoe-implement-GRO-support.patch @@ -23,7 +23,7 @@ Signed-off-by: Felix Fietkau #include -@@ -435,7 +436,7 @@ static int pppoe_rcv(struct sk_buff *skb +@@ -434,7 +435,7 @@ static int pppoe_rcv(struct sk_buff *skb if (skb->len < len) goto drop; @@ -32,7 +32,7 @@ Signed-off-by: Felix Fietkau goto drop; ph = pppoe_hdr(skb); -@@ -1173,6 +1174,161 @@ static struct pernet_operations pppoe_ne +@@ -1176,6 +1177,161 @@ static struct pernet_operations pppoe_ne .size = sizeof(struct pppoe_net), }; @@ -194,7 +194,7 @@ Signed-off-by: Felix Fietkau static int __init pppoe_init(void) { int err; -@@ -1189,6 +1345,7 @@ static int __init pppoe_init(void) +@@ -1192,6 +1348,7 @@ static int __init pppoe_init(void) if (err) goto out_unregister_pppoe_proto; @@ -202,7 +202,7 @@ Signed-off-by: Felix Fietkau dev_add_pack(&pppoes_ptype); dev_add_pack(&pppoed_ptype); register_netdevice_notifier(&pppoe_notifier); -@@ -1208,6 +1365,7 @@ static void __exit pppoe_exit(void) +@@ -1211,6 +1368,7 @@ static void __exit pppoe_exit(void) unregister_netdevice_notifier(&pppoe_notifier); dev_remove_pack(&pppoed_ptype); dev_remove_pack(&pppoes_ptype); diff --git a/target/linux/ipq40xx/patches-6.12/999-atm-mpoa-intel-dsl-phy-support.patch b/target/linux/ipq40xx/patches-6.12/999-atm-mpoa-intel-dsl-phy-support.patch index d38070b13e1..942292e8a1f 100644 --- a/target/linux/ipq40xx/patches-6.12/999-atm-mpoa-intel-dsl-phy-support.patch +++ b/target/linux/ipq40xx/patches-6.12/999-atm-mpoa-intel-dsl-phy-support.patch @@ -4,7 +4,7 @@ Subject: [PATCH] UGW_SW-29163: ATM oam support --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c -@@ -2982,6 +2982,22 @@ char *ppp_dev_name(struct ppp_channel *c +@@ -2985,6 +2985,24 @@ char *ppp_dev_name(struct ppp_channel *c return name; } @@ -17,17 +17,19 @@ Subject: [PATCH] UGW_SW-29163: ATM oam support + struct net_device *dev = NULL; + + if (pch) { -+ read_lock_bh(&pch->upl); -+ if (pch->ppp && pch->ppp->dev) -+ dev = pch->ppp->dev; -+ read_unlock_bh(&pch->upl); ++ struct ppp *ppp; ++ rcu_read_lock(); ++ ppp = rcu_dereference(pch->ppp); ++ if (ppp) ++ dev = ppp->dev; ++ rcu_read_unlock(); + } + return dev; +} /* * Disconnect a channel from the generic layer. -@@ -3633,6 +3649,7 @@ EXPORT_SYMBOL(ppp_unregister_channel); +@@ -3646,6 +3664,7 @@ EXPORT_SYMBOL(ppp_unregister_channel); EXPORT_SYMBOL(ppp_channel_index); EXPORT_SYMBOL(ppp_unit_number); EXPORT_SYMBOL(ppp_dev_name);