Commit bd780f37 authored by Chris Wilson's avatar Chris Wilson

drm/i915: Track all held rpm wakerefs

Everytime we take a wakeref, record the stack trace of where it was
taken; clearing the set if we ever drop back to no owners. For debugging
a rpm leak, we can look at all the current wakerefs and check if they
have a matching rpm_put.

v2: Use skip=0 for unwinding the stack as it appears our noinline
function doesn't appear on the stack (nor does save_stack_trace itself!)
v3: Allow rpm->debug_count to disappear between inspections and so
avoid calling krealloc(0) as that may return a ZERO_PTR not NULL! (Mika)
v4: Show who last acquire/released the runtime pm
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Reviewed-by: default avatarMika Kuoppala <mika.kuoppala@linux.intel.com>
Tested-by: default avatarMika Kuoppala <mika.kuoppala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190114142129.24398-1-chris@chris-wilson.co.uk
parent 74256b7e
...@@ -21,11 +21,11 @@ config DRM_I915_DEBUG ...@@ -21,11 +21,11 @@ config DRM_I915_DEBUG
select DEBUG_FS select DEBUG_FS
select PREEMPT_COUNT select PREEMPT_COUNT
select I2C_CHARDEV select I2C_CHARDEV
select STACKDEPOT
select DRM_DP_AUX_CHARDEV select DRM_DP_AUX_CHARDEV
select X86_MSR # used by igt/pm_rpm select X86_MSR # used by igt/pm_rpm
select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks) select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks)
select DRM_DEBUG_MM if DRM=y select DRM_DEBUG_MM if DRM=y
select STACKDEPOT if DRM=y # for DRM_DEBUG_MM
select DRM_DEBUG_SELFTEST select DRM_DEBUG_SELFTEST
select SW_SYNC # signaling validation framework (igt/syncobj*) select SW_SYNC # signaling validation framework (igt/syncobj*)
select DRM_I915_SW_FENCE_DEBUG_OBJECTS select DRM_I915_SW_FENCE_DEBUG_OBJECTS
...@@ -173,6 +173,7 @@ config DRM_I915_DEBUG_RUNTIME_PM ...@@ -173,6 +173,7 @@ config DRM_I915_DEBUG_RUNTIME_PM
bool "Enable extra state checking for runtime PM" bool "Enable extra state checking for runtime PM"
depends on DRM_I915 depends on DRM_I915
default n default n
select STACKDEPOT
help help
Choose this option to turn on extra state checking for the Choose this option to turn on extra state checking for the
runtime PM functionality. This may introduce overhead during runtime PM functionality. This may introduce overhead during
......
...@@ -2702,6 +2702,12 @@ static int i915_runtime_pm_status(struct seq_file *m, void *unused) ...@@ -2702,6 +2702,12 @@ static int i915_runtime_pm_status(struct seq_file *m, void *unused)
pci_power_name(pdev->current_state), pci_power_name(pdev->current_state),
pdev->current_state); pdev->current_state);
if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)) {
struct drm_printer p = drm_seq_file_printer(m);
print_intel_runtime_pm_wakeref(dev_priv, &p);
}
return 0; return 0;
} }
......
...@@ -905,6 +905,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv) ...@@ -905,6 +905,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv)
mutex_init(&dev_priv->pps_mutex); mutex_init(&dev_priv->pps_mutex);
i915_memcpy_init_early(dev_priv); i915_memcpy_init_early(dev_priv);
intel_runtime_pm_init_early(dev_priv);
ret = i915_workqueues_init(dev_priv); ret = i915_workqueues_init(dev_priv);
if (ret < 0) if (ret < 0)
...@@ -1807,8 +1808,7 @@ void i915_driver_unload(struct drm_device *dev) ...@@ -1807,8 +1808,7 @@ void i915_driver_unload(struct drm_device *dev)
i915_driver_cleanup_mmio(dev_priv); i915_driver_cleanup_mmio(dev_priv);
enable_rpm_wakeref_asserts(dev_priv); enable_rpm_wakeref_asserts(dev_priv);
intel_runtime_pm_cleanup(dev_priv);
WARN_ON(atomic_read(&dev_priv->runtime_pm.wakeref_count));
} }
static void i915_driver_release(struct drm_device *dev) static void i915_driver_release(struct drm_device *dev)
...@@ -2010,6 +2010,8 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) ...@@ -2010,6 +2010,8 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
out: out:
enable_rpm_wakeref_asserts(dev_priv); enable_rpm_wakeref_asserts(dev_priv);
if (!dev_priv->uncore.user_forcewake.count)
intel_runtime_pm_cleanup(dev_priv);
return ret; return ret;
} }
...@@ -2965,7 +2967,7 @@ static int intel_runtime_suspend(struct device *kdev) ...@@ -2965,7 +2967,7 @@ static int intel_runtime_suspend(struct device *kdev)
} }
enable_rpm_wakeref_asserts(dev_priv); enable_rpm_wakeref_asserts(dev_priv);
WARN_ON_ONCE(atomic_read(&dev_priv->runtime_pm.wakeref_count)); intel_runtime_pm_cleanup(dev_priv);
if (intel_uncore_arm_unclaimed_mmio_detection(dev_priv)) if (intel_uncore_arm_unclaimed_mmio_detection(dev_priv))
DRM_ERROR("Unclaimed access detected prior to suspending\n"); DRM_ERROR("Unclaimed access detected prior to suspending\n");
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/reservation.h> #include <linux/reservation.h>
#include <linux/shmem_fs.h> #include <linux/shmem_fs.h>
#include <linux/stackdepot.h>
#include <drm/intel-gtt.h> #include <drm/intel-gtt.h>
#include <drm/drm_legacy.h> /* for struct drm_dma_handle */ #include <drm/drm_legacy.h> /* for struct drm_dma_handle */
...@@ -1156,6 +1157,25 @@ struct i915_runtime_pm { ...@@ -1156,6 +1157,25 @@ struct i915_runtime_pm {
atomic_t wakeref_count; atomic_t wakeref_count;
bool suspended; bool suspended;
bool irqs_enabled; bool irqs_enabled;
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
/*
* To aide detection of wakeref leaks and general misuse, we
* track all wakeref holders. With manual markup (i.e. returning
* a cookie to each rpm_get caller which they then supply to their
* 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;
#endif
}; };
enum intel_pipe_crc_source { enum intel_pipe_crc_source {
......
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <media/cec-notifier.h> #include <media/cec-notifier.h>
struct drm_printer;
/** /**
* __wait_for - magic wait macro * __wait_for - magic wait macro
* *
...@@ -2084,6 +2086,7 @@ bool intel_psr_enabled(struct intel_dp *intel_dp); ...@@ -2084,6 +2086,7 @@ bool intel_psr_enabled(struct intel_dp *intel_dp);
void intel_init_quirks(struct drm_i915_private *dev_priv); void intel_init_quirks(struct drm_i915_private *dev_priv);
/* intel_runtime_pm.c */ /* intel_runtime_pm.c */
void intel_runtime_pm_init_early(struct drm_i915_private *dev_priv);
int intel_power_domains_init(struct drm_i915_private *); int intel_power_domains_init(struct drm_i915_private *);
void intel_power_domains_cleanup(struct drm_i915_private *dev_priv); void intel_power_domains_cleanup(struct drm_i915_private *dev_priv);
void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume); void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume);
...@@ -2106,6 +2109,7 @@ void bxt_display_core_init(struct drm_i915_private *dev_priv, bool resume); ...@@ -2106,6 +2109,7 @@ void bxt_display_core_init(struct drm_i915_private *dev_priv, bool resume);
void bxt_display_core_uninit(struct drm_i915_private *dev_priv); void bxt_display_core_uninit(struct drm_i915_private *dev_priv);
void intel_runtime_pm_enable(struct drm_i915_private *dev_priv); void intel_runtime_pm_enable(struct drm_i915_private *dev_priv);
void intel_runtime_pm_disable(struct drm_i915_private *dev_priv); void intel_runtime_pm_disable(struct drm_i915_private *dev_priv);
void intel_runtime_pm_cleanup(struct drm_i915_private *dev_priv);
const char * const char *
intel_display_power_domain_str(enum intel_display_power_domain domain); intel_display_power_domain_str(enum intel_display_power_domain domain);
...@@ -2123,23 +2127,23 @@ void icl_dbuf_slices_update(struct drm_i915_private *dev_priv, ...@@ -2123,23 +2127,23 @@ void icl_dbuf_slices_update(struct drm_i915_private *dev_priv,
u8 req_slices); u8 req_slices);
static inline void static inline void
assert_rpm_device_not_suspended(struct drm_i915_private *dev_priv) assert_rpm_device_not_suspended(struct drm_i915_private *i915)
{ {
WARN_ONCE(dev_priv->runtime_pm.suspended, WARN_ONCE(i915->runtime_pm.suspended,
"Device suspended during HW access\n"); "Device suspended during HW access\n");
} }
static inline void static inline void
assert_rpm_wakelock_held(struct drm_i915_private *dev_priv) assert_rpm_wakelock_held(struct drm_i915_private *i915)
{ {
assert_rpm_device_not_suspended(dev_priv); assert_rpm_device_not_suspended(i915);
WARN_ONCE(!atomic_read(&dev_priv->runtime_pm.wakeref_count), WARN_ONCE(!atomic_read(&i915->runtime_pm.wakeref_count),
"RPM wakelock ref not held during HW access"); "RPM wakelock ref not held during HW access");
} }
/** /**
* disable_rpm_wakeref_asserts - disable the RPM assert checks * disable_rpm_wakeref_asserts - disable the RPM assert checks
* @dev_priv: i915 device instance * @i915: i915 device instance
* *
* This function disable asserts that check if we hold an RPM wakelock * This function disable asserts that check if we hold an RPM wakelock
* reference, while keeping the device-not-suspended checks still enabled. * reference, while keeping the device-not-suspended checks still enabled.
...@@ -2156,14 +2160,14 @@ assert_rpm_wakelock_held(struct drm_i915_private *dev_priv) ...@@ -2156,14 +2160,14 @@ assert_rpm_wakelock_held(struct drm_i915_private *dev_priv)
* enable_rpm_wakeref_asserts(). * enable_rpm_wakeref_asserts().
*/ */
static inline void static inline void
disable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv) disable_rpm_wakeref_asserts(struct drm_i915_private *i915)
{ {
atomic_inc(&dev_priv->runtime_pm.wakeref_count); atomic_inc(&i915->runtime_pm.wakeref_count);
} }
/** /**
* enable_rpm_wakeref_asserts - re-enable the RPM assert checks * enable_rpm_wakeref_asserts - re-enable the RPM assert checks
* @dev_priv: i915 device instance * @i915: i915 device instance
* *
* This function re-enables the RPM assert checks after disabling them with * This function re-enables the RPM assert checks after disabling them with
* disable_rpm_wakeref_asserts. It's meant to be used only in special * disable_rpm_wakeref_asserts. It's meant to be used only in special
...@@ -2173,15 +2177,25 @@ disable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv) ...@@ -2173,15 +2177,25 @@ disable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv)
* disable_rpm_wakeref_asserts(). * disable_rpm_wakeref_asserts().
*/ */
static inline void static inline void
enable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv) enable_rpm_wakeref_asserts(struct drm_i915_private *i915)
{ {
atomic_dec(&dev_priv->runtime_pm.wakeref_count); atomic_dec(&i915->runtime_pm.wakeref_count);
} }
void intel_runtime_pm_get(struct drm_i915_private *dev_priv); void intel_runtime_pm_get(struct drm_i915_private *i915);
bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv); bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *i915);
void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv); void intel_runtime_pm_get_noresume(struct drm_i915_private *i915);
void intel_runtime_pm_put(struct drm_i915_private *dev_priv); void intel_runtime_pm_put(struct drm_i915_private *i915);
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
void print_intel_runtime_pm_wakeref(struct drm_i915_private *i915,
struct drm_printer *p);
#else
static inline void print_intel_runtime_pm_wakeref(struct drm_i915_private *i915,
struct drm_printer *p)
{
}
#endif
void chv_phy_powergate_lanes(struct intel_encoder *encoder, void chv_phy_powergate_lanes(struct intel_encoder *encoder,
bool override, unsigned int mask); bool override, unsigned int mask);
......
This diff is collapsed.
...@@ -154,15 +154,17 @@ struct drm_i915_private *mock_gem_device(void) ...@@ -154,15 +154,17 @@ struct drm_i915_private *mock_gem_device(void)
pdev->dev.archdata.iommu = (void *)-1; pdev->dev.archdata.iommu = (void *)-1;
#endif #endif
i915 = (struct drm_i915_private *)(pdev + 1);
pci_set_drvdata(pdev, i915);
intel_runtime_pm_init_early(i915);
dev_pm_domain_set(&pdev->dev, &pm_domain); dev_pm_domain_set(&pdev->dev, &pm_domain);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev);
if (pm_runtime_enabled(&pdev->dev)) if (pm_runtime_enabled(&pdev->dev))
WARN_ON(pm_runtime_get_sync(&pdev->dev)); WARN_ON(pm_runtime_get_sync(&pdev->dev));
i915 = (struct drm_i915_private *)(pdev + 1);
pci_set_drvdata(pdev, i915);
err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev); err = drm_dev_init(&i915->drm, &mock_driver, &pdev->dev);
if (err) { if (err) {
pr_err("Failed to initialise mock GEM device: err=%d\n", err); pr_err("Failed to initialise mock GEM device: err=%d\n", err);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment