Skip to content

Commit

Permalink
drm/i915: add support for checking if we hold an RPM reference
Browse files Browse the repository at this point in the history
Atm, we assert that the device is not suspended until the point when the
device is truly put to a suspended state. This is fine, but we can catch
more problems if we check that RPM refcount is non-zero. After that one
drops to zero we shouldn't access the device any more, even if the actual
device suspend may be delayed. Change assert_rpm_wakelock_held()
accordingly to check for a non-zero RPM refcount in addition to the
current device-not-suspended check.

For the new asserts to work we need to annotate every place explicitly in
the code where we expect that the device is powered. The places where we
only assume this, but may not hold an RPM reference:
- driver load
  We assume the device to be powered until we enable RPM. Make this
  explicit by taking an RPM reference around the load function.
- system and runtime sudpend/resume handlers
  These handlers are called when the RPM reference becomes 0 and know the
  exact point after which the device can get powered off. Disable the
  RPM-reference-held check for their duration.
- the IRQ, hangcheck and RPS work handlers
  These handlers are flushed in the system/runtime suspend handler
  before the device is powered off, so it's guaranteed that they won't
  run while the device is powered off even though they don't hold any
  RPM reference. Disable the RPM-reference-held check for their duration.

In all these cases we still check that the device is not suspended.
These explicit annotations also have the positive side effect of
documenting our assumptions better.

This caught additional WARNs from the atomic modeset path, those should
be fixed separately.

v2:
- remove the redundant HAS_RUNTIME_PM check (moved to patch 1) (Ville)
v3:
- use a new dedicated RPM wakelock refcount to also catch cases where
  our own RPM get/put functions were not called (Chris)
- assert also that the new RPM wakelock refcount is 0 in the RPM
  suspend handler (Chris)
- change the assert error message to be more meaningful (Chris)
- prevent false assert errors and check that the RPM wakelock is 0 in
  the RPM resume handler too
- prevent false assert errors in the hangcheck work too
- add a device not suspended assert check to the hangcheck work
v4:
- rename disable/enable_rpm_asserts to disable/enable_rpm_wakeref_asserts
  and wakelock_count to wakeref_count
- disable the wakeref asserts in the IRQ handlers and RPS work too
- update/clarify commit message
v5:
- mark places we plan to change to use proper RPM refcounting with
  separate DISABLE/ENABLE_RPM_WAKEREF_ASSERTS aliases (Chris)

Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: http://patchwork.freedesktop.org/patch/msgid/1450227139-13471-1-git-send-email-imre.deak@intel.com
  • Loading branch information
Imre Deak committed Dec 17, 2015
1 parent c9b8846 commit 1f814da
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 10 deletions.
7 changes: 7 additions & 0 deletions drivers/gpu/drm/i915/i915_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)

intel_pm_setup(dev);

intel_runtime_pm_get(dev_priv);

intel_display_crc_init(dev);

i915_dump_device_info(dev_priv);
Expand Down Expand Up @@ -1085,6 +1087,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)

i915_audio_component_init(dev_priv);

intel_runtime_pm_put(dev_priv);

return 0;

out_power_well:
Expand Down Expand Up @@ -1120,6 +1124,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
kmem_cache_destroy(dev_priv->requests);
kmem_cache_destroy(dev_priv->vmas);
kmem_cache_destroy(dev_priv->objects);

intel_runtime_pm_put(dev_priv);

kfree(dev_priv);
return ret;
}
Expand Down
39 changes: 35 additions & 4 deletions drivers/gpu/drm/i915/i915_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,8 @@ static int i915_drm_suspend(struct drm_device *dev)
dev_priv->modeset_restore = MODESET_SUSPENDED;
mutex_unlock(&dev_priv->modeset_restore_lock);

disable_rpm_wakeref_asserts(dev_priv);

/* We do a lot of poking in a lot of registers, make sure they work
* properly. */
intel_display_set_init_power(dev_priv, true);
Expand All @@ -589,7 +591,7 @@ static int i915_drm_suspend(struct drm_device *dev)
if (error) {
dev_err(&dev->pdev->dev,
"GEM idle failed, resume might fail\n");
return error;
goto out;
}

