aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-07-24 09:34:54 -0400
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2015-01-16 11:03:04 -0500
commit22463cab3f96baf6b568200a35c7648438eea7ff (patch)
treedffa7b4a250428c3964aa1f4fd9d2b4227e52aac
parentb8f80bffd51f4ef029051d6898d9c2e3e5637dc3 (diff)
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>
-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 034293ca4b9a..a32d237c0f22 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -678,30 +678,33 @@ done:
678 678
679static void ipmmu_clear_pud(struct ipmmu_vmsa_device *mmu, pud_t *pud) 679static void ipmmu_clear_pud(struct ipmmu_vmsa_device *mmu, pud_t *pud)
680{ 680{
681 /* Free the page table. */
682 pgtable_t table = pud_pgtable(*pud); 681 pgtable_t table = pud_pgtable(*pud);
683 __free_page(table);
684 682
685 /* Clear the PUD. */ 683 /* Clear the PUD. */
686 *pud = __pud(0); 684 *pud = __pud(0);
687 ipmmu_flush_pgtable(mmu, pud, sizeof(*pud)); 685 ipmmu_flush_pgtable(mmu, pud, sizeof(*pud));
686
687 /* Free the page table. */
688 __free_page(table);
688} 689}
689 690
690static void ipmmu_clear_pmd(struct ipmmu_vmsa_device *mmu, pud_t *pud, 691static void ipmmu_clear_pmd(struct ipmmu_vmsa_device *mmu, pud_t *pud,
691 pmd_t *pmd) 692 pmd_t *pmd)
692{ 693{
694 pmd_t pmdval = *pmd;
693 unsigned int i; 695 unsigned int i;
694 696
695 /* Free the page table. */
696 if (pmd_table(*pmd)) {
697 pgtable_t table = pmd_pgtable(*pmd);
698 __free_page(table);
699 }
700
701 /* Clear the PMD. */ 697 /* Clear the PMD. */
702 *pmd = __pmd(0); 698 *pmd = __pmd(0);
703 ipmmu_flush_pgtable(mmu, pmd, sizeof(*pmd)); 699 ipmmu_flush_pgtable(mmu, pmd, sizeof(*pmd));
704 700
701 /* Free the page table. */
702 if (pmd_table(pmdval)) {
703 pgtable_t table = pmd_pgtable(pmdval);
704
705 __free_page(table);
706 }
707
705 /* Check whether the PUD is still needed. */ 708 /* Check whether the PUD is still needed. */
706 pmd = pmd_offset(pud, 0); 709 pmd = pmd_offset(pud, 0);
707 for (i = 0; i < IPMMU_PTRS_PER_PMD; ++i) { 710 for (i = 0; i < IPMMU_PTRS_PER_PMD; ++i) {