diff options
Diffstat (limited to 'arch/parisc/kernel/irq.c')
| -rw-r--r-- | arch/parisc/kernel/irq.c | 122 |
1 files changed, 107 insertions, 15 deletions
diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index e255db0bb761..2e6443b1e922 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c | |||
| @@ -27,11 +27,11 @@ | |||
| 27 | #include <linux/interrupt.h> | 27 | #include <linux/interrupt.h> |
| 28 | #include <linux/kernel_stat.h> | 28 | #include <linux/kernel_stat.h> |
| 29 | #include <linux/seq_file.h> | 29 | #include <linux/seq_file.h> |
| 30 | #include <linux/spinlock.h> | ||
| 31 | #include <linux/types.h> | 30 | #include <linux/types.h> |
| 32 | #include <asm/io.h> | 31 | #include <asm/io.h> |
| 33 | 32 | ||
| 34 | #include <asm/smp.h> | 33 | #include <asm/smp.h> |
| 34 | #include <asm/ldcw.h> | ||
| 35 | 35 | ||
| 36 | #undef PARISC_IRQ_CR16_COUNTS | 36 | #undef PARISC_IRQ_CR16_COUNTS |
| 37 | 37 | ||
| @@ -166,22 +166,36 @@ int arch_show_interrupts(struct seq_file *p, int prec) | |||
| 166 | seq_printf(p, "%*s: ", prec, "STK"); | 166 | seq_printf(p, "%*s: ", prec, "STK"); |
| 167 | for_each_online_cpu(j) | 167 | for_each_online_cpu(j) |
| 168 | seq_printf(p, "%10u ", irq_stats(j)->kernel_stack_usage); | 168 | seq_printf(p, "%10u ", irq_stats(j)->kernel_stack_usage); |
| 169 | seq_printf(p, " Kernel stack usage\n"); | 169 | seq_puts(p, " Kernel stack usage\n"); |
| 170 | # ifdef CONFIG_IRQSTACKS | ||
| 171 | seq_printf(p, "%*s: ", prec, "IST"); | ||
| 172 | for_each_online_cpu(j) | ||
| 173 | seq_printf(p, "%10u ", irq_stats(j)->irq_stack_usage); | ||
| 174 | seq_puts(p, " Interrupt stack usage\n"); | ||
| 175 | # endif | ||
| 170 | #endif | 176 | #endif |
| 171 | #ifdef CONFIG_SMP | 177 | #ifdef CONFIG_SMP |
| 172 | seq_printf(p, "%*s: ", prec, "RES"); | 178 | seq_printf(p, "%*s: ", prec, "RES"); |
| 173 | for_each_online_cpu(j) | 179 | for_each_online_cpu(j) |
| 174 | seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count); | 180 | seq_printf(p, "%10u ", irq_stats(j)->irq_resched_count); |
| 175 | seq_printf(p, " Rescheduling interrupts\n"); | 181 | seq_puts(p, " Rescheduling interrupts\n"); |
| 176 | seq_printf(p, "%*s: ", prec, "CAL"); | 182 | seq_printf(p, "%*s: ", prec, "CAL"); |
| 177 | for_each_online_cpu(j) | 183 | for_each_online_cpu(j) |
| 178 | seq_printf(p, "%10u ", irq_stats(j)->irq_call_count); | 184 | seq_printf(p, "%10u ", irq_stats(j)->irq_call_count); |
| 179 | seq_printf(p, " Function call interrupts\n"); | 185 | seq_puts(p, " Function call interrupts\n"); |
| 180 | #endif | 186 | #endif |
| 187 | seq_printf(p, "%*s: ", prec, "UAH"); | ||
| 188 | for_each_online_cpu(j) | ||
| 189 | seq_printf(p, "%10u ", irq_stats(j)->irq_unaligned_count); | ||
| 190 | seq_puts(p, " Unaligned access handler traps\n"); | ||
| 191 | seq_printf(p, "%*s: ", prec, "FPA"); | ||
| 192 | for_each_online_cpu(j) | ||
| 193 | seq_printf(p, "%10u ", irq_stats(j)->irq_fpassist_count); | ||
| 194 | seq_puts(p, " Floating point assist traps\n"); | ||
| 181 | seq_printf(p, "%*s: ", prec, "TLB"); | 195 | seq_printf(p, "%*s: ", prec, "TLB"); |
| 182 | for_each_online_cpu(j) | 196 | for_each_online_cpu(j) |
| 183 | seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count); | 197 | seq_printf(p, "%10u ", irq_stats(j)->irq_tlb_count); |
| 184 | seq_printf(p, " TLB shootdowns\n"); | 198 | seq_puts(p, " TLB shootdowns\n"); |
| 185 | return 0; | 199 | return 0; |
| 186 | } | 200 | } |
| 187 | 201 | ||
| @@ -366,6 +380,24 @@ static inline int eirr_to_irq(unsigned long eirr) | |||
| 366 | return (BITS_PER_LONG - bit) + TIMER_IRQ; | 380 | return (BITS_PER_LONG - bit) + TIMER_IRQ; |
| 367 | } | 381 | } |
| 368 | 382 | ||
| 383 | #ifdef CONFIG_IRQSTACKS | ||
| 384 | /* | ||
| 385 | * IRQ STACK - used for irq handler | ||
| 386 | */ | ||
| 387 | #define IRQ_STACK_SIZE (4096 << 2) /* 16k irq stack size */ | ||
| 388 | |||
| 389 | union irq_stack_union { | ||
| 390 | unsigned long stack[IRQ_STACK_SIZE/sizeof(unsigned long)]; | ||
| 391 | volatile unsigned int slock[4]; | ||
| 392 | volatile unsigned int lock[1]; | ||
| 393 | }; | ||
| 394 | |||
| 395 | DEFINE_PER_CPU(union irq_stack_union, irq_stack_union) = { | ||
| 396 | .slock = { 1,1,1,1 }, | ||
| 397 | }; | ||
| 398 | #endif | ||
| 399 | |||
| 400 | |||
| 369 | int sysctl_panic_on_stackoverflow = 1; | 401 | int sysctl_panic_on_stackoverflow = 1; |
| 370 | 402 | ||
| 371 | static inline void stack_overflow_check(struct pt_regs *regs) | 403 | static inline void stack_overflow_check(struct pt_regs *regs) |
| @@ -378,6 +410,7 @@ static inline void stack_overflow_check(struct pt_regs *regs) | |||
| 378 | unsigned long sp = regs->gr[30]; | 410 | unsigned long sp = regs->gr[30]; |
| 379 | unsigned long stack_usage; | 411 | unsigned long stack_usage; |
| 380 | unsigned int *last_usage; | 412 | unsigned int *last_usage; |
| 413 | int cpu = smp_processor_id(); | ||
| 381 | 414 | ||
| 382 | /* if sr7 != 0, we interrupted a userspace process which we do not want | 415 | /* if sr7 != 0, we interrupted a userspace process which we do not want |
| 383 | * to check for stack overflow. We will only check the kernel stack. */ | 416 | * to check for stack overflow. We will only check the kernel stack. */ |
| @@ -386,7 +419,31 @@ static inline void stack_overflow_check(struct pt_regs *regs) | |||
| 386 | 419 | ||
| 387 | /* calculate kernel stack usage */ | 420 | /* calculate kernel stack usage */ |
| 388 | stack_usage = sp - stack_start; | 421 | stack_usage = sp - stack_start; |
| 389 | last_usage = &per_cpu(irq_stat.kernel_stack_usage, smp_processor_id()); | 422 | #ifdef CONFIG_IRQSTACKS |
| 423 | if (likely(stack_usage <= THREAD_SIZE)) | ||
| 424 | goto check_kernel_stack; /* found kernel stack */ | ||
| 425 | |||
| 426 | /* check irq stack usage */ | ||
| 427 | stack_start = (unsigned long) &per_cpu(irq_stack_union, cpu).stack; | ||
| 428 | stack_usage = sp - stack_start; | ||
| 429 | |||
| 430 | last_usage = &per_cpu(irq_stat.irq_stack_usage, cpu); | ||
| 431 | if (unlikely(stack_usage > *last_usage)) | ||
| 432 | *last_usage = stack_usage; | ||
| 433 | |||
| 434 | if (likely(stack_usage < (IRQ_STACK_SIZE - STACK_MARGIN))) | ||
| 435 | return; | ||
| 436 | |||
| 437 | pr_emerg("stackcheck: %s will most likely overflow irq stack " | ||
| 438 | "(sp:%lx, stk bottom-top:%lx-%lx)\n", | ||
| 439 | current->comm, sp, stack_start, stack_start + IRQ_STACK_SIZE); | ||
| 440 | goto panic_check; | ||
| 441 | |||
| 442 | check_kernel_stack: | ||
| 443 | #endif | ||
| 444 | |||
| 445 | /* check kernel stack usage */ | ||
| 446 | last_usage = &per_cpu(irq_stat.kernel_stack_usage, cpu); | ||
| 390 | 447 | ||
| 391 | if (unlikely(stack_usage > *last_usage)) | 448 | if (unlikely(stack_usage > *last_usage)) |
| 392 | *last_usage = stack_usage; | 449 | *last_usage = stack_usage; |
| @@ -398,31 +455,66 @@ static inline void stack_overflow_check(struct pt_regs *regs) | |||
| 398 | "(sp:%lx, stk bottom-top:%lx-%lx)\n", | 455 | "(sp:%lx, stk bottom-top:%lx-%lx)\n", |
| 399 | current->comm, sp, stack_start, stack_start + THREAD_SIZE); | 456 | current->comm, sp, stack_start, stack_start + THREAD_SIZE); |
| 400 | 457 | ||
| 458 | #ifdef CONFIG_IRQSTACKS | ||
| 459 | panic_check: | ||
| 460 | #endif | ||
| 401 | if (sysctl_panic_on_stackoverflow) | 461 | if (sysctl_panic_on_stackoverflow) |
| 402 | panic("low stack detected by irq handler - check messages\n"); | 462 | panic("low stack detected by irq handler - check messages\n"); |
| 403 | #endif | 463 | #endif |
| 404 | } | 464 | } |
| 405 | 465 | ||
| 406 | #ifdef CONFIG_IRQSTACKS | 466 | #ifdef CONFIG_IRQSTACKS |
| 407 | DEFINE_PER_CPU(union irq_stack_union, irq_stack_union); | 467 | /* in entry.S: */ |
| 468 | void call_on_stack(unsigned long p1, void *func, unsigned long new_stack); | ||
| 408 | 469 | ||
| 409 | static void execute_on_irq_stack(void *func, unsigned long param1) | 470 | static void execute_on_irq_stack(void *func, unsigned long param1) |
| 410 | { | 471 | { |
| 411 | unsigned long *irq_stack_start; | 472 | union irq_stack_union *union_ptr; |
| 412 | unsigned long irq_stack; | 473 | unsigned long irq_stack; |
| 413 | int cpu = smp_processor_id(); | 474 | volatile unsigned int *irq_stack_in_use; |
| 475 | |||
| 476 | union_ptr = &per_cpu(irq_stack_union, smp_processor_id()); | ||
| 477 | irq_stack = (unsigned long) &union_ptr->stack; | ||
| 478 | irq_stack = ALIGN(irq_stack + sizeof(irq_stack_union.slock), | ||
| 479 | 64); /* align for stack frame usage */ | ||
| 414 | 480 | ||
| 415 | irq_stack_start = &per_cpu(irq_stack_union, cpu).stack[0]; | 481 | /* We may be called recursive. If we are already using the irq stack, |
| 416 | irq_stack = (unsigned long) irq_stack_start; | 482 | * just continue to use it. Use spinlocks to serialize |
| 417 | irq_stack = ALIGN(irq_stack, 16); /* align for stack frame usage */ | 483 | * the irq stack usage. |
| 484 | */ | ||
| 485 | irq_stack_in_use = (volatile unsigned int *)__ldcw_align(union_ptr); | ||
| 486 | if (!__ldcw(irq_stack_in_use)) { | ||
| 487 | void (*direct_call)(unsigned long p1) = func; | ||
| 418 | 488 | ||
| 419 | BUG_ON(*irq_stack_start); /* report bug if we were called recursive. */ | 489 | /* We are using the IRQ stack already. |
| 420 | *irq_stack_start = 1; | 490 | * Do direct call on current stack. */ |
| 491 | direct_call(param1); | ||
| 492 | return; | ||
| 493 | } | ||
| 421 | 494 | ||
| 422 | /* This is where we switch to the IRQ stack. */ | 495 | /* This is where we switch to the IRQ stack. */ |
| 423 | call_on_stack(param1, func, irq_stack); | 496 | call_on_stack(param1, func, irq_stack); |
| 424 | 497 | ||
| 425 | *irq_stack_start = 0; | 498 | /* free up irq stack usage. */ |
| 499 | *irq_stack_in_use = 1; | ||
| 500 | } | ||
| 501 | |||
| 502 | asmlinkage void do_softirq(void) | ||
| 503 | { | ||
| 504 | __u32 pending; | ||
| 505 | unsigned long flags; | ||
| 506 | |||
| 507 | if (in_interrupt()) | ||
| 508 | return; | ||
| 509 | |||
| 510 | local_irq_save(flags); | ||
| 511 | |||
| 512 | pending = local_softirq_pending(); | ||
| 513 | |||
| 514 | if (pending) | ||
| 515 | execute_on_irq_stack(__do_softirq, 0); | ||
| 516 | |||
| 517 | local_irq_restore(flags); | ||
| 426 | } | 518 | } |
| 427 | #endif /* CONFIG_IRQSTACKS */ | 519 | #endif /* CONFIG_IRQSTACKS */ |
| 428 | 520 | ||
