Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 4445

kernel-2.6.18-194.11.1.el5.src.rpm

From: Bhavna Sarathy <bnagendr@redhat.com>
Date: Tue, 8 Dec 2009 15:47:17 -0500
Subject: [xen] fix w/sata set to ide combined mode on amd
Message-id: <4B1E7505.7030408@redhat.com>
Patchwork-id: 21745
O-Subject: Re: [RHEL5 PATCH] Xen IOMMU fix w/ SATA set to IDE combined mode
Bugzilla: 544021
RH-Acked-by: Andrew Jones <drjones@redhat.com>
RH-Acked-by: Christopher Lalancette <clalance@redhat.com>

Resolves BZ 544021

AMD M-C systems, i.e. Maranello platforms, has a few different SATA
settings, example, IDE, SATA AHCI, SATA IDE combined mode.  AMD found
an IOMMU bug when the SATA drive is in IDE combined mode.  This can
prevent RHEL5.4 from booting up properly when IOMMU is enabled.  We
have seen cases where the SATA drive is not found, this is a serious
issue.

The solution is to use a global interrupt remapping table by default.
Using a global interrupt remapping table shared by all devices has
better compatibility with certain old BIOSes. Per-device interrupt
remapping table can still be enabled by using a new parameter
"amd-iommu-perdev-intremap".

Brew build:
http://brewweb.devel.redhat.com/brew/taskinfo?taskID=2123561

Testing:
This fix has been tested on Dinar, and Drachma platforms with SATA drive
connected to the IDE Combined Mode, and the mode enabled in BIOS.

Note: White spaces in patch are consistant with upstream, and the
surrounding code in this AMD IOMMU file.

Please review and ACK.

Signed-off-by: Don Zickus <dzickus@redhat.com>

diff --git a/drivers/passthrough/amd/iommu_acpi.c b/drivers/passthrough/amd/iommu_acpi.c
index ced57d6..8a4d1a9 100644
--- a/drivers/passthrough/amd/iommu_acpi.c
+++ b/drivers/passthrough/amd/iommu_acpi.c
@@ -30,7 +30,7 @@ extern struct ivrs_mappings *ivrs_mappings;
 extern unsigned short last_bdf;
 extern int ioapic_bdf[MAX_IO_APICS];
 unsigned int parse_ivrs_table_error;
-extern int amd_iommu_debug;
+extern void *shared_intremap_table;
 
 static void add_ivrs_mapping_entry(
             u16 bdf, u16 alias_id, u8 flags, struct amd_iommu *iommu)
@@ -68,10 +68,19 @@ static void add_ivrs_mapping_entry(
     ivrs_mappings[bdf].dte_ext_int_pass = ext_int_pass;
     ivrs_mappings[bdf].dte_init_pass = init_pass;
     
-    /* allocate per-device interrupt remapping table */
-    if ( ivrs_mappings[alias_id].intremap_table == NULL )
-        ivrs_mappings[alias_id].intremap_table =
-            amd_iommu_alloc_intremap_table();
+    if (ivrs_mappings[alias_id].intremap_table == NULL )
+    {
+         /* allocate per-device interrupt remapping table */
+         if ( amd_iommu_perdev_intremap )
+             ivrs_mappings[alias_id].intremap_table =
+                amd_iommu_alloc_intremap_table();
+         else
+         {
+             if ( shared_intremap_table == NULL  )
+                 shared_intremap_table = amd_iommu_alloc_intremap_table();
+             ivrs_mappings[alias_id].intremap_table = shared_intremap_table;
+         }
+    }
     /* assgin iommu hardware */
     ivrs_mappings[bdf].iommu = iommu;
 }
diff --git a/drivers/passthrough/amd/iommu_init.c b/drivers/passthrough/amd/iommu_init.c
index 75b4173..19f671a 100644
--- a/drivers/passthrough/amd/iommu_init.c
+++ b/drivers/passthrough/amd/iommu_init.c
@@ -729,7 +729,8 @@ static int __init init_ivrs_mapping(void)
         ivrs_mappings[bdf].dte_ext_int_pass = IOMMU_CONTROL_DISABLED;
         ivrs_mappings[bdf].dte_init_pass = IOMMU_CONTROL_DISABLED;
 
