All of lore.kernel.org
 help / color / mirror / Atom feed
From: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
To: Nipun Gupta <nipun.gupta-3arQi8VN3Tc@public.gmane.org>
Cc: "devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org"
	<devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	"punit.agrawal-5wv7dgnIgG8@public.gmane.org"
	<punit.agrawal-5wv7dgnIgG8@public.gmane.org>,
	"will.deacon-5wv7dgnIgG8@public.gmane.org"
	<will.deacon-5wv7dgnIgG8@public.gmane.org>,
	"iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org"
	<iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>,
	"thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org"
	<thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>,
	"linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org"
	<linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org>
Subject: Re: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
Date: Wed, 5 Oct 2016 10:55:46 +0100	[thread overview]
Message-ID: <6ec9519b-01df-3be8-2967-7556bd306909@arm.com> (raw)
In-Reply-To: <DB6PR0402MB2694B2E5AE266F138784D2C2E6C40-2mNvjAGDOPn2WJ5A9zev/o3W/0Ik+aLCnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>

On 05/10/16 08:00, Nipun Gupta wrote:
> 
> 
>> -----Original Message-----
>> From: iommu-bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org [mailto:iommu-
>> bounces-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org] On Behalf Of Robin Murphy
>> Sent: Monday, September 12, 2016 21:44
>> To: will.deacon-5wv7dgnIgG8@public.gmane.org; joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org; iommu-cunTk1MwBs/ROKNJybVBZg@public.gmane.org
>> foundation.org; linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
>> Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; punit.agrawal-5wv7dgnIgG8@public.gmane.org;
>> thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org
>> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
>>
>> When an MSI doorbell is located downstream of an IOMMU, attaching devices
>> to a DMA ops domain and switching on translation leads to a rude shock when
>> their attempt to write to the physical address returned by the irqchip driver
>> faults (or worse, writes into some already-mapped
>> buffer) and no interrupt is forthcoming.
>>
>> Address this by adding a hook for relevant irqchip drivers to call from their
>> compose_msi_msg() callback, to swizzle the physical address with an
>> appropriatly-mapped IOVA for any device attached to one of our DMA ops
>> domains.
>>
>> Acked-by: Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>
>> Acked-by: Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org>
>> Signed-off-by: Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org>
>> ---
>>  drivers/iommu/dma-iommu.c        | 136
>> ++++++++++++++++++++++++++++++++++-----
>>  drivers/irqchip/irq-gic-v2m.c    |   3 +
>>  drivers/irqchip/irq-gic-v3-its.c |   3 +
>>  include/linux/dma-iommu.h        |   9 +++
>>  4 files changed, 136 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index
>> 00c8a08d56e7..4329d18080cf 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -25,10 +25,28 @@
>>  #include <linux/huge_mm.h>
>>  #include <linux/iommu.h>
>>  #include <linux/iova.h>
>> +#include <linux/irq.h>
>>  #include <linux/mm.h>
>>  #include <linux/scatterlist.h>
>>  #include <linux/vmalloc.h>
>>
>> +struct iommu_dma_msi_page {
>> +	struct list_head	list;
>> +	dma_addr_t		iova;
>> +	phys_addr_t		phys;
>> +};
>> +
>> +struct iommu_dma_cookie {
>> +	struct iova_domain	iovad;
>> +	struct list_head	msi_page_list;
>> +	spinlock_t		msi_lock;
>> +};
>> +
>> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
>> +*domain) {
>> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
>> +
>>  int iommu_dma_init(void)
>>  {
>>  	return iova_cache_get();
>> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
>>   */
>>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad;
>> +	struct iommu_dma_cookie *cookie;
>>
>>  	if (domain->iova_cookie)
>>  		return -EEXIST;
>>
>> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
>> -	domain->iova_cookie = iovad;
>> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> +	if (!cookie)
>> +		return -ENOMEM;
>>
>> -	return iovad ? 0 : -ENOMEM;
>> +	spin_lock_init(&cookie->msi_lock);
>> +	INIT_LIST_HEAD(&cookie->msi_page_list);
>> +	domain->iova_cookie = cookie;
>> +	return 0;
>>  }
>>  EXPORT_SYMBOL(iommu_get_dma_cookie);
>>
>> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
>>   */
>>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi, *tmp;
>>
>> -	if (!iovad)
>> +	if (!cookie)
>>  		return;
>>
>> -	if (iovad->granule)
>> -		put_iova_domain(iovad);
>> -	kfree(iovad);
>> +	if (cookie->iovad.granule)
>> +		put_iova_domain(&cookie->iovad);
>> +
>> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
>> +		list_del(&msi->list);
>> +		kfree(msi);
>> +	}
>> +	kfree(cookie);
>>  	domain->iova_cookie = NULL;
>>  }
>>  EXPORT_SYMBOL(iommu_put_dma_cookie);
>> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
>>   */
>>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long order, base_pfn, end_pfn;
>>
>>  	if (!iovad)
>> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction
>> dir, bool coherent)  static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  		dma_addr_t dma_limit)
>>  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long length = iova_align(iovad, size) >> shift;
>>
>> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  /* The IOVA allocator knows what we mapped, so just unmap whatever that
>> was */  static void __iommu_dma_unmap(struct iommu_domain *domain,
>> dma_addr_t dma_addr)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long pfn = dma_addr >> shift;
>>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@ struct
>> page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
>>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct page **pages;
>>  	struct sg_table sgt;
>> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
>> *dev, struct page *page,  {
>>  	dma_addr_t dma_addr;
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	phys_addr_t phys = page_to_phys(page) + offset;
>>  	size_t iova_off = iova_offset(iovad, phys);
>>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7 @@
>> int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
>>  		int nents, int prot)
>>  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct scatterlist *s, *prev = NULL;
>>  	dma_addr_t dma_addr;
>> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
>> dma_addr_t dma_addr)  {
>>  	return dma_addr == DMA_ERROR_CODE;
>>  }
>> +
>> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device
>> *dev,
>> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	struct iova_domain *iovad = &cookie->iovad;
>> +	struct iova *iova;
>> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
>> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
>> +		if (msi_page->phys == msi_addr)
>> +			return msi_page;
>> +
>> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
>> +	if (!msi_page)
>> +		return NULL;
>> +
>> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
> 
> I think this should be 'iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));'

