include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / arch / powerpc / mm / mmu_context_nohash.c
index 8b5de52..1f2d9ff 100644 (file)
  *     also clear mm->cpu_vm_mask bits when processes are migrated
  */
 
-#undef DEBUG
-#define DEBUG_STEAL_ONLY
-#undef DEBUG_MAP_CONSISTENCY
+//#define DEBUG_MAP_CONSISTENCY
+//#define DEBUG_CLAMP_LAST_CONTEXT   31
+//#define DEBUG_HARDER
+
+/* We don't use DEBUG because it tends to be compiled in always nowadays
+ * and this would generate way too much output
+ */
+#ifdef DEBUG_HARDER
+#define pr_hard(args...)       printk(KERN_DEBUG args)
+#define pr_hardcont(args...)   printk(KERN_CONT args)
+#else
+#define pr_hard(args...)       do { } while(0)
+#define pr_hardcont(args...)   do { } while(0)
+#endif
 
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/bootmem.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
 
 #include <asm/mmu_context.h>
 #include <asm/tlbflush.h>
-#include <linux/spinlock.h>
-
-/*
- *   The MPC8xx has only 16 contexts.  We rotate through them on each
- * task switch.  A better way would be to keep track of tasks that
- * own contexts, and implement an LRU usage.  That way very active
- * tasks don't always have to pay the TLB reload overhead.  The
- * kernel pages are mapped shared, so the kernel can run on behalf
- * of any task that makes a kernel entry.  Shared does not mean they
- * are not protected, just that the ASID comparison is not performed.
- *      -- Dan
- *
- * The IBM4xx has 256 contexts, so we can just rotate through these
- * as a way of "switching" contexts.  If the TID of the TLB is zero,
- * the PID/TID comparison is disabled, so we can use a TID of zero
- * to represent all kernel pages as shared among all contexts.
- *     -- Dan
- */
-
-#ifdef CONFIG_8xx
-#define LAST_CONTEXT           15
-#define FIRST_CONTEXT          0
-
-#elif defined(CONFIG_4xx)
-#define LAST_CONTEXT           255
-#define FIRST_CONTEXT          1
 
-#elif defined(CONFIG_E200) || defined(CONFIG_E500)
-#define LAST_CONTEXT           255
-#define FIRST_CONTEXT          1
+static unsigned int first_context, last_context;
+static unsigned int next_context, nr_free_contexts;
+static unsigned long *context_map;
+static unsigned long *stale_map[NR_CPUS];
+static struct mm_struct **context_mm;
+static DEFINE_RAW_SPINLOCK(context_lock);
 
-#else
-#error Unsupported processor type
-#endif
+#define CTX_MAP_SIZE   \
+       (sizeof(unsigned long) * (last_context / BITS_PER_LONG + 1))
 
-static unsigned int next_context, nr_free_contexts;
-static unsigned long context_map[LAST_CONTEXT / BITS_PER_LONG + 1];
-static unsigned long stale_map[NR_CPUS][LAST_CONTEXT / BITS_PER_LONG + 1];
-static struct mm_struct *context_mm[LAST_CONTEXT+1];
-static spinlock_t context_lock = SPIN_LOCK_UNLOCKED;
 
 /* Steal a context from a task that has one at the moment.
  *
@@ -95,10 +82,9 @@ static spinlock_t context_lock = SPIN_LOCK_UNLOCKED;
 static unsigned int steal_context_smp(unsigned int id)
 {
        struct mm_struct *mm;
-       unsigned int cpu, max;
+       unsigned int cpu, max, i;
 
- again:
-       max = LAST_CONTEXT - FIRST_CONTEXT;
+       max = last_context - first_context;
 
        /* Attempt to free next_context first and then loop until we manage */
        while (max--) {
@@ -110,29 +96,38 @@ static unsigned int steal_context_smp(unsigned int id)
                 */
                if (mm->context.active) {
                        id++;
-                       if (id > LAST_CONTEXT)
-                               id = FIRST_CONTEXT;
+                       if (id > last_context)
+                               id = first_context;
                        continue;
                }
-               pr_debug("[%d] steal context %d from mm @%p\n",
-                        smp_processor_id(), id, mm);
+               pr_hardcont(" | steal %d from 0x%p", id, mm);
 
                /* Mark this mm has having no context anymore */
                mm->context.id = MMU_NO_CONTEXT;
 
-               /* Mark it stale on all CPUs that used this mm */
-               for_each_cpu_mask_nr(cpu, mm->cpu_vm_mask)
-                       __set_bit(id, stale_map[cpu]);
+               /* Mark it stale on all CPUs that used this mm. For threaded
+                * implementations, we set it on all threads on each core
+                * represented in the mask. A future implementation will use
+                * a core map instead but this will do for now.
+                */
+               for_each_cpu(cpu, mm_cpumask(mm)) {
+                       for (i = cpu_first_thread_in_core(cpu);
+                            i <= cpu_last_thread_in_core(cpu); i++)
+                               __set_bit(id, stale_map[i]);
+                       cpu = i - 1;
+               }
                return id;
        }
 
        /* This will happen if you have more CPUs than available contexts,
         * all we can do here is wait a bit and try again
         */
