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>
This commit is contained in:
Vasily Gorbik 2019-11-22 19:18:58 +01:00
parent 0610154650
commit e7409367ab
1 changed files with 45 additions and 0 deletions

View File

@ -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;