1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3 4""" 5Tests for the netdev netlink family. 6""" 7 8import errno 9from os import system 10from lib.py import ksft_run, ksft_exit 11from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_raises, ksft_busy_wait 12from lib.py import NetdevFamily, NetdevSimDev, NlError, ip 13 14 15def empty_check(nf) -> None: 16 devs = nf.dev_get({}, dump=True) 17 ksft_ge(len(devs), 1) 18 19 20def lo_check(nf) -> None: 21 lo_info = nf.dev_get({"ifindex": 1}) 22 ksft_eq(len(lo_info['xdp-features']), 0) 23 ksft_eq(len(lo_info['xdp-rx-metadata-features']), 0) 24 25 26def dev_dump_reject_attr(nf) -> None: 27 """Test that dev-get dump rejects attributes (no dump request policy).""" 28 with ksft_raises(NlError) as cm: 29 nf.dev_get({'ifindex': 1}, dump=True) 30 ksft_eq(cm.exception.nl_msg.error, -errno.EINVAL) 31 ksft_eq(cm.exception.nl_msg.extack['msg'], 'Unknown attribute type') 32 ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') 33 34 35def napi_list_check(nf) -> None: 36 with NetdevSimDev(queue_count=100) as nsimdev: 37 nsim = nsimdev.nsims[0] 38 39 ip(f"link set dev {nsim.ifname} up") 40 41 napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) 42 ksft_eq(len(napis), 100) 43 44 for q in [50, 0, 99]: 45 for i in range(4): 46 nsim.dfs_write("queue_reset", f"{q} {i}") 47 napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) 48 ksft_eq(len(napis), 100, 49 comment=f"queue count after reset queue {q} mode {i}") 50 51def napi_set_threaded(nf) -> None: 52 """ 53 Test that verifies various cases of napi threaded 54 set and unset at napi and device level. 55 """ 56 with NetdevSimDev(queue_count=2) as nsimdev: 57 nsim = nsimdev.nsims[0] 58 59 ip(f"link set dev {nsim.ifname} up") 60 61 napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) 62 ksft_eq(len(napis), 2) 63 64 napi0_id = napis[0]['id'] 65 napi1_id = napis[1]['id'] 66 67 # set napi threaded and verify 68 nf.napi_set({'id': napi0_id, 'threaded': "enabled"}) 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 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 ip(f"link set dev {nsim.ifname} down") 79 ip(f"link set dev {nsim.ifname} up") 80 81 # verify if napi threaded is still set 82 napi0 = nf.napi_get({'id': napi0_id}) 83 ksft_eq(napi0['threaded'], "enabled") 84 ksft_ne(napi0.get('pid'), None) 85 86 # check it is still not set for napi1 87 napi1 = nf.napi_get({'id': napi1_id}) 88 ksft_eq(napi1['threaded'], "disabled") 89 ksft_eq(napi1.get('pid'), None) 90 91 # unset napi threaded and verify 92 nf.napi_set({'id': napi0_id, 'threaded': "disabled"}) 93 napi0 = nf.napi_get({'id': napi0_id}) 94 ksft_eq(napi0['threaded'], "disabled") 95 ksft_eq(napi0.get('pid'), None) 96 97 # set threaded at device level 98 system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded") 99 100 # check napi threaded is set for both napis 101 napi0 = nf.napi_get({'id': napi0_id}) 102 ksft_eq(napi0['threaded'], "enabled") 103 ksft_ne(napi0.get('pid'), None) 104 napi1 = nf.napi_get({'id': napi1_id}) 105 ksft_eq(napi1['threaded'], "enabled") 106 ksft_ne(napi1.get('pid'), None) 107 108 # unset threaded at device level 109 system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") 110 111 # check napi threaded is unset for both napis 112 napi0 = nf.napi_get({'id': napi0_id}) 113 ksft_eq(napi0['threaded'], "disabled") 114 ksft_eq(napi0.get('pid'), None) 115 napi1 = nf.napi_get({'id': napi1_id}) 116 ksft_eq(napi1['threaded'], "disabled") 117 ksft_eq(napi1.get('pid'), None) 118 119 # set napi threaded for napi0 120 nf.napi_set({'id': napi0_id, 'threaded': 1}) 121 napi0 = nf.napi_get({'id': napi0_id}) 122 ksft_eq(napi0['threaded'], "enabled") 123 ksft_ne(napi0.get('pid'), None) 124 125 # unset threaded at device level 126 system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") 127 128 # check napi threaded is unset for both napis 129 napi0 = nf.napi_get({'id': napi0_id}) 130 ksft_eq(napi0['threaded'], "disabled") 131 ksft_eq(napi0.get('pid'), None) 132 napi1 = nf.napi_get({'id': napi1_id}) 133 ksft_eq(napi1['threaded'], "disabled") 134 ksft_eq(napi1.get('pid'), None) 135 136def dev_set_threaded(nf) -> None: 137 """ 138 Test that verifies various cases of napi threaded 139 set and unset at device level using sysfs. 140 """ 141 with NetdevSimDev(queue_count=2) as nsimdev: 142 nsim = nsimdev.nsims[0] 143 144 ip(f"link set dev {nsim.ifname} up") 145 146 napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) 147 ksft_eq(len(napis), 2) 148 149 napi0_id = napis[0]['id'] 150 napi1_id = napis[1]['id'] 151 152 # set threaded 153 system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded") 154 155 # check napi threaded is set for both napis 156 napi0 = nf.napi_get({'id': napi0_id}) 157 ksft_eq(napi0['threaded'], "enabled") 158 ksft_ne(napi0.get('pid'), None) 159 napi1 = nf.napi_get({'id': napi1_id}) 160 ksft_eq(napi1['threaded'], "enabled") 161 ksft_ne(napi1.get('pid'), None) 162 163 # unset threaded 164 system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") 165 166 # check napi threaded is unset for both napis 167 napi0 = nf.napi_get({'id': napi0_id}) 168 ksft_eq(napi0['threaded'], "disabled") 169 ksft_eq(napi0.get('pid'), None) 170 napi1 = nf.napi_get({'id': napi1_id}) 171 ksft_eq(napi1['threaded'], "disabled") 172 ksft_eq(napi1.get('pid'), None) 173 174def nsim_rxq_reset_down(nf) -> None: 175 """ 176 Test that the queue API supports resetting a queue 177 while the interface is down. We should convert this 178 test to testing real HW once more devices support 179 queue API. 180 """ 181 with NetdevSimDev(queue_count=4) as nsimdev: 182 nsim = nsimdev.nsims[0] 183 184 ip(f"link set dev {nsim.ifname} down") 185 for i in [0, 2, 3]: 186 nsim.dfs_write("queue_reset", f"1 {i}") 187 188 189def page_pool_check(nf) -> None: 190 with NetdevSimDev() as nsimdev: 191 nsim = nsimdev.nsims[0] 192 193 def up(): 194 ip(f"link set dev {nsim.ifname} up") 195 196 def down(): 197 ip(f"link set dev {nsim.ifname} down") 198 199 def get_pp(): 200 pp_list = nf.page_pool_get({}, dump=True) 201 return [pp for pp in pp_list if pp.get("ifindex") == nsim.ifindex] 202 203 # No page pools when down 204 down() 205 ksft_eq(len(get_pp()), 0) 206 207 # Up, empty page pool appears 208 up() 209 pp_list = get_pp() 210 ksft_ge(len(pp_list), 0) 211 refs = sum([pp["inflight"] for pp in pp_list]) 212 ksft_eq(refs, 0) 213 214 # Down, it disappears, again 215 down() 216 pp_list = get_pp() 217 ksft_eq(len(pp_list), 0) 218 219 # Up, allocate a page 220 up() 221 nsim.dfs_write("pp_hold", "y") 222 pp_list = nf.page_pool_get({}, dump=True) 223 refs = sum([pp["inflight"] for pp in pp_list if pp.get("ifindex") == nsim.ifindex]) 224 ksft_ge(refs, 1) 225 226 # Now let's leak a page 227 down() 228 pp_list = get_pp() 229 ksft_eq(len(pp_list), 1) 230 refs = sum([pp["inflight"] for pp in pp_list]) 231 ksft_eq(refs, 1) 232 attached = [pp for pp in pp_list if "detach-time" not in pp] 233 ksft_eq(len(attached), 0) 234 235 # New pp can get created, and we'll have two 236 up() 237 pp_list = get_pp() 238 attached = [pp for pp in pp_list if "detach-time" not in pp] 239 detached = [pp for pp in pp_list if "detach-time" in pp] 240 ksft_eq(len(attached), 1) 241 ksft_eq(len(detached), 1) 242 243 # Free the old page and the old pp is gone 244 nsim.dfs_write("pp_hold", "n") 245 # Freeing check is once a second so we may need to retry 246 ksft_busy_wait(lambda: len(get_pp()) == 1, deadline=2) 247 248 # And down... 249 down() 250 ksft_eq(len(get_pp()), 0) 251 252 # Last, leave the page hanging for destroy, nothing to check 253 # we're trying to exercise the orphaning path in the kernel 254 up() 255 nsim.dfs_write("pp_hold", "y") 256 257 258def main() -> None: 259 """ Ksft boiler plate main """ 260 nf = NetdevFamily() 261 ksft_run([empty_check, 262 lo_check, 263 dev_dump_reject_attr, 264 napi_list_check, 265 napi_set_threaded, 266 dev_set_threaded, 267 nsim_rxq_reset_down, 268 page_pool_check], 269 args=(nf, )) 270 ksft_exit() 271 272 273if __name__ == "__main__": 274 main() 275