[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 24/31] Rename the drivers/xen/pcifront/* driver to drivers/pci/xen-pcifront.c.
Also the Kconfig and Makefile are appropiately updated. This is the last step in the rename process. Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx> --- drivers/pci/Kconfig | 9 + drivers/pci/Makefile | 2 + drivers/pci/xen-pcifront.c | 1117 +++++++++++++++++++++++++++++++++++++++++ drivers/xen/Kconfig | 17 - drivers/xen/Makefile | 1 - drivers/xen/pcifront/Makefile | 7 - drivers/xen/pcifront/xenbus.c | 1117 ----------------------------------------- 7 files changed, 1128 insertions(+), 1142 deletions(-) create mode 100644 drivers/pci/xen-pcifront.c delete mode 100644 drivers/xen/pcifront/Makefile delete mode 100644 drivers/xen/pcifront/xenbus.c diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index fdc864f..97a59d5 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -51,6 +51,15 @@ config PCI_STUB When in doubt, say N. +config XEN_PCIDEV_FRONTEND + bool "Xen PCI Frontend" + depends on PCI && X86_64 + select HOTPLUG + default y + help + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + config HT_IRQ bool "Interrupts on hypertransport devices" default y diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 3590082..f44cc7c 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -59,6 +59,8 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o obj-$(CONFIG_PCI_STUB) += pci-stub.o +obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o + ifeq ($(CONFIG_PCI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c new file mode 100644 index 0000000..854850d --- /dev/null +++ b/drivers/pci/xen-pcifront.c @@ -0,0 +1,1117 @@ +/* + * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn) + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <xen/xenbus.h> +#include <xen/events.h> +#include <xen/grant_table.h> +#include <xen/page.h> +#include <linux/spinlock.h> +#include <linux/pci.h> +#include <xen/xenbus.h> +#include <xen/interface/io/pciif.h> +#include <linux/interrupt.h> +#include <asm/atomic.h> +#include <linux/workqueue.h> +#include <asm/bitops.h> +#include <linux/time.h> + + +#ifndef __init_refok +#define __init_refok +#endif + +#define INVALID_GRANT_REF (0) +#define INVALID_EVTCHN (-1) + + +struct pci_bus_entry { + struct list_head list; + struct pci_bus *bus; +}; + +#define _PDEVB_op_active (0) +#define PDEVB_op_active (1 << (_PDEVB_op_active)) + +struct pcifront_device { + struct xenbus_device *xdev; + struct list_head root_buses; + spinlock_t dev_lock; + + int evtchn; + int gnt_ref; + + int irq; + + /* Lock this when doing any operations in sh_info */ + spinlock_t sh_info_lock; + struct xen_pci_sharedinfo *sh_info; + struct work_struct op_work; + unsigned long flags; + +}; + +struct pcifront_sd { + int domain; + struct pcifront_device *pdev; +}; + +static inline struct pcifront_device * +pcifront_get_pdev(struct pcifront_sd *sd) +{ + return sd->pdev; +} + +static inline void pcifront_init_sd(struct pcifront_sd *sd, + unsigned int domain, unsigned int bus, + struct pcifront_device *pdev) +{ + sd->domain = domain; + sd->pdev = pdev; +} + +static inline void pcifront_setup_root_resources(struct pci_bus *bus, + struct pcifront_sd *sd) +{ +} + + +DEFINE_SPINLOCK(pcifront_dev_lock); +static struct pcifront_device *pcifront_dev = NULL; + +static int verbose_request = 0; +module_param(verbose_request, int, 0644); + +static int errno_to_pcibios_err(int errno) +{ + switch (errno) { + case XEN_PCI_ERR_success: + return PCIBIOS_SUCCESSFUL; + + case XEN_PCI_ERR_dev_not_found: + return PCIBIOS_DEVICE_NOT_FOUND; + + case XEN_PCI_ERR_invalid_offset: + case XEN_PCI_ERR_op_failed: + return PCIBIOS_BAD_REGISTER_NUMBER; + + case XEN_PCI_ERR_not_implemented: + return PCIBIOS_FUNC_NOT_SUPPORTED; + + case XEN_PCI_ERR_access_denied: + return PCIBIOS_SET_FAILED; + } + return errno; +} + +static inline void schedule_pcifront_aer_op(struct pcifront_device *pdev) +{ + if (test_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags) + && !test_and_set_bit(_PDEVB_op_active, &pdev->flags)) { + dev_dbg(&pdev->xdev->dev, "schedule aer frontend job\n"); + schedule_work(&pdev->op_work); + } +} + +static int do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op) +{ + int err = 0; + struct xen_pci_op *active_op = &pdev->sh_info->op; + unsigned long irq_flags; + evtchn_port_t port = pdev->evtchn; + unsigned irq = pdev->irq; + s64 ns, ns_timeout; + struct timeval tv; + + spin_lock_irqsave(&pdev->sh_info_lock, irq_flags); + + memcpy(active_op, op, sizeof(struct xen_pci_op)); + + /* Go */ + wmb(); + set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); + notify_remote_via_evtchn(port); + + /* + * We set a poll timeout of 3 seconds but give up on return after + * 2 seconds. It is better to time out too late rather than too early + * (in the latter case we end up continually re-executing poll() with a + * timeout in the past). 1s difference gives plenty of slack for error. + */ + do_gettimeofday(&tv); + ns_timeout = timeval_to_ns(&tv) + 2 * (s64)NSEC_PER_SEC; + + xen_clear_irq_pending(irq); + + while (test_bit(_XEN_PCIF_active, + (unsigned long *)&pdev->sh_info->flags)) { + xen_poll_irq_timeout(irq, jiffies + 3*HZ); + xen_clear_irq_pending(irq); + do_gettimeofday(&tv); + ns = timeval_to_ns(&tv); + if (ns > ns_timeout) { + dev_err(&pdev->xdev->dev, + "pciback not responding!!!\n"); + clear_bit(_XEN_PCIF_active, + (unsigned long *)&pdev->sh_info->flags); + err = XEN_PCI_ERR_dev_not_found; + goto out; + } + } + + /* + * We might lose backend service request since we + * reuse same evtchn with pci_conf backend response. So re-schedule + * aer pcifront service. + */ + if (test_bit(_XEN_PCIB_active, + (unsigned long*)&pdev->sh_info->flags)) { + dev_err(&pdev->xdev->dev, + "schedule aer pcifront service\n"); + schedule_pcifront_aer_op(pdev); + } + + memcpy(op, active_op, sizeof(struct xen_pci_op)); + + err = op->err; + out: + spin_unlock_irqrestore(&pdev->sh_info_lock, irq_flags); + return err; +} + +/* Access to this function is spinlocked in drivers/pci/access.c */ +static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + int err = 0; + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_conf_read, + .domain = pci_domain_nr(bus), + .bus = bus->number, + .devfn = devfn, + .offset = where, + .size = size, + }; + struct pcifront_sd *sd = bus->sysdata; + struct pcifront_device *pdev = pcifront_get_pdev(sd); + + if (verbose_request) + dev_info(&pdev->xdev->dev, + "read dev=%04x:%02x:%02x.%01x - offset %x size %d\n", + pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), where, size); + + err = do_pci_op(pdev, &op); + + if (likely(!err)) { + if (verbose_request) + dev_info(&pdev->xdev->dev, "read got back value %x\n", + op.value); + + *val = op.value; + } else if (err == -ENODEV) { + /* No device here, pretend that it just returned 0 */ + err = 0; + *val = 0; + } + + return errno_to_pcibios_err(err); +} + +/* Access to this function is spinlocked in drivers/pci/access.c */ +static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_conf_write, + .domain = pci_domain_nr(bus), + .bus = bus->number, + .devfn = devfn, + .offset = where, + .size = size, + .value = val, + }; + struct pcifront_sd *sd = bus->sysdata; + struct pcifront_device *pdev = pcifront_get_pdev(sd); + + if (verbose_request) + dev_info(&pdev->xdev->dev, + "write dev=%04x:%02x:%02x.%01x - " + "offset %x size %d val %x\n", + pci_domain_nr(bus), bus->number, + PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val); + + return errno_to_pcibios_err(do_pci_op(pdev, &op)); +} + +struct pci_ops pcifront_bus_ops = { + .read = pcifront_bus_read, + .write = pcifront_bus_write, +}; + +#ifdef CONFIG_PCI_MSI +int pci_frontend_enable_msix(struct pci_dev *dev, + struct msix_entry *entries, + int nvec) +{ + int err; + int i; + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_enable_msix, + .domain = pci_domain_nr(dev->bus), + .bus = dev->bus->number, + .devfn = dev->devfn, + .value = nvec, + }; + struct pcifront_sd *sd = dev->bus->sysdata; + struct pcifront_device *pdev = pcifront_get_pdev(sd); + + if (nvec > SH_INFO_MAX_VEC) { + printk("too much vector for pci frontend%x\n", nvec); + return -EINVAL; + } + + for (i = 0; i < nvec; i++) { + op.msix_entries[i].entry = entries[i].entry; + op.msix_entries[i].vector = entries[i].vector; + } + + err = do_pci_op(pdev, &op); + + if (!err) { + if (!op.value) { + /* we get the result */ + for ( i = 0; i < nvec; i++) + entries[i].vector = op.msix_entries[i].vector; + return 0; + } + else { + printk("enable msix get value %x\n", op.value); + return op.value; + } + } + else { + printk("enable msix get err %x\n", err); + return err; + } +} + +void pci_frontend_disable_msix(struct pci_dev* dev) +{ + int err; + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_disable_msix, + .domain = pci_domain_nr(dev->bus), + .bus = dev->bus->number, + .devfn = dev->devfn, + }; + struct pcifront_sd *sd = dev->bus->sysdata; + struct pcifront_device *pdev = pcifront_get_pdev(sd); + + err = do_pci_op(pdev, &op); + + /* What should do for error ? */ + if (err) + printk("pci_disable_msix get err %x\n", err); +} + +int pci_frontend_enable_msi(struct pci_dev *dev) +{ + int err; + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_enable_msi, + .domain = pci_domain_nr(dev->bus), + .bus = dev->bus->number, + .devfn = dev->devfn, + }; + struct pcifront_sd *sd = dev->bus->sysdata; + struct pcifront_device *pdev = pcifront_get_pdev(sd); + + err = do_pci_op(pdev, &op); + if (likely(!err)) { + dev->irq = op.value; + } + else { + printk("pci frontend enable msi failed for dev %x:%x \n", + op.bus, op.devfn); + err = -EINVAL; + } + return err; +} + +void pci_frontend_disable_msi(struct pci_dev* dev) +{ + int err; + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_disable_msi, + .domain = pci_domain_nr(dev->bus), + .bus = dev->bus->number, + .devfn = dev->devfn, + }; + struct pcifront_sd *sd = dev->bus->sysdata; + struct pcifront_device *pdev = pcifront_get_pdev(sd); + + err = do_pci_op(pdev, &op); + if (err == XEN_PCI_ERR_dev_not_found) { + /* XXX No response from backend, what shall we do? */ + printk("get no response from backend for disable MSI\n"); + return; + } + if (likely(!err)) + dev->irq = op.value; + else + /* how can pciback notify us fail? */ + printk("get fake response frombackend \n"); +} +#endif /* CONFIG_PCI_MSI */ + +/* Claim resources for the PCI frontend as-is, backend won't allow changes */ +static int pcifront_claim_resource(struct pci_dev *dev, void *data) +{ + struct pcifront_device *pdev = data; + int i; + struct resource *r; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + + if (!r->parent && r->start && r->flags) { + dev_dbg(&pdev->xdev->dev, "claiming resource %s/%d\n", + pci_name(dev), i); + pci_claim_resource(dev, i); + } + } + + return 0; +} + +int __devinit pcifront_scan_root(struct pcifront_device *pdev, + unsigned int domain, unsigned int bus) +{ + struct pci_bus *b; + struct pcifront_sd *sd = NULL; + struct pci_bus_entry *bus_entry = NULL; + int err = 0; + +#ifndef CONFIG_PCI_DOMAINS + if (domain != 0) { + dev_err(&pdev->xdev->dev, + "PCI Root in non-zero PCI Domain! domain=%d\n", domain); + dev_err(&pdev->xdev->dev, + "Please compile with CONFIG_PCI_DOMAINS\n"); + err = -EINVAL; + goto err_out; + } +#endif + + dev_info(&pdev->xdev->dev, "Creating PCI Frontend Bus %04x:%02x\n", + domain, bus); + + bus_entry = kmalloc(sizeof(*bus_entry), GFP_KERNEL); + sd = kmalloc(sizeof(*sd), GFP_KERNEL); + if (!bus_entry || !sd) { + err = -ENOMEM; + goto err_out; + } + pcifront_init_sd(sd, domain, bus, pdev); + + b = pci_scan_bus_parented(&pdev->xdev->dev, bus, + &pcifront_bus_ops, sd); + if (!b) { + dev_err(&pdev->xdev->dev, + "Error creating PCI Frontend Bus!\n"); + err = -ENOMEM; + goto err_out; + } + + pcifront_setup_root_resources(b, sd); + bus_entry->bus = b; + + list_add(&bus_entry->list, &pdev->root_buses); + + /* Claim resources before going "live" with our devices */ + pci_walk_bus(b, pcifront_claim_resource, pdev); + + pci_bus_add_devices(b); + + return 0; + + err_out: + kfree(bus_entry); + kfree(sd); + + return err; +} + +int __devinit pcifront_rescan_root(struct pcifront_device *pdev, + unsigned int domain, unsigned int bus) +{ + struct pci_bus *b; + struct pci_dev *d; + unsigned int devfn; + int err; + +#ifndef CONFIG_PCI_DOMAINS + if (domain != 0) { + dev_err(&pdev->xdev->dev, + "PCI Root in non-zero PCI Domain! domain=%d\n", domain); + dev_err(&pdev->xdev->dev, + "Please compile with CONFIG_PCI_DOMAINS\n"); + return -EINVAL; + } +#endif + + dev_info(&pdev->xdev->dev, "Rescanning PCI Frontend Bus %04x:%02x\n", + domain, bus); + + b = pci_find_bus(domain, bus); + if(!b) + /* If the bus is unknown, create it. */ + return pcifront_scan_root(pdev, domain, bus); + + /* Rescan the bus for newly attached functions and add. + * We omit handling of PCI bridge attachment because pciback prevents + * bridges from being exported. + */ + for (devfn = 0; devfn < 0x100; devfn++) { + d = pci_get_slot(b, devfn); + if(d) { + /* Device is already known. */ + pci_dev_put(d); + continue; + } + + d = pci_scan_single_device(b, devfn); + if (d) { + dev_info(&pdev->xdev->dev, "New device on " + "%04x:%02x:%02x.%02x found.\n", domain, bus, + PCI_SLOT(devfn), PCI_FUNC(devfn)); + err = pci_bus_add_device(d); + if (err) { + dev_err(&pdev->xdev->dev, "Failed to add " + " device to bus.\n"); + return err; + } + + } + } + + return 0; +} + +static void free_root_bus_devs(struct pci_bus *bus) +{ + struct pci_dev *dev; + + while (!list_empty(&bus->devices)) { + dev = container_of(bus->devices.next, struct pci_dev, + bus_list); + dev_dbg(&dev->dev, "removing device\n"); + pci_remove_bus_device(dev); + } +} + +void pcifront_free_roots(struct pcifront_device *pdev) +{ + struct pci_bus_entry *bus_entry, *t; + + dev_dbg(&pdev->xdev->dev, "cleaning up root buses\n"); + + list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) { + list_del(&bus_entry->list); + + free_root_bus_devs(bus_entry->bus); + + kfree(bus_entry->bus->sysdata); + + device_unregister(bus_entry->bus->bridge); + pci_remove_bus(bus_entry->bus); + + kfree(bus_entry); + } +} + +static pci_ers_result_t pcifront_common_process( int cmd, struct pcifront_device *pdev, + pci_channel_state_t state) +{ + pci_ers_result_t result; + struct pci_driver *pdrv; + int bus = pdev->sh_info->aer_op.bus; + int devfn = pdev->sh_info->aer_op.devfn; + struct pci_dev *pcidev; + int flag = 0; + + dev_dbg(&pdev->xdev->dev, + "pcifront AER process: cmd %x (bus:%x, devfn%x)", + cmd, bus, devfn); + result = PCI_ERS_RESULT_NONE; + + pcidev = pci_get_bus_and_slot(bus, devfn); + if (!pcidev || !pcidev->driver){ + dev_err(&pcidev->dev, + "device or driver is NULL\n"); + return result; + } + pdrv = pcidev->driver; + + if (get_driver(&pdrv->driver)) { + if (pdrv->err_handler && pdrv->err_handler->error_detected) { + dev_dbg(&pcidev->dev, + "trying to call AER service\n"); + if (pcidev) { + flag = 1; + switch(cmd) { + case XEN_PCI_OP_aer_detected: + result = pdrv->err_handler->error_detected(pcidev, state); + break; + case XEN_PCI_OP_aer_mmio: + result = pdrv->err_handler->mmio_enabled(pcidev); + break; + case XEN_PCI_OP_aer_slotreset: + result = pdrv->err_handler->slot_reset(pcidev); + break; + case XEN_PCI_OP_aer_resume: + pdrv->err_handler->resume(pcidev); + break; + default: + dev_err(&pdev->xdev->dev, + "bad request in aer recovery operation!\n"); + + } + } + } + put_driver(&pdrv->driver); + } + if (!flag) + result = PCI_ERS_RESULT_NONE; + + return result; +} + + +void pcifront_do_aer(struct work_struct *data) +{ + struct pcifront_device *pdev = container_of(data, struct pcifront_device, op_work); + int cmd = pdev->sh_info->aer_op.cmd; + pci_channel_state_t state = + (pci_channel_state_t)pdev->sh_info->aer_op.err; + + /*If a pci_conf op is in progress, + we have to wait until it is done before service aer op*/ + dev_dbg(&pdev->xdev->dev, + "pcifront service aer bus %x devfn %x\n", pdev->sh_info->aer_op.bus, + pdev->sh_info->aer_op.devfn); + + pdev->sh_info->aer_op.err = pcifront_common_process(cmd, pdev, state); + + wmb(); + clear_bit(_XEN_PCIB_active, (unsigned long*)&pdev->sh_info->flags); + notify_remote_via_evtchn(pdev->evtchn); + + /*in case of we lost an aer request in four lines time_window*/ + smp_mb__before_clear_bit(); + clear_bit( _PDEVB_op_active, &pdev->flags); + smp_mb__after_clear_bit(); + + schedule_pcifront_aer_op(pdev); + +} + +irqreturn_t pcifront_handler_aer(int irq, void *dev) +{ + struct pcifront_device *pdev = dev; + schedule_pcifront_aer_op(pdev); + return IRQ_HANDLED; +} +int pcifront_connect(struct pcifront_device *pdev) +{ + int err = 0; + + spin_lock(&pcifront_dev_lock); + + if (!pcifront_dev) { + dev_info(&pdev->xdev->dev, "Installing PCI frontend\n"); + pcifront_dev = pdev; + } + else { + dev_err(&pdev->xdev->dev, "PCI frontend already installed!\n"); + err = -EEXIST; + } + + spin_unlock(&pcifront_dev_lock); + + return err; +} + +void pcifront_disconnect(struct pcifront_device *pdev) +{ + spin_lock(&pcifront_dev_lock); + + if (pdev == pcifront_dev) { + dev_info(&pdev->xdev->dev, + "Disconnecting PCI Frontend Buses\n"); + pcifront_dev = NULL; + } + + spin_unlock(&pcifront_dev_lock); +} +static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev) +{ + struct pcifront_device *pdev; + + pdev = kzalloc(sizeof(struct pcifront_device), GFP_KERNEL); + if (pdev == NULL) + goto out; + + pdev->sh_info = + (struct xen_pci_sharedinfo *)__get_free_page(GFP_KERNEL); + if (pdev->sh_info == NULL) { + kfree(pdev); + pdev = NULL; + goto out; + } + pdev->sh_info->flags = 0; + + /*Flag for registering PV AER handler*/ + set_bit(_XEN_PCIB_AERHANDLER, (void*)&pdev->sh_info->flags); + + xdev->dev.driver_data = pdev; + pdev->xdev = xdev; + + INIT_LIST_HEAD(&pdev->root_buses); + + spin_lock_init(&pdev->dev_lock); + spin_lock_init(&pdev->sh_info_lock); + + pdev->evtchn = INVALID_EVTCHN; + pdev->gnt_ref = INVALID_GRANT_REF; + pdev->irq = -1; + + INIT_WORK(&pdev->op_work, pcifront_do_aer); + + dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n", + pdev, pdev->sh_info); + out: + return pdev; +} + +static void free_pdev(struct pcifront_device *pdev) +{ + dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev); + + pcifront_free_roots(pdev); + + /*For PCIE_AER error handling job*/ + flush_scheduled_work(); + unbind_from_irqhandler(pdev->irq, pdev); + + if (pdev->evtchn != INVALID_EVTCHN) + xenbus_free_evtchn(pdev->xdev, pdev->evtchn); + + if (pdev->gnt_ref != INVALID_GRANT_REF) + gnttab_end_foreign_access(pdev->gnt_ref, 0 /* r/w page */, + (unsigned long)pdev->sh_info); + + pdev->xdev->dev.driver_data = NULL; + + kfree(pdev); +} + +static int pcifront_publish_info(struct pcifront_device *pdev) +{ + int err = 0; + struct xenbus_transaction trans; + + err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info)); + if (err < 0) + goto out; + + pdev->gnt_ref = err; + + err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn); + if (err) + goto out; + + err = bind_evtchn_to_irqhandler(pdev->evtchn, pcifront_handler_aer, + 0, "pcifront", pdev); + if (err < 0) { + xenbus_free_evtchn(pdev->xdev, pdev->evtchn); + xenbus_dev_fatal(pdev->xdev, err, "Failed to bind evtchn to " + "irqhandler.\n"); + return err; + } + pdev->irq = err; + + do_publish: + err = xenbus_transaction_start(&trans); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error writing configuration for backend " + "(start transaction)"); + goto out; + } + + err = xenbus_printf(trans, pdev->xdev->nodename, + "pci-op-ref", "%u", pdev->gnt_ref); + if (!err) + err = xenbus_printf(trans, pdev->xdev->nodename, + "event-channel", "%u", pdev->evtchn); + if (!err) + err = xenbus_printf(trans, pdev->xdev->nodename, + "magic", XEN_PCI_MAGIC); + + if (err) { + xenbus_transaction_end(trans, 1); + xenbus_dev_fatal(pdev->xdev, err, + "Error writing configuration for backend"); + goto out; + } else { + err = xenbus_transaction_end(trans, 0); + if (err == -EAGAIN) + goto do_publish; + else if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error completing transaction " + "for backend"); + goto out; + } + } + + xenbus_switch_state(pdev->xdev, XenbusStateInitialised); + + dev_dbg(&pdev->xdev->dev, "publishing successful!\n"); + + out: + return err; +} + +static int __devinit pcifront_try_connect(struct pcifront_device *pdev) +{ + int err = -EFAULT; + int i, num_roots, len; + char str[64]; + unsigned int domain, bus; + + spin_lock(&pdev->dev_lock); + + /* Only connect once */ + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateInitialised) + goto out; + + err = pcifront_connect(pdev); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error connecting PCI Frontend"); + goto out; + } + + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, + "root_num", "%d", &num_roots); + if (err == -ENOENT) { + xenbus_dev_error(pdev->xdev, err, + "No PCI Roots found, trying 0000:00"); + err = pcifront_scan_root(pdev, 0, 0); + num_roots = 0; + } else if (err != 1) { + if (err == 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading number of PCI roots"); + goto out; + } + + for (i = 0; i < num_roots; i++) { + len = snprintf(str, sizeof(str), "root-%d", i); + if (unlikely(len >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, + "%x:%x", &domain, &bus); + if (err != 2) { + if (err >= 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading PCI root %d", i); + goto out; + } + + err = pcifront_scan_root(pdev, domain, bus); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error scanning PCI root %04x:%02x", + domain, bus); + goto out; + } + } + + err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); + if (err) + goto out; + + out: + spin_unlock(&pdev->dev_lock); + return err; +} + +static int pcifront_try_disconnect(struct pcifront_device *pdev) +{ + int err = 0; + enum xenbus_state prev_state; + + spin_lock(&pdev->dev_lock); + + prev_state = xenbus_read_driver_state(pdev->xdev->nodename); + + if (prev_state >= XenbusStateClosing) + goto out; + + if(prev_state == XenbusStateConnected) { + pcifront_free_roots(pdev); + pcifront_disconnect(pdev); + } + + err = xenbus_switch_state(pdev->xdev, XenbusStateClosed); + + out: + spin_unlock(&pdev->dev_lock); + + return err; +} + +static int __devinit pcifront_attach_devices(struct pcifront_device *pdev) +{ + int err = -EFAULT; + int i, num_roots, len; + unsigned int domain, bus; + char str[64]; + + spin_lock(&pdev->dev_lock); + + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateReconfiguring) + goto out; + + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, + "root_num", "%d", &num_roots); + if (err == -ENOENT) { + xenbus_dev_error(pdev->xdev, err, + "No PCI Roots found, trying 0000:00"); + err = pcifront_rescan_root(pdev, 0, 0); + num_roots = 0; + } else if (err != 1) { + if (err == 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading number of PCI roots"); + goto out; + } + + for (i = 0; i < num_roots; i++) { + len = snprintf(str, sizeof(str), "root-%d", i); + if (unlikely(len >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, + "%x:%x", &domain, &bus); + if (err != 2) { + if (err >= 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading PCI root %d", i); + goto out; + } + + err = pcifront_rescan_root(pdev, domain, bus); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error scanning PCI root %04x:%02x", + domain, bus); + goto out; + } + } + + xenbus_switch_state(pdev->xdev, XenbusStateConnected); + + out: + spin_unlock(&pdev->dev_lock); + return err; +} + +static int pcifront_detach_devices(struct pcifront_device *pdev) +{ + int err = 0; + int i, num_devs; + unsigned int domain, bus, slot, func; + struct pci_bus *pci_bus; + struct pci_dev *pci_dev; + char str[64]; + + spin_lock(&pdev->dev_lock); + + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateConnected) + goto out; + + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, "num_devs", "%d", + &num_devs); + if (err != 1) { + if (err >= 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading number of PCI devices"); + goto out; + } + + /* Find devices being detached and remove them. */ + for (i = 0; i < num_devs; i++) { + int l, state; + l = snprintf(str, sizeof(str), "state-%d", i); + if (unlikely(l >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%d", + &state); + if (err != 1) + state = XenbusStateUnknown; + + if (state != XenbusStateClosing) + continue; + + /* Remove device. */ + l = snprintf(str, sizeof(str), "vdev-%d", i); + if (unlikely(l >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, + "%x:%x:%x.%x", &domain, &bus, &slot, &func); + if (err != 4) { + if (err >= 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading PCI device %d", i); + goto out; + } + + pci_bus = pci_find_bus(domain, bus); + if(!pci_bus) { + dev_dbg(&pdev->xdev->dev, "Cannot get bus %04x:%02x\n", + domain, bus); + continue; + } + pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func)); + if(!pci_dev) { + dev_dbg(&pdev->xdev->dev, + "Cannot get PCI device %04x:%02x:%02x.%02x\n", + domain, bus, slot, func); + continue; + } + pci_remove_bus_device(pci_dev); + pci_dev_put(pci_dev); + + dev_dbg(&pdev->xdev->dev, + "PCI device %04x:%02x:%02x.%02x removed.\n", + domain, bus, slot, func); + } + + err = xenbus_switch_state(pdev->xdev, XenbusStateReconfiguring); + + out: + spin_unlock(&pdev->dev_lock); + return err; +} + +static void __init_refok pcifront_backend_changed(struct xenbus_device *xdev, + enum xenbus_state be_state) +{ + struct pcifront_device *pdev = xdev->dev.driver_data; + + switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateClosed: + break; + + case XenbusStateConnected: + pcifront_try_connect(pdev); + break; + + case XenbusStateClosing: + dev_warn(&xdev->dev, "backend going away!\n"); + pcifront_try_disconnect(pdev); + break; + + case XenbusStateReconfiguring: + pcifront_detach_devices(pdev); + break; + + case XenbusStateReconfigured: + pcifront_attach_devices(pdev); + break; + } +} + +static int pcifront_xenbus_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + int err = 0; + struct pcifront_device *pdev = alloc_pdev(xdev); + + if (pdev == NULL) { + err = -ENOMEM; + xenbus_dev_fatal(xdev, err, + "Error allocating pcifront_device struct"); + goto out; + } + + err = pcifront_publish_info(pdev); + + out: + return err; +} + +static int pcifront_xenbus_remove(struct xenbus_device *xdev) +{ + if (xdev->dev.driver_data) + free_pdev(xdev->dev.driver_data); + + return 0; +} + +static const struct xenbus_device_id xenpci_ids[] = { + {"pci"}, + {{0}}, +}; +MODULE_ALIAS("xen:pci"); + +static struct xenbus_driver xenbus_pcifront_driver = { + .name = "pcifront", + .owner = THIS_MODULE, + .ids = xenpci_ids, + .probe = pcifront_xenbus_probe, + .remove = pcifront_xenbus_remove, + .otherend_changed = pcifront_backend_changed, +}; + +static int __init pcifront_init(void) +{ + if (!xen_domain()) + return -ENODEV; + + return xenbus_register_frontend(&xenbus_pcifront_driver); +} + +/* Initialize after the Xen PCI Frontend Stub is initialized */ +subsys_initcall(pcifront_init); diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index d4a33b6..88eb94c 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -174,20 +174,3 @@ config ACPI_PROCESSOR_XEN depends on XEN_DOM0 && ACPI_PROCESSOR && CPU_FREQ default y -config XEN_PCIDEV_FRONTEND - bool "Xen PCI Frontend" - depends on PCI && X86_64 - select HOTPLUG - default y - help - The PCI device frontend driver allows the kernel to import arbitrary - PCI devices from a PCI backend to support PCI driver domains. - -config XEN_PCIDEV_FE_DEBUG - bool "Xen PCI Frontend Debugging" - depends on XEN_PCIDEV_FRONTEND - default n - help - Enables some debug statements within the PCI Frontend. - - diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 0bba5f2..cddfffb 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_XEN_GNTDEV) += gntdev.o obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/ obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/ -obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/ obj-$(CONFIG_XENFS) += xenfs/ obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o obj-$(CONFIG_XEN_S3) += acpi.o diff --git a/drivers/xen/pcifront/Makefile b/drivers/xen/pcifront/Makefile deleted file mode 100644 index d49136e..0000000 --- a/drivers/xen/pcifront/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -obj-y += pcifront.o - -pcifront-y := xenbus.o - -ifeq ($(CONFIG_XEN_PCIDEV_FE_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif diff --git a/drivers/xen/pcifront/xenbus.c b/drivers/xen/pcifront/xenbus.c deleted file mode 100644 index 854850d..0000000 --- a/drivers/xen/pcifront/xenbus.c +++ /dev/null @@ -1,1117 +0,0 @@ -/* - * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn) - * - * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mm.h> -#include <xen/xenbus.h> -#include <xen/events.h> -#include <xen/grant_table.h> -#include <xen/page.h> -#include <linux/spinlock.h> -#include <linux/pci.h> -#include <xen/xenbus.h> -#include <xen/interface/io/pciif.h> -#include <linux/interrupt.h> -#include <asm/atomic.h> -#include <linux/workqueue.h> -#include <asm/bitops.h> -#include <linux/time.h> - - -#ifndef __init_refok -#define __init_refok -#endif - -#define INVALID_GRANT_REF (0) -#define INVALID_EVTCHN (-1) - - -struct pci_bus_entry { - struct list_head list; - struct pci_bus *bus; -}; - -#define _PDEVB_op_active (0) -#define PDEVB_op_active (1 << (_PDEVB_op_active)) - -struct pcifront_device { - struct xenbus_device *xdev; - struct list_head root_buses; - spinlock_t dev_lock; - - int evtchn; - int gnt_ref; - - int irq; - - /* Lock this when doing any operations in sh_info */ - spinlock_t sh_info_lock; - struct xen_pci_sharedinfo *sh_info; - struct work_struct op_work; - unsigned long flags; - -}; - -struct pcifront_sd { - int domain; - struct pcifront_device *pdev; -}; - -static inline struct pcifront_device * -pcifront_get_pdev(struct pcifront_sd *sd) -{ - return sd->pdev; -} - -static inline void pcifront_init_sd(struct pcifront_sd *sd, - unsigned int domain, unsigned int bus, - struct pcifront_device *pdev) -{ - sd->domain = domain; - sd->pdev = pdev; -} - -static inline void pcifront_setup_root_resources(struct pci_bus *bus, - struct pcifront_sd *sd) -{ -} - - -DEFINE_SPINLOCK(pcifront_dev_lock); -static struct pcifront_device *pcifront_dev = NULL; - -static int verbose_request = 0; -module_param(verbose_request, int, 0644); - -static int errno_to_pcibios_err(int errno) -{ - switch (errno) { - case XEN_PCI_ERR_success: - return PCIBIOS_SUCCESSFUL; - - case XEN_PCI_ERR_dev_not_found: - return PCIBIOS_DEVICE_NOT_FOUND; - - case XEN_PCI_ERR_invalid_offset: - case XEN_PCI_ERR_op_failed: - return PCIBIOS_BAD_REGISTER_NUMBER; - - case XEN_PCI_ERR_not_implemented: - return PCIBIOS_FUNC_NOT_SUPPORTED; - - case XEN_PCI_ERR_access_denied: - return PCIBIOS_SET_FAILED; - } - return errno; -} - -static inline void schedule_pcifront_aer_op(struct pcifront_device *pdev) -{ - if (test_bit(_XEN_PCIB_active, (unsigned long *)&pdev->sh_info->flags) - && !test_and_set_bit(_PDEVB_op_active, &pdev->flags)) { - dev_dbg(&pdev->xdev->dev, "schedule aer frontend job\n"); - schedule_work(&pdev->op_work); - } -} - -static int do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op) -{ - int err = 0; - struct xen_pci_op *active_op = &pdev->sh_info->op; - unsigned long irq_flags; - evtchn_port_t port = pdev->evtchn; - unsigned irq = pdev->irq; - s64 ns, ns_timeout; - struct timeval tv; - - spin_lock_irqsave(&pdev->sh_info_lock, irq_flags); - - memcpy(active_op, op, sizeof(struct xen_pci_op)); - - /* Go */ - wmb(); - set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); - notify_remote_via_evtchn(port); - - /* - * We set a poll timeout of 3 seconds but give up on return after - * 2 seconds. It is better to time out too late rather than too early - * (in the latter case we end up continually re-executing poll() with a - * timeout in the past). 1s difference gives plenty of slack for error. - */ - do_gettimeofday(&tv); - ns_timeout = timeval_to_ns(&tv) + 2 * (s64)NSEC_PER_SEC; - - xen_clear_irq_pending(irq); - - while (test_bit(_XEN_PCIF_active, - (unsigned long *)&pdev->sh_info->flags)) { - xen_poll_irq_timeout(irq, jiffies + 3*HZ); - xen_clear_irq_pending(irq); - do_gettimeofday(&tv); - ns = timeval_to_ns(&tv); - if (ns > ns_timeout) { - dev_err(&pdev->xdev->dev, - "pciback not responding!!!\n"); - clear_bit(_XEN_PCIF_active, - (unsigned long *)&pdev->sh_info->flags); - err = XEN_PCI_ERR_dev_not_found; - goto out; - } - } - - /* - * We might lose backend service request since we - * reuse same evtchn with pci_conf backend response. So re-schedule - * aer pcifront service. - */ - if (test_bit(_XEN_PCIB_active, - (unsigned long*)&pdev->sh_info->flags)) { - dev_err(&pdev->xdev->dev, - "schedule aer pcifront service\n"); - schedule_pcifront_aer_op(pdev); - } - - memcpy(op, active_op, sizeof(struct xen_pci_op)); - - err = op->err; - out: - spin_unlock_irqrestore(&pdev->sh_info_lock, irq_flags); - return err; -} - -/* Access to this function is spinlocked in drivers/pci/access.c */ -static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * val) -{ - int err = 0; - struct xen_pci_op op = { - .cmd = XEN_PCI_OP_conf_read, - .domain = pci_domain_nr(bus), - .bus = bus->number, - .devfn = devfn, - .offset = where, - .size = size, - }; - struct pcifront_sd *sd = bus->sysdata; - struct pcifront_device *pdev = pcifront_get_pdev(sd); - - if (verbose_request) - dev_info(&pdev->xdev->dev, - "read dev=%04x:%02x:%02x.%01x - offset %x size %d\n", - pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), - PCI_FUNC(devfn), where, size); - - err = do_pci_op(pdev, &op); - - if (likely(!err)) { - if (verbose_request) - dev_info(&pdev->xdev->dev, "read got back value %x\n", - op.value); - - *val = op.value; - } else if (err == -ENODEV) { - /* No device here, pretend that it just returned 0 */ - err = 0; - *val = 0; - } - - return errno_to_pcibios_err(err); -} - -/* Access to this function is spinlocked in drivers/pci/access.c */ -static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct xen_pci_op op = { - .cmd = XEN_PCI_OP_conf_write, - .domain = pci_domain_nr(bus), - .bus = bus->number, - .devfn = devfn, - .offset = where, - .size = size, - .value = val, - }; - struct pcifront_sd *sd = bus->sysdata; - struct pcifront_device *pdev = pcifront_get_pdev(sd); - - if (verbose_request) - dev_info(&pdev->xdev->dev, - "write dev=%04x:%02x:%02x.%01x - " - "offset %x size %d val %x\n", - pci_domain_nr(bus), bus->number, - PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val); - - return errno_to_pcibios_err(do_pci_op(pdev, &op)); -} - -struct pci_ops pcifront_bus_ops = { - .read = pcifront_bus_read, - .write = pcifront_bus_write, -}; - -#ifdef CONFIG_PCI_MSI -int pci_frontend_enable_msix(struct pci_dev *dev, - struct msix_entry *entries, - int nvec) -{ - int err; - int i; - struct xen_pci_op op = { - .cmd = XEN_PCI_OP_enable_msix, - .domain = pci_domain_nr(dev->bus), - .bus = dev->bus->number, - .devfn = dev->devfn, - .value = nvec, - }; - struct pcifront_sd *sd = dev->bus->sysdata; - struct pcifront_device *pdev = pcifront_get_pdev(sd); - - if (nvec > SH_INFO_MAX_VEC) { - printk("too much vector for pci frontend%x\n", nvec); - return -EINVAL; - } - - for (i = 0; i < nvec; i++) { - op.msix_entries[i].entry = entries[i].entry; - op.msix_entries[i].vector = entries[i].vector; - } - - err = do_pci_op(pdev, &op); - - if (!err) { - if (!op.value) { - /* we get the result */ - for ( i = 0; i < nvec; i++) - entries[i].vector = op.msix_entries[i].vector; - return 0; - } - else { - printk("enable msix get value %x\n", op.value); - return op.value; - } - } - else { - printk("enable msix get err %x\n", err); - return err; - } -} - -void pci_frontend_disable_msix(struct pci_dev* dev) -{ - int err; - struct xen_pci_op op = { - .cmd = XEN_PCI_OP_disable_msix, - .domain = pci_domain_nr(dev->bus), - .bus = dev->bus->number, - .devfn = dev->devfn, - }; - struct pcifront_sd *sd = dev->bus->sysdata; - struct pcifront_device *pdev = pcifront_get_pdev(sd); - - err = do_pci_op(pdev, &op); - - /* What should do for error ? */ - if (err) - printk("pci_disable_msix get err %x\n", err); -} - -int pci_frontend_enable_msi(struct pci_dev *dev) -{ - int err; - struct xen_pci_op op = { - .cmd = XEN_PCI_OP_enable_msi, - .domain = pci_domain_nr(dev->bus), - .bus = dev->bus->number, - .devfn = dev->devfn, - }; - struct pcifront_sd *sd = dev->bus->sysdata; - struct pcifront_device *pdev = pcifront_get_pdev(sd); - - err = do_pci_op(pdev, &op); - if (likely(!err)) { - dev->irq = op.value; - } - else { - printk("pci frontend enable msi failed for dev %x:%x \n", - op.bus, op.devfn); - err = -EINVAL; - } - return err; -} - -void pci_frontend_disable_msi(struct pci_dev* dev) -{ - int err; - struct xen_pci_op op = { - .cmd = XEN_PCI_OP_disable_msi, - .domain = pci_domain_nr(dev->bus), - .bus = dev->bus->number, - .devfn = dev->devfn, - }; - struct pcifront_sd *sd = dev->bus->sysdata; - struct pcifront_device *pdev = pcifront_get_pdev(sd); - - err = do_pci_op(pdev, &op); - if (err == XEN_PCI_ERR_dev_not_found) { - /* XXX No response from backend, what shall we do? */ - printk("get no response from backend for disable MSI\n"); - return; - } - if (likely(!err)) - dev->irq = op.value; - else - /* how can pciback notify us fail? */ - printk("get fake response frombackend \n"); -} -#endif /* CONFIG_PCI_MSI */ - -/* Claim resources for the PCI frontend as-is, backend won't allow changes */ -static int pcifront_claim_resource(struct pci_dev *dev, void *data) -{ - struct pcifront_device *pdev = data; - int i; - struct resource *r; - - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - r = &dev->resource[i]; - - if (!r->parent && r->start && r->flags) { - dev_dbg(&pdev->xdev->dev, "claiming resource %s/%d\n", - pci_name(dev), i); - pci_claim_resource(dev, i); - } - } - - return 0; -} - -int __devinit pcifront_scan_root(struct pcifront_device *pdev, - unsigned int domain, unsigned int bus) -{ - struct pci_bus *b; - struct pcifront_sd *sd = NULL; - struct pci_bus_entry *bus_entry = NULL; - int err = 0; - -#ifndef CONFIG_PCI_DOMAINS - if (domain != 0) { - dev_err(&pdev->xdev->dev, - "PCI Root in non-zero PCI Domain! domain=%d\n", domain); - dev_err(&pdev->xdev->dev, - "Please compile with CONFIG_PCI_DOMAINS\n"); - err = -EINVAL; - goto err_out; - } -#endif - - dev_info(&pdev->xdev->dev, "Creating PCI Frontend Bus %04x:%02x\n", - domain, bus); - - bus_entry = kmalloc(sizeof(*bus_entry), GFP_KERNEL); - sd = kmalloc(sizeof(*sd), GFP_KERNEL); - if (!bus_entry || !sd) { - err = -ENOMEM; - goto err_out; - } - pcifront_init_sd(sd, domain, bus, pdev); - - b = pci_scan_bus_parented(&pdev->xdev->dev, bus, - &pcifront_bus_ops, sd); - if (!b) { - dev_err(&pdev->xdev->dev, - "Error creating PCI Frontend Bus!\n"); - err = -ENOMEM; - goto err_out; - } - - pcifront_setup_root_resources(b, sd); - bus_entry->bus = b; - - list_add(&bus_entry->list, &pdev->root_buses); - - /* Claim resources before going "live" with our devices */ - pci_walk_bus(b, pcifront_claim_resource, pdev); - - pci_bus_add_devices(b); - - return 0; - - err_out: - kfree(bus_entry); - kfree(sd); - - return err; -} - -int __devinit pcifront_rescan_root(struct pcifront_device *pdev, - unsigned int domain, unsigned int bus) -{ - struct pci_bus *b; - struct pci_dev *d; - unsigned int devfn; - int err; - -#ifndef CONFIG_PCI_DOMAINS - if (domain != 0) { - dev_err(&pdev->xdev->dev, - "PCI Root in non-zero PCI Domain! domain=%d\n", domain); - dev_err(&pdev->xdev->dev, - "Please compile with CONFIG_PCI_DOMAINS\n"); - return -EINVAL; - } -#endif - - dev_info(&pdev->xdev->dev, "Rescanning PCI Frontend Bus %04x:%02x\n", - domain, bus); - - b = pci_find_bus(domain, bus); - if(!b) - /* If the bus is unknown, create it. */ - return pcifront_scan_root(pdev, domain, bus); - - /* Rescan the bus for newly attached functions and add. - * We omit handling of PCI bridge attachment because pciback prevents - * bridges from being exported. - */ - for (devfn = 0; devfn < 0x100; devfn++) { - d = pci_get_slot(b, devfn); - if(d) { - /* Device is already known. */ - pci_dev_put(d); - continue; - } - - d = pci_scan_single_device(b, devfn); - if (d) { - dev_info(&pdev->xdev->dev, "New device on " - "%04x:%02x:%02x.%02x found.\n", domain, bus, - PCI_SLOT(devfn), PCI_FUNC(devfn)); - err = pci_bus_add_device(d); - if (err) { - dev_err(&pdev->xdev->dev, "Failed to add " - " device to bus.\n"); - return err; - } - - } - } - - return 0; -} - -static void free_root_bus_devs(struct pci_bus *bus) -{ - struct pci_dev *dev; - - while (!list_empty(&bus->devices)) { - dev = container_of(bus->devices.next, struct pci_dev, - bus_list); - dev_dbg(&dev->dev, "removing device\n"); - pci_remove_bus_device(dev); - } -} - -void pcifront_free_roots(struct pcifront_device *pdev) -{ - struct pci_bus_entry *bus_entry, *t; - - dev_dbg(&pdev->xdev->dev, "cleaning up root buses\n"); - - list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) { - list_del(&bus_entry->list); - - free_root_bus_devs(bus_entry->bus); - - kfree(bus_entry->bus->sysdata); - - device_unregister(bus_entry->bus->bridge); - pci_remove_bus(bus_entry->bus); - - kfree(bus_entry); - } -} - -static pci_ers_result_t pcifront_common_process( int cmd, struct pcifront_device *pdev, - pci_channel_state_t state) -{ - pci_ers_result_t result; - struct pci_driver *pdrv; - int bus = pdev->sh_info->aer_op.bus; - int devfn = pdev->sh_info->aer_op.devfn; - struct pci_dev *pcidev; - int flag = 0; - - dev_dbg(&pdev->xdev->dev, - "pcifront AER process: cmd %x (bus:%x, devfn%x)", - cmd, bus, devfn); - result = PCI_ERS_RESULT_NONE; - - pcidev = pci_get_bus_and_slot(bus, devfn); - if (!pcidev || !pcidev->driver){ - dev_err(&pcidev->dev, - "device or driver is NULL\n"); - return result; - } - pdrv = pcidev->driver; - - if (get_driver(&pdrv->driver)) { - if (pdrv->err_handler && pdrv->err_handler->error_detected) { - dev_dbg(&pcidev->dev, - "trying to call AER service\n"); - if (pcidev) { - flag = 1; - switch(cmd) { - case XEN_PCI_OP_aer_detected: - result = pdrv->err_handler->error_detected(pcidev, state); - break; - case XEN_PCI_OP_aer_mmio: - result = pdrv->err_handler->mmio_enabled(pcidev); - break; - case XEN_PCI_OP_aer_slotreset: - result = pdrv->err_handler->slot_reset(pcidev); - break; - case XEN_PCI_OP_aer_resume: - pdrv->err_handler->resume(pcidev); - break; - default: - dev_err(&pdev->xdev->dev, - "bad request in aer recovery operation!\n"); - - } - } - } - put_driver(&pdrv->driver); - } - if (!flag) - result = PCI_ERS_RESULT_NONE; - - return result; -} - - -void pcifront_do_aer(struct work_struct *data) -{ - struct pcifront_device *pdev = container_of(data, struct pcifront_device, op_work); - int cmd = pdev->sh_info->aer_op.cmd; - pci_channel_state_t state = - (pci_channel_state_t)pdev->sh_info->aer_op.err; - - /*If a pci_conf op is in progress, - we have to wait until it is done before service aer op*/ - dev_dbg(&pdev->xdev->dev, - "pcifront service aer bus %x devfn %x\n", pdev->sh_info->aer_op.bus, - pdev->sh_info->aer_op.devfn); - - pdev->sh_info->aer_op.err = pcifront_common_process(cmd, pdev, state); - - wmb(); - clear_bit(_XEN_PCIB_active, (unsigned long*)&pdev->sh_info->flags); - notify_remote_via_evtchn(pdev->evtchn); - - /*in case of we lost an aer request in four lines time_window*/ - smp_mb__before_clear_bit(); - clear_bit( _PDEVB_op_active, &pdev->flags); - smp_mb__after_clear_bit(); - - schedule_pcifront_aer_op(pdev); - -} - -irqreturn_t pcifront_handler_aer(int irq, void *dev) -{ - struct pcifront_device *pdev = dev; - schedule_pcifront_aer_op(pdev); - return IRQ_HANDLED; -} -int pcifront_connect(struct pcifront_device *pdev) -{ - int err = 0; - - spin_lock(&pcifront_dev_lock); - - if (!pcifront_dev) { - dev_info(&pdev->xdev->dev, "Installing PCI frontend\n"); - pcifront_dev = pdev; - } - else { - dev_err(&pdev->xdev->dev, "PCI frontend already installed!\n"); - err = -EEXIST; - } - - spin_unlock(&pcifront_dev_lock); - - return err; -} - -void pcifront_disconnect(struct pcifront_device *pdev) -{ - spin_lock(&pcifront_dev_lock); - - if (pdev == pcifront_dev) { - dev_info(&pdev->xdev->dev, - "Disconnecting PCI Frontend Buses\n"); - pcifront_dev = NULL; - } - - spin_unlock(&pcifront_dev_lock); -} -static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev) -{ - struct pcifront_device *pdev; - - pdev = kzalloc(sizeof(struct pcifront_device), GFP_KERNEL); - if (pdev == NULL) - goto out; - - pdev->sh_info = - (struct xen_pci_sharedinfo *)__get_free_page(GFP_KERNEL); - if (pdev->sh_info == NULL) { - kfree(pdev); - pdev = NULL; - goto out; - } - pdev->sh_info->flags = 0; - - /*Flag for registering PV AER handler*/ - set_bit(_XEN_PCIB_AERHANDLER, (void*)&pdev->sh_info->flags); - - xdev->dev.driver_data = pdev; - pdev->xdev = xdev; - - INIT_LIST_HEAD(&pdev->root_buses); - - spin_lock_init(&pdev->dev_lock); - spin_lock_init(&pdev->sh_info_lock); - - pdev->evtchn = INVALID_EVTCHN; - pdev->gnt_ref = INVALID_GRANT_REF; - pdev->irq = -1; - - INIT_WORK(&pdev->op_work, pcifront_do_aer); - - dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n", - pdev, pdev->sh_info); - out: - return pdev; -} - -static void free_pdev(struct pcifront_device *pdev) -{ - dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev); - - pcifront_free_roots(pdev); - - /*For PCIE_AER error handling job*/ - flush_scheduled_work(); - unbind_from_irqhandler(pdev->irq, pdev); - - if (pdev->evtchn != INVALID_EVTCHN) - xenbus_free_evtchn(pdev->xdev, pdev->evtchn); - - if (pdev->gnt_ref != INVALID_GRANT_REF) - gnttab_end_foreign_access(pdev->gnt_ref, 0 /* r/w page */, - (unsigned long)pdev->sh_info); - - pdev->xdev->dev.driver_data = NULL; - - kfree(pdev); -} - -static int pcifront_publish_info(struct pcifront_device *pdev) -{ - int err = 0; - struct xenbus_transaction trans; - - err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info)); - if (err < 0) - goto out; - - pdev->gnt_ref = err; - - err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn); - if (err) - goto out; - - err = bind_evtchn_to_irqhandler(pdev->evtchn, pcifront_handler_aer, - 0, "pcifront", pdev); - if (err < 0) { - xenbus_free_evtchn(pdev->xdev, pdev->evtchn); - xenbus_dev_fatal(pdev->xdev, err, "Failed to bind evtchn to " - "irqhandler.\n"); - return err; - } - pdev->irq = err; - - do_publish: - err = xenbus_transaction_start(&trans); - if (err) { - xenbus_dev_fatal(pdev->xdev, err, - "Error writing configuration for backend " - "(start transaction)"); - goto out; - } - - err = xenbus_printf(trans, pdev->xdev->nodename, - "pci-op-ref", "%u", pdev->gnt_ref); - if (!err) - err = xenbus_printf(trans, pdev->xdev->nodename, - "event-channel", "%u", pdev->evtchn); - if (!err) - err = xenbus_printf(trans, pdev->xdev->nodename, - "magic", XEN_PCI_MAGIC); - - if (err) { - xenbus_transaction_end(trans, 1); - xenbus_dev_fatal(pdev->xdev, err, - "Error writing configuration for backend"); - goto out; - } else { - err = xenbus_transaction_end(trans, 0); - if (err == -EAGAIN) - goto do_publish; - else if (err) { - xenbus_dev_fatal(pdev->xdev, err, - "Error completing transaction " - "for backend"); - goto out; - } - } - - xenbus_switch_state(pdev->xdev, XenbusStateInitialised); - - dev_dbg(&pdev->xdev->dev, "publishing successful!\n"); - - out: - return err; -} - -static int __devinit pcifront_try_connect(struct pcifront_device *pdev) -{ - int err = -EFAULT; - int i, num_roots, len; - char str[64]; - unsigned int domain, bus; - - spin_lock(&pdev->dev_lock); - - /* Only connect once */ - if (xenbus_read_driver_state(pdev->xdev->nodename) != - XenbusStateInitialised) - goto out; - - err = pcifront_connect(pdev); - if (err) { - xenbus_dev_fatal(pdev->xdev, err, - "Error connecting PCI Frontend"); - goto out; - } - - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, - "root_num", "%d", &num_roots); - if (err == -ENOENT) { - xenbus_dev_error(pdev->xdev, err, - "No PCI Roots found, trying 0000:00"); - err = pcifront_scan_root(pdev, 0, 0); - num_roots = 0; - } else if (err != 1) { - if (err == 0) - err = -EINVAL; - xenbus_dev_fatal(pdev->xdev, err, - "Error reading number of PCI roots"); - goto out; - } - - for (i = 0; i < num_roots; i++) { - len = snprintf(str, sizeof(str), "root-%d", i); - if (unlikely(len >= (sizeof(str) - 1))) { - err = -ENOMEM; - goto out; - } - - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, - "%x:%x", &domain, &bus); - if (err != 2) { - if (err >= 0) - err = -EINVAL; - xenbus_dev_fatal(pdev->xdev, err, - "Error reading PCI root %d", i); - goto out; - } - - err = pcifront_scan_root(pdev, domain, bus); - if (err) { - xenbus_dev_fatal(pdev->xdev, err, - "Error scanning PCI root %04x:%02x", - domain, bus); - goto out; - } - } - - err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); - if (err) - goto out; - - out: - spin_unlock(&pdev->dev_lock); - return err; -} - -static int pcifront_try_disconnect(struct pcifront_device *pdev) -{ - int err = 0; - enum xenbus_state prev_state; - - spin_lock(&pdev->dev_lock); - - prev_state = xenbus_read_driver_state(pdev->xdev->nodename); - - if (prev_state >= XenbusStateClosing) - goto out; - - if(prev_state == XenbusStateConnected) { - pcifront_free_roots(pdev); - pcifront_disconnect(pdev); - } - - err = xenbus_switch_state(pdev->xdev, XenbusStateClosed); - - out: - spin_unlock(&pdev->dev_lock); - - return err; -} - -static int __devinit pcifront_attach_devices(struct pcifront_device *pdev) -{ - int err = -EFAULT; - int i, num_roots, len; - unsigned int domain, bus; - char str[64]; - - spin_lock(&pdev->dev_lock); - - if (xenbus_read_driver_state(pdev->xdev->nodename) != - XenbusStateReconfiguring) - goto out; - - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, - "root_num", "%d", &num_roots); - if (err == -ENOENT) { - xenbus_dev_error(pdev->xdev, err, - "No PCI Roots found, trying 0000:00"); - err = pcifront_rescan_root(pdev, 0, 0); - num_roots = 0; - } else if (err != 1) { - if (err == 0) - err = -EINVAL; - xenbus_dev_fatal(pdev->xdev, err, - "Error reading number of PCI roots"); - goto out; - } - - for (i = 0; i < num_roots; i++) { - len = snprintf(str, sizeof(str), "root-%d", i); - if (unlikely(len >= (sizeof(str) - 1))) { - err = -ENOMEM; - goto out; - } - - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, - "%x:%x", &domain, &bus); - if (err != 2) { - if (err >= 0) - err = -EINVAL; - xenbus_dev_fatal(pdev->xdev, err, - "Error reading PCI root %d", i); - goto out; - } - - err = pcifront_rescan_root(pdev, domain, bus); - if (err) { - xenbus_dev_fatal(pdev->xdev, err, - "Error scanning PCI root %04x:%02x", - domain, bus); - goto out; - } - } - - xenbus_switch_state(pdev->xdev, XenbusStateConnected); - - out: - spin_unlock(&pdev->dev_lock); - return err; -} - -static int pcifront_detach_devices(struct pcifront_device *pdev) -{ - int err = 0; - int i, num_devs; - unsigned int domain, bus, slot, func; - struct pci_bus *pci_bus; - struct pci_dev *pci_dev; - char str[64]; - - spin_lock(&pdev->dev_lock); - - if (xenbus_read_driver_state(pdev->xdev->nodename) != - XenbusStateConnected) - goto out; - - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, "num_devs", "%d", - &num_devs); - if (err != 1) { - if (err >= 0) - err = -EINVAL; - xenbus_dev_fatal(pdev->xdev, err, - "Error reading number of PCI devices"); - goto out; - } - - /* Find devices being detached and remove them. */ - for (i = 0; i < num_devs; i++) { - int l, state; - l = snprintf(str, sizeof(str), "state-%d", i); - if (unlikely(l >= (sizeof(str) - 1))) { - err = -ENOMEM; - goto out; - } - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%d", - &state); - if (err != 1) - state = XenbusStateUnknown; - - if (state != XenbusStateClosing) - continue; - - /* Remove device. */ - l = snprintf(str, sizeof(str), "vdev-%d", i); - if (unlikely(l >= (sizeof(str) - 1))) { - err = -ENOMEM; - goto out; - } - err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, - "%x:%x:%x.%x", &domain, &bus, &slot, &func); - if (err != 4) { - if (err >= 0) - err = -EINVAL; - xenbus_dev_fatal(pdev->xdev, err, - "Error reading PCI device %d", i); - goto out; - } - - pci_bus = pci_find_bus(domain, bus); - if(!pci_bus) { - dev_dbg(&pdev->xdev->dev, "Cannot get bus %04x:%02x\n", - domain, bus); - continue; - } - pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func)); - if(!pci_dev) { - dev_dbg(&pdev->xdev->dev, - "Cannot get PCI device %04x:%02x:%02x.%02x\n", - domain, bus, slot, func); - continue; - } - pci_remove_bus_device(pci_dev); - pci_dev_put(pci_dev); - - dev_dbg(&pdev->xdev->dev, - "PCI device %04x:%02x:%02x.%02x removed.\n", - domain, bus, slot, func); - } - - err = xenbus_switch_state(pdev->xdev, XenbusStateReconfiguring); - - out: - spin_unlock(&pdev->dev_lock); - return err; -} - -static void __init_refok pcifront_backend_changed(struct xenbus_device *xdev, - enum xenbus_state be_state) -{ - struct pcifront_device *pdev = xdev->dev.driver_data; - - switch (be_state) { - case XenbusStateUnknown: - case XenbusStateInitialising: - case XenbusStateInitWait: - case XenbusStateInitialised: - case XenbusStateClosed: - break; - - case XenbusStateConnected: - pcifront_try_connect(pdev); - break; - - case XenbusStateClosing: - dev_warn(&xdev->dev, "backend going away!\n"); - pcifront_try_disconnect(pdev); - break; - - case XenbusStateReconfiguring: - pcifront_detach_devices(pdev); - break; - - case XenbusStateReconfigured: - pcifront_attach_devices(pdev); - break; - } -} - -static int pcifront_xenbus_probe(struct xenbus_device *xdev, - const struct xenbus_device_id *id) -{ - int err = 0; - struct pcifront_device *pdev = alloc_pdev(xdev); - - if (pdev == NULL) { - err = -ENOMEM; - xenbus_dev_fatal(xdev, err, - "Error allocating pcifront_device struct"); - goto out; - } - - err = pcifront_publish_info(pdev); - - out: - return err; -} - -static int pcifront_xenbus_remove(struct xenbus_device *xdev) -{ - if (xdev->dev.driver_data) - free_pdev(xdev->dev.driver_data); - - return 0; -} - -static const struct xenbus_device_id xenpci_ids[] = { - {"pci"}, - {{0}}, -}; -MODULE_ALIAS("xen:pci"); - -static struct xenbus_driver xenbus_pcifront_driver = { - .name = "pcifront", - .owner = THIS_MODULE, - .ids = xenpci_ids, - .probe = pcifront_xenbus_probe, - .remove = pcifront_xenbus_remove, - .otherend_changed = pcifront_backend_changed, -}; - -static int __init pcifront_init(void) -{ - if (!xen_domain()) - return -ENODEV; - - return xenbus_register_frontend(&xenbus_pcifront_driver); -} - -/* Initialize after the Xen PCI Frontend Stub is initialized */ -subsys_initcall(pcifront_init); -- 1.6.2.5 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |