x86/mm/tlb: Add freed_tables element to flush_tlb_info
[linux-2.6-microblaze.git] / arch / x86 / mm / tlb.c
index e96b99e..92e46f4 100644 (file)
@@ -187,6 +187,8 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
        u16 prev_asid = this_cpu_read(cpu_tlbstate.loaded_mm_asid);
        unsigned cpu = smp_processor_id();
        u64 next_tlb_gen;
+       bool need_flush;
+       u16 new_asid;
 
        /*
         * NB: The scheduler will call us with prev == next when switching
@@ -252,8 +254,6 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
 
                return;
        } else {
-               u16 new_asid;
-               bool need_flush;
                u64 last_ctx_id = this_cpu_read(cpu_tlbstate.last_ctx_id);
 
                /*
@@ -308,44 +308,44 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
                /* Let nmi_uaccess_okay() know that we're changing CR3. */
                this_cpu_write(cpu_tlbstate.loaded_mm, LOADED_MM_SWITCHING);
                barrier();
+       }
 
-               if (need_flush) {
-                       this_cpu_write(cpu_tlbstate.ctxs[new_asid].ctx_id, next->context.ctx_id);
-                       this_cpu_write(cpu_tlbstate.ctxs[new_asid].tlb_gen, next_tlb_gen);
-                       load_new_mm_cr3(next->pgd, new_asid, true);
-
-                       /*
-                        * NB: This gets called via leave_mm() in the idle path
-                        * where RCU functions differently.  Tracing normally
-                        * uses RCU, so we need to use the _rcuidle variant.
-                        *
-                        * (There is no good reason for this.  The idle code should
-                        *  be rearranged to call this before rcu_idle_enter().)
-                        */
-                       trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
-               } else {
-                       /* The new ASID is already up to date. */
-                       load_new_mm_cr3(next->pgd, new_asid, false);
-
-                       /* See above wrt _rcuidle. */
-                       trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, 0);
-               }
+       if (need_flush) {
+               this_cpu_write(cpu_tlbstate.ctxs[new_asid].ctx_id, next->context.ctx_id);
+               this_cpu_write(cpu_tlbstate.ctxs[new_asid].tlb_gen, next_tlb_gen);
+               load_new_mm_cr3(next->pgd, new_asid, true);
 
                /*
-                * Record last user mm's context id, so we can avoid
-                * flushing branch buffer with IBPB if we switch back
-                * to the same user.
+                * NB: This gets called via leave_mm() in the idle path
+                * where RCU functions differently.  Tracing normally
+                * uses RCU, so we need to use the _rcuidle variant.
+                *
+                * (There is no good reason for this.  The idle code should
+                *  be rearranged to call this before rcu_idle_enter().)
                 */
-               if (next != &init_mm)
-                       this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id);
-
-               /* Make sure we write CR3 before loaded_mm. */
-               barrier();
+               trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
+       } else {
+               /* The new ASID is already up to date. */
+               load_new_mm_cr3(next->pgd, new_asid, false);
 
-               this_cpu_write(cpu_tlbstate.loaded_mm, next);
-               this_cpu_write(cpu_tlbstate.loaded_mm_asid, new_asid);
+               /* See above wrt _rcuidle. */
+               trace_tlb_flush_rcuidle(TLB_FLUSH_ON_TASK_SWITCH, 0);
        }
 
+       /*
+        * Record last user mm's context id, so we can avoid
+        * flushing branch buffer with IBPB if we switch back
+        * to the same user.
+        */
+       if (next != &init_mm)
+               this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id);
+
+       /* Make sure we write CR3 before loaded_mm. */
+       barrier();
+
+       this_cpu_write(cpu_tlbstate.loaded_mm, next);
+       this_cpu_write(cpu_tlbstate.loaded_mm_asid, new_asid);
+
        load_mm_cr4(next);
        switch_ldt(real_prev, next);
 }
@@ -368,20 +368,7 @@ void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
        if (this_cpu_read(cpu_tlbstate.loaded_mm) == &init_mm)
                return;
 
-       if (tlb_defer_switch_to_init_mm()) {
-               /*
-                * There's a significant optimization that may be possible
-                * here.  We have accurate enough TLB flush tracking that we
-                * don't need to maintain coherence of TLB per se when we're
-                * lazy.  We do, however, need to maintain coherence of
-                * paging-structure caches.  We could, in principle, leave our
-                * old mm loaded and only switch to init_mm when
-                * tlb_remove_page() happens.
-                */
-               this_cpu_write(cpu_tlbstate.is_lazy, true);
-       } else {
-               switch_mm(NULL, &init_mm, NULL);
-       }
+       this_cpu_write(cpu_tlbstate.is_lazy, true);
 }
 
 /*
@@ -528,17 +515,16 @@ static void flush_tlb_func_common(const struct flush_tlb_info *f,
            f->new_tlb_gen == local_tlb_gen + 1 &&
            f->new_tlb_gen == mm_tlb_gen) {
                /* Partial flush */
-               unsigned long addr;
-               unsigned long nr_pages = (f->end - f->start) >> PAGE_SHIFT;
+               unsigned long nr_invalidate = (f->end - f->start) >> f->stride_shift;
+               unsigned long addr = f->start;
 
-               addr = f->start;
                while (addr < f->end) {
                        __flush_tlb_one_user(addr);
-                       addr += PAGE_SIZE;
+                       addr += 1UL << f->stride_shift;
                }
                if (local)
-                       count_vm_tlb_events(NR_TLB_LOCAL_FLUSH_ONE, nr_pages);
-               trace_tlb_flush(reason, nr_pages);
+                       count_vm_tlb_events(NR_TLB_LOCAL_FLUSH_ONE, nr_invalidate);
+               trace_tlb_flush(reason, nr_invalidate);
        } else {
                /* Full flush. */
                local_flush_tlb();
@@ -623,12 +609,15 @@ void native_flush_tlb_others(const struct cpumask *cpumask,
 static unsigned long tlb_single_page_flush_ceiling __read_mostly = 33;
 
 void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
-                               unsigned long end, unsigned long vmflag)
+                               unsigned long end, unsigned int stride_shift,
+                               bool freed_tables)
 {
        int cpu;
 
        struct flush_tlb_info info __aligned(SMP_CACHE_BYTES) = {
                .mm = mm,
+               .stride_shift = stride_shift,
+               .freed_tables = freed_tables,
        };
 
        cpu = get_cpu();
@@ -638,8 +627,7 @@ void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
 
        /* Should we flush just the requested range? */
        if ((end != TLB_FLUSH_ALL) &&
-           !(vmflag & VM_HUGETLB) &&
-           ((end - start) >> PAGE_SHIFT) <= tlb_single_page_flush_ceiling) {
+           ((end - start) >> stride_shift) <= tlb_single_page_flush_ceiling) {
                info.start = start;
                info.end = end;
        } else {