iommu/ipmmu-vmsa: Flush P[UM]D entry before freeing the child page table
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Thu, 24 Jul 2014 13:34:54 +0000 (15:34 +0200)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Fri, 16 Jan 2015 16:03:04 +0000 (18:03 +0200)
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>
drivers/iommu/ipmmu-vmsa.c

index 034293ca4b9a4e5c6c5bc652815cf512087ee1a8..a32d237c0f227242a8e3541b7af60d7c5e68e263 100644 (file)
@@ -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) {