svn commit: r357693 - in head: lib/libc/sys sys/compat/freebsd32 sys/kern sys/sys

Konstantin Belousov kib at FreeBSD.org
Sun Feb 9 11:53:16 UTC 2020


Author: kib
Date: Sun Feb  9 11:53:12 2020
New Revision: 357693
URL: https://svnweb.freebsd.org/changeset/base/357693

Log:
  Add a way to manage thread signal mask using shared word, instead of syscall.
  
  A new syscall sigfastblock(2) is added which registers a uint32_t
  variable as containing the count of blocks for signal delivery.  Its
  content is read by kernel on each syscall entry and on AST processing,
  non-zero count of blocks is interpreted same as the signal mask
  blocking all signals.
  
  The biggest downside of the feature that I see is that memory
  corruption that affects the registered fast sigblock location, would
  cause quite strange application misbehavior. For instance, the process
  would be immune to ^C (but killable by SIGKILL).
  
  With consumers (rtld and libthr added), benchmarks do not show a
  slow-down of the syscalls in micro-measurements, and macro benchmarks
  like buildworld do not demonstrate a difference. Part of the reason is
  that buildworld time is dominated by compiler, and clang already links
  to libthr. On the other hand, small utilities typically used by shell
  scripts have the total number of syscalls cut by half.
  
  The syscall is not exported from the stable libc version namespace on
  purpose.  It is intended to be used only by our C runtime
  implementation internals.
  
  Tested by:	pho
  Disscussed with:	cem, emaste, jilles
  Sponsored by:	The FreeBSD Foundation
  Differential revision:	https://reviews.freebsd.org/D12773

Added:
  head/lib/libc/sys/sigfastblock.2   (contents, props changed)
Modified:
  head/lib/libc/sys/Makefile.inc
  head/lib/libc/sys/Symbol.map
  head/sys/compat/freebsd32/syscalls.master
  head/sys/kern/capabilities.conf
  head/sys/kern/kern_exec.c
  head/sys/kern/kern_fork.c
  head/sys/kern/kern_sig.c
  head/sys/kern/kern_thread.c
  head/sys/kern/subr_syscall.c
  head/sys/kern/subr_trap.c
  head/sys/kern/syscalls.master
  head/sys/sys/proc.h
  head/sys/sys/signalvar.h

Modified: head/lib/libc/sys/Makefile.inc
==============================================================================
--- head/lib/libc/sys/Makefile.inc	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/lib/libc/sys/Makefile.inc	Sun Feb  9 11:53:12 2020	(r357693)
@@ -317,6 +317,7 @@ MAN+=	sctp_generic_recvmsg.2 \
 	shutdown.2 \
 	sigaction.2 \
 	sigaltstack.2 \
+	sigfastblock.2 \
 	sigpending.2 \
 	sigprocmask.2 \
 	sigqueue.2 \

