> Date: Mon, 30 Nov 2015 16:28:55 +1000
> From: David Gwynne <[email protected]>
>
> while playing with some options around running an interfaces send
> queue, i came up with the following. if you've deferred work to a
> taskq and you're not reference counting, this can help you guarantee
> that any work before a point has completed before proceeding.
>
> think of it like intr_barrier.
>
> i dont currently have a need for this right now, so there's no real
> pressure to put this in. it's more a backup.
>
> if someone oks it i will happily commit it though.
Never been a big fan of adding interfaces before we have an actual use
case. I might have one though in inteldrm(4). But I'll need to look
a little bit deeper into that.
One problem with the interface is that it doesn't really work for a
taskq that has multiple worker threads isn't it?
> Index: share/man/man9/task_add.9
> ===================================================================
> RCS file: /cvs/src/share/man/man9/task_add.9,v
> retrieving revision 1.16
> diff -u -p -r1.16 task_add.9
> --- share/man/man9/task_add.9 14 Sep 2015 15:14:55 -0000 1.16
> +++ share/man/man9/task_add.9 30 Nov 2015 06:24:29 -0000
> @@ -20,6 +20,7 @@
> .Sh NAME
> .Nm taskq_create ,
> .Nm taskq_destroy ,
> +.Nm taskq_barrier ,
> .Nm task_set ,
> .Nm task_add ,
> .Nm task_del ,
> @@ -37,6 +38,8 @@
> .Ft void
> .Fn taskq_destroy "struct taskq *tq"
> .Ft void
> +.Fn taskq_barrier "struct taskq *tq"
> +.Ft void
> .Fn task_set "struct task *t" "void (*fn)(void *)" "void *arg"
> .Ft int
> .Fn task_add "struct taskq *tq" "struct task *t"
> @@ -88,6 +91,15 @@ Calling
> against the system taskq is an error and will lead to undefined
> behaviour or a system fault.
> .Pp
> +.Fn taskq_barrier
> +guarantees that any task that was running on the
> +.Fa tq
> +taskq when the barrier was called has finished by the time the barrier
> +returns.
> +.Fn taskq_barrier
> +is only supported on taskqs serviced by 1 thread,
> +and may not be called by a task running in the specified taskq.
> +.Pp
> It is the responsibility of the caller to provide the
> .Fn task_set ,
> .Fn task_add ,
> @@ -163,6 +175,8 @@ argument given in
> and
> .Fn taskq_destroy
> can be called during autoconf, or from process context.
> +.Fn taskq_barrier
> +can be called from process context.
> .Fn task_set ,
> .Fn task_add ,
> and
> Index: share/man/man9/Makefile
> ===================================================================
> RCS file: /cvs/src/share/man/man9/Makefile,v
> retrieving revision 1.262
> diff -u -p -r1.262 Makefile
> --- share/man/man9/Makefile 25 Nov 2015 03:09:57 -0000 1.262
> +++ share/man/man9/Makefile 30 Nov 2015 06:24:29 -0000
> @@ -397,6 +397,7 @@ MLINKS+=systrace.9 systrace_redirect.9 \
> systrace.9 systrace_fork.9 systrace.9 systrace_exit.9
> MLINKS+=task_add.9 taskq_create.9 \
> task_add.9 taskq_destroy.9 \
> + task_add.9 taskq_barrier.9 \
> task_add.9 task_set.9 \
> task_add.9 task_del.9 \
> task_add.9 TASK_INITIALIZER.9
> Index: sys/sys/task.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/task.h,v
> retrieving revision 1.8
> diff -u -p -r1.8 task.h
> --- sys/sys/task.h 9 Feb 2015 03:15:41 -0000 1.8
> +++ sys/sys/task.h 30 Nov 2015 06:24:29 -0000
> @@ -39,6 +39,7 @@ extern struct taskq *const systqmp;
>
> struct taskq *taskq_create(const char *, unsigned int, int, unsigned int);
> void taskq_destroy(struct taskq *);
> +void taskq_barrier(struct taskq *);
>
> void task_set(struct task *, void (*)(void *), void *);
> int task_add(struct taskq *, struct task *);
> Index: sys/kern/kern_task.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/kern_task.c,v
> retrieving revision 1.15
> diff -u -p -r1.15 kern_task.c
> --- sys/kern/kern_task.c 19 Nov 2015 13:19:24 -0000 1.15
> +++ sys/kern/kern_task.c 30 Nov 2015 06:24:29 -0000
> @@ -22,6 +22,7 @@
> #include <sys/mutex.h>
> #include <sys/kthread.h>
> #include <sys/task.h>
> +#include <sys/proc.h>
>
> #define TASK_ONQUEUE 1
>
> @@ -68,6 +69,7 @@ struct taskq *const systqmp = &taskq_sys
>
> void taskq_init(void); /* called in init_main.c */
> void taskq_create_thread(void *);
> +void taskq_barrier_task(void *);
> int taskq_sleep(const volatile void *, struct mutex *, int,
> const char *, int);
> int taskq_next_work(struct taskq *, struct task *, sleepfn);
> @@ -176,6 +178,30 @@ taskq_create_thread(void *arg)
> } while (tq->tq_running < tq->tq_nthreads);
>
> mtx_leave(&tq->tq_mtx);
> +}
> +
> +void
> +taskq_barrier(struct taskq *tq)
> +{
> + struct sleep_state sls;
> + unsigned int notdone = 1;
> + struct task t = TASK_INITIALIZER(taskq_barrier_task, ¬done);
> +
> + task_add(tq, &t);
> +
> + while (notdone) {
> + sleep_setup(&sls, ¬done, PWAIT, "tqbar");
> + sleep_finish(&sls, notdone);
> + }
> +}
> +
> +void
> +taskq_barrier_task(void *p)
> +{
> + unsigned int *notdone = p;
> +
> + *notdone = 0;
> + wakeup_one(notdone);
> }
>
> void
>
>