iommu/tegra: smmu: fix unaliged sg mapping
Hiroshi Doyu [Thu, 10 Apr 2014 06:43:00 +0000 (09:43 +0300)]
map_sg() miscaluculated the number of pages to map where 'offset' and
PAGE_ALIGN was ignored. This patch fixes the ext4 memory corruption
problem when USB is used. This patch was based on Nilesh More's hard
working journey to narrow down the root cause of this problem. This bug
was introduced by the commit:

  f46788a6f7d9 - iommu/tegra: smmu: Optimize smmu_iommu_map_sg()

Bug 1418514

Change-Id: I3492ca3aad48f63bc81e50886eefc32cb6a17a8b
Reported-by: Nilesh More <nmore@nvidia.com>
Tested-by: Nilesh More <nmore@nvidia.com>
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Reviewed-on: http://git-master/r/394554
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Nilesh More <nmore@nvidia.com>
Reviewed-by: Sri Krishna Chowdary <schowdary@nvidia.com>
(cherry picked from commit 7b4dac1d522cf48e4e6bd227ff235bffa82cb755)
Signed-off-by: Winnie Hsu <whsu@nvidia.com>
Reviewed-on: http://git-master/r/394993

drivers/iommu/tegra-smmu.c

index 2dab5cd..fa817ef 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * IOMMU driver for SMMU on Tegra 3 series SoCs and later.
  *
- * Copyright (c) 2011-2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -1214,7 +1214,8 @@ static int smmu_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
        struct smmu_device *smmu = as->smmu;
        int attrs = as->pte_attr;
        size_t total = npages;
-       size_t sg_remaining = sgl->length >> PAGE_SHIFT;
+       size_t sg_remaining =
+               PAGE_ALIGN(sgl->offset + sgl->length) >> PAGE_SHIFT;
        unsigned long sg_pfn = page_to_pfn(sg_page(sgl));
 
        if (dma_get_attr(DMA_ATTR_READ_ONLY, (struct dma_attrs *)prot))
@@ -1261,7 +1262,9 @@ static int smmu_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
                        sgl = sg_next(sgl);
                        if (sgl) {
                                sg_pfn = page_to_pfn(sg_page(sgl));
-                               sg_remaining = sgl->length >> PAGE_SHIFT;
+                               sg_remaining =
+                                       PAGE_ALIGN(sgl->offset + sgl->length)
+                                       >> PAGE_SHIFT;
                        }
                }