Skip to content

Commit 2677010

Browse files
samikhawajakuba-moo
authored andcommitted
Add support to set NAPI threaded for individual NAPI
A net device has a threaded sysctl that can be used to enable threaded NAPI polling on all of the NAPI contexts under that device. Allow enabling threaded NAPI polling at individual NAPI level using netlink. Extend the netlink operation `napi-set` and allow setting the threaded attribute of a NAPI. This will enable the threaded polling on a NAPI context. Add a test in `nl_netdev.py` that verifies various cases of threaded NAPI being set at NAPI and at device level. Tested ./tools/testing/selftests/net/nl_netdev.py TAP version 13 1..7 ok 1 nl_netdev.empty_check ok 2 nl_netdev.lo_check ok 3 nl_netdev.page_pool_check ok 4 nl_netdev.napi_list_check ok 5 nl_netdev.dev_set_threaded ok 6 nl_netdev.napi_set_threaded ok 7 nl_netdev.nsim_rxq_reset_down # Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Samiullah Khawaja <[email protected]> Reviewed-by: Willem de Bruijn <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent a44312d commit 2677010

File tree

10 files changed

+162
-7
lines changed

10 files changed

+162
-7
lines changed

Documentation/netlink/specs/netdev.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,14 @@ attribute-sets:
283283
doc: The timeout, in nanoseconds, of how long to suspend irq
284284
processing, if event polling finds events
285285
type: uint
286+
-
287+
name: threaded
288+
doc: Whether the NAPI is configured to operate in threaded polling
289+
mode. If this is set to 1 then the NAPI context operates in
290+
threaded polling mode.
291+
type: uint
292+
checks:
293+
max: 1
286294
-
287295
name: xsk-info
288296
attributes: []
@@ -694,6 +702,7 @@ operations:
694702
- defer-hard-irqs
695703
- gro-flush-timeout
696704
- irq-suspend-timeout
705+
- threaded
697706
dump:
698707
request:
699708
attributes:
@@ -746,6 +755,7 @@ operations:
746755
- defer-hard-irqs
747756
- gro-flush-timeout
748757
- irq-suspend-timeout
758+
- threaded
749759
-
750760
name: bind-tx
751761
doc: Bind dmabuf to netdev for TX

Documentation/networking/napi.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,14 @@ dependent). The NAPI instance IDs will be assigned in the opposite
444444
order than the process IDs of the kernel threads.
445445

446446
Threaded NAPI is controlled by writing 0/1 to the ``threaded`` file in
447-
netdev's sysfs directory.
447+
netdev's sysfs directory. It can also be enabled for a specific NAPI using
448+
netlink interface.
449+
450+
For example, using the script:
451+
452+
.. code-block:: bash
453+
454+
$ ynl --family netdev --do napi-set --json='{"id": 66, "threaded": 1}'
448455
449456
.. rubric:: Footnotes
450457

include/linux/netdevice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ struct napi_config {
369369
u64 irq_suspend_timeout;
370370
u32 defer_hard_irqs;
371371
cpumask_t affinity_mask;
372+
bool threaded;
372373
unsigned int napi_id;
373374
};
374375

include/uapi/linux/netdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ enum {
134134
NETDEV_A_NAPI_DEFER_HARD_IRQS,
135135
NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT,
136136
NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT,
137+
NETDEV_A_NAPI_THREADED,
137138

138139
__NETDEV_A_NAPI_MAX,
139140
NETDEV_A_NAPI_MAX = (__NETDEV_A_NAPI_MAX - 1)

net/core/dev.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6961,16 +6961,38 @@ static void napi_stop_kthread(struct napi_struct *napi)
69616961
napi->thread = NULL;
69626962
}
69636963

6964+
int napi_set_threaded(struct napi_struct *napi, bool threaded)
6965+
{
6966+
if (threaded) {
6967+
if (!napi->thread) {
6968+
int err = napi_kthread_create(napi);
6969+
6970+
if (err)
6971+
return err;
6972+
}
6973+
}
6974+
6975+
if (napi->config)
6976+
napi->config->threaded = threaded;
6977+
6978+
if (!threaded && napi->thread) {
6979+
napi_stop_kthread(napi);
6980+
} else {
6981+
/* Make sure kthread is created before THREADED bit is set. */
6982+
smp_mb__before_atomic();
6983+
assign_bit(NAPI_STATE_THREADED, &napi->state, threaded);
6984+
}
6985+
6986+
return 0;
6987+
}
6988+
69646989
int dev_set_threaded(struct net_device *dev, bool threaded)
69656990
{
69666991
struct napi_struct *napi;
69676992
int err = 0;
69686993

69696994
netdev_assert_locked_or_invisible(dev);
69706995

6971-
if (dev->threaded == threaded)
6972-
return 0;
6973-
69746996
if (threaded) {
69756997
list_for_each_entry(napi, &dev->napi_list, dev_list) {
69766998
if (!napi->thread) {
@@ -7221,6 +7243,8 @@ static void napi_restore_config(struct napi_struct *n)
72217243
napi_hash_add(n);
72227244
n->config->napi_id = n->napi_id;
72237245
}
7246+
7247+
WARN_ON_ONCE(napi_set_threaded(n, n->config->threaded));
72247248
}
72257249

72267250
static void napi_save_config(struct napi_struct *n)

net/core/dev.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,13 @@ static inline void napi_set_irq_suspend_timeout(struct napi_struct *n,
315315
WRITE_ONCE(n->irq_suspend_timeout, timeout);
316316
}
317317

318+
static inline bool napi_get_threaded(struct napi_struct *n)
319+
{
320+
return test_bit(NAPI_STATE_THREADED, &n->state);
321+
}
322+
323+
int napi_set_threaded(struct napi_struct *n, bool threaded);
324+
318325
int rps_cpumask_housekeeping(struct cpumask *mask);
319326

320327
#if defined(CONFIG_DEBUG_NET) && defined(CONFIG_BPF_SYSCALL)

net/core/netdev-genl-gen.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_FD + 1]
9292
};
9393

