|
19 | 19 | #include <linux/console.h>
|
20 | 20 | #include <linux/vt_kern.h>
|
21 | 21 | #include <linux/input.h>
|
| 22 | +#include <linux/irq_work.h> |
22 | 23 | #include <linux/module.h>
|
23 | 24 | #include <linux/platform_device.h>
|
24 | 25 | #include <linux/serial_core.h>
|
@@ -48,6 +49,25 @@ static struct kgdb_io kgdboc_earlycon_io_ops;
|
48 | 49 | static int (*earlycon_orig_exit)(struct console *con);
|
49 | 50 | #endif /* IS_BUILTIN(CONFIG_KGDB_SERIAL_CONSOLE) */
|
50 | 51 |
|
| 52 | +/* |
| 53 | + * When we leave the debug trap handler we need to reset the keyboard status |
| 54 | + * (since the original keyboard state gets partially clobbered by kdb use of |
| 55 | + * the keyboard). |
| 56 | + * |
| 57 | + * The path to deliver the reset is somewhat circuitous. |
| 58 | + * |
| 59 | + * To deliver the reset we register an input handler, reset the keyboard and |
| 60 | + * then deregister the input handler. However, to get this done right, we do |
| 61 | + * have to carefully manage the calling context because we can only register |
| 62 | + * input handlers from task context. |
| 63 | + * |
| 64 | + * In particular we need to trigger the action from the debug trap handler with |
| 65 | + * all its NMI and/or NMI-like oddities. To solve this the kgdboc trap exit code |
| 66 | + * (the "post_exception" callback) uses irq_work_queue(), which is NMI-safe, to |
| 67 | + * schedule a callback from a hardirq context. From there we have to defer the |
| 68 | + * work again, this time using schedule_work(), to get a callback using the |
| 69 | + * system workqueue, which runs in task context. |
| 70 | + */ |
51 | 71 | #ifdef CONFIG_KDB_KEYBOARD
|
52 | 72 | static int kgdboc_reset_connect(struct input_handler *handler,
|
53 | 73 | struct input_dev *dev,
|
@@ -99,10 +119,17 @@ static void kgdboc_restore_input_helper(struct work_struct *dummy)
|
99 | 119 |
|
100 | 120 | static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper);
|
101 | 121 |
|
| 122 | +static void kgdboc_queue_restore_input_helper(struct irq_work *unused) |
| 123 | +{ |
| 124 | + schedule_work(&kgdboc_restore_input_work); |
| 125 | +} |
| 126 | + |
| 127 | +static DEFINE_IRQ_WORK(kgdboc_restore_input_irq_work, kgdboc_queue_restore_input_helper); |
| 128 | + |
102 | 129 | static void kgdboc_restore_input(void)
|
103 | 130 | {
|
104 | 131 | if (likely(system_state == SYSTEM_RUNNING))
|
105 |
| - schedule_work(&kgdboc_restore_input_work); |
| 132 | + irq_work_queue(&kgdboc_restore_input_irq_work); |
106 | 133 | }
|
107 | 134 |
|
108 | 135 | static int kgdboc_register_kbd(char **cptr)
|
@@ -133,6 +160,7 @@ static void kgdboc_unregister_kbd(void)
|
133 | 160 | i--;
|
134 | 161 | }
|
135 | 162 | }
|
| 163 | + irq_work_sync(&kgdboc_restore_input_irq_work); |
136 | 164 | flush_work(&kgdboc_restore_input_work);
|
137 | 165 | }
|
138 | 166 | #else /* ! CONFIG_KDB_KEYBOARD */
|
|
0 commit comments