git: 85f6b0c801ab - stable/13 - umtx: Add umtxq_requeue Linux emulation layer extension.

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Fri, 17 Jun 2022 19:37:25 UTC
The branch stable/13 has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=85f6b0c801abecd35a42d630fe3a2b6f301fc785

commit 85f6b0c801abecd35a42d630fe3a2b6f301fc785
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2021-07-29 09:43:07 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-06-17 19:33:17 +0000

    umtx: Add umtxq_requeue Linux emulation layer extension.
    
    Reviewed by:            kib
    Differential Revision:  https://reviews.freebsd.org/D31235
    MFC after:              2 weeks
    
    (cherry picked from commit 8e4d22c01d8f76ae144f32b4c5fac40451429891)
---
 sys/kern/kern_umtx.c | 37 +++++++++++++++++++++++++++++++++++++
 sys/sys/umtxvar.h    |  1 +
 2 files changed, 38 insertions(+)

diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
index 256c7e048ba5..1ffa14b7cadb 100644
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -621,6 +621,43 @@ umtxq_signal_thread(struct umtx_q *uq)
 	wakeup(uq);
 }
 
+/*
+ * Wake up a maximum of n_wake threads that are waiting on an userland
+ * object identified by key. The remaining threads are removed from queue
+ * identified by key and added to the queue identified by key2 (requeued).
+ * The n_requeue specifies an upper limit on the number of threads that
+ * are requeued to the second queue.
+ */
+int
+umtxq_requeue(struct umtx_key *key, int n_wake, struct umtx_key *key2,
+    int n_requeue)
+{
+	struct umtxq_queue *uh, *uh2;
+	struct umtx_q *uq, *uq_temp;
+	int ret;
+
+	ret = 0;
+	UMTXQ_LOCKED_ASSERT(umtxq_getchain(key));
+	UMTXQ_LOCKED_ASSERT(umtxq_getchain(key2));
+	uh = umtxq_queue_lookup(key, UMTX_SHARED_QUEUE);
+	uh2 = umtxq_queue_lookup(key2, UMTX_SHARED_QUEUE);
+	if (uh == NULL)
+		return (0);
+	TAILQ_FOREACH_SAFE(uq, &uh->head, uq_link, uq_temp) {
+		if (++ret <= n_wake) {
+			umtxq_remove(uq);
+			wakeup_one(uq);
+		} else {
+			umtxq_remove(uq);
+			uq->uq_key = *key2;
+			umtxq_insert(uq);
+			if (ret - n_wake == n_requeue)
+				break;
+		}
+	}
+	return (ret);
+}
+
 static inline int
 tstohz(const struct timespec *tsp)
 {
diff --git a/sys/sys/umtxvar.h b/sys/sys/umtxvar.h
index de1b649ed8d7..ed2d8046a5fb 100644
--- a/sys/sys/umtxvar.h
+++ b/sys/sys/umtxvar.h
@@ -210,6 +210,7 @@ void umtxq_free(struct umtx_q *);
 struct umtxq_chain *umtxq_getchain(struct umtx_key *);
 void umtxq_insert_queue(struct umtx_q *, int);
 void umtxq_remove_queue(struct umtx_q *, int);
+int umtxq_requeue(struct umtx_key *, int, struct umtx_key *, int);
 int umtxq_signal_mask(struct umtx_key *, int, u_int);
 int umtxq_sleep(struct umtx_q *, const char *,
     struct umtx_abs_timeout *);