From 0510617b85758b6e66f3c602ceccea1826440470 Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Mon, 4 Aug 2008 19:44:34 +0200 Subject: [MIPS] Fix data bus error recovery With -ffunction-section the entries in __dbe_table aren't no longer sorted, so the lookup of exception addresses in do_be() failed for some addresses. To avoid this we now sort __dbe_table. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle --- arch/mips/kernel/traps.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 426cced1e9d..1f579a8ea25 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -373,8 +373,8 @@ void __noreturn die(const char * str, const struct pt_regs * regs) do_exit(SIGSEGV); } -extern const struct exception_table_entry __start___dbe_table[]; -extern const struct exception_table_entry __stop___dbe_table[]; +extern struct exception_table_entry __start___dbe_table[]; +extern struct exception_table_entry __stop___dbe_table[]; __asm__( " .section __dbe_table, \"a\"\n" @@ -1682,4 +1682,6 @@ void __init trap_init(void) flush_icache_range(ebase, ebase + 0x400); flush_tlb_handlers(); + + sort_extable(__start___dbe_table, __stop___dbe_table); } -- cgit v1.2.3-70-g09d2 From e0cee3eea7875800451739ae38f99edcf11c133d Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Mon, 4 Aug 2008 20:53:57 +0200 Subject: [MIPS] Fix WARNING: at kernel/smp.c:290 trap_init issues flush_icache_range(), which uses ipi functions to get icache flushing done on all cpus. But this is done before interrupts are enabled and caused WARN_ON messages. This changeset introduces a new local_flush_icache_range() and uses it before interrupts (and additional CPUs) are enabled to avoid this problem. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle --- arch/mips/kernel/traps.c | 12 +++++++----- arch/mips/mm/c-r3k.c | 1 + arch/mips/mm/c-r4k.c | 18 ++++++++++++------ arch/mips/mm/c-tx39.c | 1 + arch/mips/mm/cache.c | 1 + arch/mips/mm/tlbex.c | 6 +++--- include/asm-mips/cacheflush.h | 1 + 7 files changed, 26 insertions(+), 14 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 1f579a8ea25..6bee29097a5 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -1200,7 +1200,7 @@ void *set_except_vector(int n, void *addr) if (n == 0 && cpu_has_divec) { *(u32 *)(ebase + 0x200) = 0x08000000 | (0x03ffffff & (handler >> 2)); - flush_icache_range(ebase + 0x200, ebase + 0x204); + local_flush_icache_range(ebase + 0x200, ebase + 0x204); } return (void *)old_handler; } @@ -1283,7 +1283,8 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs) *w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff); w = (u32 *)(b + ori_offset); *w = (*w & 0xffff0000) | ((u32)handler & 0xffff); - flush_icache_range((unsigned long)b, (unsigned long)(b+handler_len)); + local_flush_icache_range((unsigned long)b, + (unsigned long)(b+handler_len)); } else { /* @@ -1295,7 +1296,8 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs) w = (u32 *)b; *w++ = 0x08000000 | (((u32)handler >> 2) & 0x03fffff); /* j handler */ *w = 0; - flush_icache_range((unsigned long)b, (unsigned long)(b+8)); + local_flush_icache_range((unsigned long)b, + (unsigned long)(b+8)); } return (void *)old_handler; @@ -1515,7 +1517,7 @@ void __cpuinit per_cpu_trap_init(void) void __init set_handler(unsigned long offset, void *addr, unsigned long size) { memcpy((void *)(ebase + offset), addr, size); - flush_icache_range(ebase + offset, ebase + offset + size); + local_flush_icache_range(ebase + offset, ebase + offset + size); } static char panic_null_cerr[] __cpuinitdata = @@ -1680,7 +1682,7 @@ void __init trap_init(void) signal32_init(); #endif - flush_icache_range(ebase, ebase + 0x400); + local_flush_icache_range(ebase, ebase + 0x400); flush_tlb_handlers(); sort_extable(__start___dbe_table, __stop___dbe_table); diff --git a/arch/mips/mm/c-r3k.c b/arch/mips/mm/c-r3k.c index 27a5b466c85..5500c20c79a 100644 --- a/arch/mips/mm/c-r3k.c +++ b/arch/mips/mm/c-r3k.c @@ -320,6 +320,7 @@ void __cpuinit r3k_cache_init(void) flush_cache_range = r3k_flush_cache_range; flush_cache_page = r3k_flush_cache_page; flush_icache_range = r3k_flush_icache_range; + local_flush_icache_range = r3k_flush_icache_range; flush_cache_sigtramp = r3k_flush_cache_sigtramp; local_flush_data_cache_page = local_r3k_flush_data_cache_page; diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 71df3390c07..6e99665ae86 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -543,12 +543,8 @@ struct flush_icache_range_args { unsigned long end; }; -static inline void local_r4k_flush_icache_range(void *args) +static inline void local_r4k_flush_icache_range(unsigned long start, unsigned long end) { - struct flush_icache_range_args *fir_args = args; - unsigned long start = fir_args->start; - unsigned long end = fir_args->end; - if (!cpu_has_ic_fills_f_dc) { if (end - start >= dcache_size) { r4k_blast_dcache(); @@ -564,6 +560,15 @@ static inline void local_r4k_flush_icache_range(void *args) protected_blast_icache_range(start, end); } +static inline void local_r4k_flush_icache_range_ipi(void *args) +{ + struct flush_icache_range_args *fir_args = args; + unsigned long start = fir_args->start; + unsigned long end = fir_args->end; + + local_r4k_flush_icache_range(start, end); +} + static void r4k_flush_icache_range(unsigned long start, unsigned long end) { struct flush_icache_range_args args; @@ -571,7 +576,7 @@ static void r4k_flush_icache_range(unsigned long start, unsigned long end) args.start = start; args.end = end; - r4k_on_each_cpu(local_r4k_flush_icache_range, &args, 1); + r4k_on_each_cpu(local_r4k_flush_icache_range_ipi, &args, 1); instruction_hazard(); } @@ -1375,6 +1380,7 @@ void __cpuinit r4k_cache_init(void) local_flush_data_cache_page = local_r4k_flush_data_cache_page; flush_data_cache_page = r4k_flush_data_cache_page; flush_icache_range = r4k_flush_icache_range; + local_flush_icache_range = local_r4k_flush_icache_range; #if defined(CONFIG_DMA_NONCOHERENT) if (coherentio) { diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c index a9f7f1f5e9b..39c81820ce8 100644 --- a/arch/mips/mm/c-tx39.c +++ b/arch/mips/mm/c-tx39.c @@ -362,6 +362,7 @@ void __cpuinit tx39_cache_init(void) flush_cache_range = (void *) tx39h_flush_icache_all; flush_cache_page = (void *) tx39h_flush_icache_all; flush_icache_range = (void *) tx39h_flush_icache_all; + local_flush_icache_range = (void *) tx39h_flush_icache_all; flush_cache_sigtramp = (void *) tx39h_flush_icache_all; local_flush_data_cache_page = (void *) tx39h_flush_icache_all; diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index 034e8506f6e..1eb7c71e3d6 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -29,6 +29,7 @@ void (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start, void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); void (*flush_icache_range)(unsigned long start, unsigned long end); +void (*local_flush_icache_range)(unsigned long start, unsigned long end); void (*__flush_cache_vmap)(void); void (*__flush_cache_vunmap)(void); diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 76da73a5ab3..979cf919728 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -1273,10 +1273,10 @@ void __cpuinit build_tlb_refill_handler(void) void __cpuinit flush_tlb_handlers(void) { - flush_icache_range((unsigned long)handle_tlbl, + local_flush_icache_range((unsigned long)handle_tlbl, (unsigned long)handle_tlbl + sizeof(handle_tlbl)); - flush_icache_range((unsigned long)handle_tlbs, + local_flush_icache_range((unsigned long)handle_tlbs, (unsigned long)handle_tlbs + sizeof(handle_tlbs)); - flush_icache_range((unsigned long)handle_tlbm, + local_flush_icache_range((unsigned long)handle_tlbm, (unsigned long)handle_tlbm + sizeof(handle_tlbm)); } diff --git a/include/asm-mips/cacheflush.h b/include/asm-mips/cacheflush.h index d5c0f2fda51..03b1d69b142 100644 --- a/include/asm-mips/cacheflush.h +++ b/include/asm-mips/cacheflush.h @@ -63,6 +63,7 @@ static inline void flush_icache_page(struct vm_area_struct *vma, } extern void (*flush_icache_range)(unsigned long start, unsigned long end); +extern void (*local_flush_icache_range)(unsigned long start, unsigned long end); extern void (*__flush_cache_vmap)(void); -- cgit v1.2.3-70-g09d2 From 0011036beeffeada15acd1936d67988de21ca65e Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 26 Aug 2008 22:34:57 +0900 Subject: [MIPS] Probe initrd header only if explicitly specified Currently init_initrd() probes initrd header at the last page of kernel image, but it is valid only if addinitrd was used. If addinitrd was not used, the area contains garbage so probing there might misdetect initrd header (magic number is not strictly robust). This patch introduces CONFIG_PROBE_INITRD_HEADER to explicitly enable this probing. Signed-off-by: Atsushi Nemoto Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 9 +++++++++ arch/mips/kernel/setup.c | 33 ++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4da736e2533..49896a2a1d7 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1886,6 +1886,15 @@ config STACKTRACE_SUPPORT source "init/Kconfig" +config PROBE_INITRD_HEADER + bool "Probe initrd header created by addinitrd" + depends on BLK_DEV_INITRD + help + Probe initrd header at the last page of kernel image. + Say Y here if you are using arch/mips/boot/addinitrd.c to + add initrd or initramfs image to the kernel image. + Otherwise, say N. + menu "Bus options (PCI, PCMCIA, EISA, ISA, TC)" config HW_HAS_EISA diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 2aae76bce29..16f8edfe5cd 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -160,30 +160,33 @@ early_param("rd_size", rd_size_early); static unsigned long __init init_initrd(void) { unsigned long end; - u32 *initrd_header; /* * Board specific code or command line parser should have * already set up initrd_start and initrd_end. In these cases * perfom sanity checks and use them if all looks good. */ - if (initrd_start && initrd_end > initrd_start) - goto sanitize; + if (!initrd_start || initrd_end <= initrd_start) { +#ifdef CONFIG_PROBE_INITRD_HEADER + u32 *initrd_header; - /* - * See if initrd has been added to the kernel image by - * arch/mips/boot/addinitrd.c. In that case a header is - * prepended to initrd and is made up by 8 bytes. The fisrt - * word is a magic number and the second one is the size of - * initrd. Initrd start must be page aligned in any cases. - */ - initrd_header = __va(PAGE_ALIGN(__pa_symbol(&_end) + 8)) - 8; - if (initrd_header[0] != 0x494E5244) + /* + * See if initrd has been added to the kernel image by + * arch/mips/boot/addinitrd.c. In that case a header is + * prepended to initrd and is made up by 8 bytes. The first + * word is a magic number and the second one is the size of + * initrd. Initrd start must be page aligned in any cases. + */ + initrd_header = __va(PAGE_ALIGN(__pa_symbol(&_end) + 8)) - 8; + if (initrd_header[0] != 0x494E5244) + goto disable; + initrd_start = (unsigned long)(initrd_header + 2); + initrd_end = initrd_start + initrd_header[1]; +#else goto disable; - initrd_start = (unsigned long)(initrd_header + 2); - initrd_end = initrd_start + initrd_header[1]; +#endif + } -sanitize: if (initrd_start & ~PAGE_MASK) { pr_err("initrd start must be page aligned\n"); goto disable; -- cgit v1.2.3-70-g09d2 From e545a6140b698b2494daf0b32107bdcc5e901390 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Sun, 7 Sep 2008 16:57:22 +0200 Subject: kernel/cpu.c: create a CPU_STARTING cpu_chain notifier Right now, there is no notifier that is called on a new cpu, before the new cpu begins processing interrupts/softirqs. Various kernel function would need that notification, e.g. kvm works around by calling smp_call_function_single(), rcu polls cpu_online_map. The patch adds a CPU_STARTING notification. It also adds a helper function that sends the message to all cpu_chain handlers. Tested on x86-64. All other archs are untested. Especially on sparc, I'm not sure if I got it right. Signed-off-by: Manfred Spraul Signed-off-by: Ingo Molnar --- arch/alpha/kernel/smp.c | 3 +++ arch/arm/kernel/smp.c | 1 + arch/cris/arch-v32/kernel/smp.c | 1 + arch/ia64/kernel/smpboot.c | 1 + arch/m32r/kernel/smpboot.c | 2 ++ arch/mips/kernel/smp.c | 2 ++ arch/powerpc/kernel/smp.c | 1 + arch/s390/kernel/smp.c | 2 ++ arch/sh/kernel/smp.c | 2 ++ arch/sparc/kernel/sun4d_smp.c | 1 + arch/sparc/kernel/sun4m_smp.c | 2 ++ arch/um/kernel/smp.c | 1 + arch/x86/kernel/smpboot.c | 1 + arch/x86/mach-voyager/voyager_smp.c | 2 ++ include/linux/cpu.h | 1 + include/linux/notifier.h | 10 +++++++++- kernel/cpu.c | 19 +++++++++++++++++++ 17 files changed, 51 insertions(+), 1 deletion(-) (limited to 'arch/mips/kernel') diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 83df541650f..06b6fdab639 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -149,6 +149,9 @@ smp_callin(void) atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; + /* inform the notifiers about the new cpu */ + notify_cpu_starting(cpuid); + /* Must have completely accurate bogos. */ local_irq_enable(); diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index e9842f6767f..e42a749a56d 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -277,6 +277,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) /* * Enable local interrupts. */ + notify_cpu_starting(cpu); local_irq_enable(); local_fiq_enable(); diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c index 952a24b2f5a..52e16c6436f 100644 --- a/arch/cris/arch-v32/kernel/smp.c +++ b/arch/cris/arch-v32/kernel/smp.c @@ -178,6 +178,7 @@ void __init smp_callin(void) unmask_irq(IPI_INTR_VECT); unmask_irq(TIMER0_INTR_VECT); preempt_disable(); + notify_cpu_starting(cpu); local_irq_enable(); cpu_set(cpu, cpu_online_map); diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index bcea81e432f..333b58f218d 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -401,6 +401,7 @@ smp_callin (void) spin_lock(&vector_lock); /* Setup the per cpu irq handling data structures */ __setup_vector_irq(cpuid); + notify_cpu_starting(cpuid); cpu_set(cpuid, cpu_online_map); per_cpu(cpu_state, cpuid) = CPU_ONLINE; spin_unlock(&vector_lock); diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c index 2c03ac1d005..fc2994811f1 100644 --- a/arch/m32r/kernel/smpboot.c +++ b/arch/m32r/kernel/smpboot.c @@ -498,6 +498,8 @@ static void __init smp_online(void) { int cpu_id = smp_processor_id(); + notify_cpu_starting(cpu_id); + local_irq_enable(); /* Get our bogomips. */ diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 4410f172b8a..7b59cfb7e60 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -121,6 +121,8 @@ asmlinkage __cpuinit void start_secondary(void) cpu = smp_processor_id(); cpu_data[cpu].udelay_val = loops_per_jiffy; + notify_cpu_starting(cpu); + mp_ops->smp_finish(); set_cpu_sibling_map(cpu); diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 5337ca7bb64..c27b10a1bd7 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -453,6 +453,7 @@ int __devinit start_secondary(void *unused) secondary_cpu_time_init(); ipi_call_lock(); + notify_cpu_starting(cpu); cpu_set(cpu, cpu_online_map); /* Update sibling maps */ base = cpu_first_thread_in_core(cpu); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 00b9b4dec5e..9e8b1f9b8f4 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -585,6 +585,8 @@ int __cpuinit start_secondary(void *cpuvoid) /* Enable pfault pseudo page faults on this cpu. */ pfault_init(); + /* call cpu notifiers */ + notify_cpu_starting(smp_processor_id()); /* Mark this cpu as online */ spin_lock(&call_lock); cpu_set(smp_processor_id(), cpu_online_map); diff --git a/arch/sh/kernel/smp.c b/arch/sh/kernel/smp.c index 60c50841143..001778f9ada 100644 --- a/arch/sh/kernel/smp.c +++ b/arch/sh/kernel/smp.c @@ -82,6 +82,8 @@ asmlinkage void __cpuinit start_secondary(void) preempt_disable(); + notify_cpu_starting(smp_processor_id()); + local_irq_enable(); calibrate_delay(); diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 69596402a50..446767e8f56 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -88,6 +88,7 @@ void __init smp4d_callin(void) local_flush_cache_all(); local_flush_tlb_all(); + notify_cpu_starting(cpuid); /* * Unblock the master CPU _only_ when the scheduler state * of all secondary CPUs will be up-to-date, so after diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index a14a76ac7f3..9964890dc1d 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -71,6 +71,8 @@ void __cpuinit smp4m_callin(void) local_flush_cache_all(); local_flush_tlb_all(); + notify_cpu_starting(cpuid); + /* Get our local ticker going. */ smp_setup_percpu_timer(); diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c index be2d50c3aa9..04577214284 100644 --- a/arch/um/kernel/smp.c +++ b/arch/um/kernel/smp.c @@ -85,6 +85,7 @@ static int idle_proc(void *cpup) while (!cpu_isset(cpu, smp_commenced_mask)) cpu_relax(); + notify_cpu_starting(cpu); cpu_set(cpu, cpu_online_map); default_idle(); return 0; diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 7985c5b3f91..0b8261c3cac 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -257,6 +257,7 @@ static void __cpuinit smp_callin(void) end_local_APIC_setup(); map_cpu_to_logical_apicid(); + notify_cpu_starting(cpuid); /* * Get our bogomips. * diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index ee0fba09215..199a5f4a873 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c @@ -448,6 +448,8 @@ static void __init start_secondary(void *unused) VDEBUG(("VOYAGER SMP: CPU%d, stack at about %p\n", cpuid, &cpuid)); + notify_cpu_starting(cpuid); + /* enable interrupts */ local_irq_enable(); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index d7faf880849..c2747ac2ae4 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -69,6 +69,7 @@ static inline void unregister_cpu_notifier(struct notifier_block *nb) #endif int cpu_up(unsigned int cpu); +void notify_cpu_starting(unsigned int cpu); extern void cpu_hotplug_init(void); extern void cpu_maps_update_begin(void); extern void cpu_maps_update_done(void); diff --git a/include/linux/notifier.h b/include/linux/notifier.h index da2698b0fdd..b86fa2ffca0 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -213,9 +213,16 @@ static inline int notifier_to_errno(int ret) #define CPU_DOWN_FAILED 0x0006 /* CPU (unsigned)v NOT going down */ #define CPU_DEAD 0x0007 /* CPU (unsigned)v dead */ #define CPU_DYING 0x0008 /* CPU (unsigned)v not running any task, - * not handling interrupts, soon dead */ + * not handling interrupts, soon dead. + * Called on the dying cpu, interrupts + * are already disabled. Must not + * sleep, must not fail */ #define CPU_POST_DEAD 0x0009 /* CPU (unsigned)v dead, cpu_hotplug * lock is dropped */ +#define CPU_STARTING 0x000A /* CPU (unsigned)v soon running. + * Called on the new cpu, just before + * enabling interrupts. Must not sleep, + * must not fail */ /* Used for CPU hotplug events occuring while tasks are frozen due to a suspend * operation in progress @@ -229,6 +236,7 @@ static inline int notifier_to_errno(int ret) #define CPU_DOWN_FAILED_FROZEN (CPU_DOWN_FAILED | CPU_TASKS_FROZEN) #define CPU_DEAD_FROZEN (CPU_DEAD | CPU_TASKS_FROZEN) #define CPU_DYING_FROZEN (CPU_DYING | CPU_TASKS_FROZEN) +#define CPU_STARTING_FROZEN (CPU_STARTING | CPU_TASKS_FROZEN) /* Hibernation and suspend events */ #define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */ diff --git a/kernel/cpu.c b/kernel/cpu.c index f17e9854c24..dc45f2459ef 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -453,6 +453,25 @@ out: } #endif /* CONFIG_PM_SLEEP_SMP */ +/** + * notify_cpu_starting(cpu) - call the CPU_STARTING notifiers + * @cpu: cpu that just started + * + * This function calls the cpu_chain notifiers with CPU_STARTING. + * It must be called by the arch code on the new cpu, before the new cpu + * enables interrupts and before the "boot" cpu returns from __cpu_up(). + */ +void notify_cpu_starting(unsigned int cpu) +{ + unsigned long val = CPU_STARTING; + +#ifdef CONFIG_PM_SLEEP_SMP + if (cpu_isset(cpu, frozen_cpus)) + val = CPU_STARTING_FROZEN; +#endif /* CONFIG_PM_SLEEP_SMP */ + raw_notifier_call_chain(&cpu_chain, val, (void *)(long)cpu); +} + #endif /* CONFIG_SMP */ /* -- cgit v1.2.3-70-g09d2 From 6657fe0a0281e06ff46eb42bc18ecb68d8b14b27 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Tue, 9 Sep 2008 15:19:10 +0200 Subject: [MIPS] SMTC: Clear TIF_FPUBOUND on clone / fork. Signed-off-by: Ralf Baechle --- arch/mips/kernel/process.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index b16facd9ea8..ce7684335a4 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -148,6 +148,8 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, clear_tsk_thread_flag(p, TIF_USEDFPU); #ifdef CONFIG_MIPS_MT_FPAFF + clear_tsk_thread_flag(p, TIF_FPUBOUND); + /* * FPU affinity support is cleaner if we track the * user-visible CPU affinity from the very beginning. -- cgit v1.2.3-70-g09d2 From c65a5480ff29198c241f11d39d9e34ab22c72c69 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Mon, 12 Nov 2007 02:05:18 +0900 Subject: [MIPS] Fix potential latency problem due to non-atomic cpu_wait. If an interrupt happened between checking of NEED_RESCHED and WAIT instruction, adjust EPC to restart from checking of NEED_RESCHED. Signed-off-by: Atsushi Nemoto Signed-off-by: Ralf Baechle --- arch/mips/kernel/cpu-probe.c | 16 ++-------------- arch/mips/kernel/genex.S | 37 +++++++++++++++++++++++++++++++++++++ arch/mips/kernel/traps.c | 22 ++++++++++++++++------ 3 files changed, 55 insertions(+), 20 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 335a6ae3d59..11c92dc5379 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -45,18 +45,7 @@ static void r39xx_wait(void) local_irq_enable(); } -/* - * There is a race when WAIT instruction executed with interrupt - * enabled. - * But it is implementation-dependent wheter the pipelie restarts when - * a non-enabled interrupt is requested. - */ -static void r4k_wait(void) -{ - __asm__(" .set mips3 \n" - " wait \n" - " .set mips0 \n"); -} +extern void r4k_wait(void); /* * This variant is preferable as it allows testing need_resched and going to @@ -128,7 +117,7 @@ static int __init wait_disable(char *s) __setup("nowait", wait_disable); -static inline void check_wait(void) +void __init check_wait(void) { struct cpuinfo_mips *c = ¤t_cpu_data; @@ -242,7 +231,6 @@ static inline void check_errata(void) void __init check_bugs32(void) { - check_wait(); check_errata(); } diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index c6ada98ee04..f886dd7f708 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -20,6 +20,7 @@ #include #include #include +#include #define PANIC_PIC(msg) \ .set push; \ @@ -126,7 +127,42 @@ handle_vcei: __FINIT + .align 5 /* 32 byte rollback region */ +LEAF(r4k_wait) + .set push + .set noreorder + /* start of rollback region */ + LONG_L t0, TI_FLAGS($28) + nop + andi t0, _TIF_NEED_RESCHED + bnez t0, 1f + nop + nop + nop + .set mips3 + wait + /* end of rollback region (the region size must be power of two) */ + .set pop +1: + jr ra + END(r4k_wait) + + .macro BUILD_ROLLBACK_PROLOGUE handler + FEXPORT(rollback_\handler) + .set push + .set noat + MFC0 k0, CP0_EPC + PTR_LA k1, r4k_wait + ori k0, 0x1f /* 32 byte rollback region */ + xori k0, 0x1f + bne k0, k1, 9f + MTC0 k0, CP0_EPC +9: + .set pop + .endm + .align 5 +BUILD_ROLLBACK_PROLOGUE handle_int NESTED(handle_int, PT_SIZE, sp) #ifdef CONFIG_TRACE_IRQFLAGS /* @@ -201,6 +237,7 @@ NESTED(except_vec_ejtag_debug, 0, sp) * This prototype is copied to ebase + n*IntCtl.VS and patched * to invoke the handler */ +BUILD_ROLLBACK_PROLOGUE except_vec_vi NESTED(except_vec_vi, 0, sp) SAVE_SOME SAVE_AT diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 6bee29097a5..5fd0cd020af 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -46,6 +46,9 @@ #include #include +extern void check_wait(void); +extern asmlinkage void r4k_wait(void); +extern asmlinkage void rollback_handle_int(void); extern asmlinkage void handle_int(void); extern asmlinkage void handle_tlbm(void); extern asmlinkage void handle_tlbl(void); @@ -1251,6 +1254,9 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs) extern char except_vec_vi, except_vec_vi_lui; extern char except_vec_vi_ori, except_vec_vi_end; + extern char rollback_except_vec_vi; + char *vec_start = (cpu_wait == r4k_wait) ? + &rollback_except_vec_vi : &except_vec_vi; #ifdef CONFIG_MIPS_MT_SMTC /* * We need to provide the SMTC vectored interrupt handler @@ -1258,11 +1264,11 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs) * Status.IM bit to be masked before going there. */ extern char except_vec_vi_mori; - const int mori_offset = &except_vec_vi_mori - &except_vec_vi; + const int mori_offset = &except_vec_vi_mori - vec_start; #endif /* CONFIG_MIPS_MT_SMTC */ - const int handler_len = &except_vec_vi_end - &except_vec_vi; - const int lui_offset = &except_vec_vi_lui - &except_vec_vi; - const int ori_offset = &except_vec_vi_ori - &except_vec_vi; + const int handler_len = &except_vec_vi_end - vec_start; + const int lui_offset = &except_vec_vi_lui - vec_start; + const int ori_offset = &except_vec_vi_ori - vec_start; if (handler_len > VECTORSPACING) { /* @@ -1272,7 +1278,7 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs) panic("VECTORSPACING too small"); } - memcpy(b, &except_vec_vi, handler_len); + memcpy(b, vec_start, handler_len); #ifdef CONFIG_MIPS_MT_SMTC BUG_ON(n > 7); /* Vector index %d exceeds SMTC maximum. */ @@ -1554,6 +1560,10 @@ void __init trap_init(void) extern char except_vec3_generic, except_vec3_r4000; extern char except_vec4; unsigned long i; + int rollback; + + check_wait(); + rollback = (cpu_wait == r4k_wait); #if defined(CONFIG_KGDB) if (kgdb_early_setup) @@ -1618,7 +1628,7 @@ void __init trap_init(void) if (board_be_init) board_be_init(); - set_except_vector(0, handle_int); + set_except_vector(0, rollback ? rollback_handle_int : handle_int); set_except_vector(1, handle_tlbm); set_except_vector(2, handle_tlbl); set_except_vector(3, handle_tlbs); -- cgit v1.2.3-70-g09d2 From 6b3766a263a62fc82ca0457faa73dc34b98ceb87 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 5 Aug 2008 23:45:14 +0900 Subject: [MIPS] vmlinux.lds.S: handle .text.* The -ffunction-sections puts each text in .text.function_name section. Without this patch, most functions are placed outside _text..._etext area and it breaks show_stacktrace(), etc. Signed-off-by: Atsushi Nemoto Signed-off-by: Ralf Baechle --- arch/mips/kernel/vmlinux.lds.S | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S index b5470ceb418..afb119f3568 100644 --- a/arch/mips/kernel/vmlinux.lds.S +++ b/arch/mips/kernel/vmlinux.lds.S @@ -36,6 +36,7 @@ SECTIONS SCHED_TEXT LOCK_TEXT KPROBES_TEXT + *(.text.*) *(.fixup) *(.gnu.warning) } :text = 0 -- cgit v1.2.3-70-g09d2 From d7161a65341556bacb5e6654e133803f46f51063 Mon Sep 17 00:00:00 2001 From: Jason Wessel Date: Fri, 26 Sep 2008 10:36:41 -0500 Subject: kgdb, x86, arm, mips, powerpc: ignore user space single stepping On the x86 arch, user space single step exceptions should be ignored if they occur in the kernel space, such as ptrace stepping through a system call. First check if it is kgdb that is executing a single step, then ensure it is not an accidental traversal into the user space, while in kgdb, any other time the TIF_SINGLESTEP is set, kgdb should ignore the exception. On x86, arm, mips and powerpc, the kgdb_contthread usage was inconsistent with the way single stepping is implemented in the kgdb core. The arch specific stub should always set the kgdb_cpu_doing_single_step correctly if it is single stepping. This allows kgdb to correctly process an instruction steps if ptrace happens to be requesting an instruction step over a system call. Signed-off-by: Jason Wessel --- arch/arm/kernel/kgdb.c | 2 -- arch/mips/kernel/kgdb.c | 3 +-- arch/powerpc/kernel/kgdb.c | 5 ++--- arch/x86/kernel/kgdb.c | 18 +++++++++++------- kernel/kgdb.c | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c index aaffaecffcd..ba8ccfede96 100644 --- a/arch/arm/kernel/kgdb.c +++ b/arch/arm/kernel/kgdb.c @@ -111,8 +111,6 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, case 'D': case 'k': case 'c': - kgdb_contthread = NULL; - /* * Try to read optional parameter, pc unchanged if no parm. * If this was a compiled breakpoint, we need to move diff --git a/arch/mips/kernel/kgdb.c b/arch/mips/kernel/kgdb.c index 8f6d58ede33..6e152c80cd4 100644 --- a/arch/mips/kernel/kgdb.c +++ b/arch/mips/kernel/kgdb.c @@ -236,8 +236,7 @@ int kgdb_arch_handle_exception(int vector, int signo, int err_code, atomic_set(&kgdb_cpu_doing_single_step, -1); if (remcom_in_buffer[0] == 's') - if (kgdb_contthread) - atomic_set(&kgdb_cpu_doing_single_step, cpu); + atomic_set(&kgdb_cpu_doing_single_step, cpu); return 0; } diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index b4fdf2f2743..fe8f71dd0b3 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -347,9 +347,8 @@ int kgdb_arch_handle_exception(int vector, int signo, int err_code, linux_regs->msr |= MSR_SE; #endif kgdb_single_step = 1; - if (kgdb_contthread) - atomic_set(&kgdb_cpu_doing_single_step, - raw_smp_processor_id()); + atomic_set(&kgdb_cpu_doing_single_step, + raw_smp_processor_id()); } return 0; } diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index f47f0eb886b..00f7896c9a1 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -378,10 +378,8 @@ int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, if (remcomInBuffer[0] == 's') { linux_regs->flags |= X86_EFLAGS_TF; kgdb_single_step = 1; - if (kgdb_contthread) { - atomic_set(&kgdb_cpu_doing_single_step, - raw_smp_processor_id()); - } + atomic_set(&kgdb_cpu_doing_single_step, + raw_smp_processor_id()); } get_debugreg(dr6, 6); @@ -466,9 +464,15 @@ static int __kgdb_notify(struct die_args *args, unsigned long cmd) case DIE_DEBUG: if (atomic_read(&kgdb_cpu_doing_single_step) == - raw_smp_processor_id() && - user_mode(regs)) - return single_step_cont(regs, args); + raw_smp_processor_id()) { + if (user_mode(regs)) + return single_step_cont(regs, args); + break; + } else if (test_thread_flag(TIF_SINGLESTEP)) + /* This means a user thread is single stepping + * a system call which should be ignored + */ + return NOTIFY_DONE; /* fall through */ default: if (user_mode(regs)) diff --git a/kernel/kgdb.c b/kernel/kgdb.c index 949806ab67d..25d955dbb98 100644 --- a/kernel/kgdb.c +++ b/kernel/kgdb.c @@ -1462,7 +1462,7 @@ acquirelock: * Get the passive CPU lock which will hold all the non-primary * CPU in a spin state while the debugger is active */ - if (!kgdb_single_step || !kgdb_contthread) { + if (!kgdb_single_step) { for (i = 0; i < NR_CPUS; i++) atomic_set(&passive_cpu_wait[i], 1); } @@ -1475,7 +1475,7 @@ acquirelock: #ifdef CONFIG_SMP /* Signal the other CPUs to enter kgdb_wait() */ - if ((!kgdb_single_step || !kgdb_contthread) && kgdb_do_roundup) + if ((!kgdb_single_step) && kgdb_do_roundup) kgdb_roundup_cpus(flags); #endif @@ -1494,7 +1494,7 @@ acquirelock: kgdb_post_primary_code(ks->linux_regs, ks->ex_vector, ks->err_code); kgdb_deactivate_sw_breakpoints(); kgdb_single_step = 0; - kgdb_contthread = NULL; + kgdb_contthread = current; exception_level = 0; /* Talk to debugger with gdbserial protocol */ @@ -1508,7 +1508,7 @@ acquirelock: kgdb_info[ks->cpu].task = NULL; atomic_set(&cpu_in_kgdb[ks->cpu], 0); - if (!kgdb_single_step || !kgdb_contthread) { + if (!kgdb_single_step) { for (i = NR_CPUS-1; i >= 0; i--) atomic_set(&passive_cpu_wait[i], 0); /* -- cgit v1.2.3-70-g09d2 From b7e4226e4f427b59dc8e9c45a2a1a1ed1353a140 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 1 Oct 2008 21:52:41 +0100 Subject: [MIPS] Build fix: Fix irq flags type Though from a hardware perspective it would be sensible to use only a 32-bit unsigned int type Linux defines interrupt flags to be stored in an unsigned long and nothing else. Signed-off-by: Ralf Baechle --- arch/mips/kernel/ptrace.c | 2 +- arch/mips/kernel/smtc.c | 6 +++--- include/asm-mips/mipsregs.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 35234b92b9a..96ffc9c6d19 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -238,7 +238,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case FPC_EIR: { /* implementation / version register */ unsigned int flags; #ifdef CONFIG_MIPS_MT_SMTC - unsigned int irqflags; + unsigned long irqflags; unsigned int mtflags; #endif /* CONFIG_MIPS_MT_SMTC */ diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index a516286532a..05f2708a902 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -556,7 +556,7 @@ void mipsmt_prepare_cpus(void) void __cpuinit smtc_boot_secondary(int cpu, struct task_struct *idle) { extern u32 kernelsp[NR_CPUS]; - long flags; + unsigned long flags; int mtflags; LOCK_MT_PRA(); @@ -753,7 +753,7 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) { int tcstatus; struct smtc_ipi *pipi; - long flags; + unsigned long flags; int mtflags; if (cpu == smp_processor_id()) { @@ -975,7 +975,7 @@ static irqreturn_t ipi_interrupt(int irq, void *dev_idm) struct smtc_ipi *pipi; unsigned long tcstatus; int sent; - long flags; + unsigned long flags; unsigned int mtflags; unsigned int vpflags; diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h index a46f8e258e6..979866000da 100644 --- a/include/asm-mips/mipsregs.h +++ b/include/asm-mips/mipsregs.h @@ -1462,7 +1462,7 @@ set_c0_##name(unsigned int set) \ { \ unsigned int res; \ unsigned int omt; \ - unsigned int flags; \ + unsigned long flags; \ \ local_irq_save(flags); \ omt = __dmt(); \ @@ -1480,7 +1480,7 @@ clear_c0_##name(unsigned int clear) \ { \ unsigned int res; \ unsigned int omt; \ - unsigned int flags; \ + unsigned long flags; \ \ local_irq_save(flags); \ omt = __dmt(); \ @@ -1498,7 +1498,7 @@ change_c0_##name(unsigned int change, unsigned int new) \ { \ unsigned int res; \ unsigned int omt; \ - unsigned int flags; \ + unsigned long flags; \ \ local_irq_save(flags); \ \ -- cgit v1.2.3-70-g09d2 From 9cc123631b6630948eb5059d218f44424888daa7 Mon Sep 17 00:00:00 2001 From: "Kevin D. Kissell" Date: Tue, 9 Sep 2008 21:33:36 +0200 Subject: [MIPS] SMTC: Fix holes in SMTC and FPU affinity support. Signed-off-by: Kevin D. Kissell Signed-off-by: Ralf Baechle --- arch/mips/kernel/mips-mt-fpaff.c | 2 +- arch/mips/kernel/process.c | 19 +++++++++---------- arch/mips/kernel/traps.c | 6 ++++-- 3 files changed, 14 insertions(+), 13 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/mips-mt-fpaff.c b/arch/mips/kernel/mips-mt-fpaff.c index df4d3f2f740..dc9eb72ed9d 100644 --- a/arch/mips/kernel/mips-mt-fpaff.c +++ b/arch/mips/kernel/mips-mt-fpaff.c @@ -159,7 +159,7 @@ __setup("fpaff=", fpaff_thresh); /* * FPU Use Factor empirically derived from experiments on 34K */ -#define FPUSEFACTOR 333 +#define FPUSEFACTOR 2000 static __init int mt_fp_affinity_init(void) { diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index ce7684335a4..22fc19bbe87 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -55,7 +55,7 @@ void __noreturn cpu_idle(void) while (1) { tick_nohz_stop_sched_tick(1); while (!need_resched()) { -#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG +#ifdef CONFIG_MIPS_MT_SMTC extern void smtc_idle_loop_hook(void); smtc_idle_loop_hook(); @@ -145,19 +145,18 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, */ p->thread.cp0_status = read_c0_status() & ~(ST0_CU2|ST0_CU1); childregs->cp0_status &= ~(ST0_CU2|ST0_CU1); + +#ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC restores TCStatus after Status, and the CU bits + * are aliased there. + */ + childregs->cp0_tcstatus &= ~(ST0_CU2|ST0_CU1); +#endif clear_tsk_thread_flag(p, TIF_USEDFPU); #ifdef CONFIG_MIPS_MT_FPAFF clear_tsk_thread_flag(p, TIF_FPUBOUND); - - /* - * FPU affinity support is cleaner if we track the - * user-visible CPU affinity from the very beginning. - * The generic cpus_allowed mask will already have - * been copied from the parent before copy_thread - * is invoked. - */ - p->thread.user_cpus_allowed = p->cpus_allowed; #endif /* CONFIG_MIPS_MT_FPAFF */ if (clone_flags & CLONE_SETTLS) diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 5fd0cd020af..b602ac6eb47 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -825,8 +825,10 @@ static void mt_ase_fp_affinity(void) if (cpus_intersects(current->cpus_allowed, mt_fpu_cpumask)) { cpumask_t tmask; - cpus_and(tmask, current->thread.user_cpus_allowed, - mt_fpu_cpumask); + current->thread.user_cpus_allowed + = current->cpus_allowed; + cpus_and(tmask, current->cpus_allowed, + mt_fpu_cpumask); set_cpus_allowed(current, tmask); set_thread_flag(TIF_FPUBOUND); } -- cgit v1.2.3-70-g09d2 From d2bb01b042a38219fbddaafc214c5beb96248d2f Mon Sep 17 00:00:00 2001 From: "Kevin D. Kissell" Date: Tue, 9 Sep 2008 21:35:01 +0200 Subject: [MIPS] SMTC: Close tiny holes in the SMTC IPI replay system. Signed-off-by: Kevin D. Kissell Signed-off-by: Ralf Baechle --- arch/mips/kernel/entry.S | 10 +++--- arch/mips/kernel/smtc.c | 2 +- include/asm-mips/stackframe.h | 72 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 67 insertions(+), 17 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S index e29598ae939..ffa331029e0 100644 --- a/arch/mips/kernel/entry.S +++ b/arch/mips/kernel/entry.S @@ -79,11 +79,6 @@ FEXPORT(syscall_exit) FEXPORT(restore_all) # restore full frame #ifdef CONFIG_MIPS_MT_SMTC -/* Detect and execute deferred IPI "interrupts" */ - LONG_L s0, TI_REGS($28) - LONG_S sp, TI_REGS($28) - jal deferred_smtc_ipi - LONG_S s0, TI_REGS($28) #ifdef CONFIG_MIPS_MT_SMTC_IM_BACKSTOP /* Re-arm any temporarily masked interrupts not explicitly "acked" */ mfc0 v0, CP0_TCSTATUS @@ -112,6 +107,11 @@ FEXPORT(restore_all) # restore full frame xor t0, t0, t3 mtc0 t0, CP0_TCCONTEXT #endif /* CONFIG_MIPS_MT_SMTC_IM_BACKSTOP */ +/* Detect and execute deferred IPI "interrupts" */ + LONG_L s0, TI_REGS($28) + LONG_S sp, TI_REGS($28) + jal deferred_smtc_ipi + LONG_S s0, TI_REGS($28) #endif /* CONFIG_MIPS_MT_SMTC */ .set noat RESTORE_TEMP diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index 05f2708a902..39b491b9ad8 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -70,7 +70,7 @@ static atomic_t ipi_timer_latch[NR_CPUS]; #define IPIBUF_PER_CPU 4 -static struct smtc_ipi_q IPIQ[NR_CPUS]; +struct smtc_ipi_q IPIQ[NR_CPUS]; static struct smtc_ipi_q freeIPIq; diff --git a/include/asm-mips/stackframe.h b/include/asm-mips/stackframe.h index 051e1af0bb9..4c37c4e5f72 100644 --- a/include/asm-mips/stackframe.h +++ b/include/asm-mips/stackframe.h @@ -297,14 +297,31 @@ #ifdef CONFIG_MIPS_MT_SMTC .set mips32r2 /* - * This may not really be necessary if ints are already - * inhibited here. + * We need to make sure the read-modify-write + * of Status below isn't perturbed by an interrupt + * or cross-TC access, so we need to do at least a DMT, + * protected by an interrupt-inhibit. But setting IXMT + * also creates a few-cycle window where an IPI could + * be queued and not be detected before potentially + * returning to a WAIT or user-mode loop. It must be + * replayed. + * + * We're in the middle of a context switch, and + * we can't dispatch it directly without trashing + * some registers, so we'll try to detect this unlikely + * case and program a software interrupt in the VPE, + * as would be done for a cross-VPE IPI. To accomodate + * the handling of that case, we're doing a DVPE instead + * of just a DMT here to protect against other threads. + * This is a lot of cruft to cover a tiny window. + * If you can find a better design, implement it! + * */ mfc0 v0, CP0_TCSTATUS ori v0, TCSTATUS_IXMT mtc0 v0, CP0_TCSTATUS _ehb - DMT 5 # dmt a1 + DVPE 5 # dvpe a1 jal mips_ihb #endif /* CONFIG_MIPS_MT_SMTC */ mfc0 a0, CP0_STATUS @@ -325,17 +342,50 @@ */ LONG_L v1, PT_TCSTATUS(sp) _ehb - mfc0 v0, CP0_TCSTATUS + mfc0 a0, CP0_TCSTATUS andi v1, TCSTATUS_IXMT - /* We know that TCStatua.IXMT should be set from above */ - xori v0, v0, TCSTATUS_IXMT - or v0, v0, v1 - mtc0 v0, CP0_TCSTATUS - _ehb - andi a1, a1, VPECONTROL_TE + bnez v1, 0f + +/* + * We'd like to detect any IPIs queued in the tiny window + * above and request an software interrupt to service them + * when we ERET. + * + * Computing the offset into the IPIQ array of the executing + * TC's IPI queue in-line would be tedious. We use part of + * the TCContext register to hold 16 bits of offset that we + * can add in-line to find the queue head. + */ + mfc0 v0, CP0_TCCONTEXT + la a2, IPIQ + srl v0, v0, 16 + addu a2, a2, v0 + LONG_L v0, 0(a2) + beqz v0, 0f +/* + * If we have a queue, provoke dispatch within the VPE by setting C_SW1 + */ + mfc0 v0, CP0_CAUSE + ori v0, v0, C_SW1 + mtc0 v0, CP0_CAUSE +0: + /* + * This test should really never branch but + * let's be prudent here. Having atomized + * the shared register modifications, we can + * now EVPE, and must do so before interrupts + * are potentially re-enabled. + */ + andi a1, a1, MVPCONTROL_EVP beqz a1, 1f - emt + evpe 1: + /* We know that TCStatua.IXMT should be set from above */ + xori a0, a0, TCSTATUS_IXMT + or a0, a0, v1 + mtc0 a0, CP0_TCSTATUS + _ehb + .set mips0 #endif /* CONFIG_MIPS_MT_SMTC */ LONG_L v1, PT_EPC(sp) -- cgit v1.2.3-70-g09d2 From 8531a35e5e275b17c57c39b7911bc2b37025f28c Mon Sep 17 00:00:00 2001 From: "Kevin D. Kissell" Date: Tue, 9 Sep 2008 21:48:52 +0200 Subject: [MIPS] SMTC: Fix SMTC dyntick support. Rework of SMTC support to make it work with the new clock event system, allowing "tickless" operation, and to make it compatible with the use of the "wait_irqoff" idle loop. The new clocking scheme means that the previously optional IPI instant replay mechanism is now required, and has been made more robust. Signed-off-by: Kevin D. Kissell Signed-off-by: Ralf Baechle --- arch/mips/Kconfig | 26 +--- arch/mips/kernel/Makefile | 1 + arch/mips/kernel/cevt-r4k.c | 173 ++++++--------------- arch/mips/kernel/cevt-smtc.c | 321 +++++++++++++++++++++++++++++++++++++++ arch/mips/kernel/cpu-probe.c | 10 +- arch/mips/kernel/genex.S | 4 +- arch/mips/kernel/smtc.c | 252 +++++++++++++++--------------- arch/mips/mti-malta/malta-smtc.c | 9 +- include/asm-mips/cevt-r4k.h | 46 ++++++ include/asm-mips/irqflags.h | 26 +++- include/asm-mips/smtc.h | 8 +- 11 files changed, 597 insertions(+), 279 deletions(-) create mode 100644 arch/mips/kernel/cevt-smtc.c create mode 100644 include/asm-mips/cevt-r4k.h (limited to 'arch/mips/kernel') diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 49896a2a1d7..c930b8ceb41 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1403,7 +1403,6 @@ config MIPS_MT_SMTC depends on CPU_MIPS32_R2 #depends on CPU_MIPS64_R2 # once there is hardware ... depends on SYS_SUPPORTS_MULTITHREADING - select GENERIC_CLOCKEVENTS_BROADCAST select CPU_MIPSR2_IRQ_VI select CPU_MIPSR2_IRQ_EI select MIPS_MT @@ -1451,32 +1450,17 @@ config MIPS_VPE_LOADER Includes a loader for loading an elf relocatable object onto another VPE and running it. -config MIPS_MT_SMTC_INSTANT_REPLAY - bool "Low-latency Dispatch of Deferred SMTC IPIs" - depends on MIPS_MT_SMTC && !PREEMPT - default y - help - SMTC pseudo-interrupts between TCs are deferred and queued - if the target TC is interrupt-inhibited (IXMT). In the first - SMTC prototypes, these queued IPIs were serviced on return - to user mode, or on entry into the kernel idle loop. The - INSTANT_REPLAY option dispatches them as part of local_irq_restore() - processing, which adds runtime overhead (hence the option to turn - it off), but ensures that IPIs are handled promptly even under - heavy I/O interrupt load. - config MIPS_MT_SMTC_IM_BACKSTOP bool "Use per-TC register bits as backstop for inhibited IM bits" depends on MIPS_MT_SMTC - default y + default n help To support multiple TC microthreads acting as "CPUs" within a VPE, VPE-wide interrupt mask bits must be specially manipulated during interrupt handling. To support legacy drivers and interrupt controller management code, SMTC has a "backstop" to track and if necessary restore the interrupt mask. This has some performance - impact on interrupt service overhead. Disable it only if you know - what you are doing. + impact on interrupt service overhead. config MIPS_MT_SMTC_IRQAFF bool "Support IRQ affinity API" @@ -1486,10 +1470,8 @@ config MIPS_MT_SMTC_IRQAFF Enables SMP IRQ affinity API (/proc/irq/*/smp_affinity, etc.) for SMTC Linux kernel. Requires platform support, of which an example can be found in the MIPS kernel i8259 and Malta - platform code. It is recommended that MIPS_MT_SMTC_INSTANT_REPLAY - be enabled if MIPS_MT_SMTC_IRQAFF is used. Adds overhead to - interrupt dispatch, and should be used only if you know what - you are doing. + platform code. Adds some overhead to interrupt dispatch, and + should be used only if you know what you are doing. config MIPS_VPE_LOADER_TOM bool "Load VPE program into memory hidden from linux" diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 706f9397479..25775cb5400 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -10,6 +10,7 @@ obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \ obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o +obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c index 24a2d907aa0..4a4c59f2737 100644 --- a/arch/mips/kernel/cevt-r4k.c +++ b/arch/mips/kernel/cevt-r4k.c @@ -12,6 +12,14 @@ #include #include +#include + +/* + * The SMTC Kernel for the 34K, 1004K, et. al. replaces several + * of these routines with SMTC-specific variants. + */ + +#ifndef CONFIG_MIPS_MT_SMTC static int mips_next_event(unsigned long delta, struct clock_event_device *evt) @@ -19,60 +27,27 @@ static int mips_next_event(unsigned long delta, unsigned int cnt; int res; -#ifdef CONFIG_MIPS_MT_SMTC - { - unsigned long flags, vpflags; - local_irq_save(flags); - vpflags = dvpe(); -#endif cnt = read_c0_count(); cnt += delta; write_c0_compare(cnt); res = ((int)(read_c0_count() - cnt) > 0) ? -ETIME : 0; -#ifdef CONFIG_MIPS_MT_SMTC - evpe(vpflags); - local_irq_restore(flags); - } -#endif return res; } -static void mips_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) +#endif /* CONFIG_MIPS_MT_SMTC */ + +void mips_set_clock_mode(enum clock_event_mode mode, + struct clock_event_device *evt) { /* Nothing to do ... */ } -static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device); -static int cp0_timer_irq_installed; +DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device); +int cp0_timer_irq_installed; -/* - * Timer ack for an R4k-compatible timer of a known frequency. - */ -static void c0_timer_ack(void) -{ - write_c0_compare(read_c0_compare()); -} +#ifndef CONFIG_MIPS_MT_SMTC -/* - * Possibly handle a performance counter interrupt. - * Return true if the timer interrupt should not be checked - */ -static inline int handle_perf_irq(int r2) -{ - /* - * The performance counter overflow interrupt may be shared with the - * timer interrupt (cp0_perfcount_irq < 0). If it is and a - * performance counter has overflowed (perf_irq() == IRQ_HANDLED) - * and we can't reliably determine if a counter interrupt has also - * happened (!r2) then don't check for a timer interrupt. - */ - return (cp0_perfcount_irq < 0) && - perf_irq() == IRQ_HANDLED && - !r2; -} - -static irqreturn_t c0_compare_interrupt(int irq, void *dev_id) +irqreturn_t c0_compare_interrupt(int irq, void *dev_id) { const int r2 = cpu_has_mips_r2; struct clock_event_device *cd; @@ -93,12 +68,8 @@ static irqreturn_t c0_compare_interrupt(int irq, void *dev_id) * interrupt. Being the paranoiacs we are we check anyway. */ if (!r2 || (read_c0_cause() & (1 << 30))) { - c0_timer_ack(); -#ifdef CONFIG_MIPS_MT_SMTC - if (cpu_data[cpu].vpe_id) - goto out; - cpu = 0; -#endif + /* Clear Count/Compare Interrupt */ + write_c0_compare(read_c0_compare()); cd = &per_cpu(mips_clockevent_device, cpu); cd->event_handler(cd); } @@ -107,65 +78,16 @@ out: return IRQ_HANDLED; } -static struct irqaction c0_compare_irqaction = { +#endif /* Not CONFIG_MIPS_MT_SMTC */ + +struct irqaction c0_compare_irqaction = { .handler = c0_compare_interrupt, -#ifdef CONFIG_MIPS_MT_SMTC - .flags = IRQF_DISABLED, -#else .flags = IRQF_DISABLED | IRQF_PERCPU, -#endif .name = "timer", }; -#ifdef CONFIG_MIPS_MT_SMTC -DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device); - -static void smtc_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) -{ -} - -static void mips_broadcast(cpumask_t mask) -{ - unsigned int cpu; - - for_each_cpu_mask(cpu, mask) - smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0); -} - -static void setup_smtc_dummy_clockevent_device(void) -{ - //uint64_t mips_freq = mips_hpt_^frequency; - unsigned int cpu = smp_processor_id(); - struct clock_event_device *cd; - cd = &per_cpu(smtc_dummy_clockevent_device, cpu); - - cd->name = "SMTC"; - cd->features = CLOCK_EVT_FEAT_DUMMY; - - /* Calculate the min / max delta */ - cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32); - cd->shift = 0; //32; - cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd); - cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd); - - cd->rating = 200; - cd->irq = 17; //-1; -// if (cpu) -// cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu); -// else - cd->cpumask = cpumask_of_cpu(cpu); - - cd->set_mode = smtc_set_mode; - - cd->broadcast = mips_broadcast; - - clockevents_register_device(cd); -} -#endif - -static void mips_event_handler(struct clock_event_device *dev) +void mips_event_handler(struct clock_event_device *dev) { } @@ -177,7 +99,23 @@ static int c0_compare_int_pending(void) return (read_c0_cause() >> cp0_compare_irq) & 0x100; } -static int c0_compare_int_usable(void) +/* + * Compare interrupt can be routed and latched outside the core, + * so a single execution hazard barrier may not be enough to give + * it time to clear as seen in the Cause register. 4 time the + * pipeline depth seems reasonably conservative, and empirically + * works better in configurations with high CPU/bus clock ratios. + */ + +#define compare_change_hazard() \ + do { \ + irq_disable_hazard(); \ + irq_disable_hazard(); \ + irq_disable_hazard(); \ + irq_disable_hazard(); \ + } while (0) + +int c0_compare_int_usable(void) { unsigned int delta; unsigned int cnt; @@ -187,7 +125,7 @@ static int c0_compare_int_usable(void) */ if (c0_compare_int_pending()) { write_c0_compare(read_c0_count()); - irq_disable_hazard(); + compare_change_hazard(); if (c0_compare_int_pending()) return 0; } @@ -196,7 +134,7 @@ static int c0_compare_int_usable(void) cnt = read_c0_count(); cnt += delta; write_c0_compare(cnt); - irq_disable_hazard(); + compare_change_hazard(); if ((int)(read_c0_count() - cnt) < 0) break; /* increase delta if the timer was already expired */ @@ -205,11 +143,12 @@ static int c0_compare_int_usable(void) while ((int)(read_c0_count() - cnt) <= 0) ; /* Wait for expiry */ + compare_change_hazard(); if (!c0_compare_int_pending()) return 0; write_c0_compare(read_c0_count()); - irq_disable_hazard(); + compare_change_hazard(); if (c0_compare_int_pending()) return 0; @@ -219,6 +158,8 @@ static int c0_compare_int_usable(void) return 1; } +#ifndef CONFIG_MIPS_MT_SMTC + int __cpuinit mips_clockevent_init(void) { uint64_t mips_freq = mips_hpt_frequency; @@ -229,17 +170,6 @@ int __cpuinit mips_clockevent_init(void) if (!cpu_has_counter || !mips_hpt_frequency) return -ENXIO; -#ifdef CONFIG_MIPS_MT_SMTC - setup_smtc_dummy_clockevent_device(); - - /* - * On SMTC we only register VPE0's compare interrupt as clockevent - * device. - */ - if (cpu) - return 0; -#endif - if (!c0_compare_int_usable()) return -ENXIO; @@ -265,13 +195,9 @@ int __cpuinit mips_clockevent_init(void) cd->rating = 300; cd->irq = irq; -#ifdef CONFIG_MIPS_MT_SMTC - cd->cpumask = CPU_MASK_ALL; -#else cd->cpumask = cpumask_of_cpu(cpu); -#endif cd->set_next_event = mips_next_event; - cd->set_mode = mips_set_mode; + cd->set_mode = mips_set_clock_mode; cd->event_handler = mips_event_handler; clockevents_register_device(cd); @@ -281,12 +207,9 @@ int __cpuinit mips_clockevent_init(void) cp0_timer_irq_installed = 1; -#ifdef CONFIG_MIPS_MT_SMTC -#define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq) - setup_irq_smtc(irq, &c0_compare_irqaction, CPUCTR_IMASKBIT); -#else setup_irq(irq, &c0_compare_irqaction); -#endif return 0; } + +#endif /* Not CONFIG_MIPS_MT_SMTC */ diff --git a/arch/mips/kernel/cevt-smtc.c b/arch/mips/kernel/cevt-smtc.c new file mode 100644 index 00000000000..5162fe4b595 --- /dev/null +++ b/arch/mips/kernel/cevt-smtc.c @@ -0,0 +1,321 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2007 MIPS Technologies, Inc. + * Copyright (C) 2007 Ralf Baechle + * Copyright (C) 2008 Kevin D. Kissell, Paralogos sarl + */ +#include +#include +#include + +#include +#include +#include + +/* + * Variant clock event timer support for SMTC on MIPS 34K, 1004K + * or other MIPS MT cores. + * + * Notes on SMTC Support: + * + * SMTC has multiple microthread TCs pretending to be Linux CPUs. + * But there's only one Count/Compare pair per VPE, and Compare + * interrupts are taken opportunisitically by available TCs + * bound to the VPE with the Count register. The new timer + * framework provides for global broadcasts, but we really + * want VPE-level multicasts for best behavior. So instead + * of invoking the high-level clock-event broadcast code, + * this version of SMTC support uses the historical SMTC + * multicast mechanisms "under the hood", appearing to the + * generic clock layer as if the interrupts are per-CPU. + * + * The approach taken here is to maintain a set of NR_CPUS + * virtual timers, and track which "CPU" needs to be alerted + * at each event. + * + * It's unlikely that we'll see a MIPS MT core with more than + * 2 VPEs, but we *know* that we won't need to handle more + * VPEs than we have "CPUs". So NCPUs arrays of NCPUs elements + * is always going to be overkill, but always going to be enough. + */ + +unsigned long smtc_nexttime[NR_CPUS][NR_CPUS]; +static int smtc_nextinvpe[NR_CPUS]; + +/* + * Timestamps stored are absolute values to be programmed + * into Count register. Valid timestamps will never be zero. + * If a Zero Count value is actually calculated, it is converted + * to be a 1, which will introduce 1 or two CPU cycles of error + * roughly once every four billion events, which at 1000 HZ means + * about once every 50 days. If that's actually a problem, one + * could alternate squashing 0 to 1 and to -1. + */ + +#define MAKEVALID(x) (((x) == 0L) ? 1L : (x)) +#define ISVALID(x) ((x) != 0L) + +/* + * Time comparison is subtle, as it's really truncated + * modular arithmetic. + */ + +#define IS_SOONER(a, b, reference) \ + (((a) - (unsigned long)(reference)) < ((b) - (unsigned long)(reference))) + +/* + * CATCHUP_INCREMENT, used when the function falls behind the counter. + * Could be an increasing function instead of a constant; + */ + +#define CATCHUP_INCREMENT 64 + +static int mips_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned long flags; + unsigned int mtflags; + unsigned long timestamp, reference, previous; + unsigned long nextcomp = 0L; + int vpe = current_cpu_data.vpe_id; + int cpu = smp_processor_id(); + local_irq_save(flags); + mtflags = dmt(); + + /* + * Maintain the per-TC virtual timer + * and program the per-VPE shared Count register + * as appropriate here... + */ + reference = (unsigned long)read_c0_count(); + timestamp = MAKEVALID(reference + delta); + /* + * To really model the clock, we have to catch the case + * where the current next-in-VPE timestamp is the old + * timestamp for the calling CPE, but the new value is + * in fact later. In that case, we have to do a full + * scan and discover the new next-in-VPE CPU id and + * timestamp. + */ + previous = smtc_nexttime[vpe][cpu]; + if (cpu == smtc_nextinvpe[vpe] && ISVALID(previous) + && IS_SOONER(previous, timestamp, reference)) { + int i; + int soonest = cpu; + + /* + * Update timestamp array here, so that new + * value gets considered along with those of + * other virtual CPUs on the VPE. + */ + smtc_nexttime[vpe][cpu] = timestamp; + for_each_online_cpu(i) { + if (ISVALID(smtc_nexttime[vpe][i]) + && IS_SOONER(smtc_nexttime[vpe][i], + smtc_nexttime[vpe][soonest], reference)) { + soonest = i; + } + } + smtc_nextinvpe[vpe] = soonest; + nextcomp = smtc_nexttime[vpe][soonest]; + /* + * Otherwise, we don't have to process the whole array rank, + * we just have to see if the event horizon has gotten closer. + */ + } else { + if (!ISVALID(smtc_nexttime[vpe][smtc_nextinvpe[vpe]]) || + IS_SOONER(timestamp, + smtc_nexttime[vpe][smtc_nextinvpe[vpe]], reference)) { + smtc_nextinvpe[vpe] = cpu; + nextcomp = timestamp; + } + /* + * Since next-in-VPE may me the same as the executing + * virtual CPU, we update the array *after* checking + * its value. + */ + smtc_nexttime[vpe][cpu] = timestamp; + } + + /* + * It may be that, in fact, we don't need to update Compare, + * but if we do, we want to make sure we didn't fall into + * a crack just behind Count. + */ + if (ISVALID(nextcomp)) { + write_c0_compare(nextcomp); + ehb(); + /* + * We never return an error, we just make sure + * that we trigger the handlers as quickly as + * we can if we fell behind. + */ + while ((nextcomp - (unsigned long)read_c0_count()) + > (unsigned long)LONG_MAX) { + nextcomp += CATCHUP_INCREMENT; + write_c0_compare(nextcomp); + ehb(); + } + } + emt(mtflags); + local_irq_restore(flags); + return 0; +} + + +void smtc_distribute_timer(int vpe) +{ + unsigned long flags; + unsigned int mtflags; + int cpu; + struct clock_event_device *cd; + unsigned long nextstamp = 0L; + unsigned long reference; + + +repeat: + for_each_online_cpu(cpu) { + /* + * Find virtual CPUs within the current VPE who have + * unserviced timer requests whose time is now past. + */ + local_irq_save(flags); + mtflags = dmt(); + if (cpu_data[cpu].vpe_id == vpe && + ISVALID(smtc_nexttime[vpe][cpu])) { + reference = (unsigned long)read_c0_count(); + if ((smtc_nexttime[vpe][cpu] - reference) + > (unsigned long)LONG_MAX) { + smtc_nexttime[vpe][cpu] = 0L; + emt(mtflags); + local_irq_restore(flags); + /* + * We don't send IPIs to ourself. + */ + if (cpu != smp_processor_id()) { + smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0); + } else { + cd = &per_cpu(mips_clockevent_device, cpu); + cd->event_handler(cd); + } + } else { + /* Local to VPE but Valid Time not yet reached. */ + if (!ISVALID(nextstamp) || + IS_SOONER(smtc_nexttime[vpe][cpu], nextstamp, + reference)) { + smtc_nextinvpe[vpe] = cpu; + nextstamp = smtc_nexttime[vpe][cpu]; + } + emt(mtflags); + local_irq_restore(flags); + } + } else { + emt(mtflags); + local_irq_restore(flags); + + } + } + /* Reprogram for interrupt at next soonest timestamp for VPE */ + if (ISVALID(nextstamp)) { + write_c0_compare(nextstamp); + ehb(); + if ((nextstamp - (unsigned long)read_c0_count()) + > (unsigned long)LONG_MAX) + goto repeat; + } +} + + +irqreturn_t c0_compare_interrupt(int irq, void *dev_id) +{ + int cpu = smp_processor_id(); + + /* If we're running SMTC, we've got MIPS MT and therefore MIPS32R2 */ + handle_perf_irq(1); + + if (read_c0_cause() & (1 << 30)) { + /* Clear Count/Compare Interrupt */ + write_c0_compare(read_c0_compare()); + smtc_distribute_timer(cpu_data[cpu].vpe_id); + } + return IRQ_HANDLED; +} + + +int __cpuinit mips_clockevent_init(void) +{ + uint64_t mips_freq = mips_hpt_frequency; + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd; + unsigned int irq; + int i; + int j; + + if (!cpu_has_counter || !mips_hpt_frequency) + return -ENXIO; + if (cpu == 0) { + for (i = 0; i < num_possible_cpus(); i++) { + smtc_nextinvpe[i] = 0; + for (j = 0; j < num_possible_cpus(); j++) + smtc_nexttime[i][j] = 0L; + } + /* + * SMTC also can't have the usablility test + * run by secondary TCs once Compare is in use. + */ + if (!c0_compare_int_usable()) + return -ENXIO; + } + + /* + * With vectored interrupts things are getting platform specific. + * get_c0_compare_int is a hook to allow a platform to return the + * interrupt number of it's liking. + */ + irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; + if (get_c0_compare_int) + irq = get_c0_compare_int(); + + cd = &per_cpu(mips_clockevent_device, cpu); + + cd->name = "MIPS"; + cd->features = CLOCK_EVT_FEAT_ONESHOT; + + /* Calculate the min / max delta */ + cd->mult = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32); + cd->shift = 32; + cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); + cd->min_delta_ns = clockevent_delta2ns(0x300, cd); + + cd->rating = 300; + cd->irq = irq; + cd->cpumask = cpumask_of_cpu(cpu); + cd->set_next_event = mips_next_event; + cd->set_mode = mips_set_clock_mode; + cd->event_handler = mips_event_handler; + + clockevents_register_device(cd); + + /* + * On SMTC we only want to do the data structure + * initialization and IRQ setup once. + */ + if (cpu) + return 0; + /* + * And we need the hwmask associated with the c0_compare + * vector to be initialized. + */ + irq_hwmask[irq] = (0x100 << cp0_compare_irq); + if (cp0_timer_irq_installed) + return 0; + + cp0_timer_irq_installed = 1; + + setup_irq(irq, &c0_compare_irqaction); + + return 0; +} diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 11c92dc5379..e621fda8ab3 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -54,14 +54,18 @@ extern void r4k_wait(void); * interrupt is requested" restriction in the MIPS32/MIPS64 architecture makes * using this version a gamble. */ -static void r4k_wait_irqoff(void) +void r4k_wait_irqoff(void) { local_irq_disable(); if (!need_resched()) - __asm__(" .set mips3 \n" + __asm__(" .set push \n" + " .set mips3 \n" " wait \n" - " .set mips0 \n"); + " .set pop \n"); local_irq_enable(); + __asm__(" .globl __pastwait \n" + "__pastwait: \n"); + return; } /* diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index f886dd7f708..01dcbe38fa0 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -282,8 +282,8 @@ NESTED(except_vec_vi_handler, 0, sp) and t0, a0, t1 #ifdef CONFIG_MIPS_MT_SMTC_IM_BACKSTOP mfc0 t2, CP0_TCCONTEXT - or t0, t0, t2 - mtc0 t0, CP0_TCCONTEXT + or t2, t0, t2 + mtc0 t2, CP0_TCCONTEXT #endif /* CONFIG_MIPS_MT_SMTC_IM_BACKSTOP */ xor t1, t1, t0 mtc0 t1, CP0_STATUS diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index 39b491b9ad8..897fb2b4751 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -1,4 +1,21 @@ -/* Copyright (C) 2004 Mips Technologies, Inc */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2004 Mips Technologies, Inc + * Copyright (C) 2008 Kevin D. Kissell + */ #include #include @@ -21,7 +38,6 @@ #include #include #include -#include #include /* @@ -58,11 +74,6 @@ unsigned long irq_hwmask[NR_IRQS]; asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS]; -/* - * Clock interrupt "latch" buffers, per "CPU" - */ - -static atomic_t ipi_timer_latch[NR_CPUS]; /* * Number of InterProcessor Interrupt (IPI) message buffers to allocate @@ -282,7 +293,7 @@ static void smtc_configure_tlb(void) * phys_cpu_present_map and the logical/physical mappings. */ -int __init mipsmt_build_cpu_map(int start_cpu_slot) +int __init smtc_build_cpu_map(int start_cpu_slot) { int i, ntcs; @@ -325,7 +336,12 @@ static void smtc_tc_setup(int vpe, int tc, int cpu) write_tc_c0_tcstatus((read_tc_c0_tcstatus() & ~(TCSTATUS_TKSU | TCSTATUS_DA | TCSTATUS_IXMT)) | TCSTATUS_A); - write_tc_c0_tccontext(0); + /* + * TCContext gets an offset from the base of the IPIQ array + * to be used in low-level code to detect the presence of + * an active IPI queue + */ + write_tc_c0_tccontext((sizeof(struct smtc_ipi_q) * cpu) << 16); /* Bind tc to vpe */ write_tc_c0_tcbind(vpe); /* In general, all TCs should have the same cpu_data indications */ @@ -336,10 +352,18 @@ static void smtc_tc_setup(int vpe, int tc, int cpu) cpu_data[cpu].options &= ~MIPS_CPU_FPU; cpu_data[cpu].vpe_id = vpe; cpu_data[cpu].tc_id = tc; + /* Multi-core SMTC hasn't been tested, but be prepared */ + cpu_data[cpu].core = (read_vpe_c0_ebase() >> 1) & 0xff; } +/* + * Tweak to get Count registes in as close a sync as possible. + * Value seems good for 34K-class cores. + */ + +#define CP0_SKEW 8 -void mipsmt_prepare_cpus(void) +void smtc_prepare_cpus(int cpus) { int i, vpe, tc, ntc, nvpe, tcpervpe[NR_CPUS], slop, cpu; unsigned long flags; @@ -363,13 +387,13 @@ void mipsmt_prepare_cpus(void) IPIQ[i].head = IPIQ[i].tail = NULL; spin_lock_init(&IPIQ[i].lock); IPIQ[i].depth = 0; - atomic_set(&ipi_timer_latch[i], 0); } /* cpu_data index starts at zero */ cpu = 0; cpu_data[cpu].vpe_id = 0; cpu_data[cpu].tc_id = 0; + cpu_data[cpu].core = (read_c0_ebase() >> 1) & 0xff; cpu++; /* Report on boot-time options */ @@ -484,7 +508,8 @@ void mipsmt_prepare_cpus(void) write_vpe_c0_compare(0); /* Propagate Config7 */ write_vpe_c0_config7(read_c0_config7()); - write_vpe_c0_count(read_c0_count()); + write_vpe_c0_count(read_c0_count() + CP0_SKEW); + ehb(); } /* enable multi-threading within VPE */ write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() | VPECONTROL_TE); @@ -585,24 +610,22 @@ void __cpuinit smtc_boot_secondary(int cpu, struct task_struct *idle) void smtc_init_secondary(void) { - /* - * Start timer on secondary VPEs if necessary. - * plat_timer_setup has already have been invoked by init/main - * on "boot" TC. Like per_cpu_trap_init() hack, this assumes that - * SMTC init code assigns TCs consdecutively and in ascending order - * to across available VPEs. - */ - if (((read_c0_tcbind() & TCBIND_CURTC) != 0) && - ((read_c0_tcbind() & TCBIND_CURVPE) - != cpu_data[smp_processor_id() - 1].vpe_id)){ - write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); - } - local_irq_enable(); } void smtc_smp_finish(void) { + int cpu = smp_processor_id(); + + /* + * Lowest-numbered CPU per VPE starts a clock tick. + * Like per_cpu_trap_init() hack, this assumes that + * SMTC init code assigns TCs consdecutively and + * in ascending order across available VPEs. + */ + if (cpu > 0 && (cpu_data[cpu].vpe_id != cpu_data[cpu - 1].vpe_id)) + write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + printk("TC %d going on-line as CPU %d\n", cpu_data[smp_processor_id()].tc_id, smp_processor_id()); } @@ -755,6 +778,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) struct smtc_ipi *pipi; unsigned long flags; int mtflags; + unsigned long tcrestart; + extern void r4k_wait_irqoff(void), __pastwait(void); if (cpu == smp_processor_id()) { printk("Cannot Send IPI to self!\n"); @@ -771,8 +796,6 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) pipi->arg = (void *)action; pipi->dest = cpu; if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) { - if (type == SMTC_CLOCK_TICK) - atomic_inc(&ipi_timer_latch[cpu]); /* If not on same VPE, enqueue and send cross-VPE interrupt */ smtc_ipi_nq(&IPIQ[cpu], pipi); LOCK_CORE_PRA(); @@ -800,22 +823,29 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) if ((tcstatus & TCSTATUS_IXMT) != 0) { /* - * Spin-waiting here can deadlock, - * so we queue the message for the target TC. + * If we're in the the irq-off version of the wait + * loop, we need to force exit from the wait and + * do a direct post of the IPI. + */ + if (cpu_wait == r4k_wait_irqoff) { + tcrestart = read_tc_c0_tcrestart(); + if (tcrestart >= (unsigned long)r4k_wait_irqoff + && tcrestart < (unsigned long)__pastwait) { + write_tc_c0_tcrestart(__pastwait); + tcstatus &= ~TCSTATUS_IXMT; + write_tc_c0_tcstatus(tcstatus); + goto postdirect; + } + } + /* + * Otherwise we queue the message for the target TC + * to pick up when he does a local_irq_restore() */ write_tc_c0_tchalt(0); UNLOCK_CORE_PRA(); - /* Try to reduce redundant timer interrupt messages */ - if (type == SMTC_CLOCK_TICK) { - if (atomic_postincrement(&ipi_timer_latch[cpu])!=0){ - smtc_ipi_nq(&freeIPIq, pipi); - return; - } - } smtc_ipi_nq(&IPIQ[cpu], pipi); } else { - if (type == SMTC_CLOCK_TICK) - atomic_inc(&ipi_timer_latch[cpu]); +postdirect: post_direct_ipi(cpu, pipi); write_tc_c0_tchalt(0); UNLOCK_CORE_PRA(); @@ -883,7 +913,7 @@ static void ipi_call_interrupt(void) smp_call_function_interrupt(); } -DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device); +DECLARE_PER_CPU(struct clock_event_device, mips_clockevent_device); void ipi_decode(struct smtc_ipi *pipi) { @@ -891,20 +921,13 @@ void ipi_decode(struct smtc_ipi *pipi) struct clock_event_device *cd; void *arg_copy = pipi->arg; int type_copy = pipi->type; - int ticks; - smtc_ipi_nq(&freeIPIq, pipi); switch (type_copy) { case SMTC_CLOCK_TICK: irq_enter(); kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++; - cd = &per_cpu(smtc_dummy_clockevent_device, cpu); - ticks = atomic_read(&ipi_timer_latch[cpu]); - atomic_sub(ticks, &ipi_timer_latch[cpu]); - while (ticks) { - cd->event_handler(cd); - ticks--; - } + cd = &per_cpu(mips_clockevent_device, cpu); + cd->event_handler(cd); irq_exit(); break; @@ -937,24 +960,48 @@ void ipi_decode(struct smtc_ipi *pipi) } } +/* + * Similar to smtc_ipi_replay(), but invoked from context restore, + * so it reuses the current exception frame rather than set up a + * new one with self_ipi. + */ + void deferred_smtc_ipi(void) { - struct smtc_ipi *pipi; - unsigned long flags; -/* DEBUG */ - int q = smp_processor_id(); + int cpu = smp_processor_id(); /* * Test is not atomic, but much faster than a dequeue, * and the vast majority of invocations will have a null queue. + * If irq_disabled when this was called, then any IPIs queued + * after we test last will be taken on the next irq_enable/restore. + * If interrupts were enabled, then any IPIs added after the + * last test will be taken directly. */ - if (IPIQ[q].head != NULL) { - while((pipi = smtc_ipi_dq(&IPIQ[q])) != NULL) { - /* ipi_decode() should be called with interrupts off */ - local_irq_save(flags); + + while (IPIQ[cpu].head != NULL) { + struct smtc_ipi_q *q = &IPIQ[cpu]; + struct smtc_ipi *pipi; + unsigned long flags; + + /* + * It may be possible we'll come in with interrupts + * already enabled. + */ + local_irq_save(flags); + + spin_lock(&q->lock); + pipi = __smtc_ipi_dq(q); + spin_unlock(&q->lock); + if (pipi != NULL) ipi_decode(pipi); - local_irq_restore(flags); - } + /* + * The use of the __raw_local restore isn't + * as obviously necessary here as in smtc_ipi_replay(), + * but it's more efficient, given that we're already + * running down the IPI queue. + */ + __raw_local_irq_restore(flags); } } @@ -1066,55 +1113,53 @@ static void setup_cross_vpe_interrupts(unsigned int nvpe) /* * SMTC-specific hacks invoked from elsewhere in the kernel. - * - * smtc_ipi_replay is called from raw_local_irq_restore which is only ever - * called with interrupts disabled. We do rely on interrupts being disabled - * here because using spin_lock_irqsave()/spin_unlock_irqrestore() would - * result in a recursive call to raw_local_irq_restore(). */ -static void __smtc_ipi_replay(void) + /* + * smtc_ipi_replay is called from raw_local_irq_restore + */ + +void smtc_ipi_replay(void) { unsigned int cpu = smp_processor_id(); /* * To the extent that we've ever turned interrupts off, * we may have accumulated deferred IPIs. This is subtle. - * If we use the smtc_ipi_qdepth() macro, we'll get an - * exact number - but we'll also disable interrupts - * and create a window of failure where a new IPI gets - * queued after we test the depth but before we re-enable - * interrupts. So long as IXMT never gets set, however, * we should be OK: If we pick up something and dispatch * it here, that's great. If we see nothing, but concurrent * with this operation, another TC sends us an IPI, IXMT * is clear, and we'll handle it as a real pseudo-interrupt - * and not a pseudo-pseudo interrupt. + * and not a pseudo-pseudo interrupt. The important thing + * is to do the last check for queued message *after* the + * re-enabling of interrupts. */ - if (IPIQ[cpu].depth > 0) { - while (1) { - struct smtc_ipi_q *q = &IPIQ[cpu]; - struct smtc_ipi *pipi; - extern void self_ipi(struct smtc_ipi *); - - spin_lock(&q->lock); - pipi = __smtc_ipi_dq(q); - spin_unlock(&q->lock); - if (!pipi) - break; + while (IPIQ[cpu].head != NULL) { + struct smtc_ipi_q *q = &IPIQ[cpu]; + struct smtc_ipi *pipi; + unsigned long flags; + /* + * It's just possible we'll come in with interrupts + * already enabled. + */ + local_irq_save(flags); + + spin_lock(&q->lock); + pipi = __smtc_ipi_dq(q); + spin_unlock(&q->lock); + /* + ** But use a raw restore here to avoid recursion. + */ + __raw_local_irq_restore(flags); + + if (pipi) { self_ipi(pipi); smtc_cpu_stats[cpu].selfipis++; } } } -void smtc_ipi_replay(void) -{ - raw_local_irq_disable(); - __smtc_ipi_replay(); -} - EXPORT_SYMBOL(smtc_ipi_replay); void smtc_idle_loop_hook(void) @@ -1193,40 +1238,13 @@ void smtc_idle_loop_hook(void) } } - /* - * Now that we limit outstanding timer IPIs, check for hung TC - */ - for (tc = 0; tc < NR_CPUS; tc++) { - /* Don't check ourself - we'll dequeue IPIs just below */ - if ((tc != smp_processor_id()) && - atomic_read(&ipi_timer_latch[tc]) > timerq_limit) { - if (clock_hang_reported[tc] == 0) { - pdb_msg += sprintf(pdb_msg, - "TC %d looks hung with timer latch at %d\n", - tc, atomic_read(&ipi_timer_latch[tc])); - clock_hang_reported[tc]++; - } - } - } emt(mtflags); local_irq_restore(flags); if (pdb_msg != &id_ho_db_msg[0]) printk("CPU%d: %s", smp_processor_id(), id_ho_db_msg); #endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ - /* - * Replay any accumulated deferred IPIs. If "Instant Replay" - * is in use, there should never be any. - */ -#ifndef CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY - { - unsigned long flags; - - local_irq_save(flags); - __smtc_ipi_replay(); - local_irq_restore(flags); - } -#endif /* CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY */ + smtc_ipi_replay(); } void smtc_soft_dump(void) @@ -1242,10 +1260,6 @@ void smtc_soft_dump(void) printk("%d: %ld\n", i, smtc_cpu_stats[i].selfipis); } smtc_ipi_qdump(); - printk("Timer IPI Backlogs:\n"); - for (i=0; i < NR_CPUS; i++) { - printk("%d: %d\n", i, atomic_read(&ipi_timer_latch[i])); - } printk("%d Recoveries of \"stolen\" FPU\n", atomic_read(&smtc_fpu_recoveries)); } diff --git a/arch/mips/mti-malta/malta-smtc.c b/arch/mips/mti-malta/malta-smtc.c index 5ea705e4945..f84a46a8ae6 100644 --- a/arch/mips/mti-malta/malta-smtc.c +++ b/arch/mips/mti-malta/malta-smtc.c @@ -84,12 +84,17 @@ static void msmtc_cpus_done(void) static void __init msmtc_smp_setup(void) { - mipsmt_build_cpu_map(0); + /* + * we won't get the definitive value until + * we've run smtc_prepare_cpus later, but + * we would appear to need an upper bound now. + */ + smp_num_siblings = smtc_build_cpu_map(0); } static void __init msmtc_prepare_cpus(unsigned int max_cpus) { - mipsmt_prepare_cpus(); + smtc_prepare_cpus(max_cpus); } struct plat_smp_ops msmtc_smp_ops = { diff --git a/include/asm-mips/cevt-r4k.h b/include/asm-mips/cevt-r4k.h new file mode 100644 index 00000000000..fa4328f9124 --- /dev/null +++ b/include/asm-mips/cevt-r4k.h @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Kevin D. Kissell + */ + +/* + * Definitions used for common event timer implementation + * for MIPS 4K-type processors and their MIPS MT variants. + * Avoids unsightly extern declarations in C files. + */ +#ifndef __ASM_CEVT_R4K_H +#define __ASM_CEVT_R4K_H + +DECLARE_PER_CPU(struct clock_event_device, mips_clockevent_device); + +void mips_event_handler(struct clock_event_device *dev); +int c0_compare_int_usable(void); +void mips_set_clock_mode(enum clock_event_mode, struct clock_event_device *); +irqreturn_t c0_compare_interrupt(int, void *); + +extern struct irqaction c0_compare_irqaction; +extern int cp0_timer_irq_installed; + +/* + * Possibly handle a performance counter interrupt. + * Return true if the timer interrupt should not be checked + */ + +static inline int handle_perf_irq(int r2) +{ + /* + * The performance counter overflow interrupt may be shared with the + * timer interrupt (cp0_perfcount_irq < 0). If it is and a + * performance counter has overflowed (perf_irq() == IRQ_HANDLED) + * and we can't reliably determine if a counter interrupt has also + * happened (!r2) then don't check for a timer interrupt. + */ + return (cp0_perfcount_irq < 0) && + perf_irq() == IRQ_HANDLED && + !r2; +} + +#endif /* __ASM_CEVT_R4K_H */ diff --git a/include/asm-mips/irqflags.h b/include/asm-mips/irqflags.h index 881e8866501..701ec0ba8fa 100644 --- a/include/asm-mips/irqflags.h +++ b/include/asm-mips/irqflags.h @@ -38,8 +38,17 @@ __asm__( " .set pop \n" " .endm"); +extern void smtc_ipi_replay(void); + static inline void raw_local_irq_enable(void) { +#ifdef CONFIG_MIPS_MT_SMTC + /* + * SMTC kernel needs to do a software replay of queued + * IPIs, at the cost of call overhead on each local_irq_enable() + */ + smtc_ipi_replay(); +#endif __asm__ __volatile__( "raw_local_irq_enable" : /* no outputs */ @@ -47,6 +56,7 @@ static inline void raw_local_irq_enable(void) : "memory"); } + /* * For cli() we have to insert nops to make sure that the new value * has actually arrived in the status register before the end of this @@ -185,15 +195,14 @@ __asm__( " .set pop \n" " .endm \n"); -extern void smtc_ipi_replay(void); static inline void raw_local_irq_restore(unsigned long flags) { unsigned long __tmp1; -#ifdef CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY +#ifdef CONFIG_MIPS_MT_SMTC /* - * CONFIG_MIPS_MT_SMTC_INSTANT_REPLAY does prompt replay of deferred + * SMTC kernel needs to do a software replay of queued * IPIs, at the cost of branch and call overhead on each * local_irq_restore() */ @@ -208,6 +217,17 @@ static inline void raw_local_irq_restore(unsigned long flags) : "memory"); } +static inline void __raw_local_irq_restore(unsigned long flags) +{ + unsigned long __tmp1; + + __asm__ __volatile__( + "raw_local_irq_restore\t%0" + : "=r" (__tmp1) + : "0" (flags) + : "memory"); +} + static inline int raw_irqs_disabled_flags(unsigned long flags) { #ifdef CONFIG_MIPS_MT_SMTC diff --git a/include/asm-mips/smtc.h b/include/asm-mips/smtc.h index 3639b28f80d..ea60bf08dcb 100644 --- a/include/asm-mips/smtc.h +++ b/include/asm-mips/smtc.h @@ -6,6 +6,7 @@ */ #include +#include /* * System-wide SMTC status information @@ -38,14 +39,15 @@ struct mm_struct; struct task_struct; void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu); - +void self_ipi(struct smtc_ipi *); void smtc_flush_tlb_asid(unsigned long asid); -extern int mipsmt_build_cpu_map(int startslot); -extern void mipsmt_prepare_cpus(void); +extern int smtc_build_cpu_map(int startslot); +extern void smtc_prepare_cpus(int cpus); extern void smtc_smp_finish(void); extern void smtc_boot_secondary(int cpu, struct task_struct *t); extern void smtc_cpus_done(void); + /* * Sharing the TLB between multiple VPEs means that the * "random" index selection function is not allowed to -- cgit v1.2.3-70-g09d2 From fd3d2764ee5aad862e51c21b8239561acdea8c2f Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 3 Oct 2008 22:43:38 +0100 Subject: [MIPS] IP27: Fix build errors if CONFIG_MAPPED_KERNEL=y Signed-off-by: Ralf Baechle --- arch/mips/kernel/head.S | 1 + include/asm-mips/sn/mapped_kernel.h | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S index 361364501d3..492a0a8d70f 100644 --- a/arch/mips/kernel/head.S +++ b/arch/mips/kernel/head.S @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/include/asm-mips/sn/mapped_kernel.h b/include/asm-mips/sn/mapped_kernel.h index c3dd5d0d525..721496a0bb9 100644 --- a/include/asm-mips/sn/mapped_kernel.h +++ b/include/asm-mips/sn/mapped_kernel.h @@ -5,6 +5,8 @@ #ifndef __ASM_SN_MAPPED_KERNEL_H #define __ASM_SN_MAPPED_KERNEL_H +#include + /* * Note on how mapped kernels work: the text and data section is * compiled at cksseg segment (LOADADDR = 0xc001c000), and the @@ -29,10 +31,8 @@ #define MAPPED_ADDR_RO_TO_PHYS(x) (x - REP_BASE) #define MAPPED_ADDR_RW_TO_PHYS(x) (x - REP_BASE - 16777216) -#define MAPPED_KERN_RO_PHYSBASE(n) \ - (PLAT_NODE_DATA(n)->kern_vars.kv_ro_baseaddr) -#define MAPPED_KERN_RW_PHYSBASE(n) \ - (PLAT_NODE_DATA(n)->kern_vars.kv_rw_baseaddr) +#define MAPPED_KERN_RO_PHYSBASE(n) (hub_data(n)->kern_vars.kv_ro_baseaddr) +#define MAPPED_KERN_RW_PHYSBASE(n) (hub_data(n)->kern_vars.kv_rw_baseaddr) #define MAPPED_KERN_RO_TO_PHYS(x) \ ((unsigned long)MAPPED_ADDR_RO_TO_PHYS(x) | \ -- cgit v1.2.3-70-g09d2 From 5d9a76cd0ed367d01b0b237253adb7607e86a277 Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Sun, 17 Aug 2008 16:49:25 +0200 Subject: MIPS: Use compat_sys_ptrace This replaces mips's sys_ptrace32 with a compat_arch_ptrace and enables the new generic definition of compat_sys_ptrace instead. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Ralf Baechle --- arch/mips/kernel/ptrace32.c | 43 ++++++++---------------------------------- arch/mips/kernel/scall64-n32.S | 2 +- arch/mips/kernel/scall64-o32.S | 2 +- arch/mips/kernel/signal32.c | 12 ++++++++++++ include/asm-mips/ptrace.h | 3 +++ 5 files changed, 25 insertions(+), 37 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index 76818be6ba7..cac56a8c867 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -15,6 +15,7 @@ * binaries. */ #include +#include #include #include #include @@ -46,37 +47,13 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data); * Tracing a 32-bit process with a 64-bit strace and vice versa will not * work. I don't know how to fix this. */ -asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) { - struct task_struct *child; + int addr = caddr; + int data = cdata; int ret; -#if 0 - printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n", - (int) request, (int) pid, (unsigned long) addr, - (unsigned long) data); -#endif - lock_kernel(); - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - goto out; - } - - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out; - } - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ @@ -214,7 +191,7 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) if (!cpu_has_dsp) { tmp = 0; ret = -EIO; - goto out_tsk; + goto out; } dregs = __get_dsp_regs(child); tmp = (unsigned long) (dregs[addr - DSP_BASE]); @@ -224,14 +201,14 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) if (!cpu_has_dsp) { tmp = 0; ret = -EIO; - goto out_tsk; + goto out; } tmp = child->thread.dsp.dspcontrol; break; default: tmp = 0; ret = -EIO; - goto out_tsk; + goto out; } ret = put_user(tmp, (unsigned __user *) (unsigned long) data); break; @@ -414,10 +391,6 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) ret = ptrace_request(child, request, addr, data); break; } - -out_tsk: - put_task_struct(child); out: - unlock_kernel(); return ret; } diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index da7f1b6ea0f..324c5499dec 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -219,7 +219,7 @@ EXPORT(sysn32_call_table) PTR compat_sys_getrusage PTR compat_sys_sysinfo PTR compat_sys_times - PTR sys32_ptrace + PTR compat_sys_ptrace PTR sys_getuid /* 6100 */ PTR sys_syslog PTR sys_getgid diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index d7cd1aac9ad..85fedac99a5 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -231,7 +231,7 @@ sys_call_table: PTR sys_setuid PTR sys_getuid PTR compat_sys_stime /* 4025 */ - PTR sys32_ptrace + PTR compat_sys_ptrace PTR sys_alarm PTR sys_ni_syscall /* was sys_fstat */ PTR sys_pause diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index 572c610db1b..652709b353a 100644 --- a/arch/mips/kernel/signal32.c +++ b/arch/mips/kernel/signal32.c @@ -482,6 +482,18 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) return err; } +int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) +{ + memset(to, 0, sizeof *to); + + if (copy_from_user(to, from, 3*sizeof(int)) || + copy_from_user(to->_sifields._pad, + from->_sifields._pad, SI_PAD_SIZE32)) + return -EFAULT; + + return 0; +} + asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs) { struct sigframe32 __user *frame; diff --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h index 786f7e3c99b..c00cca24dae 100644 --- a/include/asm-mips/ptrace.h +++ b/include/asm-mips/ptrace.h @@ -9,6 +9,9 @@ #ifndef _ASM_PTRACE_H #define _ASM_PTRACE_H +#ifdef CONFIG_64BIT +#define __ARCH_WANT_COMPAT_SYS_PTRACE +#endif /* 0 - 31 are integer registers, 32 - 63 are fp registers. */ #define FPR_BASE 32 -- cgit v1.2.3-70-g09d2 From 6aa3524c182c01b8b8b7c21c4da20c742a9b4d86 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 23 Sep 2008 00:05:54 -0700 Subject: MIPS: Add HARDWARE_WATCHPOINTS definitions and support code. This is the main support code for the patch. Here we just add the code, the following patches hook it up. Signed-off-by: David Daney Signed-off-by: Ralf Baechle create mode 100644 arch/mips/include/asm/watch.h create mode 100644 arch/mips/kernel/watch.c --- arch/mips/include/asm/cpu-info.h | 6 ++ arch/mips/include/asm/processor.h | 20 ++++ arch/mips/include/asm/thread_info.h | 2 + arch/mips/include/asm/watch.h | 32 ++++++ arch/mips/kernel/Makefile | 2 +- arch/mips/kernel/watch.c | 188 ++++++++++++++++++++++++++++++++++++ 6 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 arch/mips/include/asm/watch.h create mode 100644 arch/mips/kernel/watch.c (limited to 'arch/mips/kernel') diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index 2de73dbb2e9..744cd8fb107 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -12,6 +12,8 @@ #ifndef __ASM_CPU_INFO_H #define __ASM_CPU_INFO_H +#include + #include /* @@ -69,6 +71,10 @@ struct cpuinfo_mips { int tc_id; /* Thread Context number */ #endif void *data; /* Additional data */ + unsigned int watch_reg_count; /* Number that exist */ + unsigned int watch_reg_use_cnt; /* Usable by ptrace */ +#define NUM_WATCH_REGS 4 + u16 watch_reg_masks[NUM_WATCH_REGS]; } __attribute__((aligned(SMP_CACHE_BYTES))); extern struct cpuinfo_mips cpu_data[]; diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h index a1e4453469f..18ee58e3944 100644 --- a/arch/mips/include/asm/processor.h +++ b/arch/mips/include/asm/processor.h @@ -105,6 +105,19 @@ struct mips_dsp_state { {0,} \ } +struct mips3264_watch_reg_state { + /* The width of watchlo is 32 in a 32 bit kernel and 64 in a + 64 bit kernel. We use unsigned long as it has the same + property. */ + unsigned long watchlo[NUM_WATCH_REGS]; + /* Only the mask and IRW bits from watchhi. */ + u16 watchhi[NUM_WATCH_REGS]; +}; + +union mips_watch_reg_state { + struct mips3264_watch_reg_state mips3264; +}; + typedef struct { unsigned long seg; } mm_segment_t; @@ -137,6 +150,9 @@ struct thread_struct { /* Saved state of the DSP ASE, if available. */ struct mips_dsp_state dsp; + /* Saved watch register state, if available. */ + union mips_watch_reg_state watch; + /* Other stuff associated with the thread. */ unsigned long cp0_badvaddr; /* Last user fault */ unsigned long cp0_baduaddr; /* Last kernel fault accessing USEG */ @@ -192,6 +208,10 @@ struct thread_struct { .dspr = {0, }, \ .dspcontrol = 0, \ }, \ + /* \ + * saved watch register stuff \ + */ \ + .watch = {{{0,},},}, \ /* \ * Other stuff associated with the process \ */ \ diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h index bb3060699df..3f76de73c94 100644 --- a/arch/mips/include/asm/thread_info.h +++ b/arch/mips/include/asm/thread_info.h @@ -124,6 +124,7 @@ register struct thread_info *__current_thread_info __asm__("$28"); #define TIF_32BIT_REGS 22 /* also implies 16/32 fprs */ #define TIF_32BIT_ADDR 23 /* 32-bit address space (o32/n32) */ #define TIF_FPUBOUND 24 /* thread bound to FPU-full CPU set */ +#define TIF_LOAD_WATCH 25 /* If set, load watch registers */ #define TIF_SYSCALL_TRACE 31 /* syscall trace active */ #define _TIF_SYSCALL_TRACE (1< + +#include + +void mips_install_watch_registers(void); +void mips_read_watch_registers(void); +void mips_clear_watch_registers(void); +void mips_probe_watch_registers(struct cpuinfo_mips *c); + +#ifdef CONFIG_HARDWARE_WATCHPOINTS +#define __restore_watch() do { \ + if (unlikely(test_bit(TIF_LOAD_WATCH, \ + ¤t_thread_info()->flags))) { \ + mips_install_watch_registers(); \ + } \ +} while (0) + +#else +#define __restore_watch() do {} while (0) +#endif + +#endif /* _ASM_WATCH_H */ diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 25775cb5400..d9da7112aaf 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -6,7 +6,7 @@ extra-y := head.o init_task.o vmlinux.lds obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \ ptrace.o reset.o setup.o signal.o syscall.o \ - time.o topology.o traps.o unaligned.o + time.o topology.o traps.o unaligned.o watch.o obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o diff --git a/arch/mips/kernel/watch.c b/arch/mips/kernel/watch.c new file mode 100644 index 00000000000..c1540696803 --- /dev/null +++ b/arch/mips/kernel/watch.c @@ -0,0 +1,188 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 David Daney + */ + +#include + +#include +#include + +/* + * Install the watch registers for the current thread. A maximum of + * four registers are installed although the machine may have more. + */ +void mips_install_watch_registers(void) +{ + struct mips3264_watch_reg_state *watches = + ¤t->thread.watch.mips3264; + switch (current_cpu_data.watch_reg_use_cnt) { + default: + BUG(); + case 4: + write_c0_watchlo3(watches->watchlo[3]); + /* Write 1 to the I, R, and W bits to clear them, and + 1 to G so all ASIDs are trapped. */ + write_c0_watchhi3(0x40000007 | watches->watchhi[3]); + case 3: + write_c0_watchlo2(watches->watchlo[2]); + write_c0_watchhi2(0x40000007 | watches->watchhi[2]); + case 2: + write_c0_watchlo1(watches->watchlo[1]); + write_c0_watchhi1(0x40000007 | watches->watchhi[1]); + case 1: + write_c0_watchlo0(watches->watchlo[0]); + write_c0_watchhi0(0x40000007 | watches->watchhi[0]); + } +} + +/* + * Read back the watchhi registers so the user space debugger has + * access to the I, R, and W bits. A maximum of four registers are + * read although the machine may have more. + */ +void mips_read_watch_registers(void) +{ + struct mips3264_watch_reg_state *watches = + ¤t->thread.watch.mips3264; + switch (current_cpu_data.watch_reg_use_cnt) { + default: + BUG(); + case 4: + watches->watchhi[3] = (read_c0_watchhi3() & 0x0fff); + case 3: + watches->watchhi[2] = (read_c0_watchhi2() & 0x0fff); + case 2: + watches->watchhi[1] = (read_c0_watchhi1() & 0x0fff); + case 1: + watches->watchhi[0] = (read_c0_watchhi0() & 0x0fff); + } + if (current_cpu_data.watch_reg_use_cnt == 1 && + (watches->watchhi[0] & 7) == 0) { + /* Pathological case of release 1 architecture that + * doesn't set the condition bits. We assume that + * since we got here, the watch condition was met and + * signal that the conditions requested in watchlo + * were met. */ + watches->watchhi[0] |= (watches->watchlo[0] & 7); + } + } + +/* + * Disable all watch registers. Although only four registers are + * installed, all are cleared to eliminate the possibility of endless + * looping in the watch handler. + */ +void mips_clear_watch_registers(void) +{ + switch (current_cpu_data.watch_reg_count) { + default: + BUG(); + case 8: + write_c0_watchlo7(0); + case 7: + write_c0_watchlo6(0); + case 6: + write_c0_watchlo5(0); + case 5: + write_c0_watchlo4(0); + case 4: + write_c0_watchlo3(0); + case 3: + write_c0_watchlo2(0); + case 2: + write_c0_watchlo1(0); + case 1: + write_c0_watchlo0(0); + } +} + +__cpuinit void mips_probe_watch_registers(struct cpuinfo_mips *c) +{ + unsigned int t; + + if ((c->options & MIPS_CPU_WATCH) == 0) + return; + /* + * Check which of the I,R and W bits are supported, then + * disable the register. + */ + write_c0_watchlo0(7); + t = read_c0_watchlo0(); + write_c0_watchlo0(0); + c->watch_reg_masks[0] = t & 7; + + /* Write the mask bits and read them back to determine which + * can be used. */ + c->watch_reg_count = 1; + c->watch_reg_use_cnt = 1; + t = read_c0_watchhi0(); + write_c0_watchhi0(t | 0xff8); + t = read_c0_watchhi0(); + c->watch_reg_masks[0] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + write_c0_watchlo1(7); + t = read_c0_watchlo1(); + write_c0_watchlo1(0); + c->watch_reg_masks[1] = t & 7; + + c->watch_reg_count = 2; + c->watch_reg_use_cnt = 2; + t = read_c0_watchhi1(); + write_c0_watchhi1(t | 0xff8); + t = read_c0_watchhi1(); + c->watch_reg_masks[1] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + write_c0_watchlo2(7); + t = read_c0_watchlo2(); + write_c0_watchlo2(0); + c->watch_reg_masks[2] = t & 7; + + c->watch_reg_count = 3; + c->watch_reg_use_cnt = 3; + t = read_c0_watchhi2(); + write_c0_watchhi2(t | 0xff8); + t = read_c0_watchhi2(); + c->watch_reg_masks[2] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + write_c0_watchlo3(7); + t = read_c0_watchlo3(); + write_c0_watchlo3(0); + c->watch_reg_masks[3] = t & 7; + + c->watch_reg_count = 4; + c->watch_reg_use_cnt = 4; + t = read_c0_watchhi3(); + write_c0_watchhi3(t | 0xff8); + t = read_c0_watchhi3(); + c->watch_reg_masks[3] |= (t & 0xff8); + if ((t & 0x80000000) == 0) + return; + + /* We use at most 4, but probe and report up to 8. */ + c->watch_reg_count = 5; + t = read_c0_watchhi4(); + if ((t & 0x80000000) == 0) + return; + + c->watch_reg_count = 6; + t = read_c0_watchhi5(); + if ((t & 0x80000000) == 0) + return; + + c->watch_reg_count = 7; + t = read_c0_watchhi6(); + if ((t & 0x80000000) == 0) + return; + + c->watch_reg_count = 8; +} -- cgit v1.2.3-70-g09d2 From 654f57bfb467996fb730eae96dc30ea4de989fdc Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 23 Sep 2008 00:07:16 -0700 Subject: MIPS: Probe watch registers and report configuration. Probe for watch register characteristics, and report them in /proc/cpuinfo. Signed-off-by: David Daney Signed-off-by: Ralf Baechle --- arch/mips/kernel/cpu-probe.c | 2 ++ arch/mips/kernel/proc.c | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index e621fda8ab3..0cf15457eca 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, @@ -677,6 +678,7 @@ static inline void spram_config(void) {} static inline void cpu_probe_mips(struct cpuinfo_mips *c) { decode_configs(c); + mips_probe_watch_registers(c); switch (c->processor_id & 0xff00) { case PRID_IMP_4KC: c->cputype = CPU_4KC; diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 36f06539824..75bb1300dd7 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -23,6 +23,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) unsigned int version = cpu_data[n].processor_id; unsigned int fp_vers = cpu_data[n].fpu_id; char fmt [64]; + int i; #ifdef CONFIG_SMP if (!cpu_isset(n, cpu_online_map)) @@ -50,8 +51,16 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, "tlb_entries\t\t: %d\n", cpu_data[n].tlbsize); seq_printf(m, "extra interrupt vector\t: %s\n", cpu_has_divec ? "yes" : "no"); - seq_printf(m, "hardware watchpoint\t: %s\n", - cpu_has_watch ? "yes" : "no"); + seq_printf(m, "hardware watchpoint\t: %s", + cpu_has_watch ? "yes, " : "no\n"); + if (cpu_has_watch) { + seq_printf(m, "count: %d, address/irw mask: [", + cpu_data[n].watch_reg_count); + for (i = 0; i < cpu_data[n].watch_reg_count; i++) + seq_printf(m, "%s0x%04x", i ? ", " : "" , + cpu_data[n].watch_reg_masks[i]); + seq_printf(m, "]\n"); + } seq_printf(m, "ASEs implemented\t:%s%s%s%s%s%s\n", cpu_has_mips16 ? " mips16" : "", cpu_has_mdmx ? " mdmx" : "", -- cgit v1.2.3-70-g09d2 From b67b2b703047bfaf62687ca64c9486599b609ef0 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 23 Sep 2008 00:08:45 -0700 Subject: MIPS: Watch exception handling for HARDWARE_WATCHPOINTS. Here we hook up the watch exception handler so that it sends SIGTRAP when the hardware watch registers are triggered. Signed-off-by: David Daney Signed-off-by: Ralf Baechle --- arch/mips/kernel/genex.S | 4 ++++ arch/mips/kernel/traps.c | 24 +++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index 01dcbe38fa0..757d48f0d80 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -453,7 +453,11 @@ NESTED(nmi_handler, PT_SIZE, sp) BUILD_HANDLER tr tr sti silent /* #13 */ BUILD_HANDLER fpe fpe fpe silent /* #15 */ BUILD_HANDLER mdmx mdmx sti silent /* #22 */ +#ifdef CONFIG_HARDWARE_WATCHPOINTS + BUILD_HANDLER watch watch sti silent /* #23 */ +#else BUILD_HANDLER watch watch sti verbose /* #23 */ +#endif BUILD_HANDLER mcheck mcheck cli verbose /* #24 */ BUILD_HANDLER mt mt sti silent /* #25 */ BUILD_HANDLER dsp dsp sti silent /* #26 */ diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index b602ac6eb47..80b9e070c20 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -912,13 +913,26 @@ asmlinkage void do_mdmx(struct pt_regs *regs) asmlinkage void do_watch(struct pt_regs *regs) { + u32 cause; + /* - * We use the watch exception where available to detect stack - * overflows. + * Clear WP (bit 22) bit of cause register so we don't loop + * forever. */ - dump_tlb_all(); - show_regs(regs); - panic("Caught WATCH exception - probably caused by stack overflow."); + cause = read_c0_cause(); + cause &= ~(1 << 22); + write_c0_cause(cause); + + /* + * If the current thread has the watch registers loaded, save + * their values and send SIGTRAP. Otherwise another thread + * left the registers set, clear them and continue. + */ + if (test_tsk_thread_flag(current, TIF_LOAD_WATCH)) { + mips_read_watch_registers(); + force_sig(SIGTRAP, current); + } else + mips_clear_watch_registers(); } asmlinkage void do_mcheck(struct pt_regs *regs) -- cgit v1.2.3-70-g09d2 From 0926bf953ee79b8f139741b442e5a18520f81705 Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 23 Sep 2008 00:11:26 -0700 Subject: MIPS: Ptrace support for HARDWARE_WATCHPOINTS This is the final part of the watch register patch. Here we hook up ptrace so that the user space debugger (gdb), can set and read the registers. Signed-off-by: David Daney Signed-off-by: Ralf Baechle --- arch/mips/include/asm/ptrace.h | 46 +++++++++++++++++++ arch/mips/kernel/ptrace.c | 100 ++++++++++++++++++++++++++++++++++++++++- arch/mips/kernel/ptrace32.c | 10 +++++ 3 files changed, 155 insertions(+), 1 deletion(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h index c00cca24dae..cd5e8a47812 100644 --- a/arch/mips/include/asm/ptrace.h +++ b/arch/mips/include/asm/ptrace.h @@ -74,11 +74,57 @@ struct pt_regs { #define PTRACE_POKEDATA_3264 0xc3 #define PTRACE_GET_THREAD_AREA_3264 0xc4 +/* Read and write watchpoint registers. */ +enum pt_watch_style { + pt_watch_style_mips32, + pt_watch_style_mips64 +}; +struct mips32_watch_regs { + uint32_t watchlo[8]; + /* Lower 16 bits of watchhi. */ + uint16_t watchhi[8]; + /* Valid mask and I R W bits. + * bit 0 -- 1 if W bit is usable. + * bit 1 -- 1 if R bit is usable. + * bit 2 -- 1 if I bit is usable. + * bits 3 - 11 -- Valid watchhi mask bits. + */ + uint16_t watch_masks[8]; + /* The number of valid watch register pairs. */ + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct mips64_watch_regs { + uint64_t watchlo[8]; + uint16_t watchhi[8]; + uint16_t watch_masks[8]; + uint32_t num_valid; +} __attribute__((aligned(8))); + +struct pt_watch_regs { + enum pt_watch_style style; + union { + struct mips32_watch_regs mips32; + struct mips32_watch_regs mips64; + }; +}; + +#define PTRACE_GET_WATCH_REGS 0xd0 +#define PTRACE_SET_WATCH_REGS 0xd1 + #ifdef __KERNEL__ +#include #include #include +struct task_struct; + +extern int ptrace_get_watch_regs(struct task_struct *child, + struct pt_watch_regs __user *addr); +extern int ptrace_set_watch_regs(struct task_struct *child, + struct pt_watch_regs __user *addr); + /* * Does the process account for user or for system time? */ diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 96ffc9c6d19..054861ccb4d 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -46,7 +46,8 @@ */ void ptrace_disable(struct task_struct *child) { - /* Nothing to do.. */ + /* Don't load the watchpoint registers for the ex-child. */ + clear_tsk_thread_flag(child, TIF_LOAD_WATCH); } /* @@ -167,6 +168,93 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) return 0; } +int ptrace_get_watch_regs(struct task_struct *child, + struct pt_watch_regs __user *addr) +{ + enum pt_watch_style style; + int i; + + if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0) + return -EIO; + if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs))) + return -EIO; + +#ifdef CONFIG_32BIT + style = pt_watch_style_mips32; +#define WATCH_STYLE mips32 +#else + style = pt_watch_style_mips64; +#define WATCH_STYLE mips64 +#endif + + __put_user(style, &addr->style); + __put_user(current_cpu_data.watch_reg_use_cnt, + &addr->WATCH_STYLE.num_valid); + for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) { + __put_user(child->thread.watch.mips3264.watchlo[i], + &addr->WATCH_STYLE.watchlo[i]); + __put_user(child->thread.watch.mips3264.watchhi[i] & 0xfff, + &addr->WATCH_STYLE.watchhi[i]); + __put_user(current_cpu_data.watch_reg_masks[i], + &addr->WATCH_STYLE.watch_masks[i]); + } + for (; i < 8; i++) { + __put_user(0, &addr->WATCH_STYLE.watchlo[i]); + __put_user(0, &addr->WATCH_STYLE.watchhi[i]); + __put_user(0, &addr->WATCH_STYLE.watch_masks[i]); + } + + return 0; +} + +int ptrace_set_watch_regs(struct task_struct *child, + struct pt_watch_regs __user *addr) +{ + int i; + int watch_active = 0; + unsigned long lt[NUM_WATCH_REGS]; + u16 ht[NUM_WATCH_REGS]; + + if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0) + return -EIO; + if (!access_ok(VERIFY_READ, addr, sizeof(struct pt_watch_regs))) + return -EIO; + /* Check the values. */ + for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) { + __get_user(lt[i], &addr->WATCH_STYLE.watchlo[i]); +#ifdef CONFIG_32BIT + if (lt[i] & __UA_LIMIT) + return -EINVAL; +#else + if (test_tsk_thread_flag(child, TIF_32BIT_ADDR)) { + if (lt[i] & 0xffffffff80000000UL) + return -EINVAL; + } else { + if (lt[i] & __UA_LIMIT) + return -EINVAL; + } +#endif + __get_user(ht[i], &addr->WATCH_STYLE.watchhi[i]); + if (ht[i] & ~0xff8) + return -EINVAL; + } + /* Install them. */ + for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) { + if (lt[i] & 7) + watch_active = 1; + child->thread.watch.mips3264.watchlo[i] = lt[i]; + /* Set the G bit. */ + child->thread.watch.mips3264.watchhi[i] = ht[i]; + } + + if (watch_active) + set_tsk_thread_flag(child, TIF_LOAD_WATCH); + else + clear_tsk_thread_flag(child, TIF_LOAD_WATCH); + + return 0; +} + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; @@ -440,6 +528,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) (unsigned long __user *) data); break; + case PTRACE_GET_WATCH_REGS: + ret = ptrace_get_watch_regs(child, + (struct pt_watch_regs __user *) addr); + break; + + case PTRACE_SET_WATCH_REGS: + ret = ptrace_set_watch_regs(child, + (struct pt_watch_regs __user *) addr); + break; + default: ret = ptrace_request(child, request, addr, data); break; diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index cac56a8c867..e45105e3ef0 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -387,6 +387,16 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, (unsigned long __user *) (unsigned long) data); break; + case PTRACE_GET_WATCH_REGS: + ret = ptrace_get_watch_regs(child, + (struct pt_watch_regs __user *) (unsigned long) addr); + break; + + case PTRACE_SET_WATCH_REGS: + ret = ptrace_set_watch_regs(child, + (struct pt_watch_regs __user *) (unsigned long) addr); + break; + default: ret = ptrace_request(child, request, addr, data); break; -- cgit v1.2.3-70-g09d2 From d302d05cf4d25f4fbbec0f9338fac13ee0c0ad89 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 11 Oct 2008 16:18:57 +0100 Subject: MIPS: Move ptrace prototypes to ptrace.h Signed-off-by: Ralf Baechle --- arch/mips/include/asm/ptrace.h | 6 ++++++ arch/mips/kernel/ptrace32.c | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'arch/mips/kernel') diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h index cd5e8a47812..9c22571b160 100644 --- a/arch/mips/include/asm/ptrace.h +++ b/arch/mips/include/asm/ptrace.h @@ -120,6 +120,12 @@ struct pt_watch_regs { struct task_struct; +extern int ptrace_getregs(struct task_struct *child, __s64 __user *data); +extern int ptrace_setregs(struct task_struct *child, __s64 __user *data); + +extern int ptrace_getfpregs(struct task_struct *child, __u32 __user *data); +extern int ptrace_setfpregs(struct task_struct *child, __u32 __user *data); + extern int ptrace_get_watch_regs(struct task_struct *child, struct pt_watch_regs __user *addr); extern int ptrace_set_watch_regs(struct task_struct *child, diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index e45105e3ef0..1ca34104e59 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c @@ -37,12 +37,6 @@ #include #include -int ptrace_getregs(struct task_struct *child, __s64 __user *data); -int ptrace_setregs(struct task_struct *child, __s64 __user *data); - -int ptrace_getfpregs(struct task_struct *child, __u32 __user *data); -int ptrace_setfpregs(struct task_struct *child, __u32 __user *data); - /* * Tracing a 32-bit process with a 64-bit strace and vice versa will not * work. I don't know how to fix this. -- cgit v1.2.3-70-g09d2