1 #!/usr/bin/env python3 1 #!/usr/bin/env python3 2 # SPDX-License-Identifier: GPL-2.0 2 # SPDX-License-Identifier: GPL-2.0 3 3 4 import errno 4 import errno 5 from lib.py import ksft_run, ksft_exit, ksft_p 5 from lib.py import ksft_run, ksft_exit, ksft_pr 6 from lib.py import ksft_ge, ksft_eq, ksft_in, 6 from lib.py import ksft_ge, ksft_eq, ksft_in, ksft_true, ksft_raises, KsftSkipEx, KsftXfailEx 7 from lib.py import ksft_disruptive 7 from lib.py import ksft_disruptive 8 from lib.py import EthtoolFamily, NetdevFamily 8 from lib.py import EthtoolFamily, NetdevFamily, RtnlFamily, NlError 9 from lib.py import NetDrvEnv 9 from lib.py import NetDrvEnv 10 from lib.py import ip, defer 10 from lib.py import ip, defer 11 11 12 ethnl = EthtoolFamily() 12 ethnl = EthtoolFamily() 13 netfam = NetdevFamily() 13 netfam = NetdevFamily() 14 rtnl = RtnlFamily() 14 rtnl = RtnlFamily() 15 15 16 16 17 def check_pause(cfg) -> None: 17 def check_pause(cfg) -> None: 18 global ethnl 18 global ethnl 19 19 20 try: 20 try: 21 ethnl.pause_get({"header": {"dev-index 21 ethnl.pause_get({"header": {"dev-index": cfg.ifindex}}) 22 except NlError as e: 22 except NlError as e: 23 if e.error == errno.EOPNOTSUPP: 23 if e.error == errno.EOPNOTSUPP: 24 raise KsftXfailEx("pause not suppo 24 raise KsftXfailEx("pause not supported by the device") 25 raise 25 raise 26 26 27 data = ethnl.pause_get({"header": {"dev-in 27 data = ethnl.pause_get({"header": {"dev-index": cfg.ifindex, 28 "flags" 28 "flags": {'stats'}}}) 29 ksft_true(data['stats'], "driver does not 29 ksft_true(data['stats'], "driver does not report stats") 30 30 31 31 32 def check_fec(cfg) -> None: 32 def check_fec(cfg) -> None: 33 global ethnl 33 global ethnl 34 34 35 try: 35 try: 36 ethnl.fec_get({"header": {"dev-index": 36 ethnl.fec_get({"header": {"dev-index": cfg.ifindex}}) 37 except NlError as e: 37 except NlError as e: 38 if e.error == errno.EOPNOTSUPP: 38 if e.error == errno.EOPNOTSUPP: 39 raise KsftXfailEx("FEC not support 39 raise KsftXfailEx("FEC not supported by the device") 40 raise 40 raise 41 41 42 data = ethnl.fec_get({"header": {"dev-inde 42 data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex, 43 "flags": 43 "flags": {'stats'}}}) 44 ksft_true(data['stats'], "driver does not 44 ksft_true(data['stats'], "driver does not report stats") 45 45 46 46 47 def pkt_byte_sum(cfg) -> None: 47 def pkt_byte_sum(cfg) -> None: 48 global netfam, rtnl 48 global netfam, rtnl 49 49 50 def get_qstat(test): 50 def get_qstat(test): 51 global netfam 51 global netfam 52 stats = netfam.qstats_get({}, dump=Tru 52 stats = netfam.qstats_get({}, dump=True) 53 if stats: 53 if stats: 54 for qs in stats: 54 for qs in stats: 55 if qs["ifindex"]== test.ifinde 55 if qs["ifindex"]== test.ifindex: 56 return qs 56 return qs 57 57 58 qstat = get_qstat(cfg) 58 qstat = get_qstat(cfg) 59 if qstat is None: 59 if qstat is None: 60 raise KsftSkipEx("qstats not supported 60 raise KsftSkipEx("qstats not supported by the device") 61 61 62 for key in ['tx-packets', 'tx-bytes', 'rx- 62 for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']: 63 ksft_in(key, qstat, "Drivers should al 63 ksft_in(key, qstat, "Drivers should always report basic keys") 64 64 65 # Compare stats, rtnl stats and qstats mus 65 # Compare stats, rtnl stats and qstats must match, 66 # but the interface may be up, so do a ser 66 # but the interface may be up, so do a series of dumps 67 # each time the more "recent" stats must b 67 # each time the more "recent" stats must be higher or same. 68 def stat_cmp(rstat, qstat): 68 def stat_cmp(rstat, qstat): 69 for key in ['tx-packets', 'tx-bytes', 69 for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']: 70 if rstat[key] != qstat[key]: 70 if rstat[key] != qstat[key]: 71 return rstat[key] - qstat[key] 71 return rstat[key] - qstat[key] 72 return 0 72 return 0 73 73 74 for _ in range(10): 74 for _ in range(10): 75 rtstat = rtnl.getlink({"ifi-index": cf 75 rtstat = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64'] 76 if stat_cmp(rtstat, qstat) < 0: 76 if stat_cmp(rtstat, qstat) < 0: 77 raise Exception("RTNL stats are lo 77 raise Exception("RTNL stats are lower, fetched later") 78 qstat = get_qstat(cfg) 78 qstat = get_qstat(cfg) 79 if stat_cmp(rtstat, qstat) > 0: 79 if stat_cmp(rtstat, qstat) > 0: 80 raise Exception("Qstats are lower, 80 raise Exception("Qstats are lower, fetched later") 81 81 82 82 83 def qstat_by_ifindex(cfg) -> None: 83 def qstat_by_ifindex(cfg) -> None: 84 global netfam 84 global netfam 85 global rtnl 85 global rtnl 86 86 87 # Construct a map ifindex -> [dump, by-ind 87 # Construct a map ifindex -> [dump, by-index, dump] 88 ifindexes = {} 88 ifindexes = {} 89 stats = netfam.qstats_get({}, dump=True) 89 stats = netfam.qstats_get({}, dump=True) 90 for entry in stats: 90 for entry in stats: 91 ifindexes[entry['ifindex']] = [entry, 91 ifindexes[entry['ifindex']] = [entry, None, None] 92 92 93 for ifindex in ifindexes.keys(): 93 for ifindex in ifindexes.keys(): 94 entry = netfam.qstats_get({"ifindex": 94 entry = netfam.qstats_get({"ifindex": ifindex}, dump=True) 95 ksft_eq(len(entry), 1) 95 ksft_eq(len(entry), 1) 96 ifindexes[entry[0]['ifindex']][1] = en 96 ifindexes[entry[0]['ifindex']][1] = entry[0] 97 97 98 stats = netfam.qstats_get({}, dump=True) 98 stats = netfam.qstats_get({}, dump=True) 99 for entry in stats: 99 for entry in stats: 100 ifindexes[entry['ifindex']][2] = entry 100 ifindexes[entry['ifindex']][2] = entry 101 101 102 if len(ifindexes) == 0: 102 if len(ifindexes) == 0: 103 raise KsftSkipEx("No ifindex supports 103 raise KsftSkipEx("No ifindex supports qstats") 104 104 105 # Now make sure the stats match/make sense 105 # Now make sure the stats match/make sense 106 for ifindex, triple in ifindexes.items(): 106 for ifindex, triple in ifindexes.items(): 107 all_keys = triple[0].keys() | triple[1 107 all_keys = triple[0].keys() | triple[1].keys() | triple[2].keys() 108 108 109 for key in all_keys: 109 for key in all_keys: 110 ksft_ge(triple[1][key], triple[0][ 110 ksft_ge(triple[1][key], triple[0][key], comment="bad key: " + key) 111 ksft_ge(triple[2][key], triple[1][ 111 ksft_ge(triple[2][key], triple[1][key], comment="bad key: " + key) 112 112 113 # Test invalid dumps 113 # Test invalid dumps 114 # 0 is invalid 114 # 0 is invalid 115 with ksft_raises(NlError) as cm: 115 with ksft_raises(NlError) as cm: 116 netfam.qstats_get({"ifindex": 0}, dump 116 netfam.qstats_get({"ifindex": 0}, dump=True) 117 ksft_eq(cm.exception.nl_msg.error, -34) 117 ksft_eq(cm.exception.nl_msg.error, -34) 118 ksft_eq(cm.exception.nl_msg.extack['bad-at 118 ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') 119 119 120 # loopback has no stats 120 # loopback has no stats 121 with ksft_raises(NlError) as cm: 121 with ksft_raises(NlError) as cm: 122 netfam.qstats_get({"ifindex": 1}, dump 122 netfam.qstats_get({"ifindex": 1}, dump=True) 123 ksft_eq(cm.exception.nl_msg.error, -errno. 123 ksft_eq(cm.exception.nl_msg.error, -errno.EOPNOTSUPP) 124 ksft_eq(cm.exception.nl_msg.extack['bad-at 124 ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') 125 125 126 # Try to get stats for lowest unused ifind 126 # Try to get stats for lowest unused ifindex but not 0 127 devs = rtnl.getlink({}, dump=True) 127 devs = rtnl.getlink({}, dump=True) 128 all_ifindexes = set([dev["ifi-index"] for 128 all_ifindexes = set([dev["ifi-index"] for dev in devs]) 129 lowest = 2 129 lowest = 2 130 while lowest in all_ifindexes: 130 while lowest in all_ifindexes: 131 lowest += 1 131 lowest += 1 132 132 133 with ksft_raises(NlError) as cm: 133 with ksft_raises(NlError) as cm: 134 netfam.qstats_get({"ifindex": lowest}, 134 netfam.qstats_get({"ifindex": lowest}, dump=True) 135 ksft_eq(cm.exception.nl_msg.error, -19) 135 ksft_eq(cm.exception.nl_msg.error, -19) 136 ksft_eq(cm.exception.nl_msg.extack['bad-at 136 ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex') 137 137 138 138 139 @ksft_disruptive 139 @ksft_disruptive 140 def check_down(cfg) -> None: 140 def check_down(cfg) -> None: 141 try: 141 try: 142 qstat = netfam.qstats_get({"ifindex": 142 qstat = netfam.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 143 except NlError as e: 143 except NlError as e: 144 if e.error == errno.EOPNOTSUPP: 144 if e.error == errno.EOPNOTSUPP: 145 raise KsftSkipEx("qstats not suppo 145 raise KsftSkipEx("qstats not supported by the device") 146 raise 146 raise 147 147 148 ip(f"link set dev {cfg.dev['ifname']} down 148 ip(f"link set dev {cfg.dev['ifname']} down") 149 defer(ip, f"link set dev {cfg.dev['ifname' 149 defer(ip, f"link set dev {cfg.dev['ifname']} up") 150 150 151 qstat2 = netfam.qstats_get({"ifindex": cfg 151 qstat2 = netfam.qstats_get({"ifindex": cfg.ifindex}, dump=True)[0] 152 for k, v in qstat.items(): 152 for k, v in qstat.items(): 153 ksft_ge(qstat2[k], qstat[k], comment=f 153 ksft_ge(qstat2[k], qstat[k], comment=f"{k} went backwards on device down") 154 154 155 # exercise per-queue API to make sure that 155 # exercise per-queue API to make sure that "device down" state 156 # is handled correctly and doesn't crash 156 # is handled correctly and doesn't crash 157 netfam.qstats_get({"ifindex": cfg.ifindex, 157 netfam.qstats_get({"ifindex": cfg.ifindex, "scope": "queue"}, dump=True) 158 158 159 159 160 def main() -> None: 160 def main() -> None: 161 with NetDrvEnv(__file__) as cfg: 161 with NetDrvEnv(__file__) as cfg: 162 ksft_run([check_pause, check_fec, pkt_ 162 ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex, 163 check_down], 163 check_down], 164 args=(cfg, )) 164 args=(cfg, )) 165 ksft_exit() 165 ksft_exit() 166 166 167 167 168 if __name__ == "__main__": 168 if __name__ == "__main__": 169 main() 169 main()
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.