Merge branch 'x86/mm' into core/percpu
authorIngo Molnar <mingo@elte.hu>
Wed, 21 Jan 2009 09:39:51 +0000 (10:39 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 21 Jan 2009 09:39:51 +0000 (10:39 +0100)
Conflicts:
arch/x86/mm/fault.c

1  2 
arch/powerpc/kernel/vmlinux.lds.S
arch/powerpc/sysdev/mpic.c
arch/x86/mm/fault.c
arch/x86/mm/pat.c
arch/x86/mm/tlb.c
include/linux/magic.h
kernel/exit.c
kernel/fork.c
kernel/sched.c

Simple merge
Simple merge
index 37242c405f1675a096038899ebeb34623f5c2377,033292dc9e214d96b28ff6154bca2af366b4b711..65709a6aa6ee046dab8e3368075a371cd4e2a6a0
@@@ -430,6 -429,190 +430,196 @@@ static noinline void pgtable_bad(struc
  }
  #endif
  
+ static noinline void no_context(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address)
+ {
+       struct task_struct *tsk = current;
++      unsigned long *stackend;
++
+ #ifdef CONFIG_X86_64
+       unsigned long flags;
+       int sig;
+ #endif
+       /* Are we prepared to handle this kernel fault?  */
+       if (fixup_exception(regs))
+               return;
+       /*
+        * X86_32
+        * Valid to do another page fault here, because if this fault
+        * had been triggered by is_prefetch fixup_exception would have
+        * handled it.
+        *
+        * X86_64
+        * Hall of shame of CPU/BIOS bugs.
+        */
+       if (is_prefetch(regs, error_code, address))
+               return;
+       if (is_errata93(regs, address))
+               return;
+       /*
+        * Oops. The kernel tried to access some bad page. We'll have to
+        * terminate things with extreme prejudice.
+        */
+ #ifdef CONFIG_X86_32
+       bust_spinlocks(1);
+ #else
+       flags = oops_begin();
+ #endif
+       show_fault_oops(regs, error_code, address);
++      stackend = end_of_stack(tsk);
++      if (*stackend != STACK_END_MAGIC)
++              printk(KERN_ALERT "Thread overran stack, or stack corrupted\n");
++
+       tsk->thread.cr2 = address;
+       tsk->thread.trap_no = 14;
+       tsk->thread.error_code = error_code;
+ #ifdef CONFIG_X86_32
+       die("Oops", regs, error_code);
+       bust_spinlocks(0);
+       do_exit(SIGKILL);
+ #else
+       sig = SIGKILL;
+       if (__die("Oops", regs, error_code))
+               sig = 0;
+       /* Executive summary in case the body of the oops scrolled away */
+       printk(KERN_EMERG "CR2: %016lx\n", address);
+       oops_end(flags, regs, sig);
+ #endif
+ }
+ static void __bad_area_nosemaphore(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address,
+                       int si_code)
+ {
+       struct task_struct *tsk = current;
+       /* User mode accesses just cause a SIGSEGV */
+       if (error_code & PF_USER) {
+               /*
+                * It's possible to have interrupts off here.
+                */
+               local_irq_enable();
+               /*
+                * Valid to do another page fault here because this one came
+                * from user space.
+                */
+               if (is_prefetch(regs, error_code, address))
+                       return;
+               if (is_errata100(regs, address))
+                       return;
+               if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
+                   printk_ratelimit()) {
+                       printk(
+                       "%s%s[%d]: segfault at %lx ip %p sp %p error %lx",
+                       task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG,
+                       tsk->comm, task_pid_nr(tsk), address,
+                       (void *) regs->ip, (void *) regs->sp, error_code);
+                       print_vma_addr(" in ", regs->ip);
+                       printk("\n");
+               }
+               tsk->thread.cr2 = address;
+               /* Kernel addresses are always protection faults */
+               tsk->thread.error_code = error_code | (address >= TASK_SIZE);
+               tsk->thread.trap_no = 14;
+               force_sig_info_fault(SIGSEGV, si_code, address, tsk);
+               return;
+       }
+       if (is_f00f_bug(regs, address))
+               return;
+       no_context(regs, error_code, address);
+ }
+ static noinline void bad_area_nosemaphore(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address)
+ {
+       __bad_area_nosemaphore(regs, error_code, address, SEGV_MAPERR);
+ }
+ static void __bad_area(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address,
+                       int si_code)
+ {
+       struct mm_struct *mm = current->mm;
+       /*
+        * Something tried to access memory that isn't in our memory map..
+        * Fix it, but check if it's kernel or user first..
+        */
+       up_read(&mm->mmap_sem);
+       __bad_area_nosemaphore(regs, error_code, address, si_code);
+ }
+ static noinline void bad_area(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address)
+ {
+       __bad_area(regs, error_code, address, SEGV_MAPERR);
+ }
+ static noinline void bad_area_access_error(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address)
+ {
+       __bad_area(regs, error_code, address, SEGV_ACCERR);
+ }
+ /* TODO: fixup for "mm-invoke-oom-killer-from-page-fault.patch" */
+ static void out_of_memory(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address)
+ {
+       /*
+        * We ran out of memory, call the OOM killer, and return the userspace
+        * (which will retry the fault, or kill us if we got oom-killed).
+        */
+       up_read(&current->mm->mmap_sem);
+       pagefault_out_of_memory();
+ }
+ static void do_sigbus(struct pt_regs *regs,
+                       unsigned long error_code, unsigned long address)
+ {
+       struct task_struct *tsk = current;
+       struct mm_struct *mm = tsk->mm;
+       up_read(&mm->mmap_sem);
+       /* Kernel mode? Handle exceptions or die */
+       if (!(error_code & PF_USER))
+               no_context(regs, error_code, address);
+ #ifdef CONFIG_X86_32
+       /* User space => ok to do another page fault */
+       if (is_prefetch(regs, error_code, address))
+               return;
+ #endif
+       tsk->thread.cr2 = address;
+       tsk->thread.error_code = error_code;
+       tsk->thread.trap_no = 14;
+       force_sig_info_fault(SIGBUS, BUS_ADRERR, address, tsk);
+ }
+ static noinline void mm_fault_error(struct pt_regs *regs,
+               unsigned long error_code, unsigned long address, unsigned int fault)
+ {
+       if (fault & VM_FAULT_OOM)
+               out_of_memory(regs, error_code, address);
+       else if (fault & VM_FAULT_SIGBUS)
+               do_sigbus(regs, error_code, address);
+       else
+               BUG();
+ }
  static int spurious_fault_check(unsigned long error_code, pte_t *pte)
  {
        if ((error_code & PF_WRITE) && !pte_write(*pte))
Simple merge
index b3ca1b940654fed9a0317d7765cee816745f38e5,0000000000000000000000000000000000000000..72a6d4ebe34dd9b2a4441dc422dbcd46ed47bb2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,296 -1,0 +1,296 @@@
-  *    the right per cpu variable for the flush data.
 +#include <linux/init.h>
 +
 +#include <linux/mm.h>
 +#include <linux/spinlock.h>
 +#include <linux/smp.h>
 +#include <linux/interrupt.h>
 +#include <linux/module.h>
 +
 +#include <asm/tlbflush.h>
 +#include <asm/mmu_context.h>
 +#include <asm/apic.h>
 +#include <asm/uv/uv.h>
 +
 +DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate)
 +                      = { &init_mm, 0, };
 +
 +#include <mach_ipi.h>
 +/*
 + *    Smarter SMP flushing macros.
 + *            c/o Linus Torvalds.
 + *
 + *    These mean you can really definitely utterly forget about
 + *    writing to user space from interrupts. (Its not allowed anyway).
 + *
 + *    Optimizations Manfred Spraul <manfred@colorfullife.com>
 + *
 + *    More scalable flush, from Andi Kleen
 + *
 + *    To avoid global state use 8 different call vectors.
 + *    Each CPU uses a specific vector to trigger flushes on other
 + *    CPUs. Depending on the received vector the target CPUs look into
-       char pad[SMP_CACHE_BYTES];
- } ____cacheline_aligned;
++ *    the right array slot for the flush data.
 + *
 + *    With more than 8 CPUs they are hashed to the 8 available
 + *    vectors. The limited global vector space forces us to this right now.
 + *    In future when interrupts are split into per CPU domains this could be
 + *    fixed, at the cost of triggering multiple IPIs in some cases.
 + */
 +
 +union smp_flush_state {
 +      struct {
 +              struct mm_struct *flush_mm;
 +              unsigned long flush_va;
 +              spinlock_t tlbstate_lock;
 +              DECLARE_BITMAP(flush_cpumask, NR_CPUS);
 +      };
- static DEFINE_PER_CPU(union smp_flush_state, flush_state);
++      char pad[CONFIG_X86_INTERNODE_CACHE_BYTES];
++} ____cacheline_internodealigned_in_smp;
 +
 +/* State is put into the per CPU data section, but padded
 +   to a full cache line because other CPUs can access it and we don't
 +   want false sharing in the per cpu data segment. */
