On Sun, 6 Oct 2013, Joel Sing wrote:
...
> I like the concept of this diff, however it is not sufficient to solve
> the multithreaded profiling problem. The reason for this is that you're
> still sending SPROCESS signals, which are delivered to the main thread
> unless another thread has diverted the signal. This means that all of
> the profiling signals end up on the main thread, not the thread that was
> consuming CPU time.
>
> This can be fixed by either using ptsignal(p, SIG{PROF,VTALARM},
> STHREAD) instead of psignal(), or by including your other diff that
> changes the signal delivery within ptsignal - regardless, I think the
> previous diff is worth pursuing since it potentially reduces the number
> of signal related context switches.
Okay, here's a complete diff that, at least on my box with a regress test
that jsing@ should be committing soon, solves the problem, reduces the
total complexity of the itimer signal delivery, and reduces the context
switches caused by signal by having a thread sending a signal to its own
process take delivery of the signal if it can.
(While checking up on something else, I found that FreeBSD already uses
this take the cannoli^Wsignal logic.)
ok?
Philip
Index: bin/ps/ps.1
===================================================================
RCS file: /cvs/src/bin/ps/ps.1,v
retrieving revision 1.84
diff -u -p -r1.84 ps.1
--- bin/ps/ps.1 22 Sep 2013 17:28:34 -0000 1.84
+++ bin/ps/ps.1 7 Oct 2013 02:08:32 -0000
@@ -222,6 +222,8 @@ The thread flags (in hexadecimal), as de
.Aq Pa sys/proc.h :
.Bd -literal
P_INKTR 0x1 writing ktrace(2) record
+P_PROFPEND 0x2 this thread needs SIGPROF
+P_ALRMPEND 0x4 this thread needs SIGVTALRM
P_SIGSUSPEND 0x8 need to restore before-suspend mask
P_SELECT 0x40 selecting; wakeup/waiting danger
P_SINTR 0x80 sleep is interruptible
Index: sys/sys/proc.h
===================================================================
RCS file: /cvs/src/sys/sys/proc.h,v
retrieving revision 1.170
diff -u -p -r1.170 proc.h
--- sys/sys/proc.h 22 Sep 2013 17:28:33 -0000 1.170
+++ sys/sys/proc.h 7 Oct 2013 02:08:35 -0000
@@ -209,8 +209,6 @@ struct process {
struct timespec ps_start; /* starting time. */
struct timeout ps_realit_to; /* real-time itimer trampoline. */
- struct timeout ps_virt_to; /* virtual itimer trampoline. */
- struct timeout ps_prof_to; /* prof itimer trampoline. */
int ps_refcnt; /* Number of references. */
};
@@ -362,6 +360,8 @@ struct proc {
* These flags are per-thread and kept in p_flag
*/
#define P_INKTR 0x000001 /* In a ktrace op, don't
recurse */
+#define P_PROFPEND 0x000002 /* SIGPROF needs to be posted */
+#define P_ALRMPEND 0x000004 /* SIGVTALRM needs to be posted
*/
#define P_SIGSUSPEND 0x000008 /* Need to restore
before-suspend mask*/
#define P_SELECT 0x000040 /* Selecting; wakeup/waiting
danger. */
#define P_SINTR 0x000080 /* Sleep is interruptible. */
@@ -380,7 +380,8 @@ struct proc {
#define P_CPUPEG 0x40000000 /* Do not move to another cpu. */
#define P_BITS \
- ("\20\01INKTR\04SIGSUSPEND\07SELECT\010SINTR\012SYSTEM" \
+ ("\20\01INKTR\02PROFPEND\03ALRMPEND\04SIGSUSPEND\07SELECT" \
+ "\010SINTR\012SYSTEM" \
"\013TIMEOUT\016WEXIT\020OWEUPC\024SUSPSINGLE" \
"\025NOZOMBIE\027SYSTRACE\030CONTINUED\033THREAD" \
"\034SUSPSIG\035SOFTDEP\036STOPPED\037CPUPEG")
Index: sys/sys/resourcevar.h
===================================================================
RCS file: /cvs/src/sys/sys/resourcevar.h,v
retrieving revision 1.16
diff -u -p -r1.16 resourcevar.h
--- sys/sys/resourcevar.h 3 Jun 2013 16:55:22 -0000 1.16
+++ sys/sys/resourcevar.h 7 Oct 2013 02:08:35 -0000
@@ -68,8 +68,5 @@ struct plimit *limcopy(struct plimit *);
void limfree(struct plimit *);
void ruadd(struct rusage *, struct rusage *);
-
-void virttimer_trampoline(void *);
-void proftimer_trampoline(void *);
#endif
#endif /* !_SYS_RESOURCEVAR_H_ */
Index: sys/kern/kern_clock.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_clock.c,v
retrieving revision 1.82
diff -u -p -r1.82 kern_clock.c
--- sys/kern/kern_clock.c 13 Aug 2013 05:52:23 -0000 1.82
+++ sys/kern/kern_clock.c 7 Oct 2013 02:08:37 -0000
@@ -144,40 +144,11 @@ initclocks(void)
/*
* hardclock does the accounting needed for ITIMER_PROF and ITIMER_VIRTUAL.
* We don't want to send signals with psignal from hardclock because it makes
- * MULTIPROCESSOR locking very complicated. Instead we use a small trick
- * to send the signals safely and without blocking too many interrupts
- * while doing that (signal handling can be heavy).
- *
- * hardclock detects that the itimer has expired, and schedules a timeout
- * to deliver the signal. This works because of the following reasons:
- * - The timeout can be scheduled with a 1 tick time because we're
- * doing it before the timeout processing in hardclock. So it will
- * be scheduled to run as soon as possible.
- * - The timeout will be run in softclock which will run before we
- * return to userland and process pending signals.
- * - If the system is so busy that several VIRTUAL/PROF ticks are
- * sent before softclock processing, we'll send only one signal.
- * But if we'd send the signal from hardclock only one signal would
- * be delivered to the user process. So userland will only see one
- * signal anyway.
+ * MULTIPROCESSOR locking very complicated. Instead, to use an idea from
+ * FreeBSD, we set a flag on the thread and when it goes to return to
+ * userspace it signals itself.
*/
-void
-virttimer_trampoline(void *v)
-{
- struct process *pr = v;
-
- psignal(pr->ps_mainproc, SIGVTALRM);
-}
-
-void
-proftimer_trampoline(void *v)
-{
- struct process *pr = v;
-
- psignal(pr->ps_mainproc, SIGPROF);
-}
-
/*
* The real-time timer, interrupting hz times per second.
*/
@@ -196,11 +167,15 @@ hardclock(struct clockframe *frame)
*/
if (CLKF_USERMODE(frame) &&
timerisset(&pr->ps_timer[ITIMER_VIRTUAL].it_value) &&
- itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], tick) == 0)
- timeout_add(&pr->ps_virt_to, 1);
+ itimerdecr(&pr->ps_timer[ITIMER_VIRTUAL], tick) == 0) {
+ atomic_setbits_int(&p->p_flag, P_ALRMPEND);
+ need_proftick(p);
+ }
if (timerisset(&pr->ps_timer[ITIMER_PROF].it_value) &&
- itimerdecr(&pr->ps_timer[ITIMER_PROF], tick) == 0)
- timeout_add(&pr->ps_prof_to, 1);
+ itimerdecr(&pr->ps_timer[ITIMER_PROF], tick) == 0) {
+ atomic_setbits_int(&p->p_flag, P_PROFPEND);
+ need_proftick(p);
+ }
}
/*
Index: sys/kern/kern_exit.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_exit.c,v
retrieving revision 1.127
diff -u -p -r1.127 kern_exit.c
--- sys/kern/kern_exit.c 14 Sep 2013 01:35:00 -0000 1.127
+++ sys/kern/kern_exit.c 7 Oct 2013 02:08:37 -0000
@@ -185,8 +185,6 @@ exit1(struct proc *p, int rv, int flags)
if ((p->p_flag & P_THREAD) == 0) {
timeout_del(&pr->ps_realit_to);
- timeout_del(&pr->ps_virt_to);
- timeout_del(&pr->ps_prof_to);
#ifdef SYSVSEM
semexit(pr);
#endif
Index: sys/kern/kern_fork.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_fork.c,v
retrieving revision 1.153
diff -u -p -r1.153 kern_fork.c
--- sys/kern/kern_fork.c 14 Aug 2013 05:26:14 -0000 1.153
+++ sys/kern/kern_fork.c 7 Oct 2013 02:08:38 -0000
@@ -183,8 +183,6 @@ process_new(struct proc *p, struct proce
pr->ps_limit->p_refcnt++;
timeout_set(&pr->ps_realit_to, realitexpire, pr);
- timeout_set(&pr->ps_virt_to, virttimer_trampoline, pr);
- timeout_set(&pr->ps_prof_to, proftimer_trampoline, pr);
pr->ps_flags = parent->ps_flags & (PS_SUGID | PS_SUGIDEXEC);
if (parent->ps_session->s_ttyvp != NULL &&
Index: sys/kern/kern_sig.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sig.c,v
retrieving revision 1.153
diff -u -p -r1.153 kern_sig.c
--- sys/kern/kern_sig.c 6 Oct 2013 19:44:42 -0000 1.153
+++ sys/kern/kern_sig.c 7 Oct 2013 02:08:39 -0000
@@ -811,27 +811,39 @@ ptsignal(struct proc *p, int signum, enu
}
/*
- * A process-wide signal can be diverted to a different
- * thread that's in sigwait() for this signal. If there
- * isn't such a thread, then pick a thread that doesn't
- * have it blocked so that the stop/kill consideration
- * isn't delayed. Otherwise, mark it pending on the
- * main thread.
- */
- TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
- /* ignore exiting threads */
- if (q->p_flag & P_WEXIT)
- continue;
-
- /* sigwait: definitely go to this thread */
- if (q->p_sigdivert & mask) {
- p = q;
- break;
+ * If the current thread can process the signal
+ * immediately, either because it's sigwait()ing
+ * on it or has it unblocked, then have it take it.
+ */
+ q = curproc;
+ if (q != NULL && q->p_p == pr && (q->p_flag & P_WEXIT) == 0 &&
+ ((q->p_sigdivert & mask) || (q->p_sigmask & mask) == 0))
+ p = q;
+ else {
+ /*
+ * A process-wide signal can be diverted to a
+ * different thread that's in sigwait() for this
+ * signal. If there isn't such a thread, then
+ * pick a thread that doesn't have it blocked so
+ * that the stop/kill consideration isn't
+ * delayed. Otherwise, mark it pending on the
+ * main thread.
+ */
+ TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
+ /* ignore exiting threads */
+ if (q->p_flag & P_WEXIT)
+ continue;
+
+ /* sigwait: definitely go to this thread */
+ if (q->p_sigdivert & mask) {
+ p = q;
+ break;
+ }
+
+ /* unblocked: possibly go to this thread */
+ if ((q->p_sigmask & mask) == 0)
+ p = q;
}
-
- /* unblocked: possibly go to this thread */
- if ((q->p_sigmask & mask) == 0)
- p = q;
}
}
@@ -1735,6 +1747,20 @@ void
userret(struct proc *p)
{
int sig;
+
+ /* 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();
+ }
while ((sig = CURSIG(p)) != 0)
postsig(sig);
Index: sys/kern/kern_time.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_time.c,v
retrieving revision 1.83
diff -u -p -r1.83 kern_time.c
--- sys/kern/kern_time.c 6 Oct 2013 01:27:50 -0000 1.83
+++ sys/kern/kern_time.c 7 Oct 2013 02:08:39 -0000
@@ -571,10 +571,6 @@ sys_setitimer(struct proc *p, void *v, r
itimerround(&aitv.it_interval);
s = splclock();
pr->ps_timer[which] = aitv;
- if (which == ITIMER_VIRTUAL)
- timeout_del(&pr->ps_virt_to);
- if (which == ITIMER_PROF)
- timeout_del(&pr->ps_prof_to);
splx(s);
}