#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 import time from os import system from lib.py import ksft_run, ksft_exit, ksft_pr from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_busy_wait from lib.py import NetdevFamily, NetdevSimDev, ip def empty_check(nf) -> None: devs = nf.dev_get({}, dump=True) ksft_ge(len(devs), 1) def lo_check(nf) -> None: lo_info = nf.dev_get({"ifindex": 1}) ksft_eq(len(lo_info['xdp-features']), 0) ksft_eq(len(lo_info['xdp-rx-metadata-features']), 0) def napi_list_check(nf) -> None: with NetdevSimDev(queue_count=100) as nsimdev: nsim = nsimdev.nsims[0] ip(f"link set dev {nsim.ifname} up") napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) ksft_eq(len(napis), 100) for q in [50, 0, 99]: for i in range(4): nsim.dfs_write("queue_reset", f"{q} {i}") napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) ksft_eq(len(napis), 100, comment=f"queue count after reset queue {q} mode {i}") def napi_set_threaded(nf) -> None: """ Test that verifies various cases of napi threaded set and unset at napi and device level. """ with NetdevSimDev(queue_count=2) as nsimdev: nsim = nsimdev.nsims[0] ip(f"link set dev {nsim.ifname} up") napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) ksft_eq(len(napis), 2) napi0_id = napis[0]['id'] napi1_id = napis[1]['id'] # set napi threaded and verify nf.napi_set({'id': napi0_id, 'threaded': "enabled"}) napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "enabled") ksft_ne(napi0.get('pid'), None) # check it is not set for napi1 napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "disabled") ksft_eq(napi1.get('pid'), None) ip(f"link set dev {nsim.ifname} down") ip(f"link set dev {nsim.ifname} up") # verify if napi threaded is still set napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "enabled") ksft_ne(napi0.get('pid'), None) # check it is still not set for napi1 napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "disabled") ksft_eq(napi1.get('pid'), None) # unset napi threaded and verify nf.napi_set({'id': napi0_id, 'threaded': "disabled"}) napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "disabled") ksft_eq(napi0.get('pid'), None) # set threaded at device level system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded") # check napi threaded is set for both napis napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "enabled") ksft_ne(napi0.get('pid'), None) napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "enabled") ksft_ne(napi1.get('pid'), None) # unset threaded at device level system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") # check napi threaded is unset for both napis napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "disabled") ksft_eq(napi0.get('pid'), None) napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "disabled") ksft_eq(napi1.get('pid'), None) # set napi threaded for napi0 nf.napi_set({'id': napi0_id, 'threaded': 1}) napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "enabled") ksft_ne(napi0.get('pid'), None) # unset threaded at device level system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") # check napi threaded is unset for both napis napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "disabled") ksft_eq(napi0.get('pid'), None) napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "disabled") ksft_eq(napi1.get('pid'), None) def dev_set_threaded(nf) -> None: """ Test that verifies various cases of napi threaded set and unset at device level using sysfs. """ with NetdevSimDev(queue_count=2) as nsimdev: nsim = nsimdev.nsims[0] ip(f"link set dev {nsim.ifname} up") napis = nf.napi_get({'ifindex': nsim.ifindex}, dump=True) ksft_eq(len(napis), 2) napi0_id = napis[0]['id'] napi1_id = napis[1]['id'] # set threaded system(f"echo 1 > /sys/class/net/{nsim.ifname}/threaded") # check napi threaded is set for both napis napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "enabled") ksft_ne(napi0.get('pid'), None) napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "enabled") ksft_ne(napi1.get('pid'), None) # unset threaded system(f"echo 0 > /sys/class/net/{nsim.ifname}/threaded") # check napi threaded is unset for both napis napi0 = nf.napi_get({'id': napi0_id}) ksft_eq(napi0['threaded'], "disabled") ksft_eq(napi0.get('pid'), None) napi1 = nf.napi_get({'id': napi1_id}) ksft_eq(napi1['threaded'], "disabled") ksft_eq(napi1.get('pid'), None) def nsim_rxq_reset_down(nf) -> None: """ Test that the queue API supports resetting a queue while the interface is down. We should convert this test to testing real HW once more devices support queue API. """ with NetdevSimDev(queue_count=4) as nsimdev: nsim = nsimdev.nsims[0] ip(f"link set dev {nsim.ifname} down") for i in [0, 2, 3]: nsim.dfs_write("queue_reset", f"1 {i}") def page_pool_check(nf) -> None: with NetdevSimDev() as nsimdev: nsim = nsimdev.nsims[0] def up(): ip(f"link set dev {nsim.ifname} up") def down(): ip(f"link set dev {nsim.ifname} down") def get_pp(): pp_list = nf.page_pool_get({}, dump=True) return [pp for pp in pp_list if pp.get("ifindex") == nsim.ifindex] # No page pools when down down() ksft_eq(len(get_pp()), 0) # Up, empty page pool appears up() pp_list = get_pp() ksft_ge(len(pp_list), 0) refs = sum([pp["inflight"] for pp in pp_list]) ksft_eq(refs, 0) # Down, it disappears, again down() pp_list = get_pp() ksft_eq(len(pp_list), 0) # Up, allocate a page up() nsim.dfs_write("pp_hold", "y") pp_list = nf.page_pool_get({}, dump=True) refs = sum([pp["inflight"] for pp in pp_list if pp.get("ifindex") == nsim.ifindex]) ksft_ge(refs, 1) # Now let's leak a page down() pp_list = get_pp() ksft_eq(len(pp_list), 1) refs = sum([pp["inflight"] for pp in pp_list]) ksft_eq(refs, 1) attached = [pp for pp in pp_list if "detach-time" not in pp] ksft_eq(len(attached), 0) # New pp can get created, and we'll have two up() pp_list = get_pp() attached = [pp for pp in pp_list if "detach-time" not in pp] detached = [pp for pp in pp_list if "detach-time" in pp] ksft_eq(len(attached), 1) ksft_eq(len(detached), 1) # Free the old page and the old pp is gone nsim.dfs_write("pp_hold", "n") # Freeing check is once a second so we may need to retry ksft_busy_wait(lambda: len(get_pp()) == 1, deadline=2) # And down... down() ksft_eq(len(get_pp()), 0) # Last, leave the page hanging for destroy, nothing to check # we're trying to exercise the orphaning path in the kernel up() nsim.dfs_write("pp_hold", "y") def main() -> None: nf = NetdevFamily() ksft_run([empty_check, lo_check, page_pool_check, napi_list_check, dev_set_threaded, napi_set_threaded, nsim_rxq_reset_down], args=(nf, )) ksft_exit() if __name__ == "__main__": main()