Module Name:    src
Committed By:   riastradh
Date:           Mon Aug 27 15:25:14 UTC 2018

Modified Files:
        src/sys/external/bsd/drm2/include/linux: dma-buf.h reservation.h
        src/sys/external/bsd/drm2/linux: linux_dma_buf.c linux_reservation.c

Log Message:
Implement poll for reservations and dma bufs.


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/external/bsd/drm2/include/linux/dma-buf.h
cvs rdiff -u -r1.7 -r1.8 \
    src/sys/external/bsd/drm2/include/linux/reservation.h
cvs rdiff -u -r1.3 -r1.4 src/sys/external/bsd/drm2/linux/linux_dma_buf.c
cvs rdiff -u -r1.7 -r1.8 src/sys/external/bsd/drm2/linux/linux_reservation.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/include/linux/dma-buf.h
diff -u src/sys/external/bsd/drm2/include/linux/dma-buf.h:1.3 src/sys/external/bsd/drm2/include/linux/dma-buf.h:1.4
--- src/sys/external/bsd/drm2/include/linux/dma-buf.h:1.3	Mon Aug 27 15:22:54 2018
+++ src/sys/external/bsd/drm2/include/linux/dma-buf.h	Mon Aug 27 15:25:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: dma-buf.h,v 1.3 2018/08/27 15:22:54 riastradh Exp $	*/
+/*	$NetBSD: dma-buf.h,v 1.4 2018/08/27 15:25:13 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -88,7 +88,7 @@ struct dma_buf {
 
 	kmutex_t			db_lock;
 	volatile unsigned		db_refcnt;
-	struct selinfo			db_selq;
+	struct reservation_poll		db_resv_poll;
 	struct reservation_object	db_resv_int[];
 };
 

Index: src/sys/external/bsd/drm2/include/linux/reservation.h
diff -u src/sys/external/bsd/drm2/include/linux/reservation.h:1.7 src/sys/external/bsd/drm2/include/linux/reservation.h:1.8
--- src/sys/external/bsd/drm2/include/linux/reservation.h:1.7	Mon Aug 27 14:01:14 2018
+++ src/sys/external/bsd/drm2/include/linux/reservation.h	Mon Aug 27 15:25:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: reservation.h,v 1.7 2018/08/27 14:01:14 riastradh Exp $	*/
+/*	$NetBSD: reservation.h,v 1.8 2018/08/27 15:25:13 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -32,11 +32,10 @@
 #ifndef	_LINUX_RESERVATION_H_
 #define	_LINUX_RESERVATION_H_
 
+#include <linux/fence.h>
 #include <linux/rcupdate.h>
 #include <linux/ww_mutex.h>
 
-struct fence;
-
 struct reservation_object {
 	struct ww_mutex		lock;
 
@@ -54,6 +53,14 @@ struct reservation_object_list {
 	struct fence __rcu	*shared[];
 };
 
+/* NetBSD addition */
+struct reservation_poll {
+	kmutex_t		rp_lock;
+	struct selinfo		rp_selq;
+	struct fence_cb		rp_fcb;
+	bool			rp_claimed;
+};
+
 #define	reservation_object_add_excl_fence	linux_reservation_object_add_excl_fence
 #define	reservation_object_add_shared_fence	linux_reservation_object_add_shared_fence
 #define	reservation_object_fini			linux_reservation_object_fini
