uml: throw out CHOOSE_MODE
[safe/jmp/linux-2.6] / arch / um / kernel / irq.c
index 3c01c2b..ec1ed68 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
  * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c:
 #include "asm/system.h"
 #include "asm/errno.h"
 #include "asm/uaccess.h"
-#include "user_util.h"
 #include "kern_util.h"
 #include "irq_user.h"
 #include "irq_kern.h"
 #include "os.h"
 #include "sigio.h"
 #include "misc_constants.h"
+#include "as-layout.h"
 
 /*
  * Generic, controller-independent functions:
@@ -53,7 +53,7 @@ int show_interrupts(struct seq_file *p, void *v)
        if (i < NR_IRQS) {
                spin_lock_irqsave(&irq_desc[i].lock, flags);
                action = irq_desc[i].action;
-               if (!action) 
+               if (!action)
                        goto skip;
                seq_printf(p, "%3d: ",i);
 #ifndef CONFIG_SMP
@@ -78,7 +78,15 @@ skip:
        return 0;
 }
 
-struct irq_fd *active_fds = NULL;
+/*
+ * This list is accessed under irq_lock, except in sigio_handler,
+ * where it is safe from being modified.  IRQ handlers won't change it -
+ * if an IRQ source has vanished, it will be freed by free_irqs just
+ * before returning from sigio_handler.  That will process a separate
+ * list of irqs to free, with its own locking, coming back here to
+ * remove list elements, taking the irq_lock to do so.
+ */
+static struct irq_fd *active_fds = NULL;
 static struct irq_fd **last_irq_ptr = &active_fds;
 
 extern void free_irqs(void);
@@ -123,8 +131,8 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
        if (err < 0)
                goto out;
 
-       new_fd = um_kmalloc(sizeof(*new_fd));
        err = -ENOMEM;
+       new_fd = kmalloc(sizeof(struct irq_fd), GFP_KERNEL);
        if (new_fd == NULL)
                goto out;
 
@@ -141,6 +149,7 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
                                     .events            = events,
                                     .current_events    = 0 } );
 