-       spin_unlock(&context_lock);
+       raw_spin_unlock(&context_lock);
        cpu_relax();
-       spin_lock(&context_lock);
-       goto again;
+       raw_spin_lock(&context_lock);
+
+       /* This will cause the caller to try again */
+       return MMU_NO_CONTEXT;
 }
 #endif  /* CONFIG_SMP */
 
@@ -149,14 +144,14 @@ static unsigned int steal_context_up(unsigned int id)
        /* Pick up the victim mm */
        mm = context_mm[id];
 
-       pr_debug("[%d] steal context %d from mm @%p\n", cpu, id, mm);
-
-       /* Mark this mm has having no context anymore */
-       mm->context.id = MMU_NO_CONTEXT;
+       pr_hardcont(" | steal %d from 0x%p", id, mm);
 
        /* Flush the TLB for that context */
        local_flush_tlb_mm(mm);
 
+       /* Mark this mm has having no context anymore */
+       mm->context.id = MMU_NO_CONTEXT;
+
        /* XXX This clear should ultimately be part of local_flush_tlb_mm */
        __clear_bit(id, stale_map[cpu]);
 
@@ -169,7 +164,7 @@ static void context_check_map(void)
        unsigned int id, nrf, nact;
 
        nrf = nact = 0;
-       for (id = FIRST_CONTEXT; id <= LAST_CONTEXT; id++) {
+       for (id = first_context; id <= last_context; id++) {
                int used = test_bit(id, context_map);
                if (!used)
                        nrf++;
@@ -187,6 +182,8 @@ static void context_check_map(void)
        if (nact > num_online_cpus())
                pr_err("MMU: More active contexts than CPUs ! (%d vs %d)\n",
                       nact, num_online_cpus());
+       if (first_context > 0 && !test_bit(0, context_map))
+               pr_err("MMU: Context 0 has been freed !!!\n");
 }
 #else
 static void context_check_map(void) { }
@@ -194,35 +191,42 @@ static void context_check_map(void) { }
 
 void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
 {
-       unsigned int id, cpu = smp_processor_id();
+       unsigned int i, id, cpu = smp_processor_id();
        unsigned long *map;
 
        /* No lockless fast path .. yet */
-       spin_lock(&context_lock);
+       raw_spin_lock(&context_lock);
 
-#ifndef DEBUG_STEAL_ONLY
-       pr_debug("[%d] activating context for mm @%p, active=%d, id=%d\n",
-                cpu, next, next->context.active, next->context.id);
-#endif
+       pr_hard("[%d] activating context for mm @%p, active=%d, id=%d",
+               cpu, next, next->context.active, next->context.id);
 
 #ifdef CONFIG_SMP
        /* Mark us active and the previous one not anymore */
        next->context.active++;
        if (prev) {
+               pr_hardcont(" (old=0x%p a=%d)", prev, prev->context.active);
                WARN_ON(prev->context.active < 1);
                prev->context.active--;
        }
+
+ again:
 #endif /* CONFIG_SMP */
 
        /* If we already have a valid assigned context, skip all that */
        id = next->context.id;
-       if (likely(id != MMU_NO_CONTEXT))
+       if (likely(id != MMU_NO_CONTEXT)) {
+#ifdef DEBUG_MAP_CONSISTENCY
+               if (context_mm[id] != next)
+                       pr_err("MMU: mm 0x%p has id %d but context_mm[%d] says 0x%p\n",
+                              next, id, id, context_mm[id]);
+#endif
                goto ctxt_ok;
+       }
 
        /* We really don't have a context, let's try to acquire one */
        id = next_context;
-       if (id > LAST_CONTEXT)
-               id = FIRST_CONTEXT;
+       if (id > last_context)
+               id = first_context;
        map = context_map;
 
        /* No more free contexts, let's try to steal one */
@@ -230,6 +234,8 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
 #ifdef CONFIG_SMP
                if (num_online_cpus() > 1) {
                        id = steal_context_smp(id);
+                       if (id == MMU_NO_CONTEXT)
+                               goto again;
                        goto stolen;
                }
 #endif /* CONFIG_SMP */
@@ -240,19 +246,15 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
 
        /* We know there's at least one free context, try to find it */
        while (__test_and_set_bit(id, map)) {
-               id = find_next_zero_bit(map, LAST_CONTEXT+1, id);
-               if (id > LAST_CONTEXT)
-                       id = FIRST_CONTEXT;
+               id = find_next_zero_bit(map, last_context+1, id);
+               if (id > last_context)
+                       id = first_context;
        }
  stolen:
        next_context = id + 1;
        context_mm[id] = next;
        next->context.id = id;
-
-#ifndef DEBUG_STEAL_ONLY
-       pr_debug("[%d] picked up new id %d, nrf is now %d\n",
-                cpu, id, nr_free_contexts);
-#endif
+       pr_hardcont(" | new id=%d,nrf=%d", id, nr_free_contexts);
 
        context_check_map();
  ctxt_ok:
@@ -261,17 +263,23 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
         * local TLB for it and unmark it before we use it
         */
        if (test_bit(id, stale_map[cpu])) {
-               pr_debug("[%d] flushing stale context %d for mm @%p !\n",
-                        cpu, id, next);
+               pr_hardcont(" | stale flush %d [%d..%d]",
+                           id, cpu_first_thread_in_core(cpu),
+                           cpu_last_thread_in_core(cpu));
+
                local_flush_tlb_mm(next);
 
                /* XXX This clear should ultimately be part of local_flush_tlb_mm */
-               __clear_bit(id, stale_map[cpu]);
+               for (i = cpu_first_thread_in_core(cpu);
+                    i <= cpu_last_thread_in_core(cpu); i++) {
+                       __clear_bit(id, stale_map[i]);
+               }
        }
 
        /* Flick the MMU and release lock */
+       pr_hardcont(" -> %d\n", id);
        set_context(id, next->pgd);
-       spin_unlock(&context_lock);
+       raw_spin_unlock(&context_lock);
 }
 
 /*
@@ -279,6 +287,8 @@ void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next)
  */
 int init_new_context(struct task_struct *t, struct mm_struct *mm)
 {
+       pr_hard("initing context for mm @%p\n", mm);
+
        mm->context.id = MMU_NO_CONTEXT;
        mm->context.active = 0;
 
@@ -290,6 +300,7 @@ int init_new_context(struct task_struct *t, struct mm_struct *mm)
  */
 void destroy_context(struct mm_struct *mm)
 {
+       unsigned long flags;
        unsigned int id;
 
        if (mm->context.id == MMU_NO_CONTEXT)
@@ -297,20 +308,66 @@ void destroy_context(struct mm_struct *mm)
 
        WARN_ON(mm->context.active != 0);
 
-       spin_lock(&context_lock);
+       raw_spin_lock_irqsave(&context_lock, flags);
        id = mm->context.id;
        if (id != MMU_NO_CONTEXT) {
                __clear_bit(id, context_map);
                mm->context.id = MMU_NO_CONTEXT;
 #ifdef DEBUG_MAP_CONSISTENCY
                mm->context.active = 0;
-               context_mm[id] = NULL;
 #endif
+               context_mm[id] = NULL;
                nr_free_contexts++;
        }
-       spin_unlock(&context_lock);
+       raw_spin_unlock_irqrestore(&context_lock, flags);
 }
 
+#ifdef CONFIG_SMP
+
+static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self,
+                                           unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned int)(long)hcpu;
+#ifdef CONFIG_HOTPLUG_CPU
+       struct task_struct *p;
+#endif
+       /* We don't touch CPU 0 map, it's allocated at aboot and kept
+        * around forever
+        */
+       if (cpu == 0)
+               return NOTIFY_OK;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               pr_devel("MMU: Allocating stale context map for CPU %d\n", cpu);
+               stale_map[cpu] = kzalloc(CTX_MAP_SIZE, GFP_KERNEL);
+               break;
+#ifdef CONFIG_HOTPLUG_CPU
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               pr_devel("MMU: Freeing stale context map for CPU %d\n", cpu);
+               kfree(stale_map[cpu]);
+               stale_map[cpu] = NULL;
+
+               /* We also clear the cpu_vm_mask bits of CPUs going away */
+               read_lock(&tasklist_lock);
+               for_each_process(p) {
+                       if (p->mm)
+                               cpumask_clear_cpu(cpu, mm_cpumask(p->mm));
+               }
+               read_unlock(&tasklist_lock);
+       break;
+#endif /* CONFIG_HOTPLUG_CPU */
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata mmu_context_cpu_nb = {
+       .notifier_call  = mmu_context_cpu_notify,
+};
+
+#endif /* CONFIG_SMP */
 
 /*
  * Initialize the context management stuff.
@@ -324,13 +381,56 @@ void __init mmu_context_init(void)
        init_mm.context.active = NR_CPUS;
 
        /*
+        *   The MPC8xx has only 16 contexts.  We rotate through them on each
+        * task switch.  A better way would be to keep track of tasks that
+        * own contexts, and implement an LRU usage.  That way very active
+        * tasks don't always have to pay the TLB reload overhead.  The
+        * kernel pages are mapped shared, so the kernel can run on behalf
+        * of any task that makes a kernel entry.  Shared does not mean they
+        * are not protected, just that the ASID comparison is not performed.
+        *      -- Dan
+        *
+        * The IBM4xx has 256 contexts, so we can just rotate through these
+        * as a way of "switching" contexts.  If the TID of the TLB is zero,
+        * the PID/TID comparison is disabled, so we can use a TID of zero
+        * to represent all kernel pages as shared among all contexts.
+        *      -- Dan
+        */
+       if (mmu_has_feature(MMU_FTR_TYPE_8xx)) {
+               first_context = 0;
+               last_context = 15;
+       } else {
+               first_context = 1;
+               last_context = 255;
+       }
+
+#ifdef DEBUG_CLAMP_LAST_CONTEXT
+       last_context = DEBUG_CLAMP_LAST_CONTEXT;
+#endif
+       /*
+        * Allocate the maps used by context management
+        */
+       context_map = alloc_bootmem(CTX_MAP_SIZE);
+       context_mm = alloc_bootmem(sizeof(void *) * (last_context + 1));
+       stale_map[0] = alloc_bootmem(CTX_MAP_SIZE);
+
+#ifdef CONFIG_SMP
+       register_cpu_notifier(&mmu_context_cpu_nb);
+#endif
+
+       printk(KERN_INFO
+              "MMU: Allocated %zu bytes of context maps for %d contexts\n",
+              2 * CTX_MAP_SIZE + (sizeof(void *) * (last_context + 1)),
+              last_context - first_context + 1);
+
+       /*
         * Some processors have too few contexts to reserve one for
         * init_mm, and require using context 0 for a normal task.
         * Other processors reserve the use of context zero for the kernel.
-        * This code assumes FIRST_CONTEXT < 32.
+        * This code assumes first_context < 32.
         */
-       context_map[0] = (1 << FIRST_CONTEXT) - 1;
-       next_context = FIRST_CONTEXT;
-       nr_free_contexts = LAST_CONTEXT - FIRST_CONTEXT + 1;
+       context_map[0] = (1 << first_context) - 1;
+       next_context = first_context;
+       nr_free_contexts = last_context - first_context + 1;
 }