diff options
author | David S. Miller <davem@davemloft.net> | 2008-08-12 21:33:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-08-12 21:33:56 -0400 |
commit | 4f70f7a91bffdcc39f088748dc678953eb9a3fbd (patch) | |
tree | 934591a9518fbed87c14b758a1744cc30c9dfbb8 | |
parent | e34456825de0d3ac4c4e8fe0bdc6b599404ea06f (diff) |
sparc64: Implement IRQ stacks.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc/include/asm/irq_64.h | 4 | ||||
-rw-r--r-- | arch/sparc64/kernel/irq.c | 52 | ||||
-rw-r--r-- | arch/sparc64/kernel/kstack.h | 58 | ||||
-rw-r--r-- | arch/sparc64/kernel/process.c | 27 | ||||
-rw-r--r-- | arch/sparc64/kernel/stacktrace.c | 10 | ||||
-rw-r--r-- | arch/sparc64/kernel/traps.c | 7 | ||||
-rw-r--r-- | arch/sparc64/lib/mcount.S | 22 | ||||
-rw-r--r-- | arch/sparc64/mm/init.c | 11 |
8 files changed, 161 insertions, 30 deletions
diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index 3473e25231d..e3dd9303643 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h | |||
@@ -93,4 +93,8 @@ static inline unsigned long get_softint(void) | |||
93 | void __trigger_all_cpu_backtrace(void); | 93 | void __trigger_all_cpu_backtrace(void); |
94 | #define trigger_all_cpu_backtrace() __trigger_all_cpu_backtrace() | 94 | #define trigger_all_cpu_backtrace() __trigger_all_cpu_backtrace() |
95 | 95 | ||
96 | extern void *hardirq_stack[NR_CPUS]; | ||
97 | extern void *softirq_stack[NR_CPUS]; | ||
98 | #define __ARCH_HAS_DO_SOFTIRQ | ||
99 | |||
96 | #endif | 100 | #endif |
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index ba43d85e8dd..9b6689d9d57 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c | |||
@@ -682,10 +682,32 @@ void ack_bad_irq(unsigned int virt_irq) | |||
682 | ino, virt_irq); | 682 | ino, virt_irq); |
683 | } | 683 | } |
684 | 684 | ||
685 | void *hardirq_stack[NR_CPUS]; | ||
686 | void *softirq_stack[NR_CPUS]; | ||
687 | |||
688 | static __attribute__((always_inline)) void *set_hardirq_stack(void) | ||
689 | { | ||
690 | void *orig_sp, *sp = hardirq_stack[smp_processor_id()]; | ||
691 | |||
692 | __asm__ __volatile__("mov %%sp, %0" : "=r" (orig_sp)); | ||
693 | if (orig_sp < sp || | ||
694 | orig_sp > (sp + THREAD_SIZE)) { | ||
695 | sp += THREAD_SIZE - 192 - STACK_BIAS; | ||
696 | __asm__ __volatile__("mov %0, %%sp" : : "r" (sp)); | ||
697 | } | ||
698 | |||
699 | return orig_sp; | ||
700 | } | ||
701 | static __attribute__((always_inline)) void restore_hardirq_stack(void *orig_sp) | ||
702 | { | ||
703 | __asm__ __volatile__("mov %0, %%sp" : : "r" (orig_sp)); | ||
704 | } | ||
705 | |||
685 | void handler_irq(int irq, struct pt_regs *regs) | 706 | void handler_irq(int irq, struct pt_regs *regs) |
686 | { | 707 | { |
687 | unsigned long pstate, bucket_pa; | 708 | unsigned long pstate, bucket_pa; |
688 | struct pt_regs *old_regs; | 709 | struct pt_regs *old_regs; |
710 | void *orig_sp; | ||
689 | 711 | ||
690 | clear_softint(1 << irq); | 712 | clear_softint(1 << irq); |
691 | 713 | ||
@@ -703,6 +725,8 @@ void handler_irq(int irq, struct pt_regs *regs) | |||
703 | "i" (PSTATE_IE) | 725 | "i" (PSTATE_IE) |
704 | : "memory"); | 726 | : "memory"); |
705 | 727 | ||
728 | orig_sp = set_hardirq_stack(); | ||
729 | |||
706 | while (bucket_pa) { | 730 | while (bucket_pa) { |
707 | struct irq_desc *desc; | 731 | struct irq_desc *desc; |
708 | unsigned long next_pa; | 732 | unsigned long next_pa; |
@@ -719,10 +743,38 @@ void handler_irq(int irq, struct pt_regs *regs) | |||
719 | bucket_pa = next_pa; | 743 | bucket_pa = next_pa; |
720 | } | 744 | } |
721 | 745 | ||
746 | restore_hardirq_stack(orig_sp); | ||
747 | |||
722 | irq_exit(); | 748 | irq_exit(); |
723 | set_irq_regs(old_regs); | 749 | set_irq_regs(old_regs); |
724 | } | 750 | } |
725 | 751 | ||
752 | void do_softirq(void) | ||
753 | { | ||
754 | unsigned long flags; | ||
755 | |||
756 | if (in_interrupt()) | ||
757 | return; | ||
758 | |||
759 | local_irq_save(flags); | ||
760 | |||
761 | if (local_softirq_pending()) { | ||
762 | void *orig_sp, *sp = softirq_stack[smp_processor_id()]; | ||
763 | |||
764 | sp += THREAD_SIZE - 192 - STACK_BIAS; | ||
765 | |||
766 | __asm__ __volatile__("mov %%sp, %0\n\t" | ||
767 | "mov %1, %%sp" | ||
768 | : "=&r" (orig_sp) | ||
769 | : "r" (sp)); | ||
770 | __do_softirq(); | ||
771 | __asm__ __volatile__("mov %0, %%sp" | ||
772 | : : "r" (orig_sp)); | ||
773 | } | ||
774 | |||
775 | local_irq_restore(flags); | ||
776 | } | ||
777 | |||
726 | #ifdef CONFIG_HOTPLUG_CPU | 778 | #ifdef CONFIG_HOTPLUG_CPU |
727 | void fixup_irqs(void) | 779 | void fixup_irqs(void) |
728 | { | 780 | { |
diff --git a/arch/sparc64/kernel/kstack.h b/arch/sparc64/kernel/kstack.h new file mode 100644 index 00000000000..43909d5680e --- /dev/null +++ b/arch/sparc64/kernel/kstack.h | |||
@@ -0,0 +1,58 @@ | |||
1 | #ifndef _KSTACK_H | ||
2 | #define _KSTACK_H | ||
3 | |||
4 | #include <linux/thread_info.h> | ||
5 | #include <linux/sched.h> | ||
6 | #include <asm/ptrace.h> | ||
7 | #include <asm/irq.h> | ||
8 | |||
9 | /* SP must be STACK_BIAS adjusted already. */ | ||
10 | static inline bool kstack_valid(struct thread_info *tp, unsigned long sp) | ||
11 | { | ||
12 | unsigned long base = (unsigned long) tp; | ||
13 | |||
14 | if (sp >= (base + sizeof(struct thread_info)) && | ||
15 | sp <= (base + THREAD_SIZE - sizeof(struct sparc_stackf))) | ||
16 | return true; | ||
17 | |||
18 | base = (unsigned long) hardirq_stack[tp->cpu]; | ||
19 | if (sp >= base && | ||
20 | sp <= (base + THREAD_SIZE - sizeof(struct sparc_stackf))) | ||
21 | return true; | ||
22 | base = (unsigned long) softirq_stack[tp->cpu]; | ||
23 | if (sp >= base && | ||
24 | sp <= (base + THREAD_SIZE - sizeof(struct sparc_stackf))) | ||
25 | return true; | ||
26 | |||
27 | return false; | ||
28 | } | ||
29 | |||
30 | /* Does "regs" point to a valid pt_regs trap frame? */ | ||
31 | static inline bool kstack_is_trap_frame(struct thread_info *tp, struct pt_regs *regs) | ||
32 | { | ||
33 | unsigned long base = (unsigned long) tp; | ||
34 | unsigned long addr = (unsigned long) regs; | ||
35 | |||
36 | if (addr >= base && | ||
37 | addr <= (base + THREAD_SIZE - sizeof(*regs))) | ||
38 | goto check_magic; | ||
39 | |||
40 | base = (unsigned long) hardirq_stack[tp->cpu]; | ||
41 | if (addr >= base && | ||
42 | addr <= (base + THREAD_SIZE - sizeof(*regs))) | ||
43 | goto check_magic; | ||
44 | base = (unsigned long) softirq_stack[tp->cpu]; | ||
45 | if (addr >= base && | ||
46 | addr <= (base + THREAD_SIZE - sizeof(*regs))) | ||
47 | goto check_magic; | ||
48 | |||
49 | return false; | ||
50 | |||
51 | check_magic: | ||
52 | if ((regs->magic & ~0x1ff) == PT_REGS_MAGIC) | ||
53 | return true; | ||
54 | return false; | ||
55 | |||
56 | } | ||
57 | |||
58 | #endif /* _KSTACK_H */ | ||
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 7f5debdc5fe..15f4178592e 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c | |||
@@ -52,6 +52,8 @@ | |||
52 | #include <asm/irq_regs.h> | 52 | #include <asm/irq_regs.h> |
53 | #include <asm/smp.h> | 53 | #include <asm/smp.h> |
54 | 54 | ||
55 | #include "kstack.h" | ||
56 | |||
55 | static void sparc64_yield(int cpu) | 57 | static void sparc64_yield(int cpu) |
56 | { | 58 | { |
57 | if (tlb_type != hypervisor) | 59 | if (tlb_type != hypervisor) |
@@ -235,19 +237,6 @@ void show_regs(struct pt_regs *regs) | |||
235 | struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; | 237 | struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; |
236 | static DEFINE_SPINLOCK(global_reg_snapshot_lock); | 238 | static DEFINE_SPINLOCK(global_reg_snapshot_lock); |
237 | 239 | ||
238 | static bool kstack_valid(struct thread_info *tp, struct reg_window *rw) | ||
239 | { | ||
240 | unsigned long thread_base, fp; | ||
241 | |||
242 | thread_base = (unsigned long) tp; | ||
243 | fp = (unsigned long) rw; | ||
244 | |||
245 | if (fp < (thread_base + sizeof(struct thread_info)) || | ||
246 | fp >= (thread_base + THREAD_SIZE)) | ||
247 | return false; | ||
248 | return true; | ||
249 | } | ||
250 | |||
251 | static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, | 240 | static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, |
252 | int this_cpu) | 241 | int this_cpu) |
253 | { | 242 | { |
@@ -264,11 +253,11 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, | |||
264 | 253 | ||
265 | rw = (struct reg_window *) | 254 | rw = (struct reg_window *) |
266 | (regs->u_regs[UREG_FP] + STACK_BIAS); | 255 | (regs->u_regs[UREG_FP] + STACK_BIAS); |
267 | if (kstack_valid(tp, rw)) { | 256 | if (kstack_valid(tp, (unsigned long) rw)) { |
268 | global_reg_snapshot[this_cpu].i7 = rw->ins[7]; | 257 | global_reg_snapshot[this_cpu].i7 = rw->ins[7]; |
269 | rw = (struct reg_window *) | 258 | rw = (struct reg_window *) |
270 | (rw->ins[6] + STACK_BIAS); | 259 | (rw->ins[6] + STACK_BIAS); |
271 | if (kstack_valid(tp, rw)) | 260 | if (kstack_valid(tp, (unsigned long) rw)) |
272 | global_reg_snapshot[this_cpu].rpc = rw->ins[7]; | 261 | global_reg_snapshot[this_cpu].rpc = rw->ins[7]; |
273 | } | 262 | } |
274 | } else { | 263 | } else { |
@@ -828,7 +817,7 @@ out: | |||
828 | unsigned long get_wchan(struct task_struct *task) | 817 | unsigned long get_wchan(struct task_struct *task) |
829 | { | 818 | { |
830 | unsigned long pc, fp, bias = 0; | 819 | unsigned long pc, fp, bias = 0; |
831 | unsigned long thread_info_base; | 820 | struct thread_info *tp; |
832 | struct reg_window *rw; | 821 | struct reg_window *rw; |
833 | unsigned long ret = 0; | 822 | unsigned long ret = 0; |
834 | int count = 0; | 823 | int count = 0; |
@@ -837,14 +826,12 @@ unsigned long get_wchan(struct task_struct *task) | |||
837 | task->state == TASK_RUNNING) | 826 | task->state == TASK_RUNNING) |
838 | goto out; | 827 | goto out; |
839 | 828 | ||
840 | thread_info_base = (unsigned long) task_stack_page(task); | 829 | tp = task_thread_info(task); |
841 | bias = STACK_BIAS; | 830 | bias = STACK_BIAS; |
842 | fp = task_thread_info(task)->ksp + bias; | 831 | fp = task_thread_info(task)->ksp + bias; |
843 | 832 | ||
844 | do { | 833 | do { |
845 | /* Bogus frame pointer? */ | 834 | if (!kstack_valid(tp, fp)) |
846 | if (fp < (thread_info_base + sizeof(struct thread_info)) || | ||
847 | fp >= (thread_info_base + THREAD_SIZE)) | ||
848 | break; | 835 | break; |
849 | rw = (struct reg_window *) fp; | 836 | rw = (struct reg_window *) fp; |
850 | pc = rw->ins[7]; | 837 | pc = rw->ins[7]; |
diff --git a/arch/sparc64/kernel/stacktrace.c b/arch/sparc64/kernel/stacktrace.c index e9d7f0660f2..237e7f8a40a 100644 --- a/arch/sparc64/kernel/stacktrace.c +++ b/arch/sparc64/kernel/stacktrace.c | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <asm/ptrace.h> | 5 | #include <asm/ptrace.h> |
6 | #include <asm/stacktrace.h> | 6 | #include <asm/stacktrace.h> |
7 | 7 | ||
8 | #include "kstack.h" | ||
9 | |||
8 | void save_stack_trace(struct stack_trace *trace) | 10 | void save_stack_trace(struct stack_trace *trace) |
9 | { | 11 | { |
10 | unsigned long ksp, fp, thread_base; | 12 | unsigned long ksp, fp, thread_base; |
@@ -24,17 +26,13 @@ void save_stack_trace(struct stack_trace *trace) | |||
24 | struct pt_regs *regs; | 26 | struct pt_regs *regs; |
25 | unsigned long pc; | 27 | unsigned long pc; |
26 | 28 | ||
27 | /* Bogus frame pointer? */ | 29 | if (!kstack_valid(tp, fp)) |
28 | if (fp < (thread_base + sizeof(struct thread_info)) || | ||
29 | fp > (thread_base + THREAD_SIZE - sizeof(struct sparc_stackf))) | ||
30 | break; | 30 | break; |
31 | 31 | ||
32 | sf = (struct sparc_stackf *) fp; | 32 | sf = (struct sparc_stackf *) fp; |
33 | regs = (struct pt_regs *) (sf + 1); | 33 | regs = (struct pt_regs *) (sf + 1); |
34 | 34 | ||
35 | if (((unsigned long)regs <= | 35 | if (kstack_is_trap_frame(tp, regs)) { |
36 | (thread_base + THREAD_SIZE - sizeof(*regs))) && | ||
37 | (regs->magic & ~0x1ff) == PT_REGS_MAGIC) { | ||
38 | if (!(regs->tstate & TSTATE_PRIV)) | 36 | if (!(regs->tstate & TSTATE_PRIV)) |
39 | break; | 37 | break; |
40 | pc = regs->tpc; | 38 | pc = regs->tpc; |
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 404e8561e2d..3d924121c79 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <asm/prom.h> | 39 | #include <asm/prom.h> |
40 | 40 | ||
41 | #include "entry.h" | 41 | #include "entry.h" |
42 | #include "kstack.h" | ||
42 | 43 | ||
43 | /* When an irrecoverable trap occurs at tl > 0, the trap entry | 44 | /* When an irrecoverable trap occurs at tl > 0, the trap entry |
44 | * code logs the trap state registers at every level in the trap | 45 | * code logs the trap state registers at every level in the trap |
@@ -2115,14 +2116,12 @@ void show_stack(struct task_struct *tsk, unsigned long *_ksp) | |||
2115 | struct pt_regs *regs; | 2116 | struct pt_regs *regs; |
2116 | unsigned long pc; | 2117 | unsigned long pc; |
2117 | 2118 | ||
2118 | /* Bogus frame pointer? */ | 2119 | if (!kstack_valid(tp, fp)) |
2119 | if (fp < (thread_base + sizeof(struct thread_info)) || | ||
2120 | fp >= (thread_base + THREAD_SIZE)) | ||
2121 | break; | 2120 | break; |
2122 | sf = (struct sparc_stackf *) fp; | 2121 | sf = (struct sparc_stackf *) fp; |
2123 | regs = (struct pt_regs *) (sf + 1); | 2122 | regs = (struct pt_regs *) (sf + 1); |
2124 | 2123 | ||
2125 | if ((regs->magic & ~0x1ff) == PT_REGS_MAGIC) { | 2124 | if (kstack_is_trap_frame(tp, regs)) { |
2126 | if (!(regs->tstate & TSTATE_PRIV)) | 2125 | if (!(regs->tstate & TSTATE_PRIV)) |
2127 | break; | 2126 | break; |
2128 | pc = regs->tpc; | 2127 | pc = regs->tpc; |
diff --git a/arch/sparc64/lib/mcount.S b/arch/sparc64/lib/mcount.S index 734caf0cec0..fad90ddb3a2 100644 --- a/arch/sparc64/lib/mcount.S +++ b/arch/sparc64/lib/mcount.S | |||
@@ -49,6 +49,28 @@ mcount: | |||
49 | cmp %sp, %g3 | 49 | cmp %sp, %g3 |
50 | bg,pt %xcc, 1f | 50 | bg,pt %xcc, 1f |
51 | nop | 51 | nop |
52 | lduh [%g6 + TI_CPU], %g1 | ||
53 | sethi %hi(hardirq_stack), %g3 | ||
54 | or %g3, %lo(hardirq_stack), %g3 | ||
55 | sllx %g1, 3, %g1 | ||
56 | ldx [%g3 + %g1], %g7 | ||
57 | sub %g7, STACK_BIAS, %g7 | ||
58 | cmp %sp, %g7 | ||
59 | bleu,pt %xcc, 2f | ||
60 | sethi %hi(THREAD_SIZE), %g3 | ||
61 | add %g7, %g3, %g7 | ||
62 | cmp %sp, %g7 | ||
63 | blu,pn %xcc, 1f | ||
64 | 2: sethi %hi(softirq_stack), %g3 | ||
65 | or %g3, %lo(softirq_stack), %g3 | ||
66 | ldx [%g3 + %g1], %g7 | ||
67 | cmp %sp, %g7 | ||
68 | bleu,pt %xcc, 2f | ||
69 | sethi %hi(THREAD_SIZE), %g3 | ||
70 | add %g7, %g3, %g7 | ||
71 | cmp %sp, %g7 | ||
72 | blu,pn %xcc, 1f | ||
73 | nop | ||
52 | /* If we are already on ovstack, don't hop onto it | 74 | /* If we are already on ovstack, don't hop onto it |
53 | * again, we are already trying to output the stack overflow | 75 | * again, we are already trying to output the stack overflow |
54 | * message. | 76 | * message. |
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 4e821b3ecb0..217de3ea29e 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include <asm/sstate.h> | 49 | #include <asm/sstate.h> |
50 | #include <asm/mdesc.h> | 50 | #include <asm/mdesc.h> |
51 | #include <asm/cpudata.h> | 51 | #include <asm/cpudata.h> |
52 | #include <asm/irq.h> | ||
52 | 53 | ||
53 | #define MAX_PHYS_ADDRESS (1UL << 42UL) | 54 | #define MAX_PHYS_ADDRESS (1UL << 42UL) |
54 | #define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL) | 55 | #define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL) |
@@ -1771,6 +1772,16 @@ void __init paging_init(void) | |||
1771 | if (tlb_type == hypervisor) | 1772 | if (tlb_type == hypervisor) |
1772 | sun4v_mdesc_init(); | 1773 | sun4v_mdesc_init(); |
1773 | 1774 | ||
1775 | /* Once the OF device tree and MDESC have been setup, we know | ||
1776 | * the list of possible cpus. Therefore we can allocate the | ||
1777 | * IRQ stacks. | ||
1778 | */ | ||
1779 | for_each_possible_cpu(i) { | ||
1780 | /* XXX Use node local allocations... XXX */ | ||
1781 | softirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE)); | ||
1782 | hardirq_stack[i] = __va(lmb_alloc(THREAD_SIZE, THREAD_SIZE)); | ||
1783 | } | ||
1784 | |||
1774 | /* Setup bootmem... */ | 1785 | /* Setup bootmem... */ |
1775 | last_valid_pfn = end_pfn = bootmem_init(phys_base); | 1786 | last_valid_pfn = end_pfn = bootmem_init(phys_base); |
1776 | 1787 | ||