[PATCH] drm: Hold the mutex when dropping the last GEM reference
Chris Wilson
chris at chris-wilson.co.uk
Wed Sep 29 16:09:23 PDT 2010
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
To remmedy this, we could break the kref api and do:
if (atomic_dec_and_test(&obj->refcount)) {
mutex_lock(&dev->mutex);
if (atomic_read(&obj->refcount) == 0)
dev->funcs->free(obj);
mutex_unlock(&dev->mutex);
}
and similarly remove the BUG_ON(kref_get(obj->refcounts)==0).
It is simpler instead to simply rewrite the unlocked unreference variant
to take the dev->struct_mutex around the kref_put.
Note that no driver is currently using the free_unlocked vfunc and it is
scheduled for removal.
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=30454
Reported-by: Magnus Kessler <Magnus.Kessler at gmx.net>
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: stable at kernel.org
---
drivers/gpu/drm/drm_gem.c | 22 ----------------------
include/drm/drmP.h | 9 ++++++---
2 files changed, 6 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index f7e61be..5663d27 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -462,28 +462,6 @@ drm_gem_object_free(struct kref *kref)
}
EXPORT_SYMBOL(drm_gem_object_free);
-/**
- * Called after the last reference to the object has been lost.
- * Must be called without holding struct_mutex
- *
- * Frees the object
- */
-void
-drm_gem_object_free_unlocked(struct kref *kref)
-{
- struct drm_gem_object *obj = (struct drm_gem_object *) kref;
- struct drm_device *dev = obj->dev;
-
- if (dev->driver->gem_free_object_unlocked != NULL)
- dev->driver->gem_free_object_unlocked(obj);
- else if (dev->driver->gem_free_object != NULL) {
- mutex_lock(&dev->struct_mutex);
- dev->driver->gem_free_object(obj);
- mutex_unlock(&dev->struct_mutex);
- }
-}
-EXPORT_SYMBOL(drm_gem_object_free_unlocked);
-
static void drm_gem_object_ref_bug(struct kref *list_kref)
{
BUG();
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 07e4726..76c03a0 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1456,7 +1456,6 @@ int drm_gem_init(struct drm_device *dev);
void drm_gem_destroy(struct drm_device *dev);
void drm_gem_object_release(struct drm_gem_object *obj);
void drm_gem_object_free(struct kref *kref);
-void drm_gem_object_free_unlocked(struct kref *kref);
struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev,
size_t size);
int drm_gem_object_init(struct drm_device *dev,
@@ -1484,8 +1483,12 @@ drm_gem_object_unreference(struct drm_gem_object *obj)
static inline void
drm_gem_object_unreference_unlocked(struct drm_gem_object *obj)
{
- if (obj != NULL)
- kref_put(&obj->refcount, drm_gem_object_free_unlocked);
+ if (obj != NULL) {
+ struct drm_device *dev = obj->dev;
+ mutex_lock(&dev->struct_mutex);
+ kref_put(&obj->refcount, drm_gem_object_free);
+ mutex_unlock(&dev->struct_mutex);
+ }
}
int drm_gem_handle_create(struct drm_file *file_priv,
--
1.7.1
More information about the dri-devel
mailing list