During userret() we can take/release the kernel lock up to four times.
Nobody actually uses SIGPROF or SIGVTALRM, but a double-lock with a
signal and a temporary signal mask is plausible.
Is there any reason we can't take/release the kernel lock just once?
Do we need to drop the kernel lock in between these blocks to let
other signals arrive?
Index: kern_sig.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sig.c,v
retrieving revision 1.287
diff -u -p -r1.287 kern_sig.c
--- kern_sig.c 24 Oct 2021 00:02:25 -0000 1.287
+++ kern_sig.c 4 Nov 2021 20:57:41 -0000
@@ -1897,27 +1897,32 @@ filt_signal(struct knote *kn, long hint)
void
userret(struct proc *p)
{
- int signum;
+ int need_lock, prof, signum, vtalrm;
+
+ need_lock = 1;
/* send SIGPROF or SIGVTALRM if their timers interrupted this thread */
- if (p->p_flag & P_PROFPEND) {
- atomic_clearbits_int(&p->p_flag, P_PROFPEND);
- KERNEL_LOCK();
- psignal(p, SIGPROF);
- KERNEL_UNLOCK();
- }
- if (p->p_flag & P_ALRMPEND) {
- atomic_clearbits_int(&p->p_flag, P_ALRMPEND);
- KERNEL_LOCK();
- psignal(p, SIGVTALRM);
- KERNEL_UNLOCK();
+ prof = p->p_flag & P_PROFPEND;
+ vtalrm = p->p_flag & P_ALRMPEND;
+ if (prof || vtalrm) {
+ atomic_clearbits_int(&p->p_flag, P_PROFPEND | P_ALRMPEND);
+ if (need_lock) {
+ KERNEL_LOCK();
+ need_lock = 0;
+ }
+ if (prof)
+ psignal(p, SIGPROF);
+ if (vtalrm)
+ psignal(p, SIGVTALRM);
}
if (SIGPENDING(p) != 0) {
- KERNEL_LOCK();
+ if (need_lock) {
+ KERNEL_LOCK();
+ need_lock = 0;
+ }
while ((signum = cursig(p)) != 0)
postsig(p, signum);
- KERNEL_UNLOCK();
}
/*
@@ -1929,12 +1934,16 @@ userret(struct proc *p)
if (p->p_flag & P_SIGSUSPEND) {
atomic_clearbits_int(&p->p_flag, P_SIGSUSPEND);
p->p_sigmask = p->p_oldmask;
-
- KERNEL_LOCK();
+ if (need_lock) {
+ KERNEL_LOCK();
+ need_lock = 0;
+ }
while ((signum = cursig(p)) != 0)
postsig(p, signum);
- KERNEL_UNLOCK();
}
+
+ if (!need_lock)
+ KERNEL_UNLOCK();
if (p->p_flag & P_SUSPSINGLE)
single_thread_check(p, 0);