[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] passthrough: fix MSI-X table fixmap allocation


  • To: xen-devel <xen-devel@xxxxxxxxxxxxxxxxxxx>
  • From: Qing He <qing.he@xxxxxxxxx>
  • Date: Mon, 16 Feb 2009 16:44:23 +0800
  • Delivery-date: Mon, 16 Feb 2009 00:44:29 -0800
  • List-id: Xen developer discussion <xen-devel.lists.xensource.com>

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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.