diff options
Diffstat (limited to 'drivers/char/sysrq.c')
-rw-r--r-- | drivers/char/sysrq.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c new file mode 100644 index 000000000000..f59f7cbd525b --- /dev/null +++ b/drivers/char/sysrq.c | |||
@@ -0,0 +1,432 @@ | |||
1 | /* -*- linux-c -*- | ||
2 | * | ||
3 | * $Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $ | ||
4 | * | ||
5 | * Linux Magic System Request Key Hacks | ||
6 | * | ||
7 | * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> | ||
8 | * based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz> | ||
9 | * | ||
10 | * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com> | ||
11 | * overhauled to use key registration | ||
12 | * based upon discusions in irc://irc.openprojects.net/#kernelnewbies | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/tty.h> | ||
21 | #include <linux/mount.h> | ||
22 | #include <linux/kdev_t.h> | ||
23 | #include <linux/major.h> | ||
24 | #include <linux/reboot.h> | ||
25 | #include <linux/sysrq.h> | ||
26 | #include <linux/kbd_kern.h> | ||
27 | #include <linux/quotaops.h> | ||
28 | #include <linux/smp_lock.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/suspend.h> | ||
32 | #include <linux/writeback.h> | ||
33 | #include <linux/buffer_head.h> /* for fsync_bdev() */ | ||
34 | #include <linux/swap.h> | ||
35 | #include <linux/spinlock.h> | ||
36 | #include <linux/vt_kern.h> | ||
37 | #include <linux/workqueue.h> | ||
38 | |||
39 | #include <asm/ptrace.h> | ||
40 | |||
41 | /* Whether we react on sysrq keys or just ignore them */ | ||
42 | int sysrq_enabled = 1; | ||
43 | |||
44 | /* Loglevel sysrq handler */ | ||
45 | static void sysrq_handle_loglevel(int key, struct pt_regs *pt_regs, | ||
46 | struct tty_struct *tty) | ||
47 | { | ||
48 | int i; | ||
49 | i = key - '0'; | ||
50 | console_loglevel = 7; | ||
51 | printk("Loglevel set to %d\n", i); | ||
52 | console_loglevel = i; | ||
53 | } | ||
54 | static struct sysrq_key_op sysrq_loglevel_op = { | ||
55 | .handler = sysrq_handle_loglevel, | ||
56 | .help_msg = "loglevel0-8", | ||
57 | .action_msg = "Changing Loglevel", | ||
58 | .enable_mask = SYSRQ_ENABLE_LOG, | ||
59 | }; | ||
60 | |||
61 | |||
62 | /* SAK sysrq handler */ | ||
63 | #ifdef CONFIG_VT | ||
64 | static void sysrq_handle_SAK(int key, struct pt_regs *pt_regs, | ||
65 | struct tty_struct *tty) | ||
66 | { | ||
67 | if (tty) | ||
68 | do_SAK(tty); | ||
69 | reset_vc(vc_cons[fg_console].d); | ||
70 | } | ||
71 | static struct sysrq_key_op sysrq_SAK_op = { | ||
72 | .handler = sysrq_handle_SAK, | ||
73 | .help_msg = "saK", | ||
74 | .action_msg = "SAK", | ||
75 | .enable_mask = SYSRQ_ENABLE_KEYBOARD, | ||
76 | }; | ||
77 | #endif | ||
78 | |||
79 | #ifdef CONFIG_VT | ||
80 | /* unraw sysrq handler */ | ||
81 | static void sysrq_handle_unraw(int key, struct pt_regs *pt_regs, | ||
82 | struct tty_struct *tty) | ||
83 | { | ||
84 | struct kbd_struct *kbd = &kbd_table[fg_console]; | ||
85 | |||
86 | if (kbd) | ||
87 | kbd->kbdmode = VC_XLATE; | ||
88 | } | ||
89 | static struct sysrq_key_op sysrq_unraw_op = { | ||
90 | .handler = sysrq_handle_unraw, | ||
91 | .help_msg = "unRaw", | ||
92 | .action_msg = "Keyboard mode set to XLATE", | ||
93 | .enable_mask = SYSRQ_ENABLE_KEYBOARD, | ||
94 | }; | ||
95 | #endif /* CONFIG_VT */ | ||
96 | |||
97 | /* reboot sysrq handler */ | ||
98 | static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs, | ||
99 | struct tty_struct *tty) | ||
100 | { | ||
101 | local_irq_enable(); | ||
102 | machine_restart(NULL); | ||
103 | } | ||
104 | |||
105 | static struct sysrq_key_op sysrq_reboot_op = { | ||
106 | .handler = sysrq_handle_reboot, | ||
107 | .help_msg = "reBoot", | ||
108 | .action_msg = "Resetting", | ||
109 | .enable_mask = SYSRQ_ENABLE_BOOT, | ||
110 | }; | ||
111 | |||
112 | static void sysrq_handle_sync(int key, struct pt_regs *pt_regs, | ||
113 | struct tty_struct *tty) | ||
114 | { | ||
115 | emergency_sync(); | ||
116 | } | ||
117 | |||
118 | static struct sysrq_key_op sysrq_sync_op = { | ||
119 | .handler = sysrq_handle_sync, | ||
120 | .help_msg = "Sync", | ||
121 | .action_msg = "Emergency Sync", | ||
122 | .enable_mask = SYSRQ_ENABLE_SYNC, | ||
123 | }; | ||
124 | |||
125 | static void sysrq_handle_mountro(int key, struct pt_regs *pt_regs, | ||
126 | struct tty_struct *tty) | ||
127 | { | ||
128 | emergency_remount(); | ||
129 | } | ||
130 | |||
131 | static struct sysrq_key_op sysrq_mountro_op = { | ||
132 | .handler = sysrq_handle_mountro, | ||
133 | .help_msg = "Unmount", | ||
134 | .action_msg = "Emergency Remount R/O", | ||
135 | .enable_mask = SYSRQ_ENABLE_REMOUNT, | ||
136 | }; | ||
137 | |||
138 | /* END SYNC SYSRQ HANDLERS BLOCK */ | ||
139 | |||
140 | |||
141 | /* SHOW SYSRQ HANDLERS BLOCK */ | ||
142 | |||
143 | static void sysrq_handle_showregs(int key, struct pt_regs *pt_regs, | ||
144 | struct tty_struct *tty) | ||
145 | { | ||
146 | if (pt_regs) | ||
147 | show_regs(pt_regs); | ||
148 | } | ||
149 | static struct sysrq_key_op sysrq_showregs_op = { | ||
150 | .handler = sysrq_handle_showregs, | ||
151 | .help_msg = "showPc", | ||
152 | .action_msg = "Show Regs", | ||
153 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
154 | }; | ||
155 | |||
156 | |||
157 | static void sysrq_handle_showstate(int key, struct pt_regs *pt_regs, | ||
158 | struct tty_struct *tty) | ||
159 | { | ||
160 | show_state(); | ||
161 | } | ||
162 | static struct sysrq_key_op sysrq_showstate_op = { | ||
163 | .handler = sysrq_handle_showstate, | ||
164 | .help_msg = "showTasks", | ||
165 | .action_msg = "Show State", | ||
166 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
167 | }; | ||
168 | |||
169 | |||
170 | static void sysrq_handle_showmem(int key, struct pt_regs *pt_regs, | ||
171 | struct tty_struct *tty) | ||
172 | { | ||
173 | show_mem(); | ||
174 | } | ||
175 | static struct sysrq_key_op sysrq_showmem_op = { | ||
176 | .handler = sysrq_handle_showmem, | ||
177 | .help_msg = "showMem", | ||
178 | .action_msg = "Show Memory", | ||
179 | .enable_mask = SYSRQ_ENABLE_DUMP, | ||
180 | }; | ||
181 | |||
182 | /* SHOW SYSRQ HANDLERS BLOCK */ | ||
183 | |||
184 | |||
185 | /* SIGNAL SYSRQ HANDLERS BLOCK */ | ||
186 | |||
187 | /* signal sysrq helper function | ||
188 | * Sends a signal to all user processes */ | ||
189 | static void send_sig_all(int sig) | ||
190 | { | ||
191 | struct task_struct *p; | ||
192 | |||
193 | for_each_process(p) { | ||
194 | if (p->mm && p->pid != 1) | ||
195 | /* Not swapper, init nor kernel thread */ | ||
196 | force_sig(sig, p); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | static void sysrq_handle_term(int key, struct pt_regs *pt_regs, | ||
201 | struct tty_struct *tty) | ||
202 | { | ||
203 | send_sig_all(SIGTERM); | ||
204 | console_loglevel = 8; | ||
205 | } | ||
206 | static struct sysrq_key_op sysrq_term_op = { | ||
207 | .handler = sysrq_handle_term, | ||
208 | .help_msg = "tErm", | ||
209 | .action_msg = "Terminate All Tasks", | ||
210 | .enable_mask = SYSRQ_ENABLE_SIGNAL, | ||
211 | }; | ||
212 | |||
213 | static void moom_callback(void *ignored) | ||
214 | { | ||
215 | out_of_memory(GFP_KERNEL); | ||
216 | } | ||
217 | |||
218 | static DECLARE_WORK(moom_work, moom_callback, NULL); | ||
219 | |||
220 | static void sysrq_handle_moom(int key, struct pt_regs *pt_regs, | ||
221 | struct tty_struct *tty) | ||
222 | { | ||
223 | schedule_work(&moom_work); | ||
224 | } | ||
225 | static struct sysrq_key_op sysrq_moom_op = { | ||
226 | .handler = sysrq_handle_moom, | ||
227 | .help_msg = "Full", | ||
228 | .action_msg = "Manual OOM execution", | ||
229 | }; | ||
230 | |||
231 | static void sysrq_handle_kill(int key, struct pt_regs *pt_regs, | ||
232 | struct tty_struct *tty) | ||
233 | { | ||
234 | send_sig_all(SIGKILL); | ||
235 | console_loglevel = 8; | ||
236 | } | ||
237 | static struct sysrq_key_op sysrq_kill_op = { | ||
238 | .handler = sysrq_handle_kill, | ||
239 | .help_msg = "kIll", | ||
240 | .action_msg = "Kill All Tasks", | ||
241 | .enable_mask = SYSRQ_ENABLE_SIGNAL, | ||
242 | }; | ||
243 | |||
244 | /* END SIGNAL SYSRQ HANDLERS BLOCK */ | ||
245 | |||
246 | static void sysrq_handle_unrt(int key, struct pt_regs *pt_regs, | ||
247 | struct tty_struct *tty) | ||
248 | { | ||
249 | normalize_rt_tasks(); | ||
250 | } | ||
251 | static struct sysrq_key_op sysrq_unrt_op = { | ||
252 | .handler = sysrq_handle_unrt, | ||
253 | .help_msg = "Nice", | ||
254 | .action_msg = "Nice All RT Tasks", | ||
255 | .enable_mask = SYSRQ_ENABLE_RTNICE, | ||
256 | }; | ||
257 | |||
258 | /* Key Operations table and lock */ | ||
259 | static DEFINE_SPINLOCK(sysrq_key_table_lock); | ||
260 | #define SYSRQ_KEY_TABLE_LENGTH 36 | ||
261 | static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = { | ||
262 | /* 0 */ &sysrq_loglevel_op, | ||
263 | /* 1 */ &sysrq_loglevel_op, | ||
264 | /* 2 */ &sysrq_loglevel_op, | ||
265 | /* 3 */ &sysrq_loglevel_op, | ||
266 | /* 4 */ &sysrq_loglevel_op, | ||
267 | /* 5 */ &sysrq_loglevel_op, | ||
268 | /* 6 */ &sysrq_loglevel_op, | ||
269 | /* 7 */ &sysrq_loglevel_op, | ||
270 | /* 8 */ &sysrq_loglevel_op, | ||
271 | /* 9 */ &sysrq_loglevel_op, | ||
272 | /* a */ NULL, /* Don't use for system provided sysrqs, | ||
273 | it is handled specially on the sparc | ||
274 | and will never arrive */ | ||
275 | /* b */ &sysrq_reboot_op, | ||
276 | /* c */ NULL, | ||
277 | /* d */ NULL, | ||
278 | /* e */ &sysrq_term_op, | ||
279 | /* f */ &sysrq_moom_op, | ||
280 | /* g */ NULL, | ||
281 | /* h */ NULL, | ||
282 | /* i */ &sysrq_kill_op, | ||
283 | /* j */ NULL, | ||
284 | #ifdef CONFIG_VT | ||
285 | /* k */ &sysrq_SAK_op, | ||
286 | #else | ||
287 | /* k */ NULL, | ||
288 | #endif | ||
289 | /* l */ NULL, | ||
290 | /* m */ &sysrq_showmem_op, | ||
291 | /* n */ &sysrq_unrt_op, | ||
292 | /* o */ NULL, /* This will often be registered | ||
293 | as 'Off' at init time */ | ||
294 | /* p */ &sysrq_showregs_op, | ||
295 | /* q */ NULL, | ||
296 | #ifdef CONFIG_VT | ||
297 | /* r */ &sysrq_unraw_op, | ||
298 | #else | ||
299 | /* r */ NULL, | ||
300 | #endif | ||
301 | /* s */ &sysrq_sync_op, | ||
302 | /* t */ &sysrq_showstate_op, | ||
303 | /* u */ &sysrq_mountro_op, | ||
304 | /* v */ NULL, /* May be assigned at init time by SMP VOYAGER */ | ||
305 | /* w */ NULL, | ||
306 | /* x */ NULL, | ||
307 | /* y */ NULL, | ||
308 | /* z */ NULL | ||
309 | }; | ||
310 | |||
311 | /* key2index calculation, -1 on invalid index */ | ||
312 | static int sysrq_key_table_key2index(int key) { | ||
313 | int retval; | ||
314 | if ((key >= '0') && (key <= '9')) { | ||
315 | retval = key - '0'; | ||
316 | } else if ((key >= 'a') && (key <= 'z')) { | ||
317 | retval = key + 10 - 'a'; | ||
318 | } else { | ||
319 | retval = -1; | ||
320 | } | ||
321 | return retval; | ||
322 | } | ||
323 | |||
324 | /* | ||
325 | * get and put functions for the table, exposed to modules. | ||
326 | */ | ||
327 | |||
328 | struct sysrq_key_op *__sysrq_get_key_op (int key) { | ||
329 | struct sysrq_key_op *op_p; | ||
330 | int i; | ||
331 | |||
332 | i = sysrq_key_table_key2index(key); | ||
333 | op_p = (i == -1) ? NULL : sysrq_key_table[i]; | ||
334 | return op_p; | ||
335 | } | ||
336 | |||
337 | void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) { | ||
338 | int i; | ||
339 | |||
340 | i = sysrq_key_table_key2index(key); | ||
341 | if (i != -1) | ||
342 | sysrq_key_table[i] = op_p; | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * This is the non-locking version of handle_sysrq | ||
347 | * It must/can only be called by sysrq key handlers, | ||
348 | * as they are inside of the lock | ||
349 | */ | ||
350 | |||
351 | void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty, int check_mask) | ||
352 | { | ||
353 | struct sysrq_key_op *op_p; | ||
354 | int orig_log_level; | ||
355 | int i, j; | ||
356 | unsigned long flags; | ||
357 | |||
358 | spin_lock_irqsave(&sysrq_key_table_lock, flags); | ||
359 | orig_log_level = console_loglevel; | ||
360 | console_loglevel = 7; | ||
361 | printk(KERN_INFO "SysRq : "); | ||
362 | |||
363 | op_p = __sysrq_get_key_op(key); | ||
364 | if (op_p) { | ||
365 | /* Should we check for enabled operations (/proc/sysrq-trigger should not) | ||
366 | * and is the invoked operation enabled? */ | ||
367 | if (!check_mask || sysrq_enabled == 1 || | ||
368 | (sysrq_enabled & op_p->enable_mask)) { | ||
369 | printk ("%s\n", op_p->action_msg); | ||
370 | console_loglevel = orig_log_level; | ||
371 | op_p->handler(key, pt_regs, tty); | ||
372 | } | ||
373 | else | ||
374 | printk("This sysrq operation is disabled.\n"); | ||
375 | } else { | ||
376 | printk("HELP : "); | ||
377 | /* Only print the help msg once per handler */ | ||
378 | for (i=0; i<SYSRQ_KEY_TABLE_LENGTH; i++) | ||
379 | if (sysrq_key_table[i]) { | ||
380 | for (j=0; sysrq_key_table[i] != sysrq_key_table[j]; j++); | ||
381 | if (j == i) | ||
382 | printk ("%s ", sysrq_key_table[i]->help_msg); | ||
383 | } | ||
384 | printk ("\n"); | ||
385 | console_loglevel = orig_log_level; | ||
386 | } | ||
387 | spin_unlock_irqrestore(&sysrq_key_table_lock, flags); | ||
388 | } | ||
389 | |||
390 | /* | ||
391 | * This function is called by the keyboard handler when SysRq is pressed | ||
392 | * and any other keycode arrives. | ||
393 | */ | ||
394 | |||
395 | void handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty) | ||
396 | { | ||
397 | if (!sysrq_enabled) | ||
398 | return; | ||
399 | __handle_sysrq(key, pt_regs, tty, 1); | ||
400 | } | ||
401 | |||
402 | int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, | ||
403 | struct sysrq_key_op *remove_op_p) { | ||
404 | |||
405 | int retval; | ||
406 | unsigned long flags; | ||
407 | |||
408 | spin_lock_irqsave(&sysrq_key_table_lock, flags); | ||
409 | if (__sysrq_get_key_op(key) == remove_op_p) { | ||
410 | __sysrq_put_key_op(key, insert_op_p); | ||
411 | retval = 0; | ||
412 | } else { | ||
413 | retval = -1; | ||
414 | } | ||
415 | spin_unlock_irqrestore(&sysrq_key_table_lock, flags); | ||
416 | |||
417 | return retval; | ||
418 | } | ||
419 | |||
420 | int register_sysrq_key(int key, struct sysrq_key_op *op_p) | ||
421 | { | ||
422 | return __sysrq_swap_key_ops(key, op_p, NULL); | ||
423 | } | ||
424 | |||
425 | int unregister_sysrq_key(int key, struct sysrq_key_op *op_p) | ||
426 | { | ||
427 | return __sysrq_swap_key_ops(key, NULL, op_p); | ||
428 | } | ||
429 | |||
430 | EXPORT_SYMBOL(handle_sysrq); | ||
431 | EXPORT_SYMBOL(register_sysrq_key); | ||
432 | EXPORT_SYMBOL(unregister_sysrq_key); | ||