Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5705670d authored by Chris Wilson's avatar Chris Wilson Committed by Daniel Vetter
Browse files

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: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarChristian König <christian.koenig@amd.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20161031090806.20073-1-chris@chris-wilson.co.uk
parent 55edf41b
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -33,6 +33,19 @@ config DRM_DP_AUX_CHARDEV
	  read and write values to arbitrary DPCD registers on the DP aux
	  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
	tristate
	depends on DRM
+71 −3
Original line number Diff line number Diff line
@@ -104,6 +104,66 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
						u64 end,
						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 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);
		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;
	}

	save_stack(node);

	return 0;
}
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);
		node->hole_follows = 1;
	}

	save_stack(node);
}

/**
@@ -863,8 +929,10 @@ EXPORT_SYMBOL(drm_mm_init);
 */
void drm_mm_takedown(struct drm_mm *mm)
{
	WARN(!list_empty(&mm->head_node.node_list),
	     "Memory manager not clean during takedown.\n");
	if (WARN(!list_empty(&mm->head_node.node_list),
		 "Memory manager not clean during takedown.\n"))
		show_leaks(mm);

}
EXPORT_SYMBOL(drm_mm_takedown);

+6 −0
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@
#ifdef CONFIG_DEBUG_FS
#include <linux/seq_file.h>
#endif
#ifdef CONFIG_DRM_DEBUG_MM
#include <linux/stackdepot.h>
#endif

enum drm_mm_search_flags {
	DRM_MM_SEARCH_DEFAULT =		0,
@@ -74,6 +77,9 @@ struct drm_mm_node {
	u64 size;
	u64 __subtree_last;
	struct drm_mm *mm;
#ifdef CONFIG_DRM_DEBUG_MM
	depot_stack_handle_t stack;
#endif
};

struct drm_mm {