forked from mirrors/linux
		
	reservation: add suppport for read-only access using rcu
This adds some extra functions to deal with rcu. reservation_object_get_fences_rcu() will obtain the list of shared and exclusive fences without obtaining the ww_mutex. reservation_object_wait_timeout_rcu() will wait on all fences of the reservation_object, without obtaining the ww_mutex. reservation_object_test_signaled_rcu() will test if all fences of the reservation_object are signaled without using the ww_mutex. reservation_object_get_excl and reservation_object_get_list require the reservation object to be held, updating requires write_seqcount_begin/end. If only the exclusive fence is needed, rcu_dereference followed by fence_get_rcu can be used, if the shared fences are needed it's recommended to use the supplied functions. Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com> Acked-by: Sumit Semwal <sumit.semwal@linaro.org> Acked-by: Daniel Vetter <daniel@ffwll.ch> Reviewed-By: Thomas Hellstrom <thellstrom@vmware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									04a5faa8cb
								
							
						
					
					
						commit
						3c3b177a93
					
				
					 5 changed files with 400 additions and 54 deletions
				
			
		| 
						 | 
					@ -137,7 +137,7 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
 | 
				
			||||||
	struct reservation_object_list *fobj;
 | 
						struct reservation_object_list *fobj;
 | 
				
			||||||
	struct fence *fence_excl;
 | 
						struct fence *fence_excl;
 | 
				
			||||||
	unsigned long events;
 | 
						unsigned long events;
 | 
				
			||||||
	unsigned shared_count;
 | 
						unsigned shared_count, seq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dmabuf = file->private_data;
 | 
						dmabuf = file->private_data;
 | 
				
			||||||
	if (!dmabuf || !dmabuf->resv)
 | 
						if (!dmabuf || !dmabuf->resv)
 | 
				
			||||||
