From: "Roedel, Joerg" <Joerg.Roedel@amd.com>
To: Ohad Ben-Cohen <ohad@wizery.com>
Cc: "iommu@lists.linux-foundation.org"
<iommu@lists.linux-foundation.org>,
"linux-omap@vger.kernel.org" <linux-omap@vger.kernel.org>,
Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
David Woodhouse <dwmw2@infradead.org>,
"linux-arm-kernel@lists.infradead.org"
<linux-arm-kernel@lists.infradead.org>,
David Brown <davidb@codeaurora.org>,
Arnd Bergmann <arnd@arndb.de>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
Stepan Moskovchenko <stepanm@codeaurora.org>,
"kvm@vger.kernel.org" <kvm@vger.kernel.org>
Subject: Re: [PATCH v3 1/6] iommu/core: split mapping to page sizes as supported by the hardware
Date: Mon, 10 Oct 2011 11:47:38 +0200 [thread overview]
Message-ID: <20111010094738.GW2138@amd.com> (raw)
In-Reply-To: <CAK=WgbbNs5fWHDEputZ=jzU5YN0sn0tf6AETkcsoq_Jbvc2XgQ@mail.gmail.com>
Hi Ohad,
sorry, I was on vacation last week and had no time to look into this.
On Sun, Oct 02, 2011 at 11:58:12AM -0400, Ohad Ben-Cohen wrote:
> drivers/iommu/iommu.c | 138 ++++++++++++++++++++++++++++++++++++++++---
> drivers/iommu/omap-iovmm.c | 12 +---
> include/linux/iommu.h | 6 +-
> virt/kvm/iommu.c | 4 +-
> 4 files changed, 137 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index a7b0862..f23563f 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -16,6 +16,8 @@
> * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> */
>
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> #include <linux/kernel.h>
> #include <linux/bug.h>
> #include <linux/types.h>
> @@ -23,15 +25,54 @@
> #include <linux/slab.h>
> #include <linux/errno.h>
> #include <linux/iommu.h>
> +#include <linux/bitmap.h>
Is this still required?
>
> static struct iommu_ops *iommu_ops;
>
> +/* bitmap of supported page sizes */
> +static unsigned long iommu_pgsize_bitmap;
> +
> +/* size of the smallest supported page (in bytes) */
> +static unsigned int iommu_min_pagesz;
> +
> +/**
> + * register_iommu() - register an IOMMU hardware
> + * @ops: iommu handlers
> + * @pgsize_bitmap: bitmap of page sizes supported by the hardware
> + *
> + * Note: this is a temporary function, which will be removed once
> + * all IOMMU drivers are converted. The only reason it exists is to
> + * allow splitting the pgsizes changes to several patches in order to ease
> + * the review.
> + */
> +void register_iommu_pgsize(struct iommu_ops *ops, unsigned long pgsize_bitmap)
> +{
> + if (iommu_ops || iommu_pgsize_bitmap || !pgsize_bitmap)
> + BUG();
> +
> + iommu_ops = ops;
> + iommu_pgsize_bitmap = pgsize_bitmap;
> +
> + /* find out the minimum page size only once */
> + iommu_min_pagesz = 1 << __ffs(pgsize_bitmap);
> +}
Hmm, I thought a little bit about that and came to the conculusion it
might be best to just keep the page-sizes as a part of the iommu_ops
structure. So there is no need to extend the register_iommu interface.
Also, the bus_set_iommu interface is now in the -next branch. Would be
good if you rebase the patches to that interface.
You can find the current iommu tree with these changes at
git://git.8bytes.org/scm/iommu.git
> @@ -115,26 +156,103 @@ int iommu_domain_has_cap(struct iommu_domain *domain,
> EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
>
> int iommu_map(struct iommu_domain *domain, unsigned long iova,
> - phys_addr_t paddr, int gfp_order, int prot)
> + phys_addr_t paddr, size_t size, int prot)
> {
> - size_t size;
> + int ret = 0;
> +
> + /*
> + * both the virtual address and the physical one, as well as
> + * the size of the mapping, must be aligned (at least) to the
> + * size of the smallest page supported by the hardware
> + */
> + if (!IS_ALIGNED(iova | paddr | size, iommu_min_pagesz)) {
> + pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
> + "0x%x\n", iova, (unsigned long)paddr,
> + (unsigned long)size, iommu_min_pagesz);
> + return -EINVAL;
> + }
>
> - size = 0x1000UL << gfp_order;
> + pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
> + (unsigned long)paddr, (unsigned long)size);
>
> - BUG_ON(!IS_ALIGNED(iova | paddr, size));
> + while (size) {
> + unsigned long pgsize, addr_merge = iova | paddr;
> + unsigned int pgsize_idx;
>
> - return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
> + /* Max page size that still fits into 'size' */
> + pgsize_idx = __fls(size);
> +
> + /* need to consider alignment requirements ? */
> + if (likely(addr_merge)) {
> + /* Max page size allowed by both iova and paddr */
> + unsigned int align_pgsize_idx = __ffs(addr_merge);
> +
> + pgsize_idx = min(pgsize_idx, align_pgsize_idx);
> + }
> +
> + /* build a mask of acceptable page sizes */
> + pgsize = (1UL << (pgsize_idx + 1)) - 1;
> +
> + /* throw away page sizes not supported by the hardware */
> + pgsize &= iommu_pgsize_bitmap;
I think we need some care here and check pgsize for 0. A BUG_ON should
do.
> +
> + /* pick the biggest page */
> + pgsize_idx = __fls(pgsize);
> + pgsize = 1UL << pgsize_idx;
> +
> + /* convert index to page order */
> + pgsize_idx -= PAGE_SHIFT;
> +
> + pr_debug("mapping: iova 0x%lx pa 0x%lx order %u\n", iova,
> + (unsigned long)paddr, pgsize_idx);
> +
> + ret = iommu_ops->map(domain, iova, paddr, pgsize_idx, prot);
> + if (ret)
> + break;
> +
> + iova += pgsize;
> + paddr += pgsize;
> + size -= pgsize;
> + }
> +
> + return ret;
> }
> EXPORT_SYMBOL_GPL(iommu_map);
Otherwise this looks much better, thanks.
>
> -int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
> +int iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
> {
> - size_t size;
> + int order, unmapped_size, unmapped_order, total_unmapped = 0;
> +
> + /*
> + * The virtual address, as well as the size of the mapping, must be
> + * aligned (at least) to the size of the smallest page supported
> + * by the hardware
> + */
> + if (!IS_ALIGNED(iova | size, iommu_min_pagesz)) {
> + pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
> + iova, (unsigned long)size, iommu_min_pagesz);
> + return -EINVAL;
> + }
> +
> + pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
> + (unsigned long)size);
> +
> + while (size > total_unmapped) {
> + order = get_order(size - total_unmapped);
> +
> + unmapped_order = iommu_ops->unmap(domain, iova, order);
I think we should make sure that we call iommu_ops->unmap with the same
parameters as iommu_ops->map. Otherwise we still need some page-size
complexity in the iommu-drivers.
> + if (unmapped_order < 0)
> + return unmapped_order;
> +
> + pr_debug("unmapped: iova 0x%lx order %d\n", iova,
> + unmapped_order);
>
> - size = 0x1000UL << gfp_order;
> + unmapped_size = 0x1000UL << unmapped_order;
>
> - BUG_ON(!IS_ALIGNED(iova, size));
> + iova += unmapped_size;
> + total_unmapped += unmapped_size;
> + }
>
> - return iommu_ops->unmap(domain, iova, gfp_order);
> + return get_order(total_unmapped);
> }
When we pass the size now it makes sense to also return the
unmapped-size instead of the order.
Also the semantics of iommu_unmap need some more discussion I think. We
should require that iommu_map/iommu_unmap are called with the same
parameters for the same range. Otherwise we can give no guarantees what
a unmap-call will unmap.
Joerg
--
AMD Operating System Research Center
Advanced Micro Devices GmbH Einsteinring 24 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Landkr. Muenchen; Registerger. Muenchen, HRB Nr. 43632
WARNING: multiple messages have this Message-ID (diff)
From: Joerg.Roedel@amd.com (Roedel, Joerg)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 1/6] iommu/core: split mapping to page sizes as supported by the hardware
Date: Mon, 10 Oct 2011 11:47:38 +0200 [thread overview]
Message-ID: <20111010094738.GW2138@amd.com> (raw)
In-Reply-To: <CAK=WgbbNs5fWHDEputZ=jzU5YN0sn0tf6AETkcsoq_Jbvc2XgQ@mail.gmail.com>
Hi Ohad,
sorry, I was on vacation last week and had no time to look into this.
On Sun, Oct 02, 2011 at 11:58:12AM -0400, Ohad Ben-Cohen wrote:
> drivers/iommu/iommu.c | 138 ++++++++++++++++++++++++++++++++++++++++---
> drivers/iommu/omap-iovmm.c | 12 +---
> include/linux/iommu.h | 6 +-
> virt/kvm/iommu.c | 4 +-
> 4 files changed, 137 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index a7b0862..f23563f 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -16,6 +16,8 @@
> * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> */
>
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> #include <linux/kernel.h>
> #include <linux/bug.h>
> #include <linux/types.h>
> @@ -23,15 +25,54 @@
> #include <linux/slab.h>
> #include <linux/errno.h>
> #include <linux/iommu.h>
> +#include <linux/bitmap.h>
Is this still required?
>
> static struct iommu_ops *iommu_ops;
>
> +/* bitmap of supported page sizes */
> +static unsigned long iommu_pgsize_bitmap;
> +
> +/* size of the smallest supported page (in bytes) */
> +static unsigned int iommu_min_pagesz;
> +
> +/**
> + * register_iommu() - register an IOMMU hardware
> + * @ops: iommu handlers
> + * @pgsize_bitmap: bitmap of page sizes supported by the hardware
> + *
> + * Note: this is a temporary function, which will be removed once
> + * all IOMMU drivers are converted. The only reason it exists is to
> + * allow splitting the pgsizes changes to several patches in order to ease
> + * the review.
> + */
> +void register_iommu_pgsize(struct iommu_ops *ops, unsigned long pgsize_bitmap)
> +{
> + if (iommu_ops || iommu_pgsize_bitmap || !pgsize_bitmap)
> + BUG();
> +
> + iommu_ops = ops;
> + iommu_pgsize_bitmap = pgsize_bitmap;
> +
> + /* find out the minimum page size only once */
> + iommu_min_pagesz = 1 << __ffs(pgsize_bitmap);
> +}
Hmm, I thought a little bit about that and came to the conculusion it
might be best to just keep the page-sizes as a part of the iommu_ops
structure. So there is no need to extend the register_iommu interface.
Also, the bus_set_iommu interface is now in the -next branch. Would be
good if you rebase the patches to that interface.
You can find the current iommu tree with these changes at
git://git.8bytes.org/scm/iommu.git
> @@ -115,26 +156,103 @@ int iommu_domain_has_cap(struct iommu_domain *domain,
> EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
>
> int iommu_map(struct iommu_domain *domain, unsigned long iova,
> - phys_addr_t paddr, int gfp_order, int prot)
> + phys_addr_t paddr, size_t size, int prot)
> {
> - size_t size;
> + int ret = 0;
> +
> + /*
> + * both the virtual address and the physical one, as well as
> + * the size of the mapping, must be aligned (at least) to the
> + * size of the smallest page supported by the hardware
> + */
> + if (!IS_ALIGNED(iova | paddr | size, iommu_min_pagesz)) {
> + pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
> + "0x%x\n", iova, (unsigned long)paddr,
> + (unsigned long)size, iommu_min_pagesz);
> + return -EINVAL;
> + }
>
> - size = 0x1000UL << gfp_order;
> + pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
> + (unsigned long)paddr, (unsigned long)size);
>
> - BUG_ON(!IS_ALIGNED(iova | paddr, size));
> + while (size) {
> + unsigned long pgsize, addr_merge = iova | paddr;
> + unsigned int pgsize_idx;
>
> - return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
> + /* Max page size that still fits into 'size' */
> + pgsize_idx = __fls(size);
> +
> + /* need to consider alignment requirements ? */
> + if (likely(addr_merge)) {
> + /* Max page size allowed by both iova and paddr */
> + unsigned int align_pgsize_idx = __ffs(addr_merge);
> +
> + pgsize_idx = min(pgsize_idx, align_pgsize_idx);
> + }
> +
> + /* build a mask of acceptable page sizes */
> + pgsize = (1UL << (pgsize_idx + 1)) - 1;
> +
> + /* throw away page sizes not supported by the hardware */
> + pgsize &= iommu_pgsize_bitmap;
I think we need some care here and check pgsize for 0. A BUG_ON should
do.
> +
> + /* pick the biggest page */
> + pgsize_idx = __fls(pgsize);
> + pgsize = 1UL << pgsize_idx;
> +
> + /* convert index to page order */
> + pgsize_idx -= PAGE_SHIFT;
> +
> + pr_debug("mapping: iova 0x%lx pa 0x%lx order %u\n", iova,
> + (unsigned long)paddr, pgsize_idx);
> +
> + ret = iommu_ops->map(domain, iova, paddr, pgsize_idx, prot);
> + if (ret)
> + break;
> +
> + iova += pgsize;
> + paddr += pgsize;
> + size -= pgsize;
> + }
> +
> + return ret;
> }
> EXPORT_SYMBOL_GPL(iommu_map);
Otherwise this looks much better, thanks.
>
> -int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
> +int iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
> {
> - size_t size;
> + int order, unmapped_size, unmapped_order, total_unmapped = 0;
> +
> + /*
> + * The virtual address, as well as the size of the mapping, must be
> + * aligned (at least) to the size of the smallest page supported
> + * by the hardware
> + */
> + if (!IS_ALIGNED(iova | size, iommu_min_pagesz)) {
> + pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
> + iova, (unsigned long)size, iommu_min_pagesz);
> + return -EINVAL;
> + }
> +
> + pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
> + (unsigned long)size);
> +
> + while (size > total_unmapped) {
> + order = get_order(size - total_unmapped);
> +
> + unmapped_order = iommu_ops->unmap(domain, iova, order);
I think we should make sure that we call iommu_ops->unmap with the same
parameters as iommu_ops->map. Otherwise we still need some page-size
complexity in the iommu-drivers.
> + if (unmapped_order < 0)
> + return unmapped_order;
> +
> + pr_debug("unmapped: iova 0x%lx order %d\n", iova,
> + unmapped_order);
>
> - size = 0x1000UL << gfp_order;
> + unmapped_size = 0x1000UL << unmapped_order;
>
> - BUG_ON(!IS_ALIGNED(iova, size));
> + iova += unmapped_size;
> + total_unmapped += unmapped_size;
> + }
>
> - return iommu_ops->unmap(domain, iova, gfp_order);
> + return get_order(total_unmapped);
> }
When we pass the size now it makes sense to also return the
unmapped-size instead of the order.
Also the semantics of iommu_unmap need some more discussion I think. We
should require that iommu_map/iommu_unmap are called with the same
parameters for the same range. Otherwise we can give no guarantees what
a unmap-call will unmap.
Joerg
--
AMD Operating System Research Center
Advanced Micro Devices GmbH Einsteinring 24 85609 Dornach
General Managers: Alberto Bozzo, Andrew Bowd
Registration: Dornach, Landkr. Muenchen; Registerger. Muenchen, HRB Nr. 43632
next prev parent reply other threads:[~2011-10-10 9:49 UTC|newest]
Thread overview: 88+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-16 17:51 [PATCH v3 0/6] iommu: split mapping to page sizes as supported by the hardware Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` [PATCH v3 1/6] iommu/core: " Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-27 10:05 ` Roedel, Joerg
2011-09-27 10:05 ` Roedel, Joerg
2011-09-27 12:26 ` Ohad Ben-Cohen
2011-09-27 12:26 ` Ohad Ben-Cohen
2011-09-27 13:12 ` Roedel, Joerg
2011-09-27 13:12 ` Roedel, Joerg
2011-09-27 13:28 ` Ohad Ben-Cohen
2011-09-27 13:28 ` Ohad Ben-Cohen
2011-09-27 18:14 ` Roedel, Joerg
2011-09-27 18:14 ` Roedel, Joerg
2011-10-02 15:58 ` Ohad Ben-Cohen
2011-10-02 15:58 ` Ohad Ben-Cohen
2011-10-02 15:58 ` Ohad Ben-Cohen
2011-10-10 7:40 ` Ohad Ben-Cohen
2011-10-10 7:40 ` Ohad Ben-Cohen
2011-10-10 7:40 ` Ohad Ben-Cohen
2011-10-10 9:47 ` Roedel, Joerg [this message]
2011-10-10 9:47 ` Roedel, Joerg
2011-10-10 13:59 ` Ohad Ben-Cohen
2011-10-10 13:59 ` Ohad Ben-Cohen
2011-10-10 15:36 ` Roedel, Joerg
2011-10-10 15:36 ` Roedel, Joerg
2011-10-10 15:36 ` Roedel, Joerg
2011-10-10 17:02 ` Ohad Ben-Cohen
2011-10-10 17:02 ` Ohad Ben-Cohen
2011-10-10 22:01 ` Ohad Ben-Cohen
2011-10-10 22:01 ` Ohad Ben-Cohen
2011-10-11 10:21 ` Roedel, Joerg
2011-10-11 10:21 ` Roedel, Joerg
2011-10-11 10:21 ` Roedel, Joerg
2011-10-11 10:19 ` Roedel, Joerg
2011-10-11 10:19 ` Roedel, Joerg
2011-10-10 22:49 ` Ohad Ben-Cohen
2011-10-10 22:49 ` Ohad Ben-Cohen
2011-10-11 10:38 ` Roedel, Joerg
2011-10-11 10:38 ` Roedel, Joerg
2011-10-11 17:01 ` Ohad Ben-Cohen
2011-10-11 17:01 ` Ohad Ben-Cohen
2011-10-14 13:35 ` Roedel, Joerg
2011-10-14 13:35 ` Roedel, Joerg
2011-10-14 17:03 ` Ohad Ben-Cohen
2011-10-14 17:03 ` Ohad Ben-Cohen
2011-10-14 17:03 ` Ohad Ben-Cohen
2011-10-14 17:05 ` Ohad Ben-Cohen
2011-10-14 17:05 ` Ohad Ben-Cohen
2011-10-14 17:05 ` Ohad Ben-Cohen
2011-10-17 8:05 ` Ohad Ben-Cohen
2011-10-17 8:05 ` Ohad Ben-Cohen
2011-10-17 9:28 ` Roedel, Joerg
2011-10-17 9:28 ` Roedel, Joerg
2011-10-17 9:28 ` Roedel, Joerg
2011-10-17 9:55 ` Ohad Ben-Cohen
2011-10-17 9:55 ` Ohad Ben-Cohen
2011-10-11 14:59 ` KyongHo Cho
2011-10-11 14:59 ` KyongHo Cho
2011-10-11 14:59 ` KyongHo Cho
2011-10-11 17:04 ` Ohad Ben-Cohen
2011-10-11 17:04 ` Ohad Ben-Cohen
2011-10-11 17:04 ` Ohad Ben-Cohen
2011-10-10 12:52 ` KyongHo Cho
2011-10-10 12:52 ` KyongHo Cho
2011-10-10 14:00 ` Ohad Ben-Cohen
2011-10-10 14:00 ` Ohad Ben-Cohen
2011-09-16 17:51 ` [PATCH v3 2/6] iommu/omap: announce supported page sizes Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` [PATCH v3 3/6] iommu/msm: " Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-25 5:03 ` David Brown
2011-09-25 5:03 ` David Brown
2011-09-16 17:51 ` [PATCH v3 4/6] iommu/amd: " Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` [PATCH v3 5/6] iommu/intel: " Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` [PATCH v3 6/6] iommu/core: remove the temporary register_iommu_pgsize API Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-16 17:51 ` Ohad Ben-Cohen
2011-09-27 8:56 ` [PATCH v3 0/6] iommu: split mapping to page sizes as supported by the hardware Ohad Ben-Cohen
2011-09-27 8:56 ` Ohad Ben-Cohen
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=20111010094738.GW2138@amd.com \
--to=joerg.roedel@amd.com \
--cc=Hiroshi.DOYU@nokia.com \
--cc=arnd@arndb.de \
--cc=davidb@codeaurora.org \
--cc=dwmw2@infradead.org \
--cc=iommu@lists.linux-foundation.org \
--cc=kvm@vger.kernel.org \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-omap@vger.kernel.org \
--cc=ohad@wizery.com \
--cc=stepanm@codeaurora.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.