mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
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:
parent
0610154650
commit
e7409367ab
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user