mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-07-30 15:06:49 +00:00
config_tools: allocate interrupt lines among VMs
This patch allocates interrupt lines among VMs according to the PCI devices assigned to them. v1 -> v2: * Remove the usage of VMx_PT_INTX_NUM macro in vm_configuration.c; use the concrete numbers directly. * The static allocator will also complain if any interrupt line is allocated to a VM with LAPIC_PASSTHROUGH. v2 -> v3: * Fix a minor coding style issue. Tracked-On: #6287 Signed-off-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
parent
692ab1d529
commit
5c34fd0c56
@ -8,6 +8,9 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'library'))
|
||||
import common, board_cfg_lib, lib.error, lib.lib
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from itertools import combinations
|
||||
|
||||
LEGACY_IRQ_MAX = 16
|
||||
|
||||
@ -47,7 +50,7 @@ def create_vuart_irq_node(etree, vm_id, vuart_id, irq):
|
||||
|
||||
common.append_node(f"./legacy_vuart[@id = '{vuart_id}']/irq", irq, allocation_sos_vm_node)
|
||||
|
||||
def fn(board_etree, scenario_etree, allocation_etree):
|
||||
def alloc_sos_vuart_irqs(board_etree, scenario_etree, allocation_etree):
|
||||
irq_list = get_native_valid_irq()
|
||||
hv_debug_console = lib.lib.parse_hv_console(scenario_etree)
|
||||
native_ttys = lib.lib.get_native_ttys()
|
||||
@ -69,3 +72,133 @@ def fn(board_etree, scenario_etree, allocation_etree):
|
||||
vuart1_irq = alloc_irq(irq_list)
|
||||
|
||||
create_vuart_irq_node(allocation_etree, vm_id, "1", vuart1_irq)
|
||||
|
||||
def get_irqs_of_device(device_node):
|
||||
irqs = set()
|
||||
|
||||
# IRQs in ACPI
|
||||
for res in device_node.xpath("resource[@type='irq']"):
|
||||
irqs.update(set(map(int, res.get("int").split(", "))))
|
||||
|
||||
# PCI interrupt pin
|
||||
for res in device_node.xpath("resource[@type='interrupt_pin']"):
|
||||
irq = res.get("source", None)
|
||||
if irq is not None:
|
||||
irqs.add(int(irq))
|
||||
|
||||
return irqs
|
||||
|
||||
def alloc_device_irqs(board_etree, scenario_etree, allocation_etree):
|
||||
service_vm_id = -1
|
||||
irq_allocation = defaultdict(lambda: defaultdict(lambda: [])) # vm_id -> irq -> [device]
|
||||
|
||||
# Collect the list of devices that have to use INTx, excluding legacy UART which is to be emulated.
|
||||
device_nodes = set(board_etree.xpath("//device[count(resource[@type='irq' or @type='interrupt_pin']) > 0 and count(capability[@id='MSI' or @id='MSI-X']) = 0]"))
|
||||
uart_nodes = set(board_etree.xpath("//device[@id='PNP0501']"))
|
||||
device_nodes -= uart_nodes
|
||||
|
||||
#
|
||||
# Identify the interrupt lines each pre-launched VM uses
|
||||
#
|
||||
for vm in scenario_etree.xpath("//vm"):
|
||||
vm_type = vm.find("vm_type").text
|
||||
vm_id = int(vm.get("id"))
|
||||
if lib.lib.is_pre_launched_vm(vm_type):
|
||||
pt_intx_node = common.get_node("pt_intx", vm)
|
||||
if pt_intx_node is not None:
|
||||
pt_intx_mapping = dict(eval(f"[{pt_intx_node.text.replace(')(', '), (')}]"))
|
||||
for irq in pt_intx_mapping.keys():
|
||||
irq_allocation[vm_id][irq].append("(Explicitly assigned in scenario configuration)")
|
||||
for pci_dev in vm.xpath("pci_devs/pci_dev/text()"):
|
||||
bdf = lib.lib.BusDevFunc.from_str(pci_dev.split(" ")[0])
|
||||
address = hex((bdf.dev << 16) | (bdf.func))
|
||||
device_node = common.get_node(f"//bus[@address='{hex(bdf.bus)}']/device[@address='{address}']", board_etree)
|
||||
if device_node in device_nodes:
|
||||
irqs = get_irqs_of_device(device_node)
|
||||
for irq in irqs:
|
||||
irq_allocation[vm_id][irq].append(pci_dev)
|
||||
device_nodes.discard(device_node)
|
||||
|
||||
# Raise error when any pre-launched VM with LAPIC passthrough requires any interrupt line.
|
||||
lapic_passthru_flag = common.get_node("guest_flags[guest_flag='GUEST_FLAG_LAPIC_PASSTHROUGH']", vm)
|
||||
if lapic_passthru_flag is not None and irq_allocation[vm_id]:
|
||||
for irq, devices in irq_allocation[vm_id].items():
|
||||
print(f"Interrupt line {irq} is used by the following device(s).")
|
||||
for device in devices:
|
||||
print(f"\t{device}")
|
||||
raise lib.error.ResourceError(f"Pre-launched VM {vm_id} with LAPIC_PASSTHROUGH flag cannot use interrupt lines.")
|
||||
elif lib.lib.is_sos_vm(vm_type):
|
||||
service_vm_id = vm_id
|
||||
|
||||
#
|
||||
# Detect interrupt line conflicts
|
||||
#
|
||||
conflicts = defaultdict(lambda: defaultdict(lambda: set())) # irq -> vm_id -> devices
|
||||
|
||||
# If a service VM exists, collect its interrupt lines as well
|
||||
if service_vm_id >= 0:
|
||||
# Collect the interrupt lines that may be used by the service VM
|
||||
for device_node in device_nodes:
|
||||
acpi_object = device_node.find("acpi_object")
|
||||
description = ""
|
||||
if acpi_object is not None:
|
||||
description = acpi_object.text
|
||||
description = device_node.get("description", description)
|
||||
|
||||
# Guess BDF of the device
|
||||
bus = device_node.getparent()
|
||||
if bus.tag == "bus" and bus.get("type") == "pci" and device_node.get("address") is not None:
|
||||
bus_number = int(bus.get("address"), 16)
|
||||
address = int(device_node.get("address"), 16)
|
||||
device_number = address >> 16
|
||||
function_number = address & 0xffff
|
||||
description = f"{bus_number:02x}:{device_number:02x}.{function_number} {description}"
|
||||
|
||||
for irq in get_irqs_of_device(device_node):
|
||||
irq_allocation[service_vm_id][irq].append(description)
|
||||
|
||||
# Identify and report conflicts among interrupt lines of the VMs
|
||||
for vm1, vm2 in combinations(irq_allocation.keys(), 2):
|
||||
common_irqs = set(irq_allocation[vm1].keys()) & set(irq_allocation[vm2].keys())
|
||||
for irq in common_irqs:
|
||||
conflicts[irq][vm1].update(set(irq_allocation[vm1][irq]))
|
||||
conflicts[irq][vm2].update(set(irq_allocation[vm2][irq]))
|
||||
|
||||
if conflicts:
|
||||
print("Interrupt line conflicts detected!")
|
||||
for irq, vm_devices in sorted(conflicts.items()):
|
||||
print(f"Interrupt line {irq} is shared by the following devices.")
|
||||
for vm_id, devices in vm_devices.items():
|
||||
for device in sorted(devices):
|
||||
print(f"\tVM {vm_id}: {device}")
|
||||
raise lib.error.ResourceError(f"VMs have conflicting interrupt lines.")
|
||||
|
||||
#
|
||||
# Dump allocations to allocation_etree. The virtual interrupt line is the same as the physical one unless otherwise
|
||||
# stated in the scenario configuration.
|
||||
#
|
||||
for vm_id, alloc in irq_allocation.items():
|
||||
vm_node = common.get_node(f"/acrn-config/vm[@id = '{vm_id}']", allocation_etree)
|
||||
if vm_node is None:
|
||||
vm_node = common.append_node("/acrn-config/vm", None, allocation_etree, id = str(vm_id))
|
||||
pt_intx_node = common.get_node(f"//vm[@id='{vm_id}']/pt_intx", scenario_etree)
|
||||
pt_intx_mapping = dict(eval(f"[{pt_intx_node.text.replace(')(', '), (')}]")) if pt_intx_node is not None else {}
|
||||
for irq, devs in alloc.items():
|
||||
for dev in devs:
|
||||
if dev.startswith("("): # Allocation in the scenario configuration need not go to allocation.xml
|
||||
continue
|
||||
bdf = dev.split(" ")[0]
|
||||
dev_name = f"PTDEV_{bdf}"
|
||||
dev_node = common.get_node(f"device[@name = '{dev_name}']", vm_node)
|
||||
if dev_node is None:
|
||||
dev_node = common.append_node("./device", None, vm_node, name = dev_name)
|
||||
pt_intx_node = common.get_node(f"pt_intx", dev_node)
|
||||
virq = pt_intx_mapping.get(irq, irq)
|
||||
if pt_intx_node is None:
|
||||
common.append_node(f"./pt_intx", f"({irq}, {virq})", dev_node)
|
||||
else:
|
||||
pt_intx_node.text += f" ({irq}, {virq})"
|
||||
|
||||
def fn(board_etree, scenario_etree, allocation_etree):
|
||||
alloc_sos_vuart_irqs(board_etree, scenario_etree, allocation_etree)
|
||||
alloc_device_irqs(board_etree, scenario_etree, allocation_etree)
|
||||
|
@ -9,6 +9,8 @@
|
||||
xmlns:dyn="http://exslt.org/dynamic"
|
||||
xmlns:math="http://exslt.org/math"
|
||||
xmlns:func="http://exslt.org/functions"
|
||||
xmlns:str="http://exslt.org/strings"
|
||||
xmlns:set="http://exslt.org/sets"
|
||||
xmlns:acrn="http://projectacrn.org"
|
||||
extension-element-prefixes="func">
|
||||
|
||||
@ -586,6 +588,13 @@
|
||||
</xsl:choose>
|
||||
</func:function>
|
||||
|
||||
<func:function name="acrn:get-intx-mapping">
|
||||
<xsl:param name="pt_intx_nodes" />
|
||||
<xsl:variable name="joined" select="translate(acrn:string-join($pt_intx_nodes/text(), '', '', ''), ' ', '')" />
|
||||
<xsl:variable name="unique_mapping" select="set:distinct(str:split(str:replace($joined, ')(', ').('), '.'))" />
|
||||
<func:result select="$unique_mapping" />
|
||||
</func:function>
|
||||
|
||||
<func:function name="acrn:get-vbdf">
|
||||
<xsl:param name="vmid" />
|
||||
<xsl:param name="name" />
|
||||
|
@ -39,7 +39,6 @@
|
||||
<xsl:call-template name="vm0_passthrough_tpm" />
|
||||
<xsl:call-template name="vm_config_pci_dev_num" />
|
||||
<xsl:call-template name="vm_boot_args" />
|
||||
<xsl:call-template name="vm_pt_intx_num" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="allocation-data//ssram">
|
||||
@ -216,17 +215,4 @@
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="vm_pt_intx_num">
|
||||
<xsl:variable name="pt_intx" select="normalize-space(vm/pt_intx)" />
|
||||
<xsl:variable name="length" select="string-length($pt_intx) - string-length(translate($pt_intx, ',', ''))" />
|
||||
<xsl:choose>
|
||||
<xsl:when test="$length">
|
||||
<xsl:value-of select="acrn:define('VM0_PT_INTX_NUM', $length, 'U')" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="acrn:define('VM0_PT_INTX_NUM', 0, 'U')" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
@ -20,20 +20,7 @@
|
||||
<!-- Included headers -->
|
||||
<xsl:value-of select="acrn:include('asm/vm_config.h')" />
|
||||
|
||||
<xsl:apply-templates select="config-data/acrn-config" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="config-data/acrn-config">
|
||||
<xsl:choose>
|
||||
<xsl:when test="(vm/pt_intx) and (normalize-space(vm/pt_intx) != '')">
|
||||
<xsl:value-of select="$newline" />
|
||||
<xsl:apply-templates select="vm/pt_intx" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:text>struct pt_intx_config vm0_pt_intx[1U];</xsl:text>
|
||||
<xsl:value-of select="$newline" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:apply-templates select="config-data/acrn-config/vm" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="format_remappings">
|
||||
@ -51,26 +38,33 @@
|
||||
<xsl:value-of select="acrn:initializer('virt_gsi', concat($virt_gsi, 'U'))" />
|
||||
<xsl:text>},</xsl:text>
|
||||
<xsl:value-of select="$newline" />
|
||||
<xsl:call-template name="format_remappings">
|
||||
<xsl:with-param name="pText" select="substring-after($pText, ')')" />
|
||||
<xsl:with-param name="index" select="$index + 1" />
|
||||
</xsl:call-template>
|
||||
</xsl:if>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="vm/pt_intx">
|
||||
<xsl:variable name="length" select="string-length(current()) - string-length(translate(current(), ',', ''))" />
|
||||
<xsl:if test="$length">
|
||||
<xsl:value-of select="acrn:array-initializer('struct pt_intx_config', 'vm0_pt_intx', concat($length, 'U'))" />
|
||||
<xsl:variable name="pText" select="normalize-space(current())" />
|
||||
<xsl:variable name="index" select="'0'" />
|
||||
<xsl:call-template name="format_remappings">
|
||||
<xsl:with-param name="pText" select="$pText" />
|
||||
<xsl:with-param name="index" select="$index" />
|
||||
</xsl:call-template>
|
||||
<xsl:value-of select="$end_of_array_initializer" />
|
||||
<xsl:template match="config-data/acrn-config/vm">
|
||||
<xsl:if test="acrn:is-pre-launched-vm(vm_type)">
|
||||
<xsl:variable name="vm_id" select="@id" />
|
||||
<xsl:variable name="pt_intx" select="acrn:get-intx-mapping(//vm[@id=$vm_id]//pt_intx)" />
|
||||
<xsl:variable name="length" select="count($pt_intx)" />
|
||||
|
||||
<xsl:choose>
|
||||
<xsl:when test="$length">
|
||||
<xsl:value-of select="acrn:array-initializer('struct pt_intx_config', concat('vm', @id, '_pt_intx'), concat($length, 'U'))" />
|
||||
<xsl:for-each select="$pt_intx">
|
||||
<xsl:call-template name="format_remappings">
|
||||
<xsl:with-param name="pText" select="." />
|
||||
<xsl:with-param name="index" select="position() - 1" />
|
||||
</xsl:call-template>
|
||||
</xsl:for-each>
|
||||
<xsl:value-of select="$end_of_array_initializer" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="acrn:array-initializer('struct pt_intx_config', concat('vm', @id, '_pt_intx'), '1U')" />
|
||||
<xsl:value-of select="$end_of_array_initializer" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:value-of select="$newline" />
|
||||
</xsl:if>
|
||||
<xsl:value-of select="$newline" />
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
@ -32,19 +32,21 @@
|
||||
<xsl:value-of select="acrn:extern('struct acrn_vm_pci_dev_config', concat('vm', @id, '_pci_devs'), concat('VM', @id, '_CONFIG_PCI_DEV_NUM'))" />
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
</xsl:for-each>
|
||||
|
||||
<!-- Declaration of pt_intx -->
|
||||
<xsl:variable name="pt_intx" select="normalize-space(vm[@id = 0]/pt_intx)" />
|
||||
<xsl:variable name="length" select="string-length($pt_intx) - string-length(translate($pt_intx, ',', ''))" />
|
||||
<xsl:choose>
|
||||
<xsl:when test="$length > 0">
|
||||
<xsl:value-of select="acrn:extern('struct pt_intx_config', 'vm0_pt_intx', concat($length, 'U'))" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="acrn:extern('struct pt_intx_config', 'vm0_pt_intx', '1U')" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<!-- Declaration of pt_intx -->
|
||||
<xsl:if test="acrn:is-pre-launched-vm(vm_type)">
|
||||
<xsl:variable name="vm_id" select="@id" />
|
||||
<xsl:variable name="length" select="count(acrn:get-intx-mapping(//vm[@id=$vm_id]//pt_intx))" />
|
||||
<xsl:choose>
|
||||
<xsl:when test="$length">
|
||||
<xsl:value-of select="acrn:extern('struct pt_intx_config', concat('vm', @id, '_pt_intx'), concat($length, 'U'))" />
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select="acrn:extern('struct pt_intx_config', concat('vm', @id, '_pt_intx'), '1U')" />
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:if>
|
||||
</xsl:for-each>
|
||||
|
||||
<!-- Definition of vm_configs -->
|
||||
<xsl:value-of select="acrn:array-initializer('struct acrn_vm_config', 'vm_configs', 'CONFIG_MAX_VM_NUM')" />
|
||||
@ -253,9 +255,12 @@
|
||||
<xsl:text>},</xsl:text>
|
||||
<xsl:value-of select="$newline" />
|
||||
<xsl:value-of select="$endif" />
|
||||
<xsl:value-of select="acrn:initializer('pt_intx_num', 'VM0_PT_INTX_NUM')" />
|
||||
<xsl:value-of select="acrn:initializer('pt_intx', '&vm0_pt_intx[0U]')" />
|
||||
</xsl:if>
|
||||
|
||||
<xsl:variable name="vm_id" select="@id" />
|
||||
<xsl:variable name="length" select="count(acrn:get-intx-mapping(//vm[@id=$vm_id]//pt_intx))" />
|
||||
<xsl:value-of select="acrn:initializer('pt_intx_num', $length)" />
|
||||
<xsl:value-of select="acrn:initializer('pt_intx', concat('vm', @id, '_pt_intx'))" />
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
Loading…
Reference in New Issue
Block a user