[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [linux-2.6.18-xen] MSI 6/6: add MSI support to domain0/domainU
# HG changeset patch # User Keir Fraser <keir.fraser@xxxxxxxxxx> # Date 1209634530 -3600 # Node ID 9f9b4214bec8c82f835b43554b798e107371749f # Parent 81c5a517a42b817378b63ced1420684e9bed1e21 MSI 6/6: add MSI support to domain0/domainU Signed-off-by: Jiang Yunhong <yunhong.jiang@xxxxxxxxx> Signed-off-by: Shan Haitao <haitao.shan@xxxxxxxxx> --- drivers/pci/Kconfig | 1 drivers/pci/msi-xen.c | 710 ++++++++++++++++++++++++ drivers/pci/msi.h | 5 drivers/xen/pciback/Makefile | 1 drivers/xen/pciback/conf_space_capability_msi.c | 60 ++ drivers/xen/pciback/pci_stub.c | 24 drivers/xen/pciback/pciback.h | 14 drivers/xen/pciback/pciback_ops.c | 38 + drivers/xen/pcifront/pci_op.c | 116 +++ include/asm-i386/io_apic.h | 2 include/asm-x86_64/io_apic.h | 2 include/asm-x86_64/msi.h | 9 include/linux/pci.h | 3 include/xen/evtchn.h | 14 include/xen/interface/io/pciif.h | 15 include/xen/interface/physdev.h | 32 + 16 files changed, 1034 insertions(+), 12 deletions(-) diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/pci/Kconfig --- a/drivers/pci/Kconfig Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/pci/Kconfig Thu May 01 10:35:30 2008 +0100 @@ -5,7 +5,6 @@ config PCI_MSI bool "Message Signaled Interrupts (MSI and MSI-X)" depends on PCI depends on (X86_LOCAL_APIC && X86_IO_APIC) || IA64 - depends on !XEN help This allows device drivers to enable MSI (Message Signaled Interrupts). Message Signaled Interrupts enable a device to diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/pci/msi-xen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/pci/msi-xen.c Thu May 01 10:35:30 2008 +0100 @@ -0,0 +1,710 @@ +/* + * File: msi.c + * Purpose: PCI Message Signaled Interrupt (MSI) + * + * Copyright (C) 2003-2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@xxxxxxxxx) + */ + +#include <linux/mm.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/smp_lock.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> + +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/smp.h> + +#include "pci.h" +#include "msi.h" + +static int pci_msi_enable = 1; + +static struct msi_ops *msi_ops; + +int msi_register(struct msi_ops *ops) +{ + msi_ops = ops; + return 0; +} + +static struct list_head msi_dev_head; +static int msi_dev_head_inited = 0; +DEFINE_SPINLOCK(msi_dev_lock); + +struct msi_dev_list { + struct pci_dev *dev; + struct list_head list; + spinlock_t pirq_list_lock; + struct list_head pirq_list_head; +}; + +struct msi_pirq_entry { + struct list_head list; + int pirq; + int entry_nr; +}; + +static struct msi_dev_list *get_msi_dev_pirq_list(struct pci_dev *dev) +{ + struct msi_dev_list *msi_dev_list, *ret = NULL; + unsigned long flags; + + if (!msi_dev_head_inited) { + INIT_LIST_HEAD(&msi_dev_head); + msi_dev_head_inited = 1; + } + + spin_lock_irqsave(&msi_dev_lock, flags); + + list_for_each_entry(msi_dev_list, &msi_dev_head, list) + if ( msi_dev_list->dev == dev ) + ret = msi_dev_list; + + if ( ret ) { + spin_unlock_irqrestore(&msi_dev_lock, flags); + return ret; + } + + /* Has not allocate msi_dev until now. */ + ret = kmalloc(sizeof(struct msi_dev_list), GFP_ATOMIC); + + /* Failed to allocate msi_dev structure */ + if ( !ret ) { + spin_unlock_irqrestore(&msi_dev_lock, flags); + return NULL; + } + + list_add_tail(&ret->list, &msi_dev_head); + spin_unlock_irqrestore(&msi_dev_lock, flags); + spin_lock_init(&ret->pirq_list_lock); + INIT_LIST_HEAD(&ret->pirq_list_head); + return ret; +} + +static int attach_pirq_entry(int pirq, int entry_nr, + struct msi_dev_list *msi_dev_entry) +{ + struct msi_pirq_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + unsigned long flags; + + if (!entry) + return -ENOMEM; + entry->pirq = pirq; + entry->entry_nr = entry_nr; + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + list_add_tail(&entry->list, &msi_dev_entry->pirq_list_head); + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + return 0; +} + +/* + * pciback will provide device's owner + */ +int (*get_owner)(struct pci_dev *dev); + +int register_msi_get_owner(int (*func)(struct pci_dev *dev)) +{ + if (get_owner) { + printk(KERN_WARNING "register msi_get_owner again\n"); + return -EEXIST; + } + get_owner = func; + return 0; +} + +int unregister_msi_get_owner(int (*func)(struct pci_dev *dev)) +{ + if (get_owner == func) + get_owner = NULL; + return 0; +} + +static int msi_get_dev_owner(struct pci_dev *dev) +{ + int owner = DOMID_SELF; + + BUG_ON(!is_initial_xendomain()); + if (get_owner && (owner = get_owner(dev)) >=0 ) { + printk(KERN_INFO "get owner for dev %x get %x \n", + dev->devfn, owner); + return owner; + } + else + return DOMID_SELF; +} + +static int msi_unmap_pirq(struct pci_dev *dev, int pirq) +{ + struct physdev_unmap_pirq unmap; + int rc; + domid_t domid = DOMID_SELF; + + domid = msi_get_dev_owner(dev); + unmap.domid = domid; + unmap.pirq = pirq; + + if ((rc = HYPERVISOR_physdev_op(PHYSDEVOP_unmap_pirq, &unmap))) + printk(KERN_WARNING "unmap irq %x failed\n", pirq); + + if (rc < 0) + return rc; + return 0; +} + +/* + * Protected by msi_lock + */ +static int msi_map_pirq_to_vector(struct pci_dev *dev, int pirq, + int entry_nr, int msi) +{ + struct physdev_map_pirq map_irq; + int rc; + domid_t domid = DOMID_SELF; + + domid = msi_get_dev_owner(dev); + + map_irq.domid = domid; + map_irq.type = MAP_PIRQ_TYPE_MSI; + map_irq.index = -1; + map_irq.pirq = pirq; + map_irq.msi_info.bus = dev->bus->number; + map_irq.msi_info.devfn = dev->devfn; + map_irq.msi_info.entry_nr = entry_nr; + map_irq.msi_info.msi = msi; + + if ((rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq))) + printk(KERN_WARNING "map irq failed\n"); + + if (rc < 0) + return rc; + + return map_irq.pirq; +} + +static int msi_map_vector(struct pci_dev *dev, int entry_nr, int msi) +{ + return msi_map_pirq_to_vector(dev, -1, entry_nr, msi); +} + +static int msi_init(void) +{ + static int status = 0; + + if (pci_msi_quirk) { + pci_msi_enable = 0; + printk(KERN_WARNING "PCI: MSI quirk detected. MSI disabled.\n"); + status = -EINVAL; + } + + return status; +} + +void pci_scan_msi_device(struct pci_dev *dev) { } + +void disable_msi_mode(struct pci_dev *dev, int pos, int type) +{ + u16 control; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (type == PCI_CAP_ID_MSI) { + /* Set enabled bits to single MSI & enable MSI_enable bit */ + msi_disable(control); + pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msi_enabled = 0; + } else { + msix_disable(control); + pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msix_enabled = 0; + } + if (pci_find_capability(dev, PCI_CAP_ID_EXP)) { + /* PCI Express Endpoint device detected */ + pci_intx(dev, 1); /* enable intx */ + } +} + +static void enable_msi_mode(struct pci_dev *dev, int pos, int type) +{ + u16 control; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (type == PCI_CAP_ID_MSI) { + /* Set enabled bits to single MSI & enable MSI_enable bit */ + msi_enable(control, 1); + pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msi_enabled = 1; + } else { + msix_enable(control); + pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msix_enabled = 1; + } + if (pci_find_capability(dev, PCI_CAP_ID_EXP)) { + /* PCI Express Endpoint device detected */ + pci_intx(dev, 0); /* disable intx */ + } +} + +#ifdef CONFIG_PM +int pci_save_msi_state(struct pci_dev *dev) +{ + int pos; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos <= 0 || dev->no_msi) + return 0; + + if (!dev->msi_enabled) + return 0; + + /* Restore dev->irq to its default pin-assertion vector */ + msi_unmap_pirq(dev, dev->irq); + /* Disable MSI mode */ + disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + /* Set the flags for use of restore */ + dev->msi_enabled = 1; + return 0; +} + +void pci_restore_msi_state(struct pci_dev *dev) +{ + int pos, pirq; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos <= 0) + return; + + if (!dev->msi_enabled) + return; + + pirq = msi_map_pirq_to_vector(dev, dev->irq, 0, 1); + if (pirq < 0) + return; + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); +} + +int pci_save_msix_state(struct pci_dev *dev) +{ + int pos; + unsigned long flags; + struct msi_dev_list *msi_dev_entry; + struct msi_pirq_entry *pirq_entry, *tmp; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos <= 0 || dev->no_msi) + return 0; + + /* save the capability */ + if (!dev->msix_enabled) + return 0; + + msi_dev_entry = get_msi_dev_pirq_list(dev); + + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + if (!list_empty_careful(&msi_dev_entry->pirq_list_head)) + list_for_each_entry_safe(pirq_entry, tmp, + &msi_dev_entry->pirq_list_head, list) + msi_unmap_pirq(dev, pirq_entry->pirq); + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + + disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + /* Set the flags for use of restore */ + dev->msix_enabled = 1; + + return 0; +} + +void pci_restore_msix_state(struct pci_dev *dev) +{ + int pos; + unsigned long flags; + struct msi_dev_list *msi_dev_entry; + struct msi_pirq_entry *pirq_entry, *tmp; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos <= 0) + return; + + if (!dev->msix_enabled) + return; + + msi_dev_entry = get_msi_dev_pirq_list(dev); + + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + list_for_each_entry_safe(pirq_entry, tmp, + &msi_dev_entry->pirq_list_head, list) + msi_map_pirq_to_vector(dev, pirq_entry->pirq, pirq_entry->entry_nr, 0); + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + + enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); +} +#endif + +/** + * msi_capability_init - configure device's MSI capability structure + * @dev: pointer to the pci_dev data structure of MSI device function + * + * Setup the MSI capability structure of device function with a single + * MSI vector, regardless of device function is capable of handling + * multiple messages. A return of zero indicates the successful setup + * of an entry zero with the new MSI vector or non-zero for otherwise. + **/ +static int msi_capability_init(struct pci_dev *dev) +{ + int pos, pirq; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + pci_read_config_word(dev, msi_control_reg(pos), &control); + + pirq = msi_map_vector(dev, 0, 1); + if (pirq < 0) + return -EBUSY; + + dev->irq = pirq; + /* Set MSI enabled bits */ + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + dev->msi_enabled = 1; + + return 0; +} + +/** + * msix_capability_init - configure device's MSI-X capability + * @dev: pointer to the pci_dev data structure of MSI-X device function + * @entries: pointer to an array of struct msix_entry entries + * @nvec: number of @entries + * + * Setup the MSI-X capability structure of device function with a + * single MSI-X vector. A return of zero indicates the successful setup of + * requested MSI-X entries with allocated vectors or non-zero for otherwise. + **/ +static int msix_capability_init(struct pci_dev *dev, + struct msix_entry *entries, int nvec) +{ + int pirq, i, pos; + struct msi_dev_list *msi_dev_entry = get_msi_dev_pirq_list(dev); + struct msi_pirq_entry *pirq_entry, *tmp; + unsigned long flags; + + if (!msi_dev_entry) + return -ENOMEM; + + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + if (!list_empty_careful(&msi_dev_entry->pirq_list_head)) + { + printk(KERN_WARNING "msix pirqs for dev %02x:%02x:%01x are not freed \ + before acquire again.\n", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + list_for_each_entry_safe(pirq_entry, tmp, + &msi_dev_entry->pirq_list_head, list) { + msi_unmap_pirq(dev, pirq_entry->pirq); + list_del(&pirq_entry->list); + kfree(pirq_entry); + } + } + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + + /* MSI-X Table Initialization */ + for (i = 0; i < nvec; i++) { + pirq = msi_map_vector(dev, entries[i].entry, 0); + if (pirq < 0) + break; + attach_pirq_entry(pirq, entries[i].entry, msi_dev_entry); + dev->irq = pirq; + (entries + i)->vector = pirq; + } + if (i != nvec) { + msi_unmap_pirq(dev, dev->irq); + (entries + i)->vector = 0; + return -EBUSY; + } + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + dev->msix_enabled = 1; + + return 0; +} + +/** + * pci_enable_msi - configure device's MSI capability structure + * @dev: pointer to the pci_dev data structure of MSI device function + * + * Setup the MSI capability structure of device function with + * a single MSI vector upon its software driver call to request for + * MSI mode enabled on its hardware device function. A return of zero + * indicates the successful setup of an entry zero with the new MSI + * vector or non-zero for otherwise. + **/ +extern int pci_frontend_enable_msi(struct pci_dev *dev); +int pci_enable_msi(struct pci_dev* dev) +{ + struct pci_bus *bus; + int pos, temp, status = -EINVAL; + + if (!pci_msi_enable || !dev) + return status; + + if (dev->no_msi) + return status; + + for (bus = dev->bus; bus; bus = bus->parent) + if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) + return -EINVAL; + + status = msi_init(); + if (status < 0) + return status; + +#ifdef CONFIG_XEN_PCIDEV_FRONTEND + if (!is_initial_xendomain()) + { + int ret; + + temp = dev->irq; + ret = pci_frontend_enable_msi(dev); + if (ret) + return ret; + + dev->irq_old = temp; + + return ret; + } +#endif + + temp = dev->irq; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!pos) + return -EINVAL; + + /* Check whether driver already requested for MSI-X vectors */ + if (dev->msix_enabled) { + printk(KERN_INFO "PCI: %s: Can't enable MSI. " + "Device already has MSI-X vectors assigned\n", + pci_name(dev)); + dev->irq = temp; + return -EINVAL; + } + + status = msi_capability_init(dev); + if ( !status ) + dev->irq_old = temp; + else + dev->irq = temp; + + return status; +} + +extern void pci_frontend_disable_msi(struct pci_dev* dev); +void pci_disable_msi(struct pci_dev* dev) +{ + int pos; + int pirq; + + if (!pci_msi_enable) + return; + if (!dev) + return; + +#ifdef CONFIG_XEN_PCIDEV_FRONTEND + if (!is_initial_xendomain()) { + pci_frontend_disable_msi(dev); + dev->irq = dev->irq_old; + return; + } +#endif + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!pos) + return; + + pirq = dev->irq; + /* Restore dev->irq to its default pin-assertion vector */ + dev->irq = dev->irq_old; + msi_unmap_pirq(dev, pirq); + + /* Disable MSI mode */ + disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); +} + +/** + * pci_enable_msix - configure device's MSI-X capability structure + * @dev: pointer to the pci_dev data structure of MSI-X device function + * @entries: pointer to an array of MSI-X entries + * @nvec: number of MSI-X vectors requested for allocation by device driver + * + * Setup the MSI-X capability structure of device function with the number + * of requested vectors upon its software driver call to request for + * MSI-X mode enabled on its hardware device function. A return of zero + * indicates the successful configuration of MSI-X capability structure + * with new allocated MSI-X vectors. A return of < 0 indicates a failure. + * Or a return of > 0 indicates that driver request is exceeding the number + * of vectors available. Driver should use the returned value to re-send + * its request. + **/ +extern int pci_frontend_enable_msix(struct pci_dev *dev, + struct msix_entry *entries, int nvec); +int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) +{ + struct pci_bus *bus; + int status, pos, nr_entries; + int i, j, temp; + u16 control; + + if (!pci_msi_enable || !dev || !entries) + return -EINVAL; + + if (dev->no_msi) + return -EINVAL; + + for (bus = dev->bus; bus; bus = bus->parent) + if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) + return -EINVAL; + +#ifdef CONFIG_XEN_PCIDEV_FRONTEND + if (!is_initial_xendomain()) { + int ret; + + ret = pci_frontend_enable_msix(dev, entries, nvec); + if (ret) { + printk("get %x from pci_frontend_enable_msix\n", ret); + return ret; + } + + return 0; + } +#endif + + status = msi_init(); + if (status < 0) + return status; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (!pos) + return -EINVAL; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + nr_entries = multi_msix_capable(control); + if (nvec > nr_entries) + return -EINVAL; + + /* Check for any invalid entries */ + for (i = 0; i < nvec; i++) { + if (entries[i].entry >= nr_entries) + return -EINVAL; /* invalid entry */ + for (j = i + 1; j < nvec; j++) { + if (entries[i].entry == entries[j].entry) + return -EINVAL; /* duplicate entry */ + } + } + + temp = dev->irq; + /* Check whether driver already requested for MSI vector */ + if (dev->msi_enabled) { + printk(KERN_INFO "PCI: %s: Can't enable MSI-X. " + "Device already has an MSI vector assigned\n", + pci_name(dev)); + dev->irq = temp; + return -EINVAL; + } + + status = msix_capability_init(dev, entries, nvec); + + if ( !status ) + dev->irq_old = temp; + else + dev->irq = temp; + + return status; +} + +extern void pci_frontend_disable_msix(struct pci_dev* dev); +void pci_disable_msix(struct pci_dev* dev) +{ + int pos; + u16 control; + + + if (!pci_msi_enable) + return; + if (!dev) + return; + +#ifdef CONFIG_XEN_PCIDEV_FRONTEND + if (!is_initial_xendomain()) { + pci_frontend_disable_msix(dev); + dev->irq = dev->irq_old; + return; + } +#endif + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (!pos) + return; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSIX_FLAGS_ENABLE)) + return; + + msi_remove_pci_irq_vectors(dev); + + /* Disable MSI mode */ + disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); +} + +/** + * msi_remove_pci_irq_vectors - reclaim MSI(X) vectors to unused state + * @dev: pointer to the pci_dev data structure of MSI(X) device function + * + * Being called during hotplug remove, from which the device function + * is hot-removed. All previous assigned MSI/MSI-X vectors, if + * allocated for this device function, are reclaimed to unused state, + * which may be used later on. + **/ +void msi_remove_pci_irq_vectors(struct pci_dev* dev) +{ + unsigned long flags; + struct msi_dev_list *msi_dev_entry; + struct msi_pirq_entry *pirq_entry, *tmp; + + if (!pci_msi_enable || !dev) + return; + + msi_dev_entry = get_msi_dev_pirq_list(dev); + + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + if (!list_empty_careful(&msi_dev_entry->pirq_list_head)) + { + printk(KERN_WARNING "msix pirqs for dev %02x:%02x:%01x are not freed \ + before acquire again.\n", dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + list_for_each_entry_safe(pirq_entry, tmp, + &msi_dev_entry->pirq_list_head, list) { + msi_unmap_pirq(dev, pirq_entry->pirq); + list_del(&pirq_entry->list); + kfree(pirq_entry); + } + } + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + dev->irq = dev->irq_old; +} + +void pci_no_msi(void) +{ + pci_msi_enable = 0; +} + +EXPORT_SYMBOL(pci_enable_msi); +EXPORT_SYMBOL(pci_disable_msi); +EXPORT_SYMBOL(pci_enable_msix); +EXPORT_SYMBOL(pci_disable_msix); +#ifdef CONFIG_XEN +EXPORT_SYMBOL(register_msi_get_owner); +EXPORT_SYMBOL(unregister_msi_get_owner); +#endif + diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/pci/msi.h --- a/drivers/pci/msi.h Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/pci/msi.h Thu May 01 10:35:30 2008 +0100 @@ -84,6 +84,11 @@ extern void (*interrupt[NR_IRQS])(void); extern void (*interrupt[NR_IRQS])(void); extern int pci_vector_resources(int last, int nr_released); +#ifdef CONFIG_XEN +extern int unregister_msi_get_owner(int (*func)(struct pci_dev *dev)); +extern int register_msi_get_owner(int (*func)(struct pci_dev *dev)); +#endif + /* * MSI-X Address Register */ diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/xen/pciback/Makefile --- a/drivers/xen/pciback/Makefile Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/xen/pciback/Makefile Thu May 01 10:35:30 2008 +0100 @@ -6,6 +6,7 @@ pciback-y += conf_space.o conf_space_hea conf_space_capability_vpd.o \ conf_space_capability_pm.o \ conf_space_quirks.o +pciback-$(CONFIG_PCI_MSI) += conf_space_capability_msi.o pciback-$(CONFIG_XEN_PCIDEV_BACKEND_VPCI) += vpci.o pciback-$(CONFIG_XEN_PCIDEV_BACKEND_SLOT) += slot.o pciback-$(CONFIG_XEN_PCIDEV_BACKEND_PASS) += passthrough.o diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/xen/pciback/conf_space_capability_msi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/pciback/conf_space_capability_msi.c Thu May 01 10:35:30 2008 +0100 @@ -0,0 +1,60 @@ +/* + * PCI Backend -- Configuration overlay for MSI capability + */ +#include <linux/pci.h> +#include "conf_space.h" +#include "conf_space_capability.h" +#include <xen/interface/io/pciif.h> +#include "pciback.h" + +int pciback_enable_msi(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op) +{ + int otherend = pdev->xdev->otherend_id; + int irq; + int status; + + status = pci_enable_msi(dev); + + if (status) { + printk("error enable msi for guest %x status %x\n", otherend, status); + op->value = 0; + return XEN_PCI_ERR_op_failed; + } + + op->value = dev->irq; + return 0; +} + +int pciback_disable_msi(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op) +{ + int old_irq = dev->irq; + + pci_disable_msi(dev); + + op->value = dev->irq; + return 0; +} + +int pciback_enable_msix(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op) +{ + int otherend = pdev->xdev->otherend_id, result, i; + + result = pci_enable_msix(dev, op->msix_entries, op->value); + + op->value = result; + return result; +} + +int pciback_disable_msix(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op) +{ + + pci_disable_msix(dev); + + op->value = dev->irq; + return 0; +} + diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/xen/pciback/pci_stub.c --- a/drivers/xen/pciback/pci_stub.c Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/xen/pciback/pci_stub.c Thu May 01 10:35:30 2008 +0100 @@ -805,6 +805,23 @@ static ssize_t permissive_show(struct de DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, permissive_show, permissive_add); +#ifdef CONFIG_PCI_MSI + +int pciback_get_owner(struct pci_dev *dev) +{ + struct pcistub_device *psdev; + + psdev = pcistub_device_find(pci_domain_nr(dev->bus), dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + /* XXX will other domain has pciback support ??? */ + if (!psdev || !psdev->pdev) { + printk(KERN_WARNING "no ownder\n"); + return -1; + } + return psdev->pdev->xdev->otherend_id; +} +#endif + static void pcistub_exit(void) { driver_remove_file(&pciback_pci_driver.driver, &driver_attr_new_slot); @@ -815,6 +832,9 @@ static void pcistub_exit(void) driver_remove_file(&pciback_pci_driver.driver, &driver_attr_permissive); pci_unregister_driver(&pciback_pci_driver); +#ifdef CONFIG_PCI_MSI + unregister_msi_get_owner(pciback_get_owner); +#endif } static int __init pcistub_init(void) @@ -872,6 +892,10 @@ static int __init pcistub_init(void) err = driver_create_file(&pciback_pci_driver.driver, &driver_attr_permissive); +#ifdef CONFIG_PCI_MSI + if (!err) + err = register_msi_get_owner(pciback_get_owner); +#endif if (err) pcistub_exit(); diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/xen/pciback/pciback.h --- a/drivers/xen/pciback/pciback.h Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/xen/pciback/pciback.h Thu May 01 10:35:30 2008 +0100 @@ -93,5 +93,19 @@ int pciback_xenbus_register(void); int pciback_xenbus_register(void); void pciback_xenbus_unregister(void); +#ifdef CONFIG_PCI_MSI +int pciback_enable_msi(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op); + +int pciback_disable_msi(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op); + + +int pciback_enable_msix(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op); + +int pciback_disable_msix(struct pciback_device *pdev, + struct pci_dev *dev, struct xen_pci_op *op); +#endif extern int verbose_request; #endif diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/xen/pciback/pciback_ops.c --- a/drivers/xen/pciback/pciback_ops.c Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/xen/pciback/pciback_ops.c Thu May 01 10:35:30 2008 +0100 @@ -61,15 +61,37 @@ void pciback_do_op(void *data) if (dev == NULL) op->err = XEN_PCI_ERR_dev_not_found; - else if (op->cmd == XEN_PCI_OP_conf_read) - op->err = pciback_config_read(dev, op->offset, op->size, - &op->value); - else if (op->cmd == XEN_PCI_OP_conf_write) - op->err = pciback_config_write(dev, op->offset, op->size, - op->value); else - op->err = XEN_PCI_ERR_not_implemented; - + { + switch (op->cmd) + { + case XEN_PCI_OP_conf_read: + op->err = pciback_config_read(dev, + op->offset, op->size, &op->value); + break; + case XEN_PCI_OP_conf_write: + op->err = pciback_config_write(dev, + op->offset, op->size, op->value); + break; +#ifdef CONFIG_PCI_MSI + case XEN_PCI_OP_enable_msi: + op->err = pciback_enable_msi(pdev, dev, op); + break; + case XEN_PCI_OP_disable_msi: + op->err = pciback_disable_msi(pdev, dev, op); + break; + case XEN_PCI_OP_enable_msix: + op->err = pciback_enable_msix(pdev, dev, op); + break; + case XEN_PCI_OP_disable_msix: + op->err = pciback_disable_msix(pdev, dev, op); + break; +#endif + default: + op->err = XEN_PCI_ERR_not_implemented; + break; + } + } /* Tell the driver domain that we're done. */ wmb(); clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); diff -r 81c5a517a42b -r 9f9b4214bec8 drivers/xen/pcifront/pci_op.c --- a/drivers/xen/pcifront/pci_op.c Tue Apr 22 18:56:27 2008 +0100 +++ b/drivers/xen/pcifront/pci_op.c Thu May 01 10:35:30 2008 +0100 @@ -277,6 +277,122 @@ struct pci_ops pcifront_bus_ops = { .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 void pcifront_claim_resource(struct pci_dev *dev, void *data) { diff -r 81c5a517a42b -r 9f9b4214bec8 include/asm-i386/io_apic.h --- a/include/asm-i386/io_apic.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/asm-i386/io_apic.h Thu May 01 10:35:30 2008 +0100 @@ -12,7 +12,7 @@ #ifdef CONFIG_X86_IO_APIC -#ifdef CONFIG_PCI_MSI +#if defined(CONFIG_PCI_MSI) && !defined(CONFIG_XEN) static inline int use_pci_vector(void) {return 1;} static inline void disable_edge_ioapic_vector(unsigned int vector) { } static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { } diff -r 81c5a517a42b -r 9f9b4214bec8 include/asm-x86_64/io_apic.h --- a/include/asm-x86_64/io_apic.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/asm-x86_64/io_apic.h Thu May 01 10:35:30 2008 +0100 @@ -12,7 +12,7 @@ #ifdef CONFIG_X86_IO_APIC -#ifdef CONFIG_PCI_MSI +#if defined(CONFIG_PCI_MSI) && !defined(CONFIG_XEN) static inline int use_pci_vector(void) {return 1;} static inline void disable_edge_ioapic_vector(unsigned int vector) { } static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { } diff -r 81c5a517a42b -r 9f9b4214bec8 include/asm-x86_64/msi.h --- a/include/asm-x86_64/msi.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/asm-x86_64/msi.h Thu May 01 10:35:30 2008 +0100 @@ -7,14 +7,21 @@ #define ASM_MSI_H #include <asm/desc.h> +#ifndef CONFIG_XEN #include <asm/mach_apic.h> +#endif #include <asm/smp.h> +#ifndef CONFIG_XEN #define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) +#else +#define LAST_DYNAMIC_VECTOR 0xdf +#define LAST_DEVICE_VECTOR (LAST_DYNAMIC_VECTOR) +#endif + #define MSI_TARGET_CPU_SHIFT 12 extern struct msi_ops msi_apic_ops; - static inline int msi_arch_init(void) { msi_register(&msi_apic_ops); diff -r 81c5a517a42b -r 9f9b4214bec8 include/linux/pci.h --- a/include/linux/pci.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/linux/pci.h Thu May 01 10:35:30 2008 +0100 @@ -152,6 +152,9 @@ struct pci_dev { * directly, use the values stored here. They might be different! */ unsigned int irq; +#ifdef CONFIG_XEN + unsigned int irq_old; +#endif struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ /* These fields are used by common fixups */ diff -r 81c5a517a42b -r 9f9b4214bec8 include/xen/evtchn.h --- a/include/xen/evtchn.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/xen/evtchn.h Thu May 01 10:35:30 2008 +0100 @@ -136,4 +136,18 @@ void notify_remote_via_irq(int irq); void notify_remote_via_irq(int irq); int irq_to_evtchn_port(int irq); +#define PIRQ_SET_MAPPING 0x0 +#define PIRQ_CLEAR_MAPPING 0x1 +#define PIRQ_GET_MAPPING 0x3 +int pirq_mapstatus(int pirq, int action); +int set_pirq_hw_action(int pirq, int (*action)(int pirq, int action)); +int clear_pirq_hw_action(int pirq); + +#define PIRQ_STARTUP 1 +#define PIRQ_SHUTDOWN 2 +#define PIRQ_ENABLE 3 +#define PIRQ_DISABLE 4 +#define PIRQ_END 5 +#define PIRQ_ACK 6 + #endif /* __ASM_EVTCHN_H__ */ diff -r 81c5a517a42b -r 9f9b4214bec8 include/xen/interface/io/pciif.h --- a/include/xen/interface/io/pciif.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/xen/interface/io/pciif.h Thu May 01 10:35:30 2008 +0100 @@ -34,6 +34,10 @@ /* xen_pci_op commands */ #define XEN_PCI_OP_conf_read (0) #define XEN_PCI_OP_conf_write (1) +#define XEN_PCI_OP_enable_msi (2) +#define XEN_PCI_OP_enable_msix (3) +#define XEN_PCI_OP_disable_msi (4) +#define XEN_PCI_OP_disable_msix (5) /* xen_pci_op error numbers */ #define XEN_PCI_ERR_success (0) @@ -43,6 +47,12 @@ #define XEN_PCI_ERR_not_implemented (-4) /* XEN_PCI_ERR_op_failed - backend failed to complete the operation */ #define XEN_PCI_ERR_op_failed (-5) + +/* + * it should be PAGE_SIZE-sizeof(struct xen_pci_op))/sizeof(struct msix_entry)) + * Should not exceed 128 + */ +#define SH_INFO_MAX_VEC 128 struct xen_pci_op { /* IN: what action to perform: XEN_PCI_OP_* */ @@ -62,6 +72,11 @@ struct xen_pci_op { /* IN/OUT: Contains the result after a READ or the value to WRITE */ uint32_t value; + /* IN: Contains extra infor for this operation */ + uint32_t info; + /*IN: param for msi-x */ + struct msix_entry msix_entries[SH_INFO_MAX_VEC]; + }; struct xen_pci_sharedinfo { diff -r 81c5a517a42b -r 9f9b4214bec8 include/xen/interface/physdev.h --- a/include/xen/interface/physdev.h Tue Apr 22 18:56:27 2008 +0100 +++ b/include/xen/interface/physdev.h Thu May 01 10:35:30 2008 +0100 @@ -122,6 +122,38 @@ typedef struct physdev_irq physdev_irq_t typedef struct physdev_irq physdev_irq_t; DEFINE_XEN_GUEST_HANDLE(physdev_irq_t); +#define MAP_PIRQ_TYPE_MSI 0x0 +#define MAP_PIRQ_TYPE_GSI 0x1 +#define MAP_PIRQ_TYPE_UNKNOWN 0x2 + +#define PHYSDEVOP_map_pirq 13 +struct physdev_map_pirq { + domid_t domid; + /* IN */ + int type; + /* IN */ + int index; + /* IN or OUT */ + int pirq; + /* msi info passed to VMM */ + struct { + int bus, devfn, entry_nr; + int msi; /* 0 - MSIX 1 - MSI */ + } msi_info; +}; +typedef struct physdev_map_pirq physdev_map_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_map_pirq_t); + +#define PHYSDEVOP_unmap_pirq 14 +struct physdev_unmap_pirq { + domid_t domid; + /* IN */ + int pirq; +}; + +typedef struct physdev_unmap_pirq physdev_unmap_pirq_t; +DEFINE_XEN_GUEST_HANDLE(physdev_unmap_pirq_t); + /* * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op() * hypercall since 0x00030202. _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |