mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-02-13 14:10:03 +01:00
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 <dqfext@gmail.com> Link: https://github.com/openwrt/openwrt/pull/21892 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
parent
7aa1f7e814
commit
316492b809
8 changed files with 1021 additions and 10 deletions
|
|
@ -0,0 +1,126 @@
|
|||
From 1a3e9b7a6b09e8ab3d2af019e4a392622685855e Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
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 <dqfext@gmail.com>
|
||||
Link: https://patch.msgid.link/20250610083211.909015-1-dqfext@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
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;
|
||||
|
|
@ -0,0 +1,334 @@
|
|||
From b8844aab519a154808dbce15a132f3e8f1c34af6 Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
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 <dqfext@gmail.com>
|
||||
Link: https://patch.msgid.link/20250822012548.6232-1-dqfext@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
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);
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
From 72cdc67e7fa74931b055df3a76852bab551f1a04 Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
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 <dqfext@gmail.com>
|
||||
Reviewed-by: Eric Dumazet <edumazet@google.com>
|
||||
Link: https://patch.msgid.link/20250828012018.15922-1-dqfext@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
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;
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
From 4f54dff818d7b5b1d84becd5d90bc46e6233c0d7 Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
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 <dqfext@gmail.com>
|
||||
Reviewed-by: Eric Dumazet <edumazet@google.com>
|
||||
Link: https://patch.msgid.link/20250828012018.15922-2-dqfext@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
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;
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
From 42fcb213e58a7da33d5d2d7517b4e521025c68c3 Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
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 <dqfext@gmail.com>
|
||||
Link: https://patch.msgid.link/20260129012902.941-1-dqfext@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
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;
|
||||
|
|
@ -23,7 +23,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@@ -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 <nbd@nbd.name>
|
|||
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 <nbd@nbd.name>
|
|||
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 <nbd@nbd.name>
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue