734 taskq_dispatch_prealloc() desired

943 zio_interrupt ends up calling taskq_dispatch with TQ_SLEEP
illumos/illumos-gate@5aeb94743e

Essentially FreeBSD taskqueues already operate in a mode that
was added to Illumos with taskq_dispatch_ent change.
We even exposed the superior FreeBSD interface as taskq_dispatch_safe.
Now we just rename taskq_dispatch_safe to taskq_dispatch_ent and
struct struct ostask to taskq_ent_t, so that code differences will be
minimal.

After this change sys/cddl/compat/opensolaris/sys/taskq.h header is no
longer needed.

Note that this commit is not an MFV because the upstream change was not
individually committed to the vendor area.

MFC after:	8 days
This commit is contained in:
Andriy Gapon 2013-11-26 09:26:18 +00:00
parent cfab30ba16
commit 34140e78ab
8 changed files with 134 additions and 116 deletions

View File

@ -23,6 +23,9 @@
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _SYS_ZFS_CONTEXT_H
#define _SYS_ZFS_CONTEXT_H
@ -367,6 +370,16 @@ typedef struct taskq taskq_t;
typedef uintptr_t taskqid_t;
typedef void (task_func_t)(void *);
typedef struct taskq_ent {
struct taskq_ent *tqent_next;
struct taskq_ent *tqent_prev;
task_func_t *tqent_func;
void *tqent_arg;
uintptr_t tqent_flags;
} taskq_ent_t;
#define TQENT_FLAG_PREALLOC 0x1 /* taskq_dispatch_ent used */
#define TASKQ_PREPOPULATE 0x0001
#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
@ -378,6 +391,7 @@ typedef void (task_func_t)(void *);
#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
#define TQ_FRONT 0x08 /* Queue in front */
extern taskq_t *system_taskq;
extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
@ -386,6 +400,8 @@ extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
#define taskq_create_sysdc(a, b, d, e, p, dc, f) \
(taskq_create(a, b, maxclsyspri, d, e, f))
extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
extern void taskq_dispatch_ent(taskq_t *, task_func_t, void *, uint_t,
taskq_ent_t *);
extern void taskq_destroy(taskq_t *);
extern void taskq_wait(taskq_t *);
extern int taskq_member(taskq_t *, void *);

View File

