Currently, msix table pages are allocated a fixmap page per vector,
the available fixmap pages will be depleted when assigning devices with
large number of vectors. This patch fixes it, and a bug that prevents
cross-page MSI-X table from working properly
Signed-off-by: Qing He <qing.he@xxxxxxxxx>
---
arch/x86/msi.c | 109 +++++++++++++++++++++++++++++++++-------------
drivers/passthrough/pci.c | 1
include/asm-x86/fixmap.h | 2
include/asm-x86/msi.h | 6 +-
include/xen/pci.h | 6 ++
5 files changed, 90 insertions(+), 34 deletions(-)
diff -r c3b5e36248c9 xen/arch/x86/msi.c
--- a/xen/arch/x86/msi.c Tue Feb 03 18:14:19 2009 +0000
+++ b/xen/arch/x86/msi.c Wed Feb 04 14:41:03 2009 +0800
@@ -29,17 +29,17 @@
/* bitmap indicate which fixed map is free */
DEFINE_SPINLOCK(msix_fixmap_lock);
-DECLARE_BITMAP(msix_fixmap_pages, MAX_MSIX_PAGES);
+DECLARE_BITMAP(msix_fixmap_pages, FIX_MSIX_MAX_PAGES);
static int msix_fixmap_alloc(void)
{
- int i, rc = -1;
+ int i, rc = -ENOMEM;
spin_lock(&msix_fixmap_lock);
- for ( i = 0; i < MAX_MSIX_PAGES; i++ )
+ for ( i = 0; i < FIX_MSIX_MAX_PAGES; i++ )
if ( !test_bit(i, &msix_fixmap_pages) )
break;
- if ( i == MAX_MSIX_PAGES )
+ if ( i == FIX_MSIX_MAX_PAGES )
goto out;
rc = FIX_MSIX_IO_RESERV_BASE + i;
set_bit(i, &msix_fixmap_pages);
@@ -51,8 +51,66 @@ static int msix_fixmap_alloc(void)
static void msix_fixmap_free(int idx)
{
+ spin_lock(&msix_fixmap_lock);
if ( idx >= FIX_MSIX_IO_RESERV_BASE )
clear_bit(idx - FIX_MSIX_IO_RESERV_BASE, &msix_fixmap_pages);
+ spin_unlock(&msix_fixmap_lock);
+}
+
+static int msix_get_fixmap(struct pci_dev *dev, unsigned long table_paddr,
+ unsigned long entry_paddr)
+{
+ int nr_page, idx;
+
+ nr_page = (entry_paddr >> PAGE_SHIFT) - (table_paddr >> PAGE_SHIFT);
+
+ if ( nr_page < 0 || nr_page >= MAX_MSIX_TABLE_PAGES )
+ return -EINVAL;
+
+ spin_lock(&dev->msix_table_lock);
+ if ( dev->msix_table_refcnt[nr_page]++ == 0 )
+ {
+ idx = msix_fixmap_alloc();
+ if ( idx < 0 )
+ {
+ dev->msix_table_refcnt[nr_page]--;
+ goto out;
+ }
+ set_fixmap_nocache(idx, entry_paddr);
+ dev->msix_table_idx[nr_page] = idx;
+ }
+ else
+ idx = dev->msix_table_idx[nr_page];
+
+ out:
+ spin_unlock(&dev->msix_table_lock);
+ return idx;
+}
+
+static void msix_put_fixmap(struct pci_dev *dev, int idx)
+{
+ int i;
+ unsigned long start;
+
+ spin_lock(&dev->msix_table_lock);
+ for ( i = 0; i < MAX_MSIX_TABLE_PAGES; i++ )
+ {
+ if ( dev->msix_table_idx[i] == idx )
+ break;
+ }
+ if ( i == MAX_MSIX_TABLE_PAGES )
+ goto out;
+
+ if ( --dev->msix_table_refcnt[i] == 0 )
+ {
+ start = fix_to_virt(idx);
+ destroy_xen_mappings(start, start + PAGE_SIZE);
+ msix_fixmap_free(idx);
+ dev->msix_table_idx[i] = 0;
+ }
+
+ out:
+ spin_unlock(&dev->msix_table_lock);
}
/*
@@ -122,8 +180,7 @@ static void read_msi_msg(struct msi_desc
case PCI_CAP_ID_MSIX:
{
void __iomem *base;
- base = entry->mask_base +
- entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
+ base = entry->mask_base;
msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET);
@@ -199,8 +256,7 @@ static void write_msi_msg(struct msi_des
case PCI_CAP_ID_MSIX:
{
void __iomem *base;
- base = entry->mask_base +
- entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE;
+ base = entry->mask_base;
writel(msg->address_lo,
base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET);
@@ -288,8 +344,7 @@ static void msix_flush_writes(unsigned i
break;
case PCI_CAP_ID_MSIX:
{
- int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
+ int offset = PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
readl(entry->mask_base + offset);
break;
}
@@ -330,8 +385,7 @@ static void msi_set_mask_bit(unsigned in
break;
case PCI_CAP_ID_MSIX:
{
- int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
+ int offset = PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET;
writel(flag, entry->mask_base + offset);
readl(entry->mask_base + offset);
break;
@@ -392,13 +446,10 @@ int msi_free_vector(struct msi_desc *ent
{
unsigned long start;
- writel(1, entry->mask_base + entry->msi_attrib.entry_nr
- * PCI_MSIX_ENTRY_SIZE
- + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+ writel(1, entry->mask_base + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
start = (unsigned long)entry->mask_base & ~(PAGE_SIZE - 1);
- msix_fixmap_free(virt_to_fix(start));
- destroy_xen_mappings(start, start + PAGE_SIZE);
+ msix_put_fixmap(entry->dev, virt_to_fix(start));
}
list_del(&entry->list);
xfree(entry);
@@ -500,8 +551,8 @@ static int msix_capability_init(struct p
struct msi_desc *entry;
int pos;
u16 control;
- unsigned long phys_addr;
- u32 table_offset;
+ unsigned long table_paddr, entry_paddr;
+ u32 table_offset, entry_offset;
u8 bir;
void __iomem *base;
int idx;
@@ -525,15 +576,17 @@ static int msix_capability_init(struct p
table_offset = pci_conf_read32(bus, slot, func,
msix_table_offset_reg(pos));
bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK);
table_offset &= ~PCI_MSIX_FLAGS_BIRMASK;
- phys_addr = msi->table_base + table_offset;
- idx = msix_fixmap_alloc();
+ entry_offset = msi->entry_nr * PCI_MSIX_ENTRY_SIZE;
+
+ table_paddr = msi->table_base + table_offset;
+ entry_paddr = table_paddr + entry_offset;
+ idx = msix_get_fixmap(dev, table_paddr, entry_paddr);
if ( idx < 0 )
{
xfree(entry);
- return -ENOMEM;
+ return idx;
}
- set_fixmap_nocache(idx, phys_addr);
- base = (void *)(fix_to_virt(idx) + (phys_addr & ((1UL << PAGE_SHIFT) -
1)));
+ base = (void *)(fix_to_virt(idx) + (entry_paddr & ((1UL << PAGE_SHIFT) -
1)));
entry->msi_attrib.type = PCI_CAP_ID_MSIX;
entry->msi_attrib.is_64 = 1;
@@ -548,9 +601,7 @@ static int msix_capability_init(struct p
list_add_tail(&entry->list, &dev->msi_list);
/* Mask interrupt here */
- writel(1, entry->mask_base + entry->msi_attrib.entry_nr
- * PCI_MSIX_ENTRY_SIZE
- + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+ writel(1, entry->mask_base + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
*desc = entry;
/* Restore MSI-X enabled bits */
@@ -675,9 +726,7 @@ static void __pci_disable_msix(struct ms
BUG_ON(list_empty(&dev->msi_list));
- writel(1, entry->mask_base + entry->msi_attrib.entry_nr
- * PCI_MSIX_ENTRY_SIZE
- + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
+ writel(1, entry->mask_base + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET);
pci_conf_write16(bus, slot, func, msix_control_reg(pos), control);
}
diff -r c3b5e36248c9 xen/drivers/passthrough/pci.c
--- a/xen/drivers/passthrough/pci.c Tue Feb 03 18:14:19 2009 +0000
+++ b/xen/drivers/passthrough/pci.c Wed Feb 04 14:41:03 2009 +0800
@@ -48,6 +48,7 @@ struct pci_dev *alloc_pdev(u8 bus, u8 de
pdev->domain = NULL;
INIT_LIST_HEAD(&pdev->msi_list);
list_add(&pdev->alldevs_list, &alldevs_list);
+ spin_lock_init(&pdev->msix_table_lock);
return pdev;
}
diff -r c3b5e36248c9 xen/include/asm-x86/fixmap.h
--- a/xen/include/asm-x86/fixmap.h Tue Feb 03 18:14:19 2009 +0000
+++ b/xen/include/asm-x86/fixmap.h Wed Feb 04 14:41:03 2009 +0800
@@ -50,7 +50,7 @@ enum fixed_addresses {
FIX_IOMMU_MMIO_END = FIX_IOMMU_MMIO_BASE_0 + IOMMU_PAGES -1,
FIX_TBOOT_SHARED_BASE,
FIX_MSIX_IO_RESERV_BASE,
- FIX_MSIX_IO_RESERV_END = FIX_MSIX_IO_RESERV_BASE + MAX_MSIX_PAGES -1,
+ FIX_MSIX_IO_RESERV_END = FIX_MSIX_IO_RESERV_BASE + FIX_MSIX_MAX_PAGES -1,
__end_of_fixed_addresses
};
diff -r c3b5e36248c9 xen/include/asm-x86/msi.h
--- a/xen/include/asm-x86/msi.h Tue Feb 03 18:14:19 2009 +0000
+++ b/xen/include/asm-x86/msi.h Wed Feb 04 14:41:03 2009 +0800
@@ -49,9 +49,9 @@
/* MAX fixed pages reserved for mapping MSIX tables. */
#if defined(__x86_64__)
-#define MAX_MSIX_PAGES 512
+#define FIX_MSIX_MAX_PAGES 512
#else
-#define MAX_MSIX_PAGES 32
+#define FIX_MSIX_MAX_PAGES 32
#endif
struct msi_info {
@@ -93,7 +93,7 @@ struct msi_desc {
struct list_head list;
- void __iomem *mask_base;
+ void __iomem *mask_base; /* va for the entry in mask table */
struct pci_dev *dev;
int vector;
diff -r c3b5e36248c9 xen/include/xen/pci.h
--- a/xen/include/xen/pci.h Tue Feb 03 18:14:19 2009 +0000
+++ b/xen/include/xen/pci.h Wed Feb 04 14:41:03 2009 +0800
@@ -29,10 +29,16 @@
#define PCI_BDF(b,d,f) ((((b) & 0xff) << 8) | PCI_DEVFN(d,f))
#define PCI_BDF2(b,df) ((((b) & 0xff) << 8) | ((df) & 0xff))
+#define MAX_MSIX_TABLE_PAGES 8 /* 2048 entries */
struct pci_dev {
struct list_head alldevs_list;
struct list_head domain_list;
+
struct list_head msi_list;
+ int msix_table_refcnt[MAX_MSIX_TABLE_PAGES];
+ int msix_table_idx[MAX_MSIX_TABLE_PAGES];
+ spinlock_t msix_table_lock;
+
struct domain *domain;
const u8 bus;
const u8 devfn;
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|