| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- #!/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()
|