diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pci.c | 5 | ||||
-rw-r--r-- | drivers/xen/swiotlb-xen.c | 19 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/passthrough.c | 14 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/pci_stub.c | 112 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/pciback.h | 7 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/vpci.c | 14 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/xenbus.c | 4 |
7 files changed, 133 insertions, 42 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a7ac72639c52..cab05f31223f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1138,8 +1138,8 @@ EXPORT_SYMBOL_GPL(pci_store_saved_state); * @dev: PCI device that we're dealing with * @state: Saved state returned from pci_store_saved_state() */ -static int pci_load_saved_state(struct pci_dev *dev, - struct pci_saved_state *state) +int pci_load_saved_state(struct pci_dev *dev, + struct pci_saved_state *state) { struct pci_cap_saved_data *cap; @@ -1167,6 +1167,7 @@ static int pci_load_saved_state(struct pci_dev *dev, dev->state_saved = true; return 0; } +EXPORT_SYMBOL_GPL(pci_load_saved_state); /** * pci_load_and_free_saved_state - Reload the save state pointed to by state, diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index ebd8f218a788..810ad419e34c 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -96,8 +96,6 @@ static inline phys_addr_t xen_bus_to_phys(dma_addr_t baddr) dma_addr_t dma = (dma_addr_t)pfn << PAGE_SHIFT; phys_addr_t paddr = dma; - BUG_ON(paddr != dma); /* truncation has occurred, should never happen */ - paddr |= baddr & ~PAGE_MASK; return paddr; @@ -399,11 +397,13 @@ dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, * buffering it. */ if (dma_capable(dev, dev_addr, size) && - !range_straddles_page_boundary(phys, size) && !swiotlb_force) { + !range_straddles_page_boundary(phys, size) && + !xen_arch_need_swiotlb(dev, PFN_DOWN(phys), PFN_DOWN(dev_addr)) && + !swiotlb_force) { /* we are not interested in the dma_addr returned by * xen_dma_map_page, only in the potential cache flushes executed * by the function. */ - xen_dma_map_page(dev, page, offset, size, dir, attrs); + xen_dma_map_page(dev, page, dev_addr, offset, size, dir, attrs); return dev_addr; } @@ -417,7 +417,7 @@ dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, return DMA_ERROR_CODE; xen_dma_map_page(dev, pfn_to_page(map >> PAGE_SHIFT), - map & ~PAGE_MASK, size, dir, attrs); + dev_addr, map & ~PAGE_MASK, size, dir, attrs); dev_addr = xen_phys_to_bus(map); /* @@ -447,7 +447,7 @@ static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr, BUG_ON(dir == DMA_NONE); - xen_dma_unmap_page(hwdev, paddr, size, dir, attrs); + xen_dma_unmap_page(hwdev, dev_addr, size, dir, attrs); /* NOTE: We use dev_addr here, not paddr! */ if (is_xen_swiotlb_buffer(dev_addr)) { @@ -495,14 +495,14 @@ xen_swiotlb_sync_single(struct device *hwdev, dma_addr_t dev_addr, BUG_ON(dir == DMA_NONE); if (target == SYNC_FOR_CPU) - xen_dma_sync_single_for_cpu(hwdev, paddr, size, dir); + xen_dma_sync_single_for_cpu(hwdev, dev_addr, size, dir); /* NOTE: We use dev_addr here, not paddr! */ if (is_xen_swiotlb_buffer(dev_addr)) swiotlb_tbl_sync_single(hwdev, paddr, size, dir, target); if (target == SYNC_FOR_DEVICE) - xen_dma_sync_single_for_cpu(hwdev, paddr, size, dir); + xen_dma_sync_single_for_device(hwdev, dev_addr, size, dir); if (dir != DMA_FROM_DEVICE) return; @@ -557,6 +557,7 @@ xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, dma_addr_t dev_addr = xen_phys_to_bus(paddr); if (swiotlb_force || + xen_arch_need_swiotlb(hwdev, PFN_DOWN(paddr), PFN_DOWN(dev_addr)) || !dma_capable(hwdev, dev_addr, sg->length) || range_straddles_page_boundary(paddr, sg->length)) { phys_addr_t map = swiotlb_tbl_map_single(hwdev, @@ -574,6 +575,7 @@ xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, return 0; } xen_dma_map_page(hwdev, pfn_to_page(map >> PAGE_SHIFT), + dev_addr, map & ~PAGE_MASK, sg->length, dir, @@ -584,6 +586,7 @@ xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, * xen_dma_map_page, only in the potential cache flushes executed * by the function. */ xen_dma_map_page(hwdev, pfn_to_page(paddr >> PAGE_SHIFT), + dev_addr, paddr & ~PAGE_MASK, sg->length, dir, diff --git a/drivers/xen/xen-pciback/passthrough.c b/drivers/xen/xen-pciback/passthrough.c index 828dddc360df..f16a30e2a110 100644 --- a/drivers/xen/xen-pciback/passthrough.c +++ b/drivers/xen/xen-pciback/passthrough.c @@ -69,7 +69,7 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, } static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, - struct pci_dev *dev) + struct pci_dev *dev, bool lock) { struct passthrough_dev_data *dev_data = pdev->pci_dev_data; struct pci_dev_entry *dev_entry, *t; @@ -87,8 +87,13 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, mutex_unlock(&dev_data->lock); - if (found_dev) + if (found_dev) { + if (lock) + device_lock(&found_dev->dev); pcistub_put_pci_dev(found_dev); + if (lock) + device_unlock(&found_dev->dev); + } } static int __xen_pcibk_init_devices(struct xen_pcibk_device *pdev) @@ -156,8 +161,11 @@ static void __xen_pcibk_release_devices(struct xen_pcibk_device *pdev) struct pci_dev_entry *dev_entry, *t; list_for_each_entry_safe(dev_entry, t, &dev_data->dev_list, list) { + struct pci_dev *dev = dev_entry->dev; list_del(&dev_entry->list); - pcistub_put_pci_dev(dev_entry->dev); + device_lock(&dev->dev); + pcistub_put_pci_dev(dev); + device_unlock(&dev->dev); kfree(dev_entry); } diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index 017069a455d4..cc3cbb4435f8 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -105,7 +105,7 @@ static void pcistub_device_release(struct kref *kref) */ __pci_reset_function_locked(dev); if (pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state)) - dev_dbg(&dev->dev, "Could not reload PCI state\n"); + dev_info(&dev->dev, "Could not reload PCI state\n"); else pci_restore_state(dev); @@ -250,11 +250,15 @@ struct pci_dev *pcistub_get_pci_dev(struct xen_pcibk_device *pdev, * - 'echo BDF > unbind' with a guest still using it. See pcistub_remove * * As such we have to be careful. + * + * To make this easier, the caller has to hold the device lock. */ void pcistub_put_pci_dev(struct pci_dev *dev) { struct pcistub_device *psdev, *found_psdev = NULL; unsigned long flags; + struct xen_pcibk_dev_data *dev_data; + int ret; spin_lock_irqsave(&pcistub_devices_lock, flags); @@ -276,13 +280,20 @@ void pcistub_put_pci_dev(struct pci_dev *dev) /* Cleanup our device * (so it's ready for the next domain) */ + device_lock_assert(&dev->dev); + __pci_reset_function_locked(dev); - /* This is OK - we are running from workqueue context - * and want to inhibit the user from fiddling with 'reset' - */ - pci_reset_function(dev); - pci_restore_state(dev); - + dev_data = pci_get_drvdata(dev); + ret = pci_load_saved_state(dev, dev_data->pci_saved_state); + if (!ret) { + /* + * The usual sequence is pci_save_state & pci_restore_state + * but the guest might have messed the configuration space up. + * Use the initial version (when device was bound to us). + */ + pci_restore_state(dev); + } else + dev_info(&dev->dev, "Could not reload PCI state\n"); /* This disables the device. */ xen_pcibk_reset_device(dev); @@ -554,12 +565,14 @@ static void pcistub_remove(struct pci_dev *dev) spin_unlock_irqrestore(&pcistub_devices_lock, flags); if (found_psdev) { - dev_dbg(&dev->dev, "found device to remove - in use? %p\n", - found_psdev->pdev); + dev_dbg(&dev->dev, "found device to remove %s\n", + found_psdev->pdev ? "- in-use" : ""); if (found_psdev->pdev) { - pr_warn("****** removing device %s while still in-use! ******\n", - pci_name(found_psdev->dev)); + int domid = xen_find_device_domain_owner(dev); + + pr_warn("****** removing device %s while still in-use by domain %d! ******\n", + pci_name(found_psdev->dev), domid); pr_warn("****** driver domain may still access this device's i/o resources!\n"); pr_warn("****** shutdown driver domain before binding device\n"); pr_warn("****** to other drivers or domains\n"); @@ -567,7 +580,8 @@ static void pcistub_remove(struct pci_dev *dev) /* N.B. This ends up calling pcistub_put_pci_dev which ends up * doing the FLR. */ xen_pcibk_release_pci_dev(found_psdev->pdev, - found_psdev->dev); + found_psdev->dev, + false /* caller holds the lock. */); } spin_lock_irqsave(&pcistub_devices_lock, flags); @@ -629,10 +643,12 @@ static pci_ers_result_t common_process(struct pcistub_device *psdev, { pci_ers_result_t res = result; struct xen_pcie_aer_op *aer_op; + struct xen_pcibk_device *pdev = psdev->pdev; + struct xen_pci_sharedinfo *sh_info = pdev->sh_info; int ret; /*with PV AER drivers*/ - aer_op = &(psdev->pdev->sh_info->aer_op); + aer_op = &(sh_info->aer_op); aer_op->cmd = aer_cmd ; /*useful for error_detected callback*/ aer_op->err = state; @@ -653,36 +669,36 @@ static pci_ers_result_t common_process(struct pcistub_device *psdev, * this flag to judge whether we need to check pci-front give aer * service ack signal */ - set_bit(_PCIB_op_pending, (unsigned long *)&psdev->pdev->flags); + set_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags); /*It is possible that a pcifront conf_read_write ops request invokes * the callback which cause the spurious execution of wake_up. * Yet it is harmless and better than a spinlock here */ set_bit(_XEN_PCIB_active, - (unsigned long *)&psdev->pdev->sh_info->flags); + (unsigned long *)&sh_info->flags); wmb(); - notify_remote_via_irq(psdev->pdev->evtchn_irq); + notify_remote_via_irq(pdev->evtchn_irq); ret = wait_event_timeout(xen_pcibk_aer_wait_queue, !(test_bit(_XEN_PCIB_active, (unsigned long *) - &psdev->pdev->sh_info->flags)), 300*HZ); + &sh_info->flags)), 300*HZ); if (!ret) { if (test_bit(_XEN_PCIB_active, - (unsigned long *)&psdev->pdev->sh_info->flags)) { + (unsigned long *)&sh_info->flags)) { dev_err(&psdev->dev->dev, "pcifront aer process not responding!\n"); clear_bit(_XEN_PCIB_active, - (unsigned long *)&psdev->pdev->sh_info->flags); + (unsigned long *)&sh_info->flags); aer_op->err = PCI_ERS_RESULT_NONE; return res; } } - clear_bit(_PCIB_op_pending, (unsigned long *)&psdev->pdev->flags); + clear_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags); if (test_bit(_XEN_PCIF_active, - (unsigned long *)&psdev->pdev->sh_info->flags)) { + (unsigned long *)&sh_info->flags)) { dev_dbg(&psdev->dev->dev, "schedule pci_conf service in " DRV_NAME "\n"); xen_pcibk_test_and_schedule_op(psdev->pdev); @@ -1502,6 +1518,53 @@ parse_error: fs_initcall(pcistub_init); #endif +#ifdef CONFIG_PCI_IOV +static struct pcistub_device *find_vfs(const struct pci_dev *pdev) +{ + struct pcistub_device *psdev = NULL; + unsigned long flags; + bool found = false; + + spin_lock_irqsave(&pcistub_devices_lock, flags); + list_for_each_entry(psdev, &pcistub_devices, dev_list) { + if (!psdev->pdev && psdev->dev != pdev + && pci_physfn(psdev->dev) == pdev) { + found = true; + break; + } + } + spin_unlock_irqrestore(&pcistub_devices_lock, flags); + if (found) + return psdev; + return NULL; +} + +static int pci_stub_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + const struct pci_dev *pdev = to_pci_dev(dev); + + if (action != BUS_NOTIFY_UNBIND_DRIVER) + return NOTIFY_DONE; + + if (!pdev->is_physfn) + return NOTIFY_DONE; + + for (;;) { + struct pcistub_device *psdev = find_vfs(pdev); + if (!psdev) + break; + device_release_driver(&psdev->dev->dev); + } + return NOTIFY_DONE; +} + +static struct notifier_block pci_stub_nb = { + .notifier_call = pci_stub_notifier, +}; +#endif + static int __init xen_pcibk_init(void) { int err; @@ -1523,12 +1586,19 @@ static int __init xen_pcibk_init(void) err = xen_pcibk_xenbus_register(); if (err) pcistub_exit(); +#ifdef CONFIG_PCI_IOV + else + bus_register_notifier(&pci_bus_type, &pci_stub_nb); +#endif return err; } static void __exit xen_pcibk_cleanup(void) { +#ifdef CONFIG_PCI_IOV + bus_unregister_notifier(&pci_bus_type, &pci_stub_nb); +#endif xen_pcibk_xenbus_unregister(); pcistub_exit(); } diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h index f72af87640e0..58e38d586f52 100644 --- a/drivers/xen/xen-pciback/pciback.h +++ b/drivers/xen/xen-pciback/pciback.h @@ -99,7 +99,8 @@ struct xen_pcibk_backend { unsigned int *domain, unsigned int *bus, unsigned int *devfn); int (*publish)(struct xen_pcibk_device *pdev, publish_pci_root_cb cb); - void (*release)(struct xen_pcibk_device *pdev, struct pci_dev *dev); + void (*release)(struct xen_pcibk_device *pdev, struct pci_dev *dev, + bool lock); int (*add)(struct xen_pcibk_device *pdev, struct pci_dev *dev, int devid, publish_pci_dev_cb publish_cb); struct pci_dev *(*get)(struct xen_pcibk_device *pdev, @@ -122,10 +123,10 @@ static inline int xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, } static inline void xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, - struct pci_dev *dev) + struct pci_dev *dev, bool lock) { if (xen_pcibk_backend && xen_pcibk_backend->release) - return xen_pcibk_backend->release(pdev, dev); + return xen_pcibk_backend->release(pdev, dev, lock); } static inline struct pci_dev * diff --git a/drivers/xen/xen-pciback/vpci.c b/drivers/xen/xen-pciback/vpci.c index 51afff96c515..c99f8bb1c56c 100644 --- a/drivers/xen/xen-pciback/vpci.c +++ b/drivers/xen/xen-pciback/vpci.c @@ -145,7 +145,7 @@ out: } static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, - struct pci_dev *dev) + struct pci_dev *dev, bool lock) { int slot; struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; @@ -169,8 +169,13 @@ static void __xen_pcibk_release_pci_dev(struct xen_pcibk_device *pdev, out: mutex_unlock(&vpci_dev->lock); - if (found_dev) + if (found_dev) { + if (lock) + device_lock(&found_dev->dev); pcistub_put_pci_dev(found_dev); + if (lock) + device_unlock(&found_dev->dev); + } } static int __xen_pcibk_init_devices(struct xen_pcibk_device *pdev) @@ -208,8 +213,11 @@ static void __xen_pcibk_release_devices(struct xen_pcibk_device *pdev) struct pci_dev_entry *e, *tmp; list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot], list) { + struct pci_dev *dev = e->dev; list_del(&e->list); - pcistub_put_pci_dev(e->dev); + device_lock(&dev->dev); + pcistub_put_pci_dev(dev); + device_unlock(&dev->dev); kfree(e); } } diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index ad8d30c088fe..fe17c80ff4b7 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -247,7 +247,7 @@ static int xen_pcibk_export_device(struct xen_pcibk_device *pdev, if (err) goto out; - dev_dbg(&dev->dev, "registering for %d\n", pdev->xdev->otherend_id); + dev_info(&dev->dev, "registering for %d\n", pdev->xdev->otherend_id); if (xen_register_device_domain_owner(dev, pdev->xdev->otherend_id) != 0) { dev_err(&dev->dev, "Stealing ownership from dom%d.\n", @@ -291,7 +291,7 @@ static int xen_pcibk_remove_device(struct xen_pcibk_device *pdev, /* N.B. This ends up calling pcistub_put_pci_dev which ends up * doing the FLR. */ - xen_pcibk_release_pci_dev(pdev, dev); + xen_pcibk_release_pci_dev(pdev, dev, true /* use the lock. */); out: return err; |