@@ -62,9 +69,13 @@ struct reservation_object_list {
 #define	reservation_object_get_list		linux_reservation_object_get_list
 #define	reservation_object_held			linux_reservation_object_held
 #define	reservation_object_init			linux_reservation_object_init
+#define	reservation_object_kqfilter		linux_reservation_object_kqfilter
+#define	reservation_object_poll			linux_reservation_object_poll
 #define	reservation_object_reserve_shared	linux_reservation_object_reserve_shared
 #define	reservation_object_test_signaled_rcu	linux_reservation_object_test_signaled_rcu
 #define	reservation_object_wait_timeout_rcu	linux_reservation_object_wait_timeout_rcu
+#define	reservation_poll_fini			linux_reservation_poll_fini
+#define	reservation_poll_init			linux_reservation_poll_init
 #define	reservation_ww_class			linux_reservation_ww_class
 
 extern struct ww_class	reservation_ww_class;
@@ -90,4 +101,12 @@ bool	reservation_object_test_signaled_rc
 long	reservation_object_wait_timeout_rcu(struct reservation_object *,
 	    bool, bool, unsigned long);
 
+/* NetBSD additions */
+void	reservation_poll_init(struct reservation_poll *);
+void	reservation_poll_fini(struct reservation_poll *);
+int	reservation_object_poll(struct reservation_object *, int,
+	    struct reservation_poll *);
+int	reservation_object_kqfilter(struct reservation_object *,
+	    struct knote *, struct reservation_poll *);
+
 #endif	/* _LINUX_RESERVATION_H_ */

Index: src/sys/external/bsd/drm2/linux/linux_dma_buf.c
diff -u src/sys/external/bsd/drm2/linux/linux_dma_buf.c:1.3 src/sys/external/bsd/drm2/linux/linux_dma_buf.c:1.4
--- src/sys/external/bsd/drm2/linux/linux_dma_buf.c:1.3	Mon Aug 27 15:24:40 2018
+++ src/sys/external/bsd/drm2/linux/linux_dma_buf.c	Mon Aug 27 15:25:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_dma_buf.c,v 1.3 2018/08/27 15:24:40 riastradh Exp $	*/
+/*	$NetBSD: linux_dma_buf.c,v 1.4 2018/08/27 15:25:13 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_dma_buf.c,v 1.3 2018/08/27 15:24:40 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_dma_buf.c,v 1.4 2018/08/27 15:25:13 riastradh Exp $");
 
 #include <sys/types.h>
 #include <sys/atomic.h>
@@ -87,6 +87,7 @@ dma_buf_export(struct dma_buf_export_inf
 
 	mutex_init(&dmabuf->db_lock, MUTEX_DEFAULT, IPL_NONE);
 	dmabuf->db_refcnt = 1;
+	reservation_poll_init(&dmabuf->db_resv_poll);
 
 	if (dmabuf->resv == NULL) {
 		dmabuf->resv = &dmabuf->db_resv_int[0];
@@ -167,6 +168,7 @@ dma_buf_put(struct dma_buf *dmabuf)
 	if (atomic_dec_uint_nv(&dmabuf->db_refcnt) != 0)
 		return;
 
+	reservation_poll_fini(&dmabuf->db_resv_poll);
 	mutex_destroy(&dmabuf->db_lock);
 	if (dmabuf->resv == &dmabuf->db_resv_int[0]) {
 		reservation_object_fini(dmabuf->resv);
@@ -239,27 +241,21 @@ dmabuf_fop_close(struct file *file)
 static int
 dmabuf_fop_poll(struct file *file, int events)
 {
-#ifdef notyet
 	struct dma_buf_file *dbf = file->f_data;
 	struct dma_buf *dmabuf = dbf->dbf_dmabuf;
+	struct reservation_poll *rpoll = &dmabuf->db_resv_poll;
 
-	return reservation_object_poll(dmabuf->resv, events);
-#else
-	return -ENOSYS;
-#endif
+	return reservation_object_poll(dmabuf->resv, events, rpoll);
 }
 
 static int
-dmabuf_fop_kqfilter(struct file *file, struct knote *knote)
+dmabuf_fop_kqfilter(struct file *file, struct knote *kn)
 {
-#ifdef notyet
 	struct dma_buf_file *dbf = file->f_data;
 	struct dma_buf *dmabuf = dbf->dbf_dmabuf;
+	struct reservation_poll *rpoll = &dmabuf->db_resv_poll;
 
-	return reservation_object_kqfilter(dmabuf->resv, knote);
-#else
-	return -ENOSYS;
-#endif
+	return reservation_object_kqfilter(dmabuf->resv, kn, rpoll);
 }
 
 static int

Index: src/sys/external/bsd/drm2/linux/linux_reservation.c
diff -u src/sys/external/bsd/drm2/linux/linux_reservation.c:1.7 src/sys/external/bsd/drm2/linux/linux_reservation.c:1.8
--- src/sys/external/bsd/drm2/linux/linux_reservation.c:1.7	Mon Aug 27 14:14:13 2018
+++ src/sys/external/bsd/drm2/linux/linux_reservation.c	Mon Aug 27 15:25:14 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_reservation.c,v 1.7 2018/08/27 14:14:13 riastradh Exp $	*/
+/*	$NetBSD: linux_reservation.c,v 1.8 2018/08/27 15:25:14 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -30,10 +30,15 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_reservation.c,v 1.7 2018/08/27 14:14:13 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_reservation.c,v 1.8 2018/08/27 15:25:14 riastradh Exp $");
+
+#include <sys/param.h>
+#include <sys/poll.h>
+#include <sys/select.h>
 
 #include <linux/fence.h>
 #include <linux/reservation.h>
+#include <linux/ww_mutex.h>
 
 DEFINE_WW_CLASS(reservation_ww_class __cacheline_aligned);
 
@@ -792,3 +797,263 @@ wait:
 		return timeout;
 	goto top;
 }
+
+/*
+ * reservation_poll_init(rpoll, lock)
+ *
+ *	Initialize reservation poll state.
+ */
+void
+reservation_poll_init(struct reservation_poll *rpoll)
+{
+
+	mutex_init(&rpoll->rp_lock, MUTEX_DEFAULT, IPL_VM);
+	selinit(&rpoll->rp_selq);
+	rpoll->rp_claimed = 0;
+}
+
+/*
+ * reservation_poll_fini(rpoll)
+ *
+ *	Release any resource associated with reservation poll state.
+ */
+void
+reservation_poll_fini(struct reservation_poll *rpoll)
+{
+
+	KASSERT(rpoll->rp_claimed == 0);
+	seldestroy(&rpoll->rp_selq);
+	mutex_destroy(&rpoll->rp_lock);
+}
+
+/*
+ * reservation_poll_cb(fence, fcb)
+ *
+ *	Callback to notify a reservation poll that a fence has
+ *	completed.  Notify any waiters and allow the next poller to
+ *	claim the callback.
+ *
+ *	If one thread is waiting for the exclusive fence only, and we
+ *	spuriously notify them about a shared fence, tough.
+ */
+static void
+reservation_poll_cb(struct fence *fence, struct fence_cb *fcb)
+{
+	struct reservation_poll *rpoll = container_of(fcb,
+	    struct reservation_poll, rp_fcb);
+
+	mutex_enter(&rpoll->rp_lock);
+	selnotify(&rpoll->rp_selq, 0, NOTE_SUBMIT);
+	rpoll->rp_claimed = 0;
+	mutex_exit(&rpoll->rp_lock);
+}
+
+/*
+ * reservation_object_poll(robj, events, rpoll)
+ *
+ *	POLLOUT		wait for all fences shared and exclusive
+ *	POLLIN		wait for the exclusive fence
+ */
+int
+reservation_object_poll(struct reservation_object *robj, int events,
+    struct reservation_poll *rpoll)
+{
+	struct reservation_object_read_ticket ticket;
+	struct reservation_object_list *list;
+	struct fence *fence;
+	uint32_t i, shared_count;
+	int revents;
+	bool recorded = false;	/* curlwp is on the selq */
+	bool claimed = false;	/* we claimed the callback */
+	bool callback = false;	/* we requested a callback */
+
+	/*
+	 * Start with the maximal set of events that could be ready.
+	 * We will eliminate the events that are definitely not ready
+	 * as we go at the same time as we add callbacks to notify us
+	 * that they may be ready.
+	 */
+	revents = events & (POLLIN|POLLOUT);
+	if (revents == 0)
+		return 0;
+
+top:
+	/* Enter an RCU read section and get a read ticket.  */
+	rcu_read_lock();
+	reservation_object_read_begin(robj, &ticket);
+
+	/* If we want to wait for all fences, get the shared list.  */
+	if ((events & POLLOUT) && (list = robj->robj_list) != NULL) do {
+		/* Make sure the content of the list has been published.  */
+		membar_datadep_consumer();
+
+		/* 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 (!reservation_object_read_valid(robj, &ticket))
+			goto restart;
+
+		/*
+		 * For each fence, if it is going away, restart.
+		 * Otherwise, acquire a reference to it to test whether
+		 * it is signalled.  Stop and request a callback if we
+		 * find any that is not signalled.
+		 */
+		for (i = 0; i < shared_count; i++) {
+			fence = fence_get_rcu(list->shared[i]);
+			if (fence == NULL)
+				goto restart;
+			if (!fence_is_signaled(fence)) {
+				fence_put(fence);
+				break;
+			}
+			fence_put(fence);
+		}
+
+		/* If all shared fences have been signalled, move on.  */
+		if (i == shared_count)
+			break;
+
+		/* Put ourselves on the selq if we haven't already.  */
+		if (!recorded)
+			goto record;
+
+		/*
+		 * If someone else claimed the callback, or we already
+		 * requested it, we're guaranteed to be notified, so
+		 * assume the event is not ready.
+		 */
+		if (!claimed || callback) {
+			revents &= ~POLLOUT;
+			break;
+		}
+
+		/*
+		 * Otherwise, find the first fence that is not
+		 * signalled, request the callback, and clear POLLOUT
+		 * from the possible ready events.  If they are all
+		 * signalled, leave POLLOUT set; we will simulate the
+		 * callback later.
+		 */
+		for (i = 0; i < shared_count; i++) {
+			fence = fence_get_rcu(list->shared[i]);
+			if (fence == NULL)
+				goto restart;
+			if (!fence_add_callback(fence, &rpoll->rp_fcb,
+				reservation_poll_cb)) {
+				fence_put(fence);
+				revents &= ~POLLOUT;
+				callback = true;
+				break;
+			}
+			fence_put(fence);
+		}
+	} while (0);
+
+	/* We always wait for at least the exclusive fence, so get it.  */
+	if ((fence = robj->robj_fence) != NULL) do {
+		/* Make sure the content of the fence has been published.  */
+		membar_datadep_consumer();
+
+		/*
+		 * 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 (!reservation_object_read_valid(robj, &ticket))
+			goto restart;
+
+		/*
+		 * 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.
+		 */
+		if ((fence = fence_get_rcu(fence)) == NULL)
+			goto restart;
+		if (fence_is_signaled(fence)) {
+			fence_put(fence);
+			break;
+		}
+
+		/* Put ourselves on the selq if we haven't already.  */
+		if (!recorded) {
+			fence_put(fence);
+			goto record;
+		}
+
+		/*
+		 * If someone else claimed the callback, or we already
+		 * requested it, we're guaranteed to be notified, so
+		 * assume the event is not ready.
+		 */
+		if (!claimed || callback) {
+			revents = 0;
+			break;
+		}
+
+		/*
+		 * Otherwise, try to request the callback, and clear
+		 * all possible ready events.  If the fence has been
+		 * signalled in the interim, leave the events set; we
+		 * will simulate the callback later.
+		 */
+		if (!fence_add_callback(fence, &rpoll->rp_fcb,
+			reservation_poll_cb)) {
+			fence_put(fence);
+			revents = 0;
+			callback = true;
+			break;
+		}
+		fence_put(fence);
+	} while (0);
+
+	/* All done reading the fences.  */
+	rcu_read_unlock();
+
+	if (claimed && !callback) {
+		/*
+		 * We claimed the callback but we didn't actually
+		 * request it because a fence was signalled while we
+		 * were claiming it.  Call it ourselves now.  The
+		 * callback doesn't use the fence nor rely on holding
+		 * any of the fence locks, so this is safe.
+		 */
+		reservation_poll_cb(NULL, &rpoll->rp_fcb);
+	}
+	return revents;
+
+restart:
+	rcu_read_unlock();
+	goto top;
+
+record:
+	rcu_read_unlock();
+	mutex_enter(&rpoll->rp_lock);
+	selrecord(curlwp, &rpoll->rp_selq);
+	if (rpoll->rp_claimed)
+		claimed = rpoll->rp_claimed = true;
+	mutex_exit(&rpoll->rp_lock);
+	recorded = true;
+	goto top;
+}
+
+/*
+ * reservation_object_kqfilter(robj, kn, rpoll)
+ *
+ *	Kqueue filter for reservation objects.  Currently not
+ *	implemented because the logic to implement it is nontrivial,
+ *	and userland will presumably never use it, so it would be
+ *	dangerous to add never-tested complex code paths to the kernel.
+ */
+int
+reservation_object_kqfilter(struct reservation_object *robj, struct knote *kn,
+    struct reservation_poll *rpoll)
+{
+
+	return EINVAL;
+}

Reply via email to