Skip to content

Commit ecefbc0

Browse files
Sebastian Andrzej Siewiorkuba-moo
authored andcommitted
net: softnet_data: Make xmit per task.
Softirq is preemptible on PREEMPT_RT. Without a per-CPU lock in local_bh_disable() there is no guarantee that only one device is transmitting at a time. With preemption and multiple senders it is possible that the per-CPU `recursion' counter gets incremented by different threads and exceeds XMIT_RECURSION_LIMIT leading to a false positive recursion alert. The `more' member is subject to similar problems if set by one thread for one driver and wrongly used by another driver within another thread. Instead of adding a lock to protect the per-CPU variable it is simpler to make xmit per-task. Sending and receiving skbs happens always in thread context anyway. Having a lock to protected the per-CPU counter would block/ serialize two sending threads needlessly. It would also require a recursive lock to ensure that the owner can increment the counter further. Make the softnet_data.xmit a task_struct member on PREEMPT_RT. Add needed wrapper. Cc: Ben Segall <[email protected]> Cc: Daniel Bristot de Oliveira <[email protected]> Cc: Dietmar Eggemann <[email protected]> Cc: Juri Lelli <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Valentin Schneider <[email protected]> Cc: Vincent Guittot <[email protected]> Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent c67ef53 commit ecefbc0

File tree

5 files changed

+80
-12
lines changed

5 files changed

+80
-12
lines changed

include/linux/netdevice.h

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
#include <linux/netdev_features.h>
4545
#include <linux/neighbour.h>
46+
#include <linux/netdevice_xmit.h>
4647
#include <uapi/linux/netdevice.h>
4748
#include <uapi/linux/if_bonding.h>
4849
#include <uapi/linux/pkt_cls.h>
@@ -3223,13 +3224,7 @@ struct softnet_data {
32233224
struct sk_buff_head xfrm_backlog;
32243225
#endif
32253226
/* written and read only by owning cpu: */
3226-
struct {
3227-
u16 recursion;
3228-
u8 more;
3229-
#ifdef CONFIG_NET_EGRESS
3230-
u8 skip_txqueue;
3231-
#endif
3232-
} xmit;
3227+
struct netdev_xmit xmit;
32333228
#ifdef CONFIG_RPS
32343229
/* input_queue_head should be written by cpu owning this struct,
32353230
* and only read by other cpus. Worth using a cache line.
@@ -3257,10 +3252,18 @@ struct softnet_data {
32573252

32583253
DECLARE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
32593254

3255+
#ifndef CONFIG_PREEMPT_RT
32603256
static inline int dev_recursion_level(void)
32613257
{
32623258
return this_cpu_read(softnet_data.xmit.recursion);
32633259
}
3260+
#else
3261+
static inline int dev_recursion_level(void)
3262+
{
3263+
return current->net_xmit.recursion;
3264+
}
3265+
3266+
#endif
32643267

32653268
void __netif_schedule(struct Qdisc *q);
32663269
void netif_schedule_queue(struct netdev_queue *txq);
@@ -4872,18 +4875,35 @@ static inline ktime_t netdev_get_tstamp(struct net_device *dev,
48724875
return hwtstamps->hwtstamp;
48734876
}
48744877

4875-
static inline netdev_tx_t __netdev_start_xmit(const struct net_device_ops *ops,
4876-
struct sk_buff *skb, struct net_device *dev,
4877-
bool more)
4878+
#ifndef CONFIG_PREEMPT_RT
4879+
static inline void netdev_xmit_set_more(bool more)
48784880
{
48794881
__this_cpu_write(softnet_data.xmit.more, more);
4880-
return ops->ndo_start_xmit(skb, dev);
48814882
}
48824883

48834884
static inline bool netdev_xmit_more(void)
48844885
{
48854886
return __this_cpu_read(softnet_data.xmit.more);
48864887
}
4888+
#else
4889+
static inline void netdev_xmit_set_more(bool more)
4890+
{
4891+
current->net_xmit.more = more;
4892+
}
4893+
4894+
static inline bool netdev_xmit_more(void)
4895+
{
4896+
return current->net_xmit.more;
4897+
}
4898+
#endif
4899+
4900+
static inline netdev_tx_t __netdev_start_xmit(const struct net_device_ops *ops,
4901+
struct sk_buff *skb, struct net_device *dev,
4902+
bool more)
4903+
{
4904+
netdev_xmit_set_more(more);
4905+
return ops->ndo_start_xmit(skb, dev);
4906+
}
48874907

48884908
static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_device *dev,
48894909
struct netdev_queue *txq, bool more)

include/linux/netdevice_xmit.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
#ifndef _LINUX_NETDEVICE_XMIT_H
3+
#define _LINUX_NETDEVICE_XMIT_H
4+
5+
struct netdev_xmit {
6+
u16 recursion;
7+
u8 more;
8+
#ifdef CONFIG_NET_EGRESS
9+
u8 skip_txqueue;
10+
#endif
11+
};
12+
13+
#endif

include/linux/sched.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/signal_types.h>
3737
#include <linux/syscall_user_dispatch_types.h>
3838
#include <linux/mm_types_task.h>
39+
#include <linux/netdevice_xmit.h>
3940
#include <linux/task_io_accounting.h>
4041
#include <linux/posix-timers_types.h>
4142
#include <linux/restart_block.h>
@@ -975,7 +976,9 @@ struct task_struct {
975976
/* delay due to memory thrashing */
976977
unsigned in_thrashing:1;
977978
#endif
978-
979+
#ifdef CONFIG_PREEMPT_RT
980+
struct netdev_xmit net_xmit;
981+
#endif
979982
unsigned long atomic_flags; /* Flags requiring atomic access. */
980983

981984
struct restart_block restart_block;

net/core/dev.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3940,6 +3940,7 @@ netdev_tx_queue_mapping(struct net_device *dev, struct sk_buff *skb)
39403940
return netdev_get_tx_queue(dev, netdev_cap_txqueue(dev, qm));
39413941
}
39423942