Er, yes... I fully agree. That's why it is exactly that.

> as __alloc_iova takes input parameter as 'struct iova_domain *'

Joking aside, though, I guess you've overlooked the change introduced by
c987ff0d3cb3 ("iommu/dma: Respect IOMMU aperture when allocating")?

Robin.

> 
> Regards,
> Nipun
> 
>> +	if (!iova)
>> +		goto out_free_page;
>> +
>> +	msi_page->phys = msi_addr;
>> +	msi_page->iova = iova_dma_addr(iovad, iova);
>> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
>> prot))
>> +		goto out_free_iova;
>> +
>> +	INIT_LIST_HEAD(&msi_page->list);
>> +	list_add(&msi_page->list, &cookie->msi_page_list);
>> +	return msi_page;
>> +
>> +out_free_iova:
>> +	__free_iova(iovad, iova);
>> +out_free_page:
>> +	kfree(msi_page);
>> +	return NULL;
>> +}
>> +
>> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
>> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
>> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> +	struct iommu_dma_cookie *cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
>> +	unsigned long flags;
>> +
>> +	if (!domain || !domain->iova_cookie)
>> +		return;
>> +
>> +	cookie = domain->iova_cookie;
>> +
>> +	/*
>> +	 * We disable IRQs to rule out a possible inversion against
>> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
>> +	 * of an MSI from within an IPI handler.
>> +	 */
>> +	spin_lock_irqsave(&cookie->msi_lock, flags);
>> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
>> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
>> +
>> +	if (WARN_ON(!msi_page)) {
>> +		/*
>> +		 * We're called from a void callback, so the best we can do is
>> +		 * 'fail' by filling the message with obviously bogus values.
>> +		 * Since we got this far due to an IOMMU being present, it's
>> +		 * not like the existing address would have worked anyway...
>> +		 */
>> +		msg->address_hi = ~0U;
>> +		msg->address_lo = ~0U;
>> +		msg->data = ~0U;
>> +	} else {
>> +		msg->address_hi = upper_32_bits(msi_page->iova);
>> +		msg->address_lo &= iova_mask(&cookie->iovad);
>> +		msg->address_lo += lower_32_bits(msi_page->iova);
>> +	}
>> +}
>> diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index
>> 35eb7ac5d21f..863e073c6f7f 100644
>> --- a/drivers/irqchip/irq-gic-v2m.c
>> +++ b/drivers/irqchip/irq-gic-v2m.c
>> @@ -16,6 +16,7 @@
>>  #define pr_fmt(fmt) "GICv2m: " fmt
>>
>>  #include <linux/acpi.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/irq.h>
>>  #include <linux/irqdomain.h>
>>  #include <linux/kernel.h>
>> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data
>> *data, struct msi_msg *msg)
>>
>>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
>>  		msg->data -= v2m->spi_offset;
>> +
>> +	iommu_dma_map_msi_msg(data->irq, msg);
>>  }
>>
>>  static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-
>> its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index 36b9c28a5c91..98ff669d5962 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -18,6 +18,7 @@
>>  #include <linux/bitmap.h>
>>  #include <linux/cpu.h>
>>  #include <linux/delay.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/log2.h>
>>  #include <linux/mm.h>
>> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data
>> *d, struct msi_msg *msg)
>>  	msg->address_lo		= addr & ((1UL << 32) - 1);
>>  	msg->address_hi		= addr >> 32;
>>  	msg->data		= its_get_event_id(d);
>> +
>> +	iommu_dma_map_msi_msg(d->irq, msg);
>>  }
>>
>>  static struct irq_chip its_irq_chip = { diff --git a/include/linux/dma-iommu.h
>> b/include/linux/dma-iommu.h index 81c5c8d167ad..5ee806e41b5c 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -21,6 +21,7 @@
>>
>>  #ifdef CONFIG_IOMMU_DMA
>>  #include <linux/iommu.h>
>> +#include <linux/msi.h>
>>
>>  int iommu_dma_init(void);
>>
>> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct
>> scatterlist *sg, int nents,  int iommu_dma_supported(struct device *dev, u64
>> mask);  int iommu_dma_mapping_error(struct device *dev, dma_addr_t
>> dma_addr);
>>
>> +/* The DMA API isn't _quite_ the whole story, though... */ void
>> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
>> +
>>  #else
>>
>>  struct iommu_domain;
>> +struct msi_msg;
>>
>>  static inline int iommu_dma_init(void)
>>  {
>> @@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct
>> iommu_domain *domain)  {  }
>>
>> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
>> +{ }
>> +
>>  #endif	/* CONFIG_IOMMU_DMA */
>>  #endif	/* __KERNEL__ */
>>  #endif	/* __DMA_IOMMU_H */
>> --
>> 2.8.1.dirty
>>
>> _______________________________________________
>> iommu mailing list
>> iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org
>> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 

WARNING: multiple messages have this Message-ID (diff)
From: robin.murphy@arm.com (Robin Murphy)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
Date: Wed, 5 Oct 2016 10:55:46 +0100	[thread overview]
Message-ID: <6ec9519b-01df-3be8-2967-7556bd306909@arm.com> (raw)
In-Reply-To: <DB6PR0402MB2694B2E5AE266F138784D2C2E6C40@DB6PR0402MB2694.eurprd04.prod.outlook.com>

On 05/10/16 08:00, Nipun Gupta wrote:
> 
> 
>> -----Original Message-----
>> From: iommu-bounces at lists.linux-foundation.org [mailto:iommu-
>> bounces at lists.linux-foundation.org] On Behalf Of Robin Murphy
>> Sent: Monday, September 12, 2016 21:44
>> To: will.deacon at arm.com; joro at 8bytes.org; iommu at lists.linux-
>> foundation.org; linux-arm-kernel at lists.infradead.org
>> Cc: devicetree at vger.kernel.org; punit.agrawal at arm.com;
>> thunder.leizhen at huawei.com
>> Subject: [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs
>>
>> When an MSI doorbell is located downstream of an IOMMU, attaching devices
>> to a DMA ops domain and switching on translation leads to a rude shock when
>> their attempt to write to the physical address returned by the irqchip driver
>> faults (or worse, writes into some already-mapped
>> buffer) and no interrupt is forthcoming.
>>
>> Address this by adding a hook for relevant irqchip drivers to call from their
>> compose_msi_msg() callback, to swizzle the physical address with an
>> appropriatly-mapped IOVA for any device attached to one of our DMA ops
>> domains.
>>
>> Acked-by: Thomas Gleixner <tglx@linutronix.de>
>> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
>> Signed-off-by: Robin Murphy <robin.murphy@arm.com>
>> ---
>>  drivers/iommu/dma-iommu.c        | 136
>> ++++++++++++++++++++++++++++++++++-----
>>  drivers/irqchip/irq-gic-v2m.c    |   3 +
>>  drivers/irqchip/irq-gic-v3-its.c |   3 +
>>  include/linux/dma-iommu.h        |   9 +++
>>  4 files changed, 136 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index
>> 00c8a08d56e7..4329d18080cf 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -25,10 +25,28 @@
>>  #include <linux/huge_mm.h>
>>  #include <linux/iommu.h>
>>  #include <linux/iova.h>
>> +#include <linux/irq.h>
>>  #include <linux/mm.h>
>>  #include <linux/scatterlist.h>
>>  #include <linux/vmalloc.h>
>>
>> +struct iommu_dma_msi_page {
>> +	struct list_head	list;
>> +	dma_addr_t		iova;
>> +	phys_addr_t		phys;
>> +};
>> +
>> +struct iommu_dma_cookie {
>> +	struct iova_domain	iovad;
>> +	struct list_head	msi_page_list;
>> +	spinlock_t		msi_lock;
>> +};
>> +
>> +static inline struct iova_domain *cookie_iovad(struct iommu_domain
>> +*domain) {
>> +	return &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad; }
>> +
>>  int iommu_dma_init(void)
>>  {
>>  	return iova_cache_get();
>> @@ -43,15 +61,19 @@ int iommu_dma_init(void)
>>   */
>>  int iommu_get_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad;
>> +	struct iommu_dma_cookie *cookie;
>>
>>  	if (domain->iova_cookie)
>>  		return -EEXIST;
>>
>> -	iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
>> -	domain->iova_cookie = iovad;
>> +	cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
>> +	if (!cookie)
>> +		return -ENOMEM;
>>
>> -	return iovad ? 0 : -ENOMEM;
>> +	spin_lock_init(&cookie->msi_lock);
>> +	INIT_LIST_HEAD(&cookie->msi_page_list);
>> +	domain->iova_cookie = cookie;
>> +	return 0;
>>  }
>>  EXPORT_SYMBOL(iommu_get_dma_cookie);
>>
>> @@ -63,14 +85,20 @@ EXPORT_SYMBOL(iommu_get_dma_cookie);
>>   */
>>  void iommu_put_dma_cookie(struct iommu_domain *domain)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi, *tmp;
>>
>> -	if (!iovad)
>> +	if (!cookie)
>>  		return;
>>
>> -	if (iovad->granule)
>> -		put_iova_domain(iovad);
>> -	kfree(iovad);
>> +	if (cookie->iovad.granule)
>> +		put_iova_domain(&cookie->iovad);
>> +
>> +	list_for_each_entry_safe(msi, tmp, &cookie->msi_page_list, list) {
>> +		list_del(&msi->list);
>> +		kfree(msi);
>> +	}
>> +	kfree(cookie);
>>  	domain->iova_cookie = NULL;
>>  }
>>  EXPORT_SYMBOL(iommu_put_dma_cookie);
>> @@ -88,7 +116,7 @@ EXPORT_SYMBOL(iommu_put_dma_cookie);
>>   */
>>  int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t
>> base, u64 size)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long order, base_pfn, end_pfn;
>>
>>  	if (!iovad)
>> @@ -155,7 +183,7 @@ int dma_direction_to_prot(enum dma_data_direction
>> dir, bool coherent)  static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  		dma_addr_t dma_limit)
>>  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long length = iova_align(iovad, size) >> shift;
>>
>> @@ -171,7 +199,7 @@ static struct iova *__alloc_iova(struct iommu_domain
>> *domain, size_t size,
>>  /* The IOVA allocator knows what we mapped, so just unmap whatever that
>> was */  static void __iommu_dma_unmap(struct iommu_domain *domain,
>> dma_addr_t dma_addr)  {
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	unsigned long shift = iova_shift(iovad);
>>  	unsigned long pfn = dma_addr >> shift;
>>  	struct iova *iova = find_iova(iovad, pfn); @@ -294,7 +322,7 @@ struct
>> page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
>>  		void (*flush_page)(struct device *, const void *, phys_addr_t))  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct page **pages;
>>  	struct sg_table sgt;
>> @@ -386,7 +414,7 @@ dma_addr_t iommu_dma_map_page(struct device
>> *dev, struct page *page,  {
>>  	dma_addr_t dma_addr;
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	phys_addr_t phys = page_to_phys(page) + offset;
>>  	size_t iova_off = iova_offset(iovad, phys);
>>  	size_t len = iova_align(iovad, size + iova_off); @@ -495,7 +523,7 @@
>> int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
>>  		int nents, int prot)
>>  {
>>  	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> -	struct iova_domain *iovad = domain->iova_cookie;
>> +	struct iova_domain *iovad = cookie_iovad(domain);
>>  	struct iova *iova;
>>  	struct scatterlist *s, *prev = NULL;
>>  	dma_addr_t dma_addr;
>> @@ -587,3 +615,81 @@ int iommu_dma_mapping_error(struct device *dev,
>> dma_addr_t dma_addr)  {
>>  	return dma_addr == DMA_ERROR_CODE;
>>  }
>> +
>> +static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device
>> *dev,
>> +		phys_addr_t msi_addr, struct iommu_domain *domain) {
>> +	struct iommu_dma_cookie *cookie = domain->iova_cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	struct iova_domain *iovad = &cookie->iovad;
>> +	struct iova *iova;
>> +	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> +	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
>> +	list_for_each_entry(msi_page, &cookie->msi_page_list, list)
>> +		if (msi_page->phys == msi_addr)
>> +			return msi_page;
>> +
>> +	msi_page = kzalloc(sizeof(*msi_page), GFP_ATOMIC);
>> +	if (!msi_page)
>> +		return NULL;
>> +
>> +	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
> 
> I think this should be 'iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));'

Er, yes... I fully agree. That's why it is exactly that.

> as __alloc_iova takes input parameter as 'struct iova_domain *'

Joking aside, though, I guess you've overlooked the change introduced by
c987ff0d3cb3 ("iommu/dma: Respect IOMMU aperture when allocating")?

Robin.

> 
> Regards,
> Nipun
> 
>> +	if (!iova)
>> +		goto out_free_page;
>> +
>> +	msi_page->phys = msi_addr;
>> +	msi_page->iova = iova_dma_addr(iovad, iova);
>> +	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule,
>> prot))
>> +		goto out_free_iova;
>> +
>> +	INIT_LIST_HEAD(&msi_page->list);
>> +	list_add(&msi_page->list, &cookie->msi_page_list);
>> +	return msi_page;
>> +
>> +out_free_iova:
>> +	__free_iova(iovad, iova);
>> +out_free_page:
>> +	kfree(msi_page);
>> +	return NULL;
>> +}
>> +
>> +void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) {
>> +	struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq));
>> +	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>> +	struct iommu_dma_cookie *cookie;
>> +	struct iommu_dma_msi_page *msi_page;
>> +	phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
>> +	unsigned long flags;
>> +
>> +	if (!domain || !domain->iova_cookie)
>> +		return;
>> +
>> +	cookie = domain->iova_cookie;
>> +
>> +	/*
>> +	 * We disable IRQs to rule out a possible inversion against
>> +	 * irq_desc_lock if, say, someone tries to retarget the affinity
>> +	 * of an MSI from within an IPI handler.
>> +	 */
>> +	spin_lock_irqsave(&cookie->msi_lock, flags);
>> +	msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
>> +	spin_unlock_irqrestore(&cookie->msi_lock, flags);
>> +
>> +	if (WARN_ON(!msi_page)) {
>> +		/*
>> +		 * We're called from a void callback, so the best we can do is
>> +		 * 'fail' by filling the message with obviously bogus values.
>> +		 * Since we got this far due to an IOMMU being present, it's
>> +		 * not like the existing address would have worked anyway...
>> +		 */
>> +		msg->address_hi = ~0U;
>> +		msg->address_lo = ~0U;
>> +		msg->data = ~0U;
>> +	} else {
>> +		msg->address_hi = upper_32_bits(msi_page->iova);
>> +		msg->address_lo &= iova_mask(&cookie->iovad);
>> +		msg->address_lo += lower_32_bits(msi_page->iova);
>> +	}
>> +}
>> diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index
>> 35eb7ac5d21f..863e073c6f7f 100644
>> --- a/drivers/irqchip/irq-gic-v2m.c
>> +++ b/drivers/irqchip/irq-gic-v2m.c
>> @@ -16,6 +16,7 @@
>>  #define pr_fmt(fmt) "GICv2m: " fmt
>>
>>  #include <linux/acpi.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/irq.h>
>>  #include <linux/irqdomain.h>
>>  #include <linux/kernel.h>
>> @@ -108,6 +109,8 @@ static void gicv2m_compose_msi_msg(struct irq_data
>> *data, struct msi_msg *msg)
>>
>>  	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
>>  		msg->data -= v2m->spi_offset;
>> +
>> +	iommu_dma_map_msi_msg(data->irq, msg);
>>  }
>>
>>  static struct irq_chip gicv2m_irq_chip = { diff --git a/drivers/irqchip/irq-gic-v3-
>> its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index 36b9c28a5c91..98ff669d5962 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -18,6 +18,7 @@
>>  #include <linux/bitmap.h>
>>  #include <linux/cpu.h>
>>  #include <linux/delay.h>
>> +#include <linux/dma-iommu.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/log2.h>
>>  #include <linux/mm.h>
>> @@ -655,6 +656,8 @@ static void its_irq_compose_msi_msg(struct irq_data
>> *d, struct msi_msg *msg)
>>  	msg->address_lo		= addr & ((1UL << 32) - 1);
>>  	msg->address_hi		= addr >> 32;
>>  	msg->data		= its_get_event_id(d);
>> +
>> +	iommu_dma_map_msi_msg(d->irq, msg);
>>  }
>>
>>  static struct irq_chip its_irq_chip = { diff --git a/include/linux/dma-iommu.h
>> b/include/linux/dma-iommu.h index 81c5c8d167ad..5ee806e41b5c 100644
>> --- a/include/linux/dma-iommu.h
>> +++ b/include/linux/dma-iommu.h
>> @@ -21,6 +21,7 @@
>>
>>  #ifdef CONFIG_IOMMU_DMA
>>  #include <linux/iommu.h>
>> +#include <linux/msi.h>
>>
>>  int iommu_dma_init(void);
>>
>> @@ -62,9 +63,13 @@ void iommu_dma_unmap_sg(struct device *dev, struct
>> scatterlist *sg, int nents,  int iommu_dma_supported(struct device *dev, u64
>> mask);  int iommu_dma_mapping_error(struct device *dev, dma_addr_t
>> dma_addr);
>>
>> +/* The DMA API isn't _quite_ the whole story, though... */ void
>> +iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
>> +
>>  #else
>>
>>  struct iommu_domain;
>> +struct msi_msg;
>>
>>  static inline int iommu_dma_init(void)
>>  {
>> @@ -80,6 +85,10 @@ static inline void iommu_put_dma_cookie(struct
>> iommu_domain *domain)  {  }
>>
>> +static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
>> +{ }
>> +
>>  #endif	/* CONFIG_IOMMU_DMA */
>>  #endif	/* __KERNEL__ */
>>  #endif	/* __DMA_IOMMU_H */
>> --
>> 2.8.1.dirty
>>
>> _______________________________________________
>> iommu mailing list
>> iommu at lists.linux-foundation.org
>> https://lists.linuxfoundation.org/mailman/listinfo/iommu
> 

  parent reply	other threads:[~2016-10-05  9:55 UTC|newest]

Thread overview: 104+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-12 16:13 [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU Robin Murphy
2016-09-12 16:13 ` Robin Murphy
     [not found] ` <cover.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-12 16:13   ` [PATCH v7 01/22] Docs: dt: add PCI IOMMU map bindings Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 02/22] of/irq: Break out msi-map lookup (again) Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 03/22] iommu/of: Handle iommu-map property for PCI Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 04/22] iommu: Introduce iommu_fwspec Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <742a71630de502ac5a7a8641c6ed368d8409324d.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-13  9:54       ` [PATCH v7.1 " Robin Murphy
2016-09-13  9:54         ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 05/22] Docs: dt: document ARM SMMUv3 generic binding usage Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <2273645f1fa5c76b6b98b5fd03804ab8b55a7691.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-20 14:46       ` Rob Herring
2016-09-20 14:46         ` Rob Herring
2016-09-12 16:13   ` [PATCH v7 06/22] iommu/arm-smmu: Fall back to global bypass Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 07/22] iommu/arm-smmu: Implement of_xlate() for SMMUv3 Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 08/22] iommu/arm-smmu: Support non-PCI devices with SMMUv3 Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 09/22] iommu/arm-smmu: Set PRIVCFG in stage 1 STEs Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 10/22] iommu/arm-smmu: Handle stream IDs more dynamically Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 11/22] iommu/arm-smmu: Consolidate stream map entry state Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 12/22] iommu/arm-smmu: Keep track of S2CR state Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 13/22] iommu/arm-smmu: Refactor mmu-masters handling Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <046d2d21f988d6ece916fc45b0af0804a7f200f2.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-14 14:21       ` [PATCH v7.1 " Robin Murphy
2016-09-14 14:21         ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 14/22] iommu/arm-smmu: Streamline SMMU data lookups Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 15/22] iommu/arm-smmu: Add a stream map entry iterator Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 16/22] iommu/arm-smmu: Intelligent SMR allocation Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 17/22] iommu/arm-smmu: Convert to iommu_fwspec Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 18/22] Docs: dt: document ARM SMMU generic binding usage Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 19/22] iommu/arm-smmu: Wire up generic configuration support Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <228dc6c675f10ae7481640d4ef2f4960c170621f.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-14 14:26       ` [PATCH v7.1 " Robin Murphy
2016-09-14 14:26         ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 20/22] iommu/arm-smmu: Set domain geometry Robin Murphy
2016-09-12 16:13     ` Robin Murphy
2016-09-12 16:13   ` [PATCH v7 21/22] iommu/dma: Add support for mapping MSIs Robin Murphy
2016-09-12 16:13     ` Robin Murphy
     [not found]     ` <2273af20d844bd618c6a90b57e639700328ebf7f.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-10-05  7:00       ` Nipun Gupta
