Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[linux-drm-fsl-dcu.git] / arch / um / drivers / mconsole_kern.c
index a67dcbd78de4765c7f7f54d7f907cfd01449c2c3..178b2eff4a8ce836b5286aae5e32429480a2cf66 100644 (file)
@@ -33,7 +33,6 @@
 #include "irq_user.h"
 #include "init.h"
 #include "os.h"
-#include "umid.h"
 #include "irq_kern.h"
 #include "choose-mode.h"
 
@@ -56,7 +55,7 @@ static struct notifier_block reboot_notifier = {
 
 static LIST_HEAD(mc_requests);
 
-static void mc_work_proc(void *unused)
+static void mc_work_proc(struct work_struct *unused)
 {
        struct mconsole_entry *req;
        unsigned long flags;
@@ -72,15 +71,14 @@ static void mc_work_proc(void *unused)
        }
 }
 
-static DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
+static DECLARE_WORK(mconsole_work, mc_work_proc);
 
-static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
-                                     struct pt_regs *regs)
+static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
 {
        /* long to avoid size mismatch warnings from gcc */
        long fd;
        struct mconsole_entry *new;
-       struct mc_request req;
+       static struct mc_request req;   /* that's OK */
 
        fd = (long) dev_id;
        while (mconsole_get_request(fd, &req)){
@@ -92,6 +90,7 @@ static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
                                mconsole_reply(&req, "Out of memory", 1, 0);
                        else {
                                new->request = req;
+                               new->request.regs = get_irq_regs()->regs;
                                list_add(&new->list, &mc_requests);
                        }
                }
@@ -315,9 +314,21 @@ void mconsole_stop(struct mc_request *req)
 {
        deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
        os_set_fd_block(req->originating_fd, 1);
-       mconsole_reply(req, "", 0, 0);
-       while(mconsole_get_request(req->originating_fd, req)){
-               if(req->cmd->handler == mconsole_go) break;
+       mconsole_reply(req, "stopped", 0, 0);
+       while (mconsole_get_request(req->originating_fd, req)) {
+               if (req->cmd->handler == mconsole_go)
+                       break;
+               if (req->cmd->handler == mconsole_stop) {
+                       mconsole_reply(req, "Already stopped", 1, 0);
+                       continue;
+               }
+               if (req->cmd->handler == mconsole_sysrq) {
+                       struct pt_regs *old_regs;
+                       old_regs = set_irq_regs((struct pt_regs *)&req->regs);
+                       mconsole_sysrq(req);
+                       set_irq_regs(old_regs);
+                       continue;
+               }
                (*req->cmd->handler)(req);
        }
        os_set_fd_block(req->originating_fd, 0);
@@ -325,13 +336,15 @@ void mconsole_stop(struct mc_request *req)
        mconsole_reply(req, "", 0, 0);
 }
 
-/* This list is populated by __initcall routines. */
-
+static DEFINE_SPINLOCK(mc_devices_lock);
 static LIST_HEAD(mconsole_devices);
 
 void mconsole_register_dev(struct mc_device *new)
 {
+       spin_lock(&mc_devices_lock);
+       BUG_ON(!list_empty(&new->list));
        list_add(&new->list, &mconsole_devices);
+       spin_unlock(&mc_devices_lock);
 }
 
 static struct mc_device *mconsole_find_dev(char *name)
@@ -355,18 +368,21 @@ struct unplugged_pages {
        void *pages[UNPLUGGED_PER_PAGE];
 };
 
+static DECLARE_MUTEX(plug_mem_mutex);
 static unsigned long long unplugged_pages_count = 0;
-static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
+static LIST_HEAD(unplugged_pages);
 static int unplug_index = UNPLUGGED_PER_PAGE;
 
-static int mem_config(char *str)
+static int mem_config(char *str, char **error_out)
 {
        unsigned long long diff;
        int err = -EINVAL, i, add;
        char *ret;
 
-       if(str[0] != '=')
+       if(str[0] != '='){
+               *error_out = "Expected '=' after 'mem'";
                goto out;
+       }
 
        str++;
        if(str[0] == '-')
@@ -374,15 +390,21 @@ static int mem_config(char *str)
        else if(str[0] == '+'){
                add = 1;
        }
-       else goto out;
+       else {
+               *error_out = "Expected increment to start with '-' or '+'";
+               goto out;
+       }
 
        str++;
        diff = memparse(str, &ret);
-       if(*ret != '\0')
+       if(*ret != '\0'){
+               *error_out = "Failed to parse memory increment";
                goto out;
+       }
 
        diff /= PAGE_SIZE;
 
+       down(&plug_mem_mutex);
        for(i = 0; i < diff; i++){
                struct unplugged_pages *unplugged;
                void *addr;
@@ -423,11 +445,14 @@ static int mem_config(char *str)
                                unplugged = list_entry(entry,
                                                       struct unplugged_pages,
                                                       list);
-                               unplugged->pages[unplug_index++] = addr;
                                err = os_drop_memory(addr, PAGE_SIZE);
-                               if(err)
+                               if(err){
                                        printk("Failed to release memory - "
                                               "errno = %d\n", err);
+                                       *error_out = "Failed to release memory";
+                                       goto out_unlock;
+                               }
+                               unplugged->pages[unplug_index++] = addr;
                        }
 
                        unplugged_pages_count++;
@@ -435,6 +460,8 @@ static int mem_config(char *str)
        }
 
        err = 0;
+out_unlock:
+       up(&plug_mem_mutex);
 out:
        return err;
 }
@@ -458,12 +485,14 @@ static int mem_id(char **str, int *start_out, int *end_out)
        return 0;
 }
 
