| #!/usr/bin/env python3 | |
| # SPDX-License-Identifier: GPL-2.0 | |
| import errno | |
| from lib.py import ksft_run, ksft_exit, ksft_pr | |
| from lib.py import ksft_ge, ksft_eq, ksft_in, ksft_true, ksft_raises, KsftSkipEx, KsftXfailEx | |
| from lib.py import ksft_disruptive | |
| from lib.py import EthtoolFamily, NetdevFamily, RtnlFamily, NlError | |
| from lib.py import NetDrvEnv | |
| from lib.py import ip, defer | |
| ethnl = EthtoolFamily() | |
| netfam = NetdevFamily() | |
| rtnl = RtnlFamily() | |
| def check_pause(cfg) -> None: | |
| global ethnl | |
| try: | |
| ethnl.pause_get({"header": {"dev-index": cfg.ifindex}}) | |
| except NlError as e: | |
| if e.error == errno.EOPNOTSUPP: | |
| raise KsftXfailEx("pause not supported by the device") | |
| raise | |
| data = ethnl.pause_get({"header": {"dev-index": cfg.ifindex, | |
| "flags": {'stats'}}}) | |
| ksft_true(data['stats'], "driver does not report stats") | |
| def check_fec(cfg) -> None: | |
| global ethnl | |
| try: | |
| ethnl.fec_get({"header": {"dev-index": cfg.ifindex}}) | |
| except NlError as e: | |
| if e.error == errno.EOPNOTSUPP: | |
| raise KsftXfailEx("FEC not supported by the device") | |
| raise | |
| data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex, | |
| "flags": {'stats'}}}) | |
| ksft_true(data['stats'], "driver does not report stats") | |
| def pkt_byte_sum(cfg) -> None: | |
| global netfam, rtnl | |
| def get_qstat(test): | |
| global netfam | |
| stats = netfam.qstats_get({}, dump=True) | |
| if stats: | |
| for qs in stats: | |
| if qs["ifindex"]== test.ifindex: | |
| return qs | |
| qstat = get_qstat(cfg) | |
| if qstat is None: | |
| raise KsftSkipEx("qstats not supported by the device") | |
| for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']: | |
| ksft_in(key, qstat, "Drivers should always report basic keys") | |
| # Compare stats, rtnl stats and qstats must match, | |
| # but the interface may be up, so do a series of dumps | |
| # each time the more "recent" stats must be higher or same. | |
| def stat_cmp(rstat, qstat): | |
| for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']: | |
| if rstat[key] != qstat[key]: | |
| return rstat[key] - qstat[key] | |
| return 0 | |
| for _ in range(10): | |
| rtstat = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] | |
| if stat_cmp(rtstat, qstat) < 0: | |
| raise Exception("RTNL stats are lower, fetched later") | |
| qstat = get_qstat(cfg) | |
| if stat_cmp(rtstat, qstat) > 0: | |
| raise Exception("Qstats are lower, fetched later") | |
| def qstat_by_ifindex(cfg) -> None: | |
| global netfam | |
| global rtnl | |
| # Construct a map ifindex -> [dump, by-index, dump] | |
| ifindexes = {} | |
| stats = netfam.qstats_get({}, dump=True) | |
| for entry in stats: | |
| ifindexes[entry['ifindex']] = [entry, None, None] | |
| for ifindex in ifindexes.keys(): | |
| entry = netfam.qstats_get({"ifindex": ifindex}, dump=True) | |
| ksft_eq(len(entry), 1) | |
| ifindexes[entry[0]['ifindex']][1] = entry[0] | |
| stats = netfam.qstats_get({}, dump=True) | |
| for entry in stats: | |
| ifindexes[entry['ifindex']][2] = entry | |
| if len(ifindexes) == 0: | |
| raise KsftSkipEx("No ifindex supports qstats") | |
| # Now make sure the stats match/make sense | |
| for ifindex, triple in ifindexes.items(): | |
| all_keys = triple[0].keys() | triple[1].keys() | triple[2].keys() | |
| for key in all_keys: | |
| ksft_ge(triple[1][key], triple[0][key], comment="bad key: " + key) | |
| ksft_ge(triple[2][key], triple[1][key], comment="bad key: " + key) | |
| # Test invalid dumps | |
| # 0 is invalid | |
| with ksft_raises(NlError) as cm: | |
| netfam.qstats_get({"ifindex": 0}, dump=True) | |
| ksft_eq(cm.exception.nl_msg.error, -34) | |
| ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') | |
| # loopback has no stats | |
| with ksft_raises(NlError) as cm: | |
| netfam.qstats_get({"ifindex": 1}, dump=True) | |
| ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP) | |
| ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') | |
| # Try to get stats for lowest unused ifindex but not 0 | |
| devs = rtnl.getlink({}, dump=True) | |
| all_ifindexes = set([dev["ifi-index"] for dev in devs]) | |
| lowest = 2 | |
| while lowest in all_ifindexes: | |
| lowest += 1 | |
| with ksft_raises(NlError) as cm: | |
| netfam.qstats_get({"ifindex": lowest}, dump=True) | |
| ksft_eq(cm.exception.nl_msg.error, -19) | |
| ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') | |
| @ksft_disruptive | |
| def check_down(cfg) -> None: | |
| try: | |
| qstat = netfam.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] | |
| except NlError as e: | |
| if e.error == errno.EOPNOTSUPP: | |
| raise KsftSkipEx("qstats not supported by the device") | |
| raise | |
| ip(f"link set dev {cfg.dev['ifname']} down") | |
| defer(ip, f"link set dev {cfg.dev['ifname']} up") | |
| qstat2 = netfam.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] | |
| for k, v in qstat.items(): | |
| ksft_ge(qstat2[k], qstat[k], comment=f"{k} went backwards on device down") | |
| # exercise per-queue API to make sure that "device down" state | |
| # is handled correctly and doesn't crash | |
| netfam.qstats_get({"ifindex": cfg.ifindex, "scope": "queue"}, dump=True) | |
| def main() -> None: | |
| with NetDrvEnv(__file__) as cfg: | |
| ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex, | |
| check_down], | |
| args=(cfg, )) | |
| ksft_exit() | |
| if __name__ == "__main__": | |
| main() |