board_inspector: extract PF and VF info

This patch adds logic to the extractors to fetch the following information.

  1. All the details of an SR-IOV capability, which are reported in the
     SR-IOV extended capability structure.

  2. Correctly report the vendor ID, device ID and BAR addresses of VFs.

  3. Refer each VF back to the corresponding PF. Use XPATH to search for
     all the VFs enabled by a PF.

Tracked-On: #7301
Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Junjie Mao 2022-04-16 00:21:08 +08:00 committed by acrnsi-robot
parent 93ccc0f473
commit 46fab9e8a0
2 changed files with 112 additions and 30 deletions

View File

@ -31,7 +31,7 @@ def collect_hostbridge_resources(bus_node, bus_number):
begin, end = tuple(map(lambda x: int(f"0x{x}", base=16), fields[0].split("-")))
add_child(bus_node, "resource", type="memory", min=hex(begin), max=hex(end), len=hex(end - begin + 1))
def parse_msi(cap_node, cap_struct):
def parse_msi(cap_node, cap_struct, **kwargs):
add_child(cap_node, "count", str(1 << cap_struct.multiple_message_capable))
if cap_struct.multiple_message_capable > 0:
add_child(cap_node, "capability", id="multiple-message")
@ -42,21 +42,61 @@ def parse_msi(cap_node, cap_struct):
if cap_struct.per_vector_masking_capable:
add_child(cap_node, "capability", id="per-vector masking")
def parse_msix(cap_node, cap_struct):
def parse_msix(cap_node, cap_struct, **kwargs):
add_child(cap_node, "table_size", str(cap_struct.table_size))
add_child(cap_node, "table_bir", str(cap_struct.table_bir))
add_child(cap_node, "table_offset", hex(cap_struct.table_offset_z))
add_child(cap_node, "pba_bir", str(cap_struct.pba_bir))
add_child(cap_node, "pba_offset", hex(cap_struct.pba_offset_z))
def parse_sriov(cap_node, cap_struct, **kwargs):
device_node = cap_node.getparent()
bus_node = device_node.getparent()
assert bus_node.tag == "bus" and device_node.tag == "device"
pf_bus_id = int(bus_node.get("address"), 16)
pf_dev_id = int(device_node.get("address"), 16) >> 16
pf_func_id = int(device_node.get("address"), 16) & 0xffff
pf_bdf = (pf_bus_id << 8) | (pf_dev_id << 3) | pf_func_id
if cap_struct.vf_enable == 0 or cap_struct.num_vfs == 0:
logging.warning(f"The PCI device at {pf_bus_id:02x}:{pf_dev_id:02x}.{pf_func_id} has SR-IOV capability which is currently disabled.")
logging.warning(f"If that device is intended to provide virtual functions in your scenario, write (as root) the number of VFs to {kwargs['device_path']}/sriov_numvfs and rerun the board inspector.")
if cap_struct.vf_migration_capable:
vf_migration_cap = add_child(cap_node, "capability", id="vf_migration")
add_child(vf_migration_cap, "vf_migration_interrupt_message_number", str(cap_struct.vf_migration_interrupt_message_number))
if cap_struct.ari_capable_hierarchy_preserved:
add_child(cap_node, "capability", id="ari_capable_hierarchy_preserved")
if cap_struct.vf_10_bit_tag_requester_supported:
add_child(cap_node, "capability", id="vf_10_bit_tag_requester_supported")
add_child(cap_node, "initial_vfs", str(cap_struct.initial_vfs))
add_child(cap_node, "total_vfs", str(cap_struct.total_vfs))
add_child(cap_node, "function_dependency_link", str(cap_struct.function_dependency_link))
first_vf_bdf_node = add_child(cap_node, "first_vf_bdf")
first_vf_bdf = pf_bdf + cap_struct.first_vf_offset
add_child(first_vf_bdf_node, "bus", hex(first_vf_bdf >> 8))
add_child(first_vf_bdf_node, "device", hex((first_vf_bdf >> 3) & 0x1f))
add_child(first_vf_bdf_node, "function", hex(first_vf_bdf & 0x7))
add_child(cap_node, "vf_stride", str(cap_struct.vf_stride))
add_child(cap_node, "vf_device_id", hex(cap_struct.vf_device_id))
add_child(cap_node, "supported_page_sizes", hex(cap_struct.supported_page_sizes))
cap_parsers = {
"MSI": parse_msi,
"MSI-X": parse_msix,
"SR-IOV": parse_sriov,
}
def parse_device(bus_node, device_path):
device_name = os.path.basename(device_path)
cfg = parse_config_space(device_path)
physfn_cfg = parse_config_space(os.path.join(device_path, "physfn"))
# There are cases where Linux creates device-like nodes without a file named "config", e.g. when there is a PCIe
# non-transparent bridge (NTB) on the physical platform.
@ -85,8 +125,8 @@ def parse_device(bus_node, device_path):
logging.info(f"Resuming {device_path} failed: {str(e)}")
# Device identifiers
vendor_id = "0x{:04x}".format(cfg.header.vendor_id)
device_id = "0x{:04x}".format(cfg.header.device_id)
vendor_id = "0x{:04x}".format(cfg.header.vendor_id if physfn_cfg is None else physfn_cfg.header.vendor_id)
device_id = "0x{:04x}".format(cfg.header.device_id if physfn_cfg is None else physfn_cfg.get_cap("SR-IOV").vf_device_id)
class_code = "0x{:06x}".format(cfg.header.class_code)
if device_node.get("id") is None:
device_node.set("id", device_id)
@ -100,43 +140,85 @@ def parse_device(bus_node, device_path):
add_child(device_node, "subsystem_identifier", subdevice_id)
# BARs
idx = 0
for bar in cfg.header.bars:
resource_path = os.path.join(device_path, f"resource{idx}")
resource_type = bar.resource_type
base = bar.base
if os.path.exists(resource_path):
if bar.base == 0:
logging.debug(f"PCI {device_name}: BAR {idx} exists but is programmed with all 0. This device cannot be passed through to any VM.")
if physfn_cfg is None:
idx = 0
for bar in cfg.header.bars:
resource_path = os.path.join(device_path, f"resource{idx}")
resource_type = bar.resource_type
base = bar.base
if os.path.exists(resource_path):
if bar.base == 0:
logging.debug(f"PCI {device_name}: BAR {idx} exists but is programmed with all 0. This device cannot be passed through to any VM.")
else:
resource_node = get_node(device_node, f"./resource[@type = '{resource_type}' and @min = '{hex(base)}']")
if resource_node is None:
size = os.path.getsize(resource_path)
resource_node = add_child(device_node, "resource", None, type=resource_type, min=hex(base), max=hex(base + size - 1), len=hex(size))
resource_node.set("id", f"bar{idx}")
if isinstance(bar, MemoryBar32):
resource_node.set("width", "32")
resource_node.set("prefetchable", str(bar.prefetchable))
elif isinstance(bar, MemoryBar64):
resource_node.set("width", "64")
resource_node.set("prefetchable", str(bar.prefetchable))
elif bar.base != 0:
logging.debug(f"PCI {device_name}: Cannot detect the size of BAR {idx}")
if isinstance(bar, MemoryBar64):
idx += 2
else:
resource_node = get_node(device_node, f"./resource[@type = '{resource_type}' and @min = '{hex(base)}']")
if resource_node is None:
idx += 1
else:
pf_path = os.path.realpath(os.path.join(device_path, "physfn"))
pf_name = os.path.basename(pf_path)
m = bdf_regex.match(pf_name)
pf_bus, pf_device, pf_function = int(m.group(2), base=16), int(m.group(3), base=16), int(m.group(4), base=16)
pf_bdf = (pf_bus << 8) | (pf_device << 3) | pf_function
m = bdf_regex.match(device_name)
vf_bus, vf_device, vf_function = int(m.group(2), base=16), int(m.group(3), base=16), int(m.group(4), base=16)
vf_bdf = (vf_bus << 8) | (vf_device << 3) | vf_function
first_vf_offset = physfn_cfg.get_cap("SR-IOV").first_vf_offset
vf_stride = physfn_cfg.get_cap("SR-IOV").vf_stride
vf_id = (vf_bdf - (pf_bdf + first_vf_offset)) // vf_stride
idx = 0
for bar in physfn_cfg.get_cap("SR-IOV").vf_bars:
resource_path = os.path.join(device_path, f"resource{idx}")
resource_type = bar.resource_type
if os.path.exists(resource_path):
if bar.base == 0:
logging.debug(f"PCI {device_name}: BAR {idx} exists but is programmed with all 0. This device cannot be passed through to any VM.")
else:
size = os.path.getsize(resource_path)
base = bar.base + vf_id * size
resource_node = add_child(device_node, "resource", None, type=resource_type, min=hex(base), max=hex(base + size - 1), len=hex(size))
resource_node.set("id", f"bar{idx}")
if isinstance(bar, MemoryBar32):
resource_node.set("width", "32")
resource_node.set("prefetchable", str(bar.prefetchable))
elif isinstance(bar, MemoryBar64):
resource_node.set("width", "64")
resource_node.set("prefetchable", str(bar.prefetchable))
elif bar.base != 0:
logging.debug(f"PCI {device_name}: Cannot detect the size of BAR {idx}")
if isinstance(bar, MemoryBar64):
idx += 2
else:
idx += 1
resource_node.set("id", f"bar{idx}")
if isinstance(bar, MemoryBar32):
resource_node.set("width", "32")
resource_node.set("prefetchable", str(bar.prefetchable))
elif isinstance(bar, MemoryBar64):
resource_node.set("width", "64")
resource_node.set("prefetchable", str(bar.prefetchable))
elif bar.base != 0:
logging.debug(f"PCI {device_name}: Cannot detect the size of BAR {idx}")
if isinstance(bar, MemoryBar64):
idx += 2
else:
idx += 1
add_child(device_node, "physfn", bus=hex(pf_bus), address=hex((pf_device << 16) | pf_function))
# Capabilities
for cap in cfg.caps:
cap_node = add_child(device_node, "capability", id=cap.name)
if cap.name in cap_parsers:
cap_parsers[cap.name](cap_node, cap)
cap_parsers[cap.name](cap_node, cap, device_path=device_path)
for cap in cfg.extcaps:
cap_node = add_child(device_node, "capability", id=cap.name)
if cap.name in cap_parsers:
cap_parsers[cap.name](cap_node, cap)
cap_parsers[cap.name](cap_node, cap, device_path=device_path)
# Interrupt pin
pin = cfg.header.interrupt_pin

View File

@ -28,7 +28,7 @@ def getkey(child):
tags = ["vendor", "identifier", "subsystem_vendor", "subsystem_identifier", "class",
"acpi_object", "compatible_id", "acpi_uid", "aml_template", "status",
"resource", "capability", "interrupt_pin_routing", "dependency", "bus", "device"]
"resource", "capability", "interrupt_pin_routing", "dependency", "bus", "device", "physfn"]
if child.tag == "resource":
return (tags.index(child.tag), child.get("type"), resource_subkey(child))