Re: [PATCH net-next] ppp: consolidate RX skb queueing

Paolo Abeni <[email protected]> Thu, 30 Apr 2026 10:54:39 +0200
Newsgroups org.kernel.vger.linux-ppp,org.kernel.vger.linux-kernel,org.kernel.vger.netdev
Message-ID <[email protected]>

On 4/28/26 4:44 AM, Qingfang Deng wrote:
> In ppp_input() and ppp_receive_nonmp_frame(), received skbs are queued
> for userspace delivery using the same open-coded pattern:
> 
> 	skb_queue_tail(&pf->rq, skb);
> 	while (pf->rq.qlen > PPP_MAX_RQLEN &&
> 	       (skb = skb_dequeue(&pf->rq)))
> 		kfree_skb(skb);
> 	wake_up_interruptible(&pf->rwait);
> 
> This has a potential race: skb_queue_tail() releases the queue lock,
> then qlen is read locklessly before skb_dequeue() re-acquires it.
> Another CPU enqueueing concurrently could cause the length check to see
> stale data. This race is benign, as it only causes extra skbs to be
> freed in the worst case.
> 
> Introduce ppp_file_queue_rx_skb() to perform the enqueue, length check,
> and trim atomically under a single pf->rq.lock critical section. As both
> callers have softirq disabled, plain spin_lock() can be used instead of
> _bh()/_irqsave() variants. Since only one skb is enqueued at a time, the
> queue can exceed PPP_MAX_RQLEN by at most one frame, so replace the
> while-loop with an if-statement. While at it, use skb_queue_len()
> instead of open-coding the qlen access.
> 
> Signed-off-by: Qingfang Deng <[email protected]>
> ---
>  drivers/net/ppp/ppp_generic.c | 37 ++++++++++++++++++++++-------------
>  1 file changed, 23 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
> index 57c68efa5ff8..6ab5011540a0 100644
> --- a/drivers/net/ppp/ppp_generic.c
> +++ b/drivers/net/ppp/ppp_generic.c
> @@ -2307,6 +2307,27 @@ static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb)
>  	return !!pchb;
>  }
>  
> +/* Queue up and deliver a received skb to userspace.
> + * Must be called in softirq.
> + */
> +static void ppp_file_queue_rx_skb(struct ppp_file *pf, struct sk_buff *skb)
> +{
> +	spin_lock(&pf->rq.lock);
> +	__skb_queue_tail(&pf->rq, skb);
> +	/* limit queue length by dropping old frames */
> +	if (unlikely(skb_queue_len(&pf->rq) > PPP_MAX_RQLEN)) {
> +		struct sk_buff *old = __skb_peek(&pf->rq);
> +
> +		__skb_unlink(old, &pf->rq);
> +		spin_unlock(&pf->rq.lock);
> +		kfree_skb(old);
> +	} else {
> +		spin_unlock(&pf->rq.lock);

Note that after __skb_queue_tail(), skb_queue_len(&pf->rq) could be ==
PPP_MAX_RQLEN + 2, due to the slightly different check in
ppp_prepare_tx_skb().

I think the above it could/should be simplified to:
	while (unlikely(skb_queue_len(&pf->rq) > PPP_MAX_RQLEN))
		kfree_skb(__skb_dequeue(&pf->rq));
	spin_unlock(&pf->rq.lock);

And possibly it would make sense to consolidate the test in
ppp_prepare_tx_skb(), too for consistency - in that case an `if`
statement should become enough.

/P