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); }