Skip to content

Commit

Permalink
zpool_create_minors parallel prefetch
Browse files Browse the repository at this point in the history
Do parallel prefetch all zvol dnodes before actually creating each individual.
This will greatly reduce the import time when having a lot of zvols and disk
is slow.

Signed-off-by: Chunwei Chen <[email protected]>
  • Loading branch information
Chunwei Chen committed Dec 2, 2016
1 parent d45e010 commit 7ac557c
Showing 1 changed file with 89 additions and 10 deletions.
99 changes: 89 additions & 10 deletions module/zfs/zvol.c
Original file line number Diff line number Diff line change
Expand Up @@ -1482,14 +1482,42 @@ zvol_rename_minor(zvol_state_t *zv, const char *newname)
set_disk_ro(zv->zv_disk, readonly);
}

typedef struct minors_job {
list_t *list;
list_node_t link;
/* input */
char *name;
/* output */
int error;
} minors_job_t;

/*
* Prefetch zvol dnodes for the minors_job
*/
static void
zvol_prefetch_minors_impl(void *arg)
{
minors_job_t *job = arg;
char *dsname = job->name;
objset_t *os = NULL;

job->error = dmu_objset_own(dsname, DMU_OST_ZVOL, B_TRUE, zvol_tag,
&os);
if (job->error == 0) {
dmu_prefetch(os, ZVOL_OBJ, 0, 0, 0, ZIO_PRIORITY_SYNC_READ);
dmu_objset_disown(os, zvol_tag);
}
}

/*
* Mask errors to continue dmu_objset_find() traversal
*/
static int
zvol_create_snap_minor_cb(const char *dsname, void *arg)
{
const char *name = (const char *)arg;
minors_job_t *j = arg;
list_t *minors_list = j->list;
const char *name = j->name;

ASSERT0(MUTEX_HELD(&spa_namespace_lock));

Expand All @@ -1502,7 +1530,19 @@ zvol_create_snap_minor_cb(const char *dsname, void *arg)
dprintf("zvol_create_snap_minor_cb(): "
"%s is not a shapshot name\n", dsname);
} else {
(void) zvol_create_minor_impl(dsname);
minors_job_t *job;
char *n = strdup(dsname);
if (n == NULL)
return (0);

job = kmem_alloc(sizeof (minors_job_t), KM_SLEEP);
job->name = n;
job->list = minors_list;
job->error = 0;
list_insert_tail(minors_list, job);
/* don't care if dispatch fails, because job->error is 0 */
taskq_dispatch(system_taskq, zvol_prefetch_minors_impl, job,
TQ_SLEEP);
}

return (0);
Expand All @@ -1516,6 +1556,7 @@ zvol_create_minors_cb(const char *dsname, void *arg)
{
uint64_t snapdev;
int error;
list_t *minors_list = arg;

ASSERT0(MUTEX_HELD(&spa_namespace_lock));

Expand All @@ -1531,19 +1572,28 @@ zvol_create_minors_cb(const char *dsname, void *arg)
* snapshots and create device minor nodes for those.
*/
if (strchr(dsname, '@') == 0) {
/* create minor for the 'dsname' explicitly */
error = zvol_create_minor_impl(dsname);
if ((error == 0 || error == EEXIST) &&
(snapdev == ZFS_SNAPDEV_VISIBLE)) {
fstrans_cookie_t cookie = spl_fstrans_mark();
minors_job_t *job;
char *n = strdup(dsname);
if (n == NULL)
return (0);

job = kmem_alloc(sizeof (minors_job_t), KM_SLEEP);
job->name = n;
job->list = minors_list;
job->error = 0;
list_insert_tail(minors_list, job);
/* don't care if dispatch fails, because job->error is 0 */
taskq_dispatch(system_taskq, zvol_prefetch_minors_impl, job,
TQ_SLEEP);

if (snapdev == ZFS_SNAPDEV_VISIBLE) {
/*
* traverse snapshots only, do not traverse children,
* and skip the 'dsname'
*/
error = dmu_objset_find((char *)dsname,
zvol_create_snap_minor_cb, (void *)dsname,
zvol_create_snap_minor_cb, (void *)job,
DS_FIND_SNAPSHOTS);
spl_fstrans_unmark(cookie);
}
} else {
dprintf("zvol_create_minors_cb(): %s is not a zvol name\n",
Expand Down Expand Up @@ -1576,10 +1626,24 @@ zvol_create_minors_impl(const char *name)
int error = 0;
fstrans_cookie_t cookie;
char *atp, *parent;
list_t minors_list;
minors_job_t *job;

if (zvol_inhibit_dev)
return (0);

/*
* This is the list for prefetch jobs. Whenever we found a match
* during dmu_objset_find, we insert a minors_job to the list and do
* taskq_dispatch to parallel prefetch zvol dnodes. Note we don't need
* any lock because all list operation is done on the current thread.
*
* We will use this list to do zvol_create_minor_impl after prefetch
* so we don't have to traverse using dmu_objset_find again.
*/
list_create(&minors_list, sizeof (minors_job_t),
offsetof(minors_job_t, link));

parent = kmem_alloc(MAXPATHLEN, KM_SLEEP);
(void) strlcpy(parent, name, MAXPATHLEN);

Expand All @@ -1595,11 +1659,26 @@ zvol_create_minors_impl(const char *name)
} else {
cookie = spl_fstrans_mark();
error = dmu_objset_find(parent, zvol_create_minors_cb,
NULL, DS_FIND_CHILDREN);
&minors_list, DS_FIND_CHILDREN);
spl_fstrans_unmark(cookie);
}

kmem_free(parent, MAXPATHLEN);
taskq_wait_outstanding(system_taskq, 0);

/*
* Prefetch is completed, we can do zvol_create_minor_impl
* sequentially.
*/
while ((job = list_head(&minors_list)) != NULL) {
list_remove(&minors_list, job);
if (!job->error)
zvol_create_minor_impl(job->name);
strfree(job->name);
kmem_free(job, sizeof (minors_job_t));
}

list_destroy(&minors_list);

return (SET_ERROR(error));
}
Expand Down

0 comments on commit 7ac557c

Please sign in to comment.