WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [linux-2.6.18-xen] Save/restore PCI MSI state across S3.

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [linux-2.6.18-xen] Save/restore PCI MSI state across S3.
From: "Xen patchbot-linux-2.6.18-xen" <patchbot-linux-2.6.18-xen@xxxxxxxxxxxxxxxxxxx>
Date: Wed, 26 Nov 2008 12:00:19 -0800
Delivery-date: Wed, 26 Nov 2008 12:00:57 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1227536158 0
# Node ID 1b68d09b868fdcd724c6b86eb2790748a9fd0bb7
# Parent  163a3807cb1fb4f35304a99c63f4deac322df2da
Save/restore PCI MSI state across S3.

Signed-off-by: Haitao Shan <haitao.shan@xxxxxxxxx>
---
 drivers/pci/msi-xen.c |  177 ++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 128 insertions(+), 49 deletions(-)

diff -r 163a3807cb1f -r 1b68d09b868f drivers/pci/msi-xen.c
--- a/drivers/pci/msi-xen.c     Mon Nov 24 11:13:20 2008 +0000
+++ b/drivers/pci/msi-xen.c     Mon Nov 24 14:15:58 2008 +0000
@@ -48,6 +48,13 @@ struct msi_pirq_entry {
        struct list_head list;
        int pirq;
        int entry_nr;
+#ifdef CONFIG_PM
+       /* PM save area for MSIX address/data */
+       void __iomem *mask_base;
+       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)
@@ -83,7 +90,7 @@ static struct msi_dev_list *get_msi_dev_
        return ret;
 }
 
-static int attach_pirq_entry(int pirq, int entry_nr,
+static int attach_pirq_entry(int pirq, int entry_nr, u64 table_base,
                              struct msi_dev_list *msi_dev_entry)
 {
        struct msi_pirq_entry *entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
@@ -93,6 +100,9 @@ static int attach_pirq_entry(int pirq, i
                return -ENOMEM;
        entry->pirq = pirq;
        entry->entry_nr = entry_nr;
+#ifdef COMFIG_PM
+       entry->mask_base = table_base;
+#endif
        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);
@@ -299,104 +309,173 @@ static void enable_msi_mode(struct pci_d
 #ifdef CONFIG_PM
 int pci_save_msi_state(struct pci_dev *dev)
 {
-       int pos;
+       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;
 
-       if (!dev->msi_enabled)
+       pci_read_config_word(dev, msi_control_reg(pos), &control);
+       if (!(control & PCI_MSI_FLAGS_ENABLE))
                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;
+       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 pos, pirq;
-
+       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 (pos <= 0)
-               return;
-
-       if (!dev->msi_enabled)
-               return;
-
-       pirq = msi_map_pirq_to_vector(dev, dev->irq, 0, 0);
-       if (pirq < 0)
-               return;
+       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);
 }
 
 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, *tmp;
+       struct msi_pirq_entry *pirq_entry;
 
        pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
        if (pos <= 0 || dev->no_msi)
                return 0;
 
+       printk(KERN_CRIT "Saving MSIX cap\n");
+
        /* save the capability */
-       if (!dev->msix_enabled)
+       pci_read_config_word(dev, msi_control_reg(pos), &control);
+       if (!(control & PCI_MSIX_FLAGS_ENABLE))
                return 0;
+       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;
 
        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_unmap_pirq(dev, pirq_entry->pirq);
+       list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) {
+               int j;
+               void __iomem *base;
+
+               /* save the table */
+               base = pirq_entry->mask_base;
+               j = pirq_entry->entry_nr;
+               printk(KERN_CRIT "Save msix table entry %d pirq %x base %p\n",
+                      j, pirq_entry->pirq, base);
+
+               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);
 
-       disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX);
-       /* Set the flags for use of restore */
-       dev->msix_enabled = 1;
-
+       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)
 {
-       int pos;
+       u16 save;
+       int pos, j;
+       void __iomem *base;
+       struct pci_cap_saved_state *save_state;
        unsigned long flags;
-       u64 table_base;
        struct msi_dev_list *msi_dev_entry;
-       struct msi_pirq_entry *pirq_entry, *tmp;
+       struct msi_pirq_entry *pirq_entry;
+
+       save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX);
+       if (!save_state)
+               return;
+       printk(KERN_CRIT "Restoring MSIX cap\n");
+
+       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;
 
-       if (!dev->msix_enabled)
-               return;
-
        msi_dev_entry = get_msi_dev_pirq_list(dev);
-       table_base = find_table_base(dev, pos);
-       if (!table_base)
-               return;
 
        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) {
-               int rc = msi_map_pirq_to_vector(dev, pirq_entry->pirq,
-                                               pirq_entry->entry_nr, 
table_base);
-               if (rc < 0)
-                       printk(KERN_WARNING
-                              "%s: re-mapping irq #%d (pirq%d) failed: %d\n",
-                              pci_name(dev), pirq_entry->entry_nr,
-                              pirq_entry->pirq, rc);
+       list_for_each_entry(pirq_entry, &msi_dev_entry->pirq_list_head, list) {
+               /* route the table */
+               base = pirq_entry->mask_base;
+               j = pirq_entry->entry_nr;
+
+               printk(KERN_CRIT "Restore msix table entry %d pirq %x base 
%p\n",
+                      j, pirq_entry->pirq, base);
+               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
@@ -475,7 +554,7 @@ static int msix_capability_init(struct p
                pirq = msi_map_vector(dev, entries[i].entry, table_base);
                if (pirq < 0)
                        break;
-               attach_pirq_entry(pirq, entries[i].entry, msi_dev_entry);
+               attach_pirq_entry(pirq, entries[i].entry, table_base, 
msi_dev_entry);
                (entries + i)->vector = pirq;
        }
 
@@ -660,7 +739,7 @@ int pci_enable_msix(struct pci_dev* dev,
                        if (mapped)
                                continue;
                        irq = evtchn_map_pirq(-1, entries[i].vector);
-                       attach_pirq_entry(irq, entries[i].entry, msi_dev_entry);
+                       attach_pirq_entry(irq, entries[i].entry, 0, 
msi_dev_entry);
                        entries[i].vector = irq;
                }
         return 0;

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [linux-2.6.18-xen] Save/restore PCI MSI state across S3., Xen patchbot-linux-2.6.18-xen <=