0001
0002
0003
0004 from subprocess import PIPE, Popen
0005 import json
0006 import time
0007 import argparse
0008 import collections
0009 import sys
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 KSFT_SKIP=4
0023 Port = collections.namedtuple('Port', 'bus_info name')
0024
0025
0026 def run_command(cmd, should_fail=False):
0027 """
0028 Run a command in subprocess.
0029 Return: Tuple of (stdout, stderr).
0030 """
0031
0032 p = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True)
0033 stdout, stderr = p.communicate()
0034 stdout, stderr = stdout.decode(), stderr.decode()
0035
0036 if stderr != "" and not should_fail:
0037 print("Error sending command: %s" % cmd)
0038 print(stdout)
0039 print(stderr)
0040 return stdout, stderr
0041
0042
0043 class devlink_ports(object):
0044 """
0045 Class that holds information on the devlink ports, required to the tests;
0046 if_names: A list of interfaces in the devlink ports.
0047 """
0048
0049 def get_if_names(dev):
0050 """
0051 Get a list of physical devlink ports.
0052 Return: Array of tuples (bus_info/port, if_name).
0053 """
0054
0055 arr = []
0056
0057 cmd = "devlink -j port show"
0058 stdout, stderr = run_command(cmd)
0059 assert stderr == ""
0060 ports = json.loads(stdout)['port']
0061
0062 for port in ports:
0063 if dev in port:
0064 if ports[port]['flavour'] == 'physical':
0065 arr.append(Port(bus_info=port, name=ports[port]['netdev']))
0066
0067 return arr
0068
0069 def __init__(self, dev):
0070 self.if_names = devlink_ports.get_if_names(dev)
0071
0072
0073 def get_max_lanes(port):
0074 """
0075 Get the $port's maximum number of lanes.
0076 Return: number of lanes, e.g. 1, 2, 4 and 8.
0077 """
0078
0079 cmd = "devlink -j port show %s" % port
0080 stdout, stderr = run_command(cmd)
0081 assert stderr == ""
0082 values = list(json.loads(stdout)['port'].values())[0]
0083
0084 if 'lanes' in values:
0085 lanes = values['lanes']
0086 else:
0087 lanes = 0
0088 return lanes
0089
0090
0091 def get_split_ability(port):
0092 """
0093 Get the $port split ability.
0094 Return: split ability, true or false.
0095 """
0096
0097 cmd = "devlink -j port show %s" % port.name
0098 stdout, stderr = run_command(cmd)
0099 assert stderr == ""
0100 values = list(json.loads(stdout)['port'].values())[0]
0101
0102 return values['splittable']
0103
0104
0105 def split(k, port, should_fail=False):
0106 """
0107 Split $port into $k ports.
0108 If should_fail == True, the split should fail. Otherwise, should pass.
0109 Return: Array of sub ports after splitting.
0110 If the $port wasn't split, the array will be empty.
0111 """
0112
0113 cmd = "devlink port split %s count %s" % (port.bus_info, k)
0114 stdout, stderr = run_command(cmd, should_fail=should_fail)
0115
0116 if should_fail:
0117 if not test(stderr != "", "%s is unsplittable" % port.name):
0118 print("split an unsplittable port %s" % port.name)
0119 return create_split_group(port, k)
0120 else:
0121 if stderr == "":
0122 return create_split_group(port, k)
0123 print("didn't split a splittable port %s" % port.name)
0124
0125 return []
0126
0127
0128 def unsplit(port):
0129 """
0130 Unsplit $port.
0131 """
0132
0133 cmd = "devlink port unsplit %s" % port
0134 stdout, stderr = run_command(cmd)
0135 test(stderr == "", "Unsplit port %s" % port)
0136
0137
0138 def exists(port, dev):
0139 """
0140 Check if $port exists in the devlink ports.
0141 Return: True is so, False otherwise.
0142 """
0143
0144 return any(dev_port.name == port
0145 for dev_port in devlink_ports.get_if_names(dev))
0146
0147
0148 def exists_and_lanes(ports, lanes, dev):
0149 """
0150 Check if every port in the list $ports exists in the devlink ports and has
0151 $lanes number of lanes after splitting.
0152 Return: True if both are True, False otherwise.
0153 """
0154
0155 for port in ports:
0156 max_lanes = get_max_lanes(port)
0157 if not exists(port, dev):
0158 print("port %s doesn't exist in devlink ports" % port)
0159 return False
0160 if max_lanes != lanes:
0161 print("port %s has %d lanes, but %s were expected"
0162 % (port, lanes, max_lanes))
0163 return False
0164 return True
0165
0166
0167 def test(cond, msg):
0168 """
0169 Check $cond and print a message accordingly.
0170 Return: True is pass, False otherwise.
0171 """
0172
0173 if cond:
0174 print("TEST: %-60s [ OK ]" % msg)
0175 else:
0176 print("TEST: %-60s [FAIL]" % msg)
0177
0178 return cond
0179
0180
0181 def create_split_group(port, k):
0182 """
0183 Create the split group for $port.
0184 Return: Array with $k elements, which are the split port group.
0185 """
0186
0187 return list(port.name + "s" + str(i) for i in range(k))
0188
0189
0190 def split_unsplittable_port(port, k):
0191 """
0192 Test that splitting of unsplittable port fails.
0193 """
0194
0195
0196 new_split_group = split(k, port, should_fail=True)
0197
0198 if new_split_group != []:
0199 unsplit(port.bus_info)
0200
0201
0202 def split_splittable_port(port, k, lanes, dev):
0203 """
0204 Test that splitting of splittable port passes correctly.
0205 """
0206
0207 new_split_group = split(k, port)
0208
0209
0210
0211
0212 cmd = "udevadm settle"
0213 stdout, stderr = run_command(cmd)
0214 assert stderr == ""
0215
0216 if new_split_group != []:
0217 test(exists_and_lanes(new_split_group, lanes/k, dev),
0218 "split port %s into %s" % (port.name, k))
0219
0220 unsplit(port.bus_info)
0221
0222
0223 def make_parser():
0224 parser = argparse.ArgumentParser(description='A test for port splitting.')
0225 parser.add_argument('--dev',
0226 help='The devlink handle of the device under test. ' +
0227 'The default is the first registered devlink ' +
0228 'handle.')
0229
0230 return parser
0231
0232
0233 def main(cmdline=None):
0234 parser = make_parser()
0235 args = parser.parse_args(cmdline)
0236
0237 dev = args.dev
0238 if not dev:
0239 cmd = "devlink -j dev show"
0240 stdout, stderr = run_command(cmd)
0241 assert stderr == ""
0242
0243 devs = json.loads(stdout)['dev']
0244 if devs:
0245 dev = list(devs.keys())[0]
0246 else:
0247 print("no devlink device was found, test skipped")
0248 sys.exit(KSFT_SKIP)
0249
0250 cmd = "devlink dev show %s" % dev
0251 stdout, stderr = run_command(cmd)
0252 if stderr != "":
0253 print("devlink device %s can not be found" % dev)
0254 sys.exit(1)
0255
0256 ports = devlink_ports(dev)
0257
0258 for port in ports.if_names:
0259 max_lanes = get_max_lanes(port.name)
0260
0261
0262 if max_lanes == 0:
0263 continue
0264
0265
0266 elif max_lanes == 1:
0267 test(not get_split_ability(port),
0268 "%s should not be able to split" % port.name)
0269 split_unsplittable_port(port, max_lanes)
0270
0271
0272 else:
0273 lane = max_lanes
0274 test(get_split_ability(port),
0275 "%s should be able to split" % port.name)
0276 while lane > 1:
0277 split_splittable_port(port, lane, max_lanes, dev)
0278
0279 lane //= 2
0280
0281
0282 if __name__ == "__main__":
0283 main()