Back to home page

OSCL-LXR

 
 

    


0001 #!/usr/bin/env python
0002 # SPDX-License-Identifier: GPL-2.0
0003 
0004 import subprocess
0005 import json as j
0006 import random
0007 
0008 
0009 class SkipTest(Exception):
0010     pass
0011 
0012 
0013 class RandomValuePicker:
0014     """
0015     Class for storing shared buffer configuration. Can handle 3 different
0016     objects, pool, tcbind and portpool. Provide an interface to get random
0017     values for a specific object type as the follow:
0018       1. Pool:
0019          - random size
0020 
0021       2. TcBind:
0022          - random pool number
0023          - random threshold
0024 
0025       3. PortPool:
0026          - random threshold
0027     """
0028     def __init__(self, pools):
0029         self._pools = []
0030         for pool in pools:
0031             self._pools.append(pool)
0032 
0033     def _cell_size(self):
0034         return self._pools[0]["cell_size"]
0035 
0036     def _get_static_size(self, th):
0037         # For threshold of 16, this works out to be about 12MB on Spectrum-1,
0038         # and about 17MB on Spectrum-2.
0039         return th * 8000 * self._cell_size()
0040 
0041     def _get_size(self):
0042         return self._get_static_size(16)
0043 
0044     def _get_thtype(self):
0045         return "static"
0046 
0047     def _get_th(self, pool):
0048         # Threshold value could be any integer between 3 to 16
0049         th = random.randint(3, 16)
0050         if pool["thtype"] == "dynamic":
0051             return th
0052         else:
0053             return self._get_static_size(th)
0054 
0055     def _get_pool(self, direction):
0056         ing_pools = []
0057         egr_pools = []
0058         for pool in self._pools:
0059             if pool["type"] == "ingress":
0060                 ing_pools.append(pool)
0061             else:
0062                 egr_pools.append(pool)
0063         if direction == "ingress":
0064             arr = ing_pools
0065         else:
0066             arr = egr_pools
0067         return arr[random.randint(0, len(arr) - 1)]
0068 
0069     def get_value(self, objid):
0070         if isinstance(objid, Pool):
0071             if objid["pool"] in [4, 8, 9, 10]:
0072                 # The threshold type of pools 4, 8, 9 and 10 cannot be changed
0073                 raise SkipTest()
0074             else:
0075                 return (self._get_size(), self._get_thtype())
0076         if isinstance(objid, TcBind):
0077             if objid["tc"] >= 8:
0078                 # Multicast TCs cannot be changed
0079                 raise SkipTest()
0080             else:
0081                 pool = self._get_pool(objid["type"])
0082                 th = self._get_th(pool)
0083                 pool_n = pool["pool"]
0084                 return (pool_n, th)
0085         if isinstance(objid, PortPool):
0086             pool_n = objid["pool"]
0087             pool = self._pools[pool_n]
0088             assert pool["pool"] == pool_n
0089             th = self._get_th(pool)
0090             return (th,)
0091 
0092 
0093 class RecordValuePickerException(Exception):
0094     pass
0095 
0096 
0097 class RecordValuePicker:
0098     """
0099     Class for storing shared buffer configuration. Can handle 2 different
0100     objects, pool and tcbind. Provide an interface to get the stored values per
0101     object type.
0102     """
0103     def __init__(self, objlist):
0104         self._recs = []
0105         for item in objlist:
0106             self._recs.append({"objid": item, "value": item.var_tuple()})
0107 
0108     def get_value(self, objid):
0109         if isinstance(objid, Pool) and objid["pool"] in [4, 8, 9, 10]:
0110             # The threshold type of pools 4, 8, 9 and 10 cannot be changed
0111             raise SkipTest()
0112         if isinstance(objid, TcBind) and objid["tc"] >= 8:
0113             # Multicast TCs cannot be changed
0114             raise SkipTest()
0115         for rec in self._recs:
0116             if rec["objid"].weak_eq(objid):
0117                 return rec["value"]
0118         raise RecordValuePickerException()
0119 
0120 
0121 def run_cmd(cmd, json=False):
0122     out = subprocess.check_output(cmd, shell=True)
0123     if json:
0124         return j.loads(out)
0125     return out
0126 
0127 
0128 def run_json_cmd(cmd):
0129     return run_cmd(cmd, json=True)
0130 
0131 
0132 def log_test(test_name, err_msg=None):
0133     if err_msg:
0134         print("\t%s" % err_msg)
0135         print("TEST: %-80s  [FAIL]" % test_name)
0136     else:
0137         print("TEST: %-80s  [ OK ]" % test_name)
0138 
0139 
0140 class CommonItem(dict):
0141     varitems = []
0142 
0143     def var_tuple(self):
0144         ret = []
0145         self.varitems.sort()
0146         for key in self.varitems:
0147             ret.append(self[key])
0148         return tuple(ret)
0149 
0150     def weak_eq(self, other):
0151         for key in self:
0152             if key in self.varitems:
0153                 continue
0154             if self[key] != other[key]:
0155                 return False
0156         return True
0157 
0158 
0159 class CommonList(list):
0160     def get_by(self, by_obj):
0161         for item in self:
0162             if item.weak_eq(by_obj):
0163                 return item
0164         return None
0165 
0166     def del_by(self, by_obj):
0167         for item in self:
0168             if item.weak_eq(by_obj):
0169                 self.remove(item)
0170 
0171 
0172 class Pool(CommonItem):
0173     varitems = ["size", "thtype"]
0174 
0175     def dl_set(self, dlname, size, thtype):
0176         run_cmd("devlink sb pool set {} sb {} pool {} size {} thtype {}".format(dlname, self["sb"],
0177                                                                                 self["pool"],
0178                                                                                 size, thtype))
0179 
0180 
0181 class PoolList(CommonList):
0182     pass
0183 
0184 
0185 def get_pools(dlname, direction=None):
0186     d = run_json_cmd("devlink sb pool show -j")
0187     pools = PoolList()
0188     for pooldict in d["pool"][dlname]:
0189         if not direction or direction == pooldict["type"]:
0190             pools.append(Pool(pooldict))
0191     return pools
0192 
0193 
0194 def do_check_pools(dlname, pools, vp):
0195     for pool in pools:
0196         pre_pools = get_pools(dlname)
0197         try:
0198             (size, thtype) = vp.get_value(pool)
0199         except SkipTest:
0200             continue
0201         pool.dl_set(dlname, size, thtype)
0202         post_pools = get_pools(dlname)
0203         pool = post_pools.get_by(pool)
0204 
0205         err_msg = None
0206         if pool["size"] != size:
0207             err_msg = "Incorrect pool size (got {}, expected {})".format(pool["size"], size)
0208         if pool["thtype"] != thtype:
0209             err_msg = "Incorrect pool threshold type (got {}, expected {})".format(pool["thtype"], thtype)
0210 
0211         pre_pools.del_by(pool)
0212         post_pools.del_by(pool)
0213         if pre_pools != post_pools:
0214             err_msg = "Other pool setup changed as well"
0215         log_test("pool {} of sb {} set verification".format(pool["pool"],
0216                                                             pool["sb"]), err_msg)
0217 
0218 
0219 def check_pools(dlname, pools):
0220     # Save defaults
0221     record_vp = RecordValuePicker(pools)
0222 
0223     # For each pool, set random size and static threshold type
0224     do_check_pools(dlname, pools, RandomValuePicker(pools))
0225 
0226     # Restore defaults
0227     do_check_pools(dlname, pools, record_vp)
0228 
0229 
0230 class TcBind(CommonItem):
0231     varitems = ["pool", "threshold"]
0232 
0233     def __init__(self, port, d):
0234         super(TcBind, self).__init__(d)
0235         self["dlportname"] = port.name
0236 
0237     def dl_set(self, pool, th):
0238         run_cmd("devlink sb tc bind set {} sb {} tc {} type {} pool {} th {}".format(self["dlportname"],
0239                                                                                      self["sb"],
0240                                                                                      self["tc"],
0241                                                                                      self["type"],
0242                                                                                      pool, th))
0243 
0244 
0245 class TcBindList(CommonList):
0246     pass
0247 
0248 
0249 def get_tcbinds(ports, verify_existence=False):
0250     d = run_json_cmd("devlink sb tc bind show -j -n")
0251     tcbinds = TcBindList()
0252     for port in ports:
0253         err_msg = None
0254         if port.name not in d["tc_bind"] or len(d["tc_bind"][port.name]) == 0:
0255             err_msg = "No tc bind for port"
0256         else:
0257             for tcbinddict in d["tc_bind"][port.name]:
0258                 tcbinds.append(TcBind(port, tcbinddict))
0259         if verify_existence:
0260             log_test("tc bind existence for port {} verification".format(port.name), err_msg)
0261     return tcbinds
0262 
0263 
0264 def do_check_tcbind(ports, tcbinds, vp):
0265     for tcbind in tcbinds:
0266         pre_tcbinds = get_tcbinds(ports)
0267         try:
0268             (pool, th) = vp.get_value(tcbind)
0269         except SkipTest:
0270             continue
0271         tcbind.dl_set(pool, th)
0272         post_tcbinds = get_tcbinds(ports)
0273         tcbind = post_tcbinds.get_by(tcbind)
0274 
0275         err_msg = None
0276         if tcbind["pool"] != pool:
0277             err_msg = "Incorrect pool (got {}, expected {})".format(tcbind["pool"], pool)
0278         if tcbind["threshold"] != th:
0279             err_msg = "Incorrect threshold (got {}, expected {})".format(tcbind["threshold"], th)
0280 
0281         pre_tcbinds.del_by(tcbind)
0282         post_tcbinds.del_by(tcbind)
0283         if pre_tcbinds != post_tcbinds:
0284             err_msg = "Other tc bind setup changed as well"
0285         log_test("tc bind {}-{} of sb {} set verification".format(tcbind["dlportname"],
0286                                                                   tcbind["tc"],
0287                                                                   tcbind["sb"]), err_msg)
0288 
0289 
0290 def check_tcbind(dlname, ports, pools):
0291     tcbinds = get_tcbinds(ports, verify_existence=True)
0292 
0293     # Save defaults
0294     record_vp = RecordValuePicker(tcbinds)
0295 
0296     # Bind each port and unicast TC (TCs < 8) to a random pool and a random
0297     # threshold
0298     do_check_tcbind(ports, tcbinds, RandomValuePicker(pools))
0299 
0300     # Restore defaults
0301     do_check_tcbind(ports, tcbinds, record_vp)
0302 
0303 
0304 class PortPool(CommonItem):
0305     varitems = ["threshold"]
0306 
0307     def __init__(self, port, d):
0308         super(PortPool, self).__init__(d)
0309         self["dlportname"] = port.name
0310 
0311     def dl_set(self, th):
0312         run_cmd("devlink sb port pool set {} sb {} pool {} th {}".format(self["dlportname"],
0313                                                                          self["sb"],
0314                                                                          self["pool"], th))
0315 
0316 
0317 class PortPoolList(CommonList):
0318     pass
0319 
0320 
0321 def get_portpools(ports, verify_existence=False):
0322     d = run_json_cmd("devlink sb port pool -j -n")
0323     portpools = PortPoolList()
0324     for port in ports:
0325         err_msg = None
0326         if port.name not in d["port_pool"] or len(d["port_pool"][port.name]) == 0:
0327             err_msg = "No port pool for port"
0328         else:
0329             for portpooldict in d["port_pool"][port.name]:
0330                 portpools.append(PortPool(port, portpooldict))
0331         if verify_existence:
0332             log_test("port pool existence for port {} verification".format(port.name), err_msg)
0333     return portpools
0334 
0335 
0336 def do_check_portpool(ports, portpools, vp):
0337     for portpool in portpools:
0338         pre_portpools = get_portpools(ports)
0339         (th,) = vp.get_value(portpool)
0340         portpool.dl_set(th)
0341         post_portpools = get_portpools(ports)
0342         portpool = post_portpools.get_by(portpool)
0343 
0344         err_msg = None
0345         if portpool["threshold"] != th:
0346             err_msg = "Incorrect threshold (got {}, expected {})".format(portpool["threshold"], th)
0347 
0348         pre_portpools.del_by(portpool)
0349         post_portpools.del_by(portpool)
0350         if pre_portpools != post_portpools:
0351             err_msg = "Other port pool setup changed as well"
0352         log_test("port pool {}-{} of sb {} set verification".format(portpool["dlportname"],
0353                                                                     portpool["pool"],
0354                                                                     portpool["sb"]), err_msg)
0355 
0356 
0357 def check_portpool(dlname, ports, pools):
0358     portpools = get_portpools(ports, verify_existence=True)
0359 
0360     # Save defaults
0361     record_vp = RecordValuePicker(portpools)
0362 
0363     # For each port pool, set a random threshold
0364     do_check_portpool(ports, portpools, RandomValuePicker(pools))
0365 
0366     # Restore defaults
0367     do_check_portpool(ports, portpools, record_vp)
0368 
0369 
0370 class Port:
0371     def __init__(self, name):
0372         self.name = name
0373 
0374 
0375 class PortList(list):
0376     pass
0377 
0378 
0379 def get_ports(dlname):
0380     d = run_json_cmd("devlink port show -j")
0381     ports = PortList()
0382     for name in d["port"]:
0383         if name.find(dlname) == 0 and d["port"][name]["flavour"] == "physical":
0384             ports.append(Port(name))
0385     return ports
0386 
0387 
0388 def get_device():
0389     devices_info = run_json_cmd("devlink -j dev info")["info"]
0390     for d in devices_info:
0391         if "mlxsw_spectrum" in devices_info[d]["driver"]:
0392             return d
0393     return None
0394 
0395 
0396 class UnavailableDevlinkNameException(Exception):
0397     pass
0398 
0399 
0400 def test_sb_configuration():
0401     # Use static seed
0402     random.seed(0)
0403 
0404     dlname = get_device()
0405     if not dlname:
0406         raise UnavailableDevlinkNameException()
0407 
0408     ports = get_ports(dlname)
0409     pools = get_pools(dlname)
0410 
0411     check_pools(dlname, pools)
0412     check_tcbind(dlname, ports, pools)
0413     check_portpool(dlname, ports, pools)
0414 
0415 
0416 test_sb_configuration()