staging: sync: Add reference counting to timelines
authorErik Gilling <konkers@android.com>
Fri, 1 Mar 2013 00:43:11 +0000 (16:43 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 4 Mar 2013 09:44:07 +0000 (17:44 +0800)
If a timeline is destroyed while fences still hold pts on it, the reworked
fence release handler can cause the timeline to be freed before all it's points
are freed.

Cc: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Cc: Erik Gilling <konkers@android.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Rob Clark <robclark@gmail.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: dri-devel@lists.freedesktop.org
Cc: Android Kernel Team <kernel-team@android.com>
Signed-off-by: Erik Gilling <konkers@android.com>
[jstultz: Squished in compiler warning fix]
Signed-off-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/android/sync.c
drivers/staging/android/sync.h

index 7d4e9aaa5368627745094cd668dd45c67d32ebdb..61c27bdc5d0bafe19f2bd47592fe3cc78c8c12c7 100644 (file)
@@ -51,6 +51,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
        if (obj == NULL)
                return NULL;
 
+       kref_init(&obj->kref);
        obj->ops = ops;
        strlcpy(obj->name, name, sizeof(obj->name));
 
@@ -68,8 +69,10 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
 }
 EXPORT_SYMBOL(sync_timeline_create);
 
-static void sync_timeline_free(struct sync_timeline *obj)
+static void sync_timeline_free(struct kref *kref)
 {
+       struct sync_timeline *obj =
+               container_of(kref, struct sync_timeline, kref);
        unsigned long flags;
 
        if (obj->ops->release_obj)
@@ -84,17 +87,14 @@ static void sync_timeline_free(struct sync_timeline *obj)
 
 void sync_timeline_destroy(struct sync_timeline *obj)
 {
-       unsigned long flags;
-       bool needs_freeing;
-
-       spin_lock_irqsave(&obj->child_list_lock, flags);
        obj->destroyed = true;
-       needs_freeing = list_empty(&obj->child_list_head);
-       spin_unlock_irqrestore(&obj->child_list_lock, flags);
 
-       if (needs_freeing)
-               sync_timeline_free(obj);
-       else
+       /*
+        * If this is not the last reference, signal any children
+        * that their parent is going away.
+        */
+
+       if (!kref_put(&obj->kref, sync_timeline_free))
                sync_timeline_signal(obj);
 }
 EXPORT_SYMBOL(sync_timeline_destroy);
@@ -114,7 +114,6 @@ static void sync_timeline_remove_pt(struct sync_pt *pt)
 {
        struct sync_timeline *obj = pt->parent;
        unsigned long flags;
-       bool needs_freeing = false;
 
        spin_lock_irqsave(&obj->active_list_lock, flags);
        if (!list_empty(&pt->active_list))
@@ -124,13 +123,8 @@ static void sync_timeline_remove_pt(struct sync_pt *pt)
        spin_lock_irqsave(&obj->child_list_lock, flags);
        if (!list_empty(&pt->child_list)) {
                list_del_init(&pt->child_list);
-               needs_freeing = obj->destroyed &&
-                       list_empty(&obj->child_list_head);
        }
        spin_unlock_irqrestore(&obj->child_list_lock, flags);
-
-       if (needs_freeing)
-               sync_timeline_free(obj);
 }
 
 void sync_timeline_signal(struct sync_timeline *obj)
@@ -177,6 +171,7 @@ struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size)
                return NULL;
 
        INIT_LIST_HEAD(&pt->active_list);
+       kref_get(&parent->kref);
        sync_timeline_add_pt(parent, pt);
 
        return pt;
@@ -190,6 +185,8 @@ void sync_pt_free(struct sync_pt *pt)
 
        sync_timeline_remove_pt(pt);
 
+       kref_put(&pt->parent->kref, sync_timeline_free);
+
        kfree(pt);
 }
 EXPORT_SYMBOL(sync_pt_free);
index 00c9bae9706510dbf086b6e531704b685756ba09..15863a6ebe5149ce9c93067f8321448f12101cae 100644 (file)
@@ -80,6 +80,7 @@ struct sync_timeline_ops {
 
 /**
  * struct sync_timeline - sync object
+ * @kref:              reference count on fence.
  * @ops:               ops that define the implementaiton of the sync_timeline
  * @name:              name of the sync_timeline. Useful for debugging
  * @destoryed:         set when sync_timeline is destroyed
@@ -90,6 +91,7 @@ struct sync_timeline_ops {
  * @sync_timeline_list:        membership in global sync_timeline_list
  */
 struct sync_timeline {
+       struct kref             kref;
        const struct sync_timeline_ops  *ops;
        char                    name[32];