| 
						 | 
					@ -151,14 +151,20 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
 | 
				
			||||||
	if (!events)
 | 
						if (!events)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ww_mutex_lock(&resv->lock, NULL);
 | 
					retry:
 | 
				
			||||||
 | 
						seq = read_seqcount_begin(&resv->seq);
 | 
				
			||||||
	fobj = resv->fence;
 | 
						rcu_read_lock();
 | 
				
			||||||
	if (!fobj)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fobj = rcu_dereference(resv->fence);
 | 
				
			||||||
 | 
						if (fobj)
 | 
				
			||||||
		shared_count = fobj->shared_count;
 | 
							shared_count = fobj->shared_count;
 | 
				
			||||||
	fence_excl = resv->fence_excl;
 | 
						else
 | 
				
			||||||
 | 
							shared_count = 0;
 | 
				
			||||||
 | 
						fence_excl = rcu_dereference(resv->fence_excl);
 | 
				
			||||||
 | 
						if (read_seqcount_retry(&resv->seq, seq)) {
 | 
				
			||||||
 | 
							rcu_read_unlock();
 | 
				
			||||||
 | 
							goto retry;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fence_excl && (!(events & POLLOUT) || shared_count == 0)) {
 | 
						if (fence_excl && (!(events & POLLOUT) || shared_count == 0)) {
 | 
				
			||||||
		struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl;
 | 
							struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl;
 | 
				
			||||||
| 
						 | 
					@ -176,14 +182,20 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
 | 
				
			||||||
		spin_unlock_irq(&dmabuf->poll.lock);
 | 
							spin_unlock_irq(&dmabuf->poll.lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (events & pevents) {
 | 
							if (events & pevents) {
 | 
				
			||||||
			if (!fence_add_callback(fence_excl, &dcb->cb,
 | 
								if (!fence_get_rcu(fence_excl)) {
 | 
				
			||||||
 | 
									/* force a recheck */
 | 
				
			||||||
 | 
									events &= ~pevents;
 | 
				
			||||||
 | 
									dma_buf_poll_cb(NULL, &dcb->cb);
 | 
				
			||||||
 | 
								} else if (!fence_add_callback(fence_excl, &dcb->cb,
 | 
				
			||||||
						       dma_buf_poll_cb)) {
 | 
											       dma_buf_poll_cb)) {
 | 
				
			||||||
				events &= ~pevents;
 | 
									events &= ~pevents;
 | 
				
			||||||
 | 
									fence_put(fence_excl);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				/*
 | 
									/*
 | 
				
			||||||
				 * No callback queued, wake up any additional
 | 
									 * No callback queued, wake up any additional
 | 
				
			||||||
				 * waiters.
 | 
									 * waiters.
 | 
				
			||||||
				 */
 | 
									 */
 | 
				
			||||||
 | 
									fence_put(fence_excl);
 | 
				
			||||||
				dma_buf_poll_cb(NULL, &dcb->cb);
 | 
									dma_buf_poll_cb(NULL, &dcb->cb);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -205,13 +217,26 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (i = 0; i < shared_count; ++i) {
 | 
							for (i = 0; i < shared_count; ++i) {
 | 
				
			||||||
			struct fence *fence = fobj->shared[i];
 | 
								struct fence *fence = rcu_dereference(fobj->shared[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!fence_get_rcu(fence)) {
 | 
				
			||||||
 | 
									/*
 | 
				
			||||||
 | 
									 * fence refcount dropped to zero, this means
 | 
				
			||||||
 | 
									 * that fobj has been freed
 | 
				
			||||||
 | 
									 *
 | 
				
			||||||
 | 
									 * call dma_buf_poll_cb and force a recheck!
 | 
				
			||||||
 | 
									 */
 | 
				
			||||||
 | 
									events &= ~POLLOUT;
 | 
				
			||||||
 | 
									dma_buf_poll_cb(NULL, &dcb->cb);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if (!fence_add_callback(fence, &dcb->cb,
 | 
								if (!fence_add_callback(fence, &dcb->cb,
 | 
				
			||||||
						dma_buf_poll_cb)) {
 | 
											dma_buf_poll_cb)) {
 | 
				
			||||||
 | 
									fence_put(fence);
 | 
				
			||||||
				events &= ~POLLOUT;
 | 
									events &= ~POLLOUT;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								fence_put(fence);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* No callback queued, wake up any additional waiters. */
 | 
							/* No callback queued, wake up any additional waiters. */
 | 
				
			||||||
| 
						 | 
					@ -220,7 +245,7 @@ static unsigned int dma_buf_poll(struct file *file, poll_table *poll)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	ww_mutex_unlock(&resv->lock);
 | 
						rcu_read_unlock();
 | 
				
			||||||
	return events;
 | 
						return events;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -184,7 +184,7 @@ EXPORT_SYMBOL(fence_release);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void fence_free(struct fence *fence)
 | 
					void fence_free(struct fence *fence)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	kfree(fence);
 | 
						kfree_rcu(fence, rcu);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(fence_free);
 | 
					EXPORT_SYMBOL(fence_free);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,6 +38,11 @@
 | 
				
			||||||
DEFINE_WW_CLASS(reservation_ww_class);
 | 
					DEFINE_WW_CLASS(reservation_ww_class);
 | 
				
			||||||
EXPORT_SYMBOL(reservation_ww_class);
 | 
					EXPORT_SYMBOL(reservation_ww_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lock_class_key reservation_seqcount_class;
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(reservation_seqcount_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char reservation_seqcount_string[] = "reservation_seqcount";
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(reservation_seqcount_string);
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Reserve space to add a shared fence to a reservation_object,
 | 
					 * Reserve space to add a shared fence to a reservation_object,
 | 
				
			||||||
 * must be called with obj->lock held.
 | 
					 * must be called with obj->lock held.
 | 
				
			||||||
| 
						 | 
					@ -82,27 +87,37 @@ reservation_object_add_shared_inplace(struct reservation_object *obj,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u32 i;
 | 
						u32 i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < fobj->shared_count; ++i) {
 | 
					 | 
				
			||||||
		if (fobj->shared[i]->context == fence->context) {
 | 
					 | 
				
			||||||
			struct fence *old_fence = fobj->shared[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fence_get(fence);
 | 
						fence_get(fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fobj->shared[i] = fence;
 | 
						preempt_disable();
 | 
				
			||||||
 | 
						write_seqcount_begin(&obj->seq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < fobj->shared_count; ++i) {
 | 
				
			||||||
 | 
							struct fence *old_fence;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							old_fence = rcu_dereference_protected(fobj->shared[i],
 | 
				
			||||||
 | 
											reservation_object_held(obj));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (old_fence->context == fence->context) {
 | 
				
			||||||
 | 
								/* memory barrier is added by write_seqcount_begin */
 | 
				
			||||||
 | 
								RCU_INIT_POINTER(fobj->shared[i], fence);
 | 
				
			||||||
 | 
								write_seqcount_end(&obj->seq);
 | 
				
			||||||
 | 
								preempt_enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			fence_put(old_fence);
 | 
								fence_put(old_fence);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fence_get(fence);
 | 
					 | 
				
			||||||
	fobj->shared[fobj->shared_count] = fence;
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * make the new fence visible before incrementing
 | 
						 * memory barrier is added by write_seqcount_begin,
 | 
				
			||||||
	 * fobj->shared_count
 | 
						 * fobj->shared_count is protected by this lock too
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	smp_wmb();
 | 
						RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence);
 | 
				
			||||||
	fobj->shared_count++;
 | 
						fobj->shared_count++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						write_seqcount_end(&obj->seq);
 | 
				
			||||||
 | 
						preempt_enable();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -112,11 +127,12 @@ reservation_object_add_shared_replace(struct reservation_object *obj,
 | 
				
			||||||
				      struct fence *fence)
 | 
									      struct fence *fence)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned i;
 | 
						unsigned i;
 | 
				
			||||||
 | 
						struct fence *old_fence = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fence_get(fence);
 | 
						fence_get(fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!old) {
 | 
						if (!old) {
 | 
				
			||||||
		fobj->shared[0] = fence;
 | 
							RCU_INIT_POINTER(fobj->shared[0], fence);
 | 
				
			||||||
		fobj->shared_count = 1;
 | 
							fobj->shared_count = 1;
 | 
				
			||||||
		goto done;
 | 
							goto done;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -130,19 +146,38 @@ reservation_object_add_shared_replace(struct reservation_object *obj,
 | 
				
			||||||
	fobj->shared_count = old->shared_count;
 | 
						fobj->shared_count = old->shared_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < old->shared_count; ++i) {
 | 
						for (i = 0; i < old->shared_count; ++i) {
 | 
				
			||||||
		if (fence && old->shared[i]->context == fence->context) {
 | 
							struct fence *check;
 | 
				
			||||||
			fence_put(old->shared[i]);
 | 
					
 | 
				
			||||||
			fobj->shared[i] = fence;
 | 
							check = rcu_dereference_protected(old->shared[i],
 | 
				
			||||||
			fence = NULL;
 | 
											reservation_object_held(obj));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!old_fence && check->context == fence->context) {
 | 
				
			||||||
 | 
								old_fence = check;
 | 
				
			||||||
 | 
								RCU_INIT_POINTER(fobj->shared[i], fence);
 | 
				
			||||||
		} else
 | 
							} else
 | 
				
			||||||
			fobj->shared[i] = old->shared[i];
 | 
								RCU_INIT_POINTER(fobj->shared[i], check);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!old_fence) {
 | 
				
			||||||
 | 
							RCU_INIT_POINTER(fobj->shared[fobj->shared_count], fence);
 | 
				
			||||||
 | 
							fobj->shared_count++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (fence)
 | 
					 | 
				
			||||||
		fobj->shared[fobj->shared_count++] = fence;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
done:
 | 
					done:
 | 
				
			||||||
	obj->fence = fobj;
 | 
						preempt_disable();
 | 
				
			||||||
	kfree(old);
 | 
						write_seqcount_begin(&obj->seq);
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * RCU_INIT_POINTER can be used here,
 | 
				
			||||||
 | 
						 * seqcount provides the necessary barriers
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						RCU_INIT_POINTER(obj->fence, fobj);
 | 
				
			||||||
 | 
						write_seqcount_end(&obj->seq);
 | 
				
			||||||
 | 
						preempt_enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (old)
 | 
				
			||||||
 | 
							kfree_rcu(old, rcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (old_fence)
 | 
				
			||||||
 | 
							fence_put(old_fence);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -158,7 +193,7 @@ void reservation_object_add_shared_fence(struct reservation_object *obj,
 | 
				
			||||||
	obj->staged = NULL;
 | 
						obj->staged = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!fobj) {
 | 
						if (!fobj) {
 | 
				
			||||||
		BUG_ON(old->shared_count == old->shared_max);
 | 
							BUG_ON(old->shared_count >= old->shared_max);
 | 
				
			||||||
		reservation_object_add_shared_inplace(obj, old, fence);
 | 
							reservation_object_add_shared_inplace(obj, old, fence);
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		reservation_object_add_shared_replace(obj, old, fobj, fence);
 | 
							reservation_object_add_shared_replace(obj, old, fobj, fence);
 | 
				
			||||||
| 
						 | 
					@ -168,26 +203,275 @@ EXPORT_SYMBOL(reservation_object_add_shared_fence);
 | 
				
			||||||
void reservation_object_add_excl_fence(struct reservation_object *obj,
 | 
					void reservation_object_add_excl_fence(struct reservation_object *obj,
 | 
				
			||||||
				       struct fence *fence)
 | 
									       struct fence *fence)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct fence *old_fence = obj->fence_excl;
 | 
						struct fence *old_fence = reservation_object_get_excl(obj);
 | 
				
			||||||
	struct reservation_object_list *old;
 | 
						struct reservation_object_list *old;
 | 
				
			||||||
	u32 i = 0;
 | 
						u32 i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	old = reservation_object_get_list(obj);
 | 
						old = reservation_object_get_list(obj);
 | 
				
			||||||
	if (old) {
 | 
						if (old)
 | 
				
			||||||
		i = old->shared_count;
 | 
							i = old->shared_count;
 | 
				
			||||||
		old->shared_count = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fence)
 | 
						if (fence)
 | 
				
			||||||
		fence_get(fence);
 | 
							fence_get(fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	obj->fence_excl = fence;
 | 
						preempt_disable();
 | 
				
			||||||
 | 
						write_seqcount_begin(&obj->seq);
 | 
				
			||||||
 | 
						/* write_seqcount_begin provides the necessary memory barrier */
 | 
				
			||||||
 | 
						RCU_INIT_POINTER(obj->fence_excl, fence);
 | 
				
			||||||
 | 
						if (old)
 | 
				
			||||||
 | 
							old->shared_count = 0;
 | 
				
			||||||
 | 
						write_seqcount_end(&obj->seq);
 | 
				
			||||||
 | 
						preempt_enable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* inplace update, no shared fences */
 | 
						/* inplace update, no shared fences */
 | 
				
			||||||
	while (i--)
 | 
						while (i--)
 | 
				
			||||||
		fence_put(old->shared[i]);
 | 
							fence_put(rcu_dereference_protected(old->shared[i],
 | 
				
			||||||
 | 
											reservation_object_held(obj)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (old_fence)
 | 
						if (old_fence)
 | 
				
			||||||
		fence_put(old_fence);
 | 
							fence_put(old_fence);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(reservation_object_add_excl_fence);
 | 
					EXPORT_SYMBOL(reservation_object_add_excl_fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int reservation_object_get_fences_rcu(struct reservation_object *obj,
 | 
				
			||||||
 | 
									      struct fence **pfence_excl,
 | 
				
			||||||
 | 
									      unsigned *pshared_count,
 | 
				
			||||||
 | 
									      struct fence ***pshared)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned shared_count = 0;
 | 
				
			||||||
 | 
						unsigned retry = 1;
 | 
				
			||||||
 | 
						struct fence **shared = NULL, *fence_excl = NULL;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (retry) {
 | 
				
			||||||
 | 
							struct reservation_object_list *fobj;
 | 
				
			||||||
 | 
							unsigned seq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							seq = read_seqcount_begin(&obj->seq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fobj = rcu_dereference(obj->fence);
 | 
				
			||||||
 | 
							if (fobj) {
 | 
				
			||||||
 | 
								struct fence **nshared;
 | 
				
			||||||
 | 
								size_t sz = sizeof(*shared) * fobj->shared_max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								nshared = krealloc(shared, sz,
 | 
				
			||||||
 | 
										   GFP_NOWAIT | __GFP_NOWARN);
 | 
				
			||||||
 | 
								if (!nshared) {
 | 
				
			||||||
 | 
									rcu_read_unlock();
 | 
				
			||||||
 | 
									nshared = krealloc(shared, sz, GFP_KERNEL);
 | 
				
			||||||
 | 
									if (nshared) {
 | 
				
			||||||
 | 
										shared = nshared;
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ret = -ENOMEM;
 | 
				
			||||||
 | 
									shared_count = 0;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								shared = nshared;
 | 
				
			||||||
 | 
								memcpy(shared, fobj->shared, sz);
 | 
				
			||||||
 | 
								shared_count = fobj->shared_count;
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								shared_count = 0;
 | 
				
			||||||
 | 
							fence_excl = rcu_dereference(obj->fence_excl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							retry = read_seqcount_retry(&obj->seq, seq);
 | 
				
			||||||
 | 
							if (retry)
 | 
				
			||||||
 | 
								goto unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!fence_excl || fence_get_rcu(fence_excl)) {
 | 
				
			||||||
 | 
								unsigned i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (i = 0; i < shared_count; ++i) {
 | 
				
			||||||
 | 
									if (fence_get_rcu(shared[i]))
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/* uh oh, refcount failed, abort and retry */
 | 
				
			||||||
 | 
									while (i--)
 | 
				
			||||||
 | 
										fence_put(shared[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (fence_excl) {
 | 
				
			||||||
 | 
										fence_put(fence_excl);
 | 
				
			||||||
 | 
										fence_excl = NULL;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									retry = 1;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								retry = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock:
 | 
				
			||||||
 | 
							rcu_read_unlock();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*pshared_count = shared_count;
 | 
				
			||||||
 | 
						if (shared_count)
 | 
				
			||||||
 | 
							*pshared = shared;
 | 
				
			||||||
 | 
						else {
 | 
				
			||||||
 | 
							*pshared = NULL;
 | 
				
			||||||
 | 
							kfree(shared);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*pfence_excl = fence_excl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(reservation_object_get_fences_rcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					long reservation_object_wait_timeout_rcu(struct reservation_object *obj,
 | 
				
			||||||
 | 
										 bool wait_all, bool intr,
 | 
				
			||||||
 | 
										 unsigned long timeout)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fence *fence;
 | 
				
			||||||
 | 
						unsigned seq, shared_count, i = 0;
 | 
				
			||||||
 | 
						long ret = timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					retry:
 | 
				
			||||||
 | 
						fence = NULL;
 | 
				
			||||||
 | 
						shared_count = 0;
 | 
				
			||||||
 | 
						seq = read_seqcount_begin(&obj->seq);
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (wait_all) {
 | 
				
			||||||
 | 
							struct reservation_object_list *fobj = rcu_dereference(obj->fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fobj)
 | 
				
			||||||
 | 
								shared_count = fobj->shared_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (read_seqcount_retry(&obj->seq, seq))
 | 
				
			||||||
 | 
								goto unlock_retry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < shared_count; ++i) {
 | 
				
			||||||
 | 
								struct fence *lfence = rcu_dereference(fobj->shared[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!fence_get_rcu(lfence))
 | 
				
			||||||
 | 
									goto unlock_retry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (fence_is_signaled(lfence)) {
 | 
				
			||||||
 | 
									fence_put(lfence);
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fence = lfence;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!shared_count) {
 | 
				
			||||||
 | 
							struct fence *fence_excl = rcu_dereference(obj->fence_excl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (read_seqcount_retry(&obj->seq, seq))
 | 
				
			||||||
 | 
								goto unlock_retry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fence_excl &&
 | 
				
			||||||
 | 
							    !test_bit(FENCE_FLAG_SIGNALED_BIT, &fence_excl->flags)) {
 | 
				
			||||||
 | 
								if (!fence_get_rcu(fence_excl))
 | 
				
			||||||
 | 
									goto unlock_retry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (fence_is_signaled(fence_excl))
 | 
				
			||||||
 | 
									fence_put(fence_excl);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									fence = fence_excl;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						if (fence) {
 | 
				
			||||||
 | 
							ret = fence_wait_timeout(fence, intr, ret);
 | 
				
			||||||
 | 
							fence_put(fence);
 | 
				
			||||||
 | 
							if (ret > 0 && wait_all && (i + 1 < shared_count))
 | 
				
			||||||
 | 
								goto retry;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock_retry:
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						goto retry;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(reservation_object_wait_timeout_rcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int
 | 
				
			||||||
 | 
					reservation_object_test_signaled_single(struct fence *passed_fence)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct fence *fence, *lfence = passed_fence;
 | 
				
			||||||
 | 
						int ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags)) {
 | 
				
			||||||
 | 
							int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fence = fence_get_rcu(lfence);
 | 
				
			||||||
 | 
							if (!fence)
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = !!fence_is_signaled(fence);
 | 
				
			||||||
 | 
							fence_put(fence);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool reservation_object_test_signaled_rcu(struct reservation_object *obj,
 | 
				
			||||||
 | 
										  bool test_all)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned seq, shared_count;
 | 
				
			||||||
 | 
						int ret = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					retry:
 | 
				
			||||||
 | 
						shared_count = 0;
 | 
				
			||||||
 | 
						seq = read_seqcount_begin(&obj->seq);
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_all) {
 | 
				
			||||||
 | 
							unsigned i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							struct reservation_object_list *fobj = rcu_dereference(obj->fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fobj)
 | 
				
			||||||
 | 
								shared_count = fobj->shared_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (read_seqcount_retry(&obj->seq, seq))
 | 
				
			||||||
 | 
								goto unlock_retry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < shared_count; ++i) {
 | 
				
			||||||
 | 
								struct fence *fence = rcu_dereference(fobj->shared[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ret = reservation_object_test_signaled_single(fence);
 | 
				
			||||||
 | 
								if (ret < 0)
 | 
				
			||||||
 | 
									goto unlock_retry;
 | 
				
			||||||
 | 
								else if (!ret)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * There could be a read_seqcount_retry here, but nothing cares
 | 
				
			||||||
 | 
							 * about whether it's the old or newer fence pointers that are
 | 
				
			||||||
 | 
							 * signaled. That race could still have happened after checking
 | 
				
			||||||
 | 
							 * read_seqcount_retry. If you care, use ww_mutex_lock.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!shared_count) {
 | 
				
			||||||
 | 
							struct fence *fence_excl = rcu_dereference(obj->fence_excl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (read_seqcount_retry(&obj->seq, seq))
 | 
				
			||||||
 | 
								goto unlock_retry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fence_excl) {
 | 
				
			||||||
 | 
								ret = reservation_object_test_signaled_single(fence_excl);
 | 
				
			||||||
 | 
								if (ret < 0)
 | 
				
			||||||
 | 
									goto unlock_retry;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock_retry:
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						goto retry;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(reservation_object_test_signaled_rcu);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,7 @@
 | 
				
			||||||
#include <linux/kref.h>
 | 
					#include <linux/kref.h>
 | 
				
			||||||
#include <linux/sched.h>
 | 
					#include <linux/sched.h>
 | 
				
			||||||
#include <linux/printk.h>
 | 
					#include <linux/printk.h>
 | 
				
			||||||
 | 
					#include <linux/rcupdate.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct fence;
 | 
					struct fence;
 | 
				
			||||||
struct fence_ops;
 | 
					struct fence_ops;
 | 
				
			||||||
| 
						 | 
					@ -37,6 +38,7 @@ struct fence_cb;
 | 
				
			||||||
 * struct fence - software synchronization primitive
 | 
					 * struct fence - software synchronization primitive
 | 
				
			||||||
 * @refcount: refcount for this fence
 | 
					 * @refcount: refcount for this fence
 | 
				
			||||||
 * @ops: fence_ops associated with this fence
 | 
					 * @ops: fence_ops associated with this fence
 | 
				
			||||||
 | 
					 * @rcu: used for releasing fence with kfree_rcu
 | 
				
			||||||
 * @cb_list: list of all callbacks to call
 | 
					 * @cb_list: list of all callbacks to call
 | 
				
			||||||
 * @lock: spin_lock_irqsave used for locking
 | 
					 * @lock: spin_lock_irqsave used for locking
 | 
				
			||||||
 * @context: execution context this fence belongs to, returned by
 | 
					 * @context: execution context this fence belongs to, returned by
 | 
				
			||||||
| 
						 | 
					@ -70,6 +72,7 @@ struct fence_cb;
 | 
				
			||||||
struct fence {
 | 
					struct fence {
 | 
				
			||||||
	struct kref refcount;
 | 
						struct kref refcount;
 | 
				
			||||||
	const struct fence_ops *ops;
 | 
						const struct fence_ops *ops;
 | 
				
			||||||
 | 
						struct rcu_head rcu;
 | 
				
			||||||
	struct list_head cb_list;
 | 
						struct list_head cb_list;
 | 
				
			||||||
	spinlock_t *lock;
 | 
						spinlock_t *lock;
 | 
				
			||||||
	unsigned context, seqno;
 | 
						unsigned context, seqno;
 | 
				
			||||||
| 
						 | 
					@ -191,6 +194,20 @@ static inline struct fence *fence_get(struct fence *fence)
 | 
				
			||||||
	return fence;
 | 
						return fence;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * fence_get_rcu - get a fence from a reservation_object_list with rcu read lock
 | 
				
			||||||
 | 
					 * @fence:	[in]	fence to increase refcount of
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Function returns NULL if no refcount could be obtained, or the fence.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline struct fence *fence_get_rcu(struct fence *fence)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (kref_get_unless_zero(&fence->refcount))
 | 
				
			||||||
 | 
							return fence;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * fence_put - decreases refcount of the fence
 | 
					 * fence_put - decreases refcount of the fence
 | 
				
			||||||
 * @fence:	[in]	fence to reduce refcount of
 | 
					 * @fence:	[in]	fence to reduce refcount of
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,22 +42,29 @@
 | 
				
			||||||
#include <linux/ww_mutex.h>
 | 
					#include <linux/ww_mutex.h>
 | 
				
			||||||
#include <linux/fence.h>
 | 
					#include <linux/fence.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/seqlock.h>
 | 
				
			||||||
 | 
					#include <linux/rcupdate.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern struct ww_class reservation_ww_class;
 | 
					extern struct ww_class reservation_ww_class;
 | 
				
			||||||
 | 
					extern struct lock_class_key reservation_seqcount_class;
 | 
				
			||||||
 | 
					extern const char reservation_seqcount_string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct reservation_object_list {
 | 
					struct reservation_object_list {
 | 
				
			||||||
 | 
						struct rcu_head rcu;
 | 
				
			||||||
	u32 shared_count, shared_max;
 | 
						u32 shared_count, shared_max;
 | 
				
			||||||
	struct fence *shared[];
 | 
						struct fence __rcu *shared[];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct reservation_object {
 | 
					struct reservation_object {
 | 
				
			||||||
	struct ww_mutex lock;
 | 
						struct ww_mutex lock;
 | 
				
			||||||
 | 
						seqcount_t seq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct fence *fence_excl;
 | 
						struct fence __rcu *fence_excl;
 | 
				
			||||||
	struct reservation_object_list *fence;
 | 
						struct reservation_object_list __rcu *fence;
 | 
				
			||||||
	struct reservation_object_list *staged;
 | 
						struct reservation_object_list *staged;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define reservation_object_held(obj) lockdep_is_held(&(obj)->lock.base)
 | 
				
			||||||
#define reservation_object_assert_held(obj) \
 | 
					#define reservation_object_assert_held(obj) \
 | 
				
			||||||
	lockdep_assert_held(&(obj)->lock.base)
 | 
						lockdep_assert_held(&(obj)->lock.base)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,8 +73,9 @@ reservation_object_init(struct reservation_object *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ww_mutex_init(&obj->lock, &reservation_ww_class);
 | 
						ww_mutex_init(&obj->lock, &reservation_ww_class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	obj->fence_excl = NULL;
 | 
						__seqcount_init(&obj->seq, reservation_seqcount_string, &reservation_seqcount_class);
 | 
				
			||||||
	obj->fence = NULL;
 | 
						RCU_INIT_POINTER(obj->fence, NULL);
 | 
				
			||||||
 | 
						RCU_INIT_POINTER(obj->fence_excl, NULL);
 | 
				
			||||||
	obj->staged = NULL;
 | 
						obj->staged = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,18 +84,20 @@ reservation_object_fini(struct reservation_object *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
	struct reservation_object_list *fobj;
 | 
						struct reservation_object_list *fobj;
 | 
				
			||||||
 | 
						struct fence *excl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * This object should be dead and all references must have
 | 
						 * This object should be dead and all references must have
 | 
				
			||||||
	 * been released to it.
 | 
						 * been released to it, so no need to be protected with rcu.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (obj->fence_excl)
 | 
						excl = rcu_dereference_protected(obj->fence_excl, 1);
 | 
				
			||||||
		fence_put(obj->fence_excl);
 | 
						if (excl)
 | 
				
			||||||
 | 
							fence_put(excl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fobj = obj->fence;
 | 
						fobj = rcu_dereference_protected(obj->fence, 1);
 | 
				
			||||||
	if (fobj) {
 | 
						if (fobj) {
 | 
				
			||||||
		for (i = 0; i < fobj->shared_count; ++i)
 | 
							for (i = 0; i < fobj->shared_count; ++i)
 | 
				
			||||||
			fence_put(fobj->shared[i]);
 | 
								fence_put(rcu_dereference_protected(fobj->shared[i], 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		kfree(fobj);
 | 
							kfree(fobj);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -99,17 +109,15 @@ reservation_object_fini(struct reservation_object *obj)
 | 
				
			||||||
static inline struct reservation_object_list *
 | 
					static inline struct reservation_object_list *
 | 
				
			||||||
reservation_object_get_list(struct reservation_object *obj)
 | 
					reservation_object_get_list(struct reservation_object *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	reservation_object_assert_held(obj);
 | 
						return rcu_dereference_protected(obj->fence,
 | 
				
			||||||
 | 
										 reservation_object_held(obj));
 | 
				
			||||||
	return obj->fence;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct fence *
 | 
					static inline struct fence *
 | 
				
			||||||
reservation_object_get_excl(struct reservation_object *obj)
 | 
					reservation_object_get_excl(struct reservation_object *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	reservation_object_assert_held(obj);
 | 
						return rcu_dereference_protected(obj->fence_excl,
 | 
				
			||||||
 | 
										 reservation_object_held(obj));
 | 
				
			||||||
	return obj->fence_excl;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int reservation_object_reserve_shared(struct reservation_object *obj);
 | 
					int reservation_object_reserve_shared(struct reservation_object *obj);
 | 
				
			||||||
| 
						 | 
					@ -119,4 +127,16 @@ void reservation_object_add_shared_fence(struct reservation_object *obj,
 | 
				
			||||||
void reservation_object_add_excl_fence(struct reservation_object *obj,
 | 
					void reservation_object_add_excl_fence(struct reservation_object *obj,
 | 
				
			||||||
				       struct fence *fence);
 | 
									       struct fence *fence);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int reservation_object_get_fences_rcu(struct reservation_object *obj,
 | 
				
			||||||
 | 
									      struct fence **pfence_excl,
 | 
				
			||||||
 | 
									      unsigned *pshared_count,
 | 
				
			||||||
 | 
									      struct fence ***pshared);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					long reservation_object_wait_timeout_rcu(struct reservation_object *obj,
 | 
				
			||||||
 | 
										 bool wait_all, bool intr,
 | 
				
			||||||
 | 
										 unsigned long timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool reservation_object_test_signaled_rcu(struct reservation_object *obj,
 | 
				
			||||||
 | 
										  bool test_all);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _LINUX_RESERVATION_H */
 | 
					#endif /* _LINUX_RESERVATION_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue