]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/um/os-Linux/signal.c
uml: more __init annotations
[linux-2.6-omap-h63xx.git] / arch / um / os-Linux / signal.c
index f11b3124a0c86cd22850e178e7c234fe20f9ad7e..18e5c8b67eb8cebbe84db25bed7e64e623522dc9 100644 (file)
 #include <stdarg.h>
 #include <string.h>
 #include <sys/mman.h>
-#include "user_util.h"
 #include "user.h"
 #include "signal_kern.h"
 #include "sysdep/sigcontext.h"
-#include "sysdep/signal.h"
+#include "sysdep/barrier.h"
 #include "sigcontext.h"
 #include "mode.h"
 #include "os.h"
 #define SIGALRM_BIT 2
 #define SIGALRM_MASK (1 << SIGALRM_BIT)
 
-static int signals_enabled = 1;
-static int pending = 0;
+/* These are used by both the signal handlers and
+ * block/unblock_signals.  I don't want modifications cached in a
+ * register - they must go straight to memory.
+ */
+static volatile int signals_enabled = 1;
+static volatile int pending = 0;
 
-void sig_handler(ARCH_SIGHDLR_PARAM)
+void sig_handler(int sig, struct sigcontext *sc)
 {
-       struct sigcontext *sc;
        int enabled;
 
-       /* Must be the first thing that this handler does - x86_64 stores
-        * the sigcontext in %rdx, and we need to save it before it has a
-        * chance to get trashed.
-        */
-
-       ARCH_GET_SIGCONTEXT(sc, sig);
-
        enabled = signals_enabled;
        if(!enabled && (sig == SIGIO)){
                pending |= SIGIO_MASK;
@@ -64,33 +59,27 @@ void sig_handler(ARCH_SIGHDLR_PARAM)
        set_signals(enabled);
 }
 
-extern int timer_irq_inited;
-
 static void real_alarm_handler(int sig, struct sigcontext *sc)
 {
-       if(!timer_irq_inited){
-               signals_enabled = 1;
-               return;
-       }
+       union uml_pt_regs regs;
 
        if(sig == SIGALRM)
                switch_timers(0);
 
-       CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
-                        sig, sc);
+       if(sc != NULL)
+               copy_sc(&regs, sc);
+       regs.skas.is_user = 0;
+       unblock_signals();
+       timer_handler(sig, &regs);
 
        if(sig == SIGALRM)
                switch_timers(1);
-
 }
 
-void alarm_handler(ARCH_SIGHDLR_PARAM)
+void alarm_handler(int sig, struct sigcontext *sc)
 {
-       struct sigcontext *sc;
        int enabled;
 
-       ARCH_GET_SIGCONTEXT(sc, sig);
-
        enabled = signals_enabled;
        if(!signals_enabled){
                if(sig == SIGVTALRM)
@@ -106,29 +95,6 @@ void alarm_handler(ARCH_SIGHDLR_PARAM)
        set_signals(enabled);
 }
 
-extern void do_boot_timer_handler(struct sigcontext * sc);
-
-void boot_timer_handler(ARCH_SIGHDLR_PARAM)
-{
-       struct sigcontext *sc;
-       int enabled;
-
-       ARCH_GET_SIGCONTEXT(sc, sig);
-
-       enabled = signals_enabled;
-       if(!enabled){
-               if(sig == SIGVTALRM)
-                       pending |= SIGVTALRM_MASK;
-               else pending |= SIGALRM_MASK;
-               return;
-       }
-
-       block_signals();
-
-       do_boot_timer_handler(sc);
-       set_signals(enabled);
-}
-
 void set_sigstack(void *sig_stack, int size)
 {
        stack_t stack = ((stack_t) { .ss_flags  = 0,
@@ -149,6 +115,50 @@ void remove_sigstack(void)
                panic("disabling signal stack failed, errno = %d\n", errno);
 }
 
+void (*handlers[_NSIG])(int sig, struct sigcontext *sc);
+
+void handle_signal(int sig, struct sigcontext *sc)
+{
+       unsigned long pending = 0;
+
+       do {
+               int nested, bail;
+
+               /*
+                * pending comes back with one bit set for each
+                * interrupt that arrived while setting up the stack,
+                * plus a bit for this interrupt, plus the zero bit is
+                * set if this is a nested interrupt.
+                * If bail is true, then we interrupted another
+                * handler setting up the stack.  In this case, we
+                * have to return, and the upper handler will deal
+                * with this interrupt.
+                */
+               bail = to_irq_stack(sig, &pending);
+               if(bail)
+                       return;
+
+               nested = pending & 1;
+               pending &= ~1;
+
+               while((sig = ffs(pending)) != 0){
+                       sig--;
+                       pending &= ~(1 << sig);
+                       (*handlers[sig])(sig, sc);
+               }
+
+               /* Again, pending comes back with a mask of signals
+                * that arrived while tearing down the stack.  If this
+                * is non-zero, we just go back, set up the stack
+                * again, and handle the new interrupts.
+                */
+               if(!nested)
+                       pending = from_irq_stack(nested);
+       } while(pending);
+}
+
+extern void hard_handler(int sig);
+
 void set_handler(int sig, void (*handler)(int), int flags, ...)
 {
        struct sigaction action;
@@ -156,13 +166,16 @@ void set_handler(int sig, void (*handler)(int), int flags, ...)
        sigset_t sig_mask;
        int mask;
 
-       va_start(ap, flags);
-       action.sa_handler = handler;
+       handlers[sig] = (void (*)(int, struct sigcontext *)) handler;
+       action.sa_handler = hard_handler;
+
        sigemptyset(&action.sa_mask);
-       while((mask = va_arg(ap, int)) != -1){
+
+       va_start(ap, flags);
+       while((mask = va_arg(ap, int)) != -1)
                sigaddset(&action.sa_mask, mask);
-       }
        va_end(ap);
+
        action.sa_flags = flags;
        action.sa_restorer = NULL;
        if(sigaction(sig, &action, NULL) < 0)
@@ -187,6 +200,12 @@ int change_sig(int signal, int on)
 void block_signals(void)
 {
        signals_enabled = 0;
+       /* This must return with signals disabled, so this barrier
+        * ensures that writes are flushed out before the return.
+        * This might matter if gcc figures out how to inline this and
+        * decides to shuffle this code into the caller.
+        */
+       mb();
 }
 
 void unblock_signals(void)
@@ -206,9 +225,23 @@ void unblock_signals(void)
                 */
                signals_enabled = 1;
 
+               /* Setting signals_enabled and reading pending must
+                * happen in this order.
+                */
+               mb();
+
                save_pending = pending;
-               if(save_pending == 0)
+               if(save_pending == 0){
+                       /* This must return with signals enabled, so
+                        * this barrier ensures that writes are
+                        * flushed out before the return.  This might
+                        * matter if gcc figures out how to inline
+                        * this (unlikely, given its size) and decides
+                        * to shuffle this code into the caller.
+                        */
+                       mb();
                        return;
+               }
 
                pending = 0;
 
@@ -253,8 +286,3 @@ int set_signals(int enable)
 
        return ret;
 }
-
-void os_usr1_signal(int on)
-{
-       change_sig(SIGUSR1, on);
-}