@ -22,19 +22,15 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/zfs_context.h>
int taskq_now;
taskq_t *system_taskq;
typedef struct task {
struct task *task_next;
struct task *task_prev;
task_func_t *task_func;
void *task_arg;
} task_t;
#define TASKQ_ACTIVE 0x00010000
struct taskq {
@ -51,18 +47,18 @@ struct taskq {
int tq_maxalloc;
kcondvar_t tq_maxalloc_cv;
int tq_maxalloc_wait;
task_t *tq_freelist;
task_t tq_task;
taskq_ent_t *tq_freelist;
taskq_ent_t tq_task;
};
static task_t *
static taskq_ent_t *
task_alloc(taskq_t *tq, int tqflags)
{
task_t *t;
taskq_ent_t *t;
int rv;
again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
tq->tq_freelist = t->task_next;
tq->tq_freelist = t->tqent_next;
} else {
if (tq->tq_nalloc >= tq->tq_maxalloc) {
if (!(tqflags & KM_SLEEP))
@ -87,7 +83,7 @@ again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
}
mutex_exit(&tq->tq_lock);
t = kmem_alloc(sizeof (task_t), tqflags & KM_SLEEP);
t = kmem_alloc(sizeof (taskq_ent_t), tqflags & KM_SLEEP);
mutex_enter(&tq->tq_lock);
if (t != NULL)
@ -97,15 +93,15 @@ again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
}
static void
task_free(taskq_t *tq, task_t *t)
task_free(taskq_t *tq, taskq_ent_t *t)
{
if (tq->tq_nalloc <= tq->tq_minalloc) {
t->task_next = tq->tq_freelist;
t->tqent_next = tq->tq_freelist;
tq->tq_freelist = t;
} else {
tq->tq_nalloc--;
mutex_exit(&tq->tq_lock);
kmem_free(t, sizeof (task_t));
kmem_free(t, sizeof (taskq_ent_t));
mutex_enter(&tq->tq_lock);
}
@ -116,7 +112,7 @@ task_free(taskq_t *tq, task_t *t)
taskqid_t
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
{
task_t *t;
taskq_ent_t *t;
if (taskq_now) {
func(arg);
@ -130,26 +126,58 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
return (0);
}
if (tqflags & TQ_FRONT) {
t->task_next = tq->tq_task.task_next;
t->task_prev = &tq->tq_task;
t->tqent_next = tq->tq_task.tqent_next;
t->tqent_prev = &tq->tq_task;
} else {
t->task_next = &tq->tq_task;
t->task_prev = tq->tq_task.task_prev;
t->tqent_next = &tq->tq_task;
t->tqent_prev = tq->tq_task.tqent_prev;
}
t->task_next->task_prev = t;
t->task_prev->task_next = t;
t->task_func = func;
t->task_arg = arg;
t->tqent_next->tqent_prev = t;
t->tqent_prev->tqent_next = t;
t->tqent_func = func;
t->tqent_arg = arg;
cv_signal(&tq->tq_dispatch_cv);
mutex_exit(&tq->tq_lock);
return (1);
}
void
taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
taskq_ent_t *t)
{
ASSERT(func != NULL);
ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
/*
* Mark it as a prealloc'd task. This is important
* to ensure that we don't free it later.
*/
t->tqent_flags |= TQENT_FLAG_PREALLOC;
/*
* Enqueue the task to the underlying queue.
*/
mutex_enter(&tq->tq_lock);
if (flags & TQ_FRONT) {
t->tqent_next = tq->tq_task.tqent_next;
t->tqent_prev = &tq->tq_task;
} else {
t->tqent_next = &tq->tq_task;
t->tqent_prev = tq->tq_task.tqent_prev;
}
t->tqent_next->tqent_prev = t;
t->tqent_prev->tqent_next = t;
t->tqent_func = func;
t->tqent_arg = arg;
cv_signal(&tq->tq_dispatch_cv);
mutex_exit(&tq->tq_lock);
}
void
taskq_wait(taskq_t *tq)
{
mutex_enter(&tq->tq_lock);
while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0)
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
mutex_exit(&tq->tq_lock);
}
@ -158,27 +186,32 @@ static void *
taskq_thread(void *arg)
{
taskq_t *tq = arg;
task_t *t;
taskq_ent_t *t;
boolean_t prealloc;
mutex_enter(&tq->tq_lock);
while (tq->tq_flags & TASKQ_ACTIVE) {
if ((t = tq->tq_task.task_next) == &tq->tq_task) {
if ((t = tq->tq_task.tqent_next) == &tq->tq_task) {
if (--tq->tq_active == 0)
cv_broadcast(&tq->tq_wait_cv);
cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
tq->tq_active++;
continue;
}
t->task_prev->task_next = t->task_next;
t->task_next->task_prev = t->task_prev;
t->tqent_prev->tqent_next = t->tqent_next;
t->tqent_next->tqent_prev = t->tqent_prev;
t->tqent_next = NULL;
t->tqent_prev = NULL;
prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC;
mutex_exit(&tq->tq_lock);
rw_enter(&tq->tq_threadlock, RW_READER);
t->task_func(t->task_arg);
t->tqent_func(t->tqent_arg);
rw_exit(&tq->tq_threadlock);
mutex_enter(&tq->tq_lock);
task_free(tq, t);
if (!prealloc)
task_free(tq, t);
}
tq->tq_nthreads--;
cv_broadcast(&tq->tq_wait_cv);
@ -217,8 +250,8 @@ taskq_create(const char *name, int nthreads, pri_t pri,
tq->tq_nthreads = nthreads;
tq->tq_minalloc = minalloc;
tq->tq_maxalloc = maxalloc;
tq->tq_task.task_next = &tq->tq_task;
tq->tq_task.task_prev = &tq->tq_task;
tq->tq_task.tqent_next = &tq->tq_task;
tq->tq_task.tqent_prev = &tq->tq_task;
tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
if (flags & TASKQ_PREPOPULATE) {

View File

@ -46,7 +46,7 @@ static void
system_taskq_init(void *arg)
{
taskq_zone = uma_zcreate("taskq_zone", sizeof(struct ostask),
taskq_zone = uma_zcreate("taskq_zone", sizeof(taskq_ent_t),
NULL, NULL, NULL, NULL, 0, 0);
system_taskq = taskq_create("system_taskq", mp_ncpus, 0, 0, 0, 0);
}
@ -104,9 +104,9 @@ taskq_member(taskq_t *tq, kthread_t *thread)
static void
taskq_run(void *arg, int pending __unused)
{
struct ostask *task = arg;
taskq_ent_t *task = arg;
task->ost_func(task->ost_arg);
task->tqent_func(task->tqent_arg);
uma_zfree(taskq_zone, task);
}
@ -114,7 +114,7 @@ taskq_run(void *arg, int pending __unused)
taskqid_t
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags)
{
struct ostask *task;
taskq_ent_t *task;
int mflag, prio;
if ((flags & (TQ_SLEEP | TQ_NOQUEUE)) == TQ_SLEEP)
@ -131,26 +131,26 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags)
if (task == NULL)
return (0);
task->ost_func = func;
task->ost_arg = arg;
task->tqent_func = func;
task->tqent_arg = arg;
TASK_INIT(&task->ost_task, prio, taskq_run, task);
taskqueue_enqueue(tq->tq_queue, &task->ost_task);
TASK_INIT(&task->tqent_task, prio, taskq_run, task);
taskqueue_enqueue(tq->tq_queue, &task->tqent_task);
return ((taskqid_t)(void *)task);
}
static void
taskq_run_safe(void *arg, int pending __unused)
taskq_run_ent(void *arg, int pending __unused)
{
struct ostask *task = arg;
taskq_ent_t *task = arg;
task->ost_func(task->ost_arg);
task->tqent_func(task->tqent_arg);
}
taskqid_t
taskq_dispatch_safe(taskq_t *tq, task_func_t func, void *arg, u_int flags,
struct ostask *task)
void
taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, u_int flags,
taskq_ent_t *task)
{
int prio;
@ -160,11 +160,9 @@ taskq_dispatch_safe(taskq_t *tq, task_func_t func, void *arg, u_int flags,
*/
prio = !!(flags & TQ_FRONT);
task->ost_func = func;
task->ost_arg = arg;
task->tqent_func = func;
task->tqent_arg = arg;
TASK_INIT(&task->ost_task, prio, taskq_run_safe, task);
taskqueue_enqueue(tq->tq_queue, &task->ost_task);
return ((taskqid_t)(void *)task);
TASK_INIT(&task->tqent_task, prio, taskq_run_ent, task);
taskqueue_enqueue(tq->tq_queue, &task->tqent_task);
}

View File

@ -1,43 +0,0 @@
/*-
* Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _OPENSOLARIS_SYS_TASKQ_H_
#define _OPENSOLARIS_SYS_TASKQ_H_
#include_next <sys/taskq.h>
struct ostask {
struct task ost_task;
task_func_t *ost_func;
void *ost_arg;
};
taskqid_t taskq_dispatch_safe(taskq_t *tq, task_func_t func, void *arg,
u_int flags, struct ostask *task);
#endif /* _OPENSOLARIS_SYS_TASKQ_H_ */

View File

@ -825,7 +825,7 @@ static taskq_t *
spa_taskq_create(spa_t *spa, const char *name, enum zti_modes mode,
uint_t value)
{
uint_t flags = TASKQ_PREPOPULATE;
uint_t flags = 0;
boolean_t batch = B_FALSE;
switch (mode) {

View File

@ -24,6 +24,7 @@
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
#ifndef _ZIO_H
@ -474,10 +475,9 @@ struct zio {
zio_cksum_report_t *io_cksum_report;
uint64_t io_ena;
#ifdef _KERNEL
/* FreeBSD only. */
struct ostask io_task;
#endif
/* Taskq dispatching state */
taskq_ent_t io_tqent;
avl_node_t io_trim_node;
list_node_t io_trim_link;
};

View File

@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/zfs_context.h>
@ -1224,7 +1225,7 @@ zio_taskq_dispatch(zio_t *zio, enum zio_taskq_type q, boolean_t cutinline)
{
spa_t *spa = zio->io_spa;
zio_type_t t = zio->io_type;
int flags = TQ_SLEEP | (cutinline ? TQ_FRONT : 0);
int flags = (cutinline ? TQ_FRONT : 0);
ASSERT(q == ZIO_TASKQ_ISSUE || q == ZIO_TASKQ_INTERRUPT);
@ -1250,13 +1251,19 @@ zio_taskq_dispatch(zio_t *zio, enum zio_taskq_type q, boolean_t cutinline)
q++;
ASSERT3U(q, <, ZIO_TASKQ_TYPES);
#ifdef _KERNEL
(void) taskq_dispatch_safe(spa->spa_zio_taskq[t][q],
(task_func_t *)zio_execute, zio, flags, &zio->io_task);
/*
* NB: We are assuming that the zio can only be dispatched
* to a single taskq at a time. It would be a grievous error
* to dispatch the zio to another taskq at the same time.
*/
#if defined(illumos) || !defined(_KERNEL)
ASSERT(zio->io_tqent.tqent_next == NULL);
#else
(void) taskq_dispatch(spa->spa_zio_taskq[t][q],
(task_func_t *)zio_execute, zio, flags);
ASSERT(zio->io_tqent.tqent_task.ta_pending == 0);
#endif
taskq_dispatch_ent(spa->spa_zio_taskq[t][q],
(task_func_t *)zio_execute, zio, flags, &zio->io_tqent);
}
static boolean_t
@ -3174,16 +3181,15 @@ zio_done(zio_t *zio)
* Reexecution is potentially a huge amount of work.
* Hand it off to the otherwise-unused claim taskq.
*/
#ifdef _KERNEL
(void) taskq_dispatch_safe(
spa->spa_zio_taskq[ZIO_TYPE_CLAIM][ZIO_TASKQ_ISSUE],
(task_func_t *)zio_reexecute, zio, TQ_SLEEP,
&zio->io_task);
#if defined(illumos) || !defined(_KERNEL)
ASSERT(zio->io_tqent.tqent_next == NULL);
#else
(void) taskq_dispatch(
spa->spa_zio_taskq[ZIO_TYPE_CLAIM][ZIO_TASKQ_ISSUE],
(task_func_t *)zio_reexecute, zio, TQ_SLEEP);
ASSERT(zio->io_tqent.tqent_task.ta_pending == 0);
#endif
(void) taskq_dispatch_ent(
spa->spa_zio_taskq[ZIO_TYPE_CLAIM][ZIO_TASKQ_ISSUE],
(task_func_t *)zio_reexecute, zio, 0,
&zio->io_tqent);
}
return (ZIO_PIPELINE_STOP);
}

View File

@ -45,6 +45,12 @@ typedef struct taskq taskq_t;
typedef uintptr_t taskqid_t;
typedef void (task_func_t)(void *);
typedef struct taskq_ent {
struct task tqent_task;
task_func_t *tqent_func;
void *tqent_arg;
} taskq_ent_t;
struct proc;
/*
@ -80,6 +86,8 @@ taskq_t *taskq_create_proc(const char *, int, pri_t, int, int,
taskq_t *taskq_create_sysdc(const char *, int, int, int,
struct proc *, uint_t, uint_t);
taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
void taskq_dispatch_ent(taskq_t *, task_func_t, void *, uint_t,
taskq_ent_t *);
void nulltask(void *);
void taskq_destroy(taskq_t *);
void taskq_wait(taskq_t *);