dev: Use nested-BH locking for softnet_data.process_queue.
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Thu, 20 Jun 2024 13:22:00 +0000 (15:22 +0200)
committerJakub Kicinski <kuba@kernel.org>
Mon, 24 Jun 2024 23:41:23 +0000 (16:41 -0700)
softnet_data::process_queue is a per-CPU variable and relies on disabled
BH for its locking. Without per-CPU locking in local_bh_disable() on
PREEMPT_RT this data structure requires explicit locking.

softnet_data::input_queue_head can be updated lockless. This is fine
because this value is only update CPU local by the local backlog_napi
thread.

Add a local_lock_t to softnet_data and use local_lock_nested_bh() for locking
of process_queue. This change adds only lockdep coverage and does not
alter the functional behaviour for !PREEMPT_RT.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://patch.msgid.link/20240620132727.660738-11-bigeasy@linutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/netdevice.h
net/core/dev.c

index f6fc9066147d20ff3bc13d90cc733ef016e8c0e5..4e81660b4462d564e221e97ec1bea854ae9ccfac 100644 (file)
@@ -3202,6 +3202,7 @@ static inline bool dev_has_header(const struct net_device *dev)
 struct softnet_data {
        struct list_head        poll_list;
        struct sk_buff_head     process_queue;
+       local_lock_t            process_queue_bh_lock;
 
        /* stats */
        unsigned int            processed;
index 73c4d14e4febcbfc3837bdd0b05ba4aefb8184c2..8ef727c2ae2be503fbfb9f49eb29b818cf94fe0c 100644 (file)
@@ -449,7 +449,9 @@ static RAW_NOTIFIER_HEAD(netdev_chain);
  *     queue in the local softnet handler.
  */
 
-DEFINE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
+DEFINE_PER_CPU_ALIGNED(struct softnet_data, softnet_data) = {
+       .process_queue_bh_lock = INIT_LOCAL_LOCK(process_queue_bh_lock),
+};
 EXPORT_PER_CPU_SYMBOL(softnet_data);
 
 /* Page_pool has a lockless array/stack to alloc/recycle pages.
@@ -5949,6 +5951,7 @@ static void flush_backlog(struct work_struct *work)
        }
        backlog_unlock_irq_enable(sd);
 
+       local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
        skb_queue_walk_safe(&sd->process_queue, skb, tmp) {
                if (skb->dev->reg_state == NETREG_UNREGISTERING) {
                        __skb_unlink(skb, &sd->process_queue);
@@ -5956,6 +5959,7 @@ static void flush_backlog(struct work_struct *work)
                        rps_input_queue_head_incr(sd);
                }
        }
+       local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
        local_bh_enable();
 }
 
@@ -6077,7 +6081,9 @@ static int process_backlog(struct napi_struct *napi, int quota)
        while (again) {
                struct sk_buff *skb;
 
+               local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
                while ((skb = __skb_dequeue(&sd->process_queue))) {
+                       local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
                        rcu_read_lock();
                        __netif_receive_skb(skb);
                        rcu_read_unlock();
@@ -6086,7 +6092,9 @@ static int process_backlog(struct napi_struct *napi, int quota)
                                return work;
                        }
 
+                       local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
                }
+               local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
 
                backlog_lock_irq_disable(sd);
                if (skb_queue_empty(&sd->input_pkt_queue)) {
@@ -6101,8 +6109,10 @@ static int process_backlog(struct napi_struct *napi, int quota)
                        napi->state &= NAPIF_STATE_THREADED;
                        again = false;
                } else {
+                       local_lock_nested_bh(&softnet_data.process_queue_bh_lock);
                        skb_queue_splice_tail_init(&sd->input_pkt_queue,
                                                   &sd->process_queue);
+                       local_unlock_nested_bh(&softnet_data.process_queue_bh_lock);
                }
                backlog_unlock_irq_enable(sd);
        }