[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v2 1/3] vpci: allow queueing of mapping operations
Introduce vPCI BAR mapping task queue. Decouple map operation state from general vPCI state: in particular, move the per-BAR rangeset out of struct vpci and into the map task struct. This is preparatory work for further changes that need to perform multiple unmap/map operations before returning to guest. Signed-off-by: Stewart Hildebrand <stewart.hildebrand@xxxxxxx> --- v1->v2: * new patch Related: 622bdd962822 ("vpci/header: handle p2m range sets per BAR") --- xen/common/domain.c | 4 + xen/drivers/vpci/header.c | 197 +++++++++++++++++++++++--------------- xen/drivers/vpci/vpci.c | 3 - xen/include/xen/vpci.h | 16 +++- 4 files changed, 139 insertions(+), 81 deletions(-) diff --git a/xen/common/domain.c b/xen/common/domain.c index 303c338ef293..214795e2d2fe 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -459,6 +459,10 @@ struct vcpu *vcpu_create(struct domain *d, unsigned int vcpu_id) d->vcpu[prev_id]->next_in_list = v; } +#ifdef CONFIG_HAS_VPCI + INIT_LIST_HEAD(&v->vpci.task_queue); +#endif + /* Must be called after making new vcpu visible to for_each_vcpu(). */ vcpu_check_shutdown(v); diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c index bb76e707992c..df065a5f5faf 100644 --- a/xen/drivers/vpci/header.c +++ b/xen/drivers/vpci/header.c @@ -34,7 +34,7 @@ struct map_data { struct domain *d; - const struct vpci_bar *bar; + const struct vpci_bar_map *bar; bool map; }; @@ -173,31 +173,23 @@ static void modify_decoding(const struct pci_dev *pdev, uint16_t cmd, ASSERT_UNREACHABLE(); } -bool vpci_process_pending(struct vcpu *v) +static bool vpci_process_map_task(struct vpci_map_task *task) { - const struct pci_dev *pdev = v->vpci.pdev; - struct vpci_header *header = NULL; + const struct pci_dev *pdev = task->pdev; unsigned int i; if ( !pdev ) return false; - read_lock(&v->domain->pci_lock); - - if ( !pdev->vpci || (v->domain != pdev->domain) ) - { - v->vpci.pdev = NULL; - read_unlock(&v->domain->pci_lock); + if ( !pdev->vpci || (task->domain != pdev->domain) ) return false; - } - header = &pdev->vpci->header; - for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) + for ( i = 0; i < ARRAY_SIZE(task->bars); i++ ) { - struct vpci_bar *bar = &header->bars[i]; + struct vpci_bar_map *bar = &task->bars[i]; struct map_data data = { - .d = v->domain, - .map = v->vpci.cmd & PCI_COMMAND_MEMORY, + .d = task->domain, + .map = task->cmd & PCI_COMMAND_MEMORY, .bar = bar, }; int rc; @@ -208,57 +200,79 @@ bool vpci_process_pending(struct vcpu *v) rc = rangeset_consume_ranges(bar->mem, map_range, &data); if ( rc == -ERESTART ) - { - read_unlock(&v->domain->pci_lock); return true; - } if ( rc ) { spin_lock(&pdev->vpci->lock); /* Disable memory decoding unconditionally on failure. */ - modify_decoding(pdev, v->vpci.cmd & ~PCI_COMMAND_MEMORY, + modify_decoding(pdev, task->cmd & ~PCI_COMMAND_MEMORY, false); spin_unlock(&pdev->vpci->lock); - /* Clean all the rangesets */ - for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) - if ( !rangeset_is_empty(header->bars[i].mem) ) - rangeset_purge(header->bars[i].mem); - - v->vpci.pdev = NULL; - - read_unlock(&v->domain->pci_lock); - - if ( !is_hardware_domain(v->domain) ) - domain_crash(v->domain); + if ( !is_hardware_domain(task->domain) ) + domain_crash(task->domain); return false; } } - v->vpci.pdev = NULL; spin_lock(&pdev->vpci->lock); - modify_decoding(pdev, v->vpci.cmd, v->vpci.rom_only); + modify_decoding(pdev, task->cmd, task->rom_only); spin_unlock(&pdev->vpci->lock); - read_unlock(&v->domain->pci_lock); + return false; +} + +static void destroy_map_task(struct vpci_map_task *task) +{ + unsigned int i; + if ( !task ) + return; + + for ( i = 0; i < ARRAY_SIZE(task->bars); i++ ) + rangeset_destroy(task->bars[i].mem); + + xfree(task); +} + +bool vpci_process_pending(struct vcpu *v) +{ + struct vpci_map_task *task; + read_lock(&v->domain->pci_lock); + + while ( (task = list_first_entry_or_null(&v->vpci.task_queue, + struct vpci_map_task, + next)) != NULL ) + { + if ( vpci_process_map_task(task) ) + { + read_unlock(&v->domain->pci_lock); + return true; + } + + list_del(&task->next); + destroy_map_task(task); + } + + read_unlock(&v->domain->pci_lock); return false; } -static int __init apply_map(struct domain *d, const struct pci_dev *pdev, - uint16_t cmd) +static int __init apply_map(struct vpci_map_task *task) { - struct vpci_header *header = &pdev->vpci->header; + struct domain *d = task->domain; + const struct pci_dev *pdev = task->pdev; + uint16_t cmd = task->cmd; int rc = 0; unsigned int i; ASSERT(rw_is_write_locked(&d->pci_lock)); - for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) + for ( i = 0; i < ARRAY_SIZE(task->bars); i++ ) { - struct vpci_bar *bar = &header->bars[i]; + struct vpci_bar_map *bar = &task->bars[i]; struct map_data data = { .d = d, .map = true, .bar = bar }; if ( rangeset_is_empty(bar->mem) ) @@ -283,7 +297,48 @@ static int __init apply_map(struct domain *d, const struct pci_dev *pdev, return rc; } -static void defer_map(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) +static struct vpci_map_task *alloc_map_task(const struct pci_dev *pdev, + uint16_t cmd, bool rom_only) +{ + struct vpci_map_task *task = xzalloc(struct vpci_map_task); + unsigned int i; + + if ( !task ) + return NULL; + + BUILD_BUG_ON(ARRAY_SIZE(task->bars) != ARRAY_SIZE(pdev->vpci->header.bars)); + + for ( i = 0; i < ARRAY_SIZE(task->bars); i++ ) + { + if ( pdev->vpci->header.bars[i].type >= VPCI_BAR_MEM32 ) + { + char str[32]; + + snprintf(str, sizeof(str), "%pp:BAR%u", &pdev->sbdf, i); + + task->bars[i].mem = rangeset_new(pdev->domain, str, + RANGESETF_no_print); + + if ( !task->bars[i].mem ) + { + destroy_map_task(task); + return NULL; + } + + task->bars[i].addr = pdev->vpci->header.bars[i].addr; + task->bars[i].guest_addr = pdev->vpci->header.bars[i].guest_addr; + } + } + + task->pdev = pdev; + task->domain = pdev->domain; + task->cmd = cmd; + task->rom_only = rom_only; + + return task; +} + +static void defer_map(struct vpci_map_task *task) { struct vcpu *curr = current; @@ -293,9 +348,9 @@ static void defer_map(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) * is mapped. This can lead to parallel mapping operations being * started for the same device if the domain is not well-behaved. */ - curr->vpci.pdev = pdev; - curr->vpci.cmd = cmd; - curr->vpci.rom_only = rom_only; + + list_add_tail(&task->next, &curr->vpci.task_queue); + /* * Raise a scheduler softirq in order to prevent the guest from resuming * execution with pending mapping operations, to trigger the invocation @@ -310,11 +365,15 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) struct pci_dev *tmp; const struct domain *d; const struct vpci_msix *msix = pdev->vpci->msix; + struct vpci_map_task *task = alloc_map_task(pdev, cmd, rom_only); unsigned int i, j; int rc; ASSERT(rw_is_write_locked(&pdev->domain->pci_lock)); + if ( !task ) + return -ENOMEM; + /* * Create a rangeset per BAR that represents the current device memory * region and compare it against all the currently active BAR memory @@ -330,12 +389,13 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) for ( i = 0; i < ARRAY_SIZE(header->bars); i++ ) { struct vpci_bar *bar = &header->bars[i]; + struct rangeset *mem = task->bars[i].mem; unsigned long start = PFN_DOWN(bar->addr); unsigned long end = PFN_DOWN(bar->addr + bar->size - 1); unsigned long start_guest = PFN_DOWN(bar->guest_addr); unsigned long end_guest = PFN_DOWN(bar->guest_addr + bar->size - 1); - if ( !bar->mem ) + if ( !mem ) continue; if ( !MAPPABLE_BAR(bar) || @@ -353,7 +413,7 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) continue; } - ASSERT(rangeset_is_empty(bar->mem)); + ASSERT(rangeset_is_empty(mem)); /* * Make sure that the guest set address has the same page offset @@ -365,21 +425,23 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) gprintk(XENLOG_G_WARNING, "%pp: can't map BAR%u - offset mismatch: %#lx vs %#lx\n", &pdev->sbdf, i, bar->guest_addr, bar->addr); + destroy_map_task(task); return -EINVAL; } - rc = rangeset_add_range(bar->mem, start_guest, end_guest); + rc = rangeset_add_range(mem, start_guest, end_guest); if ( rc ) { printk(XENLOG_G_WARNING "Failed to add [%lx, %lx]: %d\n", start_guest, end_guest, rc); + destroy_map_task(task); return rc; } /* Check for overlap with the already setup BAR ranges. */ for ( j = 0; j < i; j++ ) { - struct vpci_bar *prev_bar = &header->bars[j]; + struct vpci_bar_map *prev_bar = &task->bars[j]; if ( rangeset_is_empty(prev_bar->mem) ) continue; @@ -390,16 +452,18 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) gprintk(XENLOG_WARNING, "%pp: failed to remove overlapping range [%lx, %lx]: %d\n", &pdev->sbdf, start_guest, end_guest, rc); + destroy_map_task(task); return rc; } } - rc = pci_sanitize_bar_memory(bar->mem); + rc = pci_sanitize_bar_memory(mem); if ( rc ) { gprintk(XENLOG_WARNING, "%pp: failed to sanitize BAR#%u memory: %d\n", &pdev->sbdf, i, rc); + destroy_map_task(task); return rc; } } @@ -413,7 +477,7 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) for ( j = 0; j < ARRAY_SIZE(header->bars); j++ ) { - const struct vpci_bar *bar = &header->bars[j]; + const struct vpci_bar_map *bar = &task->bars[j]; if ( rangeset_is_empty(bar->mem) ) continue; @@ -424,6 +488,7 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) gprintk(XENLOG_WARNING, "%pp: failed to remove MSIX table [%lx, %lx]: %d\n", &pdev->sbdf, start, end, rc); + destroy_map_task(task); return rc; } } @@ -468,8 +533,9 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) for ( j = 0; j < ARRAY_SIZE(header->bars); j++) { const struct vpci_bar *bar = &header->bars[j]; + struct rangeset *mem = task->bars[j].mem; - if ( !rangeset_overlaps_range(bar->mem, start, end) || + if ( !rangeset_overlaps_range(mem, start, end) || /* * If only the ROM enable bit is toggled check against * other BARs in the same device for overlaps, but not @@ -480,12 +546,13 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) bar->type == VPCI_BAR_ROM) ) continue; - rc = rangeset_remove_range(bar->mem, start, end); + rc = rangeset_remove_range(mem, start, end); if ( rc ) { gprintk(XENLOG_WARNING, "%pp: failed to remove [%lx, %lx]: %d\n", &pdev->sbdf, start, end, rc); + destroy_map_task(task); return rc; } } @@ -509,10 +576,12 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only) * will always be to establish mappings and process all the BARs. */ ASSERT((cmd & PCI_COMMAND_MEMORY) && !rom_only); - return apply_map(pdev->domain, pdev, cmd); + rc = apply_map(task); + destroy_map_task(task); + return rc; } - defer_map(pdev, cmd, rom_only); + defer_map(task); return 0; } @@ -731,18 +800,6 @@ static void cf_check rom_write( } } -static int bar_add_rangeset(const struct pci_dev *pdev, struct vpci_bar *bar, - unsigned int i) -{ - char str[32]; - - snprintf(str, sizeof(str), "%pp:BAR%u", &pdev->sbdf, i); - - bar->mem = rangeset_new(pdev->domain, str, RANGESETF_no_print); - - return !bar->mem ? -ENOMEM : 0; -} - static int vpci_init_capability_list(struct pci_dev *pdev) { int rc; @@ -947,10 +1004,6 @@ static int cf_check init_header(struct pci_dev *pdev) else bars[i].type = VPCI_BAR_MEM32; - rc = bar_add_rangeset(pdev, &bars[i], i); - if ( rc ) - goto fail; - rc = pci_size_mem_bar(pdev->sbdf, reg, &addr, &size, (i == num_bars - 1) ? PCI_BAR_LAST : 0); if ( rc < 0 ) @@ -1003,12 +1056,6 @@ static int cf_check init_header(struct pci_dev *pdev) 4, rom); if ( rc ) rom->type = VPCI_BAR_EMPTY; - else - { - rc = bar_add_rangeset(pdev, rom, num_bars); - if ( rc ) - goto fail; - } } else if ( !is_hwdom ) { diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c index 09988f04c27c..7177cfce355d 100644 --- a/xen/drivers/vpci/vpci.c +++ b/xen/drivers/vpci/vpci.c @@ -117,9 +117,6 @@ void vpci_deassign_device(struct pci_dev *pdev) iounmap(pdev->vpci->msix->table[i]); } - for ( i = 0; i < ARRAY_SIZE(pdev->vpci->header.bars); i++ ) - rangeset_destroy(pdev->vpci->header.bars[i].mem); - xfree(pdev->vpci->msix); xfree(pdev->vpci->msi); xfree(pdev->vpci); diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h index 6a481a4e89d3..c2e75076691f 100644 --- a/xen/include/xen/vpci.h +++ b/xen/include/xen/vpci.h @@ -103,7 +103,6 @@ struct vpci { uint64_t guest_addr; uint64_t size; uint64_t resizable_sizes; - struct rangeset *mem; enum { VPCI_BAR_EMPTY, VPCI_BAR_IO, @@ -194,14 +193,25 @@ struct vpci { #endif }; -struct vpci_vcpu { +#ifdef __XEN__ +struct vpci_map_task { /* Per-vcpu structure to store state while {un}mapping of PCI BARs. */ + struct list_head next; const struct pci_dev *pdev; + struct domain *domain; + struct vpci_bar_map { + uint64_t addr; + uint64_t guest_addr; + struct rangeset *mem; + } bars[PCI_HEADER_NORMAL_NR_BARS + 1]; uint16_t cmd; bool rom_only : 1; }; -#ifdef __XEN__ +struct vpci_vcpu { + struct list_head task_queue; +}; + void vpci_dump_msi(void); /* Make sure there's a hole in the p2m for the MSIX mmio areas. */ -- 2.50.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |