All of lore.kernel.org
 help / color / mirror / Atom feed
From: Julien Grall <julien.grall@arm.com>
To: Mirela Simonovic <mirela.simonovic@aggios.com>,
	xen-devel@lists.xen.org, xen-devel@lists.xenproject.org
Cc: stefano.stabellini@xilinx.com,
	Stefano Stabellini <sstabellini@kernel.org>,
	dm@aggios.com, saeed.nowshadi@xilinx.com
Subject: Re: [PATCH 10/18] xen/arm: Implement GIC suspend/resume functions (gicv2 only)
Date: Wed, 14 Nov 2018 12:41:18 +0000	[thread overview]
Message-ID: <746b0ad8-4ce1-dfaf-eadc-939657426900@arm.com> (raw)
In-Reply-To: <1542022244-22977-11-git-send-email-mirela.simonovic@aggios.com>

Hi,

I am not entirely sure where to ask it, so I will do it here. From my 
understanding of the PSCI spec, all the interrupts should have been migrated 
away from turned off CPU. Where do you ensure that in the suspend path?


On 12/11/2018 11:30, Mirela Simonovic wrote:
> System suspend may lead to a state where GIC would be powered down.
> Therefore, Xen should save/restore the context of GIC on suspend/resume.
> Note that the context consists of states of registers which are
> controlled by the hypervisor. Other GIC registers which are accessible
> by guests are saved/restored on context switch.
> Tested on Xilinx Ultrascale+ MPSoC with (and without) powering down
> the GIC.
> 
> Signed-off-by: Mirela Simonovic <mirela.simonovic@aggios.com>
> Signed-off-by: Saeed Nowshadi <saeed.nowshadi@xilinx.com>
> ---
>   xen/arch/arm/gic-v2.c     | 147 ++++++++++++++++++++++++++++++++++++++++++++++
>   xen/arch/arm/gic.c        |  27 +++++++++
>   xen/include/asm-arm/gic.h |   8 +++
>   3 files changed, 182 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
> index e7eb01f30a..bb52b64ecb 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -123,6 +123,25 @@ static DEFINE_PER_CPU(u8, gic_cpu_id);
>   /* Maximum cpu interface per GIC */
>   #define NR_GIC_CPU_IF 8
>   
> +/* GICv2 registers to be saved/restored on system suspend/resume */
> +struct gicv2_context {
> +    /* GICC context */
> +    uint32_t gicc_ctlr;
> +    uint32_t gicc_pmr;
> +    uint32_t gicc_bpr;
> +    /* GICD context */
> +    uint32_t gicd_ctlr;
> +    uint32_t *gicd_isenabler;
> +    uint32_t *gicd_isactiver;
> +    uint32_t *gicd_ipriorityr;
> +    uint32_t *gicd_itargetsr;
> +    uint32_t *gicd_icfgr;
> +};

It took me a long time to understand that you will only save the context for the 
CPU0 and Distributor.

I would prefer if we keep separate per-CPU context and common context. This 
would keep the logic very similar to the rest of the GIC drivers.

> +
> +static struct gicv2_context gicv2_context;
> +
> +static void gicv2_alloc_context(struct gicv2_context *gc);

Please don't do forward declaration. The code should be added in the correct place.

