Module Name:    src
Committed By:   riastradh
Date:           Sun Dec 19 12:26:04 UTC 2021

Modified Files:
        src/sys/external/bsd/drm2/linux: linux_dma_resv.c

Log Message:
drm: Factor out logic to read snapshot of fences in dma_resv.

Should make auditing a little easier.


To generate a diff of this commit:
cvs rdiff -u -r1.11 -r1.12 src/sys/external/bsd/drm2/linux/linux_dma_resv.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/external/bsd/drm2/linux/linux_dma_resv.c
diff -u src/sys/external/bsd/drm2/linux/linux_dma_resv.c:1.11 src/sys/external/bsd/drm2/linux/linux_dma_resv.c:1.12
--- src/sys/external/bsd/drm2/linux/linux_dma_resv.c:1.11	Sun Dec 19 12:21:30 2021
+++ src/sys/external/bsd/drm2/linux/linux_dma_resv.c	Sun Dec 19 12:26:04 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_dma_resv.c,v 1.11 2021/12/19 12:21:30 riastradh Exp $	*/
+/*	$NetBSD: linux_dma_resv.c,v 1.12 2021/12/19 12:26:04 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_dma_resv.c,v 1.11 2021/12/19 12:21:30 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_dma_resv.c,v 1.12 2021/12/19 12:26:04 riastradh Exp $");
 
 #include <sys/param.h>
 #include <sys/poll.h>
@@ -449,6 +449,105 @@ dma_resv_read_valid(const struct dma_res
 }
 
 /*
+ * dma_resv_get_shared_reader(robj, listp, shared_countp, ticket)
+ *
+ *	Set *listp and *shared_countp to a snapshot of the pointer to
+ *	and length of the shared fence list of robj and return true, or
+ *	set them to NULL/0 and return false if a writer intervened so
+ *	the caller must start over.
+ *
+ *	Both *listp and *shared_countp are unconditionally initialized
+ *	on return.  They may be NULL/0 even on success, if there is no
+ *	shared list at the moment.  Does not take any fence references.
+ */
+static bool
+dma_resv_get_shared_reader(const struct dma_resv *robj,
+    const struct dma_resv_list **listp, unsigned *shared_countp,
+    struct dma_resv_read_ticket *ticket)
+{
+	struct dma_resv_list *list;
+	unsigned shared_count = 0;
+
+	/*
+	 * Get the list and, if it is present, its length.  If the list
+	 * is present, it has a valid length.  The atomic_load_consume
+	 * pairs with the membar_producer in dma_resv_write_begin.
+	 */
+	list = atomic_load_consume(&robj->fence);
+	shared_count = list ? atomic_load_relaxed(&list->shared_count) : 0;
+
+	/*
+	 * We are done reading from robj and list.  Validate our
+	 * parking ticket.  If it's invalid, do not pass go and do not
+	 * collect $200.
+	 */
+	if (!dma_resv_read_valid(robj, ticket))
+		goto fail;
+
+	/* Success!  */
+	*listp = list;
+	*shared_countp = shared_count;
+	return true;
+
+fail:	*listp = NULL;
+	*shared_countp = 0;
+	return false;
+}
+
+/*
+ * dma_resv_get_excl_reader(robj, fencep, ticket)
+ *
+ *	Set *fencep to the exclusive fence of robj and return true, or
+ *	set it to NULL and return false if either
+ *	(a) a writer intervened, or
+ *	(b) the fence is scheduled to be destroyed after this RCU grace
+ *	    period,
+ *	in either case meaning the caller must restart.
+ *
+ *	The value of *fencep is unconditionally initialized on return.
+ *	It may be NULL, if there is no exclusive fence at the moment.
+ *	If nonnull, *fencep is referenced; caller must dma_fence_put.
+ */
+static bool
+dma_resv_get_excl_reader(const struct dma_resv *robj,
+    struct dma_fence **fencep,
+    struct dma_resv_read_ticket *ticket)
+{
+	struct dma_fence *fence;
+
+	/*
+	 * Get the candidate fence pointer.  The atomic_load_consume
+	 * pairs with the membar_consumer in dma_resv_write_begin.
+	 */
+	fence = atomic_load_consume(&robj->fence_excl);
+
+	/*
+	 * The load of robj->fence_excl is atomic, but the caller may
+	 * have previously loaded the shared fence list and should
+	 * restart if its view of the entire dma_resv object is not a
+	 * consistent snapshot.
+	 */
+	if (!dma_resv_read_valid(robj, ticket))
+		goto fail;
+
+	/*
+	 * If the fence is already scheduled to away after this RCU
+	 * read section, give up.  Otherwise, take a reference so it
+	 * won't go away until after dma_fence_put.
+	 */
+	if (fence != NULL &&
+	    (fence = dma_fence_get_rcu(fence)) == NULL)
+		goto fail;
+
+	/* Success!  */
+	*fencep = fence;
+	return true;
+
+fail:	*fencep = NULL;
+	return false;
+}
+
+/*
  * dma_resv_add_excl_fence(robj, fence)
  *
  *	Empty and release all of robj's shared fences, and clear and
@@ -659,13 +758,10 @@ top:	KASSERT(fence == NULL);
 	rcu_read_lock();
 	dma_resv_read_begin(robj, &ticket);
 
-	/*
-	 * If there is a shared list, grab it.  The atomic_load_consume
-	 * here pairs with the membar_producer in dma_resv_write_begin
-	 * to ensure the content of robj->fence is initialized before
-	 * we witness the pointer.
-	 */
-	if ((list = atomic_load_consume(&robj->fence)) != NULL) {
+	/* If there is a shared list, grab it.  */
+	if (!dma_resv_get_shared_reader(robj, &list, &shared_count, &ticket))
+		goto restart;
+	if (list != NULL) {
 
 		/* Check whether we have a buffer.  */
 		if (shared == NULL) {
@@ -711,36 +807,14 @@ top:	KASSERT(fence == NULL);
 		 * it'll invalidate the read ticket and we'll start
 		 * ove, but atomic_load in a loop will pacify kcsan.
 		 */
-		shared_count = atomic_load_relaxed(&list->shared_count);
 		for (i = 0; i < shared_count; i++)
 			shared[i] = atomic_load_relaxed(&list->shared[i]);
-	} else {
-		/* No shared list: shared count is zero.  */
-		shared_count = 0;
 	}
 
 	/* If there is an exclusive fence, grab it.  */
 	KASSERT(fence == NULL);
-	fence = atomic_load_consume(&robj->fence_excl);
-
-	/*
-	 * We are done reading from robj and list.  Validate our
-	 * parking ticket.  If it's invalid, do not pass go and do not
-	 * collect $200.
-	 */
-	if (!dma_resv_read_valid(robj, &ticket)) {
-		fence = NULL;
+	if (!dma_resv_get_excl_reader(robj, &fence, &ticket))
 		goto restart;
-	}
-
-	/*
-	 * Try to get a reference to the exclusive fence, if there is
-	 * one.  If we can't, start over.
-	 */
-	if (fence) {
-		if ((fence = dma_fence_get_rcu(fence)) == NULL)
-			goto restart;
-	}
 
 	/*
 	 * Try to get a reference to all of the shared fences.
@@ -804,18 +878,10 @@ top:	KASSERT(fence == NULL);
 	dma_resv_read_begin(src_robj, &read_ticket);
 
 	/* Get the shared list.  */
-	if ((src_list = atomic_load_consume(&src_robj->fence)) != NULL) {
-
-		/* Find out how long it is.  */
-		shared_count = atomic_load_relaxed(&src_list->shared_count);
-
-		/*
-		 * Make sure we saw a consistent snapshot of the list
-		 * pointer and length.
-		 */
-		if (!dma_resv_read_valid(src_robj, &read_ticket))
-			goto restart;
-
+	if (!dma_resv_get_shared_reader(src_robj, &src_list, &shared_count,
+		&read_ticket))
+		goto restart;
+	if (src_list != NULL) {
 		/* Allocate a new list.  */
 		dst_list = objlist_tryalloc(shared_count);
 		if (dst_list == NULL)
@@ -840,28 +906,8 @@ top:	KASSERT(fence == NULL);
 
 	/* Get the exclusive fence.  */
 	KASSERT(fence == NULL);
-	if ((fence = atomic_load_consume(&src_robj->fence_excl)) != NULL) {
-
-		/*
-		 * Make sure we saw a consistent snapshot of the fence.
-		 *
-		 * XXX I'm not actually sure this is necessary since
-		 * pointer writes are supposed to be atomic.
-		 */
-		if (!dma_resv_read_valid(src_robj, &read_ticket)) {
-			fence = NULL;
-			goto restart;
-		}
-
-		/*
-		 * If it is going away, restart.  Otherwise, acquire a
-		 * reference to it.
-		 */
-		if (!dma_fence_get_rcu(fence)) {
-			fence = NULL;
-			goto restart;
-		}
-	}
+	if (!dma_resv_get_excl_reader(src_robj, &fence, &read_ticket))
+		goto restart;
 
 	/* All done with src; exit the RCU read section.  */
 	rcu_read_unlock();
@@ -939,7 +985,7 @@ dma_resv_test_signaled_rcu(const struct 
     bool shared)
 {
 	struct dma_resv_read_ticket ticket;
-	struct dma_resv_list *list;
+	const struct dma_resv_list *list;
 	struct dma_fence *fence = NULL;
 	uint32_t i, shared_count;
 	bool signaled = true;
@@ -951,18 +997,15 @@ top:	KASSERT(fence == NULL);
 	dma_resv_read_begin(robj, &ticket);
 
 	/* If shared is requested and there is a shared list, test it.  */
-	if (shared && (list = atomic_load_consume(&robj->fence)) != NULL) {
-
-		/* Find out how long it is.  */
-		shared_count = atomic_load_relaxed(&list->shared_count);
-
-		/*
-		 * Make sure we saw a consistent snapshot of the list
-		 * pointer and length.
-		 */
-		if (!dma_resv_read_valid(robj, &ticket))
+	if (shared) {
+		if (!dma_resv_get_shared_reader(robj, &list, &shared_count,
+			&ticket))
 			goto restart;
-
+	} else {
+		list = NULL;
+		shared_count = 0;
+	}
+	if (list != NULL) {
 		/*
 		 * For each fence, if it is going away, restart.
 		 * Otherwise, acquire a reference to it to test whether
@@ -984,25 +1027,10 @@ top:	KASSERT(fence == NULL);
 
 	/* If there is an exclusive fence, test it.  */
 	KASSERT(fence == NULL);
-	if ((fence = atomic_load_consume(&robj->fence_excl)) != NULL) {
-
-		/*
-		 * Make sure we saw a consistent snapshot of the fence.
-		 *
-		 * XXX I'm not actually sure this is necessary since
-		 * pointer writes are supposed to be atomic.
-		 */
-		if (!dma_resv_read_valid(robj, &ticket)) {
-			fence = NULL;
-			goto restart;
-		}
-
-		/*
-		 * If it is going away, restart.  Otherwise, acquire a
-		 * reference to it to test whether it is signalled.
-		 */
-		if ((fence = dma_fence_get_rcu(fence)) == NULL)
-			goto restart;
+	if (!dma_resv_get_excl_reader(robj, &fence, &ticket))
+		goto restart;
+	if (fence != NULL) {
+		/* Test whether it is signalled.  If no, stop.  */
 		signaled &= dma_fence_is_signaled(fence);
 		dma_fence_put(fence);
 		fence = NULL;
@@ -1038,7 +1066,7 @@ dma_resv_wait_timeout_rcu(const struct d
     bool shared, bool intr, unsigned long timeout)
 {
 	struct dma_resv_read_ticket ticket;
-	struct dma_resv_list *list;
+	const struct dma_resv_list *list;
 	struct dma_fence *fence = NULL;
 	uint32_t i, shared_count;
 	long ret;
@@ -1053,18 +1081,15 @@ top:	KASSERT(fence == NULL);
 	dma_resv_read_begin(robj, &ticket);
 
 	/* If shared is requested and there is a shared list, wait on it.  */
-	if (shared && (list = atomic_load_consume(&robj->fence)) != NULL) {
-
-		/* Find out how long it is.  */
-		shared_count = list->shared_count;
-
-		/*
-		 * Make sure we saw a consistent snapshot of the list
-		 * pointer and length.
-		 */
-		if (!dma_resv_read_valid(robj, &ticket))
+	if (shared) {
+		if (!dma_resv_get_shared_reader(robj, &list, &shared_count,
+			&ticket))
 			goto restart;
-
+	} else {
+		list = NULL;
+		shared_count = 0;
+	}
+	if (list != NULL) {
 		/*
 		 * For each fence, if it is going away, restart.
 		 * Otherwise, acquire a reference to it to test whether
@@ -1085,26 +1110,10 @@ top:	KASSERT(fence == NULL);
 
 	/* If there is an exclusive fence, test it.  */
 	KASSERT(fence == NULL);
-	if ((fence = atomic_load_consume(&robj->fence_excl)) != NULL) {
-
-		/*
-		 * Make sure we saw a consistent snapshot of the fence.
-		 *
-		 * XXX I'm not actually sure this is necessary since
-		 * pointer writes are supposed to be atomic.
-		 */
-		if (!dma_resv_read_valid(robj, &ticket)) {
-			fence = NULL;
-			goto restart;
-		}
-
-		/*
-		 * If it is going away, restart.  Otherwise, acquire a
-		 * reference to it to test whether it is signalled.  If
-		 * not, wait for it.
-		 */
-		if ((fence = dma_fence_get_rcu(fence)) == NULL)
-			goto restart;
+	if (!dma_resv_get_excl_reader(robj, &fence, &ticket))
+		goto restart;
+	if (fence != NULL) {
+		/* Test whether it is signalled.  If no, wait.  */
 		if (!dma_fence_is_signaled(fence))
 			goto wait;
 		dma_fence_put(fence);
@@ -1207,7 +1216,7 @@ dma_resv_do_poll(const struct dma_resv *
     struct dma_resv_poll *rpoll)
 {
 	struct dma_resv_read_ticket ticket;
-	struct dma_resv_list *list;
+	const struct dma_resv_list *list;
 	struct dma_fence *fence = NULL;
 	uint32_t i, shared_count;
 	int revents;
@@ -1232,19 +1241,15 @@ top:	KASSERT(fence == NULL);
 	dma_resv_read_begin(robj, &ticket);
 
 	/* If we want to wait for all fences, get the shared list.  */
-	if ((events & POLLOUT) != 0 &&
-	    (list = atomic_load_consume(&robj->fence)) != NULL) do {
-
-		/* Find out how long it is.  */
-		shared_count = list->shared_count;
-
-		/*
-		 * Make sure we saw a consistent snapshot of the list
-		 * pointer and length.
-		 */
-		if (!dma_resv_read_valid(robj, &ticket))
+	if (events & POLLOUT) {
+		if (!dma_resv_get_shared_reader(robj, &list, &shared_count,
+			&ticket))
 			goto restart;
-
+	} else {
+		list = NULL;
+		shared_count = 0;
+	}
+	if (list != NULL) do {
 		/*
 		 * For each fence, if it is going away, restart.
 		 * Otherwise, acquire a reference to it to test whether
@@ -1310,26 +1315,13 @@ top:	KASSERT(fence == NULL);
 
 	/* We always wait for at least the exclusive fence, so get it.  */
 	KASSERT(fence == NULL);
-	if ((fence = atomic_load_consume(&robj->fence_excl)) != NULL) do {
-
-		/*
-		 * Make sure we saw a consistent snapshot of the fence.
-		 *
-		 * XXX I'm not actually sure this is necessary since
-		 * pointer writes are supposed to be atomic.
-		 */
-		if (!dma_resv_read_valid(robj, &ticket)) {
-			fence = NULL;
-			goto restart;
-		}
-
+	if (!dma_resv_get_excl_reader(robj, &fence, &ticket))
+		goto restart;
+	if (fence != NULL) do {
 		/*
-		 * If it is going away, restart.  Otherwise, acquire a
-		 * reference to it to test whether it is signalled.  If
-		 * not, stop and request a callback.
+		 * Test whether it is signalled.  If not, stop and
+		 * request a callback.
 		 */
-		if ((fence = dma_fence_get_rcu(fence)) == NULL)
-			goto restart;
 		if (dma_fence_is_signaled(fence)) {
 			dma_fence_put(fence);
 			fence = NULL;

Reply via email to