Module Name:    src
Committed By:   riastradh
Date:           Sun Dec 19 11:40:14 UTC 2021

Modified Files:
        src/sys/external/bsd/common/include/linux: workqueue.h
        src/sys/external/bsd/common/linux: linux_work.c

Log Message:
linux/workqueue: Draft queue_rcu_work.


To generate a diff of this commit:
cvs rdiff -u -r1.24 -r1.25 \
    src/sys/external/bsd/common/include/linux/workqueue.h
cvs rdiff -u -r1.54 -r1.55 src/sys/external/bsd/common/linux/linux_work.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/common/include/linux/workqueue.h
diff -u src/sys/external/bsd/common/include/linux/workqueue.h:1.24 src/sys/external/bsd/common/include/linux/workqueue.h:1.25
--- src/sys/external/bsd/common/include/linux/workqueue.h:1.24	Sun Dec 19 11:38:03 2021
+++ src/sys/external/bsd/common/include/linux/workqueue.h	Sun Dec 19 11:40:13 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: workqueue.h,v 1.24 2021/12/19 11:38:03 riastradh Exp $	*/
+/*	$NetBSD: workqueue.h,v 1.25 2021/12/19 11:40:13 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2013, 2018 The NetBSD Foundation, Inc.
@@ -39,9 +39,10 @@
 #include <linux/stringify.h>
 
 #define	INIT_DELAYED_WORK		linux_INIT_DELAYED_WORK
+#define	INIT_RCU_WORK			linux_INIT_RCU_WORK
 #define	INIT_WORK			linux_INIT_WORK
-#define	alloc_workqueue			linux_alloc_workqueue
 #define	alloc_ordered_workqueue		linux_alloc_ordered_workqueue
+#define	alloc_workqueue			linux_alloc_workqueue
 #define	cancel_delayed_work		linux_cancel_delayed_work
 #define	cancel_delayed_work_sync	linux_cancel_delayed_work_sync
 #define	cancel_work			linux_cancel_work
@@ -54,8 +55,9 @@
 #define	flush_scheduled_work		linux_flush_scheduled_work
 #define	flush_work			linux_flush_work
 #define	flush_workqueue			linux_flush_workqueue
-#define	queue_delayed_work		linux_queue_delayed_work
 #define	mod_delayed_work		linux_mod_delayed_work
+#define	queue_delayed_work		linux_queue_delayed_work
+#define	queue_rcu_work			linux_queue_rcu_work
 #define	queue_work			linux_queue_work
 #define	schedule_delayed_work		linux_schedule_delayed_work
 #define	schedule_work			linux_schedule_work
@@ -89,6 +91,8 @@ struct delayed_work {
 };
 
 struct rcu_work {
+	struct work_struct		work; /* Linux API name */
+	struct rcu_head			rw_rcu;
 };
 
 #define	WQ_FREEZABLE		__BIT(0)
