Commit 8d1c4819 authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Linus Torvalds

[PATCH] arch/i386/pci/mmconfig.c tlb flush fix

We use the fixmap for accessing pci config space in pci_mmcfg_read/write().
The problem is in pci_exp_set_dev_base(). It is caching a last
accessed address to avoid calling set_fixmap_nocache() whenever
pci_mmcfg_read/write() is used.

  static inline void pci_exp_set_dev_base(int bus, int devfn)
  {
	u32 dev_base = base | (bus << 20) | (devfn << 12);
	if (dev_base != mmcfg_last_accessed_device) {
		mmcfg_last_accessed_device = dev_base;
		set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
	}
  }

            cpu0                                        cpu1
  ---------------------------------------------------------------------------
    pci_mmcfg_read("device-A")
        pci_exp_set_dev_base()
            set_fixmap_nocache()
                                              pci_mmcfg_read("device-B")
                                                  pci_exp_set_dev_base()
                                                      set_fixmap_nocache()
    pci_mmcfg_read("device-B")
        pci_exp_set_dev_base()
            /* doesn't flush tlb */

But if cpus accessed the above order, the second pci_mmcfg_read() on
cpu0 doesn't flush the TLB, because "mmcfg_last_accessed_device" is
device-B.  So, second pci_mmcfg_read() on cpu0 accesses a device-A via
a previous TLB cache. This problem became the cause of several strange
behavior.

This patches fixes this situation by adds "mmcfg_last_accessed_cpu" check.

[ Alternatively, we could make a per-cpu mapping area or something. Not
  that it's probably worth it, but if we wanted to avoid all locking and
  instead just disable preemption, that would be the way to go. --Linus ]
Signed-off-by: default avatarOGAWA Hirofumi <hogawa@miraclelinux.com>
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e1d9fd2e
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
/* The base address of the last MMCONFIG device accessed */ /* The base address of the last MMCONFIG device accessed */
static u32 mmcfg_last_accessed_device; static u32 mmcfg_last_accessed_device;
static int mmcfg_last_accessed_cpu;
static DECLARE_BITMAP(fallback_slots, MAX_CHECK_BUS*32); static DECLARE_BITMAP(fallback_slots, MAX_CHECK_BUS*32);
...@@ -73,8 +74,11 @@ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) ...@@ -73,8 +74,11 @@ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
{ {
u32 dev_base = base | (bus << 20) | (devfn << 12); u32 dev_base = base | (bus << 20) | (devfn << 12);
if (dev_base != mmcfg_last_accessed_device) { int cpu = smp_processor_id();
if (dev_base != mmcfg_last_accessed_device ||
cpu != mmcfg_last_accessed_cpu) {
mmcfg_last_accessed_device = dev_base; mmcfg_last_accessed_device = dev_base;
mmcfg_last_accessed_cpu = cpu;
set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment