ppp: Replace per-CPU recursion counter with lock-owner field
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Tue, 15 Jul 2025 15:08:06 +0000 (17:08 +0200)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 17 Jul 2025 13:36:18 +0000 (15:36 +0200)
The per-CPU variable ppp::xmit_recursion is protecting against recursion
due to wrong configuration of the ppp unit. The per-CPU variable
relies on disabled BH for its locking. Without per-CPU locking in
local_bh_disable() on PREEMPT_RT this data structure requires explicit
locking.

The ppp::xmit_recursion is used as a per-CPU boolean. The counter is
checked early in the send routing and the transmit path is only entered
if the counter is zero. Then the counter is incremented to avoid
recursion. It used to detect recursion on channel::downl and
ppp::wlock.

Create a struct ppp_xmit_recursion and move the counter into it.
Add local_lock_t to the struct and use local_lock_nested_bh() for
locking. Due to possible nesting, the lock cannot be acquired
unconditionally but it requires an owner field to identify recursion
before attempting to acquire the lock.

The counter is incremented and checked only after the lock is acquired.
Since it functions as a boolean rather than a count, and its role is now
superseded by the owner field, it can be safely removed.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://patch.msgid.link/20250715150806.700536-2-bigeasy@linutronix.de
Reviewed-by: Guillaume Nault <gnault@redhat.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ppp/ppp_generic.c

index 4cf9d1822a83f9ec253dc738463de4081b607a43..8c98cbd4b06de94148ac4e76661c1020ea4f3b14 100644 (file)
@@ -107,6 +107,11 @@ struct ppp_file {
 #define PF_TO_PPP(pf)          PF_TO_X(pf, struct ppp)
 #define PF_TO_CHANNEL(pf)      PF_TO_X(pf, struct channel)
 
+struct ppp_xmit_recursion {
+       struct task_struct *owner;
+       local_lock_t bh_lock;
+};
+
 /*
  * Data structure describing one ppp unit.
  * A ppp unit corresponds to a ppp network interface device
@@ -120,7 +125,7 @@ struct ppp {
        int             n_channels;     /* how many channels are attached 54 */
        spinlock_t      rlock;          /* lock for receive side 58 */
        spinlock_t      wlock;          /* lock for transmit side 5c */
-       int __percpu    *xmit_recursion; /* xmit recursion detect */
+       struct ppp_xmit_recursion __percpu *xmit_recursion; /* xmit recursion detect */
        int             mru;            /* max receive unit 60 */
        unsigned int    flags;          /* control bits 64 */
        unsigned int    xstate;         /* transmit state bits 68 */
@@ -1249,13 +1254,18 @@ static int ppp_dev_configure(struct net *src_net, struct net_device *dev,
        spin_lock_init(&ppp->rlock);
        spin_lock_init(&ppp->wlock);
 
-       ppp->xmit_recursion = alloc_percpu(int);
+       ppp->xmit_recursion = alloc_percpu(struct ppp_xmit_recursion);
        if (!ppp->xmit_recursion) {
                err = -ENOMEM;
                goto err1;
        }
-       for_each_possible_cpu(cpu)
-               (*per_cpu_ptr(ppp->xmit_recursion, cpu)) = 0;
+       for_each_possible_cpu(cpu) {
+               struct ppp_xmit_recursion *xmit_recursion;
+
+               xmit_recursion = per_cpu_ptr(ppp->xmit_recursion, cpu);
+               xmit_recursion->owner = NULL;
+               local_lock_init(&xmit_recursion->bh_lock);
+       }
 
 #ifdef CONFIG_PPP_MULTILINK
        ppp->minseq = -1;
@@ -1660,15 +1670,20 @@ static void __ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
 
 static void ppp_xmit_process(struct ppp *ppp, struct sk_buff *skb)
 {
+       struct ppp_xmit_recursion *xmit_recursion;
+
        local_bh_disable();
 
-       if (unlikely(*this_cpu_ptr(ppp->xmit_recursion)))
+       xmit_recursion = this_cpu_ptr(ppp->xmit_recursion);
+       if (xmit_recursion->owner == current)
                goto err;
+       local_lock_nested_bh(&ppp->xmit_recursion->bh_lock);
+       xmit_recursion->owner = current;
 
-       (*this_cpu_ptr(ppp->xmit_recursion))++;
        __ppp_xmit_process(ppp, skb);
-       (*this_cpu_ptr(ppp->xmit_recursion))--;
 
+       xmit_recursion->owner = NULL;
+       local_unlock_nested_bh(&ppp->xmit_recursion->bh_lock);
        local_bh_enable();
 
        return;
@@ -2169,11 +2184,16 @@ static void __ppp_channel_push(struct channel *pch)
 
 static void ppp_channel_push(struct channel *pch)
 {
+       struct ppp_xmit_recursion *xmit_recursion;
+
        read_lock_bh(&pch->upl);
        if (pch->ppp) {
-               (*this_cpu_ptr(pch->ppp->xmit_recursion))++;
+               xmit_recursion = this_cpu_ptr(pch->ppp->xmit_recursion);
+               local_lock_nested_bh(&pch->ppp->xmit_recursion->bh_lock);
+               xmit_recursion->owner = current;
                __ppp_channel_push(pch);
-               (*this_cpu_ptr(pch->ppp->xmit_recursion))--;
+               xmit_recursion->owner = NULL;
+               local_unlock_nested_bh(&pch->ppp->xmit_recursion->bh_lock);
        } else {
                __ppp_channel_push(pch);
        }