]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
s390/test_unwind: add irq context tests
authorVasily Gorbik <gor@linux.ibm.com>
Fri, 22 Nov 2019 18:18:58 +0000 (19:18 +0100)
committerVasily Gorbik <gor@linux.ibm.com>
Sat, 30 Nov 2019 09:52:48 +0000 (10:52 +0100)
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>
arch/s390/lib/test_unwind.c

index db94e657c056dd6d974c16c6f29026b4d80635d6..72fa745281f093eb3b9dc726e6e5bf7177d2563b 100644 (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;