1 # Copyright 2016 Intel Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 """Tools for network card manipulation
22 from conf import settings
24 _LOGGER = logging.getLogger('tools.networkcard')
26 _PCI_DIR = '/sys/bus/pci/devices/{}/'
27 _SRIOV_NUMVFS = os.path.join(_PCI_DIR, 'sriov_numvfs')
28 _SRIOV_TOTALVFS = os.path.join(_PCI_DIR, 'sriov_totalvfs')
29 _SRIOV_VF_PREFIX = 'virtfn'
32 _PCI_DRIVER = 'driver'
35 def check_pci(pci_handle):
36 """ Checks if given extended PCI handle has correct length and fixes
39 :param pci_handle: PCI slot identifier. It can contain vsperf specific
40 suffix after '|' with VF indication. e.g. '0000:05:00.0|vf1'
44 pci = pci_handle.split('|')
49 pci[0] = '0000:' + pci[0][-7:]
50 _LOGGER.debug('Adding domain part to PCI slot %s', pci[0])
54 _LOGGER.warning('PCI slot is too long, it will be shortened to %s', pci[0])
57 # pci_handle has a strange length, but let us try to use it
58 _LOGGER.error('Unknown format of PCI slot %s', pci_handle)
61 def is_sriov_supported(pci_handle):
62 """ Checks if sriov is supported by given NIC
64 :param pci_handle: PCI slot identifier with domain part.
66 :returns: True on success, False otherwise
68 return os.path.isfile(_SRIOV_TOTALVFS.format(pci_handle))
70 def is_sriov_nic(pci_handle):
71 """ Checks if given extended PCI ID refers to the VF
73 :param pci_handle: PCI slot identifier with domain part. It can contain
74 vsperf specific suffix after '|' with VF indication.
75 e.g. '0000:05:00.0|vf1'
77 :returns: True on success, False otherwise
79 for item in pci_handle.split('|'):
80 if item.lower().startswith('vf'):
84 def set_sriov_numvfs(pci_handle, numvfs):
85 """ Checks if sriov is supported and configures given number of VFs
87 :param pci_handle: PCI slot identifier with domain part.
88 :param numvfs: Number of VFs to be configured at given NIC.
90 :returns: True on success, False otherwise
92 if not is_sriov_supported(pci_handle):
95 if get_sriov_numvfs(pci_handle) == numvfs:
98 if numvfs and get_sriov_numvfs(pci_handle) != 0:
99 if not set_sriov_numvfs(pci_handle, 0):
103 subprocess.call('sudo bash -c "echo {} > {}"'.format(numvfs, _SRIOV_NUMVFS.format(pci_handle)), shell=True)
104 return get_sriov_numvfs(pci_handle) == numvfs
106 _LOGGER.debug('Number of VFs cant be changed to %s for PF %s', numvfs, pci_handle)
109 def get_sriov_numvfs(pci_handle):
110 """ Returns the number of configured VFs
112 :param pci_handle: PCI slot identifier with domain part
113 :returns: the number of configured VFs
115 if is_sriov_supported(pci_handle):
116 with open(_SRIOV_NUMVFS.format(pci_handle), 'r') as numvfs:
117 return int(numvfs.readline().rstrip('\n'))
121 def get_sriov_totalvfs(pci_handle):
122 """ Checks if sriov is supported and returns max number of supported VFs
124 :param pci_handle: PCI slot identifier with domain part
125 :returns: the max number of supported VFs by given NIC
127 if is_sriov_supported(pci_handle):
128 with open(_SRIOV_TOTALVFS.format(pci_handle), 'r') as total:
129 return int(total.readline().rstrip('\n'))
133 def get_sriov_vfs_list(pf_pci_handle):
134 """ Returns list of PCI handles of VFs configured at given NIC/PF
136 :param pf_pci_handle: PCI slot identifier of PF with domain part.
140 if is_sriov_supported(pf_pci_handle):
141 for vf_name in glob.glob(os.path.join(_PCI_DIR, _SRIOV_VF_PREFIX + '*').format(pf_pci_handle)):
142 vfs.append(os.path.basename(os.path.realpath(vf_name)))
146 def get_sriov_pf(vf_pci_handle):
147 """ Get PCI handle of PF which belongs to given VF
149 :param vf_pci_handle: PCI slot identifier of VF with domain part.
150 :returns: PCI handle of parent PF
152 pf_path = os.path.join(_PCI_DIR, _SRIOV_PF).format(vf_pci_handle)
153 if os.path.isdir(pf_path):
154 return os.path.basename(os.path.realpath(pf_path))
158 def get_driver(pci_handle):
159 """ Returns name of kernel driver assigned to given NIC
161 :param pci_handle: PCI slot identifier with domain part.
162 :returns: string with assigned kernel driver, None otherwise
164 driver_path = os.path.join(_PCI_DIR, _PCI_DRIVER).format(pci_handle)
165 if os.path.isdir(driver_path):
166 return os.path.basename(os.path.realpath(driver_path))
170 def get_device_name(pci_handle):
171 """ Returns name of network card device name
173 :param pci_handle: PCI slot identifier with domain part.
174 :returns: string with assigned NIC device name, None otherwise
176 net_path = os.path.join(_PCI_DIR, _PCI_NET).format(pci_handle)
178 return os.listdir(net_path)[0]
179 except FileNotFoundError:
186 def get_mac(pci_handle):
187 """ Returns MAC address of given NIC
189 :param pci_handle: PCI slot identifier with domain part.
190 :returns: string with assigned MAC address, None otherwise
192 mac_path = glob.glob(os.path.join(_PCI_DIR, _PCI_NET, '*', 'address').format(pci_handle))
193 # kernel driver is loaded and MAC can be read
194 if len(mac_path) and os.path.isfile(mac_path[0]):
195 with open(mac_path[0], 'r') as _file:
196 return _file.readline().rstrip('\n')
198 # MAC address is unknown, e.g. NIC is assigned to DPDK
201 def get_nic_info(full_pci_handle):
202 """ Parse given pci handle with additional info and returns
205 :param full_pci_handle: A string with extended network card PCI ID.
206 extended PCI ID syntax: PCI_ID[|vfx][|(mac|dev)]
208 0000:06:00.0 - returns the same value
209 0000:06:00.0|vf0 - returns PCI ID of 1st virtual function of given NIC
210 0000:06:00.0|mac - returns MAC address of given NIC
211 0000:06:00.0|vf0|mac - returns MAC address of 1st virtual function of given NIC
213 :returns: A string with requested NIC data or None if data cannot be read.
215 parsed_handle = full_pci_handle.split('|')
216 if len(parsed_handle) not in (1, 2, 3):
217 _LOGGER.error("Invalid PCI device name: '%s'", full_pci_handle)
220 pci_handle = parsed_handle[0]
222 for action in parsed_handle[1:]:
223 # in case of SRIOV get PCI handle of given virtual function
224 if action.lower().startswith('vf'):
226 vf_num = int(action[2:])
227 pci_handle = get_sriov_vfs_list(pci_handle)[vf_num]
229 _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, action[2:])
232 _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, vf_num)
236 # return requested info for given PCI handle
237 if action.lower() == 'mac':
238 return get_mac(pci_handle)
239 elif action.lower() == 'dev':
240 return get_device_name(pci_handle)
242 _LOGGER.error("Invalid item '%s' in PCI handle '%s'", action, full_pci_handle)
247 def reinit_vfs(pf_pci_handle):
248 """ Reinitializates all VFs, which belong to given PF
250 :param pf_pci_handle: PCI slot identifier of PF with domain part.
252 rte_pci_tool = settings.getValue('TOOLS')['bind-tool']
254 for vf_nic in get_sriov_vfs_list(pf_pci_handle):
255 nic_driver = get_driver(vf_nic)
258 subprocess.call(['sudo', rte_pci_tool, '--unbind', vf_nic],
259 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
260 subprocess.call(['sudo', rte_pci_tool, '--bind=' + nic_driver, vf_nic],
261 stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
262 except subprocess.CalledProcessError:
263 _LOGGER.warning('Error during reinitialization of VF %s', vf_nic)
265 _LOGGER.warning("Can't detect driver for VF %s", vf_nic)