diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/arch/alpha/kernel/smp.c 2.4.0-test12-pre2-alpha/arch/alpha/kernel/smp.c --- 2.4.0-test12-pre2/arch/alpha/kernel/smp.c Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/arch/alpha/kernel/smp.c Fri Dec 1 14:55:59 2000 @@ -106,8 +106,9 @@ smp_store_cpu_info(int cpuid) { cpu_data[cpuid].loops_per_sec = loops_per_sec; - cpu_data[cpuid].last_asn - = (cpuid << WIDTH_HARDWARE_ASN) + ASN_FIRST_VERSION; + cpu_data[cpuid].last_asn = ASN_FIRST_VERSION; + cpu_data[cpuid].need_new_asn = 0; + cpu_data[cpuid].asn_lock = 0; local_irq_count(cpuid) = 0; local_bh_count(cpuid) = 0; } @@ -898,12 +899,16 @@ tbia(); } +#define asn_locked() (cpu_data[smp_processor_id()].asn_lock) + static void ipi_flush_tlb_mm(void *x) { struct mm_struct *mm = (struct mm_struct *) x; - if (mm == current->active_mm) + if (mm == current->active_mm && !asn_locked()) flush_tlb_current(mm); + else + flush_tlb_other(mm); } void @@ -911,10 +916,18 @@ { if (mm == current->active_mm) { flush_tlb_current(mm); - if (atomic_read(&mm->mm_users) <= 1) + if (atomic_read(&mm->mm_users) <= 1) { + int i, cpu, this_cpu = smp_processor_id(); + for (i = 0; i < smp_num_cpus; i++) { + cpu = cpu_logical_map(i); + if (cpu == this_cpu) + continue; + if (mm->context[cpu]) + mm->context[cpu] = 0; + } return; - } else - flush_tlb_other(mm); + } + } if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) { printk(KERN_CRIT "flush_tlb_mm: timed out\n"); @@ -931,8 +944,12 @@ ipi_flush_tlb_page(void *x) { struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x; - if (data->mm == current->active_mm) - flush_tlb_current_page(data->mm, data->vma, data->addr); + struct mm_struct * mm = data->mm; + + if (mm == current->active_mm && !asn_locked()) + flush_tlb_current_page(mm, data->vma, data->addr); + else + flush_tlb_other(mm); } void @@ -943,10 +960,18 @@ if (mm == current->active_mm) { flush_tlb_current_page(mm, vma, addr); - if (atomic_read(&mm->mm_users) <= 1) + if (atomic_read(&mm->mm_users) <= 1) { + int i, cpu, this_cpu = smp_processor_id(); + for (i = 0; i < smp_num_cpus; i++) { + cpu = cpu_logical_map(i); + if (cpu == this_cpu) + continue; + if (mm->context[cpu]) + mm->context[cpu] = 0; + } return; - } else - flush_tlb_other(mm); + } + } data.vma = vma; data.mm = mm; @@ -968,8 +993,10 @@ ipi_flush_icache_page(void *x) { struct mm_struct *mm = (struct mm_struct *) x; - if (mm == current->active_mm) + if (mm == current->active_mm && !asn_locked()) __load_new_mm_context(mm); + else + flush_tlb_other(mm); } void @@ -980,11 +1007,19 @@ if ((vma->vm_flags & VM_EXEC) == 0) return; - mm->context = 0; if (mm == current->active_mm) { __load_new_mm_context(mm); - if (atomic_read(&mm->mm_users) <= 1) + if (atomic_read(&mm->mm_users) <= 1) { + int i, cpu, this_cpu = smp_processor_id(); + for (i = 0; i < smp_num_cpus; i++) { + cpu = cpu_logical_map(i); + if (cpu == this_cpu) + continue; + if (mm->context[cpu]) + mm->context[cpu] = 0; + } return; + } } if (smp_call_function(ipi_flush_icache_page, mm, 1, 1)) { diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/arch/alpha/mm/fault.c 2.4.0-test12-pre2-alpha/arch/alpha/mm/fault.c --- 2.4.0-test12-pre2/arch/alpha/mm/fault.c Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/arch/alpha/mm/fault.c Fri Dec 1 14:55:59 2000 @@ -45,7 +45,7 @@ unsigned long mmc; mmc = __get_new_mm_context(next_mm, smp_processor_id()); - next_mm->context = mmc; + next_mm->context[smp_processor_id()] = mmc; current->thread.asn = mmc & HARDWARE_ASN_MASK; current->thread.ptbr = ((unsigned long) next_mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/include/asm-alpha/mmu_context.h 2.4.0-test12-pre2-alpha/include/asm-alpha/mmu_context.h --- 2.4.0-test12-pre2/include/asm-alpha/mmu_context.h Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/include/asm-alpha/mmu_context.h Fri Dec 1 14:56:55 2000 @@ -93,12 +93,7 @@ #endif /* CONFIG_SMP */ #define WIDTH_HARDWARE_ASN 8 -#ifdef CONFIG_SMP -#define WIDTH_THIS_PROCESSOR 5 -#else -#define WIDTH_THIS_PROCESSOR 0 -#endif -#define ASN_FIRST_VERSION (1UL << (WIDTH_THIS_PROCESSOR + WIDTH_HARDWARE_ASN)) +#define ASN_FIRST_VERSION (1UL << WIDTH_HARDWARE_ASN) #define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1) /* @@ -137,19 +132,24 @@ ev5_switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, struct task_struct *next, long cpu) { - /* Check if our ASN is of an older version, or on a different CPU, - and thus invalid. */ - /* ??? If we have two threads on different cpus, we'll continually - fight over the context. Find a way to record a per-mm, per-cpu - value for the asn. */ + /* Check if our ASN is of an older version, and thus invalid. */ + unsigned long asn; + unsigned long mmc; - unsigned long asn = cpu_last_asn(cpu); - unsigned long mmc = next_mm->context; - +#ifdef CONFIG_SMP + cpu_data[cpu].asn_lock = 1; + barrier(); +#endif + asn = cpu_last_asn(cpu); + mmc = next_mm->context[cpu]; if ((mmc ^ asn) & ~HARDWARE_ASN_MASK) { mmc = __get_new_mm_context(next_mm, cpu); - next_mm->context = mmc; + next_mm->context[cpu] = mmc; } +#ifdef CONFIG_SMP + else + cpu_data[cpu].need_new_asn = 1; +#endif /* Always update the PCB ASN. Another thread may have allocated a new mm->context (via flush_tlb_mm) without the ASN serial @@ -179,6 +179,23 @@ extern void __load_new_mm_context(struct mm_struct *); +#ifdef CONFIG_SMP +#define check_mmu_context() \ +do { \ + int cpu = smp_processor_id(); \ + cpu_data[cpu].asn_lock = 0; \ + barrier(); \ + if (cpu_data[cpu].need_new_asn) { \ + struct mm_struct * mm = current->active_mm; \ + cpu_data[cpu].need_new_asn = 0; \ + if (!mm->context[cpu]) \ + __load_new_mm_context(mm); \ + } \ +} while(0) +#else +#define check_mmu_context() do { } while(0) +#endif + __EXTERN_INLINE void ev5_activate_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm) { @@ -208,7 +225,10 @@ extern inline int init_new_context(struct task_struct *tsk, struct mm_struct *mm) { - mm->context = 0; + int i; + + for (i = 0; i < smp_num_cpus; i++) + mm->context[cpu_logical_map(i)] = 0; tsk->thread.ptbr = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT; return 0; } diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/include/asm-alpha/pgalloc.h 2.4.0-test12-pre2-alpha/include/asm-alpha/pgalloc.h --- 2.4.0-test12-pre2/include/asm-alpha/pgalloc.h Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/include/asm-alpha/pgalloc.h Fri Dec 1 14:55:59 2000 @@ -38,29 +38,6 @@ extern void smp_imb(void); #endif -/* We need to flush the userspace icache after setting breakpoints in - ptrace. I don't think it's needed in do_swap_page, or do_no_page, - but I don't know how to get rid of it either. - - Instead of indiscriminately using imb, take advantage of the fact - that icache entries are tagged with the ASN and load a new mm context. */ -/* ??? Ought to use this in arch/alpha/kernel/signal.c too. */ - -#ifndef CONFIG_SMP -static inline void -flush_icache_page(struct vm_area_struct *vma, struct page *page) -{ - if (vma->vm_flags & VM_EXEC) { - struct mm_struct *mm = vma->vm_mm; - mm->context = 0; - if (current->active_mm == mm) - __load_new_mm_context(mm); - } -} -#else -extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); -#endif - /* * Use a few helper functions to hide the ugly broken ASN @@ -83,8 +60,38 @@ static inline void flush_tlb_other(struct mm_struct *mm) { - mm->context = 0; + long * mmc = &mm->context[smp_processor_id()]; + /* + * Check it's not zero first to avoid cacheline ping pong when + * possible. + */ + if (*mmc) + *mmc = 0; +} + +/* We need to flush the userspace icache after setting breakpoints in + ptrace. I don't think it's needed in do_swap_page, or do_no_page, + but I don't know how to get rid of it either. + + Instead of indiscriminately using imb, take advantage of the fact + that icache entries are tagged with the ASN and load a new mm context. */ +/* ??? Ought to use this in arch/alpha/kernel/signal.c too. */ + +#ifndef CONFIG_SMP +static inline void +flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + if (vma->vm_flags & VM_EXEC) { + struct mm_struct *mm = vma->vm_mm; + if (current->active_mm == mm) + __load_new_mm_context(mm); + else + mm->context[smp_processor_id()] = 0; + } } +#else +extern void flush_icache_page(struct vm_area_struct *vma, struct page *page); +#endif /* * Flush just one page in the current TLB set. @@ -140,7 +147,7 @@ */ static inline void flush_tlb(void) { - flush_tlb_current(current->mm); + flush_tlb_current(current->active_mm); } /* @@ -170,10 +177,10 @@ */ static inline void flush_tlb_mm(struct mm_struct *mm) { - if (mm != current->mm) - flush_tlb_other(mm); - else + if (mm == current->active_mm) flush_tlb_current(mm); + else + flush_tlb_other(mm); } /* @@ -189,10 +196,10 @@ { struct mm_struct * mm = vma->vm_mm; - if (mm != current->mm) - flush_tlb_other(mm); - else + if (mm == current->active_mm) flush_tlb_current_page(mm, vma, addr); + else + flush_tlb_other(mm); } /* diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/include/asm-alpha/smp.h 2.4.0-test12-pre2-alpha/include/asm-alpha/smp.h --- 2.4.0-test12-pre2/include/asm-alpha/smp.h Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/include/asm-alpha/smp.h Fri Dec 1 14:55:59 2000 @@ -26,6 +26,8 @@ struct cpuinfo_alpha { unsigned long loops_per_sec; unsigned long last_asn; + int need_new_asn; + int asn_lock; unsigned long *pgd_cache; unsigned long *pte_cache; unsigned long pgtable_cache_sz; diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/include/asm-alpha/system.h 2.4.0-test12-pre2-alpha/include/asm-alpha/system.h --- 2.4.0-test12-pre2/include/asm-alpha/system.h Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/include/asm-alpha/system.h Fri Dec 1 14:55:59 2000 @@ -125,6 +125,7 @@ current = (next); \ pcbb = virt_to_phys(¤t->thread); \ (last) = alpha_switch_to(pcbb, (prev)); \ + check_mmu_context(); \ } while (0) extern struct task_struct* alpha_switch_to(unsigned long, struct task_struct*); diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/include/linux/sched.h 2.4.0-test12-pre2-alpha/include/linux/sched.h --- 2.4.0-test12-pre2/include/linux/sched.h Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/include/linux/sched.h Fri Dec 1 14:56:48 2000 @@ -205,7 +205,11 @@ int map_count; /* number of VMAs */ struct semaphore mmap_sem; spinlock_t page_table_lock; +#ifdef __alpha__ + unsigned long context[NR_CPUS]; +#else unsigned long context; +#endif unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; diff -ur -X /home/andrea/archive/kernel/dontdiff/dontdiff 2.4.0-test12-pre2/kernel/fork.c 2.4.0-test12-pre2-alpha/kernel/fork.c --- 2.4.0-test12-pre2/kernel/fork.c Fri Dec 1 14:57:04 2000 +++ 2.4.0-test12-pre2-alpha/kernel/fork.c Fri Dec 1 14:58:39 2000 @@ -133,7 +133,7 @@ mm->mmap_avl = NULL; mm->mmap_cache = NULL; mm->map_count = 0; - mm->context = 0; + /* mm->context[] is initialized in init_new_context() later */ mm->cpu_vm_mask = 0; mm->swap_cnt = 0; mm->swap_address = 0;