mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 06:13:30 +07:00
7596abf2e5
Running with CONFIG_DEBUG_SPINLOCK=y can trigger a BUG with the new IRQ stack code: BUG: spinlock lockup suspected on CPU#1 This is due to the IRQ_STACK_TO_TASK_STACK macro incorrectly retrieving the task stack pointer stashed at the top of the IRQ stack. Sayeth James: | Yup, this is what is happening. Its an off-by-one due to broken | thinking about how the stack works. My broken thinking was: | | > top ------------ | > | dummy_lr | <- irq_stack_ptr | > ------------ | > | x29 | | > ------------ | > | x19 | <- irq_stack_ptr - 0x10 | > ------------ | > | xzr | | > ------------ | | But the stack-pointer is decreased before use. So it actually looks | like this: | | > ------------ | > | | <- irq_stack_ptr | > top ------------ | > | dummy_lr | | > ------------ | > | x29 | <- irq_stack_ptr - 0x10 | > ------------ | > | x19 | | > ------------ | > | xzr | <- irq_stack_ptr - 0x20 | > ------------ | | The value being used as the original stack is x29, which in all the | tests is sp but without the current frames data, hence there are no | missing frames in the output. | | Jungseok Lee picked it up with a 32bit user space because aarch32 | can't use x29, so it remains 0 forever. The fix he posted is correct. This patch fixes the macro and adds some of this wisdom to a comment, so that the layout of the IRQ stack is well understood. Cc: James Morse <james.morse@arm.com> Reported-by: Jungseok Lee <jungseoklee85@gmail.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
66 lines
1.6 KiB
C
66 lines
1.6 KiB
C
#ifndef __ASM_IRQ_H
|
|
#define __ASM_IRQ_H
|
|
|
|
#define IRQ_STACK_SIZE THREAD_SIZE
|
|
#define IRQ_STACK_START_SP THREAD_START_SP
|
|
|
|
#ifndef __ASSEMBLER__
|
|
|
|
#include <linux/percpu.h>
|
|
|
|
#include <asm-generic/irq.h>
|
|
#include <asm/thread_info.h>
|
|
|
|
#define __ARCH_HAS_DO_SOFTIRQ
|
|
|
|
struct pt_regs;
|
|
|
|
DECLARE_PER_CPU(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack);
|
|
|
|
/*
|
|
* The highest address on the stack, and the first to be used. Used to
|
|
* find the dummy-stack frame put down by el?_irq() in entry.S, which
|
|
* is structured as follows:
|
|
*
|
|
* ------------
|
|
* | | <- irq_stack_ptr
|
|
* top ------------
|
|
* | elr_el1 |
|
|
* ------------
|
|
* | x29 | <- irq_stack_ptr - 0x10
|
|
* ------------
|
|
* | xzr |
|
|
* ------------
|
|
* | x19 | <- irq_stack_ptr - 0x20
|
|
* ------------
|
|
*
|
|
* where x19 holds a copy of the task stack pointer.
|
|
*
|
|
*/
|
|
#define IRQ_STACK_PTR(cpu) ((unsigned long)per_cpu(irq_stack, cpu) + IRQ_STACK_START_SP)
|
|
|
|
/*
|
|
* The offset from irq_stack_ptr where entry.S will store the original
|
|
* stack pointer. Used by unwind_frame() and dump_backtrace().
|
|
*/
|
|
#define IRQ_STACK_TO_TASK_STACK(ptr) *((unsigned long *)(ptr - 0x20));
|
|
|
|
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
|
|
|
|
static inline int nr_legacy_irqs(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline bool on_irq_stack(unsigned long sp, int cpu)
|
|
{
|
|
/* variable names the same as kernel/stacktrace.c */
|
|
unsigned long low = (unsigned long)per_cpu(irq_stack, cpu);
|
|
unsigned long high = low + IRQ_STACK_START_SP;
|
|
|
|
return (low <= sp && sp <= high);
|
|
}
|
|
|
|
#endif /* !__ASSEMBLER__ */
|
|
#endif
|