mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
spl-taskq: per-taskq kstats
This exposes a variety of per-taskq stats under /proc/spl/kstat/taskq, one file per taskq, named for the taskq name.instance. These include a small amount of info about the taskq config, the current state of the threads and queues, and various counters for thread and queue activity since the taskq was created. To assist with decrementing queue size counters, the list an entry is on is encoded in spare bits in the entry flags. Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Tino Reichardt <milky-zfs@mcmilk.de> Signed-off-by: Rob Norris <rob.norris@klarasystems.com> Sponsored-by: Klara, Inc. Sponsored-by: Syneto Closes #16171
This commit is contained in:
committed by
Brian Behlendorf
parent
f0ad031cd9
commit
db40fe4cf6
+300
-14
@@ -22,16 +22,98 @@
|
||||
*
|
||||
* Solaris Porting Layer (SPL) Task Queue Implementation.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2024, Klara Inc.
|
||||
* Copyright (c) 2024, Syneto
|
||||
*/
|
||||
|
||||
#include <sys/timer.h>
|
||||
#include <sys/taskq.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/tsd.h>
|
||||
#include <sys/trace_spl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/kstat.h>
|
||||
#ifdef HAVE_CPU_HOTPLUG
|
||||
#include <linux/cpuhotplug.h>
|
||||
#endif
|
||||
|
||||
typedef struct taskq_kstats {
|
||||
/* static values, for completeness */
|
||||
kstat_named_t tqks_threads_max;
|
||||
kstat_named_t tqks_entry_pool_min;
|
||||
kstat_named_t tqks_entry_pool_max;
|
||||
|
||||
/* gauges (inc/dec counters, current value) */
|
||||
kstat_named_t tqks_threads_active;
|
||||
kstat_named_t tqks_threads_idle;
|
||||
kstat_named_t tqks_threads_total;
|
||||
kstat_named_t tqks_tasks_pending;
|
||||
kstat_named_t tqks_tasks_priority;
|
||||
kstat_named_t tqks_tasks_total;
|
||||
kstat_named_t tqks_tasks_delayed;
|
||||
kstat_named_t tqks_entries_free;
|
||||
|
||||
/* counters (inc only, since taskq creation) */
|
||||
kstat_named_t tqks_threads_created;
|
||||
kstat_named_t tqks_threads_destroyed;
|
||||
kstat_named_t tqks_tasks_dispatched;
|
||||
kstat_named_t tqks_tasks_dispatched_delayed;
|
||||
kstat_named_t tqks_tasks_executed_normal;
|
||||
kstat_named_t tqks_tasks_executed_priority;
|
||||
kstat_named_t tqks_tasks_executed;
|
||||
kstat_named_t tqks_tasks_delayed_requeued;
|
||||
kstat_named_t tqks_tasks_cancelled;
|
||||
kstat_named_t tqks_thread_wakeups;
|
||||
kstat_named_t tqks_thread_wakeups_nowork;
|
||||
kstat_named_t tqks_thread_sleeps;
|
||||
} taskq_kstats_t;
|
||||
|
||||
static taskq_kstats_t taskq_kstats_template = {
|
||||
{ "threads_max", KSTAT_DATA_UINT64 },
|
||||
{ "entry_pool_min", KSTAT_DATA_UINT64 },
|
||||
{ "entry_pool_max", KSTAT_DATA_UINT64 },
|
||||
{ "threads_active", KSTAT_DATA_UINT64 },
|
||||
{ "threads_idle", KSTAT_DATA_UINT64 },
|
||||
{ "threads_total", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_pending", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_priority", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_total", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_delayed", KSTAT_DATA_UINT64 },
|
||||
{ "entries_free", KSTAT_DATA_UINT64 },
|
||||
|
||||
{ "threads_created", KSTAT_DATA_UINT64 },
|
||||
{ "threads_destroyed", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_dispatched", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_dispatched_delayed", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_executed_normal", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_executed_priority", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_executed", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_delayed_requeued", KSTAT_DATA_UINT64 },
|
||||
{ "tasks_cancelled", KSTAT_DATA_UINT64 },
|
||||
{ "thread_wakeups", KSTAT_DATA_UINT64 },
|
||||
{ "thread_wakeups_nowork", KSTAT_DATA_UINT64 },
|
||||
{ "thread_sleeps", KSTAT_DATA_UINT64 },
|
||||
};
|
||||
|
||||
#define TQSTAT_INC(tq, stat) wmsum_add(&tq->tq_sums.tqs_##stat, 1)
|
||||
#define TQSTAT_DEC(tq, stat) wmsum_add(&tq->tq_sums.tqs_##stat, -1)
|
||||
|
||||
#define _TQSTAT_MOD_LIST(mod, tq, t) do { \
|
||||
switch (t->tqent_flags & TQENT_LIST_MASK) { \
|
||||
case TQENT_LIST_NONE: ASSERT(list_empty(&t->tqent_list)); break;\
|
||||
case TQENT_LIST_PENDING: mod(tq, tasks_pending); break; \
|
||||
case TQENT_LIST_PRIORITY: mod(tq, tasks_priority); break; \
|
||||
case TQENT_LIST_DELAY: mod(tq, tasks_delayed); break; \
|
||||
} \
|
||||
} while (0)
|
||||
#define TQSTAT_INC_LIST(tq, t) _TQSTAT_MOD_LIST(TQSTAT_INC, tq, t)
|
||||
#define TQSTAT_DEC_LIST(tq, t) _TQSTAT_MOD_LIST(TQSTAT_DEC, tq, t)
|
||||
|
||||
#define TQENT_SET_LIST(t, l) \
|
||||
t->tqent_flags = (t->tqent_flags & ~TQENT_LIST_MASK) | l;
|
||||
|
||||
static int spl_taskq_thread_bind = 0;
|
||||
module_param(spl_taskq_thread_bind, int, 0644);
|
||||
MODULE_PARM_DESC(spl_taskq_thread_bind, "Bind taskq thread to CPU by default");
|
||||
@@ -134,6 +216,7 @@ retry:
|
||||
ASSERT(!timer_pending(&t->tqent_timer));
|
||||
|
||||
list_del_init(&t->tqent_list);
|
||||
TQSTAT_DEC(tq, entries_free);
|
||||
return (t);
|
||||
}
|
||||
|
||||
@@ -204,12 +287,11 @@ task_done(taskq_t *tq, taskq_ent_t *t)
|
||||
{
|
||||
ASSERT(tq);
|
||||
ASSERT(t);
|
||||
ASSERT(list_empty(&t->tqent_list));
|
||||
|
||||
/* Wake tasks blocked in taskq_wait_id() */
|
||||
wake_up_all(&t->tqent_waitq);
|
||||
|
||||
list_del_init(&t->tqent_list);
|
||||
|
||||
if (tq->tq_nalloc <= tq->tq_minalloc) {
|
||||
t->tqent_id = TASKQID_INVALID;
|
||||
t->tqent_func = NULL;
|
||||
@@ -217,6 +299,7 @@ task_done(taskq_t *tq, taskq_ent_t *t)
|
||||
t->tqent_flags = 0;
|
||||
|
||||
list_add_tail(&t->tqent_list, &tq->tq_free_list);
|
||||
TQSTAT_INC(tq, entries_free);
|
||||
} else {
|
||||
task_free(tq, t);
|
||||
}
|
||||
@@ -263,6 +346,8 @@ task_expire_impl(taskq_ent_t *t)
|
||||
spin_unlock_irqrestore(&tq->tq_lock, flags);
|
||||
|
||||
wake_up(&tq->tq_work_waitq);
|
||||
|
||||
TQSTAT_INC(tq, tasks_delayed_requeued);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -534,7 +619,10 @@ taskq_cancel_id(taskq_t *tq, taskqid_t id)
|
||||
t = taskq_find(tq, id);
|
||||
if (t && t != ERR_PTR(-EBUSY)) {
|
||||
list_del_init(&t->tqent_list);
|
||||
TQSTAT_DEC_LIST(tq, t);
|
||||
|
||||
t->tqent_flags |= TQENT_FLAG_CANCEL;
|
||||
TQSTAT_INC(tq, tasks_cancelled);
|
||||
|
||||
/*
|
||||
* When canceling the lowest outstanding task id we
|
||||
@@ -604,13 +692,19 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags)
|
||||
spin_lock(&t->tqent_lock);
|
||||
|
||||
/* Queue to the front of the list to enforce TQ_NOQUEUE semantics */
|
||||
if (flags & TQ_NOQUEUE)
|
||||
if (flags & TQ_NOQUEUE) {
|
||||
TQENT_SET_LIST(t, TQENT_LIST_PRIORITY);
|
||||
list_add(&t->tqent_list, &tq->tq_prio_list);
|
||||
/* Queue to the priority list instead of the pending list */
|
||||
else if (flags & TQ_FRONT)
|
||||
} else if (flags & TQ_FRONT) {
|
||||
TQENT_SET_LIST(t, TQENT_LIST_PRIORITY);
|
||||
list_add_tail(&t->tqent_list, &tq->tq_prio_list);
|
||||
else
|
||||
} else {
|
||||
TQENT_SET_LIST(t, TQENT_LIST_PENDING);
|
||||
list_add_tail(&t->tqent_list, &tq->tq_pend_list);
|
||||
}
|
||||
TQSTAT_INC_LIST(tq, t);
|
||||
TQSTAT_INC(tq, tasks_total);
|
||||
|
||||
t->tqent_id = rc = tq->tq_next_id;
|
||||
tq->tq_next_id++;
|
||||
@@ -629,6 +723,8 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags)
|
||||
|
||||
wake_up(&tq->tq_work_waitq);
|
||||
|
||||
TQSTAT_INC(tq, tasks_dispatched);
|
||||
|
||||
/* Spawn additional taskq threads if required. */
|
||||
if (!(flags & TQ_NOQUEUE) && tq->tq_nactive == tq->tq_nthreads)
|
||||
(void) taskq_thread_spawn(tq);
|
||||
@@ -662,6 +758,8 @@ taskq_dispatch_delay(taskq_t *tq, task_func_t func, void *arg,
|
||||
|
||||
/* Queue to the delay list for subsequent execution */
|
||||
list_add_tail(&t->tqent_list, &tq->tq_delay_list);
|
||||
TQENT_SET_LIST(t, TQENT_LIST_DELAY);
|
||||
TQSTAT_INC_LIST(tq, t);
|
||||
|
||||
t->tqent_id = rc = tq->tq_next_id;
|
||||
tq->tq_next_id++;
|
||||
@@ -676,6 +774,8 @@ taskq_dispatch_delay(taskq_t *tq, task_func_t func, void *arg,
|
||||
|
||||
spin_unlock(&t->tqent_lock);
|
||||
|
||||
TQSTAT_INC(tq, tasks_dispatched_delayed);
|
||||
|
||||
/* Spawn additional taskq threads if required. */
|
||||
if (tq->tq_nactive == tq->tq_nthreads)
|
||||
(void) taskq_thread_spawn(tq);
|
||||
@@ -724,10 +824,15 @@ taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
|
||||
t->tqent_flags |= TQENT_FLAG_PREALLOC;
|
||||
|
||||
/* Queue to the priority list instead of the pending list */
|
||||
if (flags & TQ_FRONT)
|
||||
if (flags & TQ_FRONT) {
|
||||
TQENT_SET_LIST(t, TQENT_LIST_PRIORITY);
|
||||
list_add_tail(&t->tqent_list, &tq->tq_prio_list);
|
||||
else
|
||||
} else {
|
||||
TQENT_SET_LIST(t, TQENT_LIST_PENDING);
|
||||
list_add_tail(&t->tqent_list, &tq->tq_pend_list);
|
||||
}
|
||||
TQSTAT_INC_LIST(tq, t);
|
||||
TQSTAT_INC(tq, tasks_total);
|
||||
|
||||
t->tqent_id = tq->tq_next_id;
|
||||
tq->tq_next_id++;
|
||||
@@ -742,6 +847,8 @@ taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
|
||||
|
||||
wake_up(&tq->tq_work_waitq);
|
||||
|
||||
TQSTAT_INC(tq, tasks_dispatched);
|
||||
|
||||
/* Spawn additional taskq threads if required. */
|
||||
if (tq->tq_nactive == tq->tq_nthreads)
|
||||
(void) taskq_thread_spawn(tq);
|
||||
@@ -908,6 +1015,8 @@ taskq_thread(void *args)
|
||||
wake_up(&tq->tq_wait_waitq);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
TQSTAT_INC(tq, threads_total);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
|
||||
if (list_empty(&tq->tq_pend_list) &&
|
||||
@@ -919,9 +1028,15 @@ taskq_thread(void *args)
|
||||
add_wait_queue_exclusive(&tq->tq_work_waitq, &wait);
|
||||
spin_unlock_irqrestore(&tq->tq_lock, flags);
|
||||
|
||||
TQSTAT_INC(tq, thread_sleeps);
|
||||
TQSTAT_INC(tq, threads_idle);
|
||||
|
||||
schedule();
|
||||
seq_tasks = 0;
|
||||
|
||||
TQSTAT_DEC(tq, threads_idle);
|
||||
TQSTAT_INC(tq, thread_wakeups);
|
||||
|
||||
spin_lock_irqsave_nested(&tq->tq_lock, flags,
|
||||
tq->tq_lock_class);
|
||||
remove_wait_queue(&tq->tq_work_waitq, &wait);
|
||||
@@ -931,6 +1046,8 @@ taskq_thread(void *args)
|
||||
|
||||
if ((t = taskq_next_ent(tq)) != NULL) {
|
||||
list_del_init(&t->tqent_list);
|
||||
TQSTAT_DEC_LIST(tq, t);
|
||||
TQSTAT_DEC(tq, tasks_total);
|
||||
|
||||
/*
|
||||
* A TQENT_FLAG_PREALLOC task may be reused or freed
|
||||
@@ -955,6 +1072,7 @@ taskq_thread(void *args)
|
||||
tq->tq_nactive++;
|
||||
spin_unlock_irqrestore(&tq->tq_lock, flags);
|
||||
|
||||
TQSTAT_INC(tq, threads_active);
|
||||
DTRACE_PROBE1(taskq_ent__start, taskq_ent_t *, t);
|
||||
|
||||
/* Perform the requested task */
|
||||
@@ -962,8 +1080,17 @@ taskq_thread(void *args)
|
||||
|
||||
DTRACE_PROBE1(taskq_ent__finish, taskq_ent_t *, t);
|
||||
|
||||
TQSTAT_DEC(tq, threads_active);
|
||||
if ((t->tqent_flags & TQENT_LIST_MASK) ==
|
||||
TQENT_LIST_PENDING)
|
||||
TQSTAT_INC(tq, tasks_executed_normal);
|
||||
else
|
||||
TQSTAT_INC(tq, tasks_executed_priority);
|
||||
TQSTAT_INC(tq, tasks_executed);
|
||||
|
||||
spin_lock_irqsave_nested(&tq->tq_lock, flags,
|
||||
tq->tq_lock_class);
|
||||
|
||||
tq->tq_nactive--;
|
||||
list_del_init(&tqt->tqt_active_list);
|
||||
tqt->tqt_task = NULL;
|
||||
@@ -989,7 +1116,8 @@ taskq_thread(void *args)
|
||||
tqt->tqt_id = TASKQID_INVALID;
|
||||
tqt->tqt_flags = 0;
|
||||
wake_up_all(&tq->tq_wait_waitq);
|
||||
}
|
||||
} else
|
||||
TQSTAT_INC(tq, thread_wakeups_nowork);
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
@@ -998,6 +1126,10 @@ taskq_thread(void *args)
|
||||
__set_current_state(TASK_RUNNING);
|
||||
tq->tq_nthreads--;
|
||||
list_del_init(&tqt->tqt_thread_list);
|
||||
|
||||
TQSTAT_DEC(tq, threads_total);
|
||||
TQSTAT_INC(tq, threads_destroyed);
|
||||
|
||||
error:
|
||||
kmem_free(tqt, sizeof (taskq_thread_t));
|
||||
spin_unlock_irqrestore(&tq->tq_lock, flags);
|
||||
@@ -1037,9 +1169,156 @@ taskq_thread_create(taskq_t *tq)
|
||||
|
||||
wake_up_process(tqt->tqt_thread);
|
||||
|
||||
TQSTAT_INC(tq, threads_created);
|
||||
|
||||
return (tqt);
|
||||
}
|
||||
|
||||
static void
|
||||
taskq_stats_init(taskq_t *tq)
|
||||
{
|
||||
taskq_sums_t *tqs = &tq->tq_sums;
|
||||
wmsum_init(&tqs->tqs_threads_active, 0);
|
||||
wmsum_init(&tqs->tqs_threads_idle, 0);
|
||||
wmsum_init(&tqs->tqs_threads_total, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_pending, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_priority, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_total, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_delayed, 0);
|
||||
wmsum_init(&tqs->tqs_entries_free, 0);
|
||||
wmsum_init(&tqs->tqs_threads_created, 0);
|
||||
wmsum_init(&tqs->tqs_threads_destroyed, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_dispatched, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_dispatched_delayed, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_executed_normal, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_executed_priority, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_executed, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_delayed_requeued, 0);
|
||||
wmsum_init(&tqs->tqs_tasks_cancelled, 0);
|
||||
wmsum_init(&tqs->tqs_thread_wakeups, 0);
|
||||
wmsum_init(&tqs->tqs_thread_wakeups_nowork, 0);
|
||||
wmsum_init(&tqs->tqs_thread_sleeps, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
taskq_stats_fini(taskq_t *tq)
|
||||
{
|
||||
taskq_sums_t *tqs = &tq->tq_sums;
|
||||
wmsum_fini(&tqs->tqs_threads_active);
|
||||
wmsum_fini(&tqs->tqs_threads_idle);
|
||||
wmsum_fini(&tqs->tqs_threads_total);
|
||||
wmsum_fini(&tqs->tqs_tasks_pending);
|
||||
wmsum_fini(&tqs->tqs_tasks_priority);
|
||||
wmsum_fini(&tqs->tqs_tasks_total);
|
||||
wmsum_fini(&tqs->tqs_tasks_delayed);
|
||||
wmsum_fini(&tqs->tqs_entries_free);
|
||||
wmsum_fini(&tqs->tqs_threads_created);
|
||||
wmsum_fini(&tqs->tqs_threads_destroyed);
|
||||
wmsum_fini(&tqs->tqs_tasks_dispatched);
|
||||
wmsum_fini(&tqs->tqs_tasks_dispatched_delayed);
|
||||
wmsum_fini(&tqs->tqs_tasks_executed_normal);
|
||||
wmsum_fini(&tqs->tqs_tasks_executed_priority);
|
||||
wmsum_fini(&tqs->tqs_tasks_executed);
|
||||
wmsum_fini(&tqs->tqs_tasks_delayed_requeued);
|
||||
wmsum_fini(&tqs->tqs_tasks_cancelled);
|
||||
wmsum_fini(&tqs->tqs_thread_wakeups);
|
||||
wmsum_fini(&tqs->tqs_thread_wakeups_nowork);
|
||||
wmsum_fini(&tqs->tqs_thread_sleeps);
|
||||
}
|
||||
|
||||
static int
|
||||
taskq_kstats_update(kstat_t *ksp, int rw)
|
||||
{
|
||||
if (rw == KSTAT_WRITE)
|
||||
return (EACCES);
|
||||
|
||||
taskq_t *tq = ksp->ks_private;
|
||||
taskq_kstats_t *tqks = ksp->ks_data;
|
||||
|
||||
tqks->tqks_threads_max.value.ui64 = tq->tq_maxthreads;
|
||||
tqks->tqks_entry_pool_min.value.ui64 = tq->tq_minalloc;
|
||||
tqks->tqks_entry_pool_max.value.ui64 = tq->tq_maxalloc;
|
||||
|
||||
taskq_sums_t *tqs = &tq->tq_sums;
|
||||
|
||||
tqks->tqks_threads_active.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_threads_active);
|
||||
tqks->tqks_threads_idle.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_threads_idle);
|
||||
tqks->tqks_threads_total.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_threads_total);
|
||||
tqks->tqks_tasks_pending.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_pending);
|
||||
tqks->tqks_tasks_priority.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_priority);
|
||||
tqks->tqks_tasks_total.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_total);
|
||||
tqks->tqks_tasks_delayed.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_delayed);
|
||||
tqks->tqks_entries_free.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_entries_free);
|
||||
tqks->tqks_threads_created.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_threads_created);
|
||||
tqks->tqks_threads_destroyed.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_threads_destroyed);
|
||||
tqks->tqks_tasks_dispatched.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_dispatched);
|
||||
tqks->tqks_tasks_dispatched_delayed.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_dispatched_delayed);
|
||||
tqks->tqks_tasks_executed_normal.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_executed_normal);
|
||||
tqks->tqks_tasks_executed_priority.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_executed_priority);
|
||||
tqks->tqks_tasks_executed.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_executed);
|
||||
tqks->tqks_tasks_delayed_requeued.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_delayed_requeued);
|
||||
tqks->tqks_tasks_cancelled.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_tasks_cancelled);
|
||||
tqks->tqks_thread_wakeups.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_thread_wakeups);
|
||||
tqks->tqks_thread_wakeups_nowork.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_thread_wakeups_nowork);
|
||||
tqks->tqks_thread_sleeps.value.ui64 =
|
||||
wmsum_value(&tqs->tqs_thread_sleeps);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
taskq_kstats_init(taskq_t *tq)
|
||||
{
|
||||
char name[TASKQ_NAMELEN+5]; /* 5 for dot, 3x instance digits, null */
|
||||
snprintf(name, sizeof (name), "%s.%d", tq->tq_name, tq->tq_instance);
|
||||
|
||||
kstat_t *ksp = kstat_create("taskq", 0, name, "misc",
|
||||
KSTAT_TYPE_NAMED, sizeof (taskq_kstats_t) / sizeof (kstat_named_t),
|
||||
KSTAT_FLAG_VIRTUAL);
|
||||
|
||||
if (ksp == NULL)
|
||||
return;
|
||||
|
||||
ksp->ks_private = tq;
|
||||
ksp->ks_update = taskq_kstats_update;
|
||||
ksp->ks_data = kmem_alloc(sizeof (taskq_kstats_t), KM_SLEEP);
|
||||
memcpy(ksp->ks_data, &taskq_kstats_template, sizeof (taskq_kstats_t));
|
||||
kstat_install(ksp);
|
||||
|
||||
tq->tq_ksp = ksp;
|
||||
}
|
||||
|
||||
static void
|
||||
taskq_kstats_fini(taskq_t *tq)
|
||||
{
|
||||
if (tq->tq_ksp == NULL)
|
||||
return;
|
||||
|
||||
kmem_free(tq->tq_ksp->ks_data, sizeof (taskq_kstats_t));
|
||||
kstat_delete(tq->tq_ksp);
|
||||
|
||||
tq->tq_ksp = NULL;
|
||||
}
|
||||
|
||||
taskq_t *
|
||||
taskq_create(const char *name, int threads_arg, pri_t pri,
|
||||
int minalloc, int maxalloc, uint_t flags)
|
||||
@@ -1104,6 +1383,7 @@ taskq_create(const char *name, int threads_arg, pri_t pri,
|
||||
init_waitqueue_head(&tq->tq_wait_waitq);
|
||||
tq->tq_lock_class = TQ_LOCK_GENERAL;
|
||||
INIT_LIST_HEAD(&tq->tq_taskqs);
|
||||
taskq_stats_init(tq);
|
||||
|
||||
if (flags & TASKQ_PREPOPULATE) {
|
||||
spin_lock_irqsave_nested(&tq->tq_lock, irqflags,
|
||||
@@ -1137,14 +1417,17 @@ taskq_create(const char *name, int threads_arg, pri_t pri,
|
||||
|
||||
if (rc) {
|
||||
taskq_destroy(tq);
|
||||
tq = NULL;
|
||||
} else {
|
||||
down_write(&tq_list_sem);
|
||||
tq->tq_instance = taskq_find_by_name(name) + 1;
|
||||
list_add_tail(&tq->tq_taskqs, &tq_list);
|
||||
up_write(&tq_list_sem);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
down_write(&tq_list_sem);
|
||||
tq->tq_instance = taskq_find_by_name(name) + 1;
|
||||
list_add_tail(&tq->tq_taskqs, &tq_list);
|
||||
up_write(&tq_list_sem);
|
||||
|
||||
/* Install kstats late, because the name includes tq_instance */
|
||||
taskq_kstats_init(tq);
|
||||
|
||||
return (tq);
|
||||
}
|
||||
EXPORT_SYMBOL(taskq_create);
|
||||
@@ -1177,6 +1460,8 @@ taskq_destroy(taskq_t *tq)
|
||||
|
||||
taskq_wait(tq);
|
||||
|
||||
taskq_kstats_fini(tq);
|
||||
|
||||
/* remove taskq from global list used by the kstats */
|
||||
down_write(&tq_list_sem);
|
||||
list_del(&tq->tq_taskqs);
|
||||
@@ -1230,6 +1515,7 @@ taskq_destroy(taskq_t *tq)
|
||||
|
||||
spin_unlock_irqrestore(&tq->tq_lock, flags);
|
||||
|
||||
taskq_stats_fini(tq);
|
||||
kmem_strfree(tq->tq_name);
|
||||
kmem_free(tq, sizeof (taskq_t));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user