xref: /linux/tools/testing/selftests/net/nl_netdev.py (revision 25489a4f556414445d342951615178368ee45cde)
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 dev_set_threaded(nf) -> None:
39    """
40    Test that verifies various cases of napi threaded
41    set and unset at device level using sysfs.
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 threaded
55        system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded")
56
57        # check napi threaded is set for both napis
58        napi0 = nf.napi_get({'id': napi0_id})
59        ksft_ne(napi0.get('pid'), None)
60        napi1 = nf.napi_get({'id': napi1_id})
61        ksft_ne(napi1.get('pid'), None)
62
63        # unset threaded
64        system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded")
65
66        # check napi threaded is unset for both napis
67        napi0 = nf.napi_get({'id': napi0_id})
68        ksft_eq(napi0.get('pid'), None)
69        napi1 = nf.napi_get({'id': napi1_id})
70        ksft_eq(napi1.get('pid'), None)
71
72def nsim_rxq_reset_down(nf) -> None:
73    """
74    Test that the queue API supports resetting a queue
75    while the interface is down. We should convert this
76    test to testing real HW once more devices support
77    queue API.
78    """
79    with NetdevSimDev(queue_count=4) as nsimdev:
80        nsim = nsimdev.nsims[0]
81
82        ip(f"link set dev {nsim.ifname} down")
83        for i in [0, 2, 3]:
84            nsim.dfs_write("queue_reset", f"1 {i}")
85
86
87def page_pool_check(nf) -> None:
88    with NetdevSimDev() as nsimdev:
89        nsim = nsimdev.nsims[0]
90
91        def up():
92            ip(f"link set dev {nsim.ifname} up")
93
94        def down():
95            ip(f"link set dev {nsim.ifname} down")
96
97        def get_pp():
98            pp_list = nf.page_pool_get({}, dump=True)
99            return [pp for pp in pp_list if pp.get("ifindex") == nsim.ifindex]
100
101        # No page pools when down
102        down()
103        ksft_eq(len(get_pp()), 0)
104
105        # Up, empty page pool appears
106        up()
107        pp_list = get_pp()
108        ksft_ge(len(pp_list), 0)
109        refs = sum([pp["inflight"] for pp in pp_list])
110        ksft_eq(refs, 0)
111
112        # Down, it disappears, again
113        down()
114        pp_list = get_pp()
115        ksft_eq(len(pp_list), 0)
116
117        # Up, allocate a page
118        up()
119        nsim.dfs_write("pp_hold", "y")
120        pp_list = nf.page_pool_get({}, dump=True)
121        refs = sum([pp["inflight"] for pp in pp_list if pp.get("ifindex") == nsim.ifindex])
122        ksft_ge(refs, 1)
123
124        # Now let's leak a page
125        down()
126        pp_list = get_pp()
127        ksft_eq(len(pp_list), 1)
128        refs = sum([pp["inflight"] for pp in pp_list])
129        ksft_eq(refs, 1)
130        attached = [pp for pp in pp_list if "detach-time" not in pp]
131        ksft_eq(len(attached), 0)
132
133        # New pp can get created, and we'll have two
134        up()
135        pp_list = get_pp()
136        attached = [pp for pp in pp_list if "detach-time" not in pp]
137        detached = [pp for pp in pp_list if "detach-time" in pp]
138        ksft_eq(len(attached), 1)
139        ksft_eq(len(detached), 1)
140
141        # Free the old page and the old pp is gone
142        nsim.dfs_write("pp_hold", "n")
143        # Freeing check is once a second so we may need to retry
144        ksft_busy_wait(lambda: len(get_pp()) == 1, deadline=2)
145
146        # And down...
147        down()
148        ksft_eq(len(get_pp()), 0)
149
150        # Last, leave the page hanging for destroy, nothing to check
151        # we're trying to exercise the orphaning path in the kernel
152        up()
153        nsim.dfs_write("pp_hold", "y")
154
155
156def main() -> None:
157    nf = NetdevFamily()
158    ksft_run([empty_check, lo_check, page_pool_check, napi_list_check,
159              dev_set_threaded, nsim_rxq_reset_down],
160             args=(nf, ))
161    ksft_exit()
162
163
164if __name__ == "__main__":
165    main()
166