xref: /illumos-gate/usr/src/uts/common/fs/zfs/zvol.c (revision 5c5460e9)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5fa9e4066Sahrens  * Common Development and Distribution License, Version 1.0 only
6fa9e4066Sahrens  * (the "License").  You may not use this file except in compliance
7fa9e4066Sahrens  * with the License.
8fa9e4066Sahrens  *
9fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
11fa9e4066Sahrens  * See the License for the specific language governing permissions
12fa9e4066Sahrens  * and limitations under the License.
13fa9e4066Sahrens  *
14fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19fa9e4066Sahrens  *
20fa9e4066Sahrens  * CDDL HEADER END
21fa9e4066Sahrens  */
22fa9e4066Sahrens /*
23fa9e4066Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24fa9e4066Sahrens  * Use is subject to license terms.
25fa9e4066Sahrens  */
26fa9e4066Sahrens 
27fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28fa9e4066Sahrens 
29fa9e4066Sahrens /*
30fa9e4066Sahrens  * ZFS volume emulation driver.
31fa9e4066Sahrens  *
32fa9e4066Sahrens  * Makes a DMU object look like a volume of arbitrary size, up to 2^64 bytes.
33fa9e4066Sahrens  * Volumes are accessed through the symbolic links named:
34fa9e4066Sahrens  *
35fa9e4066Sahrens  * /dev/zvol/dsk/<pool_name>/<dataset_name>
36fa9e4066Sahrens  * /dev/zvol/rdsk/<pool_name>/<dataset_name>
37fa9e4066Sahrens  *
38fa9e4066Sahrens  * These links are created by the ZFS-specific devfsadm link generator.
39fa9e4066Sahrens  * Volumes are persistent through reboot.  No user command needs to be
40fa9e4066Sahrens  * run before opening and using a device.
41fa9e4066Sahrens  */
42fa9e4066Sahrens 
43fa9e4066Sahrens #include <sys/types.h>
44fa9e4066Sahrens #include <sys/param.h>
45fa9e4066Sahrens #include <sys/errno.h>
46fa9e4066Sahrens #include <sys/aio_req.h>
47fa9e4066Sahrens #include <sys/uio.h>
48fa9e4066Sahrens #include <sys/buf.h>
49fa9e4066Sahrens #include <sys/modctl.h>
50fa9e4066Sahrens #include <sys/open.h>
51fa9e4066Sahrens #include <sys/kmem.h>
52fa9e4066Sahrens #include <sys/conf.h>
53fa9e4066Sahrens #include <sys/cmn_err.h>
54fa9e4066Sahrens #include <sys/stat.h>
55fa9e4066Sahrens #include <sys/zap.h>
56fa9e4066Sahrens #include <sys/spa.h>
57fa9e4066Sahrens #include <sys/zio.h>
58fa9e4066Sahrens #include <sys/dsl_prop.h>
59fa9e4066Sahrens #include <sys/dkio.h>
60fa9e4066Sahrens #include <sys/efi_partition.h>
61fa9e4066Sahrens #include <sys/byteorder.h>
62fa9e4066Sahrens #include <sys/pathname.h>
63fa9e4066Sahrens #include <sys/ddi.h>
64fa9e4066Sahrens #include <sys/sunddi.h>
65fa9e4066Sahrens #include <sys/crc32.h>
66fa9e4066Sahrens #include <sys/dirent.h>
67fa9e4066Sahrens #include <sys/policy.h>
68fa9e4066Sahrens #include <sys/fs/zfs.h>
69fa9e4066Sahrens #include <sys/zfs_ioctl.h>
70fa9e4066Sahrens #include <sys/mkdev.h>
71fa9e4066Sahrens 
72fa9e4066Sahrens #include "zfs_namecheck.h"
73fa9e4066Sahrens 
74fa9e4066Sahrens #define	ZVOL_OBJ		1ULL
75fa9e4066Sahrens #define	ZVOL_ZAP_OBJ		2ULL
76fa9e4066Sahrens 
77fa9e4066Sahrens static void *zvol_state;
78fa9e4066Sahrens 
79fa9e4066Sahrens /*
80fa9e4066Sahrens  * This lock protects the zvol_state structure from being modified
81fa9e4066Sahrens  * while it's being used, e.g. an open that comes in before a create
82fa9e4066Sahrens  * finishes.  It also protects temporary opens of the dataset so that,
83fa9e4066Sahrens  * e.g., an open doesn't get a spurious EBUSY.
84fa9e4066Sahrens  */
85fa9e4066Sahrens static kmutex_t zvol_state_lock;
86fa9e4066Sahrens static uint32_t zvol_minors;
87fa9e4066Sahrens 
88fa9e4066Sahrens /*
89fa9e4066Sahrens  * The in-core state of each volume.
90fa9e4066Sahrens  */
91fa9e4066Sahrens typedef struct zvol_state {
92fa9e4066Sahrens 	char		zv_name[MAXPATHLEN]; /* pool/dd name */
93fa9e4066Sahrens 	uint64_t	zv_volsize;	/* amount of space we advertise */
94fa9e4066Sahrens 	minor_t		zv_minor;	/* minor number */
95fa9e4066Sahrens 	uint8_t		zv_min_bs;	/* minimum addressable block shift */
96fa9e4066Sahrens 	uint8_t		zv_readonly;	/* hard readonly; like write-protect */
97fa9e4066Sahrens 	objset_t	*zv_objset;	/* objset handle */
98fa9e4066Sahrens 	uint32_t	zv_mode;	/* DS_MODE_* flags at open time */
99fa9e4066Sahrens 	uint32_t	zv_open_count[OTYPCNT];	/* open counts */
100fa9e4066Sahrens 	uint32_t	zv_total_opens;	/* total open count */
101fa9e4066Sahrens } zvol_state_t;
102fa9e4066Sahrens 
103fa9e4066Sahrens static void
104fa9e4066Sahrens zvol_size_changed(zvol_state_t *zv, dev_t dev)
105fa9e4066Sahrens {
106fa9e4066Sahrens 	dev = makedevice(getmajor(dev), zv->zv_minor);
107fa9e4066Sahrens 
108fa9e4066Sahrens 	VERIFY(ddi_prop_update_int64(dev, zfs_dip,
109fa9e4066Sahrens 	    "Size", zv->zv_volsize) == DDI_SUCCESS);
110fa9e4066Sahrens 	VERIFY(ddi_prop_update_int64(dev, zfs_dip,
111fa9e4066Sahrens 	    "Nblocks", lbtodb(zv->zv_volsize)) == DDI_SUCCESS);
112fa9e4066Sahrens }
113fa9e4066Sahrens 
114fa9e4066Sahrens int
115*5c5460e9Seschrock zvol_check_volsize(zfs_cmd_t *zc, uint64_t blocksize)
116fa9e4066Sahrens {
117fa9e4066Sahrens 	if (zc->zc_volsize == 0)
118fa9e4066Sahrens 		return (EINVAL);
119fa9e4066Sahrens 
120*5c5460e9Seschrock 	if (zc->zc_volsize % blocksize != 0)
121*5c5460e9Seschrock 		return (EINVAL);
122*5c5460e9Seschrock 
123fa9e4066Sahrens #ifdef _ILP32
124fa9e4066Sahrens 	if (zc->zc_volsize - 1 > SPEC_MAXOFFSET_T)
125fa9e4066Sahrens 		return (EOVERFLOW);
126fa9e4066Sahrens #endif
127fa9e4066Sahrens 	return (0);
128fa9e4066Sahrens }
129fa9e4066Sahrens 
130fa9e4066Sahrens int
131fa9e4066Sahrens zvol_check_volblocksize(zfs_cmd_t *zc)
132fa9e4066Sahrens {
133fa9e4066Sahrens 	if (zc->zc_volblocksize < SPA_MINBLOCKSIZE ||
134fa9e4066Sahrens 	    zc->zc_volblocksize > SPA_MAXBLOCKSIZE ||
135fa9e4066Sahrens 	    !ISP2(zc->zc_volblocksize))
136fa9e4066Sahrens 		return (EDOM);
137fa9e4066Sahrens 
138fa9e4066Sahrens 	return (0);
139fa9e4066Sahrens }
140fa9e4066Sahrens 
141fa9e4066Sahrens static void
142fa9e4066Sahrens zvol_readonly_changed_cb(void *arg, uint64_t newval)
143fa9e4066Sahrens {
144fa9e4066Sahrens 	zvol_state_t *zv = arg;
145fa9e4066Sahrens 
146fa9e4066Sahrens 	zv->zv_readonly = (uint8_t)newval;
147fa9e4066Sahrens }
148fa9e4066Sahrens 
149fa9e4066Sahrens int
150fa9e4066Sahrens zvol_get_stats(zfs_cmd_t *zc, objset_t *os)
151fa9e4066Sahrens {
152fa9e4066Sahrens 	int error;
153fa9e4066Sahrens 	dmu_object_info_t doi;
154fa9e4066Sahrens 
155fa9e4066Sahrens 	error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zc->zc_volsize);
156fa9e4066Sahrens 
157fa9e4066Sahrens 	if (error)
158fa9e4066Sahrens 		return (error);
159fa9e4066Sahrens 
160fa9e4066Sahrens 	error = dmu_object_info(os, ZVOL_OBJ, &doi);
161fa9e4066Sahrens 
162fa9e4066Sahrens 	if (error == 0)
163fa9e4066Sahrens 		zc->zc_volblocksize = doi.doi_data_block_size;
164fa9e4066Sahrens 
165fa9e4066Sahrens 	return (error);
166fa9e4066Sahrens }
167fa9e4066Sahrens 
168fa9e4066Sahrens /*
169fa9e4066Sahrens  * Find a free minor number.
170fa9e4066Sahrens  */
171fa9e4066Sahrens static minor_t
172fa9e4066Sahrens zvol_minor_alloc(void)
173fa9e4066Sahrens {
174fa9e4066Sahrens 	minor_t minor;
175fa9e4066Sahrens 
176fa9e4066Sahrens 	ASSERT(MUTEX_HELD(&zvol_state_lock));
177fa9e4066Sahrens 
178fa9e4066Sahrens 	for (minor = 1; minor <= ZVOL_MAX_MINOR; minor++)
179fa9e4066Sahrens 		if (ddi_get_soft_state(zvol_state, minor) == NULL)
180fa9e4066Sahrens 			return (minor);
181fa9e4066Sahrens 
182fa9e4066Sahrens 	return (0);
183fa9e4066Sahrens }
184fa9e4066Sahrens 
185fa9e4066Sahrens static zvol_state_t *
186fa9e4066Sahrens zvol_minor_lookup(char *name)
187fa9e4066Sahrens {
188fa9e4066Sahrens 	minor_t minor;
189fa9e4066Sahrens 	zvol_state_t *zv;
190fa9e4066Sahrens 
191fa9e4066Sahrens 	ASSERT(MUTEX_HELD(&zvol_state_lock));
192fa9e4066Sahrens 
193fa9e4066Sahrens 	for (minor = 1; minor <= ZVOL_MAX_MINOR; minor++) {
194fa9e4066Sahrens 		zv = ddi_get_soft_state(zvol_state, minor);
195fa9e4066Sahrens 		if (zv == NULL)
196fa9e4066Sahrens 			continue;
197fa9e4066Sahrens 		if (strcmp(zv->zv_name, name) == 0)
198fa9e4066Sahrens 			break;
199fa9e4066Sahrens 	}
200fa9e4066Sahrens 
201fa9e4066Sahrens 	return (zv);
202fa9e4066Sahrens }
203fa9e4066Sahrens 
204fa9e4066Sahrens void
205fa9e4066Sahrens zvol_create_cb(objset_t *os, void *arg, dmu_tx_t *tx)
206fa9e4066Sahrens {
207fa9e4066Sahrens 	zfs_cmd_t *zc = arg;
208fa9e4066Sahrens 	int error;
209fa9e4066Sahrens 
210fa9e4066Sahrens 	error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, zc->zc_volblocksize,
211fa9e4066Sahrens 	    DMU_OT_NONE, 0, tx);
212fa9e4066Sahrens 	ASSERT(error == 0);
213fa9e4066Sahrens 
214fa9e4066Sahrens 	error = zap_create_claim(os, ZVOL_ZAP_OBJ, DMU_OT_ZVOL_PROP,
215fa9e4066Sahrens 	    DMU_OT_NONE, 0, tx);
216fa9e4066Sahrens 	ASSERT(error == 0);
217fa9e4066Sahrens 
218fa9e4066Sahrens 	error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &zc->zc_volsize, tx);
219fa9e4066Sahrens 	ASSERT(error == 0);
220fa9e4066Sahrens }
221fa9e4066Sahrens 
222fa9e4066Sahrens /*
223fa9e4066Sahrens  * Create a minor node for the specified volume.
224fa9e4066Sahrens  */
225fa9e4066Sahrens int
226fa9e4066Sahrens zvol_create_minor(zfs_cmd_t *zc)
227fa9e4066Sahrens {
228fa9e4066Sahrens 	char *name = zc->zc_name;
229fa9e4066Sahrens 	dev_t dev = zc->zc_dev;
230fa9e4066Sahrens 	zvol_state_t *zv;
231fa9e4066Sahrens 	objset_t *os;
232fa9e4066Sahrens 	uint64_t volsize;
233fa9e4066Sahrens 	minor_t minor = 0;
234fa9e4066Sahrens 	struct pathname linkpath;
235fa9e4066Sahrens 	int ds_mode = DS_MODE_PRIMARY;
236fa9e4066Sahrens 	vnode_t *vp = NULL;
237fa9e4066Sahrens 	char *devpath;
238fa9e4066Sahrens 	size_t devpathlen = strlen(ZVOL_FULL_DEV_DIR) + 1 + strlen(name) + 1;
239fa9e4066Sahrens 	char chrbuf[30], blkbuf[30];
240fa9e4066Sahrens 	int error;
241fa9e4066Sahrens 
242fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
243fa9e4066Sahrens 
244fa9e4066Sahrens 	if ((zv = zvol_minor_lookup(name)) != NULL) {
245fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
246fa9e4066Sahrens 		return (EEXIST);
247fa9e4066Sahrens 	}
248fa9e4066Sahrens 
249fa9e4066Sahrens 	if (strchr(name, '@') != 0)
250fa9e4066Sahrens 		ds_mode |= DS_MODE_READONLY;
251fa9e4066Sahrens 
252fa9e4066Sahrens 	error = dmu_objset_open(name, DMU_OST_ZVOL, ds_mode, &os);
253fa9e4066Sahrens 
254fa9e4066Sahrens 	if (error) {
255fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
256fa9e4066Sahrens 		return (error);
257fa9e4066Sahrens 	}
258fa9e4066Sahrens 
259fa9e4066Sahrens 	error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize);
260fa9e4066Sahrens 
261fa9e4066Sahrens 	if (error) {
262fa9e4066Sahrens 		dmu_objset_close(os);
263fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
264fa9e4066Sahrens 		return (error);
265fa9e4066Sahrens 	}
266fa9e4066Sahrens 
267fa9e4066Sahrens 	/*
268fa9e4066Sahrens 	 * If there's an existing /dev/zvol symlink, try to use the
269fa9e4066Sahrens 	 * same minor number we used last time.
270fa9e4066Sahrens 	 */
271fa9e4066Sahrens 	devpath = kmem_alloc(devpathlen, KM_SLEEP);
272fa9e4066Sahrens 
273fa9e4066Sahrens 	(void) sprintf(devpath, "%s/%s", ZVOL_FULL_DEV_DIR, name);
274fa9e4066Sahrens 
275fa9e4066Sahrens 	error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULL, &vp);
276fa9e4066Sahrens 
277fa9e4066Sahrens 	kmem_free(devpath, devpathlen);
278fa9e4066Sahrens 
279fa9e4066Sahrens 	if (error == 0 && vp->v_type != VLNK)
280fa9e4066Sahrens 		error = EINVAL;
281fa9e4066Sahrens 
282fa9e4066Sahrens 	if (error == 0) {
283fa9e4066Sahrens 		pn_alloc(&linkpath);
284fa9e4066Sahrens 		error = pn_getsymlink(vp, &linkpath, kcred);
285fa9e4066Sahrens 		if (error == 0) {
286fa9e4066Sahrens 			char *ms = strstr(linkpath.pn_path, ZVOL_PSEUDO_DEV);
287fa9e4066Sahrens 			if (ms != NULL) {
288fa9e4066Sahrens 				ms += strlen(ZVOL_PSEUDO_DEV);
289fa9e4066Sahrens 				minor = stoi(&ms);
290fa9e4066Sahrens 			}
291fa9e4066Sahrens 		}
292fa9e4066Sahrens 		pn_free(&linkpath);
293fa9e4066Sahrens 	}
294fa9e4066Sahrens 
295fa9e4066Sahrens 	if (vp != NULL)
296fa9e4066Sahrens 		VN_RELE(vp);
297fa9e4066Sahrens 
298fa9e4066Sahrens 	/*
299fa9e4066Sahrens 	 * If we found a minor but it's already in use, we must pick a new one.
300fa9e4066Sahrens 	 */
301fa9e4066Sahrens 	if (minor != 0 && ddi_get_soft_state(zvol_state, minor) != NULL)
302fa9e4066Sahrens 		minor = 0;
303fa9e4066Sahrens 
304fa9e4066Sahrens 	if (minor == 0)
305fa9e4066Sahrens 		minor = zvol_minor_alloc();
306fa9e4066Sahrens 
307fa9e4066Sahrens 	if (minor == 0) {
308fa9e4066Sahrens 		dmu_objset_close(os);
309fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
310fa9e4066Sahrens 		return (ENXIO);
311fa9e4066Sahrens 	}
312fa9e4066Sahrens 
313fa9e4066Sahrens 	if (ddi_soft_state_zalloc(zvol_state, minor) != DDI_SUCCESS) {
314fa9e4066Sahrens 		dmu_objset_close(os);
315fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
316fa9e4066Sahrens 		return (EAGAIN);
317fa9e4066Sahrens 	}
318fa9e4066Sahrens 
319fa9e4066Sahrens 	(void) ddi_prop_update_string(minor, zfs_dip, ZVOL_PROP_NAME, name);
320fa9e4066Sahrens 
321fa9e4066Sahrens 	(void) sprintf(chrbuf, "%uc,raw", minor);
322fa9e4066Sahrens 
323fa9e4066Sahrens 	if (ddi_create_minor_node(zfs_dip, chrbuf, S_IFCHR,
324fa9e4066Sahrens 	    minor, DDI_PSEUDO, 0) == DDI_FAILURE) {
325fa9e4066Sahrens 		ddi_soft_state_free(zvol_state, minor);
326fa9e4066Sahrens 		dmu_objset_close(os);
327fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
328fa9e4066Sahrens 		return (EAGAIN);
329fa9e4066Sahrens 	}
330fa9e4066Sahrens 
331fa9e4066Sahrens 	(void) sprintf(blkbuf, "%uc", minor);
332fa9e4066Sahrens 
333fa9e4066Sahrens 	if (ddi_create_minor_node(zfs_dip, blkbuf, S_IFBLK,
334fa9e4066Sahrens 	    minor, DDI_PSEUDO, 0) == DDI_FAILURE) {
335fa9e4066Sahrens 		ddi_remove_minor_node(zfs_dip, chrbuf);
336fa9e4066Sahrens 		ddi_soft_state_free(zvol_state, minor);
337fa9e4066Sahrens 		dmu_objset_close(os);
338fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
339fa9e4066Sahrens 		return (EAGAIN);
340fa9e4066Sahrens 	}
341fa9e4066Sahrens 
342fa9e4066Sahrens 	zv = ddi_get_soft_state(zvol_state, minor);
343fa9e4066Sahrens 
344fa9e4066Sahrens 	(void) strcpy(zv->zv_name, name);
345fa9e4066Sahrens 	zv->zv_min_bs = DEV_BSHIFT;
346fa9e4066Sahrens 	zv->zv_minor = minor;
347fa9e4066Sahrens 	zv->zv_volsize = volsize;
348fa9e4066Sahrens 	zv->zv_objset = os;
349fa9e4066Sahrens 	zv->zv_mode = ds_mode;
350fa9e4066Sahrens 
351fa9e4066Sahrens 	zvol_size_changed(zv, dev);
352fa9e4066Sahrens 
353fa9e4066Sahrens 	VERIFY(dsl_prop_register(dmu_objset_ds(zv->zv_objset),
354fa9e4066Sahrens 	    "readonly", zvol_readonly_changed_cb, zv) == 0);
355fa9e4066Sahrens 
356fa9e4066Sahrens 	zvol_minors++;
357fa9e4066Sahrens 
358fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
359fa9e4066Sahrens 
360fa9e4066Sahrens 	return (0);
361fa9e4066Sahrens }
362fa9e4066Sahrens 
363fa9e4066Sahrens /*
364fa9e4066Sahrens  * Remove minor node for the specified volume.
365fa9e4066Sahrens  */
366fa9e4066Sahrens int
367fa9e4066Sahrens zvol_remove_minor(zfs_cmd_t *zc)
368fa9e4066Sahrens {
369fa9e4066Sahrens 	zvol_state_t *zv;
370fa9e4066Sahrens 	char namebuf[30];
371fa9e4066Sahrens 
372fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
373fa9e4066Sahrens 
374fa9e4066Sahrens 	if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) {
375fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
376fa9e4066Sahrens 		return (ENXIO);
377fa9e4066Sahrens 	}
378fa9e4066Sahrens 
379fa9e4066Sahrens 	if (zv->zv_total_opens != 0) {
380fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
381fa9e4066Sahrens 		return (EBUSY);
382fa9e4066Sahrens 	}
383fa9e4066Sahrens 
384fa9e4066Sahrens 	(void) sprintf(namebuf, "%uc,raw", zv->zv_minor);
385fa9e4066Sahrens 	ddi_remove_minor_node(zfs_dip, namebuf);
386fa9e4066Sahrens 
387fa9e4066Sahrens 	(void) sprintf(namebuf, "%uc", zv->zv_minor);
388fa9e4066Sahrens 	ddi_remove_minor_node(zfs_dip, namebuf);
389fa9e4066Sahrens 
390fa9e4066Sahrens 	VERIFY(dsl_prop_unregister(dmu_objset_ds(zv->zv_objset),
391fa9e4066Sahrens 	    "readonly", zvol_readonly_changed_cb, zv) == 0);
392fa9e4066Sahrens 
393fa9e4066Sahrens 	dmu_objset_close(zv->zv_objset);
394fa9e4066Sahrens 
395fa9e4066Sahrens 	zv->zv_objset = NULL;
396fa9e4066Sahrens 
397fa9e4066Sahrens 	ddi_soft_state_free(zvol_state, zv->zv_minor);
398fa9e4066Sahrens 
399fa9e4066Sahrens 	zvol_minors--;
400fa9e4066Sahrens 
401fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
402fa9e4066Sahrens 
403fa9e4066Sahrens 	return (0);
404fa9e4066Sahrens }
405fa9e4066Sahrens 
406fa9e4066Sahrens int
407fa9e4066Sahrens zvol_set_volsize(zfs_cmd_t *zc)
408fa9e4066Sahrens {
409fa9e4066Sahrens 	zvol_state_t *zv;
410fa9e4066Sahrens 	dev_t dev = zc->zc_dev;
411fa9e4066Sahrens 	dmu_tx_t *tx;
412fa9e4066Sahrens 	int error;
413*5c5460e9Seschrock 	dmu_object_info_t doi;
414fa9e4066Sahrens 
415fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
416fa9e4066Sahrens 
417fa9e4066Sahrens 	if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) {
418fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
419fa9e4066Sahrens 		return (ENXIO);
420fa9e4066Sahrens 	}
421fa9e4066Sahrens 
422*5c5460e9Seschrock 	if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 ||
423*5c5460e9Seschrock 	    (error = zvol_check_volsize(zc, doi.doi_data_block_size)) != 0) {
424*5c5460e9Seschrock 		mutex_exit(&zvol_state_lock);
425*5c5460e9Seschrock 		return (error);
426*5c5460e9Seschrock 	}
427*5c5460e9Seschrock 
428fa9e4066Sahrens 	if (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) {
429fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
430fa9e4066Sahrens 		return (EROFS);
431fa9e4066Sahrens 	}
432fa9e4066Sahrens 
433fa9e4066Sahrens 	tx = dmu_tx_create(zv->zv_objset);
434fa9e4066Sahrens 	dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, 1);
435fa9e4066Sahrens 	dmu_tx_hold_free(tx, ZVOL_OBJ, zc->zc_volsize, DMU_OBJECT_END);
436fa9e4066Sahrens 	error = dmu_tx_assign(tx, TXG_WAIT);
437fa9e4066Sahrens 	if (error) {
438fa9e4066Sahrens 		dmu_tx_abort(tx);
439fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
440fa9e4066Sahrens 		return (error);
441fa9e4066Sahrens 	}
442fa9e4066Sahrens 
443fa9e4066Sahrens 	error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1,
444fa9e4066Sahrens 	    &zc->zc_volsize, tx);
445fa9e4066Sahrens 	if (error == 0)
446fa9e4066Sahrens 		dmu_free_range(zv->zv_objset, ZVOL_OBJ, zc->zc_volsize,
447fa9e4066Sahrens 		    DMU_OBJECT_END, tx);
448fa9e4066Sahrens 
449fa9e4066Sahrens 	dmu_tx_commit(tx);
450fa9e4066Sahrens 
451fa9e4066Sahrens 	if (error == 0) {
452fa9e4066Sahrens 		zv->zv_volsize = zc->zc_volsize;
453fa9e4066Sahrens 		zvol_size_changed(zv, dev);
454fa9e4066Sahrens 	}
455fa9e4066Sahrens 
456fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
457fa9e4066Sahrens 
458fa9e4066Sahrens 	return (error);
459fa9e4066Sahrens }
460fa9e4066Sahrens 
461fa9e4066Sahrens int
462fa9e4066Sahrens zvol_set_volblocksize(zfs_cmd_t *zc)
463fa9e4066Sahrens {
464fa9e4066Sahrens 	zvol_state_t *zv;
465fa9e4066Sahrens 	dmu_tx_t *tx;
466fa9e4066Sahrens 	int error;
467fa9e4066Sahrens 
468fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
469fa9e4066Sahrens 
470fa9e4066Sahrens 	if ((zv = zvol_minor_lookup(zc->zc_name)) == NULL) {
471fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
472fa9e4066Sahrens 		return (ENXIO);
473fa9e4066Sahrens 	}
474fa9e4066Sahrens 
475fa9e4066Sahrens 	if (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) {
476fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
477fa9e4066Sahrens 		return (EROFS);
478fa9e4066Sahrens 	}
479fa9e4066Sahrens 
480fa9e4066Sahrens 	tx = dmu_tx_create(zv->zv_objset);
481fa9e4066Sahrens 	dmu_tx_hold_bonus(tx, ZVOL_OBJ);
482fa9e4066Sahrens 	error = dmu_tx_assign(tx, TXG_WAIT);
483fa9e4066Sahrens 	if (error) {
484fa9e4066Sahrens 		dmu_tx_abort(tx);
485fa9e4066Sahrens 	} else {
486fa9e4066Sahrens 		error = dmu_object_set_blocksize(zv->zv_objset, ZVOL_OBJ,
487fa9e4066Sahrens 		    zc->zc_volblocksize, 0, tx);
488fa9e4066Sahrens 		if (error == ENOTSUP)
489fa9e4066Sahrens 			error = EBUSY;
490fa9e4066Sahrens 		dmu_tx_commit(tx);
491fa9e4066Sahrens 	}
492fa9e4066Sahrens 
493fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
494fa9e4066Sahrens 
495fa9e4066Sahrens 	return (error);
496fa9e4066Sahrens }
497fa9e4066Sahrens 
498fa9e4066Sahrens /*ARGSUSED*/
499fa9e4066Sahrens int
500fa9e4066Sahrens zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr)
501fa9e4066Sahrens {
502fa9e4066Sahrens 	minor_t minor = getminor(*devp);
503fa9e4066Sahrens 	zvol_state_t *zv;
504fa9e4066Sahrens 
505fa9e4066Sahrens 	if (minor == 0)			/* This is the control device */
506fa9e4066Sahrens 		return (0);
507fa9e4066Sahrens 
508fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
509fa9e4066Sahrens 
510fa9e4066Sahrens 	zv = ddi_get_soft_state(zvol_state, minor);
511fa9e4066Sahrens 	if (zv == NULL) {
512fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
513fa9e4066Sahrens 		return (ENXIO);
514fa9e4066Sahrens 	}
515fa9e4066Sahrens 
516fa9e4066Sahrens 	ASSERT(zv->zv_objset != NULL);
517fa9e4066Sahrens 
518fa9e4066Sahrens 	if ((flag & FWRITE) &&
519fa9e4066Sahrens 	    (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY))) {
520fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
521fa9e4066Sahrens 		return (EROFS);
522fa9e4066Sahrens 	}
523fa9e4066Sahrens 
524fa9e4066Sahrens 	if (zv->zv_open_count[otyp] == 0 || otyp == OTYP_LYR) {
525fa9e4066Sahrens 		zv->zv_open_count[otyp]++;
526fa9e4066Sahrens 		zv->zv_total_opens++;
527fa9e4066Sahrens 	}
528fa9e4066Sahrens 
529fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
530fa9e4066Sahrens 
531fa9e4066Sahrens 	return (0);
532fa9e4066Sahrens }
533fa9e4066Sahrens 
534fa9e4066Sahrens /*ARGSUSED*/
535fa9e4066Sahrens int
536fa9e4066Sahrens zvol_close(dev_t dev, int flag, int otyp, cred_t *cr)
537fa9e4066Sahrens {
538fa9e4066Sahrens 	minor_t minor = getminor(dev);
539fa9e4066Sahrens 	zvol_state_t *zv;
540fa9e4066Sahrens 
541fa9e4066Sahrens 	if (minor == 0)		/* This is the control device */
542fa9e4066Sahrens 		return (0);
543fa9e4066Sahrens 
544fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
545fa9e4066Sahrens 
546fa9e4066Sahrens 	zv = ddi_get_soft_state(zvol_state, minor);
547fa9e4066Sahrens 	if (zv == NULL) {
548fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
549fa9e4066Sahrens 		return (ENXIO);
550fa9e4066Sahrens 	}
551fa9e4066Sahrens 
552fa9e4066Sahrens 	/*
553fa9e4066Sahrens 	 * The next statement is a workaround for the following DDI bug:
554fa9e4066Sahrens 	 * 6343604 specfs race: multiple "last-close" of the same device
555fa9e4066Sahrens 	 */
556fa9e4066Sahrens 	if (zv->zv_total_opens == 0) {
557fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
558fa9e4066Sahrens 		return (0);
559fa9e4066Sahrens 	}
560fa9e4066Sahrens 
561fa9e4066Sahrens 	/*
562fa9e4066Sahrens 	 * If the open count is zero, this is a spurious close.
563fa9e4066Sahrens 	 * That indicates a bug in the kernel / DDI framework.
564fa9e4066Sahrens 	 */
565fa9e4066Sahrens 	ASSERT(zv->zv_open_count[otyp] != 0);
566fa9e4066Sahrens 	ASSERT(zv->zv_total_opens != 0);
567fa9e4066Sahrens 
568fa9e4066Sahrens 	/*
569fa9e4066Sahrens 	 * You may get multiple opens, but only one close.
570fa9e4066Sahrens 	 */
571fa9e4066Sahrens 	zv->zv_open_count[otyp]--;
572fa9e4066Sahrens 	zv->zv_total_opens--;
573fa9e4066Sahrens 
574fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
575fa9e4066Sahrens 
576fa9e4066Sahrens 	return (0);
577fa9e4066Sahrens }
578fa9e4066Sahrens 
579fa9e4066Sahrens int
580fa9e4066Sahrens zvol_strategy(buf_t *bp)
581fa9e4066Sahrens {
582fa9e4066Sahrens 	zvol_state_t *zv = ddi_get_soft_state(zvol_state, getminor(bp->b_edev));
583fa9e4066Sahrens 	uint64_t off, volsize;
584fa9e4066Sahrens 	size_t size, resid;
585fa9e4066Sahrens 	char *addr;
586fa9e4066Sahrens 	int error = 0;
587fa9e4066Sahrens 
588fa9e4066Sahrens 	if (zv == NULL) {
589fa9e4066Sahrens 		bioerror(bp, ENXIO);
590fa9e4066Sahrens 		biodone(bp);
591fa9e4066Sahrens 		return (0);
592fa9e4066Sahrens 	}
593fa9e4066Sahrens 
594fa9e4066Sahrens 	if (getminor(bp->b_edev) == 0) {
595fa9e4066Sahrens 		bioerror(bp, EINVAL);
596fa9e4066Sahrens 		biodone(bp);
597fa9e4066Sahrens 		return (0);
598fa9e4066Sahrens 	}
599fa9e4066Sahrens 
600fa9e4066Sahrens 	if (zv->zv_readonly && !(bp->b_flags & B_READ)) {
601fa9e4066Sahrens 		bioerror(bp, EROFS);
602fa9e4066Sahrens 		biodone(bp);
603fa9e4066Sahrens 		return (0);
604fa9e4066Sahrens 	}
605fa9e4066Sahrens 
606fa9e4066Sahrens 	off = ldbtob(bp->b_blkno);
607fa9e4066Sahrens 	volsize = zv->zv_volsize;
608fa9e4066Sahrens 
609fa9e4066Sahrens 	ASSERT(zv->zv_objset != NULL);
610fa9e4066Sahrens 
611fa9e4066Sahrens 	bp_mapin(bp);
612fa9e4066Sahrens 	addr = bp->b_un.b_addr;
613fa9e4066Sahrens 	resid = bp->b_bcount;
614fa9e4066Sahrens 
615fa9e4066Sahrens 	while (resid != 0 && off < volsize) {
616fa9e4066Sahrens 
617fa9e4066Sahrens 		size = MIN(resid, 1UL << 20);	/* cap at 1MB per tx */
618fa9e4066Sahrens 
619fa9e4066Sahrens 		if (size > volsize - off)	/* don't write past the end */
620fa9e4066Sahrens 			size = volsize - off;
621fa9e4066Sahrens 
622fa9e4066Sahrens 		if (bp->b_flags & B_READ) {
623fa9e4066Sahrens 			error = dmu_read_canfail(zv->zv_objset, ZVOL_OBJ,
624fa9e4066Sahrens 			    off, size, addr);
625fa9e4066Sahrens 		} else {
626fa9e4066Sahrens 			dmu_tx_t *tx = dmu_tx_create(zv->zv_objset);
627fa9e4066Sahrens 			dmu_tx_hold_write(tx, ZVOL_OBJ, off, size);
628fa9e4066Sahrens 			error = dmu_tx_assign(tx, TXG_WAIT);
629fa9e4066Sahrens 			if (error) {
630fa9e4066Sahrens 				dmu_tx_abort(tx);
631fa9e4066Sahrens 			} else {
632fa9e4066Sahrens 				dmu_write(zv->zv_objset, ZVOL_OBJ,
633fa9e4066Sahrens 				    off, size, addr, tx);
634fa9e4066Sahrens 				dmu_tx_commit(tx);
635fa9e4066Sahrens 			}
636fa9e4066Sahrens 		}
637fa9e4066Sahrens 		if (error)
638fa9e4066Sahrens 			break;
639fa9e4066Sahrens 		off += size;
640fa9e4066Sahrens 		addr += size;
641fa9e4066Sahrens 		resid -= size;
642fa9e4066Sahrens 	}
643fa9e4066Sahrens 
644fa9e4066Sahrens 	if ((bp->b_resid = resid) == bp->b_bcount)
645fa9e4066Sahrens 		bioerror(bp, off > volsize ? EINVAL : error);
646fa9e4066Sahrens 
647fa9e4066Sahrens 	biodone(bp);
648fa9e4066Sahrens 	return (0);
649fa9e4066Sahrens }
650fa9e4066Sahrens 
651fa9e4066Sahrens /*ARGSUSED*/
652fa9e4066Sahrens int
653fa9e4066Sahrens zvol_read(dev_t dev, uio_t *uiop, cred_t *cr)
654fa9e4066Sahrens {
655fa9e4066Sahrens 	return (physio(zvol_strategy, NULL, dev, B_READ, minphys, uiop));
656fa9e4066Sahrens }
657fa9e4066Sahrens 
658fa9e4066Sahrens /*ARGSUSED*/
659fa9e4066Sahrens int
660fa9e4066Sahrens zvol_write(dev_t dev, uio_t *uiop, cred_t *cr)
661fa9e4066Sahrens {
662fa9e4066Sahrens 	return (physio(zvol_strategy, NULL, dev, B_WRITE, minphys, uiop));
663fa9e4066Sahrens }
664fa9e4066Sahrens 
665fa9e4066Sahrens /*ARGSUSED*/
666fa9e4066Sahrens int
667fa9e4066Sahrens zvol_aread(dev_t dev, struct aio_req *aio, cred_t *cr)
668fa9e4066Sahrens {
669fa9e4066Sahrens 	return (aphysio(zvol_strategy, anocancel, dev, B_READ, minphys, aio));
670fa9e4066Sahrens }
671fa9e4066Sahrens 
672fa9e4066Sahrens /*ARGSUSED*/
673fa9e4066Sahrens int
674fa9e4066Sahrens zvol_awrite(dev_t dev, struct aio_req *aio, cred_t *cr)
675fa9e4066Sahrens {
676fa9e4066Sahrens 	return (aphysio(zvol_strategy, anocancel, dev, B_WRITE, minphys, aio));
677fa9e4066Sahrens }
678fa9e4066Sahrens 
679fa9e4066Sahrens /*
680fa9e4066Sahrens  * Dirtbag ioctls to support mkfs(1M) for UFS filesystems.  See dkio(7I).
681fa9e4066Sahrens  */
682fa9e4066Sahrens /*ARGSUSED*/
683fa9e4066Sahrens int
684fa9e4066Sahrens zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
685fa9e4066Sahrens {
686fa9e4066Sahrens 	zvol_state_t *zv;
687fa9e4066Sahrens 	struct dk_cinfo dkc;
688fa9e4066Sahrens 	struct dk_minfo dkm;
689fa9e4066Sahrens 	dk_efi_t efi;
690fa9e4066Sahrens 	efi_gpt_t gpt;
691fa9e4066Sahrens 	efi_gpe_t gpe;
692fa9e4066Sahrens 	struct uuid uuid = EFI_RESERVED;
693fa9e4066Sahrens 	uint32_t crc;
694fa9e4066Sahrens 	int error = 0;
695fa9e4066Sahrens 
696fa9e4066Sahrens 	mutex_enter(&zvol_state_lock);
697fa9e4066Sahrens 
698fa9e4066Sahrens 	zv = ddi_get_soft_state(zvol_state, getminor(dev));
699fa9e4066Sahrens 
700fa9e4066Sahrens 	if (zv == NULL) {
701fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
702fa9e4066Sahrens 		return (ENXIO);
703fa9e4066Sahrens 	}
704fa9e4066Sahrens 
705fa9e4066Sahrens 	switch (cmd) {
706fa9e4066Sahrens 
707fa9e4066Sahrens 	case DKIOCINFO:
708fa9e4066Sahrens 		bzero(&dkc, sizeof (dkc));
709fa9e4066Sahrens 		(void) strcpy(dkc.dki_cname, "zvol");
710fa9e4066Sahrens 		(void) strcpy(dkc.dki_dname, "zvol");
711fa9e4066Sahrens 		dkc.dki_ctype = DKC_UNKNOWN;
712fa9e4066Sahrens 		dkc.dki_maxtransfer = 1 << 15;
713fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
714fa9e4066Sahrens 		if (ddi_copyout(&dkc, (void *)arg, sizeof (dkc), flag))
715fa9e4066Sahrens 			error = EFAULT;
716fa9e4066Sahrens 		return (error);
717fa9e4066Sahrens 
718fa9e4066Sahrens 	case DKIOCGMEDIAINFO:
719fa9e4066Sahrens 		bzero(&dkm, sizeof (dkm));
720fa9e4066Sahrens 		dkm.dki_lbsize = 1U << zv->zv_min_bs;
721fa9e4066Sahrens 		dkm.dki_capacity = zv->zv_volsize >> zv->zv_min_bs;
722fa9e4066Sahrens 		dkm.dki_media_type = DK_UNKNOWN;
723fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
724fa9e4066Sahrens 		if (ddi_copyout(&dkm, (void *)arg, sizeof (dkm), flag))
725fa9e4066Sahrens 			error = EFAULT;
726fa9e4066Sahrens 		return (error);
727fa9e4066Sahrens 
728fa9e4066Sahrens 	case DKIOCGETEFI:
729fa9e4066Sahrens 		if (ddi_copyin((void *)arg, &efi, sizeof (dk_efi_t), flag)) {
730fa9e4066Sahrens 			mutex_exit(&zvol_state_lock);
731fa9e4066Sahrens 			return (EFAULT);
732fa9e4066Sahrens 		}
733fa9e4066Sahrens 
734fa9e4066Sahrens 		bzero(&gpt, sizeof (gpt));
735fa9e4066Sahrens 		bzero(&gpe, sizeof (gpe));
736fa9e4066Sahrens 
737fa9e4066Sahrens 		efi.dki_data = (void *)(uintptr_t)efi.dki_data_64;
738fa9e4066Sahrens 
739fa9e4066Sahrens 		if (efi.dki_length < sizeof (gpt) + sizeof (gpe)) {
740fa9e4066Sahrens 			mutex_exit(&zvol_state_lock);
741fa9e4066Sahrens 			return (EINVAL);
742fa9e4066Sahrens 		}
743fa9e4066Sahrens 
744fa9e4066Sahrens 		efi.dki_length = sizeof (gpt) + sizeof (gpe);
745fa9e4066Sahrens 
746fa9e4066Sahrens 		gpt.efi_gpt_Signature = LE_64(EFI_SIGNATURE);
747*5c5460e9Seschrock 		gpt.efi_gpt_Revision = LE_32(EFI_VERSION_CURRENT);
748fa9e4066Sahrens 		gpt.efi_gpt_HeaderSize = LE_32(sizeof (gpt));
749fa9e4066Sahrens 		gpt.efi_gpt_FirstUsableLBA = LE_64(0ULL);
750fa9e4066Sahrens 		gpt.efi_gpt_LastUsableLBA =
751fa9e4066Sahrens 		    LE_64((zv->zv_volsize >> zv->zv_min_bs) - 1);
752fa9e4066Sahrens 		gpt.efi_gpt_NumberOfPartitionEntries = LE_32(1);
753fa9e4066Sahrens 		gpt.efi_gpt_SizeOfPartitionEntry = LE_32(sizeof (gpe));
754fa9e4066Sahrens 
755fa9e4066Sahrens 		UUID_LE_CONVERT(gpe.efi_gpe_PartitionTypeGUID, uuid);
756fa9e4066Sahrens 		gpe.efi_gpe_StartingLBA = gpt.efi_gpt_FirstUsableLBA;
757fa9e4066Sahrens 		gpe.efi_gpe_EndingLBA = gpt.efi_gpt_LastUsableLBA;
758fa9e4066Sahrens 
759fa9e4066Sahrens 		CRC32(crc, &gpe, sizeof (gpe), -1U, crc32_table);
760fa9e4066Sahrens 		gpt.efi_gpt_PartitionEntryArrayCRC32 = LE_32(~crc);
761fa9e4066Sahrens 
762fa9e4066Sahrens 		CRC32(crc, &gpt, sizeof (gpt), -1U, crc32_table);
763fa9e4066Sahrens 		gpt.efi_gpt_HeaderCRC32 = LE_32(~crc);
764fa9e4066Sahrens 
765fa9e4066Sahrens 		mutex_exit(&zvol_state_lock);
766fa9e4066Sahrens 		if (ddi_copyout(&gpt, efi.dki_data, sizeof (gpt), flag) ||
767fa9e4066Sahrens 		    ddi_copyout(&gpe, efi.dki_data + 1, sizeof (gpe), flag))
768fa9e4066Sahrens 			error = EFAULT;
769fa9e4066Sahrens 		return (error);
770fa9e4066Sahrens 
771fa9e4066Sahrens 	default:
772fa9e4066Sahrens 		error = ENOTSUP;
773fa9e4066Sahrens 		break;
774fa9e4066Sahrens 
775fa9e4066Sahrens 	}
776fa9e4066Sahrens 	mutex_exit(&zvol_state_lock);
777fa9e4066Sahrens 	return (error);
778fa9e4066Sahrens }
779fa9e4066Sahrens 
780fa9e4066Sahrens int
781fa9e4066Sahrens zvol_busy(void)
782fa9e4066Sahrens {
783fa9e4066Sahrens 	return (zvol_minors != 0);
784fa9e4066Sahrens }
785fa9e4066Sahrens 
786fa9e4066Sahrens void
787fa9e4066Sahrens zvol_init(void)
788fa9e4066Sahrens {
789fa9e4066Sahrens 	VERIFY(ddi_soft_state_init(&zvol_state, sizeof (zvol_state_t), 1) == 0);
790fa9e4066Sahrens 	mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL);
791fa9e4066Sahrens }
792fa9e4066Sahrens 
793fa9e4066Sahrens void
794fa9e4066Sahrens zvol_fini(void)
795fa9e4066Sahrens {
796fa9e4066Sahrens 	mutex_destroy(&zvol_state_lock);
797fa9e4066Sahrens 	ddi_soft_state_fini(&zvol_state);
798fa9e4066Sahrens }
799