From: Luca Tettamanti <kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
Cc: Dan Kenigsberg <dank-atKUWr5tajBWk0Htik3J/w@public.gmane.org>,
Luca Tettamanti
<kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
qemu-devel-qX2TKyscuCcdnm+yROfE0A@public.gmane.org
Subject: [PATCH/RFC 4/4] Add support for dynamic ticks.
Date: Thu, 16 Aug 2007 22:41:17 +0200 [thread overview]
Message-ID: <1187296877557-git-send-email-kronos.it@gmail.com> (raw)
In-Reply-To: <11872968773524-git-send-email-kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
If DYNAMIC_TICKS is defined qemu does not attepmt to generate SIGALRM at a
constant rate. Rather, the system timer is set to generate SIGALRM only
when it is needed. DYNAMIC_TICKS reduces the number of SIGALRMs sent to
idle dynamic-ticked guests.
Original patch from Dan Kenigsberg <dank-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
Signed-off-by: Luca Tettamanti <kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
qemu/configure | 5 ++
qemu/vl.c | 149 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 148 insertions(+), 6 deletions(-)
diff --git a/qemu/configure b/qemu/configure
index 365b7fb..38373db 100755
--- a/qemu/configure
+++ b/qemu/configure
@@ -262,6 +262,8 @@ for opt do
;;
--enable-uname-release=*) uname_release="$optarg"
;;
+ --disable-dynamic-ticks) dynamic_ticks="no"
+ ;;
esac
done
@@ -788,6 +790,9 @@ echo "TARGET_DIRS=$target_list" >> $config_mak
if [ "$build_docs" = "yes" ] ; then
echo "BUILD_DOCS=yes" >> $config_mak
fi
+if test "$dynamic_ticks" != "no" ; then
+ echo "#define DYNAMIC_TICKS 1" >> $config_h
+fi
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
diff --git a/qemu/vl.c b/qemu/vl.c
index 0373beb..096729d 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -748,12 +748,42 @@ struct QEMUTimer {
struct qemu_alarm_timer {
char const *name;
+ unsigned int flags;
int (*start)(struct qemu_alarm_timer *t);
void (*stop)(struct qemu_alarm_timer *t);
+ void (*rearm)(struct qemu_alarm_timer *t);
void *priv;
};
+#define ALARM_FLAG_DYNTICKS 0x1
+
+#ifdef DYNAMIC_TICKS
+
+static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
+{
+ return t->flags & ALARM_FLAG_DYNTICKS;
+}
+
+static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) {
+ if (!alarm_has_dynticks(t))
+ return;
+
+ t->rearm(t);
+}
+
+#else /* DYNAMIC_TICKS */
+
+static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
+{
+ return 0;
+}
+
+static void qemu_rearm_alarm_timer(void) {
+}
+
+#endif /* DYNAMIC_TICKS */
+
static struct qemu_alarm_timer *alarm_timer;
#ifdef _WIN32
@@ -772,6 +802,14 @@ static void win32_stop_timer(struct qemu_alarm_timer *t);
static int unix_start_timer(struct qemu_alarm_timer *t);
static void unix_stop_timer(struct qemu_alarm_timer *t);
+#ifdef DYNAMIC_TICKS
+
+static int dynticks_start_timer(struct qemu_alarm_timer *t);
+static void dynticks_stop_timer(struct qemu_alarm_timer *t);
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t);
+
+#endif
+
#ifdef __linux__
static int hpet_start_timer(struct qemu_alarm_timer *t);
@@ -785,16 +823,19 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t);
#endif /* _WIN32 */
static struct qemu_alarm_timer alarm_timers[] = {
+#ifndef _WIN32
+#ifdef DYNAMIC_TICKS
+ {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer, dynticks_stop_timer, dynticks_rearm_timer, NULL},
+#endif
#ifdef __linux__
/* HPET - if available - is preferred */
- {"hpet", hpet_start_timer, hpet_stop_timer, NULL},
+ {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},
/* ...otherwise try RTC */
- {"rtc", rtc_start_timer, rtc_stop_timer, NULL},
+ {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL},
#endif
-#ifndef _WIN32
- {"unix", unix_start_timer, unix_stop_timer, NULL},
+ {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL},
#else
- {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data},
+ {"win32", 0, win32_start_timer, win32_stop_timer, NULL, &alarm_win32_data},
#endif
{NULL, }
};
@@ -913,6 +954,8 @@ void qemu_del_timer(QEMUTimer *ts)
}
pt = &t->next;
}
+
+ qemu_rearm_alarm_timer(alarm_timer);
}
/* modify the current timer so that it will be fired when current_time
@@ -972,6 +1015,7 @@ static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time)
/* run the callback (the timer list can be modified) */
ts->cb(ts->opaque);
}
+ qemu_rearm_alarm_timer(alarm_timer);
}
int64_t qemu_get_clock(QEMUClock *clock)
@@ -1079,7 +1123,8 @@ static void host_alarm_handler(int host_signum)
last_clock = ti;
}
#endif
- if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
+ if (alarm_has_dynticks(alarm_timer) ||
+ qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
qemu_get_clock(vm_clock)) ||
qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
qemu_get_clock(rt_clock))) {
@@ -1207,6 +1252,97 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t)
#endif /* !defined(__linux__) */
+#ifdef DYNAMIC_TICKS
+static int dynticks_start_timer(struct qemu_alarm_timer *t)
+{
+ struct sigevent ev;
+ timer_t host_timer;
+ struct sigaction act;
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+#if defined(TARGET_I386) && defined(USE_CODE_COPY)
+ act.sa_flags |= SA_ONSTACK;
+#endif
+ act.sa_handler = host_alarm_handler;
+
+ sigaction(SIGALRM, &act, NULL);
+
+ ev.sigev_value.sival_int = 0;
+ ev.sigev_notify = SIGEV_SIGNAL;
+ ev.sigev_signo = SIGALRM;
+
+ if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
+ perror("timer_create");
+
+ /* disable dynticks */
+ fprintf(stderr, "Dynamic Ticks disabled\n");
+
+ return -1;
+ }
+
+ t->priv = (void *)host_timer;
+
+ return 0;
+}
+
+static void dynticks_stop_timer(struct qemu_alarm_timer *t)
+{
+ timer_t host_timer = (timer_t)t->priv;
+
+ timer_delete(host_timer);
+}
+
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
+{
+ timer_t host_timer = (timer_t)t->priv;
+ struct itimerspec timeout;
+ int64_t nearest_delta_us = INT64_MAX;
+
+ if (active_timers[QEMU_TIMER_REALTIME] ||
+ active_timers[QEMU_TIMER_VIRTUAL]) {
+ int64_t vmdelta_us, current_us;
+
+ if (active_timers[QEMU_TIMER_REALTIME])
+ nearest_delta_us = (active_timers[QEMU_TIMER_REALTIME]->expire_time - qemu_get_clock(rt_clock))*1000;
+
+ if (active_timers[QEMU_TIMER_VIRTUAL]) {
+ /* round up */
+ vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time - qemu_get_clock(vm_clock)+999)/1000;
+ if (vmdelta_us < nearest_delta_us)
+ nearest_delta_us = vmdelta_us;
+ }
+
+ /* Avoid arming the timer to negative, zero, or too low values */
+ /* TODO: MIN_TIMER_REARM_US should be optimized */
+ #define MIN_TIMER_REARM_US 250
+ if (nearest_delta_us <= MIN_TIMER_REARM_US)
+ nearest_delta_us = MIN_TIMER_REARM_US;
+
+ /* check whether a timer is already running */
+ if (timer_gettime(host_timer, &timeout)) {
+ perror("gettime");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
+ current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000;
+ if (current_us && current_us <= nearest_delta_us)
+ return;
+
+ timeout.it_interval.tv_sec = 0;
+ timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
+ timeout.it_value.tv_sec = nearest_delta_us / 1000000;
+ timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000;
+ if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) {
+ perror("settime");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
+ }
+}
+
+#endif /* DYNAMIC_TICKS */
+
static int unix_start_timer(struct qemu_alarm_timer *t)
{
struct sigaction act;
@@ -6259,6 +6395,7 @@ void vm_start(void)
cpu_enable_ticks();
vm_running = 1;
vm_state_notify(1);
+ qemu_rearm_alarm_timer(alarm_timer);
}
}
--
1.5.2.4
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems? Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
WARNING: multiple messages have this Message-ID (diff)
From: Luca Tettamanti <kronos.it@gmail.com>
To: kvm-devel@lists.sourceforge.net
Cc: Dan Kenigsberg <dank@qumranet.com>,
Luca Tettamanti <kronos.it@gmail.com>,
qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH/RFC 4/4] Add support for dynamic ticks.
Date: Thu, 16 Aug 2007 22:41:17 +0200 [thread overview]
Message-ID: <1187296877557-git-send-email-kronos.it@gmail.com> (raw)
In-Reply-To: <11872968773524-git-send-email-kronos.it@gmail.com>
If DYNAMIC_TICKS is defined qemu does not attepmt to generate SIGALRM at a
constant rate. Rather, the system timer is set to generate SIGALRM only
when it is needed. DYNAMIC_TICKS reduces the number of SIGALRMs sent to
idle dynamic-ticked guests.
Original patch from Dan Kenigsberg <dank@qumranet.com>
Signed-off-by: Luca Tettamanti <kronos.it@gmail.com>
---
qemu/configure | 5 ++
qemu/vl.c | 149 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 148 insertions(+), 6 deletions(-)
diff --git a/qemu/configure b/qemu/configure
index 365b7fb..38373db 100755
--- a/qemu/configure
+++ b/qemu/configure
@@ -262,6 +262,8 @@ for opt do
;;
--enable-uname-release=*) uname_release="$optarg"
;;
+ --disable-dynamic-ticks) dynamic_ticks="no"
+ ;;
esac
done
@@ -788,6 +790,9 @@ echo "TARGET_DIRS=$target_list" >> $config_mak
if [ "$build_docs" = "yes" ] ; then
echo "BUILD_DOCS=yes" >> $config_mak
fi
+if test "$dynamic_ticks" != "no" ; then
+ echo "#define DYNAMIC_TICKS 1" >> $config_h
+fi
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
diff --git a/qemu/vl.c b/qemu/vl.c
index 0373beb..096729d 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -748,12 +748,42 @@ struct QEMUTimer {
struct qemu_alarm_timer {
char const *name;
+ unsigned int flags;
int (*start)(struct qemu_alarm_timer *t);
void (*stop)(struct qemu_alarm_timer *t);
+ void (*rearm)(struct qemu_alarm_timer *t);
void *priv;
};
+#define ALARM_FLAG_DYNTICKS 0x1
+
+#ifdef DYNAMIC_TICKS
+
+static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
+{
+ return t->flags & ALARM_FLAG_DYNTICKS;
+}
+
+static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) {
+ if (!alarm_has_dynticks(t))
+ return;
+
+ t->rearm(t);
+}
+
+#else /* DYNAMIC_TICKS */
+
+static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
+{
+ return 0;
+}
+
+static void qemu_rearm_alarm_timer(void) {
+}
+
+#endif /* DYNAMIC_TICKS */
+
static struct qemu_alarm_timer *alarm_timer;
#ifdef _WIN32
@@ -772,6 +802,14 @@ static void win32_stop_timer(struct qemu_alarm_timer *t);
static int unix_start_timer(struct qemu_alarm_timer *t);
static void unix_stop_timer(struct qemu_alarm_timer *t);
+#ifdef DYNAMIC_TICKS
+
+static int dynticks_start_timer(struct qemu_alarm_timer *t);
+static void dynticks_stop_timer(struct qemu_alarm_timer *t);
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t);
+
+#endif
+
#ifdef __linux__
static int hpet_start_timer(struct qemu_alarm_timer *t);
@@ -785,16 +823,19 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t);
#endif /* _WIN32 */
static struct qemu_alarm_timer alarm_timers[] = {
+#ifndef _WIN32
+#ifdef DYNAMIC_TICKS
+ {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer, dynticks_stop_timer, dynticks_rearm_timer, NULL},
+#endif
#ifdef __linux__
/* HPET - if available - is preferred */
- {"hpet", hpet_start_timer, hpet_stop_timer, NULL},
+ {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},
/* ...otherwise try RTC */
- {"rtc", rtc_start_timer, rtc_stop_timer, NULL},
+ {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL},
#endif
-#ifndef _WIN32
- {"unix", unix_start_timer, unix_stop_timer, NULL},
+ {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL},
#else
- {"win32", win32_start_timer, win32_stop_timer, &alarm_win32_data},
+ {"win32", 0, win32_start_timer, win32_stop_timer, NULL, &alarm_win32_data},
#endif
{NULL, }
};
@@ -913,6 +954,8 @@ void qemu_del_timer(QEMUTimer *ts)
}
pt = &t->next;
}
+
+ qemu_rearm_alarm_timer(alarm_timer);
}
/* modify the current timer so that it will be fired when current_time
@@ -972,6 +1015,7 @@ static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time)
/* run the callback (the timer list can be modified) */
ts->cb(ts->opaque);
}
+ qemu_rearm_alarm_timer(alarm_timer);
}
int64_t qemu_get_clock(QEMUClock *clock)
@@ -1079,7 +1123,8 @@ static void host_alarm_handler(int host_signum)
last_clock = ti;
}
#endif
- if (qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
+ if (alarm_has_dynticks(alarm_timer) ||
+ qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
qemu_get_clock(vm_clock)) ||
qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
qemu_get_clock(rt_clock))) {
@@ -1207,6 +1252,97 @@ static void rtc_stop_timer(struct qemu_alarm_timer *t)
#endif /* !defined(__linux__) */
+#ifdef DYNAMIC_TICKS
+static int dynticks_start_timer(struct qemu_alarm_timer *t)
+{
+ struct sigevent ev;
+ timer_t host_timer;
+ struct sigaction act;
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+#if defined(TARGET_I386) && defined(USE_CODE_COPY)
+ act.sa_flags |= SA_ONSTACK;
+#endif
+ act.sa_handler = host_alarm_handler;
+
+ sigaction(SIGALRM, &act, NULL);
+
+ ev.sigev_value.sival_int = 0;
+ ev.sigev_notify = SIGEV_SIGNAL;
+ ev.sigev_signo = SIGALRM;
+
+ if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
+ perror("timer_create");
+
+ /* disable dynticks */
+ fprintf(stderr, "Dynamic Ticks disabled\n");
+
+ return -1;
+ }
+
+ t->priv = (void *)host_timer;
+
+ return 0;
+}
+
+static void dynticks_stop_timer(struct qemu_alarm_timer *t)
+{
+ timer_t host_timer = (timer_t)t->priv;
+
+ timer_delete(host_timer);
+}
+
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
+{
+ timer_t host_timer = (timer_t)t->priv;
+ struct itimerspec timeout;
+ int64_t nearest_delta_us = INT64_MAX;
+
+ if (active_timers[QEMU_TIMER_REALTIME] ||
+ active_timers[QEMU_TIMER_VIRTUAL]) {
+ int64_t vmdelta_us, current_us;
+
+ if (active_timers[QEMU_TIMER_REALTIME])
+ nearest_delta_us = (active_timers[QEMU_TIMER_REALTIME]->expire_time - qemu_get_clock(rt_clock))*1000;
+
+ if (active_timers[QEMU_TIMER_VIRTUAL]) {
+ /* round up */
+ vmdelta_us = (active_timers[QEMU_TIMER_VIRTUAL]->expire_time - qemu_get_clock(vm_clock)+999)/1000;
+ if (vmdelta_us < nearest_delta_us)
+ nearest_delta_us = vmdelta_us;
+ }
+
+ /* Avoid arming the timer to negative, zero, or too low values */
+ /* TODO: MIN_TIMER_REARM_US should be optimized */
+ #define MIN_TIMER_REARM_US 250
+ if (nearest_delta_us <= MIN_TIMER_REARM_US)
+ nearest_delta_us = MIN_TIMER_REARM_US;
+
+ /* check whether a timer is already running */
+ if (timer_gettime(host_timer, &timeout)) {
+ perror("gettime");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
+ current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000;
+ if (current_us && current_us <= nearest_delta_us)
+ return;
+
+ timeout.it_interval.tv_sec = 0;
+ timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
+ timeout.it_value.tv_sec = nearest_delta_us / 1000000;
+ timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000;
+ if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) {
+ perror("settime");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
+ }
+}
+
+#endif /* DYNAMIC_TICKS */
+
static int unix_start_timer(struct qemu_alarm_timer *t)
{
struct sigaction act;
@@ -6259,6 +6395,7 @@ void vm_start(void)
cpu_enable_ticks();
vm_running = 1;
vm_state_notify(1);
+ qemu_rearm_alarm_timer(alarm_timer);
}
}
--
1.5.2.4
next prev parent reply other threads:[~2007-08-16 20:41 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-08-16 20:41 [PATCH/RFC 0/4] Rework alarm timer infrastrucure Luca Tettamanti
2007-08-16 20:41 ` [Qemu-devel] " Luca Tettamanti
[not found] ` <11872968773449-git-send-email-kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2007-08-16 20:41 ` [PATCH/RFC 1/4] " Luca Tettamanti
2007-08-16 20:41 ` [Qemu-devel] " Luca Tettamanti
[not found] ` <11872968771257-git-send-email-kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2007-08-16 20:41 ` [PATCH/RFC 2/4] Add -clock option Luca Tettamanti
2007-08-16 20:41 ` [Qemu-devel] " Luca Tettamanti
[not found] ` <11872968773155-git-send-email-kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2007-08-16 20:41 ` [PATCH/RFC 3/4] Add support for HPET periodic timer Luca Tettamanti
2007-08-16 20:41 ` [Qemu-devel] " Luca Tettamanti
[not found] ` <11872968773524-git-send-email-kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2007-08-16 20:41 ` Luca Tettamanti [this message]
2007-08-16 20:41 ` [Qemu-devel] [PATCH/RFC 4/4] Add support for dynamic ticks Luca Tettamanti
[not found] ` <1187296877557-git-send-email-kronos.it-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2007-08-17 7:16 ` Matthew Kent
2007-08-17 7:16 ` [Qemu-devel] Re: [kvm-devel] " Matthew Kent
[not found] ` <42755.207.81.93.54.1187334969.squirrel-N19E6/cp6YFSV923RJcDwA@public.gmane.org>
2007-08-19 8:08 ` Avi Kivity
2007-08-19 8:08 ` [Qemu-devel] Re: [kvm-devel] " Avi Kivity
2007-08-17 12:44 ` [PATCH/RFC 0/4] Rework alarm timer infrastrucure Avi Kivity
2007-08-17 12:44 ` [Qemu-devel] Re: [kvm-devel] " Avi Kivity
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1187296877557-git-send-email-kronos.it@gmail.com \
--to=kronos.it-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=dank-atKUWr5tajBWk0Htik3J/w@public.gmane.org \
--cc=kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
--cc=qemu-devel-qX2TKyscuCcdnm+yROfE0A@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.