+       err = -EBUSY;
        spin_lock_irqsave(&irq_lock, flags);
        for (irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next) {
                if ((irq_fd->fd == fd) && (irq_fd->type == type)) {
@@ -175,9 +184,8 @@ int activate_fd(int irq, int fd, int type, void *dev_id)
                 */
                spin_unlock_irqrestore(&irq_lock, flags);
                kfree(tmp_pfd);
-               tmp_pfd = NULL;
 
-               tmp_pfd = um_kmalloc(n);
+               tmp_pfd = kmalloc(n, GFP_KERNEL);
                if (tmp_pfd == NULL)
                        goto out_kfree;
 
@@ -243,6 +251,7 @@ void free_irq_by_fd(int fd)
        free_irq_by_cb(same_fd, &fd);
 }
 
+/* Must be called with irq_lock held */
 static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
 {
        struct irq_fd *irq;
@@ -308,6 +317,12 @@ void deactivate_fd(int fd, int irqnum)
        ignore_sigio_fd(fd);
 }
 
+/*
+ * Called just before shutdown in order to provide a clean exec
+ * environment in case the system is rebooting.  No locking because
+ * that would cause a pointless shutdown hang if something hadn't
+ * released the lock.
+ */
 int deactivate_all_fds(void)
 {
        struct irq_fd *irq;
@@ -324,30 +339,6 @@ int deactivate_all_fds(void)
        return 0;
 }
 
-#ifdef CONFIG_MODE_TT
-void forward_interrupts(int pid)
-{
-       struct irq_fd *irq;
-       unsigned long flags;
-       int err;
-
-       spin_lock_irqsave(&irq_lock, flags);
-       for (irq = active_fds; irq != NULL; irq = irq->next) {
-               err = os_set_owner(irq->fd, pid);
-               if (err < 0) {
-                       /* XXX Just remove the irq rather than
-                        * print out an infinite stream of these
-                        */
-                       printk("Failed to forward %d to pid %d, err = %d\n",
-                              irq->fd, pid, -err);
-               }
-
-               irq->pid = pid;
-       }
-       spin_unlock_irqrestore(&irq_lock, flags);
-}
-#endif
-
 /*
  * do_IRQ handles all normal device IRQ's (the special
  * SMP cross-CPU interrupts have their own specific
@@ -364,7 +355,7 @@ unsigned int do_IRQ(int irq, union uml_pt_regs *regs)
 }
 
 int um_request_irq(unsigned int irq, int fd, int type,
-                  irqreturn_t (*handler)(int, void *),
+                  irq_handler_t handler,
                   unsigned long irqflags, const char * devname,
                   void *dev_id)
 {
@@ -425,7 +416,7 @@ void __init init_IRQ(void)
        }
 }
 
-int init_aio_irq(int irq, char *name, irqreturn_t (*handler)(int, void *))
+int init_aio_irq(int irq, char *name, irq_handler_t handler)
 {
        int fds[2], err;
 
@@ -453,3 +444,114 @@ int init_aio_irq(int irq, char *name, irqreturn_t (*handler)(int, void *))
  out:
        return err;
 }
+
+/*
+ * IRQ stack entry and exit:
+ *
+ * Unlike i386, UML doesn't receive IRQs on the normal kernel stack
+ * and switch over to the IRQ stack after some preparation.  We use
+ * sigaltstack to receive signals on a separate stack from the start.
+ * These two functions make sure the rest of the kernel won't be too
+ * upset by being on a different stack.  The IRQ stack has a
+ * thread_info structure at the bottom so that current et al continue
+ * to work.
+ *
+ * to_irq_stack copies the current task's thread_info to the IRQ stack
+ * thread_info and sets the tasks's stack to point to the IRQ stack.
+ *
+ * from_irq_stack copies the thread_info struct back (flags may have
+ * been modified) and resets the task's stack pointer.
+ *
+ * Tricky bits -
+ *
+ * What happens when two signals race each other?  UML doesn't block
+ * signals with sigprocmask, SA_DEFER, or sa_mask, so a second signal
+ * could arrive while a previous one is still setting up the
+ * thread_info.
+ *
+ * There are three cases -
+ *     The first interrupt on the stack - sets up the thread_info and
+ * handles the interrupt
+ *     A nested interrupt interrupting the copying of the thread_info -
+ * can't handle the interrupt, as the stack is in an unknown state
+ *     A nested interrupt not interrupting the copying of the
+ * thread_info - doesn't do any setup, just handles the interrupt
+ *
+ * The first job is to figure out whether we interrupted stack setup.
+ * This is done by xchging the signal mask with thread_info->pending.
+ * If the value that comes back is zero, then there is no setup in
+ * progress, and the interrupt can be handled.  If the value is
+ * non-zero, then there is stack setup in progress.  In order to have
+ * the interrupt handled, we leave our signal in the mask, and it will
+ * be handled by the upper handler after it has set up the stack.
+ *
+ * Next is to figure out whether we are the outer handler or a nested
+ * one.  As part of setting up the stack, thread_info->real_thread is
+ * set to non-NULL (and is reset to NULL on exit).  This is the
+ * nesting indicator.  If it is non-NULL, then the stack is already
+ * set up and the handler can run.
+ */
+
+static unsigned long pending_mask;
+
+unsigned long to_irq_stack(unsigned long *mask_out)
+{
+       struct thread_info *ti;
+       unsigned long mask, old;
+       int nested;
+
+       mask = xchg(&pending_mask, *mask_out);
+       if(mask != 0){
+               /* If any interrupts come in at this point, we want to
+                * make sure that their bits aren't lost by our
+                * putting our bit in.  So, this loop accumulates bits
+                * until xchg returns the same value that we put in.
+                * When that happens, there were no new interrupts,
+                * and pending_mask contains a bit for each interrupt
+                * that came in.
+                */
+               old = *mask_out;
+               do {
+                       old |= mask;
+                       mask = xchg(&pending_mask, old);
+               } while(mask != old);
+               return 1;
+       }
+
+       ti = current_thread_info();
+       nested = (ti->real_thread != NULL);
+       if(!nested){
+               struct task_struct *task;
+               struct thread_info *tti;
+
+               task = cpu_tasks[ti->cpu].task;
+               tti = task_thread_info(task);
+
+               *ti = *tti;
+               ti->real_thread = tti;
+               task->stack = ti;
+       }
+
+       mask = xchg(&pending_mask, 0);
+       *mask_out |= mask | nested;
+       return 0;
+}
+
+unsigned long from_irq_stack(int nested)
+{
+       struct thread_info *ti, *to;
+       unsigned long mask;
+
+       ti = current_thread_info();
+
+       pending_mask = 1;
+
+       to = ti->real_thread;
+       current->stack = to;
+       ti->real_thread = NULL;
+       *to = *ti;
+
+       mask = xchg(&pending_mask, 0);
+       return mask & ~1;
+}
+