Skip to content

Commit

Permalink
drm/i915: Replace custom intel runtime_pm tracker with ref_tracker li…
Browse files Browse the repository at this point in the history
…brary

Beside reusing existing code, the main advantage of ref_tracker is
tracking per instance of wakeref. It allows also to catch double
put.
On the other side we lose information about the first acquire and
the last release, but the advantages outweigh it.

Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20231030-ref_tracker_i915-v1-1-006fe6b96421@intel.com
  • Loading branch information
Andrzej Hajda committed Nov 20, 2023
1 parent dfed6b5 commit b49e894
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 217 deletions.
4 changes: 4 additions & 0 deletions drivers/gpu/drm/i915/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ config DRM_I915_DEBUG
select DEBUG_FS
select PREEMPT_COUNT
select I2C_CHARDEV
select REF_TRACKER
select STACKDEPOT
select STACKTRACE
select DRM_DP_AUX_CHARDEV
select X86_MSR # used by igt/pm_rpm
select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
Expand Down Expand Up @@ -231,7 +233,9 @@ config DRM_I915_DEBUG_RUNTIME_PM
bool "Enable extra state checking for runtime PM"
depends on DRM_I915
default n
select REF_TRACKER
select STACKDEPOT
select STACKTRACE
help
Choose this option to turn on extra state checking for the
runtime PM functionality. This may introduce overhead during
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/i915/display/intel_display_power.c
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ print_async_put_domains_state(struct i915_power_domains *power_domains)
struct drm_i915_private,
display.power.domains);

drm_dbg(&i915->drm, "async_put_wakeref %u\n",
drm_dbg(&i915->drm, "async_put_wakeref %lu\n",
power_domains->async_put_wakeref);

print_power_domains(power_domains, "async_put_domains[0]",
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/i915/i915_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
intel_power_domains_driver_remove(i915);
enable_rpm_wakeref_asserts(&i915->runtime_pm);

intel_runtime_pm_driver_release(&i915->runtime_pm);
intel_runtime_pm_driver_last_release(&i915->runtime_pm);
}

static bool suspend_to_idle(struct drm_i915_private *dev_priv)
Expand Down
221 changes: 17 additions & 204 deletions drivers/gpu/drm/i915/intel_runtime_pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,243 +52,52 @@

#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)

#include <linux/sort.h>

#define STACKDEPTH 8

static noinline depot_stack_handle_t __save_depot_stack(void)
{
unsigned long entries[STACKDEPTH];
unsigned int n;

n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
return stack_depot_save(entries, n, GFP_NOWAIT | __GFP_NOWARN);
}

static void init_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
{
spin_lock_init(&rpm->debug.lock);
stack_depot_init();
ref_tracker_dir_init(&rpm->debug, INTEL_REFTRACK_DEAD_COUNT, dev_name(rpm->kdev));
}

static noinline depot_stack_handle_t
static intel_wakeref_t
track_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
{
depot_stack_handle_t stack, *stacks;
unsigned long flags;

if (rpm->no_wakeref_tracking)
return -1;

stack = __save_depot_stack();
if (!stack)
if (!rpm->available || rpm->no_wakeref_tracking)
return -1;

spin_lock_irqsave(&rpm->debug.lock, flags);

if (!rpm->debug.count)
rpm->debug.last_acquire = stack;

stacks = krealloc(rpm->debug.owners,
(rpm->debug.count + 1) * sizeof(*stacks),
GFP_NOWAIT | __GFP_NOWARN);
if (stacks) {
stacks[rpm->debug.count++] = stack;
rpm->debug.owners = stacks;
} else {
stack = -1;
}

spin_unlock_irqrestore(&rpm->debug.lock, flags);

return stack;
return intel_ref_tracker_alloc(&rpm->debug);
}

static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
depot_stack_handle_t stack)
intel_wakeref_t wakeref)
{
struct drm_i915_private *i915 = container_of(rpm,
struct drm_i915_private,
runtime_pm);
unsigned long flags, n;
bool found = false;

if (unlikely(stack == -1))
if (!rpm->available || rpm->no_wakeref_tracking)
return;

spin_lock_irqsave(&rpm->debug.lock, flags);
for (n = rpm->debug.count; n--; ) {
if (rpm->debug.owners[n] == stack) {
memmove(rpm->debug.owners + n,
rpm->debug.owners + n + 1,
(--rpm->debug.count - n) * sizeof(stack));
found = true;
break;
}
}
spin_unlock_irqrestore(&rpm->debug.lock, flags);

if (drm_WARN(&i915->drm, !found,
"Unmatched wakeref (tracking %lu), count %u\n",
rpm->debug.count, atomic_read(&rpm->wakeref_count))) {
char *buf;

buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
if (!buf)
return;

stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
DRM_DEBUG_DRIVER("wakeref %x from\n%s", stack, buf);

stack = READ_ONCE(rpm->debug.last_release);
if (stack) {
stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
DRM_DEBUG_DRIVER("wakeref last released at\n%s", buf);
}

kfree(buf);
}
intel_ref_tracker_free(&rpm->debug, wakeref);
}

