X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fum%2Fos-Linux%2Fsignal.c;h=b897e8592d7713c3730bfb4ab15f0b9ca716eb1d;hb=a2f44b27303a5353859d77a3e96a1d3f33f56ab7;hp=c7bfd5ee392573b5a79c9eb8dc676b00306c7046;hpb=b14c9c33594c5c8da3a20863d663f3709ed508ec;p=linux-2.6.git diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index c7bfd5ee3..b897e8592 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -4,28 +4,64 @@ */ #include -#include "time_user.h" +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "user.h" +#include "signal_kern.h" +#include "sysdep/sigcontext.h" +#include "sysdep/barrier.h" +#include "sigcontext.h" #include "mode.h" -#include "sysdep/signal.h" +#include "os.h" -void sig_handler(ARCH_SIGHDLR_PARAM) +/* These are the asynchronous signals. SIGVTALRM and SIGARLM are handled + * together under SIGVTALRM_BIT. SIGPROF is excluded because we want to + * be able to profile all of UML, not just the non-critical sections. If + * profiling is not thread-safe, then that is not my problem. We can disable + * profiling when SMP is enabled in that case. + */ +#define SIGIO_BIT 0 +#define SIGIO_MASK (1 << SIGIO_BIT) + +#define SIGVTALRM_BIT 1 +#define SIGVTALRM_MASK (1 << SIGVTALRM_BIT) + +#define SIGALRM_BIT 2 +#define SIGALRM_MASK (1 << SIGALRM_BIT) + +/* 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(int sig, struct sigcontext *sc) { - struct sigcontext *sc; + int enabled; + + enabled = signals_enabled; + if(!enabled && (sig == SIGIO)){ + pending |= SIGIO_MASK; + return; + } + + block_signals(); - ARCH_GET_SIGCONTEXT(sc, sig); CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas, sig, sc); -} -extern int timer_irq_inited; + set_signals(enabled); +} -void alarm_handler(ARCH_SIGHDLR_PARAM) +static void real_alarm_handler(int sig, struct sigcontext *sc) { - struct sigcontext *sc; - - ARCH_GET_SIGCONTEXT(sc, sig); - if(!timer_irq_inited) return; - if(sig == SIGALRM) switch_timers(0); @@ -34,15 +70,181 @@ void alarm_handler(ARCH_SIGHDLR_PARAM) if(sig == SIGALRM) switch_timers(1); + } -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ +void alarm_handler(int sig, struct sigcontext *sc) +{ + int enabled; + + enabled = signals_enabled; + if(!signals_enabled){ + if(sig == SIGVTALRM) + pending |= SIGVTALRM_MASK; + else pending |= SIGALRM_MASK; + + return; + } + + block_signals(); + + real_alarm_handler(sig, sc); + set_signals(enabled); +} + +void set_sigstack(void *sig_stack, int size) +{ + stack_t stack = ((stack_t) { .ss_flags = 0, + .ss_sp = (__ptr_t) sig_stack, + .ss_size = size - sizeof(void *) }); + + if(sigaltstack(&stack, NULL) != 0) + panic("enabling signal stack failed, errno = %d\n", errno); +} + +void remove_sigstack(void) +{ + stack_t stack = ((stack_t) { .ss_flags = SS_DISABLE, + .ss_sp = NULL, + .ss_size = 0 }); + + if(sigaltstack(&stack, NULL) != 0) + panic("disabling signal stack failed, errno = %d\n", errno); +} + +void (*handlers[_NSIG])(int sig, struct sigcontext *sc); + +extern void hard_handler(int sig); + +void set_handler(int sig, void (*handler)(int), int flags, ...) +{ + struct sigaction action; + va_list ap; + sigset_t sig_mask; + int mask; + + handlers[sig] = (void (*)(int, struct sigcontext *)) handler; + action.sa_handler = hard_handler; + + sigemptyset(&action.sa_mask); + + 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) + panic("sigaction failed - errno = %d\n", errno); + + sigemptyset(&sig_mask); + sigaddset(&sig_mask, sig); + if(sigprocmask(SIG_UNBLOCK, &sig_mask, NULL) < 0) + panic("sigprocmask failed - errno = %d\n", errno); +} + +int change_sig(int signal, int on) +{ + sigset_t sigset, old; + + sigemptyset(&sigset); + sigaddset(&sigset, signal); + sigprocmask(on ? SIG_UNBLOCK : SIG_BLOCK, &sigset, &old); + return(!sigismember(&old, signal)); +} + +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) +{ + int save_pending; + + if(signals_enabled == 1) + return; + + /* We loop because the IRQ handler returns with interrupts off. So, + * interrupts may have arrived and we need to re-enable them and + * recheck pending. + */ + while(1){ + /* Save and reset save_pending after enabling signals. This + * way, pending won't be changed while we're reading it. + */ + signals_enabled = 1; + + /* Setting signals_enabled and reading pending must + * happen in this order. + */ + mb(); + + save_pending = pending; + 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; + + /* We have pending interrupts, so disable signals, as the + * handlers expect them off when they are called. They will + * be enabled again above. + */ + + signals_enabled = 0; + + /* Deal with SIGIO first because the alarm handler might + * schedule, leaving the pending SIGIO stranded until we come + * back here. + */ + if(save_pending & SIGIO_MASK) + CHOOSE_MODE_PROC(sig_handler_common_tt, + sig_handler_common_skas, SIGIO, NULL); + + if(save_pending & SIGALRM_MASK) + real_alarm_handler(SIGALRM, NULL); + + if(save_pending & SIGVTALRM_MASK) + real_alarm_handler(SIGVTALRM, NULL); + } +} + +int get_signals(void) +{ + return signals_enabled; +} + +int set_signals(int enable) +{ + int ret; + if(signals_enabled == enable) + return enable; + + ret = signals_enabled; + if(enable) + unblock_signals(); + else block_signals(); + + return ret; +} + +void os_usr1_signal(int on) +{ + change_sig(SIGUSR1, on); +}