[1/3] drm/i915: Separate wakeref tracking
Commit Message
From: Chris Wilson <chris.p.wilson@intel.com>
Extract the callstack tracking of intel_runtime_pm.c into its own
utility so that that we can reuse it for other online debugging of
scoped wakerefs.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
drivers/gpu/drm/i915/Kconfig.debug | 9 +
drivers/gpu/drm/i915/Makefile | 4 +
drivers/gpu/drm/i915/intel_runtime_pm.c | 244 +++------------------------
drivers/gpu/drm/i915/intel_runtime_pm.h | 10 +-
drivers/gpu/drm/i915/intel_wakeref.h | 6 +-
drivers/gpu/drm/i915/intel_wakeref_tracker.c | 234 +++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_wakeref_tracker.h | 76 +++++++++
7 files changed, 355 insertions(+), 228 deletions(-)
Comments
On Fri, 24 Feb 2023, Andrzej Hajda <andrzej.hajda@intel.com> wrote:
> From: Chris Wilson <chris.p.wilson@intel.com>
>
> Extract the callstack tracking of intel_runtime_pm.c into its own
> utility so that that we can reuse it for other online debugging of
> scoped wakerefs.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
> ---
> drivers/gpu/drm/i915/Kconfig.debug | 9 +
> drivers/gpu/drm/i915/Makefile | 4 +
> drivers/gpu/drm/i915/intel_runtime_pm.c | 244 +++------------------------
> drivers/gpu/drm/i915/intel_runtime_pm.h | 10 +-
> drivers/gpu/drm/i915/intel_wakeref.h | 6 +-
> drivers/gpu/drm/i915/intel_wakeref_tracker.c | 234 +++++++++++++++++++++++++
> drivers/gpu/drm/i915/intel_wakeref_tracker.h | 76 +++++++++
> 7 files changed, 355 insertions(+), 228 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
> index 93dfb7ed970547..5fde52107e3b44 100644
> --- a/drivers/gpu/drm/i915/Kconfig.debug
> +++ b/drivers/gpu/drm/i915/Kconfig.debug
> @@ -25,6 +25,7 @@ config DRM_I915_DEBUG
> select PREEMPT_COUNT
> select I2C_CHARDEV
> 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)
> @@ -37,6 +38,7 @@ config DRM_I915_DEBUG
> select DRM_I915_DEBUG_GEM
> select DRM_I915_DEBUG_GEM_ONCE
> select DRM_I915_DEBUG_MMIO
> + select DRM_I915_TRACK_WAKEREF
> select DRM_I915_DEBUG_RUNTIME_PM
> select DRM_I915_SW_FENCE_DEBUG_OBJECTS
> select DRM_I915_SELFTEST
> @@ -227,11 +229,18 @@ config DRM_I915_DEBUG_VBLANK_EVADE
>
> If in doubt, say "N".
>
> +config DRM_I915_TRACK_WAKEREF
> + depends on STACKDEPOT
> + depends on STACKTRACE
> + bool
> +
> config DRM_I915_DEBUG_RUNTIME_PM
> bool "Enable extra state checking for runtime PM"
> depends on DRM_I915
> default n
> select STACKDEPOT
> + select STACKTRACE
> + select DRM_I915_TRACK_WAKEREF
> help
> Choose this option to turn on extra state checking for the
> runtime PM functionality. This may introduce overhead during
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index b2f91a1f826858..42daff6d575a82 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -81,6 +81,10 @@ i915-$(CONFIG_DEBUG_FS) += \
> i915_debugfs_params.o \
> display/intel_display_debugfs.o \
> display/intel_pipe_crc.o
> +
> +i915-$(CONFIG_DRM_I915_TRACK_WAKEREF) += \
> + intel_wakeref_tracker.o
> +
> i915-$(CONFIG_PERF_EVENTS) += i915_pmu.o
>
> # "Graphics Technology" (aka we talk to the gpu)
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index 129746713d072f..72887e2bb03c21 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -52,182 +52,37 @@
>
> #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();
> + intel_wakeref_tracker_init(&rpm->debug);
> }
>
> -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)
> 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_wakeref_tracker_add(&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))
> - 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_wakeref_tracker_remove(&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;
> + struct drm_printer p = drm_debug_printer("i915");
>
> - 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);
> + intel_wakeref_tracker_reset(&rpm->debug, &p);
> }
>
> static noinline void
> __intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
> {
> - struct intel_runtime_pm_debug dbg = {};
> + struct intel_wakeref_tracker saved;
> unsigned long flags;
>
> if (!atomic_dec_and_lock_irqsave(&rpm->wakeref_count,
> @@ -235,60 +90,21 @@ __intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
> flags))
> return;
>
> - __untrack_all_wakerefs(&rpm->debug, &dbg);
> + saved = __intel_wakeref_tracker_reset(&rpm->debug);
> 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);
> + if (saved.count) {
> + struct drm_printer p = drm_debug_printer("i915");
>
> - dump_and_free_wakeref_tracking(&dbg);
> + __intel_wakeref_tracker_show(&saved, &p);
> + intel_wakeref_tracker_fini(&saved);
> + }
> }
>
> 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_wakeref_tracker_show(&rpm->debug, p);
> }
>
> #else
> @@ -297,14 +113,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)
> {
> }
>
> @@ -349,9 +165,8 @@ intel_runtime_pm_release(struct intel_runtime_pm *rpm, int wakelock)
> static intel_wakeref_t __intel_runtime_pm_get(struct intel_runtime_pm *rpm,
> bool wakelock)
> {
> - struct drm_i915_private *i915 = container_of(rpm,
> - struct drm_i915_private,
> - runtime_pm);
> + struct drm_i915_private *i915 =
> + container_of(rpm, struct drm_i915_private, runtime_pm);
> int ret;
>
> ret = pm_runtime_get_sync(rpm->kdev);
> @@ -556,9 +371,8 @@ void intel_runtime_pm_put(struct intel_runtime_pm *rpm, intel_wakeref_t wref)
> */
> void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
> {
> - struct drm_i915_private *i915 = container_of(rpm,
> - struct drm_i915_private,
> - runtime_pm);
> + struct drm_i915_private *i915 =
> + container_of(rpm, struct drm_i915_private, runtime_pm);
> struct device *kdev = rpm->kdev;
>
> /*
> @@ -611,9 +425,8 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
>
> void intel_runtime_pm_disable(struct intel_runtime_pm *rpm)
> {
> - struct drm_i915_private *i915 = container_of(rpm,
> - struct drm_i915_private,
> - runtime_pm);
> + struct drm_i915_private *i915 =
> + container_of(rpm, struct drm_i915_private, runtime_pm);
> struct device *kdev = rpm->kdev;
>
> /* Transfer rpm ownership back to core */
> @@ -628,9 +441,8 @@ void intel_runtime_pm_disable(struct intel_runtime_pm *rpm)
>
> void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
> {
> - struct drm_i915_private *i915 = container_of(rpm,
> - struct drm_i915_private,
> - runtime_pm);
> + struct drm_i915_private *i915 =
> + container_of(rpm, struct drm_i915_private, runtime_pm);
> int count = atomic_read(&rpm->wakeref_count);
>
> intel_wakeref_auto_fini(&rpm->userfault_wakeref);
> @@ -646,7 +458,7 @@ void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
> void intel_runtime_pm_init_early(struct intel_runtime_pm *rpm)
> {
> struct drm_i915_private *i915 =
> - container_of(rpm, struct drm_i915_private, runtime_pm);
> + container_of(rpm, struct drm_i915_private, runtime_pm);
Lots of unrelated changes above that should be separate patches.
> struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
> struct device *kdev = &pdev->dev;
>
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.h b/drivers/gpu/drm/i915/intel_runtime_pm.h
> index e592e8d6499a1f..a8dc2baf79844f 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.h
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.h
> @@ -83,15 +83,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 intel_wakeref_tracker debug;
There's a lot going on in the patch all around. Adding the struct to a
separate file could maybe be an individual patch to simplify the actual
changes.
This doesn't include the file that defines struct intel_wakeref_tracker;
it's included via intel_wakeref.h. But only if
> #endif
> };
>
> diff --git a/drivers/gpu/drm/i915/intel_wakeref.h b/drivers/gpu/drm/i915/intel_wakeref.h
> index 71b8a63f6f104d..20720fbcc28d46 100644
> --- a/drivers/gpu/drm/i915/intel_wakeref.h
> +++ b/drivers/gpu/drm/i915/intel_wakeref.h
> @@ -17,7 +17,9 @@
> #include <linux/timer.h>
> #include <linux/workqueue.h>
>
> -#if IS_ENABLED(CONFIG_DRM_I915_DEBUG)
> +#include "intel_wakeref_tracker.h"
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_WAKEREF)
> #define INTEL_WAKEREF_BUG_ON(expr) BUG_ON(expr)
> #else
> #define INTEL_WAKEREF_BUG_ON(expr) BUILD_BUG_ON_INVALID(expr)
> @@ -26,8 +28,6 @@
> struct intel_runtime_pm;
> struct intel_wakeref;
>
> -typedef depot_stack_handle_t intel_wakeref_t;
> -
> struct intel_wakeref_ops {
> int (*get)(struct intel_wakeref *wf);
> int (*put)(struct intel_wakeref *wf);
> diff --git a/drivers/gpu/drm/i915/intel_wakeref_tracker.c b/drivers/gpu/drm/i915/intel_wakeref_tracker.c
> new file mode 100644
> index 00000000000000..a0bcef13a1085a
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_wakeref_tracker.c
> @@ -0,0 +1,234 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2021 Intel Corporation
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/stackdepot.h>
> +#include <linux/stacktrace.h>
> +#include <linux/sort.h>
> +
> +#include <drm/drm_print.h>
> +
> +#include "intel_wakeref.h"
This should really include the corresponding .h
i.e. intel_wakeref_tracker.h too. Now it gets included via
intel_wakeref.h but I'm not sure why.
> +
> +#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 __print_depot_stack(depot_stack_handle_t stack,
> + char *buf, int sz, int indent)
> +{
> + unsigned long *entries;
> + unsigned int nr_entries;
> +
> + nr_entries = stack_depot_fetch(stack, &entries);
> + stack_trace_snprint(buf, sz, entries, nr_entries, indent);
> +}
> +
> +static int cmphandle(const void *_a, const void *_b)
> +{
> + 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;
> +}
> +
> +void
> +__intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w,
> + struct drm_printer *p)
> +{
> + unsigned long i;
> + char *buf;
> +
> + buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
> + if (!buf)
> + return;
> +
> + if (w->last_acquire) {
> + __print_depot_stack(w->last_acquire, buf, PAGE_SIZE, 2);
> + drm_printf(p, "Wakeref last acquired:\n%s", buf);
> + }
> +
> + if (w->last_release) {
> + __print_depot_stack(w->last_release, buf, PAGE_SIZE, 2);
> + drm_printf(p, "Wakeref last released:\n%s", buf);
> + }
> +
> + drm_printf(p, "Wakeref count: %lu\n", w->count);
> +
> + sort(w->owners, w->count, sizeof(*w->owners), cmphandle, NULL);
> +
> + for (i = 0; i < w->count; i++) {
> + depot_stack_handle_t stack = w->owners[i];
> + unsigned long rep;
> +
> + rep = 1;
> + while (i + 1 < w->count && w->owners[i + 1] == stack)
> + rep++, i++;
> + __print_depot_stack(stack, buf, PAGE_SIZE, 2);
> + drm_printf(p, "Wakeref x%lu taken at:\n%s", rep, buf);
> + }
> +
> + kfree(buf);
> +}
> +
> +void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w,
> + struct drm_printer *p)
> +{
> + struct intel_wakeref_tracker tmp = {};
> +
> + do {
> + unsigned long alloc = tmp.count;
> + depot_stack_handle_t *s;
> +
> + spin_lock_irq(&w->lock);
> + tmp.count = w->count;
> + if (tmp.count <= alloc)
> + memcpy(tmp.owners, w->owners, tmp.count * sizeof(*s));
> + tmp.last_acquire = w->last_acquire;
> + tmp.last_release = w->last_release;
> + spin_unlock_irq(&w->lock);
> + if (tmp.count <= alloc)
> + break;
> +
> + s = krealloc(tmp.owners,
> + tmp.count * sizeof(*s),
> + GFP_NOWAIT | __GFP_NOWARN);
> + if (!s)
> + goto out;
> +
> + tmp.owners = s;
> + } while (1);
> +
> + __intel_wakeref_tracker_show(&tmp, p);
> +
> +out:
> + intel_wakeref_tracker_fini(&tmp);
> +}
> +
> +intel_wakeref_t intel_wakeref_tracker_add(struct intel_wakeref_tracker *w)
> +{
> + depot_stack_handle_t stack, *stacks;
> + unsigned long flags;
> +
> + stack = __save_depot_stack();
> + if (!stack)
> + return -1;
> +
> + spin_lock_irqsave(&w->lock, flags);
> +
> + if (!w->count)
> + w->last_acquire = stack;
> +
> + stacks = krealloc(w->owners,
> + (w->count + 1) * sizeof(*stacks),
> + GFP_NOWAIT | __GFP_NOWARN);
> + if (stacks) {
> + stacks[w->count++] = stack;
> + w->owners = stacks;
> + } else {
> + stack = -1;
> + }
> +
> + spin_unlock_irqrestore(&w->lock, flags);
> +
> + return stack;
> +}
> +
> +void intel_wakeref_tracker_remove(struct intel_wakeref_tracker *w,
> + intel_wakeref_t stack)
> +{
> + unsigned long flags, n;
> + bool found = false;
> +
> + if (unlikely(stack == -1))
> + return;
> +
> + spin_lock_irqsave(&w->lock, flags);
> + for (n = w->count; n--; ) {
> + if (w->owners[n] == stack) {
> + memmove(w->owners + n,
> + w->owners + n + 1,
> + (--w->count - n) * sizeof(stack));
> + found = true;
> + break;
> + }
> + }
> + spin_unlock_irqrestore(&w->lock, flags);
> +
> + if (WARN(!found,
> + "Unmatched wakeref %x, tracking %lu\n",
> + stack, w->count)) {
> + char *buf;
> +
> + buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
> + if (!buf)
> + return;
> +
> + __print_depot_stack(stack, buf, PAGE_SIZE, 2);
> + pr_err("wakeref %x from\n%s", stack, buf);
> +
> + stack = READ_ONCE(w->last_release);
> + if (stack && !w->count) {
> + __print_depot_stack(stack, buf, PAGE_SIZE, 2);
> + pr_err("wakeref last released at\n%s", buf);
> + }
> +
> + kfree(buf);
> + }
> +}
> +
> +struct intel_wakeref_tracker
> +__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w)
> +{
> + struct intel_wakeref_tracker saved;
> +
> + lockdep_assert_held(&w->lock);
> +
> + saved = *w;
> +
> + w->owners = NULL;
> + w->count = 0;
> + w->last_release = __save_depot_stack();
> +
> + return saved;
> +}
> +
> +void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
> + struct drm_printer *p)
> +{
> + struct intel_wakeref_tracker tmp;
> +
> + spin_lock_irq(&w->lock);
> + tmp = __intel_wakeref_tracker_reset(w);
> + spin_unlock_irq(&w->lock);
> +
> + if (tmp.count)
> + __intel_wakeref_tracker_show(&tmp, p);
> +
> + intel_wakeref_tracker_fini(&tmp);
> +}
> +
> +void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w)
> +{
> + memset(w, 0, sizeof(*w));
> + spin_lock_init(&w->lock);
> + stack_depot_init();
> +}
> +
> +void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w)
> +{
> + kfree(w->owners);
> +}
> diff --git a/drivers/gpu/drm/i915/intel_wakeref_tracker.h b/drivers/gpu/drm/i915/intel_wakeref_tracker.h
> new file mode 100644
> index 00000000000000..61df68e28c0fbf
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_wakeref_tracker.h
> @@ -0,0 +1,76 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2019 Intel Corporation
> + */
> +
> +#ifndef INTEL_WAKEREF_TRACKER_H
> +#define INTEL_WAKEREF_TRACKER_H
> +
> +#include <linux/kconfig.h>
> +#include <linux/spinlock.h>
> +#include <linux/stackdepot.h>
> +
> +typedef depot_stack_handle_t intel_wakeref_t;
> +
> +struct drm_printer;
> +
> +struct intel_wakeref_tracker {
> + spinlock_t lock;
> +
> + depot_stack_handle_t last_acquire;
> + depot_stack_handle_t last_release;
> +
> + depot_stack_handle_t *owners;
> + unsigned long count;
> +};
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_TRACK_WAKEREF)
> +
> +void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w);
> +void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w);
> +
> +intel_wakeref_t intel_wakeref_tracker_add(struct intel_wakeref_tracker *w);
> +void intel_wakeref_tracker_remove(struct intel_wakeref_tracker *w,
> + intel_wakeref_t handle);
> +
> +struct intel_wakeref_tracker
> +__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w);
> +void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
> + struct drm_printer *p);
> +
> +void __intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w,
> + struct drm_printer *p);
> +void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w,
> + struct drm_printer *p);
> +
> +#else
> +
> +static inline void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w) {}
> +static inline void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w) {}
> +
> +static inline intel_wakeref_t
> +intel_wakeref_tracker_add(struct intel_wakeref_tracker *w)
> +{
> + return -1;
> +}
> +
> +static inline void
> +intel_wakeref_untrack_remove(struct intel_wakeref_tracker *w, intel_wakeref_t handle) {}
> +
> +static inline struct intel_wakeref_tracker
> +__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w)
> +{
> + return (struct intel_wakeref_tracker){};
> +}
> +
> +static inline void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
> + struct drm_printer *p)
> +{
> +}
> +
> +static inline void __intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w, struct drm_printer *p) {}
> +static inline void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w, struct drm_printer *p) {}
> +
> +#endif
> +
> +#endif /* INTEL_WAKEREF_TRACKER_H */
On 27.02.2023 12:50, Jani Nikula wrote:
> On Fri, 24 Feb 2023, Andrzej Hajda <andrzej.hajda@intel.com> wrote:
>> From: Chris Wilson <chris.p.wilson@intel.com>
>>
>> Extract the callstack tracking of intel_runtime_pm.c into its own
>> utility so that that we can reuse it for other online debugging of
>> scoped wakerefs.
>>
>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>> Signed-off-by: Andrzej Hajda <andrzej.hajda@intel.com>
>> ---
>> drivers/gpu/drm/i915/Kconfig.debug | 9 +
>> drivers/gpu/drm/i915/Makefile | 4 +
>> drivers/gpu/drm/i915/intel_runtime_pm.c | 244 +++------------------------
>> drivers/gpu/drm/i915/intel_runtime_pm.h | 10 +-
>> drivers/gpu/drm/i915/intel_wakeref.h | 6 +-
>> drivers/gpu/drm/i915/intel_wakeref_tracker.c | 234 +++++++++++++++++++++++++
>> drivers/gpu/drm/i915/intel_wakeref_tracker.h | 76 +++++++++
>> 7 files changed, 355 insertions(+), 228 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
>> index 93dfb7ed970547..5fde52107e3b44 100644
>> --- a/drivers/gpu/drm/i915/Kconfig.debug
>> +++ b/drivers/gpu/drm/i915/Kconfig.debug
>> @@ -25,6 +25,7 @@ config DRM_I915_DEBUG
>> select PREEMPT_COUNT
>> select I2C_CHARDEV
>> 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)
>> @@ -37,6 +38,7 @@ config DRM_I915_DEBUG
>> select DRM_I915_DEBUG_GEM
>> select DRM_I915_DEBUG_GEM_ONCE
>> select DRM_I915_DEBUG_MMIO
>> + select DRM_I915_TRACK_WAKEREF
>> select DRM_I915_DEBUG_RUNTIME_PM
>> select DRM_I915_SW_FENCE_DEBUG_OBJECTS
>> select DRM_I915_SELFTEST
>> @@ -227,11 +229,18 @@ config DRM_I915_DEBUG_VBLANK_EVADE
>>
>> If in doubt, say "N".
>>
>> +config DRM_I915_TRACK_WAKEREF
>> + depends on STACKDEPOT
>> + depends on STACKTRACE
>> + bool
>> +
>> config DRM_I915_DEBUG_RUNTIME_PM
>> bool "Enable extra state checking for runtime PM"
>> depends on DRM_I915
>> default n
>> select STACKDEPOT
>> + select STACKTRACE
>> + select DRM_I915_TRACK_WAKEREF
>> help
>> Choose this option to turn on extra state checking for the
>> runtime PM functionality. This may introduce overhead during
>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>> index b2f91a1f826858..42daff6d575a82 100644
>> --- a/drivers/gpu/drm/i915/Makefile
>> +++ b/drivers/gpu/drm/i915/Makefile
>> @@ -81,6 +81,10 @@ i915-$(CONFIG_DEBUG_FS) += \
>> i915_debugfs_params.o \
>> display/intel_display_debugfs.o \
>> display/intel_pipe_crc.o
>> +
>> +i915-$(CONFIG_DRM_I915_TRACK_WAKEREF) += \
>> + intel_wakeref_tracker.o
>> +
>> i915-$(CONFIG_PERF_EVENTS) += i915_pmu.o
>>
>> # "Graphics Technology" (aka we talk to the gpu)
>> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
>> index 129746713d072f..72887e2bb03c21 100644
>> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
>> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
>> @@ -52,182 +52,37 @@
>>
>> #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();
>> + intel_wakeref_tracker_init(&rpm->debug);
>> }
>>
>> -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)
>> 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_wakeref_tracker_add(&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))
>> - 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_wakeref_tracker_remove(&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;
>> + struct drm_printer p = drm_debug_printer("i915");
>>
>> - 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);
>> + intel_wakeref_tracker_reset(&rpm->debug, &p);
>> }
>>
>> static noinline void
>> __intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
>> {
>> - struct intel_runtime_pm_debug dbg = {};
>> + struct intel_wakeref_tracker saved;
>> unsigned long flags;
>>
>> if (!atomic_dec_and_lock_irqsave(&rpm->wakeref_count,
>> @@ -235,60 +90,21 @@ __intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
>> flags))
>> return;
>>
>> - __untrack_all_wakerefs(&rpm->debug, &dbg);
>> + saved = __intel_wakeref_tracker_reset(&rpm->debug);
>> 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);
>> + if (saved.count) {
>> + struct drm_printer p = drm_debug_printer("i915");
>>
>> - dump_and_free_wakeref_tracking(&dbg);
>> + __intel_wakeref_tracker_show(&saved, &p);
>> + intel_wakeref_tracker_fini(&saved);
>> + }
>> }
>>
>> 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_wakeref_tracker_show(&rpm->debug, p);
>> }
>>
>> #else
>> @@ -297,14 +113,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)
>> {
>> }
>>
>> @@ -349,9 +165,8 @@ intel_runtime_pm_release(struct intel_runtime_pm *rpm, int wakelock)
>> static intel_wakeref_t __intel_runtime_pm_get(struct intel_runtime_pm *rpm,
>> bool wakelock)
>> {
>> - struct drm_i915_private *i915 = container_of(rpm,
>> - struct drm_i915_private,
>> - runtime_pm);
>> + struct drm_i915_private *i915 =
>> + container_of(rpm, struct drm_i915_private, runtime_pm);
>> int ret;
>>
>> ret = pm_runtime_get_sync(rpm->kdev);
>> @@ -556,9 +371,8 @@ void intel_runtime_pm_put(struct intel_runtime_pm *rpm, intel_wakeref_t wref)
>> */
>> void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
>> {
>> - struct drm_i915_private *i915 = container_of(rpm,
>> - struct drm_i915_private,
>> - runtime_pm);
>> + struct drm_i915_private *i915 =
>> + container_of(rpm, struct drm_i915_private, runtime_pm);
>> struct device *kdev = rpm->kdev;
>>
>> /*
>> @@ -611,9 +425,8 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
>>
>> void intel_runtime_pm_disable(struct intel_runtime_pm *rpm)
>> {
>> - struct drm_i915_private *i915 = container_of(rpm,
>> - struct drm_i915_private,
>> - runtime_pm);
>> + struct drm_i915_private *i915 =
>> + container_of(rpm, struct drm_i915_private, runtime_pm);
>> struct device *kdev = rpm->kdev;
>>
>> /* Transfer rpm ownership back to core */
>> @@ -628,9 +441,8 @@ void intel_runtime_pm_disable(struct intel_runtime_pm *rpm)
>>
>> void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
>> {
>> - struct drm_i915_private *i915 = container_of(rpm,
>> - struct drm_i915_private,
>> - runtime_pm);
>> + struct drm_i915_private *i915 =
>> + container_of(rpm, struct drm_i915_private, runtime_pm);
>> int count = atomic_read(&rpm->wakeref_count);
>>
>> intel_wakeref_auto_fini(&rpm->userfault_wakeref);
>> @@ -646,7 +458,7 @@ void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
>> void intel_runtime_pm_init_early(struct intel_runtime_pm *rpm)
>> {
>> struct drm_i915_private *i915 =
>> - container_of(rpm, struct drm_i915_private, runtime_pm);
>> + container_of(rpm, struct drm_i915_private, runtime_pm);
>
> Lots of unrelated changes above that should be separate patches.
OK
>
>> struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
>> struct device *kdev = &pdev->dev;
>>
>> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.h b/drivers/gpu/drm/i915/intel_runtime_pm.h
>> index e592e8d6499a1f..a8dc2baf79844f 100644
>> --- a/drivers/gpu/drm/i915/intel_runtime_pm.h
>> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.h
>> @@ -83,15 +83,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 intel_wakeref_tracker debug;
>
> There's a lot going on in the patch all around. Adding the struct to a
> separate file could maybe be an individual patch to simplify the actual
> changes.
Hmm, the patch abstracts out wakeref_tracker from rpm to separate
'library'. In this context the change above looks quite natural and
atomic for me.
>
> This doesn't include the file that defines struct intel_wakeref_tracker;
> it's included via intel_wakeref.h. But only if
>
>> #endif
>> };
>>
>> diff --git a/drivers/gpu/drm/i915/intel_wakeref.h b/drivers/gpu/drm/i915/intel_wakeref.h
>> index 71b8a63f6f104d..20720fbcc28d46 100644
>> --- a/drivers/gpu/drm/i915/intel_wakeref.h
>> +++ b/drivers/gpu/drm/i915/intel_wakeref.h
>> @@ -17,7 +17,9 @@
>> #include <linux/timer.h>
>> #include <linux/workqueue.h>
>>
>> -#if IS_ENABLED(CONFIG_DRM_I915_DEBUG)
>> +#include "intel_wakeref_tracker.h"
>> +
>> +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_WAKEREF)
>> #define INTEL_WAKEREF_BUG_ON(expr) BUG_ON(expr)
>> #else
>> #define INTEL_WAKEREF_BUG_ON(expr) BUILD_BUG_ON_INVALID(expr)
>> @@ -26,8 +28,6 @@
>> struct intel_runtime_pm;
>> struct intel_wakeref;
>>
>> -typedef depot_stack_handle_t intel_wakeref_t;
>> -
>> struct intel_wakeref_ops {
>> int (*get)(struct intel_wakeref *wf);
>> int (*put)(struct intel_wakeref *wf);
>> diff --git a/drivers/gpu/drm/i915/intel_wakeref_tracker.c b/drivers/gpu/drm/i915/intel_wakeref_tracker.c
>> new file mode 100644
>> index 00000000000000..a0bcef13a1085a
>> --- /dev/null
>> +++ b/drivers/gpu/drm/i915/intel_wakeref_tracker.c
>> @@ -0,0 +1,234 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2021 Intel Corporation
>> + */
>> +
>> +#include <linux/slab.h>
>> +#include <linux/stackdepot.h>
>> +#include <linux/stacktrace.h>
>> +#include <linux/sort.h>
>> +
>> +#include <drm/drm_print.h>
>> +
>> +#include "intel_wakeref.h"
>
> This should really include the corresponding .h
> i.e. intel_wakeref_tracker.h too. Now it gets included via
> intel_wakeref.h but I'm not sure why.
Apparently some leftover, will fix it.
Regards
Andrzej
>
>> +
>> +#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 __print_depot_stack(depot_stack_handle_t stack,
>> + char *buf, int sz, int indent)
>> +{
>> + unsigned long *entries;
>> + unsigned int nr_entries;
>> +
>> + nr_entries = stack_depot_fetch(stack, &entries);
>> + stack_trace_snprint(buf, sz, entries, nr_entries, indent);
>> +}
>> +
>> +static int cmphandle(const void *_a, const void *_b)
>> +{
>> + 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;
>> +}
>> +
>> +void
>> +__intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w,
>> + struct drm_printer *p)
>> +{
>> + unsigned long i;
>> + char *buf;
>> +
>> + buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
>> + if (!buf)
>> + return;
>> +
>> + if (w->last_acquire) {
>> + __print_depot_stack(w->last_acquire, buf, PAGE_SIZE, 2);
>> + drm_printf(p, "Wakeref last acquired:\n%s", buf);
>> + }
>> +
>> + if (w->last_release) {
>> + __print_depot_stack(w->last_release, buf, PAGE_SIZE, 2);
>> + drm_printf(p, "Wakeref last released:\n%s", buf);
>> + }
>> +
>> + drm_printf(p, "Wakeref count: %lu\n", w->count);
>> +
>> + sort(w->owners, w->count, sizeof(*w->owners), cmphandle, NULL);
>> +
>> + for (i = 0; i < w->count; i++) {
>> + depot_stack_handle_t stack = w->owners[i];
>> + unsigned long rep;
>> +
>> + rep = 1;
>> + while (i + 1 < w->count && w->owners[i + 1] == stack)
>> + rep++, i++;
>> + __print_depot_stack(stack, buf, PAGE_SIZE, 2);
>> + drm_printf(p, "Wakeref x%lu taken at:\n%s", rep, buf);
>> + }
>> +
>> + kfree(buf);
>> +}
>> +
>> +void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w,
>> + struct drm_printer *p)
>> +{
>> + struct intel_wakeref_tracker tmp = {};
>> +
>> + do {
>> + unsigned long alloc = tmp.count;
>> + depot_stack_handle_t *s;
>> +
>> + spin_lock_irq(&w->lock);
>> + tmp.count = w->count;
>> + if (tmp.count <= alloc)
>> + memcpy(tmp.owners, w->owners, tmp.count * sizeof(*s));
>> + tmp.last_acquire = w->last_acquire;
>> + tmp.last_release = w->last_release;
>> + spin_unlock_irq(&w->lock);
>> + if (tmp.count <= alloc)
>> + break;
>> +
>> + s = krealloc(tmp.owners,
>> + tmp.count * sizeof(*s),
>> + GFP_NOWAIT | __GFP_NOWARN);
>> + if (!s)
>> + goto out;
>> +
>> + tmp.owners = s;
>> + } while (1);
>> +
>> + __intel_wakeref_tracker_show(&tmp, p);
>> +
>> +out:
>> + intel_wakeref_tracker_fini(&tmp);
>> +}
>> +
>> +intel_wakeref_t intel_wakeref_tracker_add(struct intel_wakeref_tracker *w)
>> +{
>> + depot_stack_handle_t stack, *stacks;
>> + unsigned long flags;
>> +
>> + stack = __save_depot_stack();
>> + if (!stack)
>> + return -1;
>> +
>> + spin_lock_irqsave(&w->lock, flags);
>> +
>> + if (!w->count)
>> + w->last_acquire = stack;
>> +
>> + stacks = krealloc(w->owners,
>> + (w->count + 1) * sizeof(*stacks),
>> + GFP_NOWAIT | __GFP_NOWARN);
>> + if (stacks) {
>> + stacks[w->count++] = stack;
>> + w->owners = stacks;
>> + } else {
>> + stack = -1;
>> + }
>> +
>> + spin_unlock_irqrestore(&w->lock, flags);
>> +
>> + return stack;
>> +}
>> +
>> +void intel_wakeref_tracker_remove(struct intel_wakeref_tracker *w,
>> + intel_wakeref_t stack)
>> +{
>> + unsigned long flags, n;
>> + bool found = false;
>> +
>> + if (unlikely(stack == -1))
>> + return;
>> +
>> + spin_lock_irqsave(&w->lock, flags);
>> + for (n = w->count; n--; ) {
>> + if (w->owners[n] == stack) {
>> + memmove(w->owners + n,
>> + w->owners + n + 1,
>> + (--w->count - n) * sizeof(stack));
>> + found = true;
>> + break;
>> + }
>> + }
>> + spin_unlock_irqrestore(&w->lock, flags);
>> +
>> + if (WARN(!found,
>> + "Unmatched wakeref %x, tracking %lu\n",
>> + stack, w->count)) {
>> + char *buf;
>> +
>> + buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
>> + if (!buf)
>> + return;
>> +
>> + __print_depot_stack(stack, buf, PAGE_SIZE, 2);
>> + pr_err("wakeref %x from\n%s", stack, buf);
>> +
>> + stack = READ_ONCE(w->last_release);
>> + if (stack && !w->count) {
>> + __print_depot_stack(stack, buf, PAGE_SIZE, 2);
>> + pr_err("wakeref last released at\n%s", buf);
>> + }
>> +
>> + kfree(buf);
>> + }
>> +}
>> +
>> +struct intel_wakeref_tracker
>> +__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w)
>> +{
>> + struct intel_wakeref_tracker saved;
>> +
>> + lockdep_assert_held(&w->lock);
>> +
>> + saved = *w;
>> +
>> + w->owners = NULL;
>> + w->count = 0;
>> + w->last_release = __save_depot_stack();
>> +
>> + return saved;
>> +}
>> +
>> +void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
>> + struct drm_printer *p)
>> +{
>> + struct intel_wakeref_tracker tmp;
>> +
>> + spin_lock_irq(&w->lock);
>> + tmp = __intel_wakeref_tracker_reset(w);
>> + spin_unlock_irq(&w->lock);
>> +
>> + if (tmp.count)
>> + __intel_wakeref_tracker_show(&tmp, p);
>> +
>> + intel_wakeref_tracker_fini(&tmp);
>> +}
>> +
>> +void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w)
>> +{
>> + memset(w, 0, sizeof(*w));
>> + spin_lock_init(&w->lock);
>> + stack_depot_init();
>> +}
>> +
>> +void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w)
>> +{
>> + kfree(w->owners);
>> +}
>> diff --git a/drivers/gpu/drm/i915/intel_wakeref_tracker.h b/drivers/gpu/drm/i915/intel_wakeref_tracker.h
>> new file mode 100644
>> index 00000000000000..61df68e28c0fbf
>> --- /dev/null
>> +++ b/drivers/gpu/drm/i915/intel_wakeref_tracker.h
>> @@ -0,0 +1,76 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2019 Intel Corporation
>> + */
>> +
>> +#ifndef INTEL_WAKEREF_TRACKER_H
>> +#define INTEL_WAKEREF_TRACKER_H
>> +
>> +#include <linux/kconfig.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/stackdepot.h>
>> +
>> +typedef depot_stack_handle_t intel_wakeref_t;
>> +
>> +struct drm_printer;
>> +
>> +struct intel_wakeref_tracker {
>> + spinlock_t lock;
>> +
>> + depot_stack_handle_t last_acquire;
>> + depot_stack_handle_t last_release;
>> +
>> + depot_stack_handle_t *owners;
>> + unsigned long count;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_DRM_I915_TRACK_WAKEREF)
>> +
>> +void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w);
>> +void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w);
>> +
>> +intel_wakeref_t intel_wakeref_tracker_add(struct intel_wakeref_tracker *w);
>> +void intel_wakeref_tracker_remove(struct intel_wakeref_tracker *w,
>> + intel_wakeref_t handle);
>> +
>> +struct intel_wakeref_tracker
>> +__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w);
>> +void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
>> + struct drm_printer *p);
>> +
>> +void __intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w,
>> + struct drm_printer *p);
>> +void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w,
>> + struct drm_printer *p);
>> +
>> +#else
>> +
>> +static inline void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w) {}
>> +static inline void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w) {}
>> +
>> +static inline intel_wakeref_t
>> +intel_wakeref_tracker_add(struct intel_wakeref_tracker *w)
>> +{
>> + return -1;
>> +}
>> +
>> +static inline void
>> +intel_wakeref_untrack_remove(struct intel_wakeref_tracker *w, intel_wakeref_t handle) {}
>> +
>> +static inline struct intel_wakeref_tracker
>> +__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w)
>> +{
>> + return (struct intel_wakeref_tracker){};
>> +}
>> +
>> +static inline void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
>> + struct drm_printer *p)
>> +{
>> +}
>> +
>> +static inline void __intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w, struct drm_printer *p) {}
>> +static inline void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w, struct drm_printer *p) {}
>> +
>> +#endif
>> +
>> +#endif /* INTEL_WAKEREF_TRACKER_H */
>
@@ -25,6 +25,7 @@ config DRM_I915_DEBUG
select PREEMPT_COUNT
select I2C_CHARDEV
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)
@@ -37,6 +38,7 @@ config DRM_I915_DEBUG
select DRM_I915_DEBUG_GEM
select DRM_I915_DEBUG_GEM_ONCE
select DRM_I915_DEBUG_MMIO
+ select DRM_I915_TRACK_WAKEREF
select DRM_I915_DEBUG_RUNTIME_PM
select DRM_I915_SW_FENCE_DEBUG_OBJECTS
select DRM_I915_SELFTEST
@@ -227,11 +229,18 @@ config DRM_I915_DEBUG_VBLANK_EVADE
If in doubt, say "N".
+config DRM_I915_TRACK_WAKEREF
+ depends on STACKDEPOT
+ depends on STACKTRACE
+ bool
+
config DRM_I915_DEBUG_RUNTIME_PM
bool "Enable extra state checking for runtime PM"
depends on DRM_I915
default n
select STACKDEPOT
+ select STACKTRACE
+ select DRM_I915_TRACK_WAKEREF
help
Choose this option to turn on extra state checking for the
runtime PM functionality. This may introduce overhead during
@@ -81,6 +81,10 @@ i915-$(CONFIG_DEBUG_FS) += \
i915_debugfs_params.o \
display/intel_display_debugfs.o \
display/intel_pipe_crc.o
+
+i915-$(CONFIG_DRM_I915_TRACK_WAKEREF) += \
+ intel_wakeref_tracker.o
+
i915-$(CONFIG_PERF_EVENTS) += i915_pmu.o
# "Graphics Technology" (aka we talk to the gpu)
@@ -52,182 +52,37 @@
#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();
+ intel_wakeref_tracker_init(&rpm->debug);
}
-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)
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_wakeref_tracker_add(&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))
- 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_wakeref_tracker_remove(&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;
+ struct drm_printer p = drm_debug_printer("i915");
- 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);
+ intel_wakeref_tracker_reset(&rpm->debug, &p);
}
static noinline void
__intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
{
- struct intel_runtime_pm_debug dbg = {};
+ struct intel_wakeref_tracker saved;
unsigned long flags;
if (!atomic_dec_and_lock_irqsave(&rpm->wakeref_count,
@@ -235,60 +90,21 @@ __intel_wakeref_dec_and_check_tracking(struct intel_runtime_pm *rpm)
flags))
return;
- __untrack_all_wakerefs(&rpm->debug, &dbg);
+ saved = __intel_wakeref_tracker_reset(&rpm->debug);
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);
+ if (saved.count) {
+ struct drm_printer p = drm_debug_printer("i915");
- dump_and_free_wakeref_tracking(&dbg);
+ __intel_wakeref_tracker_show(&saved, &p);
+ intel_wakeref_tracker_fini(&saved);
+ }
}
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_wakeref_tracker_show(&rpm->debug, p);
}
#else
@@ -297,14 +113,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)
{
}
@@ -349,9 +165,8 @@ intel_runtime_pm_release(struct intel_runtime_pm *rpm, int wakelock)
static intel_wakeref_t __intel_runtime_pm_get(struct intel_runtime_pm *rpm,
bool wakelock)
{
- struct drm_i915_private *i915 = container_of(rpm,
- struct drm_i915_private,
- runtime_pm);
+ struct drm_i915_private *i915 =
+ container_of(rpm, struct drm_i915_private, runtime_pm);
int ret;
ret = pm_runtime_get_sync(rpm->kdev);
@@ -556,9 +371,8 @@ void intel_runtime_pm_put(struct intel_runtime_pm *rpm, intel_wakeref_t wref)
*/
void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
{
- struct drm_i915_private *i915 = container_of(rpm,
- struct drm_i915_private,
- runtime_pm);
+ struct drm_i915_private *i915 =
+ container_of(rpm, struct drm_i915_private, runtime_pm);
struct device *kdev = rpm->kdev;
/*
@@ -611,9 +425,8 @@ void intel_runtime_pm_enable(struct intel_runtime_pm *rpm)
void intel_runtime_pm_disable(struct intel_runtime_pm *rpm)
{
- struct drm_i915_private *i915 = container_of(rpm,
- struct drm_i915_private,
- runtime_pm);
+ struct drm_i915_private *i915 =
+ container_of(rpm, struct drm_i915_private, runtime_pm);
struct device *kdev = rpm->kdev;
/* Transfer rpm ownership back to core */
@@ -628,9 +441,8 @@ void intel_runtime_pm_disable(struct intel_runtime_pm *rpm)
void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
{
- struct drm_i915_private *i915 = container_of(rpm,
- struct drm_i915_private,
- runtime_pm);
+ struct drm_i915_private *i915 =
+ container_of(rpm, struct drm_i915_private, runtime_pm);
int count = atomic_read(&rpm->wakeref_count);
intel_wakeref_auto_fini(&rpm->userfault_wakeref);
@@ -646,7 +458,7 @@ void intel_runtime_pm_driver_release(struct intel_runtime_pm *rpm)
void intel_runtime_pm_init_early(struct intel_runtime_pm *rpm)
{
struct drm_i915_private *i915 =
- container_of(rpm, struct drm_i915_private, runtime_pm);
+ container_of(rpm, struct drm_i915_private, runtime_pm);
struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
struct device *kdev = &pdev->dev;
@@ -83,15 +83,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 intel_wakeref_tracker debug;
#endif
};
@@ -17,7 +17,9 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
-#if IS_ENABLED(CONFIG_DRM_I915_DEBUG)
+#include "intel_wakeref_tracker.h"
+
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_WAKEREF)
#define INTEL_WAKEREF_BUG_ON(expr) BUG_ON(expr)
#else
#define INTEL_WAKEREF_BUG_ON(expr) BUILD_BUG_ON_INVALID(expr)
@@ -26,8 +28,6 @@
struct intel_runtime_pm;
struct intel_wakeref;
-typedef depot_stack_handle_t intel_wakeref_t;
-
struct intel_wakeref_ops {
int (*get)(struct intel_wakeref *wf);
int (*put)(struct intel_wakeref *wf);
new file mode 100644
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#include <linux/slab.h>
+#include <linux/stackdepot.h>
+#include <linux/stacktrace.h>
+#include <linux/sort.h>
+
+#include <drm/drm_print.h>
+
+#include "intel_wakeref.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 __print_depot_stack(depot_stack_handle_t stack,
+ char *buf, int sz, int indent)
+{
+ unsigned long *entries;
+ unsigned int nr_entries;
+
+ nr_entries = stack_depot_fetch(stack, &entries);
+ stack_trace_snprint(buf, sz, entries, nr_entries, indent);
+}
+
+static int cmphandle(const void *_a, const void *_b)
+{
+ 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;
+}
+
+void
+__intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w,
+ struct drm_printer *p)
+{
+ unsigned long i;
+ char *buf;
+
+ buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
+ if (!buf)
+ return;
+
+ if (w->last_acquire) {
+ __print_depot_stack(w->last_acquire, buf, PAGE_SIZE, 2);
+ drm_printf(p, "Wakeref last acquired:\n%s", buf);
+ }
+
+ if (w->last_release) {
+ __print_depot_stack(w->last_release, buf, PAGE_SIZE, 2);
+ drm_printf(p, "Wakeref last released:\n%s", buf);
+ }
+
+ drm_printf(p, "Wakeref count: %lu\n", w->count);
+
+ sort(w->owners, w->count, sizeof(*w->owners), cmphandle, NULL);
+
+ for (i = 0; i < w->count; i++) {
+ depot_stack_handle_t stack = w->owners[i];
+ unsigned long rep;
+
+ rep = 1;
+ while (i + 1 < w->count && w->owners[i + 1] == stack)
+ rep++, i++;
+ __print_depot_stack(stack, buf, PAGE_SIZE, 2);
+ drm_printf(p, "Wakeref x%lu taken at:\n%s", rep, buf);
+ }
+
+ kfree(buf);
+}
+
+void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w,
+ struct drm_printer *p)
+{
+ struct intel_wakeref_tracker tmp = {};
+
+ do {
+ unsigned long alloc = tmp.count;
+ depot_stack_handle_t *s;
+
+ spin_lock_irq(&w->lock);
+ tmp.count = w->count;
+ if (tmp.count <= alloc)
+ memcpy(tmp.owners, w->owners, tmp.count * sizeof(*s));
+ tmp.last_acquire = w->last_acquire;
+ tmp.last_release = w->last_release;
+ spin_unlock_irq(&w->lock);
+ if (tmp.count <= alloc)
+ break;
+
+ s = krealloc(tmp.owners,
+ tmp.count * sizeof(*s),
+ GFP_NOWAIT | __GFP_NOWARN);
+ if (!s)
+ goto out;
+
+ tmp.owners = s;
+ } while (1);
+
+ __intel_wakeref_tracker_show(&tmp, p);
+
+out:
+ intel_wakeref_tracker_fini(&tmp);
+}
+
+intel_wakeref_t intel_wakeref_tracker_add(struct intel_wakeref_tracker *w)
+{
+ depot_stack_handle_t stack, *stacks;
+ unsigned long flags;
+
+ stack = __save_depot_stack();
+ if (!stack)
+ return -1;
+
+ spin_lock_irqsave(&w->lock, flags);
+
+ if (!w->count)
+ w->last_acquire = stack;
+
+ stacks = krealloc(w->owners,
+ (w->count + 1) * sizeof(*stacks),
+ GFP_NOWAIT | __GFP_NOWARN);
+ if (stacks) {
+ stacks[w->count++] = stack;
+ w->owners = stacks;
+ } else {
+ stack = -1;
+ }
+
+ spin_unlock_irqrestore(&w->lock, flags);
+
+ return stack;
+}
+
+void intel_wakeref_tracker_remove(struct intel_wakeref_tracker *w,
+ intel_wakeref_t stack)
+{
+ unsigned long flags, n;
+ bool found = false;
+
+ if (unlikely(stack == -1))
+ return;
+
+ spin_lock_irqsave(&w->lock, flags);
+ for (n = w->count; n--; ) {
+ if (w->owners[n] == stack) {
+ memmove(w->owners + n,
+ w->owners + n + 1,
+ (--w->count - n) * sizeof(stack));
+ found = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&w->lock, flags);
+
+ if (WARN(!found,
+ "Unmatched wakeref %x, tracking %lu\n",
+ stack, w->count)) {
+ char *buf;
+
+ buf = kmalloc(PAGE_SIZE, GFP_NOWAIT | __GFP_NOWARN);
+ if (!buf)
+ return;
+
+ __print_depot_stack(stack, buf, PAGE_SIZE, 2);
+ pr_err("wakeref %x from\n%s", stack, buf);
+
+ stack = READ_ONCE(w->last_release);
+ if (stack && !w->count) {
+ __print_depot_stack(stack, buf, PAGE_SIZE, 2);
+ pr_err("wakeref last released at\n%s", buf);
+ }
+
+ kfree(buf);
+ }
+}
+
+struct intel_wakeref_tracker
+__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w)
+{
+ struct intel_wakeref_tracker saved;
+
+ lockdep_assert_held(&w->lock);
+
+ saved = *w;
+
+ w->owners = NULL;
+ w->count = 0;
+ w->last_release = __save_depot_stack();
+
+ return saved;
+}
+
+void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
+ struct drm_printer *p)
+{
+ struct intel_wakeref_tracker tmp;
+
+ spin_lock_irq(&w->lock);
+ tmp = __intel_wakeref_tracker_reset(w);
+ spin_unlock_irq(&w->lock);
+
+ if (tmp.count)
+ __intel_wakeref_tracker_show(&tmp, p);
+
+ intel_wakeref_tracker_fini(&tmp);
+}
+
+void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w)
+{
+ memset(w, 0, sizeof(*w));
+ spin_lock_init(&w->lock);
+ stack_depot_init();
+}
+
+void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w)
+{
+ kfree(w->owners);
+}
new file mode 100644
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2019 Intel Corporation
+ */
+
+#ifndef INTEL_WAKEREF_TRACKER_H
+#define INTEL_WAKEREF_TRACKER_H
+
+#include <linux/kconfig.h>
+#include <linux/spinlock.h>
+#include <linux/stackdepot.h>
+
+typedef depot_stack_handle_t intel_wakeref_t;
+
+struct drm_printer;
+
+struct intel_wakeref_tracker {
+ spinlock_t lock;
+
+ depot_stack_handle_t last_acquire;
+ depot_stack_handle_t last_release;
+
+ depot_stack_handle_t *owners;
+ unsigned long count;
+};
+
+#if IS_ENABLED(CONFIG_DRM_I915_TRACK_WAKEREF)
+
+void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w);
+void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w);
+
+intel_wakeref_t intel_wakeref_tracker_add(struct intel_wakeref_tracker *w);
+void intel_wakeref_tracker_remove(struct intel_wakeref_tracker *w,
+ intel_wakeref_t handle);
+
+struct intel_wakeref_tracker
+__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w);
+void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
+ struct drm_printer *p);
+
+void __intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w,
+ struct drm_printer *p);
+void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w,
+ struct drm_printer *p);
+
+#else
+
+static inline void intel_wakeref_tracker_init(struct intel_wakeref_tracker *w) {}
+static inline void intel_wakeref_tracker_fini(struct intel_wakeref_tracker *w) {}
+
+static inline intel_wakeref_t
+intel_wakeref_tracker_add(struct intel_wakeref_tracker *w)
+{
+ return -1;
+}
+
+static inline void
+intel_wakeref_untrack_remove(struct intel_wakeref_tracker *w, intel_wakeref_t handle) {}
+
+static inline struct intel_wakeref_tracker
+__intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w)
+{
+ return (struct intel_wakeref_tracker){};
+}
+
+static inline void intel_wakeref_tracker_reset(struct intel_wakeref_tracker *w,
+ struct drm_printer *p)
+{
+}
+
+static inline void __intel_wakeref_tracker_show(const struct intel_wakeref_tracker *w, struct drm_printer *p) {}
+static inline void intel_wakeref_tracker_show(struct intel_wakeref_tracker *w, struct drm_printer *p) {}
+
+#endif
+
+#endif /* INTEL_WAKEREF_TRACKER_H */