drm: Track drm_mm allocators and show leaks on shutdown
We can use the kernel's stack tracer and depot to record the allocation site of every drm_mm user. Then on shutdown, as well as warning that allocated nodes still reside with the drm_mm range manager, we can display who allocated them to aide tracking down the leak. v2: Move Kconfig around so it lies underneath the DRM options submenu. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Reviewed-by: Christian König <christian.koenig@amd.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: http://patchwork.freedesktop.org/patch/msgid/20161031090806.20073-1-chris@chris-wilson.co.uk
This commit is contained in:
Родитель
55edf41b69
Коммит
5705670d04
|
@ -33,6 +33,19 @@ config DRM_DP_AUX_CHARDEV
|
||||||
read and write values to arbitrary DPCD registers on the DP aux
|
read and write values to arbitrary DPCD registers on the DP aux
|
||||||
channel.
|
channel.
|
||||||
|
|
||||||
|
config DRM_DEBUG_MM
|
||||||
|
bool "Insert extra checks and debug info into the DRM range managers"
|
||||||
|
default n
|
||||||
|
depends on DRM
|
||||||
|
select STACKDEPOT
|
||||||
|
help
|
||||||
|
Enable allocation tracking of memory manager and leak detection on
|
||||||
|
shutdown.
|
||||||
|
|
||||||
|
Recommended for driver developers only.
|
||||||
|
|
||||||
|
If in doubt, say "N".
|
||||||
|
|
||||||
config DRM_KMS_HELPER
|
config DRM_KMS_HELPER
|
||||||
tristate
|
tristate
|
||||||
depends on DRM
|
depends on DRM
|
||||||
|
|
|
@ -104,6 +104,66 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
|
||||||
u64 end,
|
u64 end,
|
||||||
enum drm_mm_search_flags flags);
|
enum drm_mm_search_flags flags);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DRM_DEBUG_MM
|
||||||
|
#define STACKDEPTH 32
|
||||||
|
#define BUFSZ 4096
|
||||||
|
|
||||||
|
static noinline void save_stack(struct drm_mm_node *node)
|
||||||
|
{
|
||||||
|
unsigned long entries[STACKDEPTH];
|
||||||
|
struct stack_trace trace = {
|
||||||
|
.entries = entries,
|
||||||
|
.max_entries = STACKDEPTH,
|
||||||
|
.skip = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
save_stack_trace(&trace);
|
||||||
|
if (trace.nr_entries != 0 &&
|
||||||
|
trace.entries[trace.nr_entries-1] == ULONG_MAX)
|
||||||
|
trace.nr_entries--;
|
||||||
|
|
||||||
|
/* May be called under spinlock, so avoid sleeping */
|
||||||
|
node->stack = depot_save_stack(&trace, GFP_NOWAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_leaks(struct drm_mm *mm)
|
||||||
|
{
|
||||||
|
struct drm_mm_node *node;
|
||||||
|
unsigned long entries[STACKDEPTH];
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
buf = kmalloc(BUFSZ, GFP_KERNEL);
|
||||||
|
if (!buf)
|
||||||
|
return;
|
||||||
|
|
||||||
|
list_for_each_entry(node, &mm->head_node.node_list, node_list) {
|
||||||
|
struct stack_trace trace = {
|
||||||
|
.entries = entries,
|
||||||
|
.max_entries = STACKDEPTH
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!node->stack) {
|
||||||
|
DRM_ERROR("node [%08llx + %08llx]: unknown owner\n",
|
||||||
|
node->start, node->size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
depot_fetch_stack(node->stack, &trace);
|
||||||
|
snprint_stack_trace(buf, BUFSZ, &trace, 0);
|
||||||
|
DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s",
|
||||||
|
node->start, node->size, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef STACKDEPTH
|
||||||
|
#undef BUFSZ
|
||||||
|
#else
|
||||||
|
static void save_stack(struct drm_mm_node *node) { }
|
||||||
|
static void show_leaks(struct drm_mm *mm) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
#define START(node) ((node)->start)
|
#define START(node) ((node)->start)
|
||||||
#define LAST(node) ((node)->start + (node)->size - 1)
|
#define LAST(node) ((node)->start + (node)->size - 1)
|
||||||
|
|
||||||
|
@ -228,6 +288,8 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
|
||||||
list_add(&node->hole_stack, &mm->hole_stack);
|
list_add(&node->hole_stack, &mm->hole_stack);
|
||||||
node->hole_follows = 1;
|
node->hole_follows = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
save_stack(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,6 +355,8 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
|
||||||
node->hole_follows = 1;
|
node->hole_follows = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
save_stack(node);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(drm_mm_reserve_node);
|
EXPORT_SYMBOL(drm_mm_reserve_node);
|
||||||
|
@ -397,6 +461,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
|
||||||
list_add(&node->hole_stack, &mm->hole_stack);
|
list_add(&node->hole_stack, &mm->hole_stack);
|
||||||
node->hole_follows = 1;
|
node->hole_follows = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
save_stack(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -861,10 +927,12 @@ EXPORT_SYMBOL(drm_mm_init);
|
||||||
* Note that it is a bug to call this function on an allocator which is not
|
* Note that it is a bug to call this function on an allocator which is not
|
||||||
* clean.
|
* clean.
|
||||||
*/
|
*/
|
||||||
void drm_mm_takedown(struct drm_mm * mm)
|
void drm_mm_takedown(struct drm_mm *mm)
|
||||||
{
|
{
|
||||||
WARN(!list_empty(&mm->head_node.node_list),
|
if (WARN(!list_empty(&mm->head_node.node_list),
|
||||||
"Memory manager not clean during takedown.\n");
|
"Memory manager not clean during takedown.\n"))
|
||||||
|
show_leaks(mm);
|
||||||
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(drm_mm_takedown);
|
EXPORT_SYMBOL(drm_mm_takedown);
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_DRM_DEBUG_MM
|
||||||
|
#include <linux/stackdepot.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
enum drm_mm_search_flags {
|
enum drm_mm_search_flags {
|
||||||
DRM_MM_SEARCH_DEFAULT = 0,
|
DRM_MM_SEARCH_DEFAULT = 0,
|
||||||
|
@ -74,6 +77,9 @@ struct drm_mm_node {
|
||||||
u64 size;
|
u64 size;
|
||||||
u64 __subtree_last;
|
u64 __subtree_last;
|
||||||
struct drm_mm *mm;
|
struct drm_mm *mm;
|
||||||
|
#ifdef CONFIG_DRM_DEBUG_MM
|
||||||
|
depot_stack_handle_t stack;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct drm_mm {
|
struct drm_mm {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче