From bce34f1ec467a0a950570765db605ff4bc4409f0 Mon Sep 17 00:00:00 2001 From: Vladimir Koshelev Date: Tue, 27 Sep 2011 17:39:14 +0400 Subject: [PATCH 21/32] irq balance patch --- palacios/include/devices/irq_balance.h | 14 ++ palacios/src/devices/Kconfig | 11 ++- palacios/src/devices/Makefile | 2 + palacios/src/devices/irq_balance.c | 222 ++++++++++++++++++++++++++++++++ palacios/src/devices/pci_passthrough.c | 10 ++ palacios/src/devices/smbios.c | 15 +- 6 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 palacios/include/devices/irq_balance.h create mode 100644 palacios/src/devices/irq_balance.c diff --git a/palacios/include/devices/irq_balance.h b/palacios/include/devices/irq_balance.h new file mode 100644 index 0000000..f9f4520 --- /dev/null +++ b/palacios/include/devices/irq_balance.h @@ -0,0 +1,14 @@ +/* + * irq_balance.h + * + * Created on: Sep 26, 2011 + * Author: vedun + */ + +#ifndef __IRQ_BALANCE_H_ +#define __IRQ_BALANCE_H_ + +int v3_irq_do_balance(struct v3_vm_info * vm_info, struct vm_device * device, struct pci_config_header * hdr); + + +#endif /* __IRQ_BALANCE_H_ */ diff --git a/palacios/src/devices/Kconfig b/palacios/src/devices/Kconfig index e98e016..7201006 100644 --- a/palacios/src/devices/Kconfig +++ b/palacios/src/devices/Kconfig @@ -292,6 +292,13 @@ config PASSTHROUGH_PCI help Enables hardware devices to be passed through to the VM +config IRQ_BALANCE + bool "IRQ balance" + default y + depends on PASSTHROUGH_PCI + help + Do irqbalance for marked passthrough devices + config SERIAL_PASSTHROUGH bool "Passthrough serial port IRQ" default n @@ -492,7 +499,9 @@ config SMBIOS config SMBIOS_DEBUG bool "Debug SMBios virtualization" default n - depends on SMBIOS + depends on SMBIOS + + diff --git a/palacios/src/devices/Makefile b/palacios/src/devices/Makefile index 03ba8d0..5d383de 100644 --- a/palacios/src/devices/Makefile +++ b/palacios/src/devices/Makefile @@ -26,6 +26,8 @@ obj-$(V3_CONFIG_SWAPBYPASS_DISK_CACHE2) += swapbypass_cache2.o obj-$(V3_CONFIG_DISK_MODEL) += disk_model.o obj-$(V3_CONFIG_NIC_BRIDGE) += nic_bridge.o +obj-$(V3_CONFIG_IRQ_BALANCE) += irq_balance.o + obj-$(V3_CONFIG_NE2K) += ne2k.o obj-$(V3_CONFIG_RTL8139) += rtl8139.o diff --git a/palacios/src/devices/irq_balance.c b/palacios/src/devices/irq_balance.c new file mode 100644 index 0000000..b724f04 --- /dev/null +++ b/palacios/src/devices/irq_balance.c @@ -0,0 +1,222 @@ + +#ifdef __V3VEE__ + +#include +#include +#include + +#include +#include + + + +#define IRQBALANCE_HIGH_PRIORITY ((uint32_t)0x1) +#define IRQBALANCE_DEVICE_MARKED ((uint32_t)0x2) + +#define PCI_CFG_ADDR 0xcf8 +#define PCI_CFG_DATA 0xcfc + +#define PCI_REG_IRQ 0x3C + +#define PCI_BUS_MAX 256 +#define PCI_DEV_MAX 32 +#define PCI_FN_MAX 7 + +struct irqbalance_list { + struct list_head head; + uint16_t vendor_id; + uint16_t device_id; + char * id; + uint8_t irq; + uint32_t flags; +}; + +struct freeirq_list { + struct list_head head; + uint8_t irq; +}; + +static struct list_head devices; +static struct list_head irqs; + +static int prefetched = 0; + +static int is_device_in_list(uint16_t vendor, uint16_t device) { + struct irqbalance_list * cur; + list_for_each_entry(cur, &devices, head) { + if (((cur->flags & IRQBALANCE_DEVICE_MARKED) == 0) && (cur->vendor_id == vendor) + && (cur->device_id == device)) { + cur->flags |= IRQBALANCE_DEVICE_MARKED; + return 1; + } + } + return 0; +} + +static int add_if_not_exists(uint8_t irq) { + struct freeirq_list *cur; + list_for_each_entry(cur, &irqs, head) { + if (cur->irq == irq) + return 0; + } + struct freeirq_list * new = V3_Malloc(sizeof(struct freeirq_list)); + new->irq = irq; + list_add(&new->head, &irqs); + return 1; +} + +static int irq_balance_prefetch(struct v3_vm_info * vm_info) { + PrintDebug("IRQ Balance prefetch\n"); + INIT_LIST_HEAD(&devices); + INIT_LIST_HEAD(&irqs); + v3_cfg_tree_t * cfg_root = vm_info->cfg_data->cfg; + v3_cfg_tree_t * cfg_devices = v3_cfg_subtree(cfg_root, "devices"); + if (cfg_devices == NULL) { + PrintError("Config file have no devices section, exiting...\n"); + return -1; + } + + PrintDebug("Scanning configuration file\n"); + + v3_cfg_tree_t * cur_device = v3_cfg_subtree(cfg_devices, "device"); + uint32_t high_prioriny_count = 0; + while (cur_device != NULL) { + if (strcmp(v3_cfg_val(cur_device, "class"), "PCI_PASSTHROUGH") == 0) { + v3_cfg_tree_t * irq_balance = v3_cfg_subtree(cur_device, "irq_balance"); + if (irq_balance != NULL) { + struct irqbalance_list *device = V3_Malloc(sizeof(struct irqbalance_list)); + device->vendor_id = atox(v3_cfg_val(cur_device, "vendor_id")); + device->device_id = atox(v3_cfg_val(cur_device, "device_id")); + device->id = v3_cfg_val(cur_device, "id"); + device->irq = 0xff; + device->flags = 0; + if ((v3_cfg_subtree(irq_balance, "high_priority") != NULL)) { + device->flags = IRQBALANCE_HIGH_PRIORITY; + high_prioriny_count++; + } + + + PrintDebug("Adding device : %04hX:%04hX %s : %s\n", device->vendor_id, device->device_id, + device->id, + device->flags ? "high priority" : "normal priority"); + + list_add(&device->head, &devices); + } + } + cur_device = v3_cfg_next_branch(cur_device); + } + + PrintDebug("Scanning PCI configuration space\n"); + + union pci_addr_reg { + uint32_t value; + struct { + uint_t rsvd1 : 2; + uint_t reg : 6; + uint_t func : 3; + uint_t dev : 5; + uint_t bus : 8; + uint_t rsvd2 : 7; + uint_t enable : 1; + } __attribute__((packed)); + } __attribute__((packed)); + union pci_addr_reg pci_addr = {0x80000000}; + uint_t i, j, k; + + union { + uint32_t value; + struct { + uint16_t vendor; + uint16_t device; + } __attribute__((packed)); + } __attribute__((packed)) pci_hdr = {0}; + + + uint32_t irq_count = 0; + for (i = 0, pci_addr.bus = 0; i < PCI_BUS_MAX; i++, pci_addr.bus++) + for (j = 0, pci_addr.dev = 0; j < PCI_DEV_MAX; j++, pci_addr.dev++) + for (k = 0, pci_addr.func = 0; k < PCI_FN_MAX; k++, pci_addr.func++) { + + v3_outdw(PCI_CFG_ADDR, pci_addr.value); + pci_hdr.value = v3_indw(PCI_CFG_DATA); + if (pci_hdr.vendor != 0xffff || pci_hdr.device != 0xffff) { + PrintDebug("%d:%d.%d found %hX:%hX\n", i, j, k, pci_hdr.vendor, pci_hdr.device); + if (is_device_in_list(pci_hdr.vendor, pci_hdr.device)) { + pci_addr.reg = PCI_REG_IRQ / 4; + v3_outdw(PCI_CFG_ADDR, pci_addr.value); + uint8_t irq = v3_inb(PCI_CFG_DATA); + PrintDebug("Trying to add irq %d\n", irq); + if (add_if_not_exists(irq)) { + irq_count++; + PrintDebug("Adding new irq %d\n", irq); + } + pci_addr.reg = 0; + } + } + } + + if (high_prioriny_count >= irq_count) { + PrintError("Too many high priority devices. Disabling high priority option...\n"); + struct irqbalance_list * cur; + list_for_each_entry(cur, &devices, head) { + cur->flags &= ~(IRQBALANCE_HIGH_PRIORITY); + } + } + + struct freeirq_list *cur; + list_for_each_entry(cur, &irqs, head) { + struct irqbalance_list *cur_dev; + list_for_each_entry(cur_dev, &devices, head) { + if (cur_dev->flags & IRQBALANCE_HIGH_PRIORITY) { + cur_dev->flags &= !(IRQBALANCE_HIGH_PRIORITY); + cur_dev->irq = cur->irq; + cur->irq = 0xff; + irq_count--; + break; + } + } + } + + uint8_t rest[irq_count]; + uint_t pointer = 0; + list_for_each_entry(cur, &irqs, head) { + if (cur->irq != 0xff) { + rest[pointer] = cur->irq; + pointer++; + } + } + pointer = pointer % irq_count; + V3_ASSERT(pointer == 0); + + struct irqbalance_list *cur_dev; + list_for_each_entry(cur_dev, &devices, head) { + if (cur_dev->irq == 0xff) { + cur_dev->irq = rest[pointer]; + pointer = (pointer + 1) % irq_count; + } + PrintDebug("IRQBALANCE : setting to %s irq %d\n", cur_dev->id, cur_dev->irq); + } + prefetched = 1; + return 0; +} + +int v3_irq_do_balance(struct v3_vm_info * vm_info, struct vm_device * device, struct pci_config_header * hdr) { + if (!prefetched) + irq_balance_prefetch(vm_info); + + struct irqbalance_list *cur_dev; + list_for_each_entry(cur_dev, &devices, head) { + if (strcmp(cur_dev->id, device->name) == 0) { + PrintDebug("Real : setting to %s irq %d\n", cur_dev->id, cur_dev->irq); + hdr->intr_line = cur_dev->irq; + return 0; + } + } + PrintError("Device with name %s not found\n", device->name); + return -1; +} + + +#endif + + diff --git a/palacios/src/devices/pci_passthrough.c b/palacios/src/devices/pci_passthrough.c index 11e603d..a0e7469 100644 --- a/palacios/src/devices/pci_passthrough.c +++ b/palacios/src/devices/pci_passthrough.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "pci_msi_types.h" @@ -1761,6 +1762,15 @@ static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * de V3_ASSERT(devno >= 0 && devno < 32); } + PrintDebug("Checking irq_balance...\n"); + if (v3_cfg_subtree(cfg, "irq_balance")) { + PrintDebug("Do irq balance...\n"); + if (v3_irq_do_balance(vm_info, dev, &state->real_hdr)) { + PrintError("Cannot do irq balance for device %hhX02:%02hhX.%01hhX\n", bus_num, devno, fn); + return -1; + } + + } pci_dev = v3_pci_register_passthrough_device(state->pci_bus, state->real_hdr.header_type, diff --git a/palacios/src/devices/smbios.c b/palacios/src/devices/smbios.c index 4fa6169..8963fc7 100644 --- a/palacios/src/devices/smbios.c +++ b/palacios/src/devices/smbios.c @@ -28,7 +28,7 @@ #define SMBIOS_TABLE_LOCATION 0xC7000 //#define SMBIOS_TABLE_LOCATION 0xF37FE000 -#define V3_CONFIG_SMBIOS_USE_MEM_HOOK +//#define V3_CONFIG_SMBIOS_USE_MEM_HOOK struct smbios_entry_point { @@ -426,6 +426,7 @@ static int smbios_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { #endif + void * target; #ifdef V3_CONFIG_SMBIOS_USE_MEM_HOOK if (v3_hook_full_mem(vm, V3_MEM_CORE_ANY, entry_point->struct_table_pointer, (addr_t) entry_point->struct_table_pointer + PAGE_SIZE * page_count - 1, @@ -435,11 +436,13 @@ static int smbios_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { } #else - if (v3_add_shadow_mem(vm, V3_MEM_CORE_ANY, (addr_t) entry_point->struct_table_pointer, - (addr_t) entry_point->struct_table_pointer + PAGE_SIZE * page_count - 1,(addr_t) pages)) { + if (v3_gpa_to_hva(&(vm->cores[0]), SMBIOS_TABLE_LOCATION , (addr_t *)&target) == -1) { PrintError("Can't add memory region..."); return -1; } + + memcpy(target, pages, PAGE_SIZE_4KB * page_count); + #endif entry_point->ieps = 0; @@ -447,11 +450,7 @@ static int smbios_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { entry_point->eps = 0; entry_point->eps = calc_checksum(((void *)entry_point), entry_point->epl); - - void * target; - - if (v3_gpa_to_hva(&(vm->cores[0]), - SMBIOS_DEFAULT_LOCATION , (addr_t *)&target) == -1) { + if (v3_gpa_to_hva(&(vm->cores[0]), SMBIOS_DEFAULT_LOCATION , (addr_t *)&target) == -1) { PrintError("Cannot inject mptable due to unmapped bios!\n"); return -1; } -- 1.7.5.4