diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-01-04 13:57:56 -0500 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2010-01-06 12:40:10 -0500 |
commit | 9ea8d05932c082a7ccbd9dc2e10687c88a70bd13 (patch) | |
tree | f41ae389dac10fb80e0d32d6b4f47da4d51889da /drivers/gpu/drm/i915/intel_display.c | |
parent | 29bd0ae25f8cb96b63560c2cbccec77b425e1603 (diff) |
drm/i915: Hold struct mutex whilst pinning power context bo.
Hugh found an error path where we were attempting to unref a bo without
holding the struct mutex:
[drm:intel_init_clock_gating] *ERROR* failed to pin power context: -16
------------[ cut here ]------------
WARNING: at drivers/gpu/drm/drm_gem.c:438 drm_gem_object_free+0x20/0x5e()
Hardware name: ESPRIMO Mobile V5505
Modules linked in: snd_pcm_oss snd_mixer_oss snd_seq snd_seq_device
Pid: 3793, comm: s2ram Not tainted 2.6.33-rc2 #4
Call Trace:
[<7815298e>] warn_slowpath_common+0x59/0x6b
[<781529b3>] warn_slowpath_null+0x13/0x18
[<78317c1a>] ? drm_gem_object_free+0x20/0x5e
[<78317c1a>] drm_gem_object_free+0x20/0x5e
[<78317bfa>] ? drm_gem_object_free+0x0/0x5e
[<7829df11>] kref_put+0x38/0x45
[<7833a5f0>] intel_init_clock_gating+0x232/0x271
[<78317bfa>] ? drm_gem_object_free+0x0/0x5e
[<7832c307>] i915_restore_state+0x21a/0x2b3
[<7832379d>] i915_resume+0x3c/0xbb
[<78174fe5>] ? trace_hardirqs_on_caller+0xfc/0x123
[<7831c756>] ? drm_class_resume+0x0/0x3e
[<7831c78d>] drm_class_resume+0x37/0x3e
[<78351e0a>] legacy_resume+0x1e/0x51
[<78351ece>] device_resume+0x91/0xab
[<7831c756>] ? drm_class_resume+0x0/0x3e
[<78352226>] dpm_resume+0x58/0x10f
[<783522fb>] dpm_resume_end+0x1e/0x2c
[<78180f80>] suspend_devices_and_enter+0x61/0x84
[<78180ff8>] enter_state+0x55/0x83
[<7818091c>] state_store+0x94/0xaa
[<7829d09e>] kobj_attr_store+0x1e/0x23
[<782098e0>] sysfs_write_file+0x66/0x99
[<781cd2f0>] vfs_write+0x8a/0x108
[<781cd408>] sys_write+0x3c/0x63
[<78125c10>] sysenter_do_call+0x12/0x36
---[ end trace a343537f29950fda ]---
It is in fact slightly more insiduous that first appears since we are
attempting to not just free the object without the lock, but are trying
to do the whole bo manipulation without holding the lock.
Reported-by: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 73 |
1 files changed, 47 insertions, 26 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index af61dd915f2b..84705b7e01ec 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -4414,6 +4414,42 @@ static const struct drm_mode_config_funcs intel_mode_funcs = { | |||
4414 | .fb_changed = intelfb_probe, | 4414 | .fb_changed = intelfb_probe, |
4415 | }; | 4415 | }; |
4416 | 4416 | ||
4417 | static struct drm_gem_object * | ||
4418 | intel_alloc_power_context(struct drm_device *dev) | ||
4419 | { | ||
4420 | struct drm_gem_object *pwrctx; | ||
4421 | int ret; | ||
4422 | |||
4423 | pwrctx = drm_gem_object_alloc(dev, 4096); | ||
4424 | if (!pwrctx) { | ||
4425 | DRM_DEBUG("failed to alloc power context, RC6 disabled\n"); | ||
4426 | return NULL; | ||
4427 | } | ||
4428 | |||
4429 | mutex_lock(&dev->struct_mutex); | ||
4430 | ret = i915_gem_object_pin(pwrctx, 4096); | ||
4431 | if (ret) { | ||
4432 | DRM_ERROR("failed to pin power context: %d\n", ret); | ||
4433 | goto err_unref; | ||
4434 | } | ||
4435 | |||
4436 | ret = i915_gem_object_set_to_gtt_domain(pwrctx, 1); | ||
4437 | if (ret) { | ||
4438 | DRM_ERROR("failed to set-domain on power context: %d\n", ret); | ||
4439 | goto err_unpin; | ||
4440 | } | ||
4441 | mutex_unlock(&dev->struct_mutex); | ||
4442 | |||
4443 | return pwrctx; | ||
4444 | |||
4445 | err_unpin: | ||
4446 | i915_gem_object_unpin(pwrctx); | ||
4447 | err_unref: | ||
4448 | drm_gem_object_unreference(pwrctx); | ||
4449 | mutex_unlock(&dev->struct_mutex); | ||
4450 | return NULL; | ||
4451 | } | ||
4452 | |||
4417 | void intel_init_clock_gating(struct drm_device *dev) | 4453 | void intel_init_clock_gating(struct drm_device *dev) |
4418 | { | 4454 | { |
4419 | struct drm_i915_private *dev_priv = dev->dev_private; | 4455 | struct drm_i915_private *dev_priv = dev->dev_private; |
@@ -4467,41 +4503,26 @@ void intel_init_clock_gating(struct drm_device *dev) | |||
4467 | * to save state. | 4503 | * to save state. |
4468 | */ | 4504 | */ |
4469 | if (I915_HAS_RC6(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) { | 4505 | if (I915_HAS_RC6(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) { |
4470 | struct drm_gem_object *pwrctx; | 4506 | struct drm_i915_gem_object *obj_priv = NULL; |
4471 | struct drm_i915_gem_object *obj_priv; | ||
4472 | int ret; | ||
4473 | 4507 | ||
4474 | if (dev_priv->pwrctx) { | 4508 | if (dev_priv->pwrctx) { |
4475 | obj_priv = dev_priv->pwrctx->driver_private; | 4509 | obj_priv = dev_priv->pwrctx->driver_private; |
4476 | } else { | 4510 | } else { |
4477 | pwrctx = drm_gem_object_alloc(dev, 4096); | 4511 | struct drm_gem_object *pwrctx; |
4478 | if (!pwrctx) { | ||
4479 | DRM_DEBUG("failed to alloc power context, " | ||
4480 | "RC6 disabled\n"); | ||
4481 | goto out; | ||
4482 | } | ||
4483 | 4512 | ||
4484 | ret = i915_gem_object_pin(pwrctx, 4096); | 4513 | pwrctx = intel_alloc_power_context(dev); |
4485 | if (ret) { | 4514 | if (pwrctx) { |
4486 | DRM_ERROR("failed to pin power context: %d\n", | 4515 | dev_priv->pwrctx = pwrctx; |
4487 | ret); | 4516 | obj_priv = pwrctx->driver_private; |
4488 | drm_gem_object_unreference(pwrctx); | ||
4489 | goto out; | ||
4490 | } | 4517 | } |
4491 | |||
4492 | i915_gem_object_set_to_gtt_domain(pwrctx, 1); | ||
4493 | |||
4494 | dev_priv->pwrctx = pwrctx; | ||
4495 | obj_priv = pwrctx->driver_private; | ||
4496 | } | 4518 | } |
4497 | 4519 | ||
4498 | I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN); | 4520 | if (obj_priv) { |
4499 | I915_WRITE(MCHBAR_RENDER_STANDBY, | 4521 | I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN); |
4500 | I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT); | 4522 | I915_WRITE(MCHBAR_RENDER_STANDBY, |
4523 | I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT); | ||
4524 | } | ||
4501 | } | 4525 | } |
4502 | |||
4503 | out: | ||
4504 | return; | ||
4505 | } | 4526 | } |
4506 | 4527 | ||
4507 | /* Set up chip specific display functions */ | 4528 | /* Set up chip specific display functions */ |