From 5d008ab7898d29fe39ff38b15f6382d1ced74de4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 13 Dec 2017 23:32:25 +0000 Subject: [PATCH] kernel: Make signal handling 16bit clean 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 | 12 ++-- Kernel/process.c | 120 ++++++++++++++++++++++++++++------------ Kernel/syscall_proc.c | 14 +++-- 3 files changed, 104 insertions(+), 42 deletions(-) diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index d4927ec8..a3f62dd7 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -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 */ diff --git a/Kernel/process.c b/Kernel/process.c index 1995034c..41a4a690 100644 --- a/Kernel/process.c +++ b/Kernel/process.c @@ -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); diff --git a/Kernel/syscall_proc.c b/Kernel/syscall_proc.c index 47fd9792..ea93928e 100644 --- a/Kernel/syscall_proc.c +++ b/Kernel/syscall_proc.c @@ -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; -- 2.34.1