sharedbuffer_configuration.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #!/usr/bin/env python
  2. # SPDX-License-Identifier: GPL-2.0
  3. import subprocess
  4. import json as j
  5. import random
  6. class SkipTest(Exception):
  7. pass
  8. class RandomValuePicker:
  9. """
  10. Class for storing shared buffer configuration. Can handle 3 different
  11. objects, pool, tcbind and portpool. Provide an interface to get random
  12. values for a specific object type as the follow:
  13. 1. Pool:
  14. - random size
  15. 2. TcBind:
  16. - random pool number
  17. - random threshold
  18. 3. PortPool:
  19. - random threshold
  20. """
  21. def __init__(self, pools):
  22. self._pools = []
  23. for pool in pools:
  24. self._pools.append(pool)
  25. def _cell_size(self):
  26. return self._pools[0]["cell_size"]
  27. def _get_static_size(self, th):
  28. # For threshold of 16, this works out to be about 12MB on Spectrum-1,
  29. # and about 17MB on Spectrum-2.
  30. return th * 8000 * self._cell_size()
  31. def _get_size(self):
  32. return self._get_static_size(16)
  33. def _get_thtype(self):
  34. return "static"
  35. def _get_th(self, pool):
  36. # Threshold value could be any integer between 3 to 16
  37. th = random.randint(3, 16)
  38. if pool["thtype"] == "dynamic":
  39. return th
  40. else:
  41. return self._get_static_size(th)
  42. def _get_pool(self, direction):
  43. ing_pools = []
  44. egr_pools = []
  45. for pool in self._pools:
  46. if pool["type"] == "ingress":
  47. ing_pools.append(pool)
  48. else:
  49. egr_pools.append(pool)
  50. if direction == "ingress":
  51. arr = ing_pools
  52. else:
  53. arr = egr_pools
  54. return arr[random.randint(0, len(arr) - 1)]
  55. def get_value(self, objid):
  56. if isinstance(objid, Pool):
  57. if objid["pool"] in [4, 8, 9, 10]:
  58. # The threshold type of pools 4, 8, 9 and 10 cannot be changed
  59. raise SkipTest()
  60. else:
  61. return (self._get_size(), self._get_thtype())
  62. if isinstance(objid, TcBind):
  63. if objid["tc"] >= 8:
  64. # Multicast TCs cannot be changed
  65. raise SkipTest()
  66. else:
  67. pool = self._get_pool(objid["type"])
  68. th = self._get_th(pool)
  69. pool_n = pool["pool"]
  70. return (pool_n, th)
  71. if isinstance(objid, PortPool):
  72. pool_n = objid["pool"]
  73. pool = self._pools[pool_n]
  74. assert pool["pool"] == pool_n
  75. th = self._get_th(pool)
  76. return (th,)
  77. class RecordValuePickerException(Exception):
  78. pass
  79. class RecordValuePicker:
  80. """
  81. Class for storing shared buffer configuration. Can handle 2 different
  82. objects, pool and tcbind. Provide an interface to get the stored values per
  83. object type.
  84. """
  85. def __init__(self, objlist):
  86. self._recs = []
  87. for item in objlist:
  88. self._recs.append({"objid": item, "value": item.var_tuple()})
  89. def get_value(self, objid):
  90. if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
  91. # The threshold type of pools 4, 8, 9 and 10 cannot be changed
  92. raise SkipTest()
  93. if isinstance(objid, TcBind) and objid["tc"] >= 8:
  94. # Multicast TCs cannot be changed
  95. raise SkipTest()
  96. for rec in self._recs:
  97. if rec["objid"].weak_eq(objid):
  98. return rec["value"]
  99. raise RecordValuePickerException()
  100. def run_cmd(cmd, json=False):
  101. out = subprocess.check_output(cmd, shell=True)
  102. if json:
  103. return j.loads(out)
  104. return out
  105. def run_json_cmd(cmd):
  106. return run_cmd(cmd, json=True)
  107. def log_test(test_name, err_msg=None):
  108. if err_msg:
  109. print("\t%s" % err_msg)
  110. print("TEST: %-80s [FAIL]" % test_name)
  111. else:
  112. print("TEST: %-80s [ OK ]" % test_name)
  113. class CommonItem(dict):
  114. varitems = []
  115. def var_tuple(self):
  116. ret = []
  117. self.varitems.sort()
  118. for key in self.varitems:
  119. ret.append(self[key])
  120. return tuple(ret)
  121. def weak_eq(self, other):
  122. for key in self:
  123. if key in self.varitems:
  124. continue
  125. if self[key] != other[key]:
  126. return False
  127. return True
  128. class CommonList(list):
  129. def get_by(self, by_obj):
  130. for item in self:
  131. if item.weak_eq(by_obj):
  132. return item
  133. return None
  134. def del_by(self, by_obj):
  135. for item in self:
  136. if item.weak_eq(by_obj):
  137. self.remove(item)
  138. class Pool(CommonItem):
  139. varitems = ["size", "thtype"]
  140. def dl_set(self, dlname, size, thtype):
  141. run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
  142. self["pool"],
  143. size, thtype))
  144. class PoolList(CommonList):
  145. pass
  146. def get_pools(dlname, direction=None):
  147. d = run_json_cmd("devlink sb pool show -j")
  148. pools = PoolList()
  149. for pooldict in d["pool"][dlname]:
  150. if not direction or direction == pooldict["type"]:
  151. pools.append(Pool(pooldict))
  152. return pools
  153. def do_check_pools(dlname, pools, vp):
  154. for pool in pools:
  155. pre_pools = get_pools(dlname)
  156. try:
  157. (size, thtype) = vp.get_value(pool)
  158. except SkipTest:
  159. continue
  160. pool.dl_set(dlname, size, thtype)
  161. post_pools = get_pools(dlname)
  162. pool = post_pools.get_by(pool)
  163. err_msg = None
  164. if pool["size"] != size:
  165. err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
  166. if pool["thtype"] != thtype:
  167. err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
  168. pre_pools.del_by(pool)
  169. post_pools.del_by(pool)
  170. if pre_pools != post_pools:
  171. err_msg = "Other pool setup changed as well"
  172. log_test("pool {} of sb {} set verification".format(pool["pool"],
  173. pool["sb"]), err_msg)
  174. def check_pools(dlname, pools):
  175. # Save defaults
  176. record_vp = RecordValuePicker(pools)
  177. # For each pool, set random size and static threshold type
  178. do_check_pools(dlname, pools, RandomValuePicker(pools))
  179. # Restore defaults
  180. do_check_pools(dlname, pools, record_vp)
  181. class TcBind(CommonItem):
  182. varitems = ["pool", "threshold"]
  183. def __init__(self, port, d):
  184. super(TcBind, self).__init__(d)
  185. self["dlportname"] = port.name
  186. def dl_set(self, pool, th):
  187. run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
  188. self["sb"],
  189. self["tc"],
  190. self["type"],
  191. pool, th))
  192. class TcBindList(CommonList):
  193. pass
  194. def get_tcbinds(ports, verify_existence=False):
  195. d = run_json_cmd("devlink sb tc bind show -j -n")
  196. tcbinds = TcBindList()
  197. for port in ports:
  198. err_msg = None
  199. if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
  200. err_msg = "No tc bind for port"
  201. else:
  202. for tcbinddict in d["tc_bind"][port.name]:
  203. tcbinds.append(TcBind(port, tcbinddict))
  204. if verify_existence:
  205. log_test("tc bind existence for port {} verification".format(port.name), err_msg)
  206. return tcbinds
  207. def do_check_tcbind(ports, tcbinds, vp):
  208. for tcbind in tcbinds:
  209. pre_tcbinds = get_tcbinds(ports)
  210. try:
  211. (pool, th) = vp.get_value(tcbind)
  212. except SkipTest:
  213. continue
  214. tcbind.dl_set(pool, th)
  215. post_tcbinds = get_tcbinds(ports)
  216. tcbind = post_tcbinds.get_by(tcbind)
  217. err_msg = None
  218. if tcbind["pool"] != pool:
  219. err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
  220. if tcbind["threshold"] != th:
  221. err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
  222. pre_tcbinds.del_by(tcbind)
  223. post_tcbinds.del_by(tcbind)
  224. if pre_tcbinds != post_tcbinds:
  225. err_msg = "Other tc bind setup changed as well"
  226. log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
  227. tcbind["tc"],
  228. tcbind["sb"]), err_msg)
  229. def check_tcbind(dlname, ports, pools):
  230. tcbinds = get_tcbinds(ports, verify_existence=True)
  231. # Save defaults
  232. record_vp = RecordValuePicker(tcbinds)
  233. # Bind each port and unicast TC (TCs < 8) to a random pool and a random
  234. # threshold
  235. do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
  236. # Restore defaults
  237. do_check_tcbind(ports, tcbinds, record_vp)
  238. class PortPool(CommonItem):
  239. varitems = ["threshold"]
  240. def __init__(self, port, d):
  241. super(PortPool, self).__init__(d)
  242. self["dlportname"] = port.name
  243. def dl_set(self, th):
  244. run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
  245. self["sb"],
  246. self["pool"], th))
  247. class PortPoolList(CommonList):
  248. pass
  249. def get_portpools(ports, verify_existence=False):
  250. d = run_json_cmd("devlink sb port pool -j -n")
  251. portpools = PortPoolList()
  252. for port in ports:
  253. err_msg = None
  254. if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
  255. err_msg = "No port pool for port"
  256. else:
  257. for portpooldict in d["port_pool"][port.name]:
  258. portpools.append(PortPool(port, portpooldict))
  259. if verify_existence:
  260. log_test("port pool existence for port {} verification".format(port.name), err_msg)
  261. return portpools
  262. def do_check_portpool(ports, portpools, vp):
  263. for portpool in portpools:
  264. pre_portpools = get_portpools(ports)
  265. (th,) = vp.get_value(portpool)
  266. portpool.dl_set(th)
  267. post_portpools = get_portpools(ports)
  268. portpool = post_portpools.get_by(portpool)
  269. err_msg = None
  270. if portpool["threshold"] != th:
  271. err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
  272. pre_portpools.del_by(portpool)
  273. post_portpools.del_by(portpool)
  274. if pre_portpools != post_portpools:
  275. err_msg = "Other port pool setup changed as well"
  276. log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
  277. portpool["pool"],
  278. portpool["sb"]), err_msg)
  279. def check_portpool(dlname, ports, pools):
  280. portpools = get_portpools(ports, verify_existence=True)
  281. # Save defaults
  282. record_vp = RecordValuePicker(portpools)
  283. # For each port pool, set a random threshold
  284. do_check_portpool(ports, portpools, RandomValuePicker(pools))
  285. # Restore defaults
  286. do_check_portpool(ports, portpools, record_vp)
  287. class Port:
  288. def __init__(self, name):
  289. self.name = name
  290. class PortList(list):
  291. pass
  292. def get_ports(dlname):
  293. d = run_json_cmd("devlink port show -j")
  294. ports = PortList()
  295. for name in d["port"]:
  296. if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
  297. ports.append(Port(name))
  298. return ports
  299. def get_device():
  300. devices_info = run_json_cmd("devlink -j dev info")["info"]
  301. for d in devices_info:
  302. if "mlxsw_spectrum" in devices_info[d]["driver"]:
  303. return d
  304. return None
  305. class UnavailableDevlinkNameException(Exception):
  306. pass
  307. def test_sb_configuration():
  308. # Use static seed
  309. random.seed(0)
  310. dlname = get_device()
  311. if not dlname:
  312. raise UnavailableDevlinkNameException()
  313. ports = get_ports(dlname)
  314. pools = get_pools(dlname)
  315. check_pools(dlname, pools)
  316. check_tcbind(dlname, ports, pools)
  317. check_portpool(dlname, ports, pools)
  318. test_sb_configuration()