Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39e16ba1 authored by Christian König's avatar Christian König Committed by Alex Deucher
Browse files

dma-buf: make reservation_object_copy_fences rcu save

parent 6d281b1f
Loading
Loading
Loading
Loading
+42 −14
Original line number Diff line number Diff line
@@ -266,8 +266,7 @@ EXPORT_SYMBOL(reservation_object_add_excl_fence);
* @dst: the destination reservation object
* @src: the source reservation object
*
* Copy all fences from src to dst. Both src->lock as well as dst-lock must be
* held.
* Copy all fences from src to dst. dst-lock must be held.
*/
int reservation_object_copy_fences(struct reservation_object *dst,
				   struct reservation_object *src)
@@ -277,33 +276,62 @@ int reservation_object_copy_fences(struct reservation_object *dst,
	size_t size;
	unsigned i;

	src_list = reservation_object_get_list(src);
	rcu_read_lock();
	src_list = rcu_dereference(src->fence);

retry:
	if (src_list) {
		size = offsetof(typeof(*src_list),
				shared[src_list->shared_count]);
		unsigned shared_count = src_list->shared_count;

		size = offsetof(typeof(*src_list), shared[shared_count]);
		rcu_read_unlock();

		dst_list = kmalloc(size, GFP_KERNEL);
		if (!dst_list)
			return -ENOMEM;

		dst_list->shared_count = src_list->shared_count;
		dst_list->shared_max = src_list->shared_count;
		for (i = 0; i < src_list->shared_count; ++i)
			dst_list->shared[i] =
				dma_fence_get(src_list->shared[i]);
		rcu_read_lock();
		src_list = rcu_dereference(src->fence);
		if (!src_list || src_list->shared_count > shared_count) {
			kfree(dst_list);
			goto retry;
		}

		dst_list->shared_count = 0;
		dst_list->shared_max = shared_count;
		for (i = 0; i < src_list->shared_count; ++i) {
			struct dma_fence *fence;

			fence = rcu_dereference(src_list->shared[i]);
			if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
				     &fence->flags))
				continue;

			if (!dma_fence_get_rcu(fence)) {
				kfree(dst_list);
				src_list = rcu_dereference(src->fence);
				goto retry;
			}

			if (dma_fence_is_signaled(fence)) {
				dma_fence_put(fence);
				continue;
			}

			dst_list->shared[dst_list->shared_count++] = fence;
		}
	} else {
		dst_list = NULL;
	}

	new = dma_fence_get_rcu_safe(&src->fence_excl);
	rcu_read_unlock();

	kfree(dst->staged);
	dst->staged = NULL;

	src_list = reservation_object_get_list(dst);

	old = reservation_object_get_excl(dst);
	new = reservation_object_get_excl(src);

	dma_fence_get(new);

	preempt_disable();
	write_seqcount_begin(&dst->seq);