Patchwork [RFC] Register Linux dyntick timer as per-thread signal

login
register
mail settings
Submitter Jan Kiszka
Date June 16, 2011, 9:31 a.m.
Message ID <4DF9CD7E.5020509@siemens.com>
Download mbox | patch
Permalink /patch/100611/
State New
Headers show

Comments

Jan Kiszka - June 16, 2011, 9:31 a.m.
Ingo Molnar pointed out that sending the timer signal to the whole
process, just blocking it everywhere, is suboptimal with an increasing
number of threads. QEMU is using this pattern so far.

But Linux provides a (non-portable) way to restrict the signal to a
single thread: Use SIGEV_THREAD_ID unless we are forced to emulate
signalfd via an additional thread. That case could theoretically be
optimized as well, but it doesn't look worth bothering.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 compatfd.c   |   11 +++++++++++
 compatfd.h   |    1 +
 qemu-timer.c |   12 +++++++++++-
 3 files changed, 23 insertions(+), 1 deletions(-)
Ingo Molnar - June 16, 2011, 10:26 a.m.
* Jan Kiszka <jan.kiszka@siemens.com> wrote:

> Ingo Molnar pointed out that sending the timer signal to the whole
> process, just blocking it everywhere, is suboptimal with an increasing
> number of threads. QEMU is using this pattern so far.
> 
> But Linux provides a (non-portable) way to restrict the signal to a
> single thread: Use SIGEV_THREAD_ID unless we are forced to emulate
> signalfd via an additional thread. That case could theoretically be
> optimized as well, but it doesn't look worth bothering.

Would be nice to mention it in the changelog that the context and 
motivation for my remark was a patch sent for tools/kvm/ by Asias He:

  kvm tools: Block SIGALRM for vcpu thread using sig_block() helper

Thanks,

	Ingo
Richard Henderson - June 16, 2011, 2:39 p.m.
On 06/16/2011 02:31 AM, Jan Kiszka wrote:
>      ev.sigev_value.sival_int = 0;
> -    ev.sigev_notify = SIGEV_SIGNAL;
>      ev.sigev_signo = SIGALRM;
> +#ifdef SIGEV_THREAD_ID
> +    if (qemu_signalfd_available()) {
> +        ev.sigev_notify = SIGEV_THREAD_ID;
> +        ev._sigev_un._tid = qemu_get_thread_id();
> +    } else
> +#endif /* SIGEV_THREAD_ID */
> +    {
> +        ev.sigev_notify = SIGEV_SIGNAL;
> +    }
>  

Rather than do the else-inside-ifdef thing, why not
leave the original setting of sigev_notify where it
was, and let the ifdef overwrite it?


r~
Alexandre Raymond - June 16, 2011, 3:24 p.m.
Hi Jan,

On Thu, Jun 16, 2011 at 5:31 AM, Jan Kiszka <jan.kiszka@siemens.com> wrote:
> Ingo Molnar pointed out that sending the timer signal to the whole
> process, just blocking it everywhere, is suboptimal with an increasing
> number of threads. QEMU is using this pattern so far.

I am not familiar with this code, but don't you already need to block
SIGALRM properly in all threads for OSes != Linux ? If so, isn't this
patch redundant?

Alexandre
Jan Kiszka - June 17, 2011, 9:25 a.m.
On 2011-06-16 17:24, Alexandre Raymond wrote:
> Hi Jan,
> 
> On Thu, Jun 16, 2011 at 5:31 AM, Jan Kiszka <jan.kiszka@siemens.com> wrote:
>> Ingo Molnar pointed out that sending the timer signal to the whole
>> process, just blocking it everywhere, is suboptimal with an increasing
>> number of threads. QEMU is using this pattern so far.
> 
> I am not familiar with this code, but don't you already need to block
> SIGALRM properly in all threads for OSes != Linux ? If so, isn't this
> patch redundant?

Yes, we still need to block for the sake of non-Linux UNIX or
pre-signalfd Linux. That blocking becomes in fact redundant in the
signalfd case, but that's both harmless and not worth optimizing. The
key is that per-thread signals do not care about other threads having
them blocked or not, they only deal with the target thread.

Jan

Patch

diff --git a/compatfd.c b/compatfd.c
index 41586ce..31654c6 100644
--- a/compatfd.c
+++ b/compatfd.c
@@ -115,3 +115,14 @@  int qemu_signalfd(const sigset_t *mask)
 
     return qemu_signalfd_compat(mask);
 }
+
+bool qemu_signalfd_available(void)
+{
+#ifdef CONFIG_SIGNALFD
+    errno = 0;
+    syscall(SYS_signalfd, -1, NULL, _NSIG / 8);
+    return errno != ENOSYS;
+#else
+    return false;
+#endif
+}
diff --git a/compatfd.h b/compatfd.h
index fc37915..6b04877 100644
--- a/compatfd.h
+++ b/compatfd.h
@@ -39,5 +39,6 @@  struct qemu_signalfd_siginfo {
 };
 
 int qemu_signalfd(const sigset_t *mask);
+bool qemu_signalfd_available(void);
 
 #endif
diff --git a/qemu-timer.c b/qemu-timer.c
index 72066c7..09e6f17 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -803,6 +803,8 @@  static int64_t qemu_next_alarm_deadline(void)
 
 #if defined(__linux__)
 
+#include "compatfd.h"
+
 static int dynticks_start_timer(struct qemu_alarm_timer *t)
 {
     struct sigevent ev;
@@ -821,8 +823,16 @@  static int dynticks_start_timer(struct qemu_alarm_timer *t)
      */
     memset(&ev, 0, sizeof(ev));
     ev.sigev_value.sival_int = 0;
-    ev.sigev_notify = SIGEV_SIGNAL;
     ev.sigev_signo = SIGALRM;
+#ifdef SIGEV_THREAD_ID
+    if (qemu_signalfd_available()) {
+        ev.sigev_notify = SIGEV_THREAD_ID;
+        ev._sigev_un._tid = qemu_get_thread_id();
+    } else
+#endif /* SIGEV_THREAD_ID */
+    {
+        ev.sigev_notify = SIGEV_SIGNAL;
+    }
 
     if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
         perror("timer_create");