WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / drivers / net / mlxsw / sharedbuffer_configuration.py
blob2223337eed0c12adbe57ebbfae5bdbca86169c00
1 #!/usr/bin/env python
2 # SPDX-License-Identifier: GPL-2.0
4 import subprocess
5 import json as j
6 import random
9 class SkipTest(Exception):
10 pass
13 class RandomValuePicker:
14 """
15 Class for storing shared buffer configuration. Can handle 3 different
16 objects, pool, tcbind and portpool. Provide an interface to get random
17 values for a specific object type as the follow:
18 1. Pool:
19 - random size
21 2. TcBind:
22 - random pool number
23 - random threshold
25 3. PortPool:
26 - random threshold
27 """
28 def __init__(self, pools):
29 self._pools = []
30 for pool in pools:
31 self._pools.append(pool)
33 def _cell_size(self):
34 return self._pools[0]["cell_size"]
36 def _get_static_size(self, th):
37 # For threshold of 16, this works out to be about 12MB on Spectrum-1,
38 # and about 17MB on Spectrum-2.
39 return th * 8000 * self._cell_size()
41 def _get_size(self):
42 return self._get_static_size(16)
44 def _get_thtype(self):
45 return "static"
47 def _get_th(self, pool):
48 # Threshold value could be any integer between 3 to 16
49 th = random.randint(3, 16)
50 if pool["thtype"] == "dynamic":
51 return th
52 else:
53 return self._get_static_size(th)
55 def _get_pool(self, direction):
56 ing_pools = []
57 egr_pools = []
58 for pool in self._pools:
59 if pool["type"] == "ingress":
60 ing_pools.append(pool)
61 else:
62 egr_pools.append(pool)
63 if direction == "ingress":
64 arr = ing_pools
65 else:
66 arr = egr_pools
67 return arr[random.randint(0, len(arr) - 1)]
69 def get_value(self, objid):
70 if isinstance(objid, Pool):
71 if objid["pool"] in [4, 8, 9, 10]:
72 # The threshold type of pools 4, 8, 9 and 10 cannot be changed
73 raise SkipTest()
74 else:
75 return (self._get_size(), self._get_thtype())
76 if isinstance(objid, TcBind):
77 if objid["tc"] >= 8:
78 # Multicast TCs cannot be changed
79 raise SkipTest()
80 else:
81 pool = self._get_pool(objid["type"])
82 th = self._get_th(pool)
83 pool_n = pool["pool"]
84 return (pool_n, th)
85 if isinstance(objid, PortPool):
86 pool_n = objid["pool"]
87 pool = self._pools[pool_n]
88 assert pool["pool"] == pool_n
89 th = self._get_th(pool)
90 return (th,)
93 class RecordValuePickerException(Exception):
94 pass
97 class RecordValuePicker:
98 """
99 Class for storing shared buffer configuration. Can handle 2 different
100 objects, pool and tcbind. Provide an interface to get the stored values per
101 object type.
103 def __init__(self, objlist):
104 self._recs = []
105 for item in objlist:
106 self._recs.append({"objid": item, "value": item.var_tuple()})
108 def get_value(self, objid):
109 if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
110 # The threshold type of pools 4, 8, 9 and 10 cannot be changed
111 raise SkipTest()
112 if isinstance(objid, TcBind) and objid["tc"] >= 8:
113 # Multicast TCs cannot be changed
114 raise SkipTest()
115 for rec in self._recs:
116 if rec["objid"].weak_eq(objid):
117 return rec["value"]
118 raise RecordValuePickerException()
121 def run_cmd(cmd, json=False):
122 out = subprocess.check_output(cmd, shell=True)
123 if json:
124 return j.loads(out)
125 return out
128 def run_json_cmd(cmd):
129 return run_cmd(cmd, json=True)
132 def log_test(test_name, err_msg=None):
133 if err_msg:
134 print("\t%s" % err_msg)
135 print("TEST: %-80s [FAIL]" % test_name)
136 else:
137 print("TEST: %-80s [ OK ]" % test_name)
140 class CommonItem(dict):
141 varitems = []
143 def var_tuple(self):
144 ret = []
145 self.varitems.sort()
146 for key in self.varitems:
147 ret.append(self[key])
148 return tuple(ret)
150 def weak_eq(self, other):
151 for key in self:
152 if key in self.varitems:
153 continue
154 if self[key] != other[key]:
155 return False
156 return True
159 class CommonList(list):
160 def get_by(self, by_obj):
161 for item in self:
162 if item.weak_eq(by_obj):
163 return item
164 return None
166 def del_by(self, by_obj):
167 for item in self:
168 if item.weak_eq(by_obj):
169 self.remove(item)
172 class Pool(CommonItem):
173 varitems = ["size", "thtype"]
175 def dl_set(self, dlname, size, thtype):
176 run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
177 self["pool"],
178 size, thtype))
181 class PoolList(CommonList):
182 pass
185 def get_pools(dlname, direction=None):
186 d = run_json_cmd("devlink sb pool show -j")
187 pools = PoolList()
188 for pooldict in d["pool"][dlname]:
189 if not direction or direction == pooldict["type"]:
190 pools.append(Pool(pooldict))
191 return pools
194 def do_check_pools(dlname, pools, vp):
195 for pool in pools:
196 pre_pools = get_pools(dlname)
197 try:
198 (size, thtype) = vp.get_value(pool)
199 except SkipTest:
200 continue
201 pool.dl_set(dlname, size, thtype)
202 post_pools = get_pools(dlname)
203 pool = post_pools.get_by(pool)
205 err_msg = None
206 if pool["size"] != size:
207 err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
208 if pool["thtype"] != thtype:
209 err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
211 pre_pools.del_by(pool)
212 post_pools.del_by(pool)
213 if pre_pools != post_pools:
214 err_msg = "Other pool setup changed as well"
215 log_test("pool {} of sb {} set verification".format(pool["pool"],
216 pool["sb"]), err_msg)
219 def check_pools(dlname, pools):
220 # Save defaults
221 record_vp = RecordValuePicker(pools)
223 # For each pool, set random size and static threshold type
224 do_check_pools(dlname, pools, RandomValuePicker(pools))
226 # Restore defaults
227 do_check_pools(dlname, pools, record_vp)
230 class TcBind(CommonItem):
231 varitems = ["pool", "threshold"]
233 def __init__(self, port, d):
234 super(TcBind, self).__init__(d)
235 self["dlportname"] = port.name
237 def dl_set(self, pool, th):
238 run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
239 self["sb"],
240 self["tc"],
241 self["type"],
242 pool, th))
245 class TcBindList(CommonList):
246 pass
249 def get_tcbinds(ports, verify_existence=False):
250 d = run_json_cmd("devlink sb tc bind show -j -n")
251 tcbinds = TcBindList()
252 for port in ports:
253 err_msg = None
254 if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
255 err_msg = "No tc bind for port"
256 else:
257 for tcbinddict in d["tc_bind"][port.name]:
258 tcbinds.append(TcBind(port, tcbinddict))
259 if verify_existence:
260 log_test("tc bind existence for port {} verification".format(port.name), err_msg)
261 return tcbinds
264 def do_check_tcbind(ports, tcbinds, vp):
265 for tcbind in tcbinds:
266 pre_tcbinds = get_tcbinds(ports)
267 try:
268 (pool, th) = vp.get_value(tcbind)
269 except SkipTest:
270 continue
271 tcbind.dl_set(pool, th)
272 post_tcbinds = get_tcbinds(ports)
273 tcbind = post_tcbinds.get_by(tcbind)
275 err_msg = None
276 if tcbind["pool"] != pool:
277 err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
278 if tcbind["threshold"] != th:
279 err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
281 pre_tcbinds.del_by(tcbind)
282 post_tcbinds.del_by(tcbind)
283 if pre_tcbinds != post_tcbinds:
284 err_msg = "Other tc bind setup changed as well"
285 log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
286 tcbind["tc"],
287 tcbind["sb"]), err_msg)
290 def check_tcbind(dlname, ports, pools):
291 tcbinds = get_tcbinds(ports, verify_existence=True)
293 # Save defaults
294 record_vp = RecordValuePicker(tcbinds)
296 # Bind each port and unicast TC (TCs < 8) to a random pool and a random
297 # threshold
298 do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
300 # Restore defaults
301 do_check_tcbind(ports, tcbinds, record_vp)
304 class PortPool(CommonItem):
305 varitems = ["threshold"]
307 def __init__(self, port, d):
308 super(PortPool, self).__init__(d)
309 self["dlportname"] = port.name
311 def dl_set(self, th):
312 run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
313 self["sb"],
314 self["pool"], th))
317 class PortPoolList(CommonList):
318 pass
321 def get_portpools(ports, verify_existence=False):
322 d = run_json_cmd("devlink sb port pool -j -n")
323 portpools = PortPoolList()
324 for port in ports:
325 err_msg = None
326 if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
327 err_msg = "No port pool for port"
328 else:
329 for portpooldict in d["port_pool"][port.name]:
330 portpools.append(PortPool(port, portpooldict))
331 if verify_existence:
332 log_test("port pool existence for port {} verification".format(port.name), err_msg)
333 return portpools
336 def do_check_portpool(ports, portpools, vp):
337 for portpool in portpools:
338 pre_portpools = get_portpools(ports)
339 (th,) = vp.get_value(portpool)
340 portpool.dl_set(th)
341 post_portpools = get_portpools(ports)
342 portpool = post_portpools.get_by(portpool)
344 err_msg = None
345 if portpool["threshold"] != th:
346 err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
348 pre_portpools.del_by(portpool)
349 post_portpools.del_by(portpool)
350 if pre_portpools != post_portpools:
351 err_msg = "Other port pool setup changed as well"
352 log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
353 portpool["pool"],
354 portpool["sb"]), err_msg)
357 def check_portpool(dlname, ports, pools):
358 portpools = get_portpools(ports, verify_existence=True)
360 # Save defaults
361 record_vp = RecordValuePicker(portpools)
363 # For each port pool, set a random threshold
364 do_check_portpool(ports, portpools, RandomValuePicker(pools))
366 # Restore defaults
367 do_check_portpool(ports, portpools, record_vp)
370 class Port:
371 def __init__(self, name):
372 self.name = name
375 class PortList(list):
376 pass
379 def get_ports(dlname):
380 d = run_json_cmd("devlink port show -j")
381 ports = PortList()
382 for name in d["port"]:
383 if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
384 ports.append(Port(name))
385 return ports
388 def get_device():
389 devices_info = run_json_cmd("devlink -j dev info")["info"]
390 for d in devices_info:
391 if "mlxsw_spectrum" in devices_info[d]["driver"]:
392 return d
393 return None
396 class UnavailableDevlinkNameException(Exception):
397 pass
400 def test_sb_configuration():
401 # Use static seed
402 random.seed(0)
404 dlname = get_device()
405 if not dlname:
406 raise UnavailableDevlinkNameException()
408 ports = get_ports(dlname)
409 pools = get_pools(dlname)
411 check_pools(dlname, pools)
412 check_tcbind(dlname, ports, pools)
413 check_portpool(dlname, ports, pools)
416 test_sb_configuration()