kernel: Make signal handling 16bit clean
authorAlan Cox <alan@linux.intel.com>
Wed, 13 Dec 2017 23:32:25 +0000 (23:32 +0000)
committerAlan Cox <alan@linux.intel.com>
Wed, 13 Dec 2017 23:32:25 +0000 (23:32 +0000)
SDCC makes a complete hash of 32bit maths and the other compilers don't handle
it that well either so rework the signals as 16bit chunks. This saves us a
whopping 350 bytes as well as speeding stuff up.

Also copy over the held bits - something we forgot to do before.

Kernel/include/kernel.h
Kernel/process.c
Kernel/syscall_proc.c

index d4927ec..a3f62dd 100644 (file)
@@ -385,7 +385,7 @@ struct mount {
 #define  SIG_DFL   (int (*)(int))0     /* Must be 0 */
 #define  SIG_IGN   (int (*)(int))1
 
-#define sigmask(sig)    (1UL<<(sig))
+#define sigmask(sig)    (1U<<((sig) & 0x0F))
 
 /* uadmin */
 
@@ -402,6 +402,12 @@ struct mount {
                                           
 /* Process table entry */
 
+struct sigbits {
+    uint16_t   s_pending;
+    uint16_t   s_ignored;
+    uint16_t   s_held;
+};
+
 typedef struct p_tab {
     /* WRS: UPDATE kernel.def IF YOU CHANGE THIS STRUCTURE */
     uint8_t     p_status;       /* Process status: MUST BE FIRST MEMBER OF STRUCT */
@@ -421,9 +427,7 @@ typedef struct p_tab {
     /* Everything below here is overlaid by time info at exit.
         * Make sure it's 32-bit aligned. */
     uint16_t    p_priority;     /* Process priority */
-    uint32_t    p_pending;      /* Bitmask of pending signals */
-    uint32_t    p_ignored;      /* Bitmask of ignored signals */
-    uint32_t    p_held;         /* Bitmask of held signals */
+    struct sigbits p_sig[2];
     uint16_t    p_waitno;       /* wait #; for finding longest waiting proc */
     uint16_t    p_timeout;      /* timeout in centiseconds - 1 */
                                 /* 0 indicates no timeout, 1 = expired */
index 1995034..41a4a69 100644 (file)
@@ -211,7 +211,10 @@ void newproc(ptptr p)
        nready++;               /* runnable process count */
 
        p->p_pptr = udata.u_ptab;
-       p->p_ignored = udata.u_ptab->p_ignored;
+       p->p_sig[0].s_ignored = udata.u_ptab->p_sig[0].s_ignored;
+       p->p_sig[1].s_ignored = udata.u_ptab->p_sig[1].s_ignored;
+       p->p_sig[0].s_held = udata.u_ptab->p_sig[0].s_held;
+       p->p_sig[1].s_held = udata.u_ptab->p_sig[1].s_held;
        p->p_tty = udata.u_ptab->p_tty;
        p->p_uid = udata.u_ptab->p_uid;
        /* Set default priority */
@@ -453,32 +456,51 @@ static uint8_t dump_core(uint8_t sig)
 }
 #endif                                    
 
+
+/* FIXME: we should keep a dirty flag so we know if we need to check for
+   signals so we can optimise this path */
+
 /* This sees if the current process has any signals set, and handles them.
  */
 
-static const uint32_t stopper = (1UL << SIGSTOP) | (1UL << SIGTTIN) | (1UL << SIGTTOU) | (1UL << SIGTSTP);
-static const uint32_t clear = (1UL << SIGSTOP) | (1UL << SIGTTIN) | (1UL << SIGTTOU) | (1UL << SIGTSTP) |
-                             (1UL << SIGCHLD) | (1UL << SIGURG) | (1UL << SIGWINCH) | (1UL << SIGIO) |
-                             (1UL << SIGCONT);
-uint8_t chksigs(void)
+#define SIGBIT(x)      (1 << (x & 15))
+
+static const uint16_t stopper[2] = {
+       0,
+       /* And the high signals */
+       SIGBIT(SIGSTOP) | SIGBIT(SIGTTIN) | SIGBIT(SIGTTOU) | SIGBIT(SIGTSTP)
+};
+
+static const uint16_t clear[2] = {
+       0,
+       /* And the high signals */
+       SIGBIT(SIGSTOP) | SIGBIT(SIGTTIN) | SIGBIT(SIGTTOU) | SIGBIT(SIGTSTP) |
+       SIGBIT(SIGCHLD) | SIGBIT(SIGURG) | SIGBIT(SIGWINCH) | SIGBIT(SIGIO) |
+       SIGBIT(SIGCONT)
+};
+
+/* Process a block of 16 signals so we can avoid using longs */
+
+static uint8_t chksigset(struct sigbits *sb, uint8_t b)
 {
-       uint8_t j;
-       uint32_t pending = udata.u_ptab->p_pending & ~udata.u_ptab->p_held;
+       uint8_t j = 1;
        int (**svec)(int) = &udata.u_sigvec[0];
-       uint32_t m;
+       uint16_t m;
+       uint16_t pending = sb->s_pending & ~sb->s_held;
 
-       /* Fast path - no signals pending means no work.
-          Cursig being set means we've already worked out what to do.
-        */
+       if (pending == 0)
+               return 0;
 
-rescan:
-       if (udata.u_cursig || !pending || udata.u_ptab->p_status == P_STOPPED)
-               return udata.u_cursig;
+       if (b == 1)  {
+               j = 0;
+               svec = &udata.u_sigvec[15]; /* ++ done at start */
+       }
 
        /* Dispatch the lowest numbered signal */
-       for (j = 1; j < NSIGS; ++j) {
+       for (; j < 15; ++j) {
                svec++;
-               m = sigmask(j);
+               /* FIXME: optimise by setting up m once and shifting */
+               m = 1 << j;
                if (!(m & pending))
                        continue;
                /* This is more complex than in V7 - we have multiple
@@ -489,26 +511,26 @@ rescan:
                           Annoyingly right now we have to context switch to the task
                           in order to stop it in the right place. That would be nice
                           to fix */
-                       if (m & stopper) {
+                       if (m & stopper[b]) {
                                /* Don't allow us to race SIGCONT */
                                irqflags_t irq = di();
                                /* FIXME: can we ever end up here not in READY/RUNNING ? */
                                nready--;
                                udata.u_ptab->p_status = P_STOPPED;
                                udata.u_ptab->p_event = j;
-                               udata.u_ptab->p_pending &= ~m;  // unset the bit
+                               sb->s_pending &= ~m;    // unset the bit
                                irqrestore(irq);
                                switchout();
                                /* Other things may have happened */
-                               goto rescan;
+                               return 0xFF;
                        }
 
                        /* The signal is being handled, so clear it even if
                           we are exiting (otherwise we'll loop in
                           chksigs) */
-                       udata.u_ptab->p_pending &= ~m;
+                       sb->s_pending &= ~m;
 
-                       if ((m & clear) || udata.u_ptab->p_pid == 1) {
+                       if ((m & clear[b]) || udata.u_ptab->p_pid == 1) {
                        /* SIGCONT is subtle - we woke the process to handle
                           the signal so ignoring here works fine */
                                continue;
@@ -527,29 +549,58 @@ rescan:
                        doexit(dump_core(j));
                } else if (*svec != SIG_IGN) {
                        /* Arrange to call the user routine at return */
-                       udata.u_ptab->p_pending &= ~m;  // unset the bit
+                       sb->s_pending &= ~m;    // unset the bit
 #ifdef DEBUG
                        kprintf("about to process signal %d\n", j);
 #endif
-                       udata.u_cursig = j;
+                       udata.u_cursig = j + b << 4;
                        break;
                }
        }
        return udata.u_cursig;
 }
 
+uint8_t chksigs(void)
+{
+       struct sigbits *sb = udata.u_ptab->p_sig;
+       uint8_t r;
+       uint8_t b;
+
+       /* Fast path - no signals pending means no work.
+          Cursig being set means we've already worked out what to do.
+        */
+rescan:
+       if (udata.u_cursig || udata.u_ptab->p_status == P_STOPPED)
+               return udata.u_cursig;
+
+       for (b = 0; b < 2; b++, sb++) {
+               r = chksigset(sb, b);
+               if (r == 0xFF)
+                       goto rescan;
+               else if (r)
+                       return udata.u_cursig;
+       }
+       return 0;
+}
+
 /*
  *     Send signal, avoid touching uarea
  */
 /* SDCC bug #2472: SDCC generates hideous code for this function due to bad
    code generation when masking longs. Not clear we can do much about it but
    file a bug */
+
 void ssig(ptptr proc, uint8_t sig)
 {
+       struct sigbits *m = proc->p_sig;
        uint32_t sigm;
+       uint8_t sigbit = sig & 0x0F;
        irqflags_t irq;
 
-       sigm = sigmask(sig);
+       if (sig > 15)
+               m++;
+
+       sigm = 1 << sigbit;
 
        irq = di();
 
@@ -563,17 +614,17 @@ void ssig(ptptr proc, uint8_t sig)
                                nready++;
                        }
                        /* CONT discards pending stops */
-                       proc->p_pending &= ~((1L << SIGSTOP) | (1L << SIGTTIN) |
-                                            (1L << SIGTTOU) | (1L << SIGTSTP));
+                       proc->p_sig[1].s_pending &= ~(SIGBIT(SIGSTOP) | SIGBIT(SIGTTIN)
+                                                    | SIGBIT(SIGTTOU) | SIGBIT(SIGTSTP));
                }
                /* STOP discards pending conts */
                if (sig >= SIGSTOP && sig <= SIGTTOU)
-                       proc->p_pending &= ~(1L << SIGCONT);
+                       proc->p_sig[0].s_pending &= ~SIGBIT(SIGCONT);
 
                /* Routine signal behaviour */
-               if (!(proc->p_ignored & sigm)) {
+               if (!(m->s_ignored & sigm)) {
                        /* Don't wake for held signals */
-                       if (!(proc->p_held & sigm)) {
+                       if (!(m->s_held & sigm)) {
                                switch (proc->p_status) {
                                case P_SLEEP:
                                        proc->p_status = P_READY;
@@ -582,7 +633,7 @@ void ssig(ptptr proc, uint8_t sig)
                                }
                                proc->p_wait = NULL;
                        }
-                       proc->p_pending |= sigm;
+                       m->s_pending |= sigm;
                }
        }
        irqrestore(irq);
@@ -619,7 +670,7 @@ void acctexit(ptptr p)
 /* FIXME: why return a value - we don't use it */
 static int signal_parent(ptptr p)
 {
-        if (p->p_ignored & (1UL << SIGCHLD)) {
+        if (p->p_sig[1].s_ignored & (1UL << SIGCHLD)) {
                /* POSIX.1 says that SIG_IGN for SIGCHLD means don't go
                   zombie, just clean up as we go */
                udata.u_ptab->p_status = P_EMPTY;
@@ -655,7 +706,8 @@ void doexit(uint16_t val)
        /* We are exiting, hold all signals  (they will never be
           delivered). If we don't do this we might take a signal
           while exiting which would be ... unfortunate */
-       udata.u_ptab->p_held = 0xFFFFFFFFUL;
+       udata.u_ptab->p_sig[0].s_held = 0xFFFFU;
+       udata.u_ptab->p_sig[1].s_held = 0xFFFFU;
        udata.u_cursig = 0;
 
        /* Discard our memory before we blow away and reuse the memory */
@@ -690,7 +742,7 @@ void doexit(uint16_t val)
                        p->p_pptr = ptab;       /* ptab is always init */
                        /* Suppose our child is a zombie and init has
                           SIGCLD blocked */
-                       if (ptab[0].p_ignored & (1UL << SIGCHLD)) {
+                       if (ptab[0].p_sig[1].s_ignored & SIGBIT(SIGCHLD)) {
                                p->p_status = P_EMPTY;
                        } else {
                                ssig(&ptab[0], SIGCHLD);
index 47fd979..ea93928 100644 (file)
@@ -455,23 +455,26 @@ arg_t _signal(void)
 {
        int16_t retval;
        irqflags_t irq;
+       struct sigbits *sb = udata.u_ptab->p_sig;
 
        if (sig < 1 || sig >= NSIGS) {
                udata.u_error = EINVAL;
                goto nogood;
        }
+       if (sig > 15)
+               sb++;
 
        irq = di();
 
        if (func == SIG_IGN) {
                if (sig != SIGKILL && sig != SIGSTOP)
-                       udata.u_ptab->p_ignored |= sigmask(sig);
+                       sb->s_ignored |= sigmask(sig);
        } else {
                if (func != SIG_DFL && !valaddr((char *) func, 1)) {
                        udata.u_error = EFAULT;
                        goto nogood;
                }
-               udata.u_ptab->p_ignored &= ~sigmask(sig);
+               sb->s_ignored &= ~sigmask(sig);
        }
        retval = (arg_t) udata.u_sigvec[sig];
        if (sig != SIGKILL && sig != SIGSTOP)
@@ -500,14 +503,17 @@ int16_t disp;
 /* Implement sighold/sigrelse */
 arg_t _sigdisp(void)
 {
+       struct sigbits *sb = udata.u_ptab->p_sig;
        if (sig < 1 || sig >= NSIGS || sig == SIGKILL || sig == SIGSTOP) {
                udata.u_error = EINVAL;
                return -1;
        }
+       if (sig > 15)
+               sb++;
        if (disp == 1)
-               udata.u_ptab->p_held |= sigmask(sig);
+               sb->s_held |= sigmask(sig);
        else
-               udata.u_ptab->p_held &= ~sigmask(sig);
+               sb->s_held &= ~sigmask(sig);
        /* Force recalculation of signal pending in the syscall return path */
        udata.u_cursig = 0;
        return 0;