diff options
author | Joerg Roedel <joro@8bytes.org> | 2013-06-20 14:22:58 -0400 |
---|---|---|
committer | Joerg Roedel <joro@8bytes.org> | 2013-06-20 17:38:42 -0400 |
commit | 5c34c403b72395e59cad64a75fb0b772b0ab9cd4 (patch) | |
tree | f3459a9eef55dfa8e382f363c55ee3a4c9b2346e /drivers/iommu | |
parent | 7d132055814ef17a6c7b69f342244c410a5e000f (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.c | 73 |
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) \ | ||
1897 | static 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 | |||
1915 | DEFINE_FREE_PT_FN(l2, free_page) | ||
1916 | DEFINE_FREE_PT_FN(l3, free_pt_l2) | ||
1917 | DEFINE_FREE_PT_FN(l4, free_pt_l3) | ||
1918 | DEFINE_FREE_PT_FN(l5, free_pt_l4) | ||
1919 | DEFINE_FREE_PT_FN(l6, free_pt_l5) | ||
1920 | |||
1896 | static void free_pagetable(struct protection_domain *domain) | 1921 | static 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 | ||
1926 | static void free_gcr3_tbl_level1(u64 *tbl) | 1951 | static void free_gcr3_tbl_level1(u64 *tbl) |