stats.py 5.4 KB

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