aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu
diff options
context:
space:
mode:
authorJoerg Roedel <joro@8bytes.org>2013-06-20 14:22:58 -0400
committerJoerg Roedel <joro@8bytes.org>2013-06-20 17:38:42 -0400
commit5c34c403b72395e59cad64a75fb0b772b0ab9cd4 (patch)
treef3459a9eef55dfa8e382f363c55ee3a4c9b2346e /drivers/iommu
parent7d132055814ef17a6c7b69f342244c410a5e000f (diff)
iommu/amd: Fix memory leak in free_pagetable
The IOMMU pagetables can have up to 6 levels, but the code in free_pagetable() only releases the first 3 levels. Fix this leak by releasing all levels. Reported-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Joerg Roedel <joro@8bytes.org> Reviewed-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/iommu')
-rw-r--r--drivers/iommu/amd_iommu.c73
1 files changed, 49 insertions, 24 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 21d02b0d907c..5cde682377a0 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1893,34 +1893,59 @@ static void domain_id_free(int id)
1893 write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); 1893 write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
1894} 1894}
1895 1895
1896#define DEFINE_FREE_PT_FN(LVL, FN) \
1897static void free_pt_##LVL (unsigned long __pt) \
1898{ \
1899 unsigned long p; \
1900 u64 *pt; \
1901 int i; \
1902 \
1903 pt = (u64 *)__pt; \
1904 \
1905 for (i = 0; i < 512; ++i) { \
1906 if (!IOMMU_PTE_PRESENT(pt[i])) \
1907 continue; \
1908 \
1909 p = (unsigned long)IOMMU_PTE_PAGE(pt[i]); \
1910 FN(p); \
1911 } \
1912 free_page((unsigned long)pt); \
1913}
1914
1915DEFINE_FREE_PT_FN(l2, free_page)
1916DEFINE_FREE_PT_FN(l3, free_pt_l2)
1917DEFINE_FREE_PT_FN(l4, free_pt_l3)
1918DEFINE_FREE_PT_FN(l5, free_pt_l4)
1919DEFINE_FREE_PT_FN(l6, free_pt_l5)
1920
1896static void free_pagetable(struct protection_domain *domain) 1921static void free_pagetable(struct protection_domain *domain)
1897{ 1922{
1898 int i, j; 1923 unsigned long root = (unsigned long)domain->pt_root;
1899 u64 *p1, *p2, *p3;
1900
1901 p1 = domain->pt_root;
1902
1903 if (!p1)
1904 return;
1905
1906 for (i = 0; i < 512; ++i) {
1907 if (!IOMMU_PTE_PRESENT(p1[i]))
1908 continue;
1909 1924
1910 p2 = IOMMU_PTE_PAGE(p1[i]); 1925 switch (domain->mode) {
1911 for (j = 0; j < 512; ++j) { 1926 case PAGE_MODE_NONE:
1912 if (!IOMMU_PTE_PRESENT(p2[j])) 1927 break;
1913 continue; 1928 case PAGE_MODE_1_LEVEL:
1914 p3 = IOMMU_PTE_PAGE(p2[j]); 1929 free_page(root);
1915 free_page((unsigned long)p3); 1930 break;
1916 } 1931 case PAGE_MODE_2_LEVEL:
1917 1932 free_pt_l2(root);
1918 free_page((unsigned long)p2); 1933 break;
1934 case PAGE_MODE_3_LEVEL:
1935 free_pt_l3(root);
1936 break;
1937 case PAGE_MODE_4_LEVEL:
1938 free_pt_l4(root);
1939 break;
1940 case PAGE_MODE_5_LEVEL:
1941 free_pt_l5(root);
1942 break;
1943 case PAGE_MODE_6_LEVEL:
1944 free_pt_l6(root);
1945 break;
1946 default:
1947 BUG();
1919 } 1948 }
1920
1921 free_page((unsigned long)p1);
1922
1923 domain->pt_root = NULL;
1924} 1949}
1925 1950
1926static void free_gcr3_tbl_level1(u64 *tbl) 1951static void free_gcr3_tbl_level1(u64 *tbl)