1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4import time
5from os import system
6from lib.py import ksft_run, ksft_exit, ksft_pr
7from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_busy_wait
8from lib.py import NetdevFamily, NetdevSimDev, ip
9
10
11def empty_check(nf) -> None:
12    devs = nf.dev_get({}, dump=True)
13    ksft_ge(len(devs), 1)
14
15
16def lo_check(nf) -> None:
17    lo_info = nf.dev_get({"ifindex": 1})
18    ksft_eq(len(lo_info['xdp-features']), 0)
19    ksft_eq(len(lo_info['xdp-rx-metadata-features']), 0)
20
21
22def napi_list_check(nf) -> None:
23    with NetdevSimDev(queue_count=100) as nsimdev:
24        nsim = nsimdev.nsims[0]
25
26        ip(f"link set dev {nsim.ifname} up")
27
28        napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True)
29        ksft_eq(len(napis), 100)
30
31        for q in [50, 0, 99]:
32            for i in range(4):
33                nsim.dfs_write("queue_reset", f"{q} {i}")
34                napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True)
35                ksft_eq(len(napis), 100,
36                        comment=f"queue count after reset queue {q} mode {i}")
37
38def 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': "enabled"})
56        napi0 = nf.napi_get({'id': napi0_id})
57        ksft_eq(napi0['threaded'], "enabled")
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'], "disabled")
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'], "enabled")
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'], "disabled")
76        ksft_eq(napi1.get('pid'), None)
77
78        # unset napi threaded and verify
79        nf.napi_set({'id': napi0_id, 'threaded': "disabled"})
80        napi0 = nf.napi_get({'id': napi0_id})
81        ksft_eq(napi0['threaded'], "disabled")
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'], "enabled")
90        ksft_ne(napi0.get('pid'), None)
91        napi1 = nf.napi_get({'id': napi1_id})
92        ksft_eq(napi1['threaded'], "enabled")
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'], "disabled")
101        ksft_eq(napi0.get('pid'), None)
102        napi1 = nf.napi_get({'id': napi1_id})
103        ksft_eq(napi1['threaded'], "disabled")
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'], "enabled")
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'], "disabled")
118        ksft_eq(napi0.get('pid'), None)
119        napi1 = nf.napi_get({'id': napi1_id})
120        ksft_eq(napi1['threaded'], "disabled")
121        ksft_eq(napi1.get('pid'), None)
122
123def dev_set_threaded(nf) -> None:
124    """
125    Test that verifies various cases of napi threaded
126    set and unset at device level using sysfs.
127    """
128    with NetdevSimDev(queue_count=2) as nsimdev:
129        nsim = nsimdev.nsims[0]
130
131        ip(f"link set dev {nsim.ifname} up")
132
133        napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True)
134        ksft_eq(len(napis), 2)
135
136        napi0_id = napis[0]['id']
137        napi1_id = napis[1]['id']
138
139        # set threaded
140        system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded")
141
142        # check napi threaded is set for both napis
143        napi0 = nf.napi_get({'id': napi0_id})
144        ksft_eq(napi0['threaded'], "enabled")
145        ksft_ne(napi0.get('pid'), None)
146        napi1 = nf.napi_get({'id': napi1_id})
147        ksft_eq(napi1['threaded'], "enabled")
148        ksft_ne(napi1.get('pid'), None)
149
150        # unset threaded
151        system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded")
152
153        # check napi threaded is unset for both napis
154        napi0 = nf.napi_get({'id': napi0_id})
155        ksft_eq(napi0['threaded'], "disabled")
156        ksft_eq(napi0.get('pid'), None)
157        napi1 = nf.napi_get({'id': napi1_id})
158        ksft_eq(napi1['threaded'], "disabled")
159        ksft_eq(napi1.get('pid'), None)
160
161def nsim_rxq_reset_down(nf) -> None:
162    """
163    Test that the queue API supports resetting a queue
164    while the interface is down. We should convert this
165    test to testing real HW once more devices support
166    queue API.
167    """
168    with NetdevSimDev(queue_count=4) as nsimdev:
169        nsim = nsimdev.nsims[0]
170
171        ip(f"link set dev {nsim.ifname} down")
172        for i in [0, 2, 3]:
173            nsim.dfs_write("queue_reset", f"1 {i}")
174
175
176def page_pool_check(nf) -> None:
177    with NetdevSimDev() as nsimdev:
178        nsim = nsimdev.nsims[0]
179
180        def up():
181            ip(f"link set dev {nsim.ifname} up")
182
183        def down():
184            ip(f"link set dev {nsim.ifname} down")
185
186        def get_pp():
187            pp_list = nf.page_pool_get({}, dump=True)
188            return [pp for pp in pp_list if pp.get("ifindex") == nsim.ifindex]
189
190        # No page pools when down
191        down()
192        ksft_eq(len(get_pp()), 0)
193
194        # Up, empty page pool appears
195        up()
196        pp_list = get_pp()
197        ksft_ge(len(pp_list), 0)
198        refs = sum([pp["inflight"] for pp in pp_list])
199        ksft_eq(refs, 0)
200
201        # Down, it disappears, again
202        down()
203        pp_list = get_pp()
204        ksft_eq(len(pp_list), 0)
205
206        # Up, allocate a page
207        up()
208        nsim.dfs_write("pp_hold", "y")
209        pp_list = nf.page_pool_get({}, dump=True)
210        refs = sum([pp["inflight"] for pp in pp_list if pp.get("ifindex") == nsim.ifindex])
211        ksft_ge(refs, 1)
212
213        # Now let's leak a page
214        down()
215        pp_list = get_pp()
216        ksft_eq(len(pp_list), 1)
217        refs = sum([pp["inflight"] for pp in pp_list])
218        ksft_eq(refs, 1)
219        attached = [pp for pp in pp_list if "detach-time" not in pp]
220        ksft_eq(len(attached), 0)
221
222        # New pp can get created, and we'll have two
223        up()
224        pp_list = get_pp()
225        attached = [pp for pp in pp_list if "detach-time" not in pp]
226        detached = [pp for pp in pp_list if "detach-time" in pp]
227        ksft_eq(len(attached), 1)
228        ksft_eq(len(detached), 1)
229
230        # Free the old page and the old pp is gone
231        nsim.dfs_write("pp_hold", "n")
232        # Freeing check is once a second so we may need to retry
233        ksft_busy_wait(lambda: len(get_pp()) == 1, deadline=2)
234
235        # And down...
236        down()
237        ksft_eq(len(get_pp()), 0)
238
239        # Last, leave the page hanging for destroy, nothing to check
240        # we're trying to exercise the orphaning path in the kernel
241        up()
242        nsim.dfs_write("pp_hold", "y")
243
244
245def main() -> None:
246    nf = NetdevFamily()
247    ksft_run([empty_check, lo_check, page_pool_check, napi_list_check,
248              dev_set_threaded, napi_set_threaded, nsim_rxq_reset_down],
249             args=(nf, ))
250    ksft_exit()
251
252
253if __name__ == "__main__":
254    main()
255