Modified: head/lib/libc/sys/Symbol.map
==============================================================================
--- head/lib/libc/sys/Symbol.map	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/lib/libc/sys/Symbol.map	Sun Feb  9 11:53:12 2020	(r357693)
@@ -567,6 +567,7 @@ FBSDprivate_1.0 {
 	__sys_extattr_set_link;
 	_extattrctl;
 	__sys_extattrctl;
+	__sys_sigfastblock;
 	_fchdir;
 	__sys_fchdir;
 	_fchflags;

Added: head/lib/libc/sys/sigfastblock.2
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libc/sys/sigfastblock.2	Sun Feb  9 11:53:12 2020	(r357693)
@@ -0,0 +1,166 @@
+.\" Copyright (c) 2016 The FreeBSD Foundation, Inc.
+.\"
+.\" This documentation was written by
+.\" Konstantin Belousov <kib at FreeBSD.org> 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 .

Modified: head/sys/compat/freebsd32/syscalls.master
==============================================================================
--- head/sys/compat/freebsd32/syscalls.master	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/compat/freebsd32/syscalls.master	Sun Feb  9 11:53:12 2020	(r357693)
@@ -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

Modified: head/sys/kern/capabilities.conf
==============================================================================
--- head/sys/kern/capabilities.conf	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/capabilities.conf	Sun Feb  9 11:53:12 2020	(r357693)
@@ -668,6 +668,7 @@ shutdown
 sigaction
 sigaltstack
 sigblock
+sigfastblock
 sigpending
 sigprocmask
 sigqueue

Modified: head/sys/kern/kern_exec.c
==============================================================================
--- head/sys/kern/kern_exec.c	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/kern_exec.c	Sun Feb  9 11:53:12 2020	(r357693)
@@ -1025,6 +1025,7 @@ exec_new_vmspace(struct image_params *imgp, struct sys
 	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 @@ exec_new_vmspace(struct image_params *imgp, struct sys
 
 	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);

Modified: head/sys/kern/kern_fork.c
==============================================================================
--- head/sys/kern/kern_fork.c	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/kern_fork.c	Sun Feb  9 11:53:12 2020	(r357693)
@@ -563,7 +563,8 @@ do_fork(struct thread *td, struct fork_req *fr, struct
 	 * 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;

Modified: head/sys/kern/kern_sig.c
==============================================================================
--- head/sys/kern/kern_sig.c	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/kern_sig.c	Sun Feb  9 11:53:12 2020	(r357693)
@@ -114,7 +114,7 @@ static int	sig_suspend_threads(struct thread *, struct
 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 @@ static int sigproptbl[NSIG] = {
 	[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 @@ sigqueue_start(void)
 	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 @@ trapsignal(struct thread *td, ksiginfo_t *ksi)
 {
 	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 @@ trapsignal(struct thread *td, ksiginfo_t *ksi)
 	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 @@ trapsignal(struct thread *td, ksiginfo_t *ksi)
 		 * 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 @@ trapsignal(struct thread *td, ksiginfo_t *ksi)
 }
 
 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 @@ tdsendsignal(struct proc *p, struct thread *td, int si
 	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 @@ ptracestop(struct thread *td, int sig, ksiginfo_t *si)
 	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 @@ stopme:
 		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 @@ stopme:
 	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 @@ issignal(struct thread *td)
 			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 @@ sig_drop_caught(struct proc *p)
 		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);
 }

Modified: head/sys/kern/kern_thread.c
==============================================================================
--- head/sys/kern/kern_thread.c	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/kern_thread.c	Sun Feb  9 11:53:12 2020	(r357693)
@@ -82,9 +82,9 @@ _Static_assert(offsetof(struct thread, td_flags) == 0x
     "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 @@ _Static_assert(offsetof(struct thread, td_flags) == 0x
     "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");

Modified: head/sys/kern/subr_syscall.c
==============================================================================
--- head/sys/kern/subr_syscall.c	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/subr_syscall.c	Sun Feb  9 11:53:12 2020	(r357693)
@@ -140,6 +140,13 @@ syscallenter(struct thread *td)
 	/* 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);

Modified: head/sys/kern/subr_trap.c
==============================================================================
--- head/sys/kern/subr_trap.c	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/subr_trap.c	Sun Feb  9 11:53:12 2020	(r357693)
@@ -116,12 +116,16 @@ userret(struct thread *td, struct trapframe *frame)
 	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 @@ ast(struct trapframe *framep)
 {
 	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 @@ ast(struct trapframe *framep)
 		 * 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 @@ ast(struct trapframe *framep)
 	 */
 	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.

Modified: head/sys/kern/syscalls.master
==============================================================================
--- head/sys/kern/syscalls.master	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/kern/syscalls.master	Sun Feb  9 11:53:12 2020	(r357693)
@@ -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

Modified: head/sys/sys/proc.h
==============================================================================
--- head/sys/sys/proc.h	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/sys/proc.h	Sun Feb  9 11:53:12 2020	(r357693)
@@ -322,6 +322,9 @@ struct thread {
 	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 @@ do {									\
 #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 @@ do {									\
 #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.

Modified: head/sys/sys/signalvar.h
==============================================================================
--- head/sys/sys/signalvar.h	Sun Feb  9 10:42:01 2020	(r357692)
+++ head/sys/sys/signalvar.h	Sun Feb  9 11:53:12 2020	(r357693)
@@ -256,7 +256,23 @@ typedef struct sigqueue {
 /* 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 @@ extern struct mtx	sigio_lock;
 #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 @@ sigallowstop(int prev)
 
 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 @@ void	pgsignal(struct pgrp *pgrp, int sig, int checkctt
 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);


More information about the svn-src-all mailing list