> +
>   static inline void writeb_gicd(uint8_t val, unsigned int offset)
>   {
>       writeb_relaxed(val, gicv2.map_dbase + offset);
> @@ -1310,6 +1329,9 @@ static int __init gicv2_init(void)
>   
>       spin_unlock(&gicv2.lock);
>   
> +    /* Allocate memory to be used for saving GIC context during the suspend */
> +    gicv2_alloc_context(&gicv2_context);
> +
>       return 0;
>   }
>   
> @@ -1319,6 +1341,129 @@ static void gicv2_do_LPI(unsigned int lpi)
>       BUG();
>   }
>   
> +static void gicv2_alloc_context(struct gicv2_context *gc)
> +{

Is it necessary to allocate them at boot? Can we make them static or allocate 
them when we suspend?

> +    uint32_t n = gicv2_info.nr_lines;
> +
> +    gc->gicd_isenabler = xzalloc_array(uint32_t, DIV_ROUND_UP(n, 32));
> +    if ( !gc->gicd_isenabler )
> +        return;
> +
> +    gc->gicd_isactiver = xzalloc_array(uint32_t, DIV_ROUND_UP(n, 32));
> +    if ( !gc->gicd_isactiver )
> +        goto free_gicd_isenabler;
> +
> +    gc->gicd_itargetsr = xzalloc_array(uint32_t, DIV_ROUND_UP(n, 4));
> +    if ( !gc->gicd_itargetsr )
> +        goto free_gicd_isactiver;
> +
> +    gc->gicd_ipriorityr = xzalloc_array(uint32_t, DIV_ROUND_UP(n, 4));
> +    if ( !gc->gicd_ipriorityr )
> +        goto free_gicd_itargetsr;
> +
> +    gc->gicd_icfgr = xzalloc_array(uint32_t, DIV_ROUND_UP(n, 16));
> +    if ( gc->gicd_icfgr )
> +        return;
> +
> +    xfree(gc->gicd_ipriorityr);
> +
> +free_gicd_itargetsr:
> +    xfree(gc->gicd_itargetsr);
> +
> +free_gicd_isactiver:
> +    xfree(gc->gicd_isactiver);
> +
> +free_gicd_isenabler:
> +    xfree(gc->gicd_isenabler);
> +    gc->gicd_isenabler = NULL;
> +}
> +
> +static int gicv2_suspend(void)
> +{
> +    int i;
> +
> +    /* Save GICC configuration */
> +    gicv2_context.gicc_ctlr = readl_gicc(GICC_CTLR);
> +    gicv2_context.gicc_pmr = readl_gicc(GICC_PMR);
> +    gicv2_context.gicc_bpr = readl_gicc(GICC_BPR);

AFAICT, those values should always be the same. So I would just restore them 
with the hardcoded value.

This could likely be factored in a separate function that could be used in 
gicv2_cpu_init as well.

> +
> +    /* If gicv2_alloc_context() hasn't allocated memory, return */
> +    if ( !gicv2_context.gicd_isenabler )
> +        return -ENOMEM;
> +
> +    /* Save GICD configuration */
> +    gicv2_context.gicd_ctlr = readl_gicd(GICD_CTLR);

Same here.

> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 32); i++ )
> +        gicv2_context.gicd_isenabler[i] = readl_gicd(GICD_ISENABLER + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 32); i++ )
> +        gicv2_context.gicd_isactiver[i] = readl_gicd(GICD_ISACTIVER + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 4); i++ )
> +        gicv2_context.gicd_ipriorityr[i] = readl_gicd(GICD_IPRIORITYR + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 4); i++ )
> +        gicv2_context.gicd_itargetsr[i] = readl_gicd(GICD_ITARGETSR + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 16); i++ )
> +        gicv2_context.gicd_icfgr[i] = readl_gicd(GICD_ICFGR + i * 4);
> +
> +    return 0;
> +}
> +
> +static void gicv2_resume(void)
> +{
> +    int i;
> +
> +    ASSERT(gicv2_context.gicd_isenabler);
> +    ASSERT(gicv2_context.gicd_isactiver);
> +    ASSERT(gicv2_context.gicd_ipriorityr);
> +    ASSERT(gicv2_context.gicd_itargetsr);
> +    ASSERT(gicv2_context.gicd_icfgr);
> +
> +    /* Disable CPU interface and distributor */
> +    writel_gicc(0, GICC_CTLR);
> +    writel_gicd(0, GICD_CTLR);
> +    isb();
> +
> +    /* Restore GICD configuration */
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 32); i++ )
> +        writel_gicd(0xffffffff, GICD_ICENABLER + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 32); i++ )
> +        writel_gicd(gicv2_context.gicd_isenabler[i], GICD_ISENABLER + i * 4);

I would prefer if there is only one for with 2 write. This would make more 
obvious why you need to clear all enabled bit first.

> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 32); i++ )
> +        writel_gicd(0xffffffff, GICD_ICACTIVER + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 32); i++ )
> +        writel_gicd(gicv2_context.gicd_isactiver[i], GICD_ISACTIVER + i * 4);

Same here.

> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 4); i++ )
> +        writel_gicd(gicv2_context.gicd_ipriorityr[i], GICD_IPRIORITYR + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 4); i++ )
> +        writel_gicd(gicv2_context.gicd_itargetsr[i], GICD_ITARGETSR + i * 4);
> +
> +    for ( i = 0; i < DIV_ROUND_UP(gicv2_info.nr_lines, 16); i++ )
> +        writel_gicd(gicv2_context.gicd_icfgr[i], GICD_ICFGR + i * 4);
> +
> +    /* Make sure all registers are restored and enable distributor */
> +    isb();
> +    writel_gicd(gicv2_context.gicd_ctlr | GICD_CTL_ENABLE, GICD_CTLR);
> +
> +    /* Restore GIC CPU interface configuration */
> +    writel_gicc(gicv2_context.gicc_pmr, GICC_PMR);
> +    writel_gicc(gicv2_context.gicc_bpr, GICC_BPR);
> +    isb();
> +
> +    /* Enable GIC CPU interface */
> +    writel_gicc(gicv2_context.gicc_ctlr | GICC_CTL_ENABLE | GICC_CTL_EOI,
> +                GICC_CTLR);
> +    isb();
> +}
> +
>   const static struct gic_hw_operations gicv2_ops = {
>       .info                = &gicv2_info,
>       .init                = gicv2_init,
> @@ -1351,6 +1496,8 @@ const static struct gic_hw_operations gicv2_ops = {
>       .map_hwdom_extra_mappings = gicv2_map_hwdown_extra_mappings,
>       .iomem_deny_access   = gicv2_iomem_deny_access,
>       .do_LPI              = gicv2_do_LPI,
> +    .suspend             = gicv2_suspend,
> +    .resume              = gicv2_resume,
>   };
>   
>   /* Set up the GIC */
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index e524ad583d..6e98f43691 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -464,6 +464,33 @@ int gic_iomem_deny_access(const struct domain *d)
>       return gic_hw_ops->iomem_deny_access(d);
>   }
>   
> +int gic_suspend(void)
> +{
> +    /* Must be called by boot CPU#0 with interrupts disabled */
> +    ASSERT(!local_irq_is_enabled());
> +    ASSERT(!smp_processor_id());
> +
> +    if ( !gic_hw_ops->suspend || !gic_hw_ops->resume )
> +        return -ENOSYS;
> +
> +    gic_hw_ops->suspend();
> +
> +    return 0;
> +}
> +
> +void gic_resume(void)
> +{
> +    /*
> +     * Must be called by boot CPU#0 with interrupts disabled after gic_suspend
> +     * has returned successfully.
> +     */
> +    ASSERT(!local_irq_is_enabled());
> +    ASSERT(!smp_processor_id());
> +    ASSERT(gic_hw_ops->resume);
> +
> +    gic_hw_ops->resume();
> +}
> +
>   static int cpu_gic_callback(struct notifier_block *nfb,
>                               unsigned long action,
>                               void *hcpu)
> diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
> index 22fa122e52..46066caac8 100644
> --- a/xen/include/asm-arm/gic.h
> +++ b/xen/include/asm-arm/gic.h
> @@ -277,6 +277,10 @@ extern int gicv_setup(struct domain *d);
>   extern void gic_save_state(struct vcpu *v);
>   extern void gic_restore_state(struct vcpu *v);
>   
> +/* Suspend/resume */
> +extern int gic_suspend(void);
> +extern void gic_resume(void);
> +
>   /* SGI (AKA IPIs) */
>   enum gic_sgi {
>       GIC_SGI_EVENT_CHECK = 0,
> @@ -390,6 +394,10 @@ struct gic_hw_operations {
>       int (*iomem_deny_access)(const struct domain *d);
>       /* Handle LPIs, which require special handling */
>       void (*do_LPI)(unsigned int lpi);
> +    /* Save GIC configuration due to the system suspend */
> +    int (*suspend)(void);
> +    /* Restore GIC configuration due to the system resume */
> +    void (*resume)(void);

The comments on tops of suspend/resume suggest that this should be called 
save/restore.

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel

  parent reply	other threads:[~2018-11-14 12:41 UTC|newest]

Thread overview: 153+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-12 11:30 [PATCH 00/18] xen/arm64: Suspend to RAM support for Xen Mirela Simonovic
2018-11-12 11:30 ` [PATCH 01/18] xen/arm: Move code that initializes VCPU context into a separate function Mirela Simonovic
2018-11-12 11:41   ` Jan Beulich
2018-11-12 11:43   ` Wei Liu
2018-11-12 13:15   ` Julien Grall
2018-11-14 11:45     ` Mirela Simonovic
2018-11-12 11:30 ` [PATCH 02/18] xen/arm: Implement PSCI system suspend call (virtual interface) Mirela Simonovic
2018-11-12 11:42   ` Jan Beulich
2018-11-12 11:50     ` Mirela Simonovic
2018-11-12 12:45       ` Jan Beulich
2018-11-12 15:27   ` Julien Grall
2018-11-12 16:35     ` Mirela Simonovic
2018-11-12 16:41       ` Andrew Cooper
2018-11-12 19:56         ` Julien Grall
2018-11-13  8:32           ` Andrew Cooper
2018-11-14 12:08             ` Mirela Simonovic
2018-11-14 12:49               ` Julien Grall
2018-11-15  0:47                 ` Andrew Cooper
2018-11-15 10:13                   ` Julien Grall
2018-11-15 10:26                     ` Andrew Cooper
2018-11-15 10:33                       ` Mirela Simonovic
2018-11-15 10:59                         ` Julien Grall
2018-11-15 11:10                           ` Mirela Simonovic
2018-11-15 11:38                             ` Julien Grall
2018-11-15 11:42                               ` Mirela Simonovic
2018-11-15 19:30                                 ` Stefano Stabellini
2018-11-15 20:25                                 ` Julien Grall
2018-11-16 22:10                               ` Dario Faggioli
2018-11-15 10:36                       ` Julien Grall
2018-11-15 10:36                       ` Julien Grall
2018-11-15 10:50                         ` Andrew Cooper
2018-11-13  1:53         ` Stefano Stabellini
2018-11-13  9:41           ` Julien Grall
2018-11-13 20:39             ` Stefano Stabellini
2018-11-14 13:10               ` Julien Grall
2018-11-14 23:08                 ` Stefano Stabellini
2018-11-12 20:29       ` Julien Grall
2018-11-13 20:39         ` Stefano Stabellini
2018-11-14 10:45           ` Julien Grall
2018-11-14 12:35             ` Mirela Simonovic
2018-11-14 13:05               ` Julien Grall
2018-11-14 14:48                 ` Julien Grall
2018-11-14 15:36                   ` Mirela Simonovic
2018-11-14 15:37                     ` Mirela Simonovic
2018-11-14 15:51                     ` Julien Grall
2018-11-13 10:23   ` Julien Grall
2018-11-13 15:09     ` Julien Grall
2018-11-14 12:44       ` Mirela Simonovic
2018-11-12 11:30 ` [PATCH 03/18] xen/arm: Save GIC and virtual timer context when the domain suspends Mirela Simonovic
2018-11-12 15:36   ` Julien Grall
2018-11-12 16:52     ` Mirela Simonovic
2018-11-12 17:00       ` Julien Grall
2018-11-12 17:42         ` Mirela Simonovic
2018-11-12 19:20           ` Julien Grall
2018-11-13 20:44             ` Stefano Stabellini
2018-11-14 10:48               ` Julien Grall
2018-11-14 22:45                 ` Stefano Stabellini
2018-11-14 23:39                   ` Julien Grall
2018-11-15  0:05                     ` Julien Grall
2018-11-15  0:35                     ` Stefano Stabellini
2018-11-15 20:29                       ` Julien Grall
2018-11-12 11:30 ` [PATCH 04/18] xen/arm: While a domain is suspended put its watchdogs on pause Mirela Simonovic
2018-11-12 11:47   ` Jan Beulich
2018-11-12 15:17     ` Mirela Simonovic
2018-11-12 15:23       ` Jan Beulich
2018-11-12 15:31         ` Mirela Simonovic
2018-11-12 15:33           ` Andrew Cooper
2018-11-16 22:40             ` Dario Faggioli
2018-11-12 11:30 ` [PATCH 05/18] xen/arm: Trigger Xen suspend when Dom0 completes suspend Mirela Simonovic
2018-11-12 15:45   ` Julien Grall
2018-11-12 23:46     ` Stefano Stabellini
2018-11-13  9:43       ` Julien Grall
2018-11-13 11:26         ` Mirela Simonovic
2018-11-13 11:42           ` Julien Grall
2018-11-14 15:07   ` Julien Grall
2018-11-14 15:40     ` Mirela Simonovic
2018-11-14 17:10       ` Julien Grall
2018-11-14 17:35         ` Mirela Simonovic
2018-11-14 18:48           ` Julien Grall
2018-11-15 12:37             ` Mirela Simonovic
2018-11-15 18:23   ` Julien Grall
2018-11-15 19:17     ` Mirela Simonovic
2018-11-15 20:47       ` Julien Grall
2018-11-12 11:30 ` [PATCH 06/18] xen/x86: Move freeze/thaw_domains into common files Mirela Simonovic
2018-11-13 22:37   ` Stefano Stabellini
2018-11-12 11:30 ` [PATCH 07/18] xen/arm: Freeze domains on suspend and thaw them on resume Mirela Simonovic
2018-11-12 11:30 ` [PATCH 08/18] xen/arm: Disable/enable non-boot physical CPUs on suspend/resume Mirela Simonovic
2018-11-13 22:35   ` Stefano Stabellini
2018-11-14 12:07     ` Julien Grall
2018-11-14 10:52   ` Julien Grall
2018-11-14 13:00     ` Mirela Simonovic
2018-11-14 13:18       ` Julien Grall
2018-11-14 23:04         ` Stefano Stabellini
2018-11-14 23:45           ` Julien Grall
2018-11-15 18:57             ` Stefano Stabellini
2018-11-15 21:09               ` Julien Grall
2018-11-12 11:30 ` [PATCH 09/18] xen/arm: Add rcu_barrier() before enabling non-boot CPUs on resume Mirela Simonovic
2018-11-13 22:42   ` Stefano Stabellini
2018-11-14 12:11   ` Julien Grall
2018-11-14 22:29     ` Stefano Stabellini
2018-11-12 11:30 ` [PATCH 10/18] xen/arm: Implement GIC suspend/resume functions (gicv2 only) Mirela Simonovic
2018-11-13 23:41   ` Stefano Stabellini
2018-11-14  9:13     ` Julien Grall
2018-11-14 11:57       ` Mirela Simonovic
2018-11-14 12:41   ` Julien Grall [this message]
2018-11-14 12:52     ` Mirela Simonovic
2018-11-14 13:24       ` Julien Grall
2018-11-14 22:18         ` Stefano Stabellini
2018-11-14 23:50           ` Julien Grall
2018-11-15 18:26             ` Stefano Stabellini
2018-11-12 11:30 ` [PATCH 11/18] xen/arm: Suspend/resume GIC on system suspend/resume Mirela Simonovic
2018-11-12 11:30 ` [PATCH 12/18] xen/arm: Suspend/resume timer interrupt generation Mirela Simonovic
2018-11-12 11:30 ` [PATCH 13/18] xen/arm: Implement PSCI SYSTEM_SUSPEND call (physical interface) Mirela Simonovic
2018-11-14  0:14   ` Stefano Stabellini
2018-11-14 12:03     ` Mirela Simonovic
2018-11-15 21:20   ` Julien Grall
2018-11-12 11:30 ` [PATCH 14/18] xen/arm: Convert setting MMU page tables code into a routine Mirela Simonovic
2018-11-12 11:30 ` [PATCH 15/18] xen/arm: Resume memory management on Xen resume Mirela Simonovic
2018-11-12 16:17   ` Julien Grall
2018-11-13  1:36     ` Stefano Stabellini
2018-11-13  9:59       ` Julien Grall
2018-11-13 21:35         ` Stefano Stabellini
2018-11-13 22:24           ` Julien Grall
2018-11-12 11:30 ` [PATCH 16/18] xen/arm: Save/restore context on suspend/resume Mirela Simonovic
2018-11-12 11:30 ` [PATCH 17/18] xen/arm: Resume Dom0 after Xen resumes Mirela Simonovic
2018-11-15 20:31   ` Julien Grall
2018-11-16 10:33     ` Mirela Simonovic
2018-11-16 11:29       ` Mirela Simonovic
2018-11-16 11:44         ` Julien Grall
2018-11-16 12:34           ` Mirela Simonovic
2018-11-16 13:24             ` Julien Grall
2018-11-16 19:03               ` Stefano Stabellini
2018-11-16 19:09                 ` Stefano Stabellini
2018-11-16 21:41                   ` Mirela Simonovic
2018-11-16 21:58                     ` Julien Grall
2018-11-16 23:01                       ` Dario Faggioli
2018-11-16 23:06                         ` Stefano Stabellini
2018-11-17 16:01                           ` Mirela Simonovic
2018-11-17 16:02                             ` Mirela Simonovic
2018-11-17 16:19                               ` Mirela Simonovic
2018-11-27 18:36                             ` Julien Grall
2018-11-29 14:02                               ` Mirela Simonovic
2018-12-04 18:15                                 ` Julien Grall
2018-11-12 11:30 ` [PATCH 18/18] xen/arm: Suspend/resume console on Xen suspend/resume Mirela Simonovic
2018-11-27 18:46   ` Julien Grall
2018-11-12 11:37 ` [PATCH 00/18] xen/arm64: Suspend to RAM support for Xen Mirela Simonovic
2018-11-12 11:49 ` Julien Grall
2018-11-12 12:01   ` Mirela Simonovic
2018-11-12 12:08     ` Julien Grall
2018-11-12 12:35       ` Mirela Simonovic
2018-11-13  2:22   ` Stefano Stabellini
2018-11-13 10:02     ` Julien Grall
2018-11-13 18:06       ` Stefano Stabellini

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=746b0ad8-4ce1-dfaf-eadc-939657426900@arm.com \
    --to=julien.grall@arm.com \
    --cc=dm@aggios.com \
    --cc=mirela.simonovic@aggios.com \
    --cc=saeed.nowshadi@xilinx.com \
    --cc=sstabellini@kernel.org \
    --cc=stefano.stabellini@xilinx.com \
    --cc=xen-devel@lists.xen.org \
    --cc=xen-devel@lists.xenproject.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.