Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband
[powerpc.git] / arch / ia64 / mm / tlb.c
index 39628fc..ffad762 100644 (file)
@@ -11,7 +11,6 @@
  * Rohit Seth <rohit.seth@intel.com>
  * Ken Chen <kenneth.w.chen@intel.com>
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -29,7 +28,7 @@
 
 static struct {
        unsigned long mask;     /* mask of supported purge page-sizes */
-       unsigned long max_bits; /* log2() of largest supported purge page-size */
+       unsigned long max_bits; /* log2 of largest supported purge page-size */
 } purge;
 
 struct ia64_ctx ia64_ctx = {
@@ -58,7 +57,7 @@ mmu_context_init (void)
 void
 wrap_mmu_context (struct mm_struct *mm)
 {
-       int i;
+       int i, cpu;
        unsigned long flush_bit;
 
        for (i=0; i <= ia64_ctx.max_ctx / BITS_PER_LONG; i++) {
@@ -72,24 +71,25 @@ wrap_mmu_context (struct mm_struct *mm)
        ia64_ctx.limit = find_next_bit(ia64_ctx.bitmap,
                                ia64_ctx.max_ctx, ia64_ctx.next);
 
-       /* can't call flush_tlb_all() here because of race condition with O(1) scheduler [EF] */
-       {
-               int cpu = get_cpu(); /* prevent preemption/migration */
-               for_each_online_cpu(i) {
-                       if (i != cpu)
-                               per_cpu(ia64_need_tlb_flush, i) = 1;
-               }
-               put_cpu();
-       }
+       /*
+        * can't call flush_tlb_all() here because of race condition
+        * with O(1) scheduler [EF]
+        */
+       cpu = get_cpu(); /* prevent preemption/migration */
+       for_each_online_cpu(i)
+               if (i != cpu)
+                       per_cpu(ia64_need_tlb_flush, i) = 1;
+       put_cpu();
        local_flush_tlb_all();
 }
 
 void
-ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start, unsigned long end, unsigned long nbits)
+ia64_global_tlb_purge (struct mm_struct *mm, unsigned long start,
+                      unsigned long end, unsigned long nbits)
 {
        static DEFINE_SPINLOCK(ptcg_lock);
 
-       if (mm != current->active_mm) {
+       if (mm != current->active_mm || !current->mm) {
                flush_tlb_all();
                return;
        }
@@ -133,7 +133,8 @@ local_flush_tlb_all (void)
 }
 
 void
-flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end)
+flush_tlb_range (struct vm_area_struct *vma, unsigned long start,
+                unsigned long end)
 {
        struct mm_struct *mm = vma->vm_mm;
        unsigned long size = end - start;
@@ -147,23 +148,26 @@ flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long
 #endif
 
        nbits = ia64_fls(size + 0xfff);
-       while (unlikely (((1UL << nbits) & purge.mask) == 0) && (nbits < purge.max_bits))
+       while (unlikely (((1UL << nbits) & purge.mask) == 0) &&
+                       (nbits < purge.max_bits))
                ++nbits;
        if (nbits > purge.max_bits)
                nbits = purge.max_bits;
        start &= ~((1UL << nbits) - 1);
 
-# ifdef CONFIG_SMP
-       platform_global_tlb_purge(mm, start, end, nbits);
-# else
        preempt_disable();
+#ifdef CONFIG_SMP
+       if (mm != current->active_mm || cpus_weight(mm->cpu_vm_mask) != 1) {
+               platform_global_tlb_purge(mm, start, end, nbits);
+               preempt_enable();
+               return;
+       }
+#endif
        do {
                ia64_ptcl(start, (nbits<<2));
                start += (1UL << nbits);
        } while (start < end);
        preempt_enable();
-# endif
-
        ia64_srlz_i();                  /* srlz.i implies srlz.d */
 }
 EXPORT_SYMBOL(flush_tlb_range);
@@ -189,5 +193,5 @@ ia64_tlb_init (void)
        local_cpu_data->ptce_stride[0] = ptce_info.stride[0];
        local_cpu_data->ptce_stride[1] = ptce_info.stride[1];
 
-       local_flush_tlb_all();          /* nuke left overs from bootstrapping... */
+       local_flush_tlb_all();  /* nuke left overs from bootstrapping... */
 }