Index: head/lib/libc/sys/Makefile.inc =================================================================== --- head/lib/libc/sys/Makefile.inc +++ head/lib/libc/sys/Makefile.inc @@ -317,6 +317,7 @@ shutdown.2 \ sigaction.2 \ sigaltstack.2 \ + sigfastblock.2 \ sigpending.2 \ sigprocmask.2 \ sigqueue.2 \ Index: head/lib/libc/sys/Symbol.map =================================================================== --- head/lib/libc/sys/Symbol.map +++ head/lib/libc/sys/Symbol.map @@ -567,6 +567,7 @@ __sys_extattr_set_link; _extattrctl; __sys_extattrctl; + __sys_sigfastblock; _fchdir; __sys_fchdir; _fchflags; Index: head/lib/libc/sys/sigfastblock.2 =================================================================== --- head/lib/libc/sys/sigfastblock.2 +++ head/lib/libc/sys/sigfastblock.2 @@ -0,0 +1,166 @@ +.\" Copyright (c) 2016 The FreeBSD Foundation, Inc. +.\" +.\" This documentation was written by +.\" Konstantin Belousov under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 13, 2019 +.Dt SIGFASTBLOCK 2 +.Os +.Sh NAME +.Nm sigfastblock +.Nd controls signals blocking with a simple memory write +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/signalvar.h +.Ft int +.Fn sigfastblock "int cmd" "void *ptr" +.Sh DESCRIPTION +.Bf -symbolic +This function is not intended for a direct usage by applications. +The functionality is provided for implementing some optimizations in +.Xr ld-elf.so.1 8 +and +.Lb libthr . +.Ef +.Pp +The function configures the kernel facility that allows a thread to +block asynchronous signals delivery with a single write to userspace +memory, avoiding overhead of system calls like +.Xr sigprocmask 2 +for establishing critical sections. +The C runtime uses it to optimize implementation of async-signal-safe +functionality. +.Pp +A thread might register a +.Dv sigblock +variable of type +.Vt int +as a location which is consulted by kernel when calculating the +blocked signal mask for delivery of asynchronous signals. +If the variable indicates that blocking is requested, then the kernel +effectively operates as if the mask containing all blockable signals was +supplied to +.Xr sigprocmask 2 . +.Pp +The variable is supposed to be modified only from the owning thread, +there is no way to guarantee visibility of update from other thread +to kernel when signals are delivered. +.Pp +Lower bits of the sigblock variable are reserved as flags, +which might be set or cleared by kernel at arbitrary moments. +Userspace code should use +.Xr atomic 9 +operations of incrementing and decrementing by +.Dv SIGFASTBLOCK_INC +quantity to recursively block or unblock signals delivery. +.Pp +If a signal would be delivered when unmasked, kernel might set the +.Dv SIGFASTBLOCK_PEND +.Dq pending signal +flag in the sigblock variable. +Userspace should perform +.Dv SIGFASTBLOCK_UNBLOCK +operation when clearing the variable if it notes the pending signal +bit is set, which would deliver the pending signals immediately. +Otherwise, signals delivery might be postponed. +.Pp +The +.Fa cmd +argument specifies one of the following operations: +.Bl -tag -width SIGFASTBLOCK_UNSETPTR +.It Dv SIGFASTBLOCK_SETPTR +Register the variable of type +.Vt int +at location pointed to by the +.Fa ptr +argument as sigblock variable for the calling thread. +.It Dv SIGFASTBLOCK_UNSETPTR +Unregister the currently registered sigblock location. +Kernel stops inferring the blocked mask from non-zero value of its +blocked count. +New location can be registered after previous one is deregistered. +.It Dv SIGFASTBLOCK_UNBLOCK +If there are pending signals which should be delivered to the calling +thread, they are delivered before returning from the call. +The sigblock variable should have zero blocking count, and indicate +that the pending signal exists. +Effectively this means that the variable should have the value +.Dv SIGFASTBLOCK_PEND . +.El +.Sh RETURN VALUES +.Rv -std +.Sh ERRORS +The operation may fail with the following errors: +.Bl -tag -width Er +.It Bq Er EBUSY +The +.Dv SIGFASTBLOCK_SETPTR +attempted while the sigblock address was already registered. +The +.Dv SIGFASTBLOCK_UNBLOCK +was called while sigblock variable value is not equal to +.Dv SIGFASTBLOCK_PEND . +.It Bq Er EINVAL +The variable address passed to +.Dv SIGFASTBLOCK_SETPTR +is not aligned naturally. +The +.Dv SIGFASTBLOCK_UNSETPTR +operation was attempted without prior successfull call to +.Dv SIGFASTBLOCK_SETPTR . +.It Bq Er EFAULT +Attempt to read or write to the sigblock variable failed. +Note that kernel generates the +.Dv SIGSEGV +signal if an attempt to read from the sigblock variable faulted +during implicit accesses from syscall entry. +.El +.Sh SEE ALSO +.Xr kill 2 , +.Xr signal 2 , +.Xr sigprocmask 2 , +.Xr libthr 3 , +.Xr ld-elf.so.1 8 +.Sh STANDARDS +The +.Nm +function is non-standard, although a similar functionality is a common +optimization provided by several other systems. +.Sh HISTORY +The +.Nm +function was introduced in +.Fx 13.0 . +.Sh BUGS +The +.Nm +symbol is currently not exported by libc, on purpose. +Consumers should either use the +.Dv __sys_fast_sigblock +symbol from the private libc namespace, or utilize +.Xr syscall 2 . Index: head/sys/compat/freebsd32/syscalls.master =================================================================== --- head/sys/compat/freebsd32/syscalls.master +++ head/sys/compat/freebsd32/syscalls.master @@ -1159,5 +1159,6 @@ int shmflags, const char *name); } 572 AUE_SHMRENAME NOPROTO { int shm_rename(const char *path_from, \ const char *path_to, int flags); } +573 AUE_NULL NOPROTO { int sigfastblock(int cmd, uint32_t *ptr); } ; vim: syntax=off Index: head/sys/kern/capabilities.conf =================================================================== --- head/sys/kern/capabilities.conf +++ head/sys/kern/capabilities.conf @@ -668,6 +668,7 @@ sigaction sigaltstack sigblock +sigfastblock sigpending sigprocmask sigqueue Index: head/sys/kern/kern_exec.c =================================================================== --- head/sys/kern/kern_exec.c +++ head/sys/kern/kern_exec.c @@ -1025,6 +1025,7 @@ int error; struct proc *p = imgp->proc; struct vmspace *vmspace = p->p_vmspace; + struct thread *td = curthread; vm_object_t obj; struct rlimit rlim_stack; vm_offset_t sv_minuser, stack_addr; @@ -1033,6 +1034,10 @@ imgp->vmspace_destroyed = 1; imgp->sysent = sv; + + td->td_pflags &= ~TDP_SIGFASTBLOCK; + td->td_sigblock_ptr = NULL; + td->td_sigblock_val = 0; /* May be called with Giant held */ EVENTHANDLER_DIRECT_INVOKE(process_exec, p, imgp); Index: head/sys/kern/kern_fork.c =================================================================== --- head/sys/kern/kern_fork.c +++ head/sys/kern/kern_fork.c @@ -563,7 +563,8 @@ * been preserved. */ p2->p_flag |= p1->p_flag & P_SUGID; - td2->td_pflags |= (td->td_pflags & TDP_ALTSTACK) | TDP_FORKING; + td2->td_pflags |= (td->td_pflags & (TDP_ALTSTACK | + TDP_SIGFASTBLOCK)) | TDP_FORKING; SESS_LOCK(p1->p_session); if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT) p2->p_flag |= P_CONTROLT; Index: head/sys/kern/kern_sig.c =================================================================== --- head/sys/kern/kern_sig.c +++ head/sys/kern/kern_sig.c @@ -114,7 +114,7 @@ static int filt_sigattach(struct knote *kn); static void filt_sigdetach(struct knote *kn); static int filt_signal(struct knote *kn, long hint); -static struct thread *sigtd(struct proc *p, int sig, int prop); +static struct thread *sigtd(struct proc *p, int sig, bool fast_sigblock); static void sigqueue_start(void); static uma_zone_t ksiginfo_zone = NULL; @@ -238,7 +238,7 @@ [SIGUSR2] = SIGPROP_KILL, }; -static void reschedule_signals(struct proc *p, sigset_t block, int flags); +sigset_t fastblock_mask; static void sigqueue_start(void) @@ -249,6 +249,8 @@ p31b_setcfg(CTL_P1003_1B_REALTIME_SIGNALS, _POSIX_REALTIME_SIGNALS); p31b_setcfg(CTL_P1003_1B_RTSIG_MAX, SIGRTMAX - SIGRTMIN + 1); p31b_setcfg(CTL_P1003_1B_SIGQUEUE_MAX, max_pending_per_proc); + SIGFILLSET(fastblock_mask); + SIG_CANTMASK(fastblock_mask); } ksiginfo_t * @@ -1995,8 +1997,8 @@ { struct sigacts *ps; struct proc *p; - int sig; - int code; + sigset_t sigmask; + int code, sig; p = td->td_proc; sig = ksi->ksi_signo; @@ -2006,8 +2008,11 @@ PROC_LOCK(p); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); + sigmask = td->td_sigmask; + if (td->td_sigblock_val != 0) + SIGSETOR(sigmask, fastblock_mask); if ((p->p_flag & P_TRACED) == 0 && SIGISMEMBER(ps->ps_sigcatch, sig) && - !SIGISMEMBER(td->td_sigmask, sig)) { + !SIGISMEMBER(sigmask, sig)) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_PSIG)) ktrpsig(sig, ps->ps_sigact[_SIG_IDX(sig)], @@ -2023,13 +2028,14 @@ * masking the signal or process is ignoring the * signal. */ - if (kern_forcesigexit && - (SIGISMEMBER(td->td_sigmask, sig) || - ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN)) { + if (kern_forcesigexit && (SIGISMEMBER(sigmask, sig) || + ps->ps_sigact[_SIG_IDX(sig)] == SIG_IGN)) { SIGDELSET(td->td_sigmask, sig); SIGDELSET(ps->ps_sigcatch, sig); SIGDELSET(ps->ps_sigignore, sig); ps->ps_sigact[_SIG_IDX(sig)] = SIG_DFL; + td->td_pflags &= ~TDP_SIGFASTBLOCK; + td->td_sigblock_val = 0; } mtx_unlock(&ps->ps_mtx); p->p_sig = sig; /* XXX to verify code */ @@ -2039,21 +2045,24 @@ } static struct thread * -sigtd(struct proc *p, int sig, int prop) +sigtd(struct proc *p, int sig, bool fast_sigblock) { struct thread *td, *signal_td; PROC_LOCK_ASSERT(p, MA_OWNED); + MPASS(!fast_sigblock || p == curproc); /* * Check if current thread can handle the signal without * switching context to another thread. */ - if (curproc == p && !SIGISMEMBER(curthread->td_sigmask, sig)) + if (curproc == p && !SIGISMEMBER(curthread->td_sigmask, sig) && + (!fast_sigblock || curthread->td_sigblock_val == 0)) return (curthread); signal_td = NULL; FOREACH_THREAD_IN_PROC(p, td) { - if (!SIGISMEMBER(td->td_sigmask, sig)) { + if (!SIGISMEMBER(td->td_sigmask, sig) && (!fast_sigblock || + td != curthread || td->td_sigblock_val == 0)) { signal_td = td; break; } @@ -2167,7 +2176,7 @@ prop = sigprop(sig); if (td == NULL) { - td = sigtd(p, sig, prop); + td = sigtd(p, sig, false); sigqueue = &p->p_sigqueue; } else sigqueue = &td->td_sigqueue; @@ -2562,7 +2571,6 @@ struct proc *p = td->td_proc; struct thread *td2; ksiginfo_t ksi; - int prop; PROC_LOCK_ASSERT(p, MA_OWNED); KASSERT(!(p->p_flag & P_WEXIT), ("Stopping exiting process")); @@ -2659,8 +2667,7 @@ ksiginfo_init(&ksi); ksi.ksi_signo = td->td_xsig; ksi.ksi_flags |= KSI_PTRACE; - prop = sigprop(td->td_xsig); - td2 = sigtd(p, td->td_xsig, prop); + td2 = sigtd(p, td->td_xsig, false); tdsendsignal(p, td2, td->td_xsig, &ksi); if (td != td2) return (0); @@ -2669,33 +2676,45 @@ return (td->td_xsig); } -static void +void reschedule_signals(struct proc *p, sigset_t block, int flags) { struct sigacts *ps; struct thread *td; int sig; + bool fastblk, pslocked; PROC_LOCK_ASSERT(p, MA_OWNED); ps = p->p_sigacts; - mtx_assert(&ps->ps_mtx, (flags & SIGPROCMASK_PS_LOCKED) != 0 ? - MA_OWNED : MA_NOTOWNED); + pslocked = (flags & SIGPROCMASK_PS_LOCKED) != 0; + mtx_assert(&ps->ps_mtx, pslocked ? MA_OWNED : MA_NOTOWNED); if (SIGISEMPTY(p->p_siglist)) return; SIGSETAND(block, p->p_siglist); + fastblk = (flags & SIGPROCMASK_FASTBLK) != 0; while ((sig = sig_ffs(&block)) != 0) { SIGDELSET(block, sig); - td = sigtd(p, sig, 0); + td = sigtd(p, sig, fastblk); + + /* + * If sigtd() selected us despite sigfastblock is + * blocking, do not activate AST or wake us, to avoid + * loop in AST handler. + */ + if (fastblk && td == curthread) + continue; + signotify(td); - if (!(flags & SIGPROCMASK_PS_LOCKED)) + if (!pslocked) mtx_lock(&ps->ps_mtx); if (p->p_flag & P_TRACED || (SIGISMEMBER(ps->ps_sigcatch, sig) && - !SIGISMEMBER(td->td_sigmask, sig))) + !SIGISMEMBER(td->td_sigmask, sig))) { tdsigwakeup(td, sig, SIG_CATCH, (SIGISMEMBER(ps->ps_sigintr, sig) ? EINTR : - ERESTART)); - if (!(flags & SIGPROCMASK_PS_LOCKED)) + ERESTART)); + } + if (!pslocked) mtx_unlock(&ps->ps_mtx); } } @@ -2844,6 +2863,24 @@ SIG_STOPSIGMASK(sigpending); if (SIGISEMPTY(sigpending)) /* no signal to send */ return (0); + + /* + * Do fast sigblock if requested by usermode. Since + * we do know that there was a signal pending at this + * point, set the FAST_SIGBLOCK_PEND as indicator for + * usermode to perform a dummy call to + * FAST_SIGBLOCK_UNBLOCK, which causes immediate + * delivery of postponed pending signal. + */ + if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) { + if (td->td_sigblock_val != 0) + SIGSETNAND(sigpending, fastblock_mask); + if (SIGISEMPTY(sigpending)) { + td->td_pflags |= TDP_SIGFASTPENDING; + return (0); + } + } + if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED && (p->p_flag2 & P2_PTRACE_FSTP) != 0 && SIGISMEMBER(sigpending, SIGSTOP)) { @@ -3913,4 +3950,119 @@ if ((sigprop(sig) & SIGPROP_IGNORE) != 0) sigqueue_delete_proc(p, sig); } +} + +int +sys_sigfastblock(struct thread *td, struct sigfastblock_args *uap) +{ + struct proc *p; + int error, res; + uint32_t oldval; + + error = 0; + switch (uap->cmd) { + case SIGFASTBLOCK_SETPTR: + if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) { + error = EBUSY; + break; + } + if (((uintptr_t)(uap->ptr) & (sizeof(uint32_t) - 1)) != 0) { + error = EINVAL; + break; + } + td->td_pflags |= TDP_SIGFASTBLOCK; + td->td_sigblock_ptr = uap->ptr; + break; + + case SIGFASTBLOCK_UNBLOCK: + if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0) { + error = EINVAL; + break; + } +again: + res = casueword32(td->td_sigblock_ptr, SIGFASTBLOCK_PEND, + &oldval, 0); + if (res == -1) { + error = EFAULT; + break; + } + if (res == 1) { + if (oldval != SIGFASTBLOCK_PEND) { + error = EBUSY; + break; + } + error = thread_check_susp(td, false); + if (error != 0) + break; + goto again; + } + td->td_sigblock_val = 0; + + /* + * Rely on normal ast mechanism to deliver pending + * signals to current thread. But notify others about + * fake unblock. + */ + p = td->td_proc; + if (error == 0 && p->p_numthreads != 1) { + PROC_LOCK(p); + reschedule_signals(p, td->td_sigmask, 0); + PROC_UNLOCK(p); + } + break; + + case SIGFASTBLOCK_UNSETPTR: + if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) { + error = EINVAL; + break; + } + res = fueword32(td->td_sigblock_ptr, &oldval); + if (res == -1) { + error = EFAULT; + break; + } + if (oldval != 0 && oldval != SIGFASTBLOCK_PEND) { + error = EBUSY; + break; + } + td->td_pflags &= ~TDP_SIGFASTBLOCK; + td->td_sigblock_val = 0; + break; + + default: + error = EINVAL; + break; + } + return (error); +} + +void +fetch_sigfastblock(struct thread *td) +{ + + if ((td->td_pflags & TDP_SIGFASTBLOCK) == 0) + return; + if (fueword32(td->td_sigblock_ptr, &td->td_sigblock_val) == -1) { + fetch_sigfastblock_failed(td, false); + return; + } + td->td_sigblock_val &= ~SIGFASTBLOCK_FLAGS; +} + +void +fetch_sigfastblock_failed(struct thread *td, bool write) +{ + ksiginfo_t ksi; + + /* + * Prevent further fetches and SIGSEGVs, allowing thread to + * issue syscalls despite corruption. + */ + td->td_pflags &= ~TDP_SIGFASTBLOCK; + + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGSEGV; + ksi.ksi_code = write ? SEGV_ACCERR : SEGV_MAPERR; + ksi.ksi_addr = td->td_sigblock_ptr; + trapsignal(td, &ksi); } Index: head/sys/kern/kern_thread.c =================================================================== --- head/sys/kern/kern_thread.c +++ head/sys/kern/kern_thread.c @@ -82,9 +82,9 @@ "struct thread KBI td_flags"); _Static_assert(offsetof(struct thread, td_pflags) == 0x104, "struct thread KBI td_pflags"); -_Static_assert(offsetof(struct thread, td_frame) == 0x480, +_Static_assert(offsetof(struct thread, td_frame) == 0x490, "struct thread KBI td_frame"); -_Static_assert(offsetof(struct thread, td_emuldata) == 0x690, +_Static_assert(offsetof(struct thread, td_emuldata) == 0x6a0, "struct thread KBI td_emuldata"); _Static_assert(offsetof(struct proc, p_flag) == 0xb0, "struct proc KBI p_flag"); @@ -102,9 +102,9 @@ "struct thread KBI td_flags"); _Static_assert(offsetof(struct thread, td_pflags) == 0xa0, "struct thread KBI td_pflags"); -_Static_assert(offsetof(struct thread, td_frame) == 0x2f0, +_Static_assert(offsetof(struct thread, td_frame) == 0x2f8, "struct thread KBI td_frame"); -_Static_assert(offsetof(struct thread, td_emuldata) == 0x338, +_Static_assert(offsetof(struct thread, td_emuldata) == 0x340, "struct thread KBI td_emuldata"); _Static_assert(offsetof(struct proc, p_flag) == 0x68, "struct proc KBI p_flag"); Index: head/sys/kern/subr_syscall.c =================================================================== --- head/sys/kern/subr_syscall.c +++ head/sys/kern/subr_syscall.c @@ -140,6 +140,13 @@ /* Let system calls set td_errno directly. */ td->td_pflags &= ~TDP_NERRNO; + /* + * Fetch fast sigblock value at the time of syscall + * entry because sleepqueue primitives might call + * cursig(). + */ + fetch_sigfastblock(td); + AUDIT_SYSCALL_ENTER(sa->code, td); error = (sa->callp->sy_call)(td, sa->args); AUDIT_SYSCALL_EXIT(error, td); Index: head/sys/kern/subr_trap.c =================================================================== --- head/sys/kern/subr_trap.c +++ head/sys/kern/subr_trap.c @@ -116,12 +116,16 @@ if (p->p_numthreads == 1) { PROC_LOCK(p); thread_lock(td); - if ((p->p_flag & P_PPWAIT) == 0) { - KASSERT(!SIGPENDING(td) || (td->td_flags & - (TDF_NEEDSIGCHK | TDF_ASTPENDING)) == - (TDF_NEEDSIGCHK | TDF_ASTPENDING), - ("failed to set signal flags for ast p %p " - "td %p fl %x", p, td, td->td_flags)); + if ((p->p_flag & P_PPWAIT) == 0 && + (td->td_pflags & TDP_SIGFASTBLOCK) == 0) { + if (SIGPENDING(td) && (td->td_flags & + (TDF_NEEDSIGCHK | TDF_ASTPENDING)) != + (TDF_NEEDSIGCHK | TDF_ASTPENDING)) { + thread_unlock(td); + panic( + "failed to set signal flags for ast p %p td %p fl %x", + p, td, td->td_flags); + } } thread_unlock(td); PROC_UNLOCK(p); @@ -218,8 +222,8 @@ { struct thread *td; struct proc *p; - int flags; - int sig; + uint32_t oldval; + int flags, sig, res; td = curthread; p = td->td_proc; @@ -298,12 +302,16 @@ * the reason for looping check for AST condition. * See comment in userret() about P_PPWAIT. */ - if ((p->p_flag & P_PPWAIT) == 0) { - KASSERT(!SIGPENDING(td) || (td->td_flags & - (TDF_NEEDSIGCHK | TDF_ASTPENDING)) == - (TDF_NEEDSIGCHK | TDF_ASTPENDING), - ("failed2 to set signal flags for ast p %p td %p " - "fl %x %x", p, td, flags, td->td_flags)); + if ((p->p_flag & P_PPWAIT) == 0 && + (td->td_pflags & TDP_SIGFASTBLOCK) == 0) { + if (SIGPENDING(td) && (td->td_flags & + (TDF_NEEDSIGCHK | TDF_ASTPENDING)) != + (TDF_NEEDSIGCHK | TDF_ASTPENDING)) { + thread_unlock(td); /* fix dumps */ + panic( + "failed2 to set signal flags for ast p %p td %p fl %x %x", + p, td, flags, td->td_flags); + } } thread_unlock(td); PROC_UNLOCK(p); @@ -317,15 +325,54 @@ */ if (flags & TDF_NEEDSIGCHK || p->p_pendingcnt > 0 || !SIGISEMPTY(p->p_siglist)) { + fetch_sigfastblock(td); PROC_LOCK(p); mtx_lock(&p->p_sigacts->ps_mtx); - while ((sig = cursig(td)) != 0) { - KASSERT(sig >= 0, ("sig %d", sig)); - postsig(sig); + if ((td->td_pflags & TDP_SIGFASTBLOCK) != 0 && + td->td_sigblock_val != 0) { + reschedule_signals(p, fastblock_mask, + SIGPROCMASK_PS_LOCKED | SIGPROCMASK_FASTBLK); + } else { + while ((sig = cursig(td)) != 0) { + KASSERT(sig >= 0, ("sig %d", sig)); + postsig(sig); + } } mtx_unlock(&p->p_sigacts->ps_mtx); PROC_UNLOCK(p); } + + /* + * Handle deferred update of the fast sigblock value, after + * the postsig() loop was performed. + */ + if (td->td_pflags & TDP_SIGFASTPENDING) { + td->td_pflags &= ~TDP_SIGFASTPENDING; + res = fueword32(td->td_sigblock_ptr, &oldval); + if (res == -1) { + fetch_sigfastblock_failed(td, false); + } else { + for (;;) { + oldval |= SIGFASTBLOCK_PEND; + res = casueword32(td->td_sigblock_ptr, oldval, + &oldval, oldval | SIGFASTBLOCK_PEND); + if (res == -1) { + fetch_sigfastblock_failed(td, true); + break; + } + if (res == 0) { + td->td_sigblock_val = oldval & + ~SIGFASTBLOCK_FLAGS; + break; + } + MPASS(res == 1); + res = thread_check_susp(td, false); + if (res != 0) + break; + } + } + } + /* * We need to check to see if we have to exit or wait due to a * single threading requirement or some other STOP condition. Index: head/sys/kern/syscalls.master =================================================================== --- head/sys/kern/syscalls.master +++ head/sys/kern/syscalls.master @@ -3212,6 +3212,12 @@ int flags ); } +573 AUE_NULL STD { + int sigfastblock( + int cmd, + _Inout_opt_ uint32_t *ptr + ); + } ; Please copy any additions and changes to the following compatability tables: ; sys/compat/freebsd32/syscalls.master Index: head/sys/sys/proc.h =================================================================== --- head/sys/sys/proc.h +++ head/sys/sys/proc.h @@ -322,6 +322,9 @@ uintptr_t td_rb_inact; /* (k) Current in-action mutex loc. */ struct syscall_args td_sa; /* (kx) Syscall parameters. Copied on fork for child tracing. */ + void *td_sigblock_ptr; /* (k) uptr for fast sigblock. */ + uint32_t td_sigblock_val; /* (k) fast sigblock value read at + td_sigblock_ptr on kern entry */ #define td_endcopy td_pcb /* @@ -486,7 +489,7 @@ #define TDP_ALTSTACK 0x00000020 /* Have alternate signal stack. */ #define TDP_DEADLKTREAT 0x00000040 /* Lock acquisition - deadlock treatment. */ #define TDP_NOFAULTING 0x00000080 /* Do not handle page faults. */ -#define TDP_UNUSED9 0x00000100 /* --available-- */ +#define TDP_SIGFASTBLOCK 0x00000100 /* Fast sigblock active */ #define TDP_OWEUPC 0x00000200 /* Call addupc() at next AST. */ #define TDP_ITHREAD 0x00000400 /* Thread is an interrupt thread. */ #define TDP_SYNCIO 0x00000800 /* Local override, disable async i/o. */ @@ -509,6 +512,7 @@ #define TDP_UIOHELD 0x10000000 /* Current uio has pages held in td_ma */ #define TDP_FORKING 0x20000000 /* Thread is being created through fork() */ #define TDP_EXECVMSPC 0x40000000 /* Execve destroyed old vmspace */ +#define TDP_SIGFASTPENDING 0x80000000 /* Pending signal due to sigfastblock */ /* * Reasons that the current thread can not be run yet. Index: head/sys/sys/signalvar.h =================================================================== --- head/sys/sys/signalvar.h +++ head/sys/sys/signalvar.h @@ -256,7 +256,23 @@ /* Flags for ksi_flags */ #define SQ_INIT 0x01 +/* + * Fast_sigblock + */ +#define SIGFASTBLOCK_SETPTR 1 +#define SIGFASTBLOCK_UNBLOCK 2 +#define SIGFASTBLOCK_UNSETPTR 3 + +#define SIGFASTBLOCK_PEND 0x1 +#define SIGFASTBLOCK_FLAGS 0xf +#define SIGFASTBLOCK_INC 0x10 + +#ifndef _KERNEL +int __sys_sigfastblock(int cmd, void *ptr); +#endif + #ifdef _KERNEL +extern sigset_t fastblock_mask; /* Return nonzero if process p has an unmasked pending signal. */ #define SIGPENDING(td) \ @@ -328,6 +344,7 @@ #define SIGPROCMASK_OLD 0x0001 #define SIGPROCMASK_PROC_LOCKED 0x0002 #define SIGPROCMASK_PS_LOCKED 0x0004 +#define SIGPROCMASK_FASTBLK 0x0008 /* * Modes for sigdeferstop(). Manages behaviour of @@ -365,6 +382,8 @@ int cursig(struct thread *td); void execsigs(struct proc *p); +void fetch_sigfastblock(struct thread *td); +void fetch_sigfastblock_failed(struct thread *td, bool write); void gsignal(int pgid, int sig, ksiginfo_t *ksi); void killproc(struct proc *p, char *why); ksiginfo_t * ksiginfo_alloc(int wait); @@ -375,6 +394,7 @@ int postsig(int sig); void kern_psignal(struct proc *p, int sig); int ptracestop(struct thread *td, int sig, ksiginfo_t *si); +void reschedule_signals(struct proc *p, sigset_t block, int flags); void sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *retmask); struct sigacts *sigacts_alloc(void); void sigacts_copy(struct sigacts *dest, struct sigacts *src);