mm: kmemleak: store objects allocated with physical address separately and check when scan
Subsystems (6)
v4 cover mm: kmemleak: store objects allocated with physical address separately and check when scan
The kmemleak_*_phys() interface uses "min_low_pfn" and "max_low_pfn" to check address. But on some architectures, kmemleak_*_phys() is called before those two variables initialized. The following steps will be taken:
1) Add OBJECT_PHYS flag and rbtree for the objects allocated with physical address
2) Store physical address in objects if allocated with OBJECT_PHYS
3) Check the boundary when scan instead of in kmemleak_*_phys()
This patch set will solve:
https://lore.kernel.org/r/[email protected]
https://lore.kernel.org/r/[email protected]
v3: https://lore.kernel.org/r/[email protected]
v2: https://lore.kernel.org/r/[email protected]
v1: https://lore.kernel.org/r/[email protected]
v3->v4:
- fix a build warning
- move the prototype change of kmemleak_alloc_phys() and the kmemleak_not_leak_phys() removal to a separate patch (patch 1)
v2->v3:
- remove the min_count argument to kmemleak_alloc_phys() function and assume it's 0
- remove unused kmemleak_not_leak_phys() function
- add functions to reduce unnecessary changes
- remove the check for kasan_reset_tag()
- add Fixes tag in patch 3
v1->v2:
- add rbtree for the objects allocated with physical address
- store physical address in objects if allocated with OBJECT_PHYS
- check the upper object boundary as well and avoid duplicate check
Patrick Wang (4):
mm: kmemleak: remove kmemleak_not_leak_phys() and the min_count
argument to kmemleak_alloc_phys()
mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical
address
mm: kmemleak: add rbtree and store physical address for objects
allocated with PA
mm: kmemleak: check physical address when scan
Documentation/dev-tools/kmemleak.rst | 1 - drivers/of/fdt.c | 2 +- include/linux/kmemleak.h | 8 +- mm/kmemleak.c | 200 ++++++++++++++++-------- mm/memblock.c | 14 +- tools/testing/memblock/linux/kmemleak.h | 2 +- 6 files changed, 145 insertions(+), 82 deletions(-)
Signature · 1 line
2.25.1
PATCH v4 1/4 mm: kmemleak: remove kmemleak_not_leak_phys() and the min_count argument to kmemleak_alloc_phys()
Remove the unused kmemleak_not_leak_phys() function. And remove the min_count argument to kmemleak_alloc_phys() function, assume it's 0.
--- Documentation/dev-tools/kmemleak.rst | 1 - drivers/of/fdt.c | 2 +- include/linux/kmemleak.h | 8 ++------ mm/kmemleak.c | 20 +++----------------- mm/memblock.c | 14 +++++++------- tools/testing/memblock/linux/kmemleak.h | 2 +- 6 files changed, 14 insertions(+), 33 deletions(-)
Documentation/dev-tools/kmemleak.rst +0 -1
@@ -174,7 +174,6 @@ mapping: - ``kmemleak_alloc_phys`` - ``kmemleak_free_part_phys`` -- ``kmemleak_not_leak_phys`` - ``kmemleak_ignore_phys`` Dealing with false positives/negatives
drivers/of/fdt.c +1 -1
@@ -529,7 +529,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,pr_debug ("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n" , uname, &base, (unsigned long )(size /SZ_1M ));if (!nomap) -kmemleak_alloc_phys (base, size,0 ,0 ); +kmemleak_alloc_phys (base, size,0 ); }else pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
include/linux/kmemleak.h +2 -6
@@ -29,10 +29,9 @@ extern void kmemleak_not_leak(const void *ptr) __ref;extern void kmemleak_ignore (const void *ptr)__ref ;extern void kmemleak_scan_area (const void *ptr,size_t size,gfp_t gfp)__ref ;extern void kmemleak_no_scan (const void *ptr)__ref ; -extern void kmemleak_alloc_phys (phys_addr_t phys,size_t size,int min_count, +extern void kmemleak_alloc_phys (phys_addr_t phys,size_t size,gfp_t gfp)__ref ;extern void kmemleak_free_part_phys (phys_addr_t phys,size_t size)__ref ; -extern void kmemleak_not_leak_phys (phys_addr_t phys)__ref ;extern void kmemleak_ignore_phys (phys_addr_t phys)__ref ; static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
@@ -107,15 +106,12 @@ static inline void kmemleak_no_scan(const void *ptr) { }static inline void kmemleak_alloc_phys (phys_addr_t phys,size_t size, -int min_count,gfp_t gfp) +gfp_t gfp) { }static inline void kmemleak_free_part_phys (phys_addr_t phys,size_t size) { } -static inline void kmemleak_not_leak_phys (phys_addr_t phys) -{ -}static inline void kmemleak_ignore_phys (phys_addr_t phys) { }
mm/kmemleak.c +3 -17
@@ -1125,15 +1125,13 @@ EXPORT_SYMBOL(kmemleak_no_scan);* address argument * @phys: physical address of the object * @size: size of the object -* @min_count: minimum number of references to this object. -* See kmemleak_alloc() * @gfp: kmalloc() flags used for kmemleak internal memory allocations */ -void __ref kmemleak_alloc_phys (phys_addr_t phys,size_t size,int min_count, -gfp_t gfp) +void __ref kmemleak_alloc_phys (phys_addr_t phys,size_t size,gfp_t gfp) {if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_alloc (__va (phys), size, min_count, gfp); +/* assume min_count 0 */ +kmemleak_alloc (__va (phys), size,0 , gfp); }EXPORT_SYMBOL (kmemleak_alloc_phys);
@@ -1151,18 +1149,6 @@ void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size) }EXPORT_SYMBOL (kmemleak_free_part_phys); -/** -* kmemleak_not_leak_phys - similar to kmemleak_not_leak but taking a physical -* address argument -* @phys: physical address of the object -*/ -void __ref kmemleak_not_leak_phys (phys_addr_t phys) -{ -if (PHYS_PFN (phys) >= min_low_pfn &&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
mm/memblock.c +7 -7
@@ -1345,8 +1345,8 @@ __next_mem_pfn_range_in_zone(u64 *idx, struct zone *zone,* from the regions with mirroring enabled and then retried from any * memory region. * -* In addition, function sets the min_count to 0 using kmemleak_alloc_phys for -* allocated boot memory block, so that it is never reported as leaks. +* In addition, function using kmemleak_alloc_phys for allocated boot +* memory block, it is never reported as leaks. * * Return: * Physical address of allocated memory block on success, %0 on failure.
@@ -1398,12 +1398,12 @@ phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,*/ if (end !=MEMBLOCK_ALLOC_NOLEAKTRACE )/* -* The min_count is set to 0 so that memblock allocated -* blocks are never reported as leaks. This is because many -* of these blocks are only referred via the physical -* address which is not looked up by kmemleak. +* Memblock allocated blocks are never reported as +* leaks. This is because many of these blocks are +* only referred via the physical address which is +* not looked up by kmemleak. */ -kmemleak_alloc_phys (found, size,0 ,0 ); +kmemleak_alloc_phys (found, size,0 );return found; }
tools/testing/memblock/linux/kmemleak.h +1 -1
@@ -7,7 +7,7 @@ static inline void kmemleak_free_part_phys(phys_addr_t phys, size_t size) }static inline void kmemleak_alloc_phys (phys_addr_t phys,size_t size, -int min_count,gfp_t gfp) +gfp_t gfp) { }
Signature · 1 line
2.25.1
Re: [PATCH v4 1/4] mm: kmemleak: remove kmemleak_not_leak_phys() and the min_count argument to kmemleak_alloc_phys()
Quoted 6 lines · Patrick Wang
Remove the unused
kmemleak_not_leak_phys()function. And remove themin_countargument tokmemleak_alloc_phys()function, assume it's 0.Suggested-by Catalin Marinas <[email protected]>Signed-off-by Patrick Wang <[email protected]>
PATCH v4 2/4 mm: kmemleak: add OBJECT_PHYS flag for objects allocated with physical address
In reply to Patrick WangAdd OBJECT_PHYS flag for object. This flag is used to identify the objects allocated with physical address. The create_object_phys() function is added as well to set that flag and is used by kmemleak_alloc_phys().
--- mm/kmemleak.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-)
mm/kmemleak.c +31 -9
@@ -172,6 +172,8 @@ struct kmemleak_object {#define OBJECT_NO_SCAN (1 << 2)/* flag set to fully scan the object when scan_area allocation failed */ #define OBJECT_FULL_SCAN (1 << 3) +/* flag set for object allocated with physical address */ +#define OBJECT_PHYS (1 << 4)#define HEX_PREFIX " "/* number of bytes to print per line; must be 16 or 32 */
@@ -574,8 +576,9 @@ static int __save_stack_trace(unsigned long *trace)* Create the metadata (struct kmemleak_object) corresponding to an allocated * memory block and add it to the object_list and object_tree_root. */ -static struct kmemleak_object *create_object (unsigned long ptr,size_t size, -int min_count,gfp_t gfp) +static struct kmemleak_object *__create_object (unsigned long ptr,size_t size, +int min_count,gfp_t gfp, +bool is_phys) {unsigned long flags;struct kmemleak_object *object, *parent;
@@ -595,7 +598,7 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,INIT_HLIST_HEAD (&object->area_list);raw_spin_lock_init (&object->lock);atomic_set (&object->use_count,1 ); - object->flags =OBJECT_ALLOCATED ; + object->flags =OBJECT_ALLOCATED | (is_phys ?OBJECT_PHYS :0 ); object->pointer = ptr; object->size =kfence_ksize ((void *)ptr) ?: size; object->excess_ref =0 ;
@@ -662,6 +665,20 @@ static struct kmemleak_object *create_object(unsigned long ptr, size_t size,return object; } +/* Create kmemleak object which allocated with virtual address. */ +static struct kmemleak_object *create_object (unsigned long ptr,size_t size, +int min_count,gfp_t gfp) +{ +return __create_object (ptr, size, min_count, gfp, false); +} + +/* Create kmemleak object which allocated with physical address. */ +static struct kmemleak_object *create_object_phys (unsigned long ptr,size_t size, +int min_count,gfp_t gfp) +{ +return __create_object (ptr, size, min_count, gfp, true); +} +/* * Mark the object as not allocated and schedule RCU freeing via put_object(). */
@@ -728,11 +745,11 @@ static void delete_object_part(unsigned long ptr, size_t size) start = object->pointer; end = object->pointer + object->size;if (ptr > start) -create_object (start, ptr - start, object->min_count, -GFP_KERNEL ); +__create_object (start, ptr - start, object->min_count, +GFP_KERNEL , object->flags &OBJECT_PHYS );if (ptr + size < end) -create_object (ptr + size, end - ptr - size, object->min_count, -GFP_KERNEL ); +__create_object (ptr + size, end - ptr - size, object->min_count, +GFP_KERNEL , object->flags &OBJECT_PHYS );__delete_object (object); }
@@ -1129,9 +1146,14 @@ EXPORT_SYMBOL(kmemleak_no_scan);*/ void __ref kmemleak_alloc_phys (phys_addr_t phys,size_t size,gfp_t gfp) { +pr_debug ("%s(0x%pa, %zu)\n" , __func__, &phys, size); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -/* assume min_count 0 */ -kmemleak_alloc (__va (phys), size,0 , gfp); +/* +* Create object with OBJECT_PHYS flag and +* assume min_count 0. +*/ +create_object_phys ((unsigned long )__va (phys), size,0 , gfp); }EXPORT_SYMBOL (kmemleak_alloc_phys);
Signature · 1 line
2.25.1
PATCH v4 3/4 mm: kmemleak: add rbtree and store physical address for objects allocated with PA
In reply to Patrick WangAdd object_phys_tree_root to store the objects allocated with physical address. Distinguish it from object_tree_root by OBJECT_PHYS flag or function argument. The physical address is stored directly in those objects.
--- mm/kmemleak.c | 133 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 42 deletions(-)
mm/kmemleak.c +91 -42
@@ -14,14 +14,16 @@* The following locks and mutexes are used by kmemleak: * * - kmemleak_lock (raw_spinlock_t): protects the object_list modifications and -* accesses to the object_tree_root. The object_list is the main list -* holding the metadata (struct kmemleak_object) for the allocated memory -* blocks. The object_tree_root is a red black tree used to look-up -* metadata based on a pointer to the corresponding memory block. The -* kmemleak_object structures are added to the object_list and -* object_tree_root in the create_object() function called from the -* kmemleak_alloc() callback and removed in delete_object() called from the -* kmemleak_free() callback +* accesses to the object_tree_root (or object_phys_tree_root). The +* object_list is the main list holding the metadata (struct kmemleak_object) +* for the allocated memory blocks. The object_tree_root and object_phys_tree_root +* are red black trees used to look-up metadata based on a pointer to the +* corresponding memory block. The object_phys_tree_root is for objects +* allocated with physical address. The kmemleak_object structures are +* added to the object_list and object_tree_root (or object_phys_tree_root) +* in the create_object() function called from the kmemleak_alloc() (or +* kmemleak_alloc_phys()) callback and removed in delete_object() called from +* the kmemleak_free() callback * - kmemleak_object.lock (raw_spinlock_t): protects a kmemleak_object. * Accesses to the metadata (e.g. count) are protected by this lock. Note * that some members of this structure may be protected by other means
@@ -195,7 +197,9 @@ static int mem_pool_free_count = ARRAY_SIZE(mem_pool);static LIST_HEAD (mem_pool_free_list);/* search tree for object boundaries */ static struct rb_root object_tree_root =RB_ROOT ; -/* protecting the access to object_list and object_tree_root */ +/* search tree for object (with OBJECT_PHYS flag) boundaries */ +static struct rb_root object_phys_tree_root =RB_ROOT ; +/* protecting the access to object_list, object_tree_root (or object_phys_tree_root) */ static DEFINE_RAW_SPINLOCK (kmemleak_lock);/* allocation caches for kmemleak internal data */
@@ -287,6 +291,9 @@ static void hex_dump_object(struct seq_file *seq,const u8 *ptr = (const u8 *)object->pointer;size_t len; +if (WARN_ON_ONCE (object->flags &OBJECT_PHYS )) +return ; +/* limit the number of lines to HEX_MAX_LINES */ len =min_t (size_t, object->size,HEX_MAX_LINES *HEX_ROW_SIZE );
@@ -380,9 +387,11 @@ static void dump_object_info(struct kmemleak_object *object)* beginning of the memory block are allowed. The kmemleak_lock must be held * when calling this function. */ -static struct kmemleak_object *lookup_object (unsigned long ptr,int alias) +static struct kmemleak_object *__lookup_object (unsigned long ptr,int alias, +bool is_phys) { -struct rb_node *rb = object_tree_root.rb_node; +struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : + object_tree_root.rb_node;unsigned long untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr);while (rb) {
@@ -408,6 +417,12 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)return NULL ; } +/* Look-up a kmemleak object which allocated with virtual address. */ +static struct kmemleak_object *lookup_object (unsigned long ptr,int alias) +{ +return __lookup_object (ptr, alias, false); +} +/* * Increment the object use_count. Return 1 if successful or 0 otherwise. Note * that once an object's use_count reached 0, the RCU freeing was already
@@ -517,14 +532,15 @@ static void put_object(struct kmemleak_object *object)/* * Look up an object in the object search tree and increase its use_count. */ -static struct kmemleak_object *find_and_get_object (unsigned long ptr,int alias) +static struct kmemleak_object *__find_and_get_object (unsigned long ptr,int alias, +bool is_phys) {unsigned long flags;struct kmemleak_object *object;rcu_read_lock ();raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =__lookup_object (ptr, alias, is_phys);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);/* check whether the object is still available */
@@ -535,28 +551,39 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias)return object; } +/* Look up and get an object which allocated with virtual address. */ +static struct kmemleak_object *find_and_get_object (unsigned long ptr,int alias) +{ +return __find_and_get_object (ptr, alias, false); +} +/* -* Remove an object from the object_tree_root and object_list. Must be called -* with the kmemleak_lock held _if_ kmemleak is still enabled. +* Remove an object from the object_tree_root (or object_phys_tree_root) +* and object_list. Must be called with the kmemleak_lock held _if_ kmemleak +* is still enabled. */ static void __remove_object (struct kmemleak_object *object) { -rb_erase (&object->rb_node, &object_tree_root); +rb_erase (&object->rb_node, object->flags &OBJECT_PHYS ? + &object_phys_tree_root : + &object_tree_root);list_del_rcu (&object->object_list); }/* * Look up an object in the object search tree and remove it from both -* object_tree_root and object_list. The returned object's use_count should be -* at least 1, as initially set by create_object(). +* object_tree_root (or object_phys_tree_root) and object_list. The +* returned object's use_count should be at least 1, as initially set +* by create_object(). */ -static struct kmemleak_object *find_and_remove_object (unsigned long ptr,int alias) +static struct kmemleak_object *find_and_remove_object (unsigned long ptr,int alias, +bool is_phys) {unsigned long flags;struct kmemleak_object *object;raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =__lookup_object (ptr, alias, is_phys);if (object)__remove_object (object);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);
@@ -574,7 +601,8 @@ static int __save_stack_trace(unsigned long *trace)/* * Create the metadata (struct kmemleak_object) corresponding to an allocated -* memory block and add it to the object_list and object_tree_root. +* memory block and add it to the object_list and object_tree_root (or +* object_phys_tree_root). */ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size, int min_count, gfp_t gfp,
@@ -631,9 +659,16 @@ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size,raw_spin_lock_irqsave (&kmemleak_lock, flags); untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); - min_addr =min (min_addr, untagged_ptr); - max_addr =max (max_addr, untagged_ptr + size); - link = &object_tree_root.rb_node; +/* +* Only update min_addr and max_addr with object +* storing virtual address. +*/ +if (!is_phys) { + min_addr =min (min_addr, untagged_ptr); + max_addr =max (max_addr, untagged_ptr + size); + } + link = is_phys ? &object_phys_tree_root.rb_node : + &object_tree_root.rb_node; rb_parent =NULL ;while (*link) { rb_parent = *link;
@@ -657,7 +692,8 @@ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size, } }rb_link_node (&object->rb_node, rb_parent, link); -rb_insert_color (&object->rb_node, &object_tree_root); +rb_insert_color (&object->rb_node, is_phys ? &object_phys_tree_root : + &object_tree_root);list_add_tail_rcu (&object->object_list, &object_list); out:
@@ -707,7 +743,7 @@ static void delete_object_full(unsigned long ptr) {struct kmemleak_object *object; - object =find_and_remove_object (ptr,0 ); + object =find_and_remove_object (ptr,0 , false);if (!object) {#ifdef DEBUG kmemleak_warn("Freeing unknown object at 0x%08lx\n",
@@ -723,12 +759,12 @@ static void delete_object_full(unsigned long ptr)* delete it. If the memory block is partially freed, the function may create * additional metadata for the remaining parts of the block. */ -static void delete_object_part (unsigned long ptr,size_t size) +static void delete_object_part (unsigned long ptr,size_t size,bool is_phys) {struct kmemleak_object *object;unsigned long start, end; - object =find_and_remove_object (ptr,1 ); + object =find_and_remove_object (ptr,1 , is_phys);if (!object) {#ifdef DEBUG kmemleak_warn("Partially freeing unknown object at 0x%08lx (size %zu)\n",
@@ -746,10 +782,10 @@ static void delete_object_part(unsigned long ptr, size_t size) end = object->pointer + object->size;if (ptr > start)__create_object (start, ptr - start, object->min_count, -GFP_KERNEL , object->flags &OBJECT_PHYS ); +GFP_KERNEL , is_phys);if (ptr + size < end)__create_object (ptr + size, end - ptr - size, object->min_count, -GFP_KERNEL , object->flags &OBJECT_PHYS ); +GFP_KERNEL , is_phys);__delete_object (object); }
@@ -770,11 +806,11 @@ static void paint_it(struct kmemleak_object *object, int color)raw_spin_unlock_irqrestore (&object->lock, flags); } -static void paint_ptr (unsigned long ptr,int color) +static void paint_ptr (unsigned long ptr,int color,bool is_phys) {struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =__find_and_get_object (ptr,0 , is_phys);if (!object) { kmemleak_warn("Trying to color unknown object at 0x%08lx as %s\n", ptr,
@@ -792,16 +828,16 @@ static void paint_ptr(unsigned long ptr, int color)*/ static void make_gray_object (unsigned long ptr) { -paint_ptr (ptr,KMEMLEAK_GREY ); +paint_ptr (ptr,KMEMLEAK_GREY , false); }/* * Mark the object as black-colored so that it is ignored from scans and * reporting. */ -static void make_black_object (unsigned long ptr) +static void make_black_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_BLACK ); +paint_ptr (ptr,KMEMLEAK_BLACK , is_phys); }/*
@@ -1007,7 +1043,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -delete_object_part ((unsigned long )ptr, size); +delete_object_part ((unsigned long )ptr, size, false); }EXPORT_SYMBOL_GPL (kmemleak_free_part);
@@ -1095,7 +1131,7 @@ void __ref kmemleak_ignore(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_black_object ((unsigned long )ptr); +make_black_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_ignore);
@@ -1153,7 +1189,7 @@ void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, gfp_t gfp)* Create object with OBJECT_PHYS flag and * assume min_count 0. */ -create_object_phys ((unsigned long )__va (phys), size,0 , gfp); +create_object_phys ((unsigned long )phys, size,0 , gfp); }EXPORT_SYMBOL (kmemleak_alloc_phys);
@@ -1166,8 +1202,10 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);*/ void __ref kmemleak_free_part_phys (phys_addr_t phys,size_t size) { +pr_debug ("%s(0x%pa)\n" , __func__, &phys); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_free_part (__va (phys), size); +delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);
@@ -1178,8 +1216,10 @@ EXPORT_SYMBOL(kmemleak_free_part_phys);*/ void __ref kmemleak_ignore_phys (phys_addr_t phys) { +pr_debug ("%s(0x%pa)\n" , __func__, &phys); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_ignore (__va (phys)); +make_black_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_ignore_phys);
@@ -1190,6 +1230,9 @@ static bool update_checksum(struct kmemleak_object *object) {u32 old_csum = object->checksum; +if (WARN_ON_ONCE (object->flags &OBJECT_PHYS )) +return false; +kasan_disable_current ();kcsan_disable_current (); object->checksum =crc32 (0 ,kasan_reset_tag ((void *)object->pointer), object->size);
@@ -1343,6 +1386,7 @@ static void scan_object(struct kmemleak_object *object) {struct kmemleak_scan_area *area;unsigned long flags; +void *obj_ptr;/* * Once the object->lock is acquired, the corresponding memory block
@@ -1354,10 +1398,15 @@ static void scan_object(struct kmemleak_object *object)if (!(object->flags &OBJECT_ALLOCATED ))/* already freed object */ goto out; + + obj_ptr = object->flags &OBJECT_PHYS ? +__va ((phys_addr_t )object->pointer) : + (void *)object->pointer; +if (hlist_empty (&object->area_list) || object->flags &OBJECT_FULL_SCAN ) { -void *start = (void *)object->pointer; -void *end = (void *)(object->pointer + object->size); +void *start = obj_ptr; +void *end = obj_ptr + object->size;void *next; do {
Signature · 1 line
2.25.1
Re: [PATCH v4 3/4] mm: kmemleak: add rbtree and store physical address for objects allocated with PA
Now we have seperated rb_tree for phys and virts addresses. But why can't we have kmemleak_free_phys()? It may apply the same format to delete_object_full().
Some users would request to remove the kmemleak object from the phys tree but we don't have this one.
Quoted 427 lines · Patrick Wang
Add
object_phys_tree_rootto store the objects allocated with physical address. Distinguish it fromobject_tree_rootbyOBJECT_PHYSflag or function argument. The physical address is stored directly in those objects.Suggested-by Catalin Marinas <[email protected]>Signed-off-by Patrick Wang <[email protected]>Reviewed-by Catalin Marinas <[email protected]>--- mm/kmemleak.c | 133 ++++++++++++++++++++++++++++++++++------------ ---- 1 file changed, 91 insertions(+), 42 deletions(-)mm/kmemleak.c +0
-0@@ -14,14 +14,16 @@* The following locks and mutexes are used by kmemleak: * * - kmemleak_lock (raw_spinlock_t): protects the object_list modifications and
- * accesses to theobject_tree_root. Theobject_listis the main
list
- * holding the metadata (structkmemleak_object) for the allocated
memory
- * blocks. Theobject_tree_rootis a red black tree used to look-
up
- * metadata based on a pointer to the corresponding memory
block. The
- *kmemleak_objectstructures are added to theobject_listand
- *object_tree_rootin thecreate_object()function called from
the
- *kmemleak_alloc()callback and removed indelete_object()called
from the
- *kmemleak_free()callback
+ * accesses to theobject_tree_root(orobject_phys_tree_root).
The
+ *object_listis the main list holding the metadata (structkmemleak_object)
+ * for the allocated memory blocks. Theobject_tree_rootandobject_phys_tree_root
+ * are red black trees used to look-up metadata based on a pointer
to the
+ * corresponding memory block. Theobject_phys_tree_rootis for
objects
+ * allocated with physical address. Thekmemleak_objectstructures
are
+ * added to theobject_listandobject_tree_root(orobject_phys_tree_root)
+ * in thecreate_object()function called from thekmemleak_alloc()(or
+ *kmemleak_alloc_phys()) callback and removed indelete_object()
called from
+ * thekmemleak_free()callback
* - kmemleak_object.lock (raw_spinlock_t): protects akmemleak_object.
* Accesses to the metadata (e.g. count) are protected by this
lock. Note
* that some members of this structure may be protected by other
meansmm/kmemleak.c +3
-1@@ -195,7 +197,9 @@ static int mem_pool_free_count =ARRAY_SIZE (mem_pool);static LIST_HEAD (mem_pool_free_list);/* search tree for object boundaries */ static struct rb_root object_tree_root =RB_ROOT ; -/* protecting the access to object_list and object_tree_root */ +/* search tree for object (with OBJECT_PHYS flag) boundaries */ +static struct rb_root object_phys_tree_root =RB_ROOT ; +/* protecting the access to object_list, object_tree_root (or
object_phys_tree_root) */
staticDEFINE_RAW_SPINLOCK(kmemleak_lock);/* allocation caches for kmemleak internal data */
mm/kmemleak.c +3
-0@@ -287,6 +291,9 @@ static void hex_dump_object(struct seq_file *seq,const u8 *ptr = (const u8 *)object->pointer;size_t len; +if (WARN_ON_ONCE (object->flags &OBJECT_PHYS )) +return ; +/* limit the number of lines to HEX_MAX_LINES */ len =min_t (size_t, object->size,HEX_MAX_LINES *HEX_ROW_SIZE );@@ -380,9 +387,11 @@ static void dump_object_info(struct kmemleak_object *object) * beginning of the memory block are allowed. The kmemleak_lock mustbe held
* when calling this function.
*/
-static structkmemleak_object*lookup_object(unsigned long ptr, int alias)
+static structkmemleak_object*__lookup_object(unsigned long ptr, int alias,mm/kmemleak.c +3
-1+ bool is_phys) { -struct rb_node *rb = object_tree_root.rb_node; +struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : + object_tree_root.rb_node; unsigned long untagged_ptr = (unsignedlong)kasan_reset_tag((void *)ptr);
mm/kmemleak.c +6
-0while (rb) {@@ -408,6 +417,12 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)return NULL ; } +/* Look-up a kmemleak object which allocated with virtual address. */ +static struct kmemleak_object *lookup_object (unsigned long ptr,int alias) +{ +return __lookup_object (ptr, alias, false); +} +/*
* Increment the objectuse_count. Return 1 if successful or 0
otherwise. Note
* that once an object'suse_countreached 0, the RCU freeing was
alreadymm/kmemleak.c +0
-0@@ -517,14 +532,15 @@ static void put_object(struct kmemleak_object *object)/* * Look up an object in the object search tree and increase its
use_count.
*/
-static structkmemleak_object*find_and_get_object(unsigned long ptr, int alias)
+static structkmemleak_object*__find_and_get_object(unsigned long ptr, int alias,mm/kmemleak.c +8
-2+ bool is_phys) {unsigned long flags;struct kmemleak_object *object;rcu_read_lock ();raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =__lookup_object (ptr, alias, is_phys);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);/* check whether the object is still available */ @@ -535,28 +551,39 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias)return object; } +/* Look up and get an object which allocated with virtual address. */ +static struct kmemleak_object *find_and_get_object (unsigned long ptr,int alias) +{ +return __find_and_get_object (ptr, alias, false); +} +/* -* Remove an object from the object_tree_root and object_list. Must be called
- * with thekmemleak_lockheld _if_ kmemleak is still enabled.
+ * Remove an object from theobject_tree_root(orobject_phys_tree_root)
+ * andobject_list. Must be called with thekmemleak_lockheld _if_ kmemleak
+ * is still enabled.
*/
static void__remove_object(struct kmemleak_object *object){mm/kmemleak.c +3
-1-rb_erase (&object->rb_node, &object_tree_root); +rb_erase (&object->rb_node, object->flags &OBJECT_PHYS ? + &object_phys_tree_root : + &object_tree_root);list_del_rcu (&object->object_list); }/* * Look up an object in the object search tree and remove it from both
- *object_tree_rootandobject_list. The returned object'suse_countshould be
- * at least 1, as initially set bycreate_object().
+ *object_tree_root(orobject_phys_tree_root) andobject_list. The
+ * returned object'suse_countshould be at least 1, as initially set
+ * bycreate_object().
*/
-static structkmemleak_object*find_and_remove_object(unsigned long ptr, int alias)
+static structkmemleak_object*find_and_remove_object(unsigned long ptr, int alias,mm/kmemleak.c +2
-1+ bool is_phys) {unsigned long flags;struct kmemleak_object *object;raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =__lookup_object (ptr, alias, is_phys);if (object)__remove_object (object);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);@@ -574,7 +601,8 @@ static int __save_stack_trace(unsigned long *trace)/* * Create the metadata (struct kmemleak_object) corresponding to an allocated
- * memory block and add it to theobject_listandobject_tree_root.
+ * memory block and add it to theobject_listandobject_tree_root(or
+ *object_phys_tree_root).
*/
static structkmemleak_object*__create_object(unsigned long ptr,size_tsize,mm/kmemleak.c +10
-3int min_count, gfp_t gfp,@@ -631,9 +659,16 @@ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size, raw_spin_lock_irqsave(&kmemleak_lock, flags); untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); - min_addr =min (min_addr, untagged_ptr); - max_addr =max (max_addr, untagged_ptr + size); - link = &object_tree_root.rb_node; +/* +* Only update min_addr and max_addr with object +* storing virtual address. +*/ +if (!is_phys) { + min_addr =min (min_addr, untagged_ptr); + max_addr =max (max_addr, untagged_ptr + size); + } + link = is_phys ? &object_phys_tree_root.rb_node : + &object_tree_root.rb_node; rb_parent =NULL ;while (*link) {mm/kmemleak.c +1
-1rb_parent = *link;@@ -657,7 +692,8 @@ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size, } } rb_link_node(&object->rb_node, rb_parent, link); - rb_insert_color(&object->rb_node, &object_tree_root); + rb_insert_color(&object->rb_node, is_phys ?
&object_phys_tree_root:mm/kmemleak.c +2
-1+ &object_tree_root);list_add_tail_rcu (&object->object_list, &object_list); out:@@ -707,7 +743,7 @@ static void delete_object_full(unsigned long ptr) {struct kmemleak_object *object; - object =find_and_remove_object (ptr,0 ); + object =find_and_remove_object (ptr,0 , false);if (!object) {#ifdef DEBUG kmemleak_warn("Freeing unknown object at 0x%08lx\n",@@ -723,12 +759,12 @@ static void delete_object_full(unsigned long ptr) * delete it. If the memory block is partially freed, the functionmay create
* additional metadata for the remaining parts of the block. */mm/kmemleak.c +1
-1-static void delete_object_part (unsigned long ptr,size_t size) +static void delete_object_part(unsigned long ptr, size_t size, bool
is_phys)
{mm/kmemleak.c +1
-1struct kmemleak_object *object;unsigned long start, end; - object =find_and_remove_object (ptr,1 ); + object =find_and_remove_object (ptr,1 , is_phys);if (!object) {#ifdef DEBUG kmemleak_warn("Partially freeing unknown object at0x%08lx (size %zu)\n",
mm/kmemleak.c +2
-2@@ -746,10 +782,10 @@ static void delete_object_part(unsigned long ptr, size_t size) end = object->pointer + object->size;if (ptr > start)__create_object (start, ptr - start, object->min_count, -GFP_KERNEL , object->flags &OBJECT_PHYS ); +GFP_KERNEL , is_phys);if (ptr + size < end)__create_object (ptr + size, end - ptr - size, object- >min_count, -GFP_KERNEL , object->flags &OBJECT_PHYS ); +GFP_KERNEL , is_phys);mm/kmemleak.c +2
-2__delete_object (object); }@@ -770,11 +806,11 @@ static void paint_it(struct kmemleak_object *object, int color)raw_spin_unlock_irqrestore (&object->lock, flags); } -static void paint_ptr (unsigned long ptr,int color) +static void paint_ptr (unsigned long ptr,int color,bool is_phys) {struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =__find_and_get_object (ptr,0 , is_phys);if (!object) { kmemleak_warn("Trying to color unknown object at0x%08lx as %s\n",
mm/kmemleak.c +1
-1ptr,@@ -792,16 +828,16 @@ static void paint_ptr(unsigned long ptr, intcolor) */ static void make_gray_object (unsigned long ptr) { -paint_ptr (ptr,KMEMLEAK_GREY ); +paint_ptr (ptr,KMEMLEAK_GREY , false); }/* * Mark the object as black-colored so that it is ignored from scans and
* reporting.
*/mm/kmemleak.c +5
-5-static void make_black_object (unsigned long ptr) +static void make_black_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_BLACK ); +paint_ptr (ptr,KMEMLEAK_BLACK , is_phys); }/* @@ -1007,7 +1043,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size) pr_debug("%s(0x%p)\n", __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -delete_object_part ((unsigned long )ptr, size); +delete_object_part ((unsigned long )ptr, size, false); }EXPORT_SYMBOL_GPL (kmemleak_free_part);@@ -1095,7 +1131,7 @@ void __ref kmemleak_ignore(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_black_object ((unsigned long )ptr); +make_black_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_ignore);@@ -1153,7 +1189,7 @@ void __ref kmemleak_alloc_phys(phys_addr_tphys, size_t size, gfp_t gfp) * Create object with OBJECT_PHYS flag and * assume min_count 0. */ -create_object_phys ((unsigned long )__va (phys), size,0 , gfp); +create_object_phys ((unsigned long )phys, size,0 , gfp); }
EXPORT_SYMBOL(kmemleak_alloc_phys);mm/kmemleak.c +9
-2@@ -1166,8 +1202,10 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);*/ void __ref kmemleak_free_part_phys (phys_addr_t phys,size_t size) { +pr_debug ("%s(0x%pa)\n" , __func__, &phys); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_free_part (__va (phys), size); +delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);@@ -1178,8 +1216,10 @@ EXPORT_SYMBOL(kmemleak_free_part_phys);*/ void __ref kmemleak_ignore_phys (phys_addr_t phys) { +pr_debug ("%s(0x%pa)\n" , __func__, &phys); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_ignore (__va (phys)); +make_black_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_ignore_phys);@@ -1190,6 +1230,9 @@ static bool update_checksum(struct kmemleak_object *object) {u32 old_csum = object->checksum; +if (WARN_ON_ONCE (object->flags &OBJECT_PHYS )) +return false; +kasan_disable_current ();kcsan_disable_current ();
object->checksum= crc32(0, kasan_reset_tag((void *)object-Quoted 1 line
pointer),
object->size);mm/kmemleak.c +1
-0@@ -1343,6 +1386,7 @@ static void scan_object(struct kmemleak_object *object) {struct kmemleak_scan_area *area;unsigned long flags; +void *obj_ptr;/* * Once the
object->lockis acquired, the corresponding memory
blockmm/kmemleak.c +7
-2@@ -1354,10 +1398,15 @@ static void scan_object(struct kmemleak_object *object)if (!(object->flags &OBJECT_ALLOCATED ))/* already freed object */ goto out; + + obj_ptr = object->flags &OBJECT_PHYS ? +__va ((phys_addr_t )object->pointer) : + (void *)object->pointer; +if (hlist_empty (&object->area_list) || object->flags &OBJECT_FULL_SCAN ) { -void *start = (void *)object->pointer; -void *end = (void *)(object->pointer + object->size); +void *start = obj_ptr; +void *end = obj_ptr + object->size;void *next;do {
Re: [PATCH v4 3/4] mm: kmemleak: add rbtree and store physical address for objects allocated with PA
Quoted 6 lines · Yee Lee
Now we have seperated
rb_treefor phys and virts addresses. But why can't we havekmemleak_free_phys()? It may apply the same format todelete_object_full().Some users would request to remove the kmemleak object from the phys tree but we don't have this one.
Please check this, an issue happened at kfence with the latest kmemleak patches. kfence pool allocated memory from memblock but have no way to free it from the phys tree.
https://lkml.org/lkml/2022/6/23/486
Quoted 454 lines · Patrick Wang
Quoted 452 lines
Add
object_phys_tree_rootto store the objects allocated with physical address. Distinguish it fromobject_tree_rootbyOBJECT_PHYSflag or function argument. The physical address is stored directly in those objects.Suggested-by Catalin Marinas <[email protected]>Signed-off-by Patrick Wang <[email protected]>Reviewed-by Catalin Marinas <[email protected]>--- mm/kmemleak.c | 133 ++++++++++++++++++++++++++++++++++------------ ---- 1 file changed, 91 insertions(+), 42 deletions(-)mm/kmemleak.c +0
-0@@ -14,14 +14,16 @@* The following locks and mutexes are used by kmemleak: * * - kmemleak_lock (raw_spinlock_t): protects the object_list modifications and
- * accesses to theobject_tree_root. Theobject_listis the main
list
- * holding the metadata (structkmemleak_object) for the
allocated
memory
- * blocks. Theobject_tree_rootis a red black tree used to
look-
up
- * metadata based on a pointer to the corresponding memory
block. The
- *kmemleak_objectstructures are added to theobject_listand
- *object_tree_rootin thecreate_object()function called from
the
- *kmemleak_alloc()callback and removed indelete_object()
called
from the
- *kmemleak_free()callback
+ * accesses to theobject_tree_root(orobject_phys_tree_root).
The
+ *object_listis the main list holding the metadata (structkmemleak_object)
+ * for the allocated memory blocks. Theobject_tree_rootandobject_phys_tree_root
+ * are red black trees used to look-up metadata based on a
pointer
to the
+ * corresponding memory block. Theobject_phys_tree_rootis for
objects
+ * allocated with physical address. Thekmemleak_object
structures
are
+ * added to theobject_listandobject_tree_root(orobject_phys_tree_root)
+ * in thecreate_object()function called from thekmemleak_alloc()(or
+ *kmemleak_alloc_phys()) callback and removed indelete_object()
called from
+ * thekmemleak_free()callback
* - kmemleak_object.lock (raw_spinlock_t): protects akmemleak_object.
* Accesses to the metadata (e.g. count) are protected by this
lock. Note
* that some members of this structure may be protected by other
meansmm/kmemleak.c +3
-1@@ -195,7 +197,9 @@ static int mem_pool_free_count =ARRAY_SIZE (mem_pool);static LIST_HEAD (mem_pool_free_list);/* search tree for object boundaries */ static struct rb_root object_tree_root =RB_ROOT ; -/* protecting the access to object_list and object_tree_root */ +/* search tree for object (with OBJECT_PHYS flag) boundaries */ +static struct rb_root object_phys_tree_root =RB_ROOT ; +/* protecting the access to object_list, object_tree_root (or
object_phys_tree_root) */
staticDEFINE_RAW_SPINLOCK(kmemleak_lock);/* allocation caches for kmemleak internal data */
mm/kmemleak.c +3
-0@@ -287,6 +291,9 @@ static void hex_dump_object(struct seq_file *seq, const u8 *ptr = (const u8 *)object->pointer;size_t len; +if (WARN_ON_ONCE (object->flags &OBJECT_PHYS )) +return ; +/* limit the number of lines to HEX_MAX_LINES */ len = min_t(size_t, object->size, HEX_MAX_LINES *
HEX_ROW_SIZE);mm/kmemleak.c +0
-0@@ -380,9 +387,11 @@ static void dump_object_info(struct kmemleak_object *object) * beginning of the memory block are allowed. The kmemleak_lockmust
be held
* when calling this function.
*/
-static structkmemleak_object*lookup_object(unsigned long ptr, int
alias)
+static structkmemleak_object*__lookup_object(unsigned long ptr, int alias,mm/kmemleak.c +3
-1+ bool is_phys) { -struct rb_node *rb = object_tree_root.rb_node; +struct rb_node *rb = is_phys ? object_phys_tree_root.rb_node : + object_tree_root.rb_node; unsigned long untagged_ptr = (unsignedlong)kasan_reset_tag((void *)ptr);
mm/kmemleak.c +2
-0while (rb) {@@ -408,6 +417,12 @@ static struct kmemleak_object *lookup_object(unsigned long ptr, int alias)return NULL ; } +/* Look-up a kmemleak object which allocated with virtual address. */ +static struct kmemleak_object *lookup_object(unsigned long ptr,int
alias)mm/kmemleak.c +4
-0+{ +return __lookup_object (ptr, alias, false); +} +/* * Increment the object use_count. Return 1 if successful or 0 otherwise. Note
* that once an object'suse_countreached 0, the RCU freeing was
alreadymm/kmemleak.c +0
-0@@ -517,14 +532,15 @@ static void put_object(struct kmemleak_object *object)/* * Look up an object in the object search tree and increase its
use_count.
*/
-static structkmemleak_object*find_and_get_object(unsigned long ptr, int alias)
+static structkmemleak_object*__find_and_get_object(unsigned long ptr, int alias,mm/kmemleak.c +8
-2+ bool is_phys) {unsigned long flags;struct kmemleak_object *object;rcu_read_lock ();raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =__lookup_object (ptr, alias, is_phys);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);/* check whether the object is still available */ @@ -535,28 +551,39 @@ static struct kmemleak_object *find_and_get_object(unsigned long ptr, int alias)return object; } +/* Look up and get an object which allocated with virtual address. */ +static struct kmemleak_object *find_and_get_object (unsigned long ptr,int alias) +{ +return __find_and_get_object (ptr, alias, false); +} +/* -* Remove an object from the object_tree_root and object_list. Must
be called
- * with thekmemleak_lockheld _if_ kmemleak is still enabled.
+ * Remove an object from theobject_tree_root(orobject_phys_tree_root)
+ * andobject_list. Must be called with thekmemleak_lockheld _if_
kmemleak
+ * is still enabled.
*/
static void__remove_object(struct kmemleak_object *object){mm/kmemleak.c +3
-1-rb_erase (&object->rb_node, &object_tree_root); +rb_erase (&object->rb_node, object->flags &OBJECT_PHYS ? + &object_phys_tree_root : + &object_tree_root);list_del_rcu (&object->object_list); }/* * Look up an object in the object search tree and remove it from both
- *object_tree_rootandobject_list. The returned object'suse_count
should be
- * at least 1, as initially set bycreate_object().
+ *object_tree_root(orobject_phys_tree_root) andobject_list. The
+ * returned object'suse_countshould be at least 1, as initially set
+ * bycreate_object().
*/
-static structkmemleak_object*find_and_remove_object(unsigned long
ptr, int alias)
+static structkmemleak_object*find_and_remove_object(unsigned long
ptr, int alias,mm/kmemleak.c +2
-1+ bool is_phys) {unsigned long flags;struct kmemleak_object *object;raw_spin_lock_irqsave (&kmemleak_lock, flags); - object =lookup_object (ptr, alias); + object =__lookup_object (ptr, alias, is_phys);if (object)__remove_object (object);raw_spin_unlock_irqrestore (&kmemleak_lock, flags);@@ -574,7 +601,8 @@ static int __save_stack_trace(unsigned long *trace)/* * Create the metadata (struct kmemleak_object) corresponding to an
allocated
- * memory block and add it to theobject_listandobject_tree_root.
+ * memory block and add it to theobject_listandobject_tree_root(or
+ *object_phys_tree_root).
*/
static structkmemleak_object*__create_object(unsigned long ptr,size_tsize,mm/kmemleak.c +10
-3int min_count, gfp_t gfp,@@ -631,9 +659,16 @@ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size, raw_spin_lock_irqsave(&kmemleak_lock, flags); untagged_ptr = (unsigned long )kasan_reset_tag ((void *)ptr); - min_addr =min (min_addr, untagged_ptr); - max_addr =max (max_addr, untagged_ptr + size); - link = &object_tree_root.rb_node; +/* +* Only update min_addr and max_addr with object +* storing virtual address. +*/ +if (!is_phys) { + min_addr =min (min_addr, untagged_ptr); + max_addr =max (max_addr, untagged_ptr + size); + } + link = is_phys ? &object_phys_tree_root.rb_node : + &object_tree_root.rb_node; rb_parent =NULL ;while (*link) {mm/kmemleak.c +1
-1rb_parent = *link;@@ -657,7 +692,8 @@ static struct kmemleak_object *__create_object(unsigned long ptr, size_t size, } } rb_link_node(&object->rb_node, rb_parent, link); - rb_insert_color(&object->rb_node, &object_tree_root); + rb_insert_color(&object->rb_node, is_phys ?
&object_phys_tree_root:mm/kmemleak.c +2
-1+ &object_tree_root);list_add_tail_rcu (&object->object_list, &object_list); out:@@ -707,7 +743,7 @@ static void delete_object_full(unsigned long ptr) {struct kmemleak_object *object; - object =find_and_remove_object (ptr,0 ); + object =find_and_remove_object (ptr,0 , false);if (!object) {#ifdef DEBUG mm/kmemleak.c +0
-0kmemleak_warn("Freeing unknown object at 0x%08lx\n",@@ -723,12 +759,12 @@ static void delete_object_full(unsigned long ptr) * delete it. If the memory block is partially freed, the functionmay create
* additional metadata for the remaining parts of the block. */mm/kmemleak.c +1
-1-static void delete_object_part (unsigned long ptr,size_t size) +static void delete_object_part(unsigned long ptr, size_t size,bool
is_phys)
{mm/kmemleak.c +1
-1struct kmemleak_object *object;unsigned long start, end; - object =find_and_remove_object (ptr,1 ); + object =find_and_remove_object (ptr,1 , is_phys);if (!object) {#ifdef DEBUG kmemleak_warn("Partially freeing unknown object at0x%08lx (size %zu)\n",
mm/kmemleak.c +2
-2@@ -746,10 +782,10 @@ static void delete_object_part(unsigned long ptr, size_t size) end = object->pointer + object->size;if (ptr > start)__create_object (start, ptr - start, object->min_count, -GFP_KERNEL , object->flags &OBJECT_PHYS ); +GFP_KERNEL , is_phys);if (ptr + size < end)__create_object (ptr + size, end - ptr - size, object- > min_count, -GFP_KERNEL , object->flags &OBJECT_PHYS ); +GFP_KERNEL , is_phys);mm/kmemleak.c +2
-2__delete_object (object); }@@ -770,11 +806,11 @@ static void paint_it(struct kmemleak_object *object, int color)raw_spin_unlock_irqrestore (&object->lock, flags); } -static void paint_ptr (unsigned long ptr,int color) +static void paint_ptr (unsigned long ptr,int color,bool is_phys) {struct kmemleak_object *object; - object =find_and_get_object (ptr,0 ); + object =__find_and_get_object (ptr,0 , is_phys);if (!object) { kmemleak_warn("Trying to color unknown object at0x%08lx as %s\n",
mm/kmemleak.c +1
-1ptr,@@ -792,16 +828,16 @@ static void paint_ptr(unsigned long ptr, intcolor) */ static void make_gray_object (unsigned long ptr) { -paint_ptr (ptr,KMEMLEAK_GREY ); +paint_ptr (ptr,KMEMLEAK_GREY , false); }/* * Mark the object as black-colored so that it is ignored from scans
and
* reporting.
*/mm/kmemleak.c +3
-3-static void make_black_object (unsigned long ptr) +static void make_black_object (unsigned long ptr,bool is_phys) { -paint_ptr (ptr,KMEMLEAK_BLACK ); +paint_ptr (ptr,KMEMLEAK_BLACK , is_phys); }/* @@ -1007,7 +1043,7 @@ void __ref kmemleak_free_part(const void *ptr, size_t size)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -delete_object_part ((unsigned long )ptr, size); +delete_object_part ((unsigned long )ptr, size, false); }
EXPORT_SYMBOL_GPL(kmemleak_free_part);mm/kmemleak.c +2
-2@@ -1095,7 +1131,7 @@ void __ref kmemleak_ignore(const void *ptr)pr_debug ("%s(0x%p)\n" , __func__, ptr);if (kmemleak_enabled && ptr && !IS_ERR (ptr)) -make_black_object ((unsigned long )ptr); +make_black_object ((unsigned long )ptr, false); }EXPORT_SYMBOL (kmemleak_ignore);@@ -1153,7 +1189,7 @@ void __ref kmemleak_alloc_phys(phys_addr_tphys, size_t size, gfp_t gfp) * Create object with OBJECT_PHYS flag and * assume min_count 0. */ -create_object_phys ((unsigned long )__va (phys), size,0 , gfp); +create_object_phys ((unsigned long )phys, size,0 , gfp); }
EXPORT_SYMBOL(kmemleak_alloc_phys);mm/kmemleak.c +9
-2@@ -1166,8 +1202,10 @@ EXPORT_SYMBOL(kmemleak_alloc_phys);*/ void __ref kmemleak_free_part_phys (phys_addr_t phys,size_t size) { +pr_debug ("%s(0x%pa)\n" , __func__, &phys); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_free_part (__va (phys), size); +delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);@@ -1178,8 +1216,10 @@ EXPORT_SYMBOL(kmemleak_free_part_phys);*/ void __ref kmemleak_ignore_phys (phys_addr_t phys) { +pr_debug ("%s(0x%pa)\n" , __func__, &phys); +if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) -kmemleak_ignore (__va (phys)); +make_black_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_ignore_phys);@@ -1190,6 +1230,9 @@ static bool update_checksum(struct kmemleak_object *object) {u32 old_csum = object->checksum; +if (WARN_ON_ONCE (object->flags &OBJECT_PHYS )) +return false; +kasan_disable_current ();kcsan_disable_current ();
object->checksum= crc32(0, kasan_reset_tag((void *)object-Quoted 1 line
pointer),
object->size);mm/kmemleak.c +0
-0@@ -1343,6 +1386,7 @@ static void scan_object(struct
kmemleak_object
*object)
{mm/kmemleak.c +1
-0struct kmemleak_scan_area *area;unsigned long flags; +void *obj_ptr;/* * Once the object->lock is acquired, the corresponding memory block
mm/kmemleak.c +7
-2@@ -1354,10 +1398,15 @@ static void scan_object(struct kmemleak_object *object)if (!(object->flags &OBJECT_ALLOCATED ))/* already freed object */ goto out; + + obj_ptr = object->flags &OBJECT_PHYS ? +__va ((phys_addr_t )object->pointer) : + (void *)object->pointer; +if (hlist_empty (&object->area_list) || object->flags &OBJECT_FULL_SCAN ) { -void *start = (void *)object->pointer; -void *end = (void *)(object->pointer + object->size); +void *start = obj_ptr; +void *end = obj_ptr + object->size;void *next;do {
Catalin Marinas
maintainerRe: [PATCH v4 3/4] mm: kmemleak: add rbtree and store physical address for objects allocated with PA
Quoted 5 lines · Yee Lee
Quoted 4 lines · Yee Lee
Now we have seperated
rb_treefor phys and virts addresses. But why can't we havekmemleak_free_phys()? It may apply the same format todelete_object_full().
Quoted 7 lines
Quoted 2 lines
Some users would request to remove the kmemleak object from the phys tree but we don't have this one.
Please check this, an issue happened at kfence with the latest kmemleak patches. kfence pool allocated memory from memblock but have no way to free it from the phys tree.
https://lkml.org/lkml/2022/6/23/486
I don't think I was cc'ed on the other thread but at a quick look, what you probably want is:
kmemleak_ignore_phys(__kfence_pool);
instead of the current kmemleak_free(). With Patrick's changes, you can no longer tell kmemleak about an object with a physical address and free it with the virtual one.
Signature · 1 line
Catalin
Re: [PATCH v4 3/4] mm: kmemleak: add rbtree and store physical address for objects allocated with PA
In reply to Yee LeeQuoted 5 lines · Yee Lee
Quoted 4 lines · Yee Lee
Now we have seperated
rb_treefor phys and virts addresses. But why can't we havekmemleak_free_phys()? It may apply the same format todelete_object_full().
Quoted 7 lines
Quoted 2 lines
Some users would request to remove the kmemleak object from the phys tree but we don't have this one.
Please check this, an issue happened at kfence with the latest kmemleak patches. kfence pool allocated memory from memblock but have no way to free it from the phys tree.
https://lkml.org/lkml/2022/6/23/486
Hi Yee,
Thanks for your information. Similar situation appears in percpu.c (address allocated with memblock, object freed with kmemleak_free(), if I didn't miss others). Kmemleak_ignore_phys() could replace kmemleak_free() for physical objects like Catalin said. And adding kmemleak_free_phys() might not be essential, because there are few places that meet the above situation.
Thanks,
Patrick
PATCH v4 4/4 mm: kmemleak: check physical address when scan
In reply to Patrick WangCheck the physical address of objects for its boundary when scan instead of in kmemleak_*_phys().
--- mm/kmemleak.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
mm/kmemleak.c +14 -3
@@ -1184,7 +1184,7 @@ void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, gfp_t gfp) {pr_debug ("%s(0x%pa, %zu)\n" , __func__, &phys, size); -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) +if (kmemleak_enabled)/* * Create object with OBJECT_PHYS flag and * assume min_count 0.
@@ -1204,7 +1204,7 @@ void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size) {pr_debug ("%s(0x%pa)\n" , __func__, &phys); -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) +if (kmemleak_enabled)delete_object_part ((unsigned long )phys, size, true); }EXPORT_SYMBOL (kmemleak_free_part_phys);
@@ -1218,7 +1218,7 @@ void __ref kmemleak_ignore_phys(phys_addr_t phys) {pr_debug ("%s(0x%pa)\n" , __func__, &phys); -if (PHYS_PFN (phys) >= min_low_pfn &&PHYS_PFN (phys) < max_low_pfn) +if (kmemleak_enabled)make_black_object ((unsigned long )phys, true); }EXPORT_SYMBOL (kmemleak_ignore_phys);
@@ -1493,6 +1493,17 @@ static void kmemleak_scan(void) dump_object_info(object); }#endif + +/* ignore objects outside lowmem (paint them black) */ +if ((object->flags &OBJECT_PHYS ) && + !(object->flags &OBJECT_NO_SCAN )) { +unsigned long phys = object->pointer; + +if (PHYS_PFN (phys) < min_low_pfn || +PHYS_PFN (phys + object->size) >= max_low_pfn) +__paint_it (object,KMEMLEAK_BLACK ); + } +/* reset the reference count (whiten the object) */ object->count =0 ;if (color_gray (object) &&get_object (object))
Signature · 1 line
2.25.1
Hi Patrick,
Quoted 9 lines · Patrick Wang
The kmemleak_*_phys() interface uses "
min_low_pfn" and "max_low_pfn" to check address. But on some architectures, kmemleak_*_phys() is called before those two variables initialized. The following steps will be taken:1) Add
OBJECT_PHYSflag and rbtree for the objects allocated with physical address
2) Store physical address in objects if allocated withOBJECT_PHYS
3) Check the boundary when scan instead of in kmemleak_*_phys()
Thanks for your series!
Quoted 3 lines
This patch set will solve:
https://lore.kernel.org/r/[email protected]
https://lore.kernel.org/r/[email protected]
Thanks, this finally gets rid of the thousands of suspected memory leaks reported since commit 23c2d497de21f258 ("mm: kmemleak: take a full lowmem check in kmemleak_*_phys()") in v5.18-rc3 on my arm64 boards.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds