zfs: enable SPA_PROCESS on the kernel side

The purpose of this change is to group kernelthreads specific to a
particular ZFS pool under a kernel process.  There can be many dozens of
threads per pool.  This change improves observability of those threads.

This change consists of several subchanges:
1. illumos taskq_create_proc can now pass its process parameter to
taskqueue.  Also, use zfsproc instead of NULL for taskq_create.  Caveat:
zfsproc might not be initialized yet.  But in that case it is still NULL,
so not worse than before.

2. illumos sys/proc.h: kthread id is stored in t_did field, not t_tid.

3. zfs: enable SPA_PROCESS on the kernel side.  The change is a bit hairy
as newproc() is implemented privately to spa.c.  I couldn't think of a
better way to populate process name than to poke inside the argument for
the process routine.

4. illumos thread_create: allow assigning thread to process other than
zfsproc.

5. zfs: expose spa_proc to other users, assign sync and quiesce threads
to it.

Pool-specific threads created using (relatively new) zthr mechanism are
still assigned to the zfskern process rather than to a respective
zpool-xxx process.  I am going to address this a bit later.

Reviewed by:	no one
MFC after:	5 weeks
Relnotes:	perhaps
Differential Revision: https://reviews.freebsd.org/D9720
This commit is contained in:
Andriy Gapon 2019-11-04 13:30:37 +00:00
parent 124003d587
commit eb819923ec
6 changed files with 72 additions and 15 deletions

View File

@ -62,9 +62,9 @@ system_taskq_fini(void *arg)
}
SYSUNINIT(system_taskq_fini, SI_SUB_CONFIGURE, SI_ORDER_ANY, system_taskq_fini, NULL);
taskq_t *
taskq_create(const char *name, int nthreads, pri_t pri, int minalloc __unused,
int maxalloc __unused, uint_t flags)
static taskq_t *
taskq_create_impl(const char *name, int nthreads, pri_t pri, proc_t *proc,
uint_t flags)
{
taskq_t *tq;
@ -74,17 +74,24 @@ taskq_create(const char *name, int nthreads, pri_t pri, int minalloc __unused,
tq = kmem_alloc(sizeof(*tq), KM_SLEEP);
tq->tq_queue = taskqueue_create(name, M_WAITOK, taskqueue_thread_enqueue,
&tq->tq_queue);
(void) taskqueue_start_threads(&tq->tq_queue, nthreads, pri, "%s", name);
(void) taskqueue_start_threads_in_proc(&tq->tq_queue, nthreads, pri,
proc, "%s", name);
return ((taskq_t *)tq);
}
taskq_t *
taskq_create_proc(const char *name, int nthreads, pri_t pri, int minalloc,
int maxalloc, proc_t *proc __unused, uint_t flags)
taskq_create(const char *name, int nthreads, pri_t pri, int minalloc __unused,
int maxalloc __unused, uint_t flags)
{
return (taskq_create_impl(name, nthreads, pri, zfsproc, flags));
}
return (taskq_create(name, nthreads, pri, minalloc, maxalloc, flags));
taskq_t *
taskq_create_proc(const char *name, int nthreads, pri_t pri, int minalloc,
int maxalloc, proc_t *proc, uint_t flags)
{
return (taskq_create_impl(name, nthreads, pri, proc, flags));
}
void

View File

@ -47,12 +47,13 @@
#define maxclsyspri PVM
#define max_ncpus (mp_maxid + 1)
#define boot_max_ncpus (mp_maxid + 1)
#define syscid 1
#define TS_RUN 0
#define p0 proc0
#define t_tid td_tid
#define t_did td_tid
typedef short pri_t;
typedef struct thread _kthread;
@ -67,6 +68,7 @@ do_thread_create(caddr_t stk, size_t stksize, void (*proc)(void *), void *arg,
size_t len, proc_t *pp, int state, pri_t pri)
{
kthread_t *td = NULL;
proc_t **ppp;
int error;
/*
@ -75,9 +77,13 @@ do_thread_create(caddr_t stk, size_t stksize, void (*proc)(void *), void *arg,
ASSERT(stk == NULL);
ASSERT(len == 0);
ASSERT(state == TS_RUN);
ASSERT(pp == &p0);
ASSERT(pp != NULL);
error = kproc_kthread_add(proc, arg, &zfsproc, &td, RFSTOPPED,
if (pp == &p0)
ppp = &zfsproc;
else
ppp = &pp;
error = kproc_kthread_add(proc, arg, ppp, &td, RFSTOPPED,
stksize / PAGE_SIZE, "zfskern", "solthread %p", proc);
if (error == 0) {
thread_lock(td);

View File

@ -176,7 +176,11 @@ boolean_t zio_taskq_sysdc = B_TRUE; /* use SDC scheduling class */
uint_t zio_taskq_basedc = 80; /* base duty cycle */
#endif
#ifdef _KERNEL
#define SPA_PROCESS
#endif
boolean_t spa_create_process = B_TRUE; /* no process ==> no sysdc */
extern int zfs_sync_pass_deferred_free;
/*
@ -1090,23 +1094,49 @@ spa_create_zio_taskqs(spa_t *spa)
}
}
#ifdef _KERNEL
#ifdef SPA_PROCESS
static int
newproc(void (*pc)(void *), void *arg, id_t cid, int pri,
void **ct, pid_t pid)
{
va_list ap;
spa_t *spa = (spa_t *)arg; /* XXX */
struct proc *newp;
struct thread *td;
int error;
ASSERT(ct == NULL);
ASSERT(pid == 0);
ASSERT(cid == syscid);
error = kproc_create(pc, arg, &newp, 0, 0, "zpool-%s", spa->spa_name);
if (error != 0)
return (error);
td = FIRST_THREAD_IN_PROC(newp);
thread_lock(td);
sched_prio(td, pri);
thread_unlock(td);
return (0);
}
static void
spa_thread(void *arg)
{
callb_cpr_t cprinfo;
spa_t *spa = arg;
#ifdef illumos
user_t *pu = PTOU(curproc);
#endif
CALLB_CPR_INIT(&cprinfo, &spa->spa_proc_lock, callb_generic_cpr,
spa->spa_name);
ASSERT(curproc != &p0);
#ifdef illumos
(void) snprintf(pu->u_psargs, sizeof (pu->u_psargs),
"zpool-%s", spa->spa_name);
(void) strlcpy(pu->u_comm, pu->u_psargs, sizeof (pu->u_comm));
#endif
#ifdef PSRSET_BIND
/* bind this thread to the requested psrset */
@ -1160,11 +1190,14 @@ spa_thread(void *arg)
cv_broadcast(&spa->spa_proc_cv);
CALLB_CPR_EXIT(&cprinfo); /* drops spa_proc_lock */
#ifdef illumos
mutex_enter(&curproc->p_lock);
lwp_exit();
#else
kthread_exit();
#endif
}
#endif /* SPA_PROCESS */
#endif
/*
* Activate an uninitialized pool.
@ -1211,7 +1244,9 @@ spa_activate(spa_t *spa, int mode)
mutex_exit(&spa->spa_proc_lock);
/* If we didn't create a process, we need to create our taskqs. */
#ifndef SPA_PROCESS
ASSERT(spa->spa_proc == &p0);
#endif /* SPA_PROCESS */
if (spa->spa_proc == &p0) {
spa_create_zio_taskqs(spa);
}
@ -1315,6 +1350,7 @@ spa_deactivate(spa_t *spa)
mutex_exit(&spa->spa_proc_lock);
#ifdef SPA_PROCESS
#ifdef illumos
/*
* We want to make sure spa_thread() has actually exited the ZFS
* module, so that the module can't be unloaded out from underneath
@ -1324,6 +1360,7 @@ spa_deactivate(spa_t *spa)
thread_join(spa->spa_did);
spa->spa_did = 0;
}
#endif
#endif /* SPA_PROCESS */
}

View File

@ -1935,6 +1935,12 @@ spa_deadman_synctime(spa_t *spa)
return (spa->spa_deadman_synctime);
}
struct proc *
spa_proc(spa_t *spa)
{
return (spa->spa_proc);
}
uint64_t
dva_get_dsize_sync(spa_t *spa, const dva_t *dva)
{

View File

@ -833,6 +833,7 @@ extern uint64_t spa_bootfs(spa_t *spa);
extern uint64_t spa_delegation(spa_t *spa);
extern objset_t *spa_meta_objset(spa_t *spa);
extern uint64_t spa_deadman_synctime(spa_t *spa);
extern struct proc *spa_proc(spa_t *spa);
extern uint64_t spa_dirty_data(spa_t *spa);
/* Miscellaneous support routines */

View File

@ -209,7 +209,7 @@ txg_sync_start(dsl_pool_t *dp)
tx->tx_threads = 2;
tx->tx_quiesce_thread = thread_create(NULL, 0, txg_quiesce_thread,
dp, 0, &p0, TS_RUN, minclsyspri);
dp, 0, spa_proc(dp->dp_spa), TS_RUN, minclsyspri);
/*
* The sync thread can need a larger-than-default stack size on
@ -217,7 +217,7 @@ txg_sync_start(dsl_pool_t *dp)
* scrub_visitbp() recursion.
*/
tx->tx_sync_thread = thread_create(NULL, 32<<10, txg_sync_thread,
dp, 0, &p0, TS_RUN, minclsyspri);
dp, 0, spa_proc(dp->dp_spa), TS_RUN, minclsyspri);
mutex_exit(&tx->tx_sync_lock);
}