xref: /linux/tools/testing/selftests/drivers/net/stats.py (revision 24168c5e6dfbdd5b414f048f47f75d64533296ca)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4from lib.py import ksft_run, ksft_exit, ksft_pr
5from lib.py import ksft_ge, ksft_eq, ksft_in, ksft_true, ksft_raises, KsftSkipEx, KsftXfailEx
6from lib.py import EthtoolFamily, NetdevFamily, RtnlFamily, NlError
7from lib.py import NetDrvEnv
8
9ethnl = EthtoolFamily()
10netfam = NetdevFamily()
11rtnl = RtnlFamily()
12
13
14def check_pause(cfg) -> None:
15    global ethnl
16
17    try:
18        ethnl.pause_get({"header": {"dev-index": cfg.ifindex}})
19    except NlError as e:
20        if e.error == 95:
21            raise KsftXfailEx("pause not supported by the device")
22        raise
23
24    data = ethnl.pause_get({"header": {"dev-index": cfg.ifindex,
25                                       "flags": {'stats'}}})
26    ksft_true(data['stats'], "driver does not report stats")
27
28
29def check_fec(cfg) -> None:
30    global ethnl
31
32    try:
33        ethnl.fec_get({"header": {"dev-index": cfg.ifindex}})
34    except NlError as e:
35        if e.error == 95:
36            raise KsftXfailEx("FEC not supported by the device")
37        raise
38
39    data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex,
40                                     "flags": {'stats'}}})
41    ksft_true(data['stats'], "driver does not report stats")
42
43
44def pkt_byte_sum(cfg) -> None:
45    global netfam, rtnl
46
47    def get_qstat(test):
48        global netfam
49        stats = netfam.qstats_get({}, dump=True)
50        if stats:
51            for qs in stats:
52                if qs["ifindex"]== test.ifindex:
53                    return qs
54
55    qstat = get_qstat(cfg)
56    if qstat is None:
57        raise KsftSkipEx("qstats not supported by the device")
58
59    for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']:
60        ksft_in(key, qstat, "Drivers should always report basic keys")
61
62    # Compare stats, rtnl stats and qstats must match,
63    # but the interface may be up, so do a series of dumps
64    # each time the more "recent" stats must be higher or same.
65    def stat_cmp(rstat, qstat):
66        for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']:
67            if rstat[key] != qstat[key]:
68                return rstat[key] - qstat[key]
69        return 0
70
71    for _ in range(10):
72        rtstat = rtnl.getlink({"ifi-index": cfg.ifindex})['stats64']
73        if stat_cmp(rtstat, qstat) < 0:
74            raise Exception("RTNL stats are lower, fetched later")
75        qstat = get_qstat(cfg)
76        if stat_cmp(rtstat, qstat) > 0:
77            raise Exception("Qstats are lower, fetched later")
78
79
80def qstat_by_ifindex(cfg) -> None:
81    global netfam
82    global rtnl
83
84    # Construct a map ifindex -> [dump, by-index, dump]
85    ifindexes = {}
86    stats = netfam.qstats_get({}, dump=True)
87    for entry in stats:
88        ifindexes[entry['ifindex']] = [entry, None, None]
89
90    for ifindex in ifindexes.keys():
91        entry = netfam.qstats_get({"ifindex": ifindex}, dump=True)
92        ksft_eq(len(entry), 1)
93        ifindexes[entry[0]['ifindex']][1] = entry[0]
94
95    stats = netfam.qstats_get({}, dump=True)
96    for entry in stats:
97        ifindexes[entry['ifindex']][2] = entry
98
99    if len(ifindexes) == 0:
100        raise KsftSkipEx("No ifindex supports qstats")
101
102    # Now make sure the stats match/make sense
103    for ifindex, triple in ifindexes.items():
104        all_keys = triple[0].keys() | triple[1].keys() | triple[2].keys()
105
106        for key in all_keys:
107            ksft_ge(triple[1][key], triple[0][key], comment="bad key: " + key)
108            ksft_ge(triple[2][key], triple[1][key], comment="bad key: " + key)
109
110    # Test invalid dumps
111    # 0 is invalid
112    with ksft_raises(NlError) as cm:
113        netfam.qstats_get({"ifindex": 0}, dump=True)
114    ksft_eq(cm.exception.nl_msg.error, -34)
115    ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex')
116
117    # loopback has no stats
118    with ksft_raises(NlError) as cm:
119        netfam.qstats_get({"ifindex": 1}, dump=True)
120    ksft_eq(cm.exception.nl_msg.error, -95)
121    ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex')
122
123    # Try to get stats for lowest unused ifindex but not 0
124    devs = rtnl.getlink({}, dump=True)
125    all_ifindexes = set([dev["ifi-index"] for dev in devs])
126    lowest = 2
127    while lowest in all_ifindexes:
128        lowest += 1
129
130    with ksft_raises(NlError) as cm:
131        netfam.qstats_get({"ifindex": lowest}, dump=True)
132    ksft_eq(cm.exception.nl_msg.error, -19)
133    ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.ifindex')
134
135
136def main() -> None:
137    with NetDrvEnv(__file__) as cfg:
138        ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex],
139                 args=(cfg, ))
140    ksft_exit()
141
142
143if __name__ == "__main__":
144    main()
145