]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - arch/um/drivers/mconsole_kern.c
mm/backing-dev.c: remove recently-added WARN_ON()
[mirror_ubuntu-zesty-kernel.git] / arch / um / drivers / mconsole_kern.c
CommitLineData
1da177e4
LT
1/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
827b3f6a 3 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
1da177e4
LT
4 * Licensed under the GPL
5 */
6
827b3f6a
JD
7#include <linux/console.h>
8#include <linux/ctype.h>
9#include <linux/interrupt.h>
10#include <linux/list.h>
11#include <linux/mm.h>
12#include <linux/module.h>
13#include <linux/notifier.h>
14#include <linux/reboot.h>
15#include <linux/proc_fs.h>
16#include <linux/slab.h>
17#include <linux/syscalls.h>
18#include <linux/utsname.h>
19#include <linux/workqueue.h>
20#include <linux/mutex.h>
21#include <asm/uaccess.h>
22
ae2587e4
JD
23#include "init.h"
24#include "irq_kern.h"
25#include "irq_user.h"
1da177e4 26#include "kern_util.h"
1da177e4
LT
27#include "mconsole.h"
28#include "mconsole_kern.h"
1da177e4 29#include "os.h"
1da177e4 30
d50084a2 31static int do_unlink_socket(struct notifier_block *notifier,
1da177e4
LT
32 unsigned long what, void *data)
33{
ae2587e4 34 return mconsole_unlink_socket();
1da177e4
LT
35}
36
37
38static struct notifier_block reboot_notifier = {
39 .notifier_call = do_unlink_socket,
40 .priority = 0,
41};
42
d50084a2 43/* Safe without explicit locking for now. Tasklets provide their own
1da177e4
LT
44 * locking, and the interrupt handler is safe because it can't interrupt
45 * itself and it can only happen on CPU 0.
46 */
47
9010772c 48static LIST_HEAD(mc_requests);
1da177e4 49
6d5aefb8 50static void mc_work_proc(struct work_struct *unused)
1da177e4
LT
51{
52 struct mconsole_entry *req;
53 unsigned long flags;
54
ae2587e4 55 while (!list_empty(&mc_requests)) {
dbdb4c06 56 local_irq_save(flags);
ae2587e4 57 req = list_entry(mc_requests.next, struct mconsole_entry, list);
1da177e4
LT
58 list_del(&req->list);
59 local_irq_restore(flags);
60 req->request.cmd->handler(&req->request);
61 kfree(req);
62 }
63}
64
6d5aefb8 65static DECLARE_WORK(mconsole_work, mc_work_proc);
1da177e4 66
7bea96fd 67static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
1da177e4
LT
68{
69 /* long to avoid size mismatch warnings from gcc */
70 long fd;
71 struct mconsole_entry *new;
3a51237d 72 static struct mc_request req; /* that's OK */
1da177e4
LT
73
74 fd = (long) dev_id;
ae2587e4
JD
75 while (mconsole_get_request(fd, &req)) {
76 if (req.cmd->context == MCONSOLE_INTR)
1da177e4
LT
77 (*req.cmd->handler)(&req);
78 else {
60baa158 79 new = kmalloc(sizeof(*new), GFP_NOWAIT);
ae2587e4 80 if (new == NULL)
1da177e4
LT
81 mconsole_reply(&req, "Out of memory", 1, 0);
82 else {
83 new->request = req;
3a51237d 84 new->request.regs = get_irq_regs()->regs;
1da177e4
LT
85 list_add(&new->list, &mc_requests);
86 }
87 }
88 }
ae2587e4 89 if (!list_empty(&mc_requests))
1da177e4
LT
90 schedule_work(&mconsole_work);
91 reactivate_fd(fd, MCONSOLE_IRQ);
ae2587e4 92 return IRQ_HANDLED;
1da177e4
LT
93}
94
95void mconsole_version(struct mc_request *req)
96{
97 char version[256];
98
e9ff3990 99 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
ae2587e4
JD
100 utsname()->nodename, utsname()->release, utsname()->version,
101 utsname()->machine);
1da177e4
LT
102 mconsole_reply(req, version, 0, 0);
103}
104
105void mconsole_log(struct mc_request *req)
106{
107 int len;
108 char *ptr = req->request.data;
109
110 ptr += strlen("log ");
111
112 len = req->len - (ptr - req->request.data);
ae2587e4 113 printk(KERN_WARNING "%.*s", len, ptr);
1da177e4
LT
114 mconsole_reply(req, "", 0, 0);
115}
116
117/* This is a more convoluted version of mconsole_proc, which has some stability
118 * problems; however, we need it fixed, because it is expected that UML users
119 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
120 * show the real procfs content, not the ones from hppfs.*/
121#if 0
122void mconsole_proc(struct mc_request *req)
123{
124 struct nameidata nd;
125 struct file_system_type *proc;
126 struct super_block *super;
127 struct file *file;
128 int n, err;
129 char *ptr = req->request.data, *buf;
130
131 ptr += strlen("proc");
ae2587e4 132 while (isspace(*ptr)) ptr++;
1da177e4
LT
133
134 proc = get_fs_type("proc");
ae2587e4 135 if (proc == NULL) {
1da177e4
LT
136 mconsole_reply(req, "procfs not registered", 1, 0);
137 goto out;
138 }
139
140 super = (*proc->get_sb)(proc, 0, NULL, NULL);
141 put_filesystem(proc);
ae2587e4 142 if (super == NULL) {
1da177e4
LT
143 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
144 goto out;
145 }
146 up_write(&super->s_umount);
147
4ac91378
JB
148 nd.path.dentry = super->s_root;
149 nd.path.mnt = NULL;
1da177e4
LT
150 nd.flags = O_RDONLY + 1;
151 nd.last_type = LAST_ROOT;
152
153 /* START: it was experienced that the stability problems are closed
154 * if commenting out these two calls + the below read cycle. To
155 * make UML crash again, it was enough to readd either one.*/
156 err = link_path_walk(ptr, &nd);
ae2587e4 157 if (err) {
1da177e4
LT
158 mconsole_reply(req, "Failed to look up file", 1, 0);
159 goto out_kill;
160 }
161
4ac91378 162 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY);
ae2587e4 163 if (IS_ERR(file)) {
1da177e4
LT
164 mconsole_reply(req, "Failed to open file", 1, 0);
165 goto out_kill;
166 }
167 /*END*/
168
169 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
ae2587e4 170 if (buf == NULL) {
1da177e4
LT
171 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
172 goto out_fput;
173 }
174
ae2587e4 175 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
1da177e4
LT
176 do {
177 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
178 &file->f_pos);
ae2587e4 179 if (n >= 0) {
1da177e4
LT
180 buf[n] = '\0';
181 mconsole_reply(req, buf, 0, (n > 0));
182 }
183 else {
184 mconsole_reply(req, "Read of file failed",
185 1, 0);
186 goto out_free;
187 }
ae2587e4 188 } while (n > 0);
1da177e4
LT
189 }
190 else mconsole_reply(req, "", 0, 0);
191
192 out_free:
193 kfree(buf);
194 out_fput:
195 fput(file);
196 out_kill:
197 deactivate_super(super);
198 out: ;
199}
200#endif
201
202void mconsole_proc(struct mc_request *req)
203{
204 char path[64];
205 char *buf;
206 int len;
207 int fd;
208 int first_chunk = 1;
209 char *ptr = req->request.data;
210
211 ptr += strlen("proc");
ae2587e4
JD
212 while (isspace(*ptr))
213 ptr++;
1da177e4
LT
214 snprintf(path, sizeof(path), "/proc/%s", ptr);
215
216 fd = sys_open(path, 0, 0);
217 if (fd < 0) {
218 mconsole_reply(req, "Failed to open file", 1, 0);
ae2587e4 219 printk(KERN_ERR "open %s: %d\n",path,fd);
1da177e4
LT
220 goto out;
221 }
222
223 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
ae2587e4 224 if (buf == NULL) {
1da177e4
LT
225 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
226 goto out_close;
227 }
228
229 for (;;) {
230 len = sys_read(fd, buf, PAGE_SIZE-1);
231 if (len < 0) {
232 mconsole_reply(req, "Read of file failed", 1, 0);
233 goto out_free;
234 }
ae2587e4 235 /* Begin the file content on his own line. */
1da177e4
LT
236 if (first_chunk) {
237 mconsole_reply(req, "\n", 0, 1);
238 first_chunk = 0;
239 }
240 if (len == PAGE_SIZE-1) {
241 buf[len] = '\0';
242 mconsole_reply(req, buf, 0, 1);
243 } else {
244 buf[len] = '\0';
245 mconsole_reply(req, buf, 0, 0);
246 break;
247 }
248 }
249
250 out_free:
251 kfree(buf);
252 out_close:
253 sys_close(fd);
254 out:
255 /* nothing */;
256}
257
258#define UML_MCONSOLE_HELPTEXT \
259"Commands: \n\
260 version - Get kernel version \n\
261 help - Print this message \n\
262 halt - Halt UML \n\
263 reboot - Reboot UML \n\
264 config <dev>=<config> - Add a new device to UML; \n\
265 same syntax as command line \n\
266 config <dev> - Query the configuration of a device \n\
267 remove <dev> - Remove a device from UML \n\
268 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
db805812 269 cad - invoke the Ctrl-Alt-Del handler \n\
1da177e4
LT
270 stop - pause the UML; it will do nothing until it receives a 'go' \n\
271 go - continue the UML after a 'stop' \n\
272 log <string> - make UML enter <string> into the kernel log\n\
273 proc <file> - returns the contents of the UML's /proc/<file>\n\
3eddddcf 274 stack <pid> - returns the stack of the specified pid\n\
1da177e4
LT
275"
276
277void mconsole_help(struct mc_request *req)
278{
279 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
280}
281
282void mconsole_halt(struct mc_request *req)
283{
284 mconsole_reply(req, "", 0, 0);
285 machine_halt();
286}
287
288void mconsole_reboot(struct mc_request *req)
289{
290 mconsole_reply(req, "", 0, 0);
291 machine_restart(NULL);
292}
293
1da177e4
LT
294void mconsole_cad(struct mc_request *req)
295{
296 mconsole_reply(req, "", 0, 0);
297 ctrl_alt_del();
298}
299
300void mconsole_go(struct mc_request *req)
301{
302 mconsole_reply(req, "Not stopped", 1, 0);
303}
304
305void mconsole_stop(struct mc_request *req)
306{
307 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
308 os_set_fd_block(req->originating_fd, 1);
3a51237d 309 mconsole_reply(req, "stopped", 0, 0);
cc0be0fb
KS
310 for (;;) {
311 if (!mconsole_get_request(req->originating_fd, req))
312 continue;
3a51237d
AV
313 if (req->cmd->handler == mconsole_go)
314 break;
315 if (req->cmd->handler == mconsole_stop) {
316 mconsole_reply(req, "Already stopped", 1, 0);
317 continue;
318 }
319 if (req->cmd->handler == mconsole_sysrq) {
320 struct pt_regs *old_regs;
321 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
322 mconsole_sysrq(req);
323 set_irq_regs(old_regs);
324 continue;
325 }
1da177e4
LT
326 (*req->cmd->handler)(req);
327 }
328 os_set_fd_block(req->originating_fd, 0);
329 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
330 mconsole_reply(req, "", 0, 0);
331}
332
84f48d4f 333static DEFINE_SPINLOCK(mc_devices_lock);
42947cb9 334static LIST_HEAD(mconsole_devices);
1da177e4
LT
335
336void mconsole_register_dev(struct mc_device *new)
337{
84f48d4f
JD
338 spin_lock(&mc_devices_lock);
339 BUG_ON(!list_empty(&new->list));
1da177e4 340 list_add(&new->list, &mconsole_devices);
84f48d4f 341 spin_unlock(&mc_devices_lock);
1da177e4
LT
342}
343
344static struct mc_device *mconsole_find_dev(char *name)
345{
346 struct list_head *ele;
347 struct mc_device *dev;
348
ae2587e4 349 list_for_each(ele, &mconsole_devices) {
1da177e4 350 dev = list_entry(ele, struct mc_device, list);
ae2587e4
JD
351 if (!strncmp(name, dev->name, strlen(dev->name)))
352 return dev;
1da177e4 353 }
ae2587e4 354 return NULL;
1da177e4
LT
355}
356
02dea087
JD
357#define UNPLUGGED_PER_PAGE \
358 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
359
360struct unplugged_pages {
361 struct list_head list;
362 void *pages[UNPLUGGED_PER_PAGE];
363};
364
e98fa281 365static DEFINE_MUTEX(plug_mem_mutex);
02dea087 366static unsigned long long unplugged_pages_count = 0;
c59bce62 367static LIST_HEAD(unplugged_pages);
02dea087
JD
368static int unplug_index = UNPLUGGED_PER_PAGE;
369
f28169d2 370static int mem_config(char *str, char **error_out)
02dea087
JD
371{
372 unsigned long long diff;
373 int err = -EINVAL, i, add;
374 char *ret;
375
ae2587e4 376 if (str[0] != '=') {
f28169d2 377 *error_out = "Expected '=' after 'mem'";
02dea087 378 goto out;
f28169d2 379 }
02dea087
JD
380
381 str++;
ae2587e4 382 if (str[0] == '-')
02dea087 383 add = 0;
ae2587e4 384 else if (str[0] == '+') {
02dea087
JD
385 add = 1;
386 }
f28169d2
JD
387 else {
388 *error_out = "Expected increment to start with '-' or '+'";
389 goto out;
390 }
02dea087
JD
391
392 str++;
393 diff = memparse(str, &ret);
ae2587e4 394 if (*ret != '\0') {
f28169d2 395 *error_out = "Failed to parse memory increment";
02dea087 396 goto out;
f28169d2 397 }
02dea087
JD
398
399 diff /= PAGE_SIZE;
400
e98fa281 401 mutex_lock(&plug_mem_mutex);
ae2587e4 402 for (i = 0; i < diff; i++) {
02dea087
JD
403 struct unplugged_pages *unplugged;
404 void *addr;
405
ae2587e4
JD
406 if (add) {
407 if (list_empty(&unplugged_pages))
02dea087
JD
408 break;
409
410 unplugged = list_entry(unplugged_pages.next,
411 struct unplugged_pages, list);
ae2587e4 412 if (unplug_index > 0)
02dea087
JD
413 addr = unplugged->pages[--unplug_index];
414 else {
415 list_del(&unplugged->list);
416 addr = unplugged;
417 unplug_index = UNPLUGGED_PER_PAGE;
418 }
419
420 free_page((unsigned long) addr);
421 unplugged_pages_count--;
422 }
423 else {
424 struct page *page;
425
426 page = alloc_page(GFP_ATOMIC);
ae2587e4 427 if (page == NULL)
02dea087
JD
428 break;
429
430 unplugged = page_address(page);
ae2587e4 431 if (unplug_index == UNPLUGGED_PER_PAGE) {
02dea087
JD
432 list_add(&unplugged->list, &unplugged_pages);
433 unplug_index = 0;
434 }
435 else {
436 struct list_head *entry = unplugged_pages.next;
437 addr = unplugged;
438
439 unplugged = list_entry(entry,
440 struct unplugged_pages,
441 list);
02dea087 442 err = os_drop_memory(addr, PAGE_SIZE);
ae2587e4
JD
443 if (err) {
444 printk(KERN_ERR "Failed to release "
445 "memory - errno = %d\n", err);
f28169d2 446 *error_out = "Failed to release memory";
84f48d4f 447 goto out_unlock;
f28169d2
JD
448 }
449 unplugged->pages[unplug_index++] = addr;
02dea087
JD
450 }
451
452 unplugged_pages_count++;
453 }
454 }
455
456 err = 0;
84f48d4f 457out_unlock:
e98fa281 458 mutex_unlock(&plug_mem_mutex);
02dea087
JD
459out:
460 return err;
461}
462
463static int mem_get_config(char *name, char *str, int size, char **error_out)
464{
465 char buf[sizeof("18446744073709551615")];
466 int len = 0;
467
468 sprintf(buf, "%ld", uml_physmem);
469 CONFIG_CHUNK(str, size, len, buf, 1);
470
471 return len;
472}
473
474static int mem_id(char **str, int *start_out, int *end_out)
475{
476 *start_out = 0;
477 *end_out = 0;
478
479 return 0;
480}
481
f28169d2 482static int mem_remove(int n, char **error_out)
02dea087 483{
f28169d2 484 *error_out = "Memory doesn't support the remove operation";
02dea087
JD
485 return -EBUSY;
486}
487
488static struct mc_device mem_mc = {
84f48d4f 489 .list = LIST_HEAD_INIT(mem_mc.list),
02dea087
JD
490 .name = "mem",
491 .config = mem_config,
492 .get_config = mem_get_config,
493 .id = mem_id,
494 .remove = mem_remove,
495};
496
97a1fcbb 497static int __init mem_mc_init(void)
02dea087 498{
ae2587e4 499 if (can_drop_memory())
02dea087 500 mconsole_register_dev(&mem_mc);
ae2587e4
JD
501 else printk(KERN_ERR "Can't release memory to the host - memory "
502 "hotplug won't be supported\n");
02dea087
JD
503 return 0;
504}
505
506__initcall(mem_mc_init);
507
1da177e4
LT
508#define CONFIG_BUF_SIZE 64
509
d50084a2 510static void mconsole_get_config(int (*get_config)(char *, char *, int,
1da177e4
LT
511 char **),
512 struct mc_request *req, char *name)
513{
514 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
515 int n, size;
516
ae2587e4 517 if (get_config == NULL) {
1da177e4
LT
518 mconsole_reply(req, "No get_config routine defined", 1, 0);
519 return;
520 }
521
522 error = NULL;
91b165c0 523 size = ARRAY_SIZE(default_buf);
1da177e4
LT
524 buf = default_buf;
525
ae2587e4 526 while (1) {
1da177e4 527 n = (*get_config)(name, buf, size, &error);
ae2587e4 528 if (error != NULL) {
1da177e4
LT
529 mconsole_reply(req, error, 1, 0);
530 goto out;
531 }
532
ae2587e4 533 if (n <= size) {
1da177e4
LT
534 mconsole_reply(req, buf, 0, 0);
535 goto out;
536 }
537
ae2587e4 538 if (buf != default_buf)
1da177e4
LT
539 kfree(buf);
540
541 size = n;
542 buf = kmalloc(size, GFP_KERNEL);
ae2587e4 543 if (buf == NULL) {
1da177e4
LT
544 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
545 return;
546 }
547 }
548 out:
ae2587e4 549 if (buf != default_buf)
1da177e4 550 kfree(buf);
1da177e4
LT
551}
552
553void mconsole_config(struct mc_request *req)
554{
555 struct mc_device *dev;
f28169d2 556 char *ptr = req->request.data, *name, *error_string = "";
1da177e4
LT
557 int err;
558
559 ptr += strlen("config");
ae2587e4
JD
560 while (isspace(*ptr))
561 ptr++;
1da177e4 562 dev = mconsole_find_dev(ptr);
ae2587e4 563 if (dev == NULL) {
1da177e4
LT
564 mconsole_reply(req, "Bad configuration option", 1, 0);
565 return;
566 }
567
568 name = &ptr[strlen(dev->name)];
569 ptr = name;
ae2587e4 570 while ((*ptr != '=') && (*ptr != '\0'))
1da177e4
LT
571 ptr++;
572
ae2587e4 573 if (*ptr == '=') {
f28169d2
JD
574 err = (*dev->config)(name, &error_string);
575 mconsole_reply(req, error_string, err, 0);
1da177e4
LT
576 }
577 else mconsole_get_config(dev->get_config, req, name);
578}
579
580void mconsole_remove(struct mc_request *req)
581{
d50084a2 582 struct mc_device *dev;
29d56cfe 583 char *ptr = req->request.data, *err_msg = "";
3a331a51 584 char error[256];
29d56cfe 585 int err, start, end, n;
1da177e4
LT
586
587 ptr += strlen("remove");
ae2587e4 588 while (isspace(*ptr)) ptr++;
1da177e4 589 dev = mconsole_find_dev(ptr);
ae2587e4 590 if (dev == NULL) {
1da177e4
LT
591 mconsole_reply(req, "Bad remove option", 1, 0);
592 return;
593 }
29d56cfe 594
3a331a51
JD
595 ptr = &ptr[strlen(dev->name)];
596
597 err = 1;
598 n = (*dev->id)(&ptr, &start, &end);
ae2587e4 599 if (n < 0) {
3a331a51
JD
600 err_msg = "Couldn't parse device number";
601 goto out;
602 }
ae2587e4 603 else if ((n < start) || (n > end)) {
3a331a51
JD
604 sprintf(error, "Invalid device number - must be between "
605 "%d and %d", start, end);
606 err_msg = error;
607 goto out;
608 }
29d56cfe 609
f28169d2
JD
610 err_msg = NULL;
611 err = (*dev->remove)(n, &err_msg);
ae2587e4 612 switch(err) {
d40f6d71
JD
613 case 0:
614 err_msg = "";
615 break;
3a331a51 616 case -ENODEV:
ae2587e4 617 if (err_msg == NULL)
f28169d2 618 err_msg = "Device doesn't exist";
3a331a51
JD
619 break;
620 case -EBUSY:
ae2587e4 621 if (err_msg == NULL)
f28169d2 622 err_msg = "Device is currently open";
3a331a51
JD
623 break;
624 default:
625 break;
626 }
627out:
29d56cfe 628 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
629}
630
f92afe56
JD
631struct mconsole_output {
632 struct list_head list;
633 struct mc_request *req;
634};
635
84f48d4f 636static DEFINE_SPINLOCK(client_lock);
6f517d3f
JD
637static LIST_HEAD(clients);
638static char console_buf[MCONSOLE_MAX_DATA];
6f517d3f
JD
639
640static void console_write(struct console *console, const char *string,
54fa0ba4 641 unsigned int len)
6f517d3f
JD
642{
643 struct list_head *ele;
644 int n;
645
ae2587e4 646 if (list_empty(&clients))
6f517d3f
JD
647 return;
648
54fa0ba4
JD
649 while (len > 0) {
650 n = min((size_t) len, ARRAY_SIZE(console_buf));
651 strncpy(console_buf, string, n);
6f517d3f
JD
652 string += n;
653 len -= n;
6f517d3f 654
ae2587e4 655 list_for_each(ele, &clients) {
f92afe56 656 struct mconsole_output *entry;
6f517d3f 657
f92afe56 658 entry = list_entry(ele, struct mconsole_output, list);
54fa0ba4 659 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
6f517d3f 660 }
6f517d3f
JD
661 }
662}
663
664static struct console mc_console = { .name = "mc",
665 .write = console_write,
a174b30e 666 .flags = CON_ENABLED,
6f517d3f
JD
667 .index = -1 };
668
669static int mc_add_console(void)
670{
671 register_console(&mc_console);
672 return 0;
673}
674
675late_initcall(mc_add_console);
676
677static void with_console(struct mc_request *req, void (*proc)(void *),
678 void *arg)
679{
f92afe56 680 struct mconsole_output entry;
6f517d3f
JD
681 unsigned long flags;
682
f92afe56 683 entry.req = req;
84f48d4f 684 spin_lock_irqsave(&client_lock, flags);
6f517d3f 685 list_add(&entry.list, &clients);
84f48d4f 686 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
687
688 (*proc)(arg);
689
54fa0ba4 690 mconsole_reply_len(req, "", 0, 0, 0);
6f517d3f 691
84f48d4f 692 spin_lock_irqsave(&client_lock, flags);
6f517d3f 693 list_del(&entry.list);
84f48d4f 694 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
695}
696
4111b025 697#ifdef CONFIG_MAGIC_SYSRQ
54fa0ba4
JD
698
699#include <linux/sysrq.h>
700
4111b025
JD
701static void sysrq_proc(void *arg)
702{
703 char *op = arg;
7bea96fd 704 handle_sysrq(*op, NULL);
4111b025
JD
705}
706
707void mconsole_sysrq(struct mc_request *req)
708{
709 char *ptr = req->request.data;
710
711 ptr += strlen("sysrq");
ae2587e4 712 while (isspace(*ptr)) ptr++;
4111b025 713
ae2587e4
JD
714 /*
715 * With 'b', the system will shut down without a chance to reply,
4111b025
JD
716 * so in this case, we reply first.
717 */
ae2587e4 718 if (*ptr == 'b')
4111b025
JD
719 mconsole_reply(req, "", 0, 0);
720
721 with_console(req, sysrq_proc, ptr);
722}
723#else
724void mconsole_sysrq(struct mc_request *req)
725{
726 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
727}
728#endif
729
6f517d3f
JD
730static void stack_proc(void *arg)
731{
732 struct task_struct *from = current, *to = arg;
733
734 to->thread.saved_task = from;
735 switch_to(from, to, from);
736}
737
ae2587e4
JD
738/*
739 * Mconsole stack trace
3eddddcf
JD
740 * Added by Allan Graves, Jeff Dike
741 * Dumps a stacks registers to the linux console.
742 * Usage stack <pid>.
743 */
42fda663 744void mconsole_stack(struct mc_request *req)
3eddddcf 745{
3a331a51
JD
746 char *ptr = req->request.data;
747 int pid_requested= -1;
3eddddcf
JD
748 struct task_struct *to = NULL;
749
ae2587e4
JD
750 /*
751 * Would be nice:
3a331a51 752 * 1) Send showregs output to mconsole.
3eddddcf
JD
753 * 2) Add a way to stack dump all pids.
754 */
755
3a331a51 756 ptr += strlen("stack");
ae2587e4
JD
757 while (isspace(*ptr))
758 ptr++;
3eddddcf 759
ae2587e4
JD
760 /*
761 * Should really check for multiple pids or reject bad args here
762 */
3a331a51 763 /* What do the arguments in mconsole_reply mean? */
ae2587e4 764 if (sscanf(ptr, "%d", &pid_requested) == 0) {
3a331a51
JD
765 mconsole_reply(req, "Please specify a pid", 1, 0);
766 return;
767 }
3eddddcf 768
827b3f6a 769 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
ae2587e4 770 if ((to == NULL) || (pid_requested == 0)) {
3a331a51
JD
771 mconsole_reply(req, "Couldn't find that pid", 1, 0);
772 return;
773 }
6f517d3f 774 with_console(req, stack_proc, to);
3eddddcf 775}
3eddddcf 776
ae2587e4
JD
777/*
778 * Changed by mconsole_setup, which is __setup, and called before SMP is
1da177e4
LT
779 * active.
780 */
d50084a2 781static char *notify_socket = NULL;
1da177e4 782
97a1fcbb 783static int __init mconsole_init(void)
1da177e4
LT
784{
785 /* long to avoid size mismatch warnings from gcc */
786 long sock;
787 int err;
788 char file[256];
789
ae2587e4
JD
790 if (umid_file_name("mconsole", file, sizeof(file)))
791 return -1;
1da177e4
LT
792 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
793
794 sock = os_create_unix_socket(file, sizeof(file), 1);
ae2587e4
JD
795 if (sock < 0) {
796 printk(KERN_ERR "Failed to initialize management console\n");
797 return 1;
1da177e4 798 }
438ee679
JD
799 if (os_set_fd_block(sock, 0))
800 goto out;
1da177e4
LT
801
802 register_reboot_notifier(&reboot_notifier);
803
804 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
bd6aa650 805 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
1da177e4 806 "mconsole", (void *)sock);
ae2587e4
JD
807 if (err) {
808 printk(KERN_ERR "Failed to get IRQ for management console\n");
438ee679 809 goto out;
1da177e4
LT
810 }
811
ae2587e4 812 if (notify_socket != NULL) {
970d6e3a 813 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
ae2587e4 814 if (notify_socket != NULL)
1da177e4 815 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
d50084a2 816 mconsole_socket_name,
1da177e4
LT
817 strlen(mconsole_socket_name) + 1);
818 else printk(KERN_ERR "mconsole_setup failed to strdup "
819 "string\n");
820 }
821
ae2587e4 822 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
1da177e4 823 MCONSOLE_VERSION, mconsole_socket_name);
ae2587e4 824 return 0;
438ee679
JD
825
826 out:
827 os_close_file(sock);
828 return 1;
1da177e4
LT
829}
830
831__initcall(mconsole_init);
832
833static int write_proc_mconsole(struct file *file, const char __user *buffer,
834 unsigned long count, void *data)
835{
836 char *buf;
837
838 buf = kmalloc(count + 1, GFP_KERNEL);
ae2587e4
JD
839 if (buf == NULL)
840 return -ENOMEM;
1da177e4 841
ae2587e4 842 if (copy_from_user(buf, buffer, count)) {
1da177e4
LT
843 count = -EFAULT;
844 goto out;
845 }
846
847 buf[count] = '\0';
848
849 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
850 out:
851 kfree(buf);
ae2587e4 852 return count;
1da177e4
LT
853}
854
855static int create_proc_mconsole(void)
856{
857 struct proc_dir_entry *ent;
858
ae2587e4
JD
859 if (notify_socket == NULL)
860 return 0;
1da177e4
LT
861
862 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
ae2587e4
JD
863 if (ent == NULL) {
864 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
865 "failed\n");
866 return 0;
1da177e4
LT
867 }
868
869 ent->read_proc = NULL;
870 ent->write_proc = write_proc_mconsole;
ae2587e4 871 return 0;
1da177e4
LT
872}
873
874static DEFINE_SPINLOCK(notify_spinlock);
875
876void lock_notify(void)
877{
878 spin_lock(&notify_spinlock);
879}
880
881void unlock_notify(void)
882{
883 spin_unlock(&notify_spinlock);
884}
885
886__initcall(create_proc_mconsole);
887
088bec41 888#define NOTIFY "notify:"
1da177e4
LT
889
890static int mconsole_setup(char *str)
891{
ae2587e4 892 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
1da177e4
LT
893 str += strlen(NOTIFY);
894 notify_socket = str;
895 }
896 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
ae2587e4 897 return 1;
1da177e4
LT
898}
899
088bec41 900__setup("mconsole=", mconsole_setup);
1da177e4
LT
901
902__uml_help(mconsole_setup,
903"mconsole=notify:<socket>\n"
904" Requests that the mconsole driver send a message to the named Unix\n"
905" socket containing the name of the mconsole socket. This also serves\n"
906" to notify outside processes when UML has booted far enough to respond\n"
907" to mconsole requests.\n\n"
908);
909
910static int notify_panic(struct notifier_block *self, unsigned long unused1,
911 void *ptr)
912{
913 char *message = ptr;
914
ae2587e4
JD
915 if (notify_socket == NULL)
916 return 0;
1da177e4 917
d50084a2 918 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
1da177e4 919 strlen(message) + 1);
ae2587e4 920 return 0;
1da177e4
LT
921}
922
923static struct notifier_block panic_exit_notifier = {
924 .notifier_call = notify_panic,
925 .next = NULL,
926 .priority = 1
927};
928
929static int add_notifier(void)
930{
e041c683
AS
931 atomic_notifier_chain_register(&panic_notifier_list,
932 &panic_exit_notifier);
ae2587e4 933 return 0;
1da177e4
LT
934}
935
936__initcall(add_notifier);
937
938char *mconsole_notify_socket(void)
939{
ae2587e4 940 return notify_socket;
1da177e4
LT
941}
942
943EXPORT_SYMBOL(mconsole_notify_socket);