From: ddugger@redhat.com <ddugger@redhat.com> Date: Mon, 6 Apr 2009 15:51:41 -0600 Subject: [pci] xen: dom0/domU MSI support using PHSYDEV_map_irq Message-id: 200904062151.n36Lpf95013502@sobek.n0ano.com O-Subject: [RHEL5.4 PATCH 1/3] BZ484227: VTD: dom0/domU MSI support using PHSYDEV_map_irq Bugzilla: 484227 RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com> Add in MSI support (from changeset 525) and move Xen variable out of the pci_dev struct (from changeset 790) Moving the msi related variable irq_old out of struct pci_dev is logically more consistent and has the additional benefit that xen kernel and vanilla kernel now have the same pci_dev layout Upstream Status: Accepted (CS 525, 790) Signed-off-by: Qing He <qing.he@intel.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Qing He <qing.he@intel.com> Signed-off-by: Don Dugger <donald.d.dugger@intel.com> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 3c23973..14e6e02 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -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 || PPC64 - depends on !XEN help This allows device drivers to enable MSI (Message Signaled Interrupts). Message Signaled Interrupts enable a device to diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 8b96bbf..6d93152 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -49,3 +49,8 @@ endif ifeq ($(CONFIG_PCI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif + +ifdef CONFIG_XEN +include $(srctree)/scripts/Makefile.xen +obj-y := $(call cherrypickxen, $(obj-y)) +endif diff --git a/drivers/pci/msi-xen.c b/drivers/pci/msi-xen.c new file mode 100644 index 0000000..afa83d4 --- /dev/null +++ b/drivers/pci/msi-xen.c @@ -0,0 +1,840 @@ +/* + * File: msi.c + * Purpose: PCI Message Signaled Interrupt (MSI) + * + * Copyright (C) 2003-2004 Intel + * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) + */ + +#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 <xen/evtchn.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 LIST_HEAD(msi_dev_head); +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; + /* Used for saving/restoring MSI-X tables */ + void __iomem *mask_base; + /* Store default pre-assigned irq */ + unsigned int default_irq; +}; + +struct msi_pirq_entry { + struct list_head list; + int pirq; + int entry_nr; +#ifdef CONFIG_PM + /* PM save area for MSIX address/data */ + u32 address_hi_save; + u32 address_lo_save; + u32 data_save; +#endif +}; + +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; + + 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 = kzalloc(sizeof(struct msi_dev_list), GFP_ATOMIC); + + /* Failed to allocate msi_dev structure */ + if ( !ret ) { + spin_unlock_irqrestore(&msi_dev_lock, flags); + return NULL; + } + + ret->dev = dev; + spin_lock_init(&ret->pirq_list_lock); + INIT_LIST_HEAD(&ret->pirq_list_head); + list_add_tail(&ret->list, &msi_dev_head); + spin_unlock_irqrestore(&msi_dev_lock, flags); + 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; +} + +static void detach_pirq_entry(int entry_nr, + struct msi_dev_list *msi_dev_entry) +{ + unsigned long flags; + struct msi_pirq_entry *pirq_entry; + + list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) { + if (pirq_entry->entry_nr == entry_nr) { + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + list_del(&pirq_entry->list); + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + kfree(pirq_entry); + return; + } + } +} + +/* + * pciback will provide device's owner + */ +static 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) + return -EINVAL; + get_owner = NULL; + return 0; +} + +static int msi_get_dev_owner(struct pci_dev *dev) +{ + int owner; + + 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; + } + + return DOMID_SELF; +} + +static int msi_unmap_pirq(struct pci_dev *dev, int pirq) +{ + struct physdev_unmap_pirq unmap; + int rc; + + unmap.domid = msi_get_dev_owner(dev); + 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; +} + +static u64 find_table_base(struct pci_dev *dev, int pos) +{ + u8 bar; + u32 reg; + unsigned long flags; + + pci_read_config_dword(dev, msix_table_offset_reg(pos), ®); + bar = reg & PCI_MSIX_FLAGS_BIRMASK; + + flags = pci_resource_flags(dev, bar); + if (flags & (IORESOURCE_DISABLED | IORESOURCE_UNSET | IORESOURCE_BUSY)) + return 0; + + return pci_resource_start(dev, bar); +} + +/* + * Protected by msi_lock + */ +static int msi_map_pirq_to_vector(struct pci_dev *dev, int pirq, + int entry_nr, u64 table_base) +{ + 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 < 0 ? -1 : pirq; + map_irq.bus = dev->bus->number; + map_irq.devfn = dev->devfn; + map_irq.entry_nr = entry_nr; + map_irq.table_base = table_base; + + if ((rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq))) + printk(KERN_WARNING "map irq failed\n"); + + if (rc < 0) + return rc; + /* This happens when MSI support is not enabled in Xen. */ + if (rc == 0 && map_irq.pirq < 0) + return -ENOSYS; + + BUG_ON(map_irq.pirq <= 0); + + return map_irq.pirq; +} + +static int msi_map_vector(struct pci_dev *dev, int entry_nr, u64 table_base) +{ + return msi_map_pirq_to_vector(dev, -1, entry_nr, table_base); +} + +static int msi_init(void) +{ + /* backport: checking pci_msi_quirk */ + return 0; +} + +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, i = 0; + u16 control; + struct pci_cap_saved_state *save_state; + u32 *cap; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos <= 0 || dev->no_msi) + return 0; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSI_FLAGS_ENABLE)) + return 0; + + save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5, + GFP_KERNEL); + if (!save_state) { + printk(KERN_ERR "Out of memory in pci_save_msi_state\n"); + return -ENOMEM; + } + cap = &save_state->data[0]; + + pci_read_config_dword(dev, pos, &cap[i++]); + control = cap[0] >> 16; + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]); + if (control & PCI_MSI_FLAGS_64BIT) { + pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]); + pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]); + } else + pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]); + if (control & PCI_MSI_FLAGS_MASKBIT) + pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]); + save_state->cap_nr = PCI_CAP_ID_MSI; + pci_add_saved_cap(dev, save_state); + return 0; +} + +void pci_restore_msi_state(struct pci_dev *dev) +{ + int i = 0, pos; + u16 control; + struct pci_cap_saved_state *save_state; + u32 *cap; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSI); + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (!save_state || pos <= 0) + return; + cap = &save_state->data[0]; + + control = cap[i++] >> 16; + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, cap[i++]); + if (control & PCI_MSI_FLAGS_64BIT) { + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, cap[i++]); + pci_write_config_dword(dev, pos + PCI_MSI_DATA_64, cap[i++]); + } else + pci_write_config_dword(dev, pos + PCI_MSI_DATA_32, cap[i++]); + if (control & PCI_MSI_FLAGS_MASKBIT) + pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT, cap[i++]); + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + pci_remove_saved_cap(save_state); + kfree(save_state); +} +EXPORT_SYMBOL_GPL(pci_restore_msi_state); + +int pci_save_msix_state(struct pci_dev *dev) +{ + int pos; + u16 control; + struct pci_cap_saved_state *save_state; + unsigned long flags; + struct msi_dev_list *msi_dev_entry; + struct msi_pirq_entry *pirq_entry; + void __iomem *base; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos <= 0 || dev->no_msi) + return 0; + + /* save the capability */ + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (!(control & PCI_MSIX_FLAGS_ENABLE)) + return 0; + + msi_dev_entry = get_msi_dev_pirq_list(dev); + /* If we failed to map the MSI-X table at pci_enable_msix, + * We could not support saving them here. + */ + if (!(base = msi_dev_entry->mask_base)) + return -ENOMEM; + + save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16), + GFP_KERNEL); + if (!save_state) { + printk(KERN_ERR "Out of memory in pci_save_msix_state\n"); + return -ENOMEM; + } + *((u16 *)&save_state->data[0]) = control; + + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) { + int j; + + /* save the table */ + j = pirq_entry->entry_nr; + pirq_entry->address_lo_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + pirq_entry->address_hi_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + pirq_entry->data_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + } + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + + save_state->cap_nr = PCI_CAP_ID_MSIX; + pci_add_saved_cap(dev, save_state); + return 0; +} + +void pci_restore_msix_state(struct pci_dev *dev) +{ + u16 save; + int pos, j; + void __iomem *base; + struct pci_cap_saved_state *save_state; + unsigned long flags; + struct msi_dev_list *msi_dev_entry; + struct msi_pirq_entry *pirq_entry; + + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX); + if (!save_state) + return; + + save = *((u16 *)&save_state->data[0]); + pci_remove_saved_cap(save_state); + kfree(save_state); + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos <= 0) + return; + + msi_dev_entry = get_msi_dev_pirq_list(dev); + base = msi_dev_entry->mask_base; + + spin_lock_irqsave(&msi_dev_entry->pirq_list_lock, flags); + list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) { + /* route the table */ + j = pirq_entry->entry_nr; + writel(pirq_entry->address_lo_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + writel(pirq_entry->address_hi_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + writel(pirq_entry->data_save, + base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + } + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + + pci_write_config_word(dev, msi_control_reg(pos), save); + 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, 0); + 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) +{ + u64 table_base; + u16 control; + int pirq, i, j, mapped, pos, nr_entries; + struct msi_dev_list *msi_dev_entry = get_msi_dev_pirq_list(dev); + struct msi_pirq_entry *pirq_entry; + + if (!msi_dev_entry) + return -ENOMEM; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + table_base = find_table_base(dev, pos); + if (!table_base) + return -ENODEV; + + pci_read_config_word(dev, msi_control_reg(pos), &control); + nr_entries = multi_msix_capable(control); + if (!msi_dev_entry->mask_base) + msi_dev_entry->mask_base = + ioremap_nocache(table_base, nr_entries * PCI_MSIX_ENTRY_SIZE); + + /* MSI-X Table Initialization */ + for (i = 0; i < nvec; i++) { + mapped = 0; + list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) { + if (pirq_entry->entry_nr == entries[i].entry) { + printk(KERN_WARNING "msix entry %d for dev %02x:%02x:%01x are \ + not freed before acquire again.\n", entries[i].entry, + dev->bus->number, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + (entries + i)->vector = pirq_entry->pirq; + mapped = 1; + break; + } + } + if (mapped) + continue; + pirq = msi_map_vector(dev, entries[i].entry, table_base); + if (pirq < 0) + break; + attach_pirq_entry(pirq, entries[i].entry, msi_dev_entry); + (entries + i)->vector = pirq; + } + + if (i != nvec) { + for (j = --i; j >= 0; j--) { + msi_unmap_pirq(dev, entries[j].vector); + detach_pirq_entry(entries[j].entry, msi_dev_entry); + entries[j].vector = 0; + } + return -EBUSY; + } + + 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; + struct msi_dev_list *msi_dev_entry = get_msi_dev_pirq_list(dev); + + 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; + + msi_dev_entry->default_irq = 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 ) + msi_dev_entry->default_irq = 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; + struct msi_dev_list *msi_dev_entry = get_msi_dev_pirq_list(dev); + + if (!pci_msi_enable) + return; + if (!dev) + return; + +#ifdef CONFIG_XEN_PCIDEV_FRONTEND + if (!is_initial_xendomain()) { + pci_frontend_disable_msi(dev); + dev->irq = msi_dev_entry->default_irq; + 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 = msi_dev_entry->default_irq; + 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; + struct msi_dev_list *msi_dev_entry = get_msi_dev_pirq_list(dev); + + 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; + + temp = dev->irq; + ret = pci_frontend_enable_msix(dev, entries, nvec); + if (ret) { + printk("get %x from pci_frontend_enable_msix\n", ret); + return ret; + } + msi_dev_entry->default_irq = temp; + + 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 ) + msi_dev_entry->default_irq = 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; + struct msi_dev_list *msi_dev_entry = get_msi_dev_pirq_list(dev); + + + if (!pci_msi_enable) + return; + if (!dev) + return; + +#ifdef CONFIG_XEN_PCIDEV_FRONTEND + if (!is_initial_xendomain()) { + pci_frontend_disable_msix(dev); + dev->irq = msi_dev_entry->default_irq; + 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(&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); + list_del(&pirq_entry->list); + kfree(pirq_entry); + } + spin_unlock_irqrestore(&msi_dev_entry->pirq_list_lock, flags); + iounmap(msi_dev_entry->mask_base); + msi_dev_entry->mask_base = NULL; + dev->irq = msi_dev_entry->default_irq; +} + +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 --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 61cb569..ff97ba8 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -43,17 +43,19 @@ extern void pci_remove_legacy_files(struct pci_bus *bus); extern struct rw_semaphore pci_bus_sem; extern unsigned int pci_pm_d3_delay; -#ifdef CONFIG_PCI_MSI +#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PM) void disable_msi_mode(struct pci_dev *dev, int pos, int type); void pci_no_msi(void); int pci_save_msi_state(struct pci_dev *dev); int pci_save_msix_state(struct pci_dev *dev); +void pci_restore_msi_state(struct pci_dev *dev); void pci_restore_msix_state(struct pci_dev *dev); #else static inline void disable_msi_mode(struct pci_dev *dev, int pos, int type) { } static inline void pci_no_msi(void) { } static inline int pci_save_msi_state(struct pci_dev *dev) { return 0; } static inline int pci_save_msix_state(struct pci_dev *dev) { return 0; } +static inline void pci_restore_msi_state(struct pci_dev *dev) {} static inline void pci_restore_msix_state(struct pci_dev *dev) {} #endif diff --git a/drivers/xen/pciback/Makefile b/drivers/xen/pciback/Makefile index c7185fb..1aa5df2 100644 --- a/drivers/xen/pciback/Makefile +++ b/drivers/xen/pciback/Makefile @@ -6,6 +6,7 @@ pciback-y += conf_space.o conf_space_header.o \ 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 --git a/drivers/xen/pciback/conf_space_capability_msi.c b/drivers/xen/pciback/conf_space_capability_msi.c new file mode 100644 index 0000000..762e396 --- /dev/null +++ b/drivers/xen/pciback/conf_space_capability_msi.c @@ -0,0 +1,79 @@ +/* + * PCI Backend -- Configuration overlay for MSI capability + */ +#include <linux/pci.h> +#include <linux/slab.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 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) +{ + 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 i, result; + struct msix_entry *entries; + + if (op->value > SH_INFO_MAX_VEC) + return -EINVAL; + + entries = kmalloc(op->value * sizeof(*entries), GFP_KERNEL); + if (entries == NULL) + return -ENOMEM; + + for (i = 0; i < op->value; i++) { + entries[i].entry = op->msix_entries[i].entry; + entries[i].vector = op->msix_entries[i].vector; + } + + result = pci_enable_msix(dev, entries, op->value); + + for (i = 0; i < op->value; i++) { + op->msix_entries[i].entry = entries[i].entry; + op->msix_entries[i].vector = entries[i].vector; + } + + kfree(entries); + + 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 --git a/drivers/xen/pciback/pci_stub.c b/drivers/xen/pciback/pci_stub.c index b338559..68fa0c4 100644 --- a/drivers/xen/pciback/pci_stub.c +++ b/drivers/xen/pciback/pci_stub.c @@ -809,6 +809,23 @@ static ssize_t permissive_show(struct device_driver *drv, char *buf) 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 owner\n"); + return -1; + } + return psdev->pdev->xdev->otherend_id; +} +#endif + static int __init pcistub_init(void) { int pos = 0; @@ -856,6 +873,8 @@ static int __init pcistub_init(void) driver_create_file(&pciback_pci_driver.driver, &driver_attr_quirks); driver_create_file(&pciback_pci_driver.driver, &driver_attr_permissive); + err = register_msi_get_owner(pciback_get_owner); + out: return err; @@ -908,6 +927,7 @@ static void __exit pciback_cleanup(void) driver_remove_file(&pciback_pci_driver.driver, &driver_attr_permissive); pci_unregister_driver(&pciback_pci_driver); + WARN_ON(unregister_msi_get_owner(pciback_get_owner)); } module_init(pciback_init); diff --git a/drivers/xen/pciback/pciback.h b/drivers/xen/pciback/pciback.h index 4b876e4..35e8f74 100644 --- a/drivers/xen/pciback/pciback.h +++ b/drivers/xen/pciback/pciback.h @@ -89,5 +89,19 @@ void pciback_do_op(void *data); 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 --git a/drivers/xen/pciback/pciback_ops.c b/drivers/xen/pciback/pciback_ops.c index 690941b..6c67a03 100644 --- a/drivers/xen/pciback/pciback_ops.c +++ b/drivers/xen/pciback/pciback_ops.c @@ -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 --git a/drivers/xen/pcifront/pci_op.c b/drivers/xen/pcifront/pci_op.c index 7619ccf..a1418c1 100644 --- a/drivers/xen/pcifront/pci_op.c +++ b/drivers/xen/pcifront/pci_op.c @@ -161,6 +161,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 --git a/include/asm-i386/io_apic.h b/include/asm-i386/io_apic.h index 5092e81..99f7e40 100644 --- a/include/asm-i386/io_apic.h +++ b/include/asm-i386/io_apic.h @@ -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 --git a/include/asm-i386/msi.h b/include/asm-i386/msi.h index b11c4b7..daef927 100644 --- a/include/asm-i386/msi.h +++ b/include/asm-i386/msi.h @@ -7,9 +7,17 @@ #define ASM_MSI_H #include <asm/desc.h> +#ifndef CONFIG_XEN #include <mach_apic.h> +#endif +#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; diff --git a/include/asm-x86_64/io_apic.h b/include/asm-x86_64/io_apic.h index 21fff71..9a81eaf 100644 --- a/include/asm-x86_64/io_apic.h +++ b/include/asm-x86_64/io_apic.h @@ -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 --git a/include/asm-x86_64/msi.h b/include/asm-x86_64/msi.h index 3ad2346..00a2016 100644 --- a/include/asm-x86_64/msi.h +++ b/include/asm-x86_64/msi.h @@ -7,10 +7,18 @@ #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; diff --git a/include/linux/pci.h b/include/linux/pci.h index 736aa36..5dccf2a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -663,6 +663,10 @@ static inline int pci_enable_msix(struct pci_dev* dev, static inline void pci_disable_msix(struct pci_dev *dev) {} static inline void msi_remove_pci_irq_vectors(struct pci_dev *dev) {} static inline void pci_restore_msi_state(struct pci_dev *dev) {} +#ifdef CONFIG_XEN +#define register_msi_get_owner(func) 0 +#define unregister_msi_get_owner(func) 0 +#endif #else extern void pci_scan_msi_device(struct pci_dev *dev); extern int pci_enable_msi(struct pci_dev *dev); @@ -672,6 +676,10 @@ extern int pci_enable_msix(struct pci_dev* dev, extern void pci_disable_msix(struct pci_dev *dev); extern void msi_remove_pci_irq_vectors(struct pci_dev *dev); extern void pci_restore_msi_state(struct pci_dev *dev); +#ifdef CONFIG_XEN +extern int register_msi_get_owner(int (*func)(struct pci_dev *dev)); +extern int unregister_msi_get_owner(int (*func)(struct pci_dev *dev)); +#endif #endif #ifndef CONFIG_EEH diff --git a/include/xen/evtchn.h b/include/xen/evtchn.h index 0889060..14de1ce 100644 --- a/include/xen/evtchn.h +++ b/include/xen/evtchn.h @@ -117,4 +117,18 @@ static inline void notify_remote_via_evtchn(int port) */ extern void notify_remote_via_irq(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 --git a/include/xen/interface/io/pciif.h b/include/xen/interface/io/pciif.h index a1c9ab7..2cd466a 100644 --- a/include/xen/interface/io/pciif.h +++ b/include/xen/interface/io/pciif.h @@ -16,6 +16,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) @@ -26,6 +30,12 @@ /* 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_* */ uint32_t cmd; @@ -44,6 +54,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 --git a/include/xen/interface/physdev.h b/include/xen/interface/physdev.h index 9b0baee..03e3413 100644 --- a/include/xen/interface/physdev.h +++ b/include/xen/interface/physdev.h @@ -98,6 +98,41 @@ struct physdev_irq { }; 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; + /* IN */ + int bus; + /* IN */ + int devfn; + /* IN */ + int entry_nr; + /* IN */ + uint64_t table_base; +}; +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() @@ -133,6 +168,7 @@ DEFINE_XEN_GUEST_HANDLE(physdev_op_t); #define PHYSDEVOP_APIC_READ PHYSDEVOP_apic_read #define PHYSDEVOP_APIC_WRITE PHYSDEVOP_apic_write #define PHYSDEVOP_ASSIGN_VECTOR PHYSDEVOP_alloc_irq_vector +#define PHYSDEVOP_FREE_VECTOR PHYSDEVOP_free_irq_vector #define PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY XENIRQSTAT_needs_eoi #define PHYSDEVOP_IRQ_SHARED XENIRQSTAT_shared