9494
/* NETDEV_CMD_NAPI_SET - do */
95-
static const struct nla_policy netdev_napi_set_nl_policy[NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT + 1] = {
95+
static const struct nla_policy netdev_napi_set_nl_policy[NETDEV_A_NAPI_THREADED + 1] = {
9696
[NETDEV_A_NAPI_ID] = { .type = NLA_U32, },
9797
[NETDEV_A_NAPI_DEFER_HARD_IRQS] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_napi_defer_hard_irqs_range),
9898
[NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT] = { .type = NLA_UINT, },
9999
[NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT] = { .type = NLA_UINT, },
100+
[NETDEV_A_NAPI_THREADED] = NLA_POLICY_MAX(NLA_UINT, 1),
100101
};
101102

102103
/* NETDEV_CMD_BIND_TX - do */
@@ -193,7 +194,7 @@ static const struct genl_split_ops netdev_nl_ops[] = {
193194
.cmd = NETDEV_CMD_NAPI_SET,
194195
.doit = netdev_nl_napi_set_doit,
195196
.policy = netdev_napi_set_nl_policy,
196-
.maxattr = NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT,
197+
.maxattr = NETDEV_A_NAPI_THREADED,
197198
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
198199
},
199200
{

net/core/netdev-genl.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ netdev_nl_napi_fill_one(struct sk_buff *rsp, struct napi_struct *napi,
184184
if (napi->irq >= 0 && nla_put_u32(rsp, NETDEV_A_NAPI_IRQ, napi->irq))
185185
goto nla_put_failure;
186186

187+
if (nla_put_uint(rsp, NETDEV_A_NAPI_THREADED,
188+
napi_get_threaded(napi)))
189+
goto nla_put_failure;
190+
187191
if (napi->thread) {
188192
pid = task_pid_nr(napi->thread);
189193
if (nla_put_u32(rsp, NETDEV_A_NAPI_PID, pid))
@@ -322,8 +326,18 @@ netdev_nl_napi_set_config(struct napi_struct *napi, struct genl_info *info)
322326
{
323327
u64 irq_suspend_timeout = 0;
324328
u64 gro_flush_timeout = 0;
329+
u8 threaded = 0;
325330
u32 defer = 0;
326331

332+
if (info->attrs[NETDEV_A_NAPI_THREADED]) {
333+
int ret;
334+
335+
threaded = nla_get_uint(info->attrs[NETDEV_A_NAPI_THREADED]);
336+
ret = napi_set_threaded(napi, !!threaded);
337+
if (ret)
338+
return ret;
339+
}
340+
327341
if (info->attrs[NETDEV_A_NAPI_DEFER_HARD_IRQS]) {
328342
defer = nla_get_u32(info->attrs[NETDEV_A_NAPI_DEFER_HARD_IRQS]);
329343
napi_set_defer_hard_irqs(napi, defer);

tools/include/uapi/linux/netdev.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ enum {
134134
NETDEV_A_NAPI_DEFER_HARD_IRQS,
135135
NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT,
136136
NETDEV_A_NAPI_IRQ_SUSPEND_TIMEOUT,
137+
NETDEV_A_NAPI_THREADED,
137138

138139
__NETDEV_A_NAPI_MAX,
139140
NETDEV_A_NAPI_MAX = (__NETDEV_A_NAPI_MAX - 1)

tools/testing/selftests/net/nl_netdev.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,91 @@ def napi_list_check(nf) -> None:
3535
ksft_eq(len(napis), 100,
3636
comment=f"queue count after reset queue {q} mode {i}")
3737

38+
def napi_set_threaded(nf) -> None:
39+
"""
40+
Test that verifies various cases of napi threaded
41+
set and unset at napi and device level.
42+
"""
43+
with NetdevSimDev(queue_count=2) as nsimdev:
44+
nsim = nsimdev.nsims[0]
45+
46+
ip(f"link set dev {nsim.ifname} up")
47+
48+
napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True)
49+
ksft_eq(len(napis), 2)
50+
51+
napi0_id = napis[0]['id']
52+
napi1_id = napis[1]['id']
53+
54+
# set napi threaded and verify
55+
nf.napi_set({'id': napi0_id, 'threaded': 1})
56+
napi0 = nf.napi_get({'id': napi0_id})
57+
ksft_eq(napi0['threaded'], 1)
58+
ksft_ne(napi0.get('pid'), None)
59+
60+
# check it is not set for napi1
61+
napi1 = nf.napi_get({'id': napi1_id})
62+
ksft_eq(napi1['threaded'], 0)
63+
ksft_eq(napi1.get('pid'), None)
64+
65+
ip(f"link set dev {nsim.ifname} down")
66+
ip(f"link set dev {nsim.ifname} up")
67+
68+
# verify if napi threaded is still set
69+
napi0 = nf.napi_get({'id': napi0_id})
70+
ksft_eq(napi0['threaded'], 1)
71+
ksft_ne(napi0.get('pid'), None)
72+
73+
# check it is still not set for napi1
74+
napi1 = nf.napi_get({'id': napi1_id})
75+
ksft_eq(napi1['threaded'], 0)
76+
ksft_eq(napi1.get('pid'), None)
77+
78+
# unset napi threaded and verify
79+
nf.napi_set({'id': napi0_id, 'threaded': 0})
80+
napi0 = nf.napi_get({'id': napi0_id})
81+
ksft_eq(napi0['threaded'], 0)
82+
ksft_eq(napi0.get('pid'), None)
83+
84+
# set threaded at device level
85+
system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded")
86+
87+
# check napi threaded is set for both napis
88+
napi0 = nf.napi_get({'id': napi0_id})
89+
ksft_eq(napi0['threaded'], 1)
90+
ksft_ne(napi0.get('pid'), None)
91+
napi1 = nf.napi_get({'id': napi1_id})
92+
ksft_eq(napi1['threaded'], 1)
93+
ksft_ne(napi1.get('pid'), None)
94+
95+
# unset threaded at device level
96+
system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded")
97+
98+
# check napi threaded is unset for both napis
99+
napi0 = nf.napi_get({'id': napi0_id})
100+
ksft_eq(napi0['threaded'], 0)
101+
ksft_eq(napi0.get('pid'), None)
102+
napi1 = nf.napi_get({'id': napi1_id})
103+
ksft_eq(napi1['threaded'], 0)
104+
ksft_eq(napi1.get('pid'), None)
105+
106+
# set napi threaded for napi0
107+
nf.napi_set({'id': napi0_id, 'threaded': 1})
108+
napi0 = nf.napi_get({'id': napi0_id})
109+
ksft_eq(napi0['threaded'], 1)
110+
ksft_ne(napi0.get('pid'), None)
111+
112+
# unset threaded at device level
113+
system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded")
114+
115+
# check napi threaded is unset for both napis
116+
napi0 = nf.napi_get({'id': napi0_id})
117+
ksft_eq(napi0['threaded'], 0)
118+
ksft_eq(napi0.get('pid'), None)
119+
napi1 = nf.napi_get({'id': napi1_id})
120+
ksft_eq(napi1['threaded'], 0)
121+
ksft_eq(napi1.get('pid'), None)
122+
38123
def dev_set_threaded(nf) -> None:
39124
"""
40125
Test that verifies various cases of napi threaded
@@ -56,17 +141,21 @@ def dev_set_threaded(nf) -> None:
56141

57142
# check napi threaded is set for both napis
58143
napi0 = nf.napi_get({'id': napi0_id})
144+
ksft_eq(napi0['threaded'], 1)
59145
ksft_ne(napi0.get('pid'), None)
60146
napi1 = nf.napi_get({'id': napi1_id})
147+
ksft_eq(napi1['threaded'], 1)
61148
ksft_ne(napi1.get('pid'), None)
62149

63150
# unset threaded
64151
system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded")
65152

66153
# check napi threaded is unset for both napis
67154
napi0 = nf.napi_get({'id': napi0_id})
155+
ksft_eq(napi0['threaded'], 0)
68156
ksft_eq(napi0.get('pid'), None)
69157
napi1 = nf.napi_get({'id': napi1_id})
158+
ksft_eq(napi1['threaded'], 0)
70159
ksft_eq(napi1.get('pid'), None)
71160

72161
def nsim_rxq_reset_down(nf) -> None:
@@ -156,7 +245,7 @@ def get_pp():
156245
def main() -> None:
157246
nf = NetdevFamily()
158247
ksft_run([empty_check, lo_check, page_pool_check, napi_list_check,
159-
dev_set_threaded, nsim_rxq_reset_down],
248+
dev_set_threaded, napi_set_threaded, nsim_rxq_reset_down],
160249
args=(nf, ))
161250
ksft_exit()
162251

0 commit comments

Comments
 (0)