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); +}