2016-10-05  7:00         ` Nipun Gupta
     [not found]         ` <DB6PR0402MB2694B2E5AE266F138784D2C2E6C40-2mNvjAGDOPn2WJ5A9zev/o3W/0Ik+aLCnBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
2016-10-05  9:55           ` Robin Murphy [this message]
2016-10-05  9:55             ` Robin Murphy
     [not found]             ` <6ec9519b-01df-3be8-2967-7556bd306909-5wv7dgnIgG8@public.gmane.org>
2016-10-05 11:31               ` Nipun Gupta
2016-10-05 11:31                 ` Nipun Gupta
2016-09-12 16:14   ` [PATCH v7 22/22] iommu/dma: Avoid PCI host bridge windows Robin Murphy
2016-09-12 16:14     ` Robin Murphy
     [not found]     ` <5f7bfee298f98d29a35933d3e0252d32b83d62b8.1473695704.git.robin.murphy-5wv7dgnIgG8@public.gmane.org>
2016-09-14 10:55       ` Marek Szyprowski
2016-09-14 10:55         ` Marek Szyprowski
     [not found]         ` <ab8693f6-20d6-2a95-9f1f-0607e72bc012-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2016-09-14 11:10           ` Robin Murphy
2016-09-14 11:10             ` Robin Murphy
     [not found]             ` <49c51c4f-cb00-445d-b8f8-b632babf2b3e-5wv7dgnIgG8@public.gmane.org>
