diff mbox

[15/30] staging: sync: Add reference counting to timelines

Message ID 1362098606-26469-16-git-send-email-john.stultz@linaro.org
State Accepted
Commit c5b86b7418f46220a623277718d6a909f520477b
Headers show

Commit Message

John Stultz March 1, 2013, 12:43 a.m. UTC
From: Erik Gilling <konkers@android.com>

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: Greg KH <gregkh@linuxfoundation.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>
---
 drivers/staging/android/sync.c |   29 +++++++++++++----------------
 drivers/staging/android/sync.h |    2 ++
 2 files changed, 15 insertions(+), 16 deletions(-)
diff mbox

Patch

diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c
index 7d4e9aa..61c27bd 100644
--- a/drivers/staging/android/sync.c
+++ b/drivers/staging/android/sync.c
@@ -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);
diff --git a/drivers/staging/android/sync.h b/drivers/staging/android/sync.h
index 00c9bae..15863a6 100644
--- a/drivers/staging/android/sync.h
+++ b/drivers/staging/android/sync.h
@@ -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];