-        spin_lock_init(&ivrs_mappings[bdf].intremap_lock);
+        if ( amd_iommu_perdev_intremap )
+            spin_lock_init(&ivrs_mappings[bdf].intremap_lock);
     }
     return 0;
 }
diff --git a/drivers/passthrough/amd/iommu_intr.c b/drivers/passthrough/amd/iommu_intr.c
index e6d899a..371c7a3 100644
--- a/drivers/passthrough/amd/iommu_intr.c
+++ b/drivers/passthrough/amd/iommu_intr.c
@@ -26,6 +26,15 @@ int ioapic_bdf[MAX_IO_APICS];
 #define INTREMAP_TABLE_ORDER    1
 extern struct ivrs_mappings *ivrs_mappings;
 extern unsigned short ivrs_bdf_entries;
+void *shared_intremap_table;
+static DEFINE_SPINLOCK(shared_intremap_lock);
+
+static spinlock_t* get_intremap_lock(int req_id)
+{
+    return (amd_iommu_perdev_intremap ?
+           &ivrs_mappings[req_id].intremap_lock:
+           &shared_intremap_lock);
+}
 
 static int get_intremap_requestor_id(int bdf)
 {
@@ -102,10 +111,10 @@ static void update_intremap_entry_from_ioapic(
     u8 delivery_mode, dest, vector, dest_mode;
     struct IO_APIC_route_entry *rte = ioapic_rte;
     int req_id;
-
+    spinlock_t *lock;
 
     req_id = get_intremap_requestor_id(bdf);
-
+    lock = get_intremap_lock(req_id);
     /* only remap interrupt vector when lower 32 bits in ioapic ire changed */
     if ( rte_upper )
     {
@@ -114,10 +123,10 @@ static void update_intremap_entry_from_ioapic(
         dest_mode = rte->dest_mode;
         dest = rte->dest.logical.logical_dest;
         
-        spin_lock_irqsave(&ivrs_mappings[req_id].intremap_lock, flags);
+        spin_lock_irqsave(lock, flags);
         entry = (u32*)get_intremap_entry(req_id, vector, delivery_mode);
         update_intremap_entry(entry, vector, delivery_mode, dest_mode, dest);
-        spin_unlock_irqrestore(&ivrs_mappings[req_id].intremap_lock, flags);
+        spin_unlock_irqrestore(lock, flags);
         
         if ( iommu->enabled )
         {
@@ -142,6 +151,7 @@ int __init amd_iommu_setup_ioapic_remapping(void)
     u8 delivery_mode, dest, vector, dest_mode;
     u16 bdf, req_id, bus, devfn;
     struct amd_iommu *iommu;
+    spinlock_t *lock;
 
     /* Read ioapic entries and update interrupt remapping table accordingly */
     for ( apic = 0; apic < nr_ioapics; apic++ )
@@ -167,17 +177,18 @@ int __init amd_iommu_setup_ioapic_remapping(void)
             }
             
             req_id = get_intremap_requestor_id(bdf);
+            lock = get_intremap_lock(req_id);
+
             delivery_mode = rte.delivery_mode;
             vector = rte.vector;
             dest_mode = rte.dest_mode;
             dest = rte.dest.logical.logical_dest;
 
-            spin_lock_irqsave(&ivrs_mappings[req_id].intremap_lock, flags);
+            spin_lock_irqsave(lock, flags);
             entry = (u32*)get_intremap_entry(req_id, vector, delivery_mode);
             update_intremap_entry(entry, vector, delivery_mode, dest_mode, 
                                   dest);
-            spin_unlock_irqrestore(&ivrs_mappings[req_id].intremap_lock,
-                                   flags);
+            spin_unlock_irqrestore(lock, flags);
 
             if ( iommu->enabled )
               {
@@ -253,13 +264,15 @@ static void update_intremap_entry_from_msi_msg(
     u16 dev_id, alias_id, bus, devfn, req_id;
 
     u8 delivery_mode, dest, vector, dest_mode;
+    spinlock_t *lock;
 
     dev_id = (pdev->bus << 8) | pdev->devfn;
     bus = pdev->bus;
     devfn = pdev->devfn;
     req_id = get_dma_requestor_id(dev_id);
+    lock = get_intremap_lock(req_id);
 
-    spin_lock_irqsave(&ivrs_mappings[req_id].intremap_lock, flags);
+    spin_lock_irqsave(lock, flags);
     dest_mode = (msg->address_lo >> MSI_ADDR_DESTMODE_SHIFT) & 0x1;
     delivery_mode = (msg->data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x1;
     vector = (msg->data >> MSI_DATA_VECTOR_SHIFT) & MSI_DATA_VECTOR_MASK;
@@ -267,7 +280,7 @@ static void update_intremap_entry_from_msi_msg(
 
     entry = (u32*)get_intremap_entry(req_id, (u8)vector, (u8)delivery_mode);
     update_intremap_entry(entry, vector, delivery_mode, dest_mode, dest);
-    spin_unlock_irqrestore(&ivrs_mappings[req_id].intremap_lock, flags);
+    spin_unlock_irqrestore(lock, flags);
     
     /*
      * In some special cases, a pci-e device(e.g SATA controller in IDE mode)
@@ -276,14 +289,15 @@ static void update_intremap_entry_from_msi_msg(
      * devices.
      */
     alias_id = get_intremap_requestor_id(dev_id);
+    lock = get_intremap_lock(alias_id);
     if ( ( dev_id != alias_id ) &&
          ivrs_mappings[alias_id].intremap_table != NULL )
     {
-        spin_lock_irqsave(&ivrs_mappings[alias_id].intremap_lock, flags);
+        spin_lock_irqsave(lock, flags);
         entry = (u32*)get_intremap_entry(alias_id, vector, delivery_mode);
         update_intremap_entry(entry, vector, delivery_mode, dest_mode, dest);
         invalidate_interrupt_table(iommu, alias_id);
-        spin_unlock_irqrestore(&ivrs_mappings[alias_id].intremap_lock, flags);
+        spin_unlock_irqrestore(lock, flags);
     }
 
     if ( iommu->enabled )
diff --git a/drivers/passthrough/iommu.c b/drivers/passthrough/iommu.c
index 2d16428..e97ff3b 100644
--- a/drivers/passthrough/iommu.c
+++ b/drivers/passthrough/iommu.c
@@ -40,6 +40,7 @@ int iommu_passthrough = 0;
 int iommu_snoop = 0;
 int iommu_intremap = 0;
 int amd_iommu_debug=0;
+int amd_iommu_perdev_intremap = 0;
 
 static void __init parse_iommu_param(char *s)
 {
@@ -48,6 +49,7 @@ static void __init parse_iommu_param(char *s)
     iommu_snoop = 1;
     iommu_intremap = 1;
     amd_iommu_debug = 0;
+    amd_iommu_perdev_intremap = 0;
 
     do {
         ss = strchr(s, ',');
@@ -69,6 +71,8 @@ static void __init parse_iommu_param(char *s)
             iommu_intremap = 0;
         else if ( !strcmp(s, "amd-iommu-debug") )
             amd_iommu_debug = 1;
+        else if ( !strcmp(s, "amd-iommu-perdev-intremap") )
+            amd_iommu_perdev_intremap = 1;
 
         s = ss + 1;
     } while ( ss );
diff --git a/include/asm-x86/hvm/svm/amd-iommu-proto.h b/include/asm-x86/hvm/svm/amd-iommu-proto.h
index 00eaf36..c744f7a 100644
--- a/include/asm-x86/hvm/svm/amd-iommu-proto.h
+++ b/include/asm-x86/hvm/svm/amd-iommu-proto.h
@@ -33,6 +33,8 @@
 #define PAGE_ALIGN(addr)    (((addr) + PAGE_SIZE - 1) & PAGE_MASK)
 
 extern int amd_iommu_debug;
+extern int amd_iommu_perdev_intremap;
+
 #define AMD_IOMMU_DEBUG(fmt, args...)                       \
   do                                                        \
   {                                                         \