2016-09-14 12:35               ` Marek Szyprowski
2016-09-14 12:35                 ` Marek Szyprowski
     [not found]                 ` <dc9f945f-2756-ab70-d061-9fdc7c5afdee-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2016-09-14 13:25                   ` Robin Murphy
2016-09-14 13:25                     ` Robin Murphy
     [not found]                     ` <bbdc42fa-ea35-945f-3e2a-e0ab03fc997d-5wv7dgnIgG8@public.gmane.org>
2016-09-15  7:08                       ` Marek Szyprowski
2016-09-15  7:08                         ` Marek Szyprowski
2016-09-13 12:14   ` [PATCH v7 00/22] Generic DT bindings for PCI IOMMUs and ARM SMMU Auger Eric
2016-09-13 12:14     ` Auger Eric
     [not found]     ` <92f27a6b-9752-516d-3924-c552fc6a5ace-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-13 12:40       ` Robin Murphy
2016-09-13 12:40         ` Robin Murphy
     [not found]         ` <e24821be-5cc4-52b3-f961-1eb32cf58293-5wv7dgnIgG8@public.gmane.org>
2016-09-13 12:57           ` Auger Eric
2016-09-13 12:57             ` Auger Eric
2016-09-14  8:41   ` Auger Eric
2016-09-14  8:41     ` Auger Eric
     [not found]     ` <11ebd81e-2ea5-5ff3-35b3-be95f03e05bd-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-14  9:20       ` Will Deacon
