Merge branch 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 15 Sep 2009 16:19:38 +0000 (09:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 15 Sep 2009 16:19:38 +0000 (09:19 -0700)
* 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  x86, pat: Fix cacheflush address in change_page_attr_set_clr()
  mm: remove !NUMA condition from PAGEFLAGS_EXTENDED condition set
  x86: Fix earlyprintk=dbgp for machines without NX
  x86, pat: Sanity check remap_pfn_range for RAM region
  x86, pat: Lookup the protection from memtype list on vm_insert_pfn()
  x86, pat: Add lookup_memtype to get the current memtype of a paddr
  x86, pat: Use page flags to track memtypes of RAM pages
  x86, pat: Generalize the use of page flag PG_uncached
  x86, pat: Add rbtree to do quick lookup in memtype tracking
  x86, pat: Add PAT reserve free to io_mapping* APIs
  x86, pat: New i/f for driver to request memtype for IO regions
  x86, pat: ioremap to follow same PAT restrictions as other PAT users
  x86, pat: Keep identity maps consistent with mmaps even when pat_disabled
  x86, mtrr: make mtrr_aps_delayed_init static bool
  x86, pat/mtrr: Rendezvous all the cpus for MTRR/PAT init
  generic-ipi: Allow cpus not yet online to call smp_call_function with irqs disabled
  x86: Fix an incorrect argument of reserve_bootmem()
  x86: Fix system crash when loading with "reservetop" parameter

1  2 
arch/x86/Kconfig
arch/x86/kernel/setup.c
arch/x86/kernel/smpboot.c
arch/x86/mm/ioremap.c
arch/x86/mm/pat.c
kernel/cpu.c
mm/Kconfig

diff --combined arch/x86/Kconfig
index 74bf3e30ce75f85d75ef9e19070ba1fe3bfa93bd,c1e588131f4a1518bd3aa5f523eaffc448db8cff..a800b0faaad66798bab88823f14672a48a0c5042
@@@ -38,7 -38,7 +38,7 @@@ config X8
        select HAVE_FUNCTION_GRAPH_FP_TEST
        select HAVE_FUNCTION_TRACE_MCOUNT_TEST
        select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
 -      select HAVE_FTRACE_SYSCALLS
 +      select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_KVM
        select HAVE_ARCH_KGDB
        select HAVE_ARCH_TRACEHOOK
@@@ -179,10 -179,6 +179,10 @@@ config ARCH_SUPPORTS_OPTIMIZED_INLININ
  config ARCH_SUPPORTS_DEBUG_PAGEALLOC
        def_bool y
  
 +config HAVE_INTEL_TXT
 +      def_bool y
 +      depends on EXPERIMENTAL && DMAR && ACPI
 +
  # Use the generic interrupt handling code in kernel/irq/:
  config GENERIC_HARDIRQS
        bool
@@@ -590,6 -586,7 +590,6 @@@ config GART_IOMM
        bool "GART IOMMU support" if EMBEDDED
        default y
        select SWIOTLB
 -      select AGP
        depends on X86_64 && PCI
        ---help---
          Support for full DMA access of devices with 32bit memory access only
@@@ -1417,6 -1414,10 +1417,10 @@@ config X86_PA
  
          If unsure, say Y.
  
+ config ARCH_USES_PG_UNCACHED
+       def_bool y
+       depends on X86_PAT
  config EFI
        bool "EFI runtime service support"
        depends on ACPI
diff --combined arch/x86/kernel/setup.c
index 61f86f24142068146377c797edc6cf260ea4c217,eb1f1e6e52b00f988025f4e4d3a20fb8011e02b1..19f15c4076fb6f3f57afa01ddc211dbdce1ae202
@@@ -66,7 -66,6 +66,7 @@@
  
  #include <linux/percpu.h>
  #include <linux/crash_dump.h>
 +#include <linux/tboot.h>
  
  #include <video/edid.h>
  
@@@ -712,6 -711,21 +712,21 @@@ void __init setup_arch(char **cmdline_p
        printk(KERN_INFO "Command line: %s\n", boot_command_line);
  #endif
  
+       strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
+       *cmdline_p = command_line;
+ #ifdef CONFIG_X86_64
+       /*
+        * Must call this twice: Once just to detect whether hardware doesn't
+        * support NX (so that the early EHCI debug console setup can safely
+        * call set_fixmap(), and then again after parsing early parameters to
+        * honor the respective command line option.
+        */
+       check_efer();
+ #endif
+       parse_early_param();
        /* VMI may relocate the fixmap; do this before touching ioremap area */
        vmi_init();
  
  #endif
  #endif
  
-       strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
-       *cmdline_p = command_line;
-       parse_early_param();
  #ifdef CONFIG_X86_64
        check_efer();
  #endif
        paravirt_pagetable_setup_done(swapper_pg_dir);
        paravirt_post_allocator_init();
  
 +      tboot_probe();
 +
  #ifdef CONFIG_X86_64
        map_vsyscall();
  #endif
index 9d8319183aae1495248be69172cb07e1338472cb,d720b7e0cf3db38c13b5a720239eceaf7be3535d..a25eeec00080b5f9801e30c08e2d4a5f65ae904e
@@@ -47,7 -47,6 +47,7 @@@
  #include <linux/bootmem.h>
  #include <linux/err.h>
  #include <linux/nmi.h>
 +#include <linux/tboot.h>
  
  #include <asm/acpi.h>
  #include <asm/desc.h>
@@@ -435,8 -434,7 +435,8 @@@ const struct cpumask *cpu_coregroup_mas
         * For perf, we return last level cache shared map.
         * And for power savings, we return cpu_core_map
         */
 -      if (sched_mc_power_savings || sched_smt_power_savings)
 +      if ((sched_mc_power_savings || sched_smt_power_savings) &&
 +          !(cpu_has(c, X86_FEATURE_AMD_DCM)))
                return cpu_core_mask(cpu);
        else
                return c->llc_shared_map;
@@@ -1118,9 -1116,22 +1118,22 @@@ void __init native_smp_prepare_cpus(uns
  
        if (is_uv_system())
                uv_system_init();
+       set_mtrr_aps_delayed_init();
  out:
        preempt_enable();
  }
+ void arch_enable_nonboot_cpus_begin(void)
+ {
+       set_mtrr_aps_delayed_init();
+ }
+ void arch_enable_nonboot_cpus_end(void)
+ {
+       mtrr_aps_init();
+ }
  /*
   * Early setup to make printk work.
   */
@@@ -1142,6 -1153,7 +1155,7 @@@ void __init native_smp_cpus_done(unsign
        setup_ioapic_dest();
  #endif
        check_nmi_watchdog();
+       mtrr_aps_init();
  }
  
  static int __initdata setup_possible_cpus = -1;
@@@ -1319,7 -1331,6 +1333,7 @@@ void play_dead_common(void
  void native_play_dead(void)
  {
        play_dead_common();
 +      tboot_shutdown(TB_SHUTDOWN_WFS);
        wbinvd_halt();
  }
  
diff --combined arch/x86/mm/ioremap.c
index 04e1ad60c63a23a3527960a01cf6e2cff091d959,2a76eba9da216eb1eeb5272be4d799d59b8cd259..334e63ca7b2b468d096828cd56684604ca3ab0c4
  #include <asm/pgalloc.h>
  #include <asm/pat.h>
  
 -static inline int phys_addr_valid(resource_size_t addr)
 -{
 -#ifdef CONFIG_PHYS_ADDR_T_64BIT
 -      return !(addr >> boot_cpu_data.x86_phys_bits);
 -#else
 -      return 1;
 -#endif
 -}
 -
 -#ifdef CONFIG_X86_64
 -
 -unsigned long __phys_addr(unsigned long x)
 -{
 -      if (x >= __START_KERNEL_map) {
 -              x -= __START_KERNEL_map;
 -              VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE);
 -              x += phys_base;
 -      } else {
 -              VIRTUAL_BUG_ON(x < PAGE_OFFSET);
 -              x -= PAGE_OFFSET;
 -              VIRTUAL_BUG_ON(!phys_addr_valid(x));
 -      }
 -      return x;
 -}
 -EXPORT_SYMBOL(__phys_addr);
 -
 -bool __virt_addr_valid(unsigned long x)
 -{
 -      if (x >= __START_KERNEL_map) {
 -              x -= __START_KERNEL_map;
 -              if (x >= KERNEL_IMAGE_SIZE)
 -                      return false;
 -              x += phys_base;
 -      } else {
 -              if (x < PAGE_OFFSET)
 -                      return false;
 -              x -= PAGE_OFFSET;
 -              if (!phys_addr_valid(x))
 -                      return false;
 -      }
 -
 -      return pfn_valid(x >> PAGE_SHIFT);
 -}
 -EXPORT_SYMBOL(__virt_addr_valid);
 -
 -#else
 -
 -#ifdef CONFIG_DEBUG_VIRTUAL
 -unsigned long __phys_addr(unsigned long x)
 -{
 -      /* VMALLOC_* aren't constants  */
 -      VIRTUAL_BUG_ON(x < PAGE_OFFSET);
 -      VIRTUAL_BUG_ON(__vmalloc_start_set && is_vmalloc_addr((void *) x));
 -      return x - PAGE_OFFSET;
 -}
 -EXPORT_SYMBOL(__phys_addr);
 -#endif
 -
 -bool __virt_addr_valid(unsigned long x)
 -{
 -      if (x < PAGE_OFFSET)
 -              return false;
 -      if (__vmalloc_start_set && is_vmalloc_addr((void *) x))
 -              return false;
 -      if (x >= FIXADDR_START)
 -              return false;
 -      return pfn_valid((x - PAGE_OFFSET) >> PAGE_SHIFT);
 -}
 -EXPORT_SYMBOL(__virt_addr_valid);
 -
 -#endif
 +#include "physaddr.h"
  
  int page_is_ram(unsigned long pagenr)
  {
@@@ -158,24 -228,14 +158,14 @@@ static void __iomem *__ioremap_caller(r
        retval = reserve_memtype(phys_addr, (u64)phys_addr + size,
                                                prot_val, &new_prot_val);
        if (retval) {
-               pr_debug("Warning: reserve_memtype returned %d\n", retval);
+               printk(KERN_ERR "ioremap reserve_memtype failed %d\n", retval);
                return NULL;
        }
  
        if (prot_val != new_prot_val) {
-               /*
-                * Do not fallback to certain memory types with certain
-                * requested type:
-                * - request is uc-, return cannot be write-back
-                * - request is uc-, return cannot be write-combine
-                * - request is write-combine, return cannot be write-back
-                */
-               if ((prot_val == _PAGE_CACHE_UC_MINUS &&
-                    (new_prot_val == _PAGE_CACHE_WB ||
-                     new_prot_val == _PAGE_CACHE_WC)) ||
-                   (prot_val == _PAGE_CACHE_WC &&
-                    new_prot_val == _PAGE_CACHE_WB)) {
-                       pr_debug(
+               if (!is_new_memtype_allowed(phys_addr, size,
+                                           prot_val, new_prot_val)) {
+                       printk(KERN_ERR
                "ioremap error for 0x%llx-0x%llx, requested 0x%lx, got 0x%lx\n",
                                (unsigned long long)phys_addr,
                                (unsigned long long)(phys_addr + size),
diff --combined arch/x86/mm/pat.c
index b2f7d3e59b86b656d49993a5870e9e47651e3d7c,d2a72abc9de1cd4db94d8fff641d7b56ef67857a..d7ebc3a10f2f1aa96aa00913e4b3f2472149facc
@@@ -15,6 -15,7 +15,7 @@@
  #include <linux/gfp.h>
  #include <linux/mm.h>
  #include <linux/fs.h>
+ #include <linux/rbtree.h>
  
  #include <asm/cacheflush.h>
  #include <asm/processor.h>
@@@ -148,11 -149,10 +149,10 @@@ static char *cattr_name(unsigned long f
   * areas). All the aliases have the same cache attributes of course.
   * Zero attributes are represented as holes.
   *
-  * Currently the data structure is a list because the number of mappings
-  * are expected to be relatively small. If this should be a problem
-  * it could be changed to a rbtree or similar.
+  * The data structure is a list that is also organized as an rbtree
+  * sorted on the start address of memtype range.
   *
-  * memtype_lock protects the whole list.
+  * memtype_lock protects both the linear list and rbtree.
   */
  
  struct memtype {
        u64                     end;
        unsigned long           type;
        struct list_head        nd;
+       struct rb_node          rb;
  };
  
+ static struct rb_root memtype_rbroot = RB_ROOT;
  static LIST_HEAD(memtype_list);
  static DEFINE_SPINLOCK(memtype_lock); /* protects memtype list */
  
+ static struct memtype *memtype_rb_search(struct rb_root *root, u64 start)
+ {
+       struct rb_node *node = root->rb_node;
+       struct memtype *last_lower = NULL;
+       while (node) {
+               struct memtype *data = container_of(node, struct memtype, rb);
+               if (data->start < start) {
+                       last_lower = data;
+                       node = node->rb_right;
+               } else if (data->start > start) {
+                       node = node->rb_left;
+               } else
+                       return data;
+       }
+       /* Will return NULL if there is no entry with its start <= start */
+       return last_lower;
+ }
+ static void memtype_rb_insert(struct rb_root *root, struct memtype *data)
+ {
+       struct rb_node **new = &(root->rb_node);
+       struct rb_node *parent = NULL;
+       while (*new) {
+               struct memtype *this = container_of(*new, struct memtype, rb);
+               parent = *new;
+               if (data->start <= this->start)
+                       new = &((*new)->rb_left);
+               else if (data->start > this->start)
+                       new = &((*new)->rb_right);
+       }
+       rb_link_node(&data->rb, parent, new);
+       rb_insert_color(&data->rb, root);
+ }
  /*
   * Does intersection of PAT memory type and MTRR memory type and returns
   * the resulting memory type as PAT understands it.
@@@ -218,9 -260,6 +260,6 @@@ chk_conflict(struct memtype *new, struc
        return -EBUSY;
  }
  
- static struct memtype *cached_entry;
- static u64 cached_start;
  static int pat_pagerange_is_ram(unsigned long start, unsigned long end)
  {
        int ram_page = 0, not_rampage = 0;
  }
  
  /*
-  * For RAM pages, mark the pages as non WB memory type using
-  * PageNonWB (PG_arch_1). We allow only one set_memory_uc() or
-  * set_memory_wc() on a RAM page at a time before marking it as WB again.
-  * This is ok, because only one driver will be owning the page and
-  * doing set_memory_*() calls.
+  * For RAM pages, we use page flags to mark the pages with appropriate type.
+  * Here we do two pass:
+  * - Find the memtype of all the pages in the range, look for any conflicts
+  * - In case of no conflicts, set the new memtype for pages in the range
   *
-  * For now, we use PageNonWB to track that the RAM page is being mapped
-  * as non WB. In future, we will have to use one more flag
-  * (or some other mechanism in page_struct) to distinguish between
-  * UC and WC mapping.
+  * Caller must hold memtype_lock for atomicity.
   */
  static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type,
                                  unsigned long *new_type)
  {
        struct page *page;
-       u64 pfn, end_pfn;
+       u64 pfn;
+       if (req_type == _PAGE_CACHE_UC) {
+               /* We do not support strong UC */
+               WARN_ON_ONCE(1);
+               req_type = _PAGE_CACHE_UC_MINUS;
+       }
  
        for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
-               page = pfn_to_page(pfn);
-               if (page_mapped(page) || PageNonWB(page))
-                       goto out;
+               unsigned long type;
  
-               SetPageNonWB(page);
+               page = pfn_to_page(pfn);
+               type = get_page_memtype(page);
+               if (type != -1) {
+                       printk(KERN_INFO "reserve_ram_pages_type failed "
+                               "0x%Lx-0x%Lx, track 0x%lx, req 0x%lx\n",
+                               start, end, type, req_type);
+                       if (new_type)
+                               *new_type = type;
+                       return -EBUSY;
+               }
        }
-       return 0;
  
- out:
-       end_pfn = pfn;
-       for (pfn = (start >> PAGE_SHIFT); pfn < end_pfn; ++pfn) {
+       if (new_type)
+               *new_type = req_type;
+       for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
                page = pfn_to_page(pfn);
-               ClearPageNonWB(page);
+               set_page_memtype(page, req_type);
        }
-       return -EINVAL;
+       return 0;
  }
  
  static int free_ram_pages_type(u64 start, u64 end)
  {
        struct page *page;
-       u64 pfn, end_pfn;
+       u64 pfn;
  
        for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
                page = pfn_to_page(pfn);
-               if (page_mapped(page) || !PageNonWB(page))
-                       goto out;
-               ClearPageNonWB(page);
+               set_page_memtype(page, -1);
        }
        return 0;
- out:
-       end_pfn = pfn;
-       for (pfn = (start >> PAGE_SHIFT); pfn < end_pfn; ++pfn) {
-               page = pfn_to_page(pfn);
-               SetPageNonWB(page);
-       }
-       return -EINVAL;
  }
  
  /*
@@@ -339,6 -376,8 +376,8 @@@ int reserve_memtype(u64 start, u64 end
                if (new_type) {
                        if (req_type == -1)
                                *new_type = _PAGE_CACHE_WB;
+                       else if (req_type == _PAGE_CACHE_WC)
+                               *new_type = _PAGE_CACHE_UC_MINUS;
                        else
                                *new_type = req_type & _PAGE_CACHE_MASK;
                }
                *new_type = actual_type;
  
        is_range_ram = pat_pagerange_is_ram(start, end);
-       if (is_range_ram == 1)
-               return reserve_ram_pages_type(start, end, req_type,
-                                             new_type);
-       else if (is_range_ram < 0)
+       if (is_range_ram == 1) {
+               spin_lock(&memtype_lock);
+               err = reserve_ram_pages_type(start, end, req_type, new_type);
+               spin_unlock(&memtype_lock);
+               return err;
+       } else if (is_range_ram < 0) {
                return -EINVAL;
+       }
  
        new  = kmalloc(sizeof(struct memtype), GFP_KERNEL);
        if (!new)
  
        spin_lock(&memtype_lock);
  
-       if (cached_entry && start >= cached_start)
-               entry = cached_entry;
-       else
+       entry = memtype_rb_search(&memtype_rbroot, new->start);
+       if (likely(entry != NULL)) {
+               /* To work correctly with list_for_each_entry_continue */
+               entry = list_entry(entry->nd.prev, struct memtype, nd);
+       } else {
                entry = list_entry(&memtype_list, struct memtype, nd);
+       }
  
        /* Search for existing mapping that overlaps the current range */
        where = NULL;
        list_for_each_entry_continue(entry, &memtype_list, nd) {
                if (end <= entry->start) {
                        where = entry->nd.prev;
-                       cached_entry = list_entry(where, struct memtype, nd);
                        break;
                } else if (start <= entry->start) { /* end > entry->start */
                        err = chk_conflict(new, entry, new_type);
                                dprintk("Overlap at 0x%Lx-0x%Lx\n",
                                        entry->start, entry->end);
                                where = entry->nd.prev;
-                               cached_entry = list_entry(where,
-                                                       struct memtype, nd);
                        }
                        break;
                } else if (start < entry->end) { /* start > entry->start */
                        if (!err) {
                                dprintk("Overlap at 0x%Lx-0x%Lx\n",
                                        entry->start, entry->end);
-                               cached_entry = list_entry(entry->nd.prev,
-                                                       struct memtype, nd);
  
                                /*
                                 * Move to right position in the linked
                return err;
        }
  
-       cached_start = start;
        if (where)
                list_add(&new->nd, where);
        else
                list_add_tail(&new->nd, &memtype_list);
  
+       memtype_rb_insert(&memtype_rbroot, new);
        spin_unlock(&memtype_lock);
  
        dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n",
  
  int free_memtype(u64 start, u64 end)
  {
-       struct memtype *entry;
+       struct memtype *entry, *saved_entry;
        int err = -EINVAL;
        int is_range_ram;
  
                return 0;
  
        is_range_ram = pat_pagerange_is_ram(start, end);
-       if (is_range_ram == 1)
-               return free_ram_pages_type(start, end);
-       else if (is_range_ram < 0)
+       if (is_range_ram == 1) {
+               spin_lock(&memtype_lock);
+               err = free_ram_pages_type(start, end);
+               spin_unlock(&memtype_lock);
+               return err;
+       } else if (is_range_ram < 0) {
                return -EINVAL;
+       }
  
        spin_lock(&memtype_lock);
+       entry = memtype_rb_search(&memtype_rbroot, start);
+       if (unlikely(entry == NULL))
+               goto unlock_ret;
+       /*
+        * Saved entry points to an entry with start same or less than what
+        * we searched for. Now go through the list in both directions to look
+        * for the entry that matches with both start and end, with list stored
+        * in sorted start address
+        */
+       saved_entry = entry;
        list_for_each_entry(entry, &memtype_list, nd) {
                if (entry->start == start && entry->end == end) {
-                       if (cached_entry == entry || cached_start == start)
-                               cached_entry = NULL;
+                       rb_erase(&entry->rb, &memtype_rbroot);
+                       list_del(&entry->nd);
+                       kfree(entry);
+                       err = 0;
+                       break;
+               } else if (entry->start > start) {
+                       break;
+               }
+       }
+       if (!err)
+               goto unlock_ret;
  
+       entry = saved_entry;
+       list_for_each_entry_reverse(entry, &memtype_list, nd) {
+               if (entry->start == start && entry->end == end) {
+                       rb_erase(&entry->rb, &memtype_rbroot);
                        list_del(&entry->nd);
                        kfree(entry);
                        err = 0;
                        break;
+               } else if (entry->start < start) {
+                       break;
                }
        }
+ unlock_ret:
        spin_unlock(&memtype_lock);
  
        if (err) {
  }
  
  
+ /**
+  * lookup_memtype - Looksup the memory type for a physical address
+  * @paddr: physical address of which memory type needs to be looked up
+  *
+  * Only to be called when PAT is enabled
+  *
+  * Returns _PAGE_CACHE_WB, _PAGE_CACHE_WC, _PAGE_CACHE_UC_MINUS or
+  * _PAGE_CACHE_UC
+  */
+ static unsigned long lookup_memtype(u64 paddr)
+ {
+       int rettype = _PAGE_CACHE_WB;
+       struct memtype *entry;
+       if (is_ISA_range(paddr, paddr + PAGE_SIZE - 1))
+               return rettype;
+       if (pat_pagerange_is_ram(paddr, paddr + PAGE_SIZE)) {
+               struct page *page;
+               spin_lock(&memtype_lock);
+               page = pfn_to_page(paddr >> PAGE_SHIFT);
+               rettype = get_page_memtype(page);
+               spin_unlock(&memtype_lock);
+               /*
+                * -1 from get_page_memtype() implies RAM page is in its
+                * default state and not reserved, and hence of type WB
+                */
+               if (rettype == -1)
+                       rettype = _PAGE_CACHE_WB;
+               return rettype;
+       }
+       spin_lock(&memtype_lock);
+       entry = memtype_rb_search(&memtype_rbroot, paddr);
+       if (entry != NULL)
+               rettype = entry->type;
+       else
+               rettype = _PAGE_CACHE_UC_MINUS;
+       spin_unlock(&memtype_lock);
+       return rettype;
+ }
+ /**
+  * io_reserve_memtype - Request a memory type mapping for a region of memory
+  * @start: start (physical address) of the region
+  * @end: end (physical address) of the region
+  * @type: A pointer to memtype, with requested type. On success, requested
+  * or any other compatible type that was available for the region is returned
+  *
+  * On success, returns 0
+  * On failure, returns non-zero
+  */
+ int io_reserve_memtype(resource_size_t start, resource_size_t end,
+                       unsigned long *type)
+ {
+       resource_size_t size = end - start;
+       unsigned long req_type = *type;
+       unsigned long new_type;
+       int ret;
+       WARN_ON_ONCE(iomem_map_sanity_check(start, size));
+       ret = reserve_memtype(start, end, req_type, &new_type);
+       if (ret)
+               goto out_err;
+       if (!is_new_memtype_allowed(start, size, req_type, new_type))
+               goto out_free;
+       if (kernel_map_sync_memtype(start, size, new_type) < 0)
+               goto out_free;
+       *type = new_type;
+       return 0;
+ out_free:
+       free_memtype(start, end);
+       ret = -EBUSY;
+ out_err:
+       return ret;
+ }
+ /**
+  * io_free_memtype - Release a memory type mapping for a region of memory
+  * @start: start (physical address) of the region
+  * @end: end (physical address) of the region
+  */
+ void io_free_memtype(resource_size_t start, resource_size_t end)
+ {
+       free_memtype(start, end);
+ }
  pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
                                unsigned long size, pgprot_t vma_prot)
  {
@@@ -577,7 -749,7 +749,7 @@@ int kernel_map_sync_memtype(u64 base, u
  {
        unsigned long id_sz;
  
-       if (!pat_enabled || base >= __pa(high_memory))
+       if (base >= __pa(high_memory))
                return 0;
  
        id_sz = (__pa(high_memory) < base + size) ?
@@@ -612,11 -784,29 +784,29 @@@ static int reserve_pfn_range(u64 paddr
        is_ram = pat_pagerange_is_ram(paddr, paddr + size);
  
        /*
-        * reserve_pfn_range() doesn't support RAM pages. Maintain the current
-        * behavior with RAM pages by returning success.
+        * reserve_pfn_range() for RAM pages. We do not refcount to keep
+        * track of number of mappings of RAM pages. We can assert that
+        * the type requested matches the type of first page in the range.
         */
-       if (is_ram != 0)
+       if (is_ram) {
+               if (!pat_enabled)
+                       return 0;
+               flags = lookup_memtype(paddr);
+               if (want_flags != flags) {
+                       printk(KERN_WARNING
+                       "%s:%d map pfn RAM range req %s for %Lx-%Lx, got %s\n",
+                               current->comm, current->pid,
+                               cattr_name(want_flags),
+                               (unsigned long long)paddr,
+                               (unsigned long long)(paddr + size),
+                               cattr_name(flags));
+                       *vma_prot = __pgprot((pgprot_val(*vma_prot) &
+                                             (~_PAGE_CACHE_MASK)) |
+                                            flags);
+               }
                return 0;
+       }
  
        ret = reserve_memtype(paddr, paddr + size, want_flags, &flags);
        if (ret)
@@@ -678,14 -868,6 +868,6 @@@ int track_pfn_vma_copy(struct vm_area_s
        unsigned long vma_size = vma->vm_end - vma->vm_start;
        pgprot_t pgprot;
  
-       if (!pat_enabled)
-               return 0;
-       /*
-        * For now, only handle remap_pfn_range() vmas where
-        * is_linear_pfn_mapping() == TRUE. Handling of
-        * vm_insert_pfn() is TBD.
-        */
        if (is_linear_pfn_mapping(vma)) {
                /*
                 * reserve the whole chunk covered by vma. We need the
  int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
                        unsigned long pfn, unsigned long size)
  {
+       unsigned long flags;
        resource_size_t paddr;
        unsigned long vma_size = vma->vm_end - vma->vm_start;
  
-       if (!pat_enabled)
-               return 0;
-       /*
-        * For now, only handle remap_pfn_range() vmas where
-        * is_linear_pfn_mapping() == TRUE. Handling of
-        * vm_insert_pfn() is TBD.
-        */
        if (is_linear_pfn_mapping(vma)) {
                /* reserve the whole chunk starting from vm_pgoff */
                paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
                return reserve_pfn_range(paddr, vma_size, prot, 0);
        }
  
+       if (!pat_enabled)
+               return 0;
+       /* for vm_insert_pfn and friends, we set prot based on lookup */
+       flags = lookup_memtype(pfn << PAGE_SHIFT);
+       *prot = __pgprot((pgprot_val(vma->vm_page_prot) & (~_PAGE_CACHE_MASK)) |
+                        flags);
        return 0;
  }
  
@@@ -744,14 -927,6 +927,6 @@@ void untrack_pfn_vma(struct vm_area_str
        resource_size_t paddr;
        unsigned long vma_size = vma->vm_end - vma->vm_start;
  
-       if (!pat_enabled)
-               return;
-       /*
-        * For now, only handle remap_pfn_range() vmas where
-        * is_linear_pfn_mapping() == TRUE. Handling of
-        * vm_insert_pfn() is TBD.
-        */
        if (is_linear_pfn_mapping(vma)) {
                /* free the whole chunk starting from vm_pgoff */
                paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
@@@ -827,7 -1002,7 +1002,7 @@@ static int memtype_seq_show(struct seq_
        return 0;
  }
  
 -static struct seq_operations memtype_seq_ops = {
 +static const struct seq_operations memtype_seq_ops = {
        .start = memtype_seq_start,
        .next  = memtype_seq_next,
        .stop  = memtype_seq_stop,
diff --combined kernel/cpu.c
index 67a60076dd7e2f7eb81b50970a7f600d0011cc80,f5f9485b8c0f9a30f4084b0c9a8846f74e05d72d..6ba0f1ecb21230de60f6ab9fd7b2f58907b48d55
@@@ -401,7 -401,6 +401,7 @@@ int disable_nonboot_cpus(void
                        break;
                }
        }
 +
        if (!error) {
                BUG_ON(num_online_cpus() > 1);
                /* Make sure the CPUs won't be enabled by someone else */
        return error;
  }
  
+ void __weak arch_enable_nonboot_cpus_begin(void)
+ {
+ }
+ void __weak arch_enable_nonboot_cpus_end(void)
+ {
+ }
  void __ref enable_nonboot_cpus(void)
  {
        int cpu, error;
                goto out;
  
        printk("Enabling non-boot CPUs ...\n");
+       arch_enable_nonboot_cpus_begin();
        for_each_cpu(cpu, frozen_cpus) {
                error = _cpu_up(cpu, 1);
                if (!error) {
                }
                printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error);
        }
+       arch_enable_nonboot_cpus_end();
        cpumask_clear(frozen_cpus);
  out:
        cpu_maps_update_done();
diff --combined mm/Kconfig
index fe5f674d7a7d571a1f456d2df3d5a31bb965db2e,fe221c7df752ec8b7048d3660ecf8bf965d7ad39..3aa519f52e18141e48cdf18db2c76e708df13dae
@@@ -153,7 -153,7 +153,7 @@@ config MEMORY_HOTREMOV
  #
  config PAGEFLAGS_EXTENDED
        def_bool y
-       depends on 64BIT || SPARSEMEM_VMEMMAP || !NUMA || !SPARSEMEM
+       depends on 64BIT || SPARSEMEM_VMEMMAP || !SPARSEMEM
  
  # Heavily threaded applications may benefit from splitting the mm-wide
  # page_table_lock, so that faults on different parts of the user address
@@@ -225,9 -225,9 +225,9 @@@ config DEFAULT_MMAP_MIN_ADD
          For most ia64, ppc64 and x86 users with lots of address space
          a value of 65536 is reasonable and should cause no problems.
          On arm and other archs it should not be higher than 32768.
 -        Programs which use vm86 functionality would either need additional
 -        permissions from either the LSM or the capabilities module or have
 -        this protection disabled.
 +        Programs which use vm86 functionality or have some need to map
 +        this low address space will need CAP_SYS_RAWIO or disable this
 +        protection by setting the value to 0.
  
          This value can be changed after boot using the
          /proc/sys/vm/mmap_min_addr tunable.