iommu/dma-iommu: Split iommu_dma_map_msi_msg() in two parts

On RT, iommu_dma_map_msi_msg() may be called from non-preemptible
context. This will lead to a splat with CONFIG_DEBUG_ATOMIC_SLEEP as
the function is using spin_lock (they can sleep on RT).

iommu_dma_map_msi_msg() is used to map the MSI page in the IOMMU PT
and update the MSI message with the IOVA.

Only the part to lookup for the MSI page requires to be called in
preemptible context. As the MSI page cannot change over the lifecycle
of the MSI interrupt, the lookup can be cached and re-used later on.

iomma_dma_map_msi_msg() is now split in two functions:
    - iommu_dma_prepare_msi(): This function will prepare the mapping
    in the IOMMU and store the cookie in the structure msi_desc. This
    function should be called in preemptible context.
    - iommu_dma_compose_msi_msg(): This function will update the MSI
    message with the IOVA when the device is behind an IOMMU.

Signed-off-by: Julien Grall <julien.grall@arm.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Acked-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This commit is contained in:
Julien Grall 2019-05-01 14:58:19 +01:00 committed by Marc Zyngier
parent aaebdf8d68
commit ece6e6f021
3 changed files with 63 additions and 9 deletions

View File

@ -94,6 +94,7 @@ config IOMMU_DMA
bool bool
select IOMMU_API select IOMMU_API
select IOMMU_IOVA select IOMMU_IOVA
select IRQ_MSI_IOMMU
select NEED_SG_DMA_LENGTH select NEED_SG_DMA_LENGTH
config FSL_PAMU config FSL_PAMU

View File

@ -888,17 +888,18 @@ out_free_page:
return NULL; return NULL;
} }
void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr)
{ {
struct device *dev = msi_desc_to_dev(irq_get_msi_desc(irq)); struct device *dev = msi_desc_to_dev(desc);
struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct iommu_dma_cookie *cookie; struct iommu_dma_cookie *cookie;
struct iommu_dma_msi_page *msi_page; struct iommu_dma_msi_page *msi_page;
phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
unsigned long flags; unsigned long flags;
if (!domain || !domain->iova_cookie) if (!domain || !domain->iova_cookie) {
return; desc->iommu_cookie = NULL;
return 0;
}
cookie = domain->iova_cookie; cookie = domain->iova_cookie;
@ -911,7 +912,36 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain); msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
spin_unlock_irqrestore(&cookie->msi_lock, flags); spin_unlock_irqrestore(&cookie->msi_lock, flags);
if (WARN_ON(!msi_page)) { msi_desc_set_iommu_cookie(desc, msi_page);
if (!msi_page)
return -ENOMEM;
return 0;
}
void iommu_dma_compose_msi_msg(struct msi_desc *desc,
struct msi_msg *msg)
{
struct device *dev = msi_desc_to_dev(desc);
const struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
const struct iommu_dma_msi_page *msi_page;
msi_page = msi_desc_get_iommu_cookie(desc);
if (!domain || !domain->iova_cookie || WARN_ON(!msi_page))
return;
msg->address_hi = upper_32_bits(msi_page->iova);
msg->address_lo &= cookie_msi_granule(domain->iova_cookie) - 1;
msg->address_lo += lower_32_bits(msi_page->iova);
}
void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
{
struct msi_desc *desc = irq_get_msi_desc(irq);
phys_addr_t msi_addr = (u64)msg->address_hi << 32 | msg->address_lo;
if (WARN_ON(iommu_dma_prepare_msi(desc, msi_addr))) {
/* /*
* We're called from a void callback, so the best we can do is * We're called from a void callback, so the best we can do is
* 'fail' by filling the message with obviously bogus values. * 'fail' by filling the message with obviously bogus values.
@ -922,8 +952,6 @@ void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
msg->address_lo = ~0U; msg->address_lo = ~0U;
msg->data = ~0U; msg->data = ~0U;
} else { } else {
msg->address_hi = upper_32_bits(msi_page->iova); iommu_dma_compose_msi_msg(desc, msg);
msg->address_lo &= cookie_msi_granule(cookie) - 1;
msg->address_lo += lower_32_bits(msi_page->iova);
} }
} }

View File

@ -71,12 +71,26 @@ void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir, unsigned long attrs); size_t size, enum dma_data_direction dir, unsigned long attrs);
/* The DMA API isn't _quite_ the whole story, though... */ /* The DMA API isn't _quite_ the whole story, though... */
/*
* iommu_dma_prepare_msi() - Map the MSI page in the IOMMU device
*
* The MSI page will be stored in @desc.
*
* Return: 0 on success otherwise an error describing the failure.
*/
int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr);
/* Update the MSI message if required. */
void iommu_dma_compose_msi_msg(struct msi_desc *desc,
struct msi_msg *msg);
void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg); void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
#else #else
struct iommu_domain; struct iommu_domain;
struct msi_desc;
struct msi_msg; struct msi_msg;
struct device; struct device;
@ -99,6 +113,17 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
{ {
} }
static inline int iommu_dma_prepare_msi(struct msi_desc *desc,
phys_addr_t msi_addr)
{
return 0;
}
static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc,
struct msi_msg *msg)
{
}
static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg) static inline void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg)
{ {
} }