diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-09-30 04:10:26 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-10-01 07:08:45 -0400 |
commit | 39b4d07aa3583ceefe73622841303a0a3e942ca1 (patch) | |
tree | d42f6e782f331b1d967f50ca3a02b9e51ea88515 /include | |
parent | 29d08b3efddca628b0360411ab2b85f7b1723f48 (diff) |
drm: Hold the mutex when dropping the last GEM reference (v2)
In order to be fully threadsafe we need to check that the drm_gem_object
refcount is still 0 after acquiring the mutex in order to call the free
function. Otherwise, we may encounter scenarios like:
Thread A: Thread B:
drm_gem_close
unreference_unlocked
kref_put mutex_lock
... i915_gem_evict
... kref_get -> BUG
... i915_gem_unbind
... kref_put
... i915_gem_object_free
... mutex_unlock
mutex_lock
i915_gem_object_free -> BUG
i915_gem_object_unbind
kfree
mutex_unlock
Note that no driver is currently using the free_unlocked vfunc and it is
scheduled for removal, hasten that process.
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=30454
Reported-and-Tested-by: Magnus Kessler <Magnus.Kessler@gmx.net>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/drm/drmP.h | 10 |
1 files changed, 6 insertions, 4 deletions
diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 07e4726a4ee0..4c9461a4f9e6 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h | |||
@@ -808,7 +808,6 @@ struct drm_driver { | |||
808 | */ | 808 | */ |
809 | int (*gem_init_object) (struct drm_gem_object *obj); | 809 | int (*gem_init_object) (struct drm_gem_object *obj); |
810 | void (*gem_free_object) (struct drm_gem_object *obj); | 810 | void (*gem_free_object) (struct drm_gem_object *obj); |
811 | void (*gem_free_object_unlocked) (struct drm_gem_object *obj); | ||
812 | 811 | ||
813 | /* vga arb irq handler */ | 812 | /* vga arb irq handler */ |
814 | void (*vgaarb_irq)(struct drm_device *dev, bool state); | 813 | void (*vgaarb_irq)(struct drm_device *dev, bool state); |
@@ -1456,7 +1455,6 @@ int drm_gem_init(struct drm_device *dev); | |||
1456 | void drm_gem_destroy(struct drm_device *dev); | 1455 | void drm_gem_destroy(struct drm_device *dev); |
1457 | void drm_gem_object_release(struct drm_gem_object *obj); | 1456 | void drm_gem_object_release(struct drm_gem_object *obj); |
1458 | void drm_gem_object_free(struct kref *kref); | 1457 | void drm_gem_object_free(struct kref *kref); |
1459 | void drm_gem_object_free_unlocked(struct kref *kref); | ||
1460 | struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, | 1458 | struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, |
1461 | size_t size); | 1459 | size_t size); |
1462 | int drm_gem_object_init(struct drm_device *dev, | 1460 | int drm_gem_object_init(struct drm_device *dev, |
@@ -1484,8 +1482,12 @@ drm_gem_object_unreference(struct drm_gem_object *obj) | |||
1484 | static inline void | 1482 | static inline void |
1485 | drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) | 1483 | drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) |
1486 | { | 1484 | { |
1487 | if (obj != NULL) | 1485 | if (obj != NULL) { |
1488 | kref_put(&obj->refcount, drm_gem_object_free_unlocked); | 1486 | struct drm_device *dev = obj->dev; |
1487 | mutex_lock(&dev->struct_mutex); | ||
1488 | kref_put(&obj->refcount, drm_gem_object_free); | ||
1489 | mutex_unlock(&dev->struct_mutex); | ||
1490 | } | ||
1489 | } | 1491 | } |
1490 | 1492 | ||
1491 | int drm_gem_handle_create(struct drm_file *file_priv, | 1493 | int drm_gem_handle_create(struct drm_file *file_priv, |