@@ -151,16 +151,7 @@ static const char *timeline_fence_get_timeline_name(struct dma_fence *fence)
static void timeline_fence_release(struct dma_fence *fence)
{
- struct sync_pt *pt = dma_fence_to_sync_pt(fence);
struct sync_timeline *parent = dma_fence_parent(fence);
- unsigned long flags;
-
- spin_lock_irqsave(fence->lock, flags);
- if (!list_empty(&pt->link)) {
- list_del(&pt->link);
- rb_erase(&pt->node, &parent->pt_tree);
- }
- spin_unlock_irqrestore(fence->lock, flags);
sync_timeline_put(parent);
dma_fence_free(fence);
@@ -229,7 +220,6 @@ static const struct dma_fence_ops timeline_fence_ops = {
*/
static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
{
- LIST_HEAD(signalled);
struct sync_pt *pt, *next;
trace_sync_timeline(obj);
@@ -242,20 +232,14 @@ static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
if (!timeline_fence_signaled(&pt->base))
break;
- dma_fence_get(&pt->base);
-
- list_move_tail(&pt->link, &signalled);
+ list_del(&pt->link);
rb_erase(&pt->node, &obj->pt_tree);
dma_fence_signal_locked(&pt->base);
+ dma_fence_put(&pt->base);
}
spin_unlock_irq(&obj->lock);
-
- list_for_each_entry_safe(pt, next, &signalled, link) {
- list_del_init(&pt->link);
- dma_fence_put(&pt->base);
- }
}
/**
@@ -299,13 +283,11 @@ static struct sync_pt *sync_pt_create(struct sync_timeline *obj,
} else if (cmp < 0) {
p = &parent->rb_left;
} else {
- if (dma_fence_get_rcu(&other->base)) {
- sync_timeline_put(obj);
- kfree(pt);
- pt = other;
- goto unlock;
- }
- p = &parent->rb_left;
+ /* This is later put in sw_sync_ioctl_create_fence. */
+ dma_fence_get(&other->base);
+ dma_fence_put(&pt->base);
+ pt = other;
+ goto unlock;
}
}
rb_link_node(&pt->node, parent, p);
@@ -314,6 +296,8 @@ static struct sync_pt *sync_pt_create(struct sync_timeline *obj,
parent = rb_next(&pt->node);
list_add_tail(&pt->link,
parent ? &rb_entry(parent, typeof(*pt), node)->link : &obj->pt_list);
+ /* Adding to the list requires a reference. */
+ dma_fence_get(&pt->base);
}
unlock:
spin_unlock_irq(&obj->lock);
@@ -354,6 +338,7 @@ static int sw_sync_debugfs_release(struct inode *inode, struct file *file)
list_for_each_entry_safe(pt, next, &obj->pt_list, link) {
dma_fence_set_error(&pt->base, -ENOENT);
dma_fence_signal_locked(&pt->base);
+ dma_fence_put(&pt->base);
}
spin_unlock_irq(&obj->lock);
@@ -386,7 +371,14 @@ static long sw_sync_ioctl_create_fence(struct sync_timeline *obj,
}
sync_file = sync_file_create(&pt->base);
+
+ /*
+ * Puts the extra reference returned by sync_pt_create. This is necessary
+ * to avoid a race where the fence is signaled, removed from the list and
+ * released right after sync_pt_create releases the lock and returns.
+ */
dma_fence_put(&pt->base);
+
if (!sync_file) {
err = -ENOMEM;
goto err;