3943+
#ifndef CONFIG_PREEMPT_RT
39433944
static bool netdev_xmit_txqueue_skipped(void)
39443945
{
39453946
return __this_cpu_read(softnet_data.xmit.skip_txqueue);
@@ -3950,6 +3951,19 @@ void netdev_xmit_skip_txqueue(bool skip)
39503951
__this_cpu_write(softnet_data.xmit.skip_txqueue, skip);
39513952
}
39523953
EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue);
3954+
3955+
#else
3956+
static bool netdev_xmit_txqueue_skipped(void)
3957+
{
3958+
return current->net_xmit.skip_txqueue;
3959+
}
3960+
3961+
void netdev_xmit_skip_txqueue(bool skip)
3962+
{
3963+
current->net_xmit.skip_txqueue = skip;
3964+
}
3965+
EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue);
3966+
#endif
39533967
#endif /* CONFIG_NET_EGRESS */
39543968

39553969
#ifdef CONFIG_NET_XGRESS

net/core/dev.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ struct napi_struct *napi_by_id(unsigned int napi_id);
150150
void kick_defer_list_purge(struct softnet_data *sd, unsigned int cpu);
151151

152152
#define XMIT_RECURSION_LIMIT 8
153+
154+
#ifndef CONFIG_PREEMPT_RT
153155
static inline bool dev_xmit_recursion(void)
154156
{
155157
return unlikely(__this_cpu_read(softnet_data.xmit.recursion) >
@@ -165,6 +167,22 @@ static inline void dev_xmit_recursion_dec(void)
165167
{
166168
__this_cpu_dec(softnet_data.xmit.recursion);
167169
}
170+
#else
171+
static inline bool dev_xmit_recursion(void)
172+
{
173+
return unlikely(current->net_xmit.recursion > XMIT_RECURSION_LIMIT);
174+
}
175+
176+
static inline void dev_xmit_recursion_inc(void)
177+
{
178+
current->net_xmit.recursion++;
179+
}
180+
181+
static inline void dev_xmit_recursion_dec(void)
182+
{
183+
current->net_xmit.recursion--;
184+
}
185+
#endif
168186

169187
int dev_set_hwtstamp_phylib(struct net_device *dev,
170188
struct kernel_hwtstamp_config *cfg,

0 commit comments

Comments
 (0)