-       f = &per_cpu(flush_state, sender);
++static union smp_flush_state flush_state[NUM_INVALIDATE_TLB_VECTORS];
 +
 +/*
 + * We cannot call mmdrop() because we are in interrupt context,
 + * instead update mm->cpu_vm_mask.
 + */
 +void leave_mm(int cpu)
 +{
 +      if (percpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
 +              BUG();
 +      cpu_clear(cpu, percpu_read(cpu_tlbstate.active_mm)->cpu_vm_mask);
 +      load_cr3(swapper_pg_dir);
 +}
 +EXPORT_SYMBOL_GPL(leave_mm);
 +
 +/*
 + *
 + * The flush IPI assumes that a thread switch happens in this order:
 + * [cpu0: the cpu that switches]
 + * 1) switch_mm() either 1a) or 1b)
 + * 1a) thread switch to a different mm
 + * 1a1) cpu_clear(cpu, old_mm->cpu_vm_mask);
 + *    Stop ipi delivery for the old mm. This is not synchronized with
 + *    the other cpus, but smp_invalidate_interrupt ignore flush ipis
 + *    for the wrong mm, and in the worst case we perform a superfluous
 + *    tlb flush.
 + * 1a2) set cpu mmu_state to TLBSTATE_OK
 + *    Now the smp_invalidate_interrupt won't call leave_mm if cpu0
 + *    was in lazy tlb mode.
 + * 1a3) update cpu active_mm
 + *    Now cpu0 accepts tlb flushes for the new mm.
 + * 1a4) cpu_set(cpu, new_mm->cpu_vm_mask);
 + *    Now the other cpus will send tlb flush ipis.
 + * 1a4) change cr3.
 + * 1b) thread switch without mm change
 + *    cpu active_mm is correct, cpu0 already handles
 + *    flush ipis.
 + * 1b1) set cpu mmu_state to TLBSTATE_OK
 + * 1b2) test_and_set the cpu bit in cpu_vm_mask.
 + *    Atomically set the bit [other cpus will start sending flush ipis],
 + *    and test the bit.
 + * 1b3) if the bit was 0: leave_mm was called, flush the tlb.
 + * 2) switch %%esp, ie current
 + *
 + * The interrupt must handle 2 special cases:
 + * - cr3 is changed before %%esp, ie. it cannot use current->{active_,}mm.
 + * - the cpu performs speculative tlb reads, i.e. even if the cpu only
 + *   runs in kernel space, the cpu could load tlb entries for user space
 + *   pages.
 + *
 + * The good news is that cpu mmu_state is local to each cpu, no
 + * write/read ordering problems.
 + */
 +
 +/*
 + * TLB flush IPI:
 + *
 + * 1) Flush the tlb entries if the cpu uses the mm that's being flushed.
 + * 2) Leave the mm if we are in the lazy tlb mode.
 + *
 + * Interrupts are disabled.
 + */
 +
 +/*
 + * FIXME: use of asmlinkage is not consistent.  On x86_64 it's noop
 + * but still used for documentation purpose but the usage is slightly
 + * inconsistent.  On x86_32, asmlinkage is regparm(0) but interrupt
 + * entry calls in with the first parameter in %eax.  Maybe define
 + * intrlinkage?
 + */
 +#ifdef CONFIG_X86_64
 +asmlinkage
 +#endif
 +void smp_invalidate_interrupt(struct pt_regs *regs)
 +{
 +      unsigned int cpu;
 +      unsigned int sender;
 +      union smp_flush_state *f;
 +
 +      cpu = smp_processor_id();
 +      /*
 +       * orig_rax contains the negated interrupt vector.
 +       * Use that to determine where the sender put the data.
 +       */
 +      sender = ~regs->orig_ax - INVALIDATE_TLB_VECTOR_START;
-       f = &per_cpu(flush_state, sender);
++      f = &flush_state[sender];
 +
 +      if (!cpumask_test_cpu(cpu, to_cpumask(f->flush_cpumask)))
 +              goto out;
 +              /*
 +               * This was a BUG() but until someone can quote me the
 +               * line from the intel manual that guarantees an IPI to
 +               * multiple CPUs is retried _only_ on the erroring CPUs
 +               * its staying as a return
 +               *
 +               * BUG();
 +               */
 +
 +      if (f->flush_mm == percpu_read(cpu_tlbstate.active_mm)) {
 +              if (percpu_read(cpu_tlbstate.state) == TLBSTATE_OK) {
 +                      if (f->flush_va == TLB_FLUSH_ALL)
 +                              local_flush_tlb();
 +                      else
 +                              __flush_tlb_one(f->flush_va);
 +              } else
 +                      leave_mm(cpu);
 +      }
 +out:
 +      ack_APIC_irq();
 +      smp_mb__before_clear_bit();
 +      cpumask_clear_cpu(cpu, to_cpumask(f->flush_cpumask));
 +      smp_mb__after_clear_bit();
 +      inc_irq_stat(irq_tlb_count);
 +}
 +
 +static void flush_tlb_others_ipi(const struct cpumask *cpumask,
 +                               struct mm_struct *mm, unsigned long va)
 +{
 +      unsigned int sender;
 +      union smp_flush_state *f;
 +
 +      /* Caller has disabled preemption */
 +      sender = smp_processor_id() % NUM_INVALIDATE_TLB_VECTORS;
-       for_each_possible_cpu(i)
-               spin_lock_init(&per_cpu(flush_state, i).tlbstate_lock);
++      f = &flush_state[sender];
 +
 +      /*
 +       * Could avoid this lock when
 +       * num_online_cpus() <= NUM_INVALIDATE_TLB_VECTORS, but it is
 +       * probably not worth checking this for a cache-hot lock.
 +       */
 +      spin_lock(&f->tlbstate_lock);
 +
 +      f->flush_mm = mm;
 +      f->flush_va = va;
 +      cpumask_andnot(to_cpumask(f->flush_cpumask),
 +                     cpumask, cpumask_of(smp_processor_id()));
 +
 +      /*
 +       * Make the above memory operations globally visible before
 +       * sending the IPI.
 +       */
 +      smp_mb();
 +      /*
 +       * We have to send the IPI only to
 +       * CPUs affected.
 +       */
 +      send_IPI_mask(to_cpumask(f->flush_cpumask),
 +                    INVALIDATE_TLB_VECTOR_START + sender);
 +
 +      while (!cpumask_empty(to_cpumask(f->flush_cpumask)))
 +              cpu_relax();
 +
 +      f->flush_mm = NULL;
 +      f->flush_va = 0;
 +      spin_unlock(&f->tlbstate_lock);
 +}
 +
 +void native_flush_tlb_others(const struct cpumask *cpumask,
 +                           struct mm_struct *mm, unsigned long va)
 +{
 +      if (is_uv_system()) {
 +              unsigned int cpu;
 +
 +              cpu = get_cpu();
 +              cpumask = uv_flush_tlb_others(cpumask, mm, va, cpu);
 +              if (cpumask)
 +                      flush_tlb_others_ipi(cpumask, mm, va);
 +              put_cpu();
 +              return;
 +      }
 +      flush_tlb_others_ipi(cpumask, mm, va);
 +}
 +
 +static int __cpuinit init_smp_flush(void)
 +{
 +      int i;
 +
++      for (i = 0; i < ARRAY_SIZE(flush_state); i++)
++              spin_lock_init(&flush_state[i].tlbstate_lock);
 +
 +      return 0;
 +}
 +core_initcall(init_smp_flush);
 +
 +void flush_tlb_current_task(void)
 +{
 +      struct mm_struct *mm = current->mm;
 +
 +      preempt_disable();
 +
 +      local_flush_tlb();
 +      if (cpumask_any_but(&mm->cpu_vm_mask, smp_processor_id()) < nr_cpu_ids)
 +              flush_tlb_others(&mm->cpu_vm_mask, mm, TLB_FLUSH_ALL);
 +      preempt_enable();
 +}
 +
 +void flush_tlb_mm(struct mm_struct *mm)
 +{
 +      preempt_disable();
 +
 +      if (current->active_mm == mm) {
 +              if (current->mm)
 +                      local_flush_tlb();
 +              else
 +                      leave_mm(smp_processor_id());
 +      }
 +      if (cpumask_any_but(&mm->cpu_vm_mask, smp_processor_id()) < nr_cpu_ids)
 +              flush_tlb_others(&mm->cpu_vm_mask, mm, TLB_FLUSH_ALL);
 +
 +      preempt_enable();
 +}
 +
 +void flush_tlb_page(struct vm_area_struct *vma, unsigned long va)
 +{
 +      struct mm_struct *mm = vma->vm_mm;
 +
 +      preempt_disable();
 +
 +      if (current->active_mm == mm) {
 +              if (current->mm)
 +                      __flush_tlb_one(va);
 +              else
 +                      leave_mm(smp_processor_id());
 +      }
 +
 +      if (cpumask_any_but(&mm->cpu_vm_mask, smp_processor_id()) < nr_cpu_ids)
 +              flush_tlb_others(&mm->cpu_vm_mask, mm, va);
 +
 +      preempt_enable();
 +}
 +
 +static void do_flush_tlb_all(void *info)
 +{
 +      unsigned long cpu = smp_processor_id();
 +
 +      __flush_tlb_all();
 +      if (percpu_read(cpu_tlbstate.state) == TLBSTATE_LAZY)
 +              leave_mm(cpu);
 +}
 +
 +void flush_tlb_all(void)
 +{
 +      on_each_cpu(do_flush_tlb_all, NULL, 1);
 +}
Simple merge
diff --cc kernel/exit.c
Simple merge
diff --cc kernel/fork.c
Simple merge
diff --cc kernel/sched.c
Simple merge