summaryrefslogtreecommitdiffstats
path: root/drivers/iommu/ipmmu-vmsa.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-07-24 15:34:54 +0200
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2015-01-16 18:03:04 +0200
commit22463cab3f96baf6b568200a35c7648438eea7ff (patch)
treedffa7b4a250428c3964aa1f4fd9d2b4227e52aac /drivers/iommu/ipmmu-vmsa.c
parentb8f80bffd51f4ef029051d6898d9c2e3e5637dc3 (diff)
downloadlinux-0-day-22463cab3f96baf6b568200a35c7648438eea7ff.tar.gz
linux-0-day-22463cab3f96baf6b568200a35c7648438eea7ff.tar.xz
iommu/ipmmu-vmsa: Flush P[UM]D entry before freeing the child page table
When clearing PUD or PMD entries the child page table (if any) is freed and the PUD or PMD entry is then cleared. This result in a small race condition window during which a free page table could be accessed by the IPMMU. Fix it by clearing and flushing the PUD or PMD entry before freeing the child page table. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Diffstat (limited to 'drivers/iommu/ipmmu-vmsa.c')
-rw-r--r--drivers/iommu/ipmmu-vmsa.c19
1 files changed, 11 insertions, 8 deletions
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index 034293ca4b9a4..a32d237c0f227 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -678,30 +678,33 @@ done:
static void ipmmu_clear_pud(struct ipmmu_vmsa_device *mmu, pud_t *pud)
{
- /* Free the page table. */
pgtable_t table = pud_pgtable(*pud);
- __free_page(table);
/* Clear the PUD. */
*pud = __pud(0);
ipmmu_flush_pgtable(mmu, pud, sizeof(*pud));
+
+ /* Free the page table. */
+ __free_page(table);
}
static void ipmmu_clear_pmd(struct ipmmu_vmsa_device *mmu, pud_t *pud,
pmd_t *pmd)
{
+ pmd_t pmdval = *pmd;
unsigned int i;
- /* Free the page table. */
- if (pmd_table(*pmd)) {
- pgtable_t table = pmd_pgtable(*pmd);
- __free_page(table);
- }
-
/* Clear the PMD. */
*pmd = __pmd(0);
ipmmu_flush_pgtable(mmu, pmd, sizeof(*pmd));
+ /* Free the page table. */
+ if (pmd_table(pmdval)) {
+ pgtable_t table = pmd_pgtable(pmdval);
+
+ __free_page(table);
+ }
+
/* Check whether the PUD is still needed. */
pmd = pmd_offset(pud, 0);
for (i = 0; i < IPMMU_PTRS_PER_PMD; ++i) {