-static int mem_remove(int n)
+static int mem_remove(int n, char **error_out)
 {
+       *error_out = "Memory doesn't support the remove operation";
        return -EBUSY;
 }
 
 static struct mc_device mem_mc = {
+       .list           = LIST_HEAD_INIT(mem_mc.list),
        .name           = "mem",
        .config         = mem_config,
        .get_config     = mem_get_config,
@@ -530,7 +559,7 @@ static void mconsole_get_config(int (*get_config)(char *, char *, int,
 void mconsole_config(struct mc_request *req)
 {
        struct mc_device *dev;
-       char *ptr = req->request.data, *name;
+       char *ptr = req->request.data, *name, *error_string = "";
        int err;
 
        ptr += strlen("config");
@@ -547,8 +576,8 @@ void mconsole_config(struct mc_request *req)
                ptr++;
 
        if(*ptr == '='){
-               err = (*dev->config)(name);
-               mconsole_reply(req, "", err, 0);
+               err = (*dev->config)(name, &error_string);
+               mconsole_reply(req, error_string, err, 0);
        }
        else mconsole_get_config(dev->get_config, req, name);
 }
@@ -583,13 +612,16 @@ void mconsole_remove(struct mc_request *req)
                goto out;
        }
 
-       err = (*dev->remove)(n);
+       err_msg = NULL;
+       err = (*dev->remove)(n, &err_msg);
        switch(err){
        case -ENODEV:
-               err_msg = "Device doesn't exist";
+               if(err_msg == NULL)
+                       err_msg = "Device doesn't exist";
                break;
        case -EBUSY:
-               err_msg = "Device is currently open";
+               if(err_msg == NULL)
+                       err_msg = "Device is currently open";
                break;
        default:
                break;
@@ -603,7 +635,7 @@ struct mconsole_output {
        struct mc_request *req;
 };
 
-static DEFINE_SPINLOCK(console_lock);
+static DEFINE_SPINLOCK(client_lock);
 static LIST_HEAD(clients);
 static char console_buf[MCONSOLE_MAX_DATA];
 static int console_index = 0;
@@ -658,24 +690,25 @@ static void with_console(struct mc_request *req, void (*proc)(void *),
        unsigned long flags;
 
        entry.req = req;
+       spin_lock_irqsave(&client_lock, flags);
        list_add(&entry.list, &clients);
-       spin_lock_irqsave(&console_lock, flags);
+       spin_unlock_irqrestore(&client_lock, flags);
 
        (*proc)(arg);
 
        mconsole_reply_len(req, console_buf, console_index, 0, 0);
        console_index = 0;
 
-       spin_unlock_irqrestore(&console_lock, flags);
+       spin_lock_irqsave(&client_lock, flags);
        list_del(&entry.list);
+       spin_unlock_irqrestore(&client_lock, flags);
 }
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static void sysrq_proc(void *arg)
 {
        char *op = arg;
-
-       handle_sysrq(*op, &current->thread.regs, NULL);
+       handle_sysrq(*op, NULL);
 }
 
 void mconsole_sysrq(struct mc_request *req)