summaryrefslogtreecommitdiff
path: root/mm/kmemleak.c
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2016-10-11 13:55:11 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-11 15:06:33 -0700
commit9099daed9c6991a512c1f74b92ec49daf9408cda (patch)
tree9ebac94f168a0d5d843b715c1d19708d12a1c8c0 /mm/kmemleak.c
parent0549a3c02efb350776bc869685a361045efd3a29 (diff)
mm: kmemleak: avoid using __va() on addresses that don't have a lowmem mapping
Some of the kmemleak_*() callbacks in memblock, bootmem, CMA convert a physical address to a virtual one using __va(). However, such physical addresses may sometimes be located in highmem and using __va() is incorrect, leading to inconsistent object tracking in kmemleak. The following functions have been added to the kmemleak API and they take a physical address as the object pointer. They only perform the corresponding action if the address has a lowmem mapping: kmemleak_alloc_phys kmemleak_free_part_phys kmemleak_not_leak_phys kmemleak_ignore_phys The affected calling places have been updated to use the new kmemleak API. Link: http://lkml.kernel.org/r/1471531432-16503-1-git-send-email-catalin.marinas@arm.com Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Reported-by: Vignesh R <vigneshr@ti.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/kmemleak.c')
-rw-r--r--mm/kmemleak.c47
1 files changed, 47 insertions, 0 deletions
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 086292f7c59d..a5e453cf05c4 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -90,6 +90,8 @@
#include <linux/cache.h>
#include <linux/percpu.h>
#include <linux/hardirq.h>
+#include <linux/bootmem.h>
+#include <linux/pfn.h>
#include <linux/mmzone.h>
#include <linux/slab.h>
#include <linux/thread_info.h>
@@ -1121,6 +1123,51 @@ void __ref kmemleak_no_scan(const void *ptr)
}
EXPORT_SYMBOL(kmemleak_no_scan);
+/**
+ * kmemleak_alloc_phys - similar to kmemleak_alloc but taking a physical
+ * address argument
+ */
+void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count,
+ gfp_t gfp)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_alloc(__va(phys), size, min_count, gfp);
+}
+EXPORT_SYMBOL(kmemleak_alloc_phys);
+
+/**
+ * kmemleak_free_part_phys - similar to kmemleak_free_part but taking a
+ * physical address argument
+ */
+void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_free_part(__va(phys), size);
+}
+EXPORT_SYMBOL(kmemleak_free_part_phys);
+
+/**
+ * kmemleak_not_leak_phys - similar to kmemleak_not_leak but taking a physical
+ * address argument
+ */
+void __ref kmemleak_not_leak_phys(phys_addr_t phys)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_not_leak(__va(phys));
+}
+EXPORT_SYMBOL(kmemleak_not_leak_phys);
+
+/**
+ * kmemleak_ignore_phys - similar to kmemleak_ignore but taking a physical
+ * address argument
+ */
+void __ref kmemleak_ignore_phys(phys_addr_t phys)
+{
+ if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+ kmemleak_ignore(__va(phys));
+}
+EXPORT_SYMBOL(kmemleak_ignore_phys);
+
/*
* Update an object's checksum and return true if it was modified.
*/