Changeset View
Changeset View
Standalone View
Standalone View
head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c
Show All 24 Lines | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Portions Copyright 2010 Robert Milkowski | * Portions Copyright 2010 Robert Milkowski | ||||
* | * | ||||
* Copyright 2017 Nexenta Systems, Inc. All rights reserved. | * Copyright 2017 Nexenta Systems, Inc. All rights reserved. | ||||
* Copyright (c) 2012, 2017 by Delphix. All rights reserved. | * Copyright (c) 2012, 2017 by Delphix. All rights reserved. | ||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved. | * Copyright (c) 2013, Joyent, Inc. All rights reserved. | ||||
* Copyright (c) 2014 Integros [integros.com] | * Copyright (c) 2014 Integros [integros.com] | ||||
* Copyright (c) 2016 Actifio, Inc. All rights reserved. | |||||
*/ | */ | ||||
/* Portions Copyright 2011 Martin Matuska <mm@FreeBSD.org> */ | /* Portions Copyright 2011 Martin Matuska <mm@FreeBSD.org> */ | ||||
/* | /* | ||||
* ZFS volume emulation driver. | * ZFS volume emulation driver. | ||||
* | * | ||||
* Makes a DMU object look like a volume of arbitrary size, up to 2^64 bytes. | * Makes a DMU object look like a volume of arbitrary size, up to 2^64 bytes. | ||||
▲ Show 20 Lines • Show All 139 Lines • ▼ Show 20 Lines | |||||
#ifndef illumos | #ifndef illumos | ||||
int zv_state; | int zv_state; | ||||
int zv_volmode; /* Provide GEOM or cdev */ | int zv_volmode; /* Provide GEOM or cdev */ | ||||
struct bio_queue_head zv_queue; | struct bio_queue_head zv_queue; | ||||
struct mtx zv_queue_mtx; /* zv_queue mutex */ | struct mtx zv_queue_mtx; /* zv_queue mutex */ | ||||
#endif | #endif | ||||
} zvol_state_t; | } zvol_state_t; | ||||
typedef enum { | |||||
ZVOL_ASYNC_CREATE_MINORS, | |||||
ZVOL_ASYNC_REMOVE_MINORS, | |||||
ZVOL_ASYNC_RENAME_MINORS, | |||||
ZVOL_ASYNC_MAX | |||||
} zvol_async_op_t; | |||||
typedef struct { | |||||
zvol_async_op_t op; | |||||
char pool[ZFS_MAX_DATASET_NAME_LEN]; | |||||
char name1[ZFS_MAX_DATASET_NAME_LEN]; | |||||
char name2[ZFS_MAX_DATASET_NAME_LEN]; | |||||
} zvol_task_t; | |||||
#ifndef illumos | #ifndef illumos | ||||
static LIST_HEAD(, zvol_state) all_zvols; | static LIST_HEAD(, zvol_state) all_zvols; | ||||
#endif | #endif | ||||
/* | /* | ||||
* zvol specific flags | * zvol specific flags | ||||
*/ | */ | ||||
#define ZVOL_RDONLY 0x1 | #define ZVOL_RDONLY 0x1 | ||||
#define ZVOL_DUMPIFIED 0x2 | #define ZVOL_DUMPIFIED 0x2 | ||||
▲ Show 20 Lines • Show All 406 Lines • ▼ Show 20 Lines | zvol_name2minor(const char *name, minor_t *minor) | ||||
mutex_exit(&zfsdev_state_lock); | mutex_exit(&zfsdev_state_lock); | ||||
return (zv ? 0 : -1); | return (zv ? 0 : -1); | ||||
} | } | ||||
#endif /* illumos */ | #endif /* illumos */ | ||||
/* | /* | ||||
* Create a minor node (plus a whole lot more) for the specified volume. | * Create a minor node (plus a whole lot more) for the specified volume. | ||||
*/ | */ | ||||
int | static int | ||||
zvol_create_minor(const char *name) | zvol_create_minor(const char *name) | ||||
{ | { | ||||
zfs_soft_state_t *zs; | zfs_soft_state_t *zs; | ||||
zvol_state_t *zv; | zvol_state_t *zv; | ||||
objset_t *os; | objset_t *os; | ||||
#ifdef illumos | #ifdef illumos | ||||
dmu_object_info_t doi; | dmu_object_info_t doi; | ||||
minor_t minor = 0; | minor_t minor = 0; | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | #else /* !illumos */ | ||||
zv = kmem_zalloc(sizeof(*zv), KM_SLEEP); | zv = kmem_zalloc(sizeof(*zv), KM_SLEEP); | ||||
zv->zv_state = 0; | zv->zv_state = 0; | ||||
error = dsl_prop_get_integer(name, | error = dsl_prop_get_integer(name, | ||||
zfs_prop_to_name(ZFS_PROP_VOLMODE), &mode, NULL); | zfs_prop_to_name(ZFS_PROP_VOLMODE), &mode, NULL); | ||||
if (error != 0 || mode == ZFS_VOLMODE_DEFAULT) | if (error != 0 || mode == ZFS_VOLMODE_DEFAULT) | ||||
mode = volmode; | mode = volmode; | ||||
DROP_GIANT(); | |||||
zv->zv_volmode = mode; | zv->zv_volmode = mode; | ||||
if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { | if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { | ||||
g_topology_lock(); | g_topology_lock(); | ||||
gp = g_new_geomf(&zfs_zvol_class, "zfs::zvol::%s", name); | gp = g_new_geomf(&zfs_zvol_class, "zfs::zvol::%s", name); | ||||
gp->start = zvol_geom_start; | gp->start = zvol_geom_start; | ||||
gp->access = zvol_geom_access; | gp->access = zvol_geom_access; | ||||
pp = g_new_providerf(gp, "%s/%s", ZVOL_DRIVER, name); | pp = g_new_providerf(gp, "%s/%s", ZVOL_DRIVER, name); | ||||
pp->flags |= G_PF_DIRECT_RECEIVE | G_PF_DIRECT_SEND; | pp->flags |= G_PF_DIRECT_RECEIVE | G_PF_DIRECT_SEND; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | #endif | ||||
zvol_minors++; | zvol_minors++; | ||||
mutex_exit(&zfsdev_state_lock); | mutex_exit(&zfsdev_state_lock); | ||||
#ifndef illumos | #ifndef illumos | ||||
if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { | if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { | ||||
zvol_geom_run(zv); | zvol_geom_run(zv); | ||||
g_topology_unlock(); | g_topology_unlock(); | ||||
} | } | ||||
PICKUP_GIANT(); | |||||
ZFS_LOG(1, "ZVOL %s created.", name); | ZFS_LOG(1, "ZVOL %s created.", name); | ||||
#endif | #endif | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
Show All 37 Lines | |||||
#ifdef illumos | #ifdef illumos | ||||
ddi_soft_state_free(zfsdev_state, minor); | ddi_soft_state_free(zfsdev_state, minor); | ||||
#endif | #endif | ||||
zvol_minors--; | zvol_minors--; | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
zvol_remove_minor(const char *name) | |||||
{ | |||||
zvol_state_t *zv; | |||||
int rc; | |||||
mutex_enter(&zfsdev_state_lock); | |||||
if ((zv = zvol_minor_lookup(name)) == NULL) { | |||||
mutex_exit(&zfsdev_state_lock); | |||||
return (SET_ERROR(ENXIO)); | |||||
} | |||||
rc = zvol_remove_zv(zv); | |||||
mutex_exit(&zfsdev_state_lock); | |||||
return (rc); | |||||
} | |||||
int | |||||
zvol_first_open(zvol_state_t *zv) | zvol_first_open(zvol_state_t *zv) | ||||
{ | { | ||||
dmu_object_info_t doi; | dmu_object_info_t doi; | ||||
objset_t *os; | objset_t *os; | ||||
uint64_t volsize; | uint64_t volsize; | ||||
int error; | int error; | ||||
uint64_t readonly; | uint64_t readonly; | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | zvol_update_volsize(objset_t *os, uint64_t volsize) | ||||
if (error == 0) | if (error == 0) | ||||
error = dmu_free_long_range(os, | error = dmu_free_long_range(os, | ||||
ZVOL_OBJ, volsize, DMU_OBJECT_END); | ZVOL_OBJ, volsize, DMU_OBJECT_END); | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
zvol_remove_minors(const char *name) | zvol_remove_minors_impl(const char *name) | ||||
{ | { | ||||
#ifdef illumos | #ifdef illumos | ||||
zvol_state_t *zv; | zvol_state_t *zv; | ||||
char *namebuf; | char *namebuf; | ||||
minor_t minor; | minor_t minor; | ||||
namebuf = kmem_zalloc(strlen(name) + 2, KM_SLEEP); | namebuf = kmem_zalloc(strlen(name) + 2, KM_SLEEP); | ||||
(void) strncpy(namebuf, name, strlen(name)); | (void) strncpy(namebuf, name, strlen(name)); | ||||
Show All 11 Lines | #ifdef illumos | ||||
mutex_exit(&zfsdev_state_lock); | mutex_exit(&zfsdev_state_lock); | ||||
#else /* !illumos */ | #else /* !illumos */ | ||||
zvol_state_t *zv, *tzv; | zvol_state_t *zv, *tzv; | ||||
size_t namelen; | size_t namelen; | ||||
namelen = strlen(name); | namelen = strlen(name); | ||||
DROP_GIANT(); | |||||
mutex_enter(&zfsdev_state_lock); | mutex_enter(&zfsdev_state_lock); | ||||
LIST_FOREACH_SAFE(zv, &all_zvols, zv_links, tzv) { | LIST_FOREACH_SAFE(zv, &all_zvols, zv_links, tzv) { | ||||
if (strcmp(zv->zv_name, name) == 0 || | if (strcmp(zv->zv_name, name) == 0 || | ||||
(strncmp(zv->zv_name, name, namelen) == 0 && | (strncmp(zv->zv_name, name, namelen) == 0 && | ||||
strlen(zv->zv_name) > namelen && (zv->zv_name[namelen] == '/' || | strlen(zv->zv_name) > namelen && (zv->zv_name[namelen] == '/' || | ||||
zv->zv_name[namelen] == '@'))) { | zv->zv_name[namelen] == '@'))) { | ||||
(void) zvol_remove_zv(zv); | (void) zvol_remove_zv(zv); | ||||
} | } | ||||
} | } | ||||
mutex_exit(&zfsdev_state_lock); | mutex_exit(&zfsdev_state_lock); | ||||
PICKUP_GIANT(); | |||||
#endif /* illumos */ | #endif /* illumos */ | ||||
} | } | ||||
static int | static int | ||||
zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize) | zvol_update_live_volsize(zvol_state_t *zv, uint64_t volsize) | ||||
{ | { | ||||
uint64_t old_volsize = 0ULL; | uint64_t old_volsize = 0ULL; | ||||
int error = 0; | int error = 0; | ||||
▲ Show 20 Lines • Show All 1,886 Lines • ▼ Show 20 Lines | for (;;) { | ||||
} | } | ||||
} | } | ||||
kmem_free(sname, MAXPATHLEN); | kmem_free(sname, MAXPATHLEN); | ||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
zvol_create_minors(const char *name) | zvol_create_minors_impl(const char *name) | ||||
{ | { | ||||
uint64_t cookie; | uint64_t cookie; | ||||
objset_t *os; | objset_t *os; | ||||
char *osname, *p; | char *osname, *p; | ||||
int error, len; | int error, len; | ||||
if (dataset_name_hidden(name)) | if (dataset_name_hidden(name)) | ||||
return (0); | return (0); | ||||
Show All 39 Lines | if (!dataset_name_hidden(osname)) | ||||
(void) dmu_objset_prefetch(osname, NULL); | (void) dmu_objset_prefetch(osname, NULL); | ||||
} | } | ||||
#endif | #endif | ||||
cookie = 0; | cookie = 0; | ||||
while (dmu_dir_list_next(os, MAXPATHLEN - (p - osname), p, NULL, | while (dmu_dir_list_next(os, MAXPATHLEN - (p - osname), p, NULL, | ||||
&cookie) == 0) { | &cookie) == 0) { | ||||
dmu_objset_rele(os, FTAG); | dmu_objset_rele(os, FTAG); | ||||
(void)zvol_create_minors(osname); | (void)zvol_create_minors_impl(osname); | ||||
if ((error = dmu_objset_hold(name, FTAG, &os)) != 0) { | if ((error = dmu_objset_hold(name, FTAG, &os)) != 0) { | ||||
printf("ZFS WARNING: Unable to put hold on %s (error=%d).\n", | printf("ZFS WARNING: Unable to put hold on %s (error=%d).\n", | ||||
name, error); | name, error); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
dmu_objset_rele(os, FTAG); | dmu_objset_rele(os, FTAG); | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | if (zv->zv_volmode == ZFS_VOLMODE_GEOM) { | ||||
if (make_dev_s(&args, &zv->zv_dev, | if (make_dev_s(&args, &zv->zv_dev, | ||||
"%s/%s", ZVOL_DRIVER, newname) == 0) | "%s/%s", ZVOL_DRIVER, newname) == 0) | ||||
zv->zv_dev->si_iosize_max = MAXPHYS; | zv->zv_dev->si_iosize_max = MAXPHYS; | ||||
} | } | ||||
strlcpy(zv->zv_name, newname, sizeof(zv->zv_name)); | strlcpy(zv->zv_name, newname, sizeof(zv->zv_name)); | ||||
} | } | ||||
void | void | ||||
zvol_rename_minors(const char *oldname, const char *newname) | zvol_rename_minors_impl(const char *oldname, const char *newname) | ||||
{ | { | ||||
char name[MAXPATHLEN]; | char name[MAXPATHLEN]; | ||||
struct g_provider *pp; | struct g_provider *pp; | ||||
struct g_geom *gp; | struct g_geom *gp; | ||||
size_t oldnamelen, newnamelen; | size_t oldnamelen, newnamelen; | ||||
zvol_state_t *zv; | zvol_state_t *zv; | ||||
char *namebuf; | char *namebuf; | ||||
boolean_t locked = B_FALSE; | boolean_t locked = B_FALSE; | ||||
oldnamelen = strlen(oldname); | oldnamelen = strlen(oldname); | ||||
newnamelen = strlen(newname); | newnamelen = strlen(newname); | ||||
DROP_GIANT(); | |||||
/* See comment in zvol_open(). */ | /* See comment in zvol_open(). */ | ||||
if (!MUTEX_HELD(&zfsdev_state_lock)) { | if (!MUTEX_HELD(&zfsdev_state_lock)) { | ||||
mutex_enter(&zfsdev_state_lock); | mutex_enter(&zfsdev_state_lock); | ||||
locked = B_TRUE; | locked = B_TRUE; | ||||
} | } | ||||
LIST_FOREACH(zv, &all_zvols, zv_links) { | LIST_FOREACH(zv, &all_zvols, zv_links) { | ||||
if (strcmp(zv->zv_name, oldname) == 0) { | if (strcmp(zv->zv_name, oldname) == 0) { | ||||
zvol_rename_minor(zv, newname); | zvol_rename_minor(zv, newname); | ||||
} else if (strncmp(zv->zv_name, oldname, oldnamelen) == 0 && | } else if (strncmp(zv->zv_name, oldname, oldnamelen) == 0 && | ||||
(zv->zv_name[oldnamelen] == '/' || | (zv->zv_name[oldnamelen] == '/' || | ||||
zv->zv_name[oldnamelen] == '@')) { | zv->zv_name[oldnamelen] == '@')) { | ||||
snprintf(name, sizeof(name), "%s%c%s", newname, | snprintf(name, sizeof(name), "%s%c%s", newname, | ||||
zv->zv_name[oldnamelen], | zv->zv_name[oldnamelen], | ||||
zv->zv_name + oldnamelen + 1); | zv->zv_name + oldnamelen + 1); | ||||
zvol_rename_minor(zv, name); | zvol_rename_minor(zv, name); | ||||
} | } | ||||
} | } | ||||
if (locked) | if (locked) | ||||
mutex_exit(&zfsdev_state_lock); | mutex_exit(&zfsdev_state_lock); | ||||
PICKUP_GIANT(); | } | ||||
static zvol_task_t * | |||||
zvol_task_alloc(zvol_async_op_t op, const char *name1, const char *name2) | |||||
{ | |||||
zvol_task_t *task; | |||||
char *delim; | |||||
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP); | |||||
task->op = op; | |||||
delim = strchr(name1, '/'); | |||||
strlcpy(task->pool, name1, delim ? (delim - name1 + 1) : MAXNAMELEN); | |||||
strlcpy(task->name1, name1, MAXNAMELEN); | |||||
if (name2 != NULL) | |||||
strlcpy(task->name2, name2, MAXNAMELEN); | |||||
return (task); | |||||
} | |||||
static void | |||||
zvol_task_free(zvol_task_t *task) | |||||
{ | |||||
kmem_free(task, sizeof (zvol_task_t)); | |||||
} | |||||
/* | |||||
* The worker thread function performed asynchronously. | |||||
*/ | |||||
static void | |||||
zvol_task_cb(void *param) | |||||
{ | |||||
zvol_task_t *task = (zvol_task_t *)param; | |||||
switch (task->op) { | |||||
case ZVOL_ASYNC_CREATE_MINORS: | |||||
(void) zvol_create_minors_impl(task->name1); | |||||
break; | |||||
case ZVOL_ASYNC_REMOVE_MINORS: | |||||
zvol_remove_minors_impl(task->name1); | |||||
break; | |||||
case ZVOL_ASYNC_RENAME_MINORS: | |||||
zvol_rename_minors_impl(task->name1, task->name2); | |||||
break; | |||||
default: | |||||
VERIFY(0); | |||||
break; | |||||
} | |||||
zvol_task_free(task); | |||||
} | |||||
static void | |||||
zvol_minors_helper(spa_t *spa, zvol_async_op_t op, const char *name1, | |||||
const char *name2) | |||||
{ | |||||
zvol_task_t *task; | |||||
if (dataset_name_hidden(name1)) | |||||
return; | |||||
if (name2 != NULL && dataset_name_hidden(name2)) | |||||
return; | |||||
task = zvol_task_alloc(op, name1, name2); | |||||
(void)taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP); | |||||
} | |||||
void | |||||
zvol_create_minors(spa_t *spa, const char *name) | |||||
{ | |||||
zvol_minors_helper(spa, ZVOL_ASYNC_CREATE_MINORS, name, NULL); | |||||
} | |||||
void | |||||
zvol_remove_minors(spa_t *spa, const char *name) | |||||
{ | |||||
zvol_minors_helper(spa, ZVOL_ASYNC_REMOVE_MINORS, name, NULL); | |||||
} | |||||
void | |||||
zvol_rename_minors(spa_t *spa, const char *oldname, const char *newname) | |||||
{ | |||||
zvol_minors_helper(spa, ZVOL_ASYNC_RENAME_MINORS, oldname, newname); | |||||
} | } | ||||
static int | static int | ||||
zvol_d_open(struct cdev *dev, int flags, int fmt, struct thread *td) | zvol_d_open(struct cdev *dev, int flags, int fmt, struct thread *td) | ||||
{ | { | ||||
zvol_state_t *zv = dev->si_drv2; | zvol_state_t *zv = dev->si_drv2; | ||||
int err = 0; | int err = 0; | ||||
▲ Show 20 Lines • Show All 181 Lines • Show Last 20 Lines |