diff options
author | Vasily Gorbik | 2019-11-22 19:18:58 +0100 |
---|---|---|
committer | Vasily Gorbik | 2019-11-30 10:52:48 +0100 |
commit | e7409367abe54ad04868552b9d9fe4a56acc753d (patch) | |
tree | 4de5d1e67c4926c97eec152aaea8c25db3eb1f34 /arch | |
parent | 0610154650f161d56a0bef0d9678ae1de7360019 (diff) |
s390/test_unwind: add irq context tests
Add unwinding from irq context tests. Unwinder should be able to unwind
through irq stack to task stack up to task pt_regs.
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/s390/lib/test_unwind.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index db94e657c056..72fa745281f0 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -11,6 +11,8 @@ #include <linux/module.h> #include <linux/string.h> #include <linux/wait.h> +#include <asm/irq.h> +#include <asm/delay.h> #define BT_BUF_SIZE (PAGE_SIZE * 4) @@ -100,11 +102,15 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, /* State of the task being unwound. */ struct unwindme { int flags; + int ret; + struct task_struct *task; struct completion task_ready; wait_queue_head_t task_wq; unsigned long sp; }; +static struct unwindme *unwindme; + /* Values of unwindme.flags. */ #define UWM_DEFAULT 0x0 #define UWM_THREAD 0x1 /* Unwind a separate task. */ @@ -112,6 +118,7 @@ struct unwindme { #define UWM_SP 0x4 /* Pass sp to test_unwind(). */ #define UWM_CALLER 0x8 /* Unwind starting from caller. */ #define UWM_SWITCH_STACK 0x10 /* Use CALL_ON_STACK. */ +#define UWM_IRQ 0x20 /* Unwind from irq context. */ static __always_inline unsigned long get_psw_addr(void) { @@ -173,6 +180,34 @@ static noinline int unwindme_func1(void *u) return unwindme_func2((struct unwindme *)u); } +static void unwindme_irq_handler(struct ext_code ext_code, + unsigned int param32, + unsigned long param64) +{ + struct unwindme *u = READ_ONCE(unwindme); + + if (u && u->task == current) { + unwindme = NULL; + u->task = NULL; + u->ret = unwindme_func1(u); + } +} + +static int test_unwind_irq(struct unwindme *u) +{ + preempt_disable(); + if (register_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler)) { + pr_info("Couldn't reqister external interrupt handler"); + return -1; + } + u->task = current; + unwindme = u; + udelay(1); + unregister_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler); + preempt_enable(); + return u->ret; +} + /* Spawns a task and passes it to test_unwind(). */ static int test_unwind_task(struct unwindme *u) { @@ -211,6 +246,8 @@ static int test_unwind_flags(int flags) u.flags = flags; if (u.flags & UWM_THREAD) return test_unwind_task(&u); + else if (u.flags & UWM_IRQ) + return test_unwind_irq(&u); else return unwindme_func1(&u); } @@ -241,6 +278,14 @@ do { \ TEST(UWM_THREAD); TEST(UWM_THREAD | UWM_SP); TEST(UWM_THREAD | UWM_CALLER | UWM_SP); + TEST(UWM_IRQ); + TEST(UWM_IRQ | UWM_SWITCH_STACK); + TEST(UWM_IRQ | UWM_SP); + TEST(UWM_IRQ | UWM_REGS); + TEST(UWM_IRQ | UWM_SP | UWM_REGS); + TEST(UWM_IRQ | UWM_CALLER | UWM_SP); + TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS); + TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); #undef TEST return ret; |