Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/irq: Run softirqs off the top of the irq stack

Nowadays, irq_exit() calls __do_softirq() pretty much directly
instead of calling do_softirq() which switches to the decicated
softirq stack.

This has lead to observed stack overflows on powerpc since we call
irq_enter() and irq_exit() outside of the scope that switches to
the irq stack.

This fixes it by moving the stack switching up a level, making
irq_enter() and irq_exit() run off the irq stack.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

+62 -65
+2 -2
arch/powerpc/include/asm/irq.h
··· 69 69 70 70 extern void irq_ctx_init(void); 71 71 extern void call_do_softirq(struct thread_info *tp); 72 - extern int call_handle_irq(int irq, void *p1, 73 - struct thread_info *tp, void *func); 72 + extern void call_do_irq(struct pt_regs *regs, struct thread_info *tp); 74 73 extern void do_IRQ(struct pt_regs *regs); 74 + extern void __do_irq(struct pt_regs *regs); 75 75 76 76 int irq_choose_cpu(const struct cpumask *mask); 77 77
+52 -52
arch/powerpc/kernel/irq.c
··· 441 441 } 442 442 #endif 443 443 444 - static inline void handle_one_irq(unsigned int irq) 445 - { 446 - struct thread_info *curtp, *irqtp; 447 - unsigned long saved_sp_limit; 448 - struct irq_desc *desc; 449 - 450 - desc = irq_to_desc(irq); 451 - if (!desc) 452 - return; 453 - 454 - /* Switch to the irq stack to handle this */ 455 - curtp = current_thread_info(); 456 - irqtp = hardirq_ctx[smp_processor_id()]; 457 - 458 - if (curtp == irqtp) { 459 - /* We're already on the irq stack, just handle it */ 460 - desc->handle_irq(irq, desc); 461 - return; 462 - } 463 - 464 - saved_sp_limit = current->thread.ksp_limit; 465 - 466 - irqtp->task = curtp->task; 467 - irqtp->flags = 0; 468 - 469 - /* Copy the softirq bits in preempt_count so that the 470 - * softirq checks work in the hardirq context. */ 471 - irqtp->preempt_count = (irqtp->preempt_count & ~SOFTIRQ_MASK) | 472 - (curtp->preempt_count & SOFTIRQ_MASK); 473 - 474 - current->thread.ksp_limit = (unsigned long)irqtp + 475 - _ALIGN_UP(sizeof(struct thread_info), 16); 476 - 477 - call_handle_irq(irq, desc, irqtp, desc->handle_irq); 478 - current->thread.ksp_limit = saved_sp_limit; 479 - irqtp->task = NULL; 480 - 481 - /* Set any flag that may have been set on the 482 - * alternate stack 483 - */ 484 - if (irqtp->flags) 485 - set_bits(irqtp->flags, &curtp->flags); 486 - } 487 - 488 444 static inline void check_stack_overflow(void) 489 445 { 490 446 #ifdef CONFIG_DEBUG_STACKOVERFLOW ··· 457 501 #endif 458 502 } 459 503 460 - void do_IRQ(struct pt_regs *regs) 504 + void __do_irq(struct pt_regs *regs) 461 505 { 462 - struct pt_regs *old_regs = set_irq_regs(regs); 506 + struct irq_desc *desc; 463 507 unsigned int irq; 464 508 465 509 irq_enter(); ··· 475 519 */ 476 520 irq = ppc_md.get_irq(); 477 521 478 - /* We can hard enable interrupts now */ 522 + /* We can hard enable interrupts now to allow perf interrupts */ 479 523 may_hard_irq_enable(); 480 524 481 525 /* And finally process it */ 482 - if (irq != NO_IRQ) 483 - handle_one_irq(irq); 484 - else 526 + if (unlikely(irq == NO_IRQ)) 485 527 __get_cpu_var(irq_stat).spurious_irqs++; 528 + else { 529 + desc = irq_to_desc(irq); 530 + if (likely(desc)) 531 + desc->handle_irq(irq, desc); 532 + } 486 533 487 534 trace_irq_exit(regs); 488 535 489 536 irq_exit(); 537 + } 538 + 539 + void do_IRQ(struct pt_regs *regs) 540 + { 541 + struct pt_regs *old_regs = set_irq_regs(regs); 542 + struct thread_info *curtp, *irqtp; 543 + unsigned long saved_sp_limit; 544 + 545 + /* Switch to the irq stack to handle this */ 546 + curtp = current_thread_info(); 547 + irqtp = hardirq_ctx[raw_smp_processor_id()]; 548 + 549 + /* Already there ? */ 550 + if (unlikely(curtp == irqtp)) { 551 + __do_irq(regs); 552 + set_irq_regs(old_regs); 553 + return; 554 + } 555 + 556 + /* Adjust the stack limit */ 557 + saved_sp_limit = current->thread.ksp_limit; 558 + current->thread.ksp_limit = (unsigned long)irqtp + 559 + _ALIGN_UP(sizeof(struct thread_info), 16); 560 + 561 + 562 + /* Prepare the thread_info in the irq stack */ 563 + irqtp->task = curtp->task; 564 + irqtp->flags = 0; 565 + 566 + /* Copy the preempt_count so that the [soft]irq checks work. */ 567 + irqtp->preempt_count = curtp->preempt_count; 568 + 569 + /* Switch stack and call */ 570 + call_do_irq(regs, irqtp); 571 + 572 + /* Restore stack limit */ 573 + current->thread.ksp_limit = saved_sp_limit; 574 + irqtp->task = NULL; 575 + 576 + /* Copy back updates to the thread_info */ 577 + if (irqtp->flags) 578 + set_bits(irqtp->flags, &curtp->flags); 579 + 490 580 set_irq_regs(old_regs); 491 581 } 492 582 ··· 594 592 memset((void *)softirq_ctx[i], 0, THREAD_SIZE); 595 593 tp = softirq_ctx[i]; 596 594 tp->cpu = i; 597 - tp->preempt_count = 0; 598 595 599 596 memset((void *)hardirq_ctx[i], 0, THREAD_SIZE); 600 597 tp = hardirq_ctx[i]; 601 598 tp->cpu = i; 602 - tp->preempt_count = HARDIRQ_OFFSET; 603 599 } 604 600 } 605 601
+4 -5
arch/powerpc/kernel/misc_32.S
··· 47 47 mtlr r0 48 48 blr 49 49 50 - _GLOBAL(call_handle_irq) 50 + _GLOBAL(call_do_irq) 51 51 mflr r0 52 52 stw r0,4(r1) 53 - mtctr r6 54 - stwu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r5) 55 - mr r1,r5 56 - bctrl 53 + stwu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r4) 54 + mr r1,r4 55 + bl __do_irq 57 56 lwz r1,0(r1) 58 57 lwz r0,4(r1) 59 58 mtlr r0
+4 -6
arch/powerpc/kernel/misc_64.S
··· 40 40 mtlr r0 41 41 blr 42 42 43 - _GLOBAL(call_handle_irq) 44 - ld r8,0(r6) 43 + _GLOBAL(call_do_irq) 45 44 mflr r0 46 45 std r0,16(r1) 47 - mtctr r8 48 - stdu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r5) 49 - mr r1,r5 50 - bctrl 46 + stdu r1,THREAD_SIZE-STACK_FRAME_OVERHEAD(r4) 47 + mr r1,r4 48 + bl .__do_irq 51 49 ld r1,0(r1) 52 50 ld r0,16(r1) 53 51 mtlr r0