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