2016-09-14  9:20         ` Will Deacon
     [not found]         ` <20160914092051.GB19622-5wv7dgnIgG8@public.gmane.org>
2016-09-14  9:35           ` Auger Eric
2016-09-14  9:35             ` Auger Eric
2016-09-14 10:35       ` Robin Murphy
2016-09-14 10:35         ` Robin Murphy
     [not found]         ` <d03ea5e7-59f1-8b49-4ba2-d05fc2030ebc-5wv7dgnIgG8@public.gmane.org>
2016-09-14 12:32           ` Auger Eric
2016-09-14 12:32             ` Auger Eric
     [not found]             ` <04a0a682-4fdc-8d62-57cd-efdf730582c6-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-14 12:53               ` Robin Murphy
2016-09-14 12:53                 ` Robin Murphy
     [not found]                 ` <c2645c5e-edd3-2b31-4311-0ca621a915e2-5wv7dgnIgG8@public.gmane.org>
2016-09-15  9:29                   ` Auger Eric
2016-09-15  9:29                     ` Auger Eric
     [not found]                     ` <4d87d5f2-0350-b5f8-ffc3-4e9377cf1f87-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-15 10:15                       ` Robin Murphy
2016-09-15 10:15                         ` Robin Murphy
     [not found]                         ` <fc4ce398-4eeb-f2ca-b964-e9f466be79c4-5wv7dgnIgG8@public.gmane.org>
2016-09-15 16:46                           ` Auger Eric
2016-09-15 16:46                             ` Auger Eric
     [not found]                             ` <1838c65d-5944-8946-781c-b420bea1acab-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-16 16:18                               ` Robin Murphy
2016-09-16 16:18                                 ` Robin Murphy
     [not found]                                 ` <f16db032-1905-9804-0607-fe007af72b0e-5wv7dgnIgG8@public.gmane.org>
2016-09-19 12:13                                   ` Auger Eric
2016-09-19 12:13                                     ` Auger Eric
     [not found]                                     ` <48f3bc10-3966-7d50-d070-7ec7f0946c92-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2016-09-19 12:24                                       ` Will Deacon
2016-09-19 12:24                                         ` Will Deacon
     [not found]                                         ` <20160919122435.GD9005-5wv7dgnIgG8@public.gmane.org>
2016-09-19 12:41                                           ` Robin Murphy
2016-09-19 12:41                                             ` Robin Murphy
     [not found]                                             ` <99ee0946-c7ff-e6e4-08c1-ff686ea1a8a5-5wv7dgnIgG8@public.gmane.org>
2016-09-19 14:17                                               ` Will Deacon
2016-09-19 14:17                                                 ` Will Deacon

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=6ec9519b-01df-3be8-2967-7556bd306909@arm.com \
    --to=robin.murphy-5wv7dgnigg8@public.gmane.org \
    --cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=nipun.gupta-3arQi8VN3Tc@public.gmane.org \
    --cc=punit.agrawal-5wv7dgnIgG8@public.gmane.org \
    --cc=thunder.leizhen-hv44wF8Li93QT0dZR+AlfA@public.gmane.org \
    --cc=will.deacon-5wv7dgnIgG8@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.