| From foo@baz Mon Sep 17 12:33:31 CEST 2018 |
| From: Geert Uytterhoeven <geert+renesas@glider.be> |
| Date: Fri, 20 Jul 2018 18:16:59 +0200 |
| Subject: iommu/ipmmu-vmsa: Fix allocation in atomic context |
| |
| From: Geert Uytterhoeven <geert+renesas@glider.be> |
| |
| [ Upstream commit 46583e8c48c5a094ba28060615b3a7c8c576690f ] |
| |
| When attaching a device to an IOMMU group with |
| CONFIG_DEBUG_ATOMIC_SLEEP=y: |
| |
| BUG: sleeping function called from invalid context at mm/slab.h:421 |
| in_atomic(): 1, irqs_disabled(): 128, pid: 61, name: kworker/1:1 |
| ... |
| Call trace: |
| ... |
| arm_lpae_alloc_pgtable+0x114/0x184 |
| arm_64_lpae_alloc_pgtable_s1+0x2c/0x128 |
| arm_32_lpae_alloc_pgtable_s1+0x40/0x6c |
| alloc_io_pgtable_ops+0x60/0x88 |
| ipmmu_attach_device+0x140/0x334 |
| |
| ipmmu_attach_device() takes a spinlock, while arm_lpae_alloc_pgtable() |
| allocates memory using GFP_KERNEL. Originally, the ipmmu-vmsa driver |
| had its own custom page table allocation implementation using |
| GFP_ATOMIC, hence the spinlock was fine. |
| |
| Fix this by replacing the spinlock by a mutex, like the arm-smmu driver |
| does. |
| |
| Fixes: f20ed39f53145e45 ("iommu/ipmmu-vmsa: Use the ARM LPAE page table allocator") |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| Signed-off-by: Joerg Roedel <jroedel@suse.de> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/iommu/ipmmu-vmsa.c | 9 ++++----- |
| 1 file changed, 4 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/iommu/ipmmu-vmsa.c |
| +++ b/drivers/iommu/ipmmu-vmsa.c |
| @@ -54,7 +54,7 @@ struct ipmmu_vmsa_domain { |
| struct io_pgtable_ops *iop; |
| |
| unsigned int context_id; |
| - spinlock_t lock; /* Protects mappings */ |
| + struct mutex mutex; /* Protects mappings */ |
| }; |
| |
| struct ipmmu_vmsa_iommu_priv { |
| @@ -523,7 +523,7 @@ static struct iommu_domain *__ipmmu_doma |
| if (!domain) |
| return NULL; |
| |
| - spin_lock_init(&domain->lock); |
| + mutex_init(&domain->mutex); |
| |
| return &domain->io_domain; |
| } |
| @@ -548,7 +548,6 @@ static int ipmmu_attach_device(struct io |
| struct iommu_fwspec *fwspec = dev->iommu_fwspec; |
| struct ipmmu_vmsa_device *mmu = priv->mmu; |
| struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
| - unsigned long flags; |
| unsigned int i; |
| int ret = 0; |
| |
| @@ -557,7 +556,7 @@ static int ipmmu_attach_device(struct io |
| return -ENXIO; |
| } |
| |
| - spin_lock_irqsave(&domain->lock, flags); |
| + mutex_lock(&domain->mutex); |
| |
| if (!domain->mmu) { |
| /* The domain hasn't been used yet, initialize it. */ |
| @@ -574,7 +573,7 @@ static int ipmmu_attach_device(struct io |
| } else |
| dev_info(dev, "Reusing IPMMU context %u\n", domain->context_id); |
| |
| - spin_unlock_irqrestore(&domain->lock, flags); |
| + mutex_unlock(&domain->mutex); |
| |
| if (ret < 0) |
| return ret; |