intel_guc_suspend(dev);
Expand Down Expand Up @@ -632,7 +634,10 @@ static int i915_drm_suspend(struct drm_device *dev)
if (HAS_CSR(dev_priv))
flush_work(&dev_priv->csr.work);

return 0;
out:
enable_rpm_wakeref_asserts(dev_priv);

return error;
}

static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
Expand All @@ -641,6 +646,8 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
bool fw_csr;
int ret;

disable_rpm_wakeref_asserts(dev_priv);

fw_csr = suspend_to_idle(dev_priv) && dev_priv->csr.dmc_payload;
/*
* In case of firmware assisted context save/restore don't manually
Expand All @@ -659,7 +666,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
if (!fw_csr)
intel_power_domains_init_hw(dev_priv, true);

return ret;
goto out;
}

pci_disable_device(drm_dev->pdev);
Expand All @@ -680,7 +687,10 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)

dev_priv->suspended_to_idle = suspend_to_idle(dev_priv);

return 0;
out:
enable_rpm_wakeref_asserts(dev_priv);

return ret;
}

int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state)
Expand Down Expand Up @@ -711,6 +721,8 @@ static int i915_drm_resume(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;

disable_rpm_wakeref_asserts(dev_priv);

mutex_lock(&dev->struct_mutex);
i915_gem_restore_gtt_mappings(dev);
mutex_unlock(&dev->struct_mutex);
Expand Down Expand Up @@ -775,6 +787,8 @@ static int i915_drm_resume(struct drm_device *dev)

drm_kms_helper_poll_enable(dev);

enable_rpm_wakeref_asserts(dev_priv);

return 0;
}

Expand All @@ -799,6 +813,8 @@ static int i915_drm_resume_early(struct drm_device *dev)

pci_set_master(dev->pdev);

disable_rpm_wakeref_asserts(dev_priv);

if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
ret = vlv_resume_prepare(dev_priv, false);
if (ret)
Expand All @@ -820,6 +836,8 @@ static int i915_drm_resume_early(struct drm_device *dev)
out:
dev_priv->suspended_to_idle = false;

enable_rpm_wakeref_asserts(dev_priv);

return ret;
}

Expand Down Expand Up @@ -1452,6 +1470,9 @@ static int intel_runtime_suspend(struct device *device)

return -EAGAIN;
}

disable_rpm_wakeref_asserts(dev_priv);

/*
* We are safe here against re-faults, since the fault handler takes
* an RPM reference.
Expand All @@ -1471,10 +1492,15 @@ static int intel_runtime_suspend(struct device *device)
DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret);
intel_runtime_pm_enable_interrupts(dev_priv);

enable_rpm_wakeref_asserts(dev_priv);

return ret;
}

intel_uncore_forcewake_reset(dev, false);

enable_rpm_wakeref_asserts(dev_priv);
WARN_ON_ONCE(atomic_read(&dev_priv->pm.wakeref_count));
dev_priv->pm.suspended = true;

/*
Expand Down Expand Up @@ -1518,6 +1544,9 @@ static int intel_runtime_resume(struct device *device)

DRM_DEBUG_KMS("Resuming device\n");

WARN_ON_ONCE(atomic_read(&dev_priv->pm.wakeref_count));
disable_rpm_wakeref_asserts(dev_priv);

intel_opregion_notify_adapter(dev, PCI_D0);
dev_priv->pm.suspended = false;

Expand Down Expand Up @@ -1552,6 +1581,8 @@ static int intel_runtime_resume(struct device *device)

intel_enable_gt_powersave(dev);

enable_rpm_wakeref_asserts(dev_priv);

if (ret)
DRM_ERROR("Runtime resume failed, disabling it (%d)\n", ret);
else
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1605,6 +1605,7 @@ struct skl_wm_level {
* For more, read the Documentation/power/runtime_pm.txt.
*/
struct i915_runtime_pm {
atomic_t wakeref_count;
bool suspended;
bool irqs_enabled;
};
Expand Down
Loading

0 comments on commit 1f814da

Please sign in to comment.