static int cmphandle(const void *_a, const void *_b)
static void untrack_all_intel_runtime_pm_wakerefs(struct intel_runtime_pm *rpm)
{
const depot_stack_handle_t * const a = _a, * const b = _b;

if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else
return 0;
}

static void
__print_intel_runtime_pm_wakeref(struct drm_printer *p,
const struct intel_runtime_pm_debug *dbg)
{
unsigned long i;
char *buf;

buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
if (!buf)
return;

if (dbg->last_acquire) {
stack_depot_snprint(dbg->last_acquire, buf, PAGE_SIZE, 2);
drm_printf(p, "Wakeref last acquired:\n%s", buf);
}

if (dbg->last_release) {
stack_depot_snprint(dbg->last_release, buf, PAGE_SIZE, 2);
drm_printf(p, "Wakeref last released:\n%s", buf);
}

drm_printf(p, "Wakeref count: %lu\n", dbg->count);

sort(dbg->owners, dbg->count, sizeof(*dbg->owners), cmphandle, NULL);

for (i = 0; i < dbg->count; i++) {
depot_stack_handle_t stack = dbg->owners[i];
unsigned long rep;

rep = 1;
while (i + 1 < dbg->count && dbg->owners[i + 1] == stack)
rep++, i++;
stack_depot_snprint(stack, buf, PAGE_SIZE, 2);
drm_printf(p, "Wakeref x%lu taken at:\n%s", rep, buf);
}

kfree(buf);
}

static noinline void
__untrack_all_wakerefs(struct intel_runtime_pm_debug *debug,
struct intel_runtime_pm_debug *saved)
{
*saved = *debug;

debug->owners = NULL;
debug->count = 0;
debug->last_release = __save_depot_stack();
}

static void
dump_and_free_wakeref_tracking(struct intel_runtime_pm_debug *debug)
{
if (debug->count) {
struct drm_printer p = drm_debug_printer("i915");

__print_intel_runtime_pm_wakeref(&p, debug);
}

kfree(debug->owners);
ref_tracker_dir_exit(&rpm->debug);
}

static noinline void
__intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
{
struct intel_runtime_pm_debug dbg = {};
unsigned long flags;

if (!atomic_dec_and_lock_irqsave(&rpm->wakeref_count,
&rpm->debug.lock,
flags))
return;

__untrack_all_wakerefs(&rpm->debug, &dbg);
ref_tracker_dir_print_locked(&rpm->debug, INTEL_REFTRACK_PRINT_LIMIT);
spin_unlock_irqrestore(&rpm->debug.lock, flags);

dump_and_free_wakeref_tracking(&dbg);
}

static noinline void
untrack_all_intel_runtime_pm_wakerefs(struct intel_runtime_pm *rpm)
{
struct intel_runtime_pm_debug dbg = {};
unsigned long flags;

spin_lock_irqsave(&rpm->debug.lock, flags);
__untrack_all_wakerefs(&rpm->debug, &dbg);
spin_unlock_irqrestore(&rpm->debug.lock, flags);

dump_and_free_wakeref_tracking(&dbg);
}

void print_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
struct drm_printer *p)
{
struct intel_runtime_pm_debug dbg = {};

do {
unsigned long alloc = dbg.count;
depot_stack_handle_t *s;

spin_lock_irq(&rpm->debug.lock);
dbg.count = rpm->debug.count;
if (dbg.count <= alloc) {
memcpy(dbg.owners,
rpm->debug.owners,
dbg.count * sizeof(*s));
}
dbg.last_acquire = rpm->debug.last_acquire;
dbg.last_release = rpm->debug.last_release;
spin_unlock_irq(&rpm->debug.lock);
if (dbg.count <= alloc)
break;

s = krealloc(dbg.owners,
dbg.count * sizeof(*s),
GFP_NOWAIT | __GFP_NOWARN);
if (!s)
goto out;

dbg.owners = s;
} while (1);

__print_intel_runtime_pm_wakeref(p, &dbg);

out:
kfree(dbg.owners);
intel_ref_tracker_show(&rpm->debug, p);
}

#else
Expand All @@ -297,14 +106,14 @@ static void init_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
{
}

static depot_stack_handle_t
static intel_wakeref_t
track_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm)
{
return -1;
}

static void untrack_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
intel_wakeref_t wref)
intel_wakeref_t wakeref)
{
}

Expand Down Expand Up @@ -639,7 +448,11 @@ void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
"i915 raw-wakerefs=%d wakelocks=%d on cleanup\n",
intel_rpm_raw_wakeref_count(count),
intel_rpm_wakelock_count(count));
}

void intel_runtime_pm_driver_last_release(struct intel_runtime_pm *rpm)
{
intel_runtime_pm_driver_release(rpm);
untrack_all_intel_runtime_pm_wakerefs(rpm);
}

Expand Down
11 changes: 2 additions & 9 deletions drivers/gpu/drm/i915/intel_runtime_pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,7 @@ struct intel_runtime_pm {
* paired rpm_put) we can remove corresponding pairs of and keep
* the array trimmed to active wakerefs.
*/
struct intel_runtime_pm_debug {
spinlock_t lock;

depot_stack_handle_t last_acquire;
depot_stack_handle_t last_release;

depot_stack_handle_t *owners;
unsigned long count;
} debug;
struct ref_tracker_dir debug;
#endif
};

Expand Down Expand Up @@ -189,6 +181,7 @@ void intel_runtime_pm_init_early(struct intel_runtime_pm *rpm);
void intel_runtime_pm_enable(struct intel_runtime_pm *rpm);
void intel_runtime_pm_disable(struct intel_runtime_pm *rpm);
void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm);
void intel_runtime_pm_driver_last_release(struct intel_runtime_pm *rpm);

intel_wakeref_t intel_runtime_pm_get(struct intel_runtime_pm *rpm);
intel_wakeref_t intel_runtime_pm_get_if_in_use(struct intel_runtime_pm *rpm);
Expand Down
28 changes: 28 additions & 0 deletions drivers/gpu/drm/i915/intel_wakeref.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,31 @@ void intel_wakeref_auto_fini(struct intel_wakeref_auto *wf)
intel_wakeref_auto(wf, 0);
INTEL_WAKEREF_BUG_ON(wf->wakeref);
}

void intel_ref_tracker_show(struct ref_tracker_dir *dir,
struct drm_printer *p)
{
const size_t buf_size = PAGE_SIZE;
char *buf, *sb, *se;
size_t count;

buf = kmalloc(buf_size, GFP_NOWAIT);
if (!buf)
return;

count = ref_tracker_dir_snprint(dir, buf, buf_size);
if (!count)
goto free;
/* printk does not like big buffers, so we split it */
for (sb = buf; *sb; sb = se + 1) {
se = strchrnul(sb, '\n');
drm_printf(p, "%.*s", (int)(se - sb + 1), sb);
if (!*se)
break;
}
if (count >= buf_size)
drm_printf(p, "\n...dropped %zd extra bytes of leak report.\n",
count + 1 - buf_size);
free:
kfree(buf);
}
Loading

0 comments on commit b49e894

Please sign in to comment.