@@ -145,6 +149,9 @@ bool	cancel_delayed_work_sync(struct del
 bool	flush_delayed_work(struct delayed_work *);
 bool	delayed_work_pending(const struct delayed_work *);
 
+void	INIT_RCU_WORK(struct rcu_work *, void (*fn)(struct work_struct *));
+void	queue_rcu_work(struct workqueue_struct *, struct rcu_work *);
+
 struct work_struct *
 	current_work(void);
 

Index: src/sys/external/bsd/common/linux/linux_work.c
diff -u src/sys/external/bsd/common/linux/linux_work.c:1.54 src/sys/external/bsd/common/linux/linux_work.c:1.55
--- src/sys/external/bsd/common/linux/linux_work.c:1.54	Sun Dec 19 11:40:05 2021
+++ src/sys/external/bsd/common/linux/linux_work.c	Sun Dec 19 11:40:14 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: linux_work.c,v 1.54 2021/12/19 11:40:05 riastradh Exp $	*/
+/*	$NetBSD: linux_work.c,v 1.55 2021/12/19 11:40:14 riastradh Exp $	*/
 
 /*-
  * Copyright (c) 2018 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_work.c,v 1.54 2021/12/19 11:40:05 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_work.c,v 1.55 2021/12/19 11:40:14 riastradh Exp $");
 
 #include <sys/types.h>
 #include <sys/atomic.h>
@@ -56,6 +56,7 @@ struct workqueue_struct {
 	kmutex_t		wq_lock;
 	kcondvar_t		wq_cv;
 	struct dwork_head	wq_delayed; /* delayed work scheduled */
+	struct work_head	wq_rcu;	    /* RCU work scheduled */
 	struct work_head	wq_queue;   /* work to run */
 	struct work_head	wq_dqueue;  /* delayed work to run now */
 	struct work_struct	*wq_current_work;
@@ -91,6 +92,8 @@ SDT_PROBE_DEFINE2(sdt, linux, work, rele
     "struct work_struct *"/*work*/, "struct workqueue_struct *"/*wq*/);
 SDT_PROBE_DEFINE2(sdt, linux, work, queue,
     "struct work_struct *"/*work*/, "struct workqueue_struct *"/*wq*/);
+SDT_PROBE_DEFINE2(sdt, linux, work, rcu,
+    "struct rcu_work *"/*work*/, "struct workqueue_struct *"/*wq*/);
 SDT_PROBE_DEFINE2(sdt, linux, work, cancel,
     "struct work_struct *"/*work*/, "struct workqueue_struct *"/*wq*/);
 SDT_PROBE_DEFINE3(sdt, linux, work, schedule,
@@ -260,6 +263,7 @@ alloc_workqueue(const char *name, int fl
 	mutex_init(&wq->wq_lock, MUTEX_DEFAULT, IPL_VM);
 	cv_init(&wq->wq_cv, name);
 	TAILQ_INIT(&wq->wq_delayed);
+	TAILQ_INIT(&wq->wq_rcu);
 	TAILQ_INIT(&wq->wq_queue);
 	TAILQ_INIT(&wq->wq_dqueue);
 	wq->wq_current_work = NULL;
@@ -279,6 +283,7 @@ alloc_workqueue(const char *name, int fl
 
 fail0:	KASSERT(TAILQ_EMPTY(&wq->wq_dqueue));
 	KASSERT(TAILQ_EMPTY(&wq->wq_queue));
+	KASSERT(TAILQ_EMPTY(&wq->wq_rcu));
 	KASSERT(TAILQ_EMPTY(&wq->wq_delayed));
 	cv_destroy(&wq->wq_cv);
 	mutex_destroy(&wq->wq_lock);
@@ -346,6 +351,12 @@ destroy_workqueue(struct workqueue_struc
 	}
 	mutex_exit(&wq->wq_lock);
 
+	/* Wait for all scheduled RCU work to complete.  */
+	mutex_enter(&wq->wq_lock);
+	while (!TAILQ_EMPTY(&wq->wq_rcu))
+		cv_wait(&wq->wq_cv, &wq->wq_lock);
+	mutex_exit(&wq->wq_lock);
+
 	/*
 	 * At this point, no new work can be put on the queue.
 	 */
@@ -364,6 +375,7 @@ destroy_workqueue(struct workqueue_struc
 	KASSERT(wq->wq_current_work == NULL);
 	KASSERT(TAILQ_EMPTY(&wq->wq_dqueue));
 	KASSERT(TAILQ_EMPTY(&wq->wq_queue));
+	KASSERT(TAILQ_EMPTY(&wq->wq_rcu));
 	KASSERT(TAILQ_EMPTY(&wq->wq_delayed));
 	cv_destroy(&wq->wq_cv);
 	mutex_destroy(&wq->wq_lock);
@@ -1613,3 +1625,53 @@ delayed_work_pending(const struct delaye
 
 	return work_pending(&dw->work);
 }
+
+/*
+ * INIT_RCU_WORK(rw, fn)
+ *
+ *	Initialize rw for use with a workqueue to call fn in a worker
+ *	thread after an RCU grace period.  There is no corresponding
+ *	destruction operation.
+ */
+void
+INIT_RCU_WORK(struct rcu_work *rw, void (*fn)(struct work_struct *))
+{
+
+	INIT_WORK(&rw->work, fn);
+}
+
+static void
+queue_rcu_work_cb(struct rcu_head *r)
+{
+	struct rcu_work *rw = container_of(r, struct rcu_work, rw_rcu);
+	struct workqueue_struct *wq = work_queue(&rw->work);
+
+	mutex_enter(&wq->wq_lock);
+	KASSERT(work_pending(&rw->work));
+	KASSERT(work_queue(&rw->work) == wq);
+	destroy_rcu_head(&rw->rw_rcu);
+	TAILQ_REMOVE(&wq->wq_rcu, &rw->work, work_entry);
+	TAILQ_INSERT_TAIL(&wq->wq_queue, &rw->work, work_entry);
+	cv_broadcast(&wq->wq_cv);
+	SDT_PROBE2(sdt, linux, work, queue,  &rw->work, wq);
+	mutex_exit(&wq->wq_lock);
+}
+
+/*
+ * queue_rcu_work(wq, rw)
+ *
+ *	Schedule rw to run on wq after an RCU grace period.
+ */
+void
+queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rw)
+{
+
+	mutex_enter(&wq->wq_lock);
+	if (acquire_work(&rw->work, wq)) {
+		init_rcu_head(&rw->rw_rcu);
+		SDT_PROBE2(sdt, linux, work, rcu,  rw, wq);
+		TAILQ_INSERT_TAIL(&wq->wq_rcu, &rw->work, work_entry);
+		call_rcu(&rw->rw_rcu, &queue_rcu_work_cb);
+	}
+	mutex_exit(&wq->wq_lock);
+}

Reply via email to