zvol.c revision af2c4821c0a23e873f2a63bca4145080aa2183e3
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* ZFS volume emulation driver.
*
* Makes a DMU object look like a volume of arbitrary size, up to 2^64 bytes.
* Volumes are accessed through the symbolic links named:
*
*
* These links are created by the ZFS-specific devfsadm link generator.
* Volumes are persistent through reboot. No user command needs to be
* run before opening and using a device.
*/
#include <sys/dsl_prop.h>
#include <sys/efi_partition.h>
#include <sys/byteorder.h>
#include <sys/pathname.h>
#include <sys/zfs_ioctl.h>
#include <sys/refcount.h>
#include <sys/zfs_znode.h>
#include <sys/zfs_rlock.h>
#include "zfs_namecheck.h"
#define ZVOL_OBJ 1ULL
#define ZVOL_ZAP_OBJ 2ULL
static void *zvol_state;
/*
* This lock protects the zvol_state structure from being modified
* while it's being used, e.g. an open that comes in before a create
* finishes. It also protects temporary opens of the dataset so that,
* e.g., an open doesn't get a spurious EBUSY.
*/
static kmutex_t zvol_state_lock;
static uint32_t zvol_minors;
/*
* The in-core state of each volume.
*/
typedef struct zvol_state {
} zvol_state_t;
/*
* zvol maximum transfer in one DMU tx.
*/
static void
{
}
int
{
if (volsize == 0)
return (EINVAL);
return (EINVAL);
#ifdef _ILP32
return (EOVERFLOW);
#endif
return (0);
}
int
{
if (volblocksize < SPA_MINBLOCKSIZE ||
!ISP2(volblocksize))
return (EDOM);
return (0);
}
static void
{
}
int
{
int error;
if (error)
return (error);
if (error == 0) {
}
return (error);
}
/*
* Find a free minor number.
*/
static minor_t
zvol_minor_alloc(void)
{
return (minor);
return (0);
}
static zvol_state_t *
zvol_minor_lookup(const char *name)
{
continue;
break;
}
return (zv);
}
void
{
int error;
/*
* These properites must be removed from the list so the generic
* property setting step won't apply to them.
*/
zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0);
DMU_OT_NONE, 0, tx);
DMU_OT_NONE, 0, tx);
}
/*
* Replay a TX_WRITE ZIL transaction that didn't get committed
* after a system failure
*/
static int
{
int error;
if (byteswap)
if (error) {
} else {
}
return (error);
}
/* ARGSUSED */
static int
{
return (ENOTSUP);
}
/*
* Callback vectors for replaying records.
* Only TX_WRITE is needed for zvol.
*/
zvol_replay_err, /* 0 no such transaction type */
zvol_replay_err, /* TX_CREATE */
zvol_replay_err, /* TX_MKDIR */
zvol_replay_err, /* TX_MKXATTR */
zvol_replay_err, /* TX_SYMLINK */
zvol_replay_err, /* TX_REMOVE */
zvol_replay_err, /* TX_RMDIR */
zvol_replay_err, /* TX_LINK */
zvol_replay_err, /* TX_RENAME */
zvol_replay_write, /* TX_WRITE */
zvol_replay_err, /* TX_TRUNCATE */
zvol_replay_err, /* TX_SETATTR */
zvol_replay_err, /* TX_ACL */
};
/*
* Create a minor node for the specified volume.
*/
int
{
int ds_mode = DS_MODE_PRIMARY;
char *devpath;
int error;
return (EEXIST);
}
if (error) {
return (error);
}
if (error) {
return (error);
}
/*
* same minor number we used last time.
*/
if (error == 0) {
if (error == 0) {
}
}
}
/*
* If we found a minor but it's already in use, we must pick a new one.
*/
minor = 0;
if (minor == 0)
minor = zvol_minor_alloc();
if (minor == 0) {
return (ENXIO);
}
return (EAGAIN);
}
(char *)name);
return (EAGAIN);
}
return (EAGAIN);
}
/* get and cache the blocksize */
/* XXX this should handle the possible i/o error */
zvol_minors++;
return (0);
}
/*
* Remove minor node for the specified volume.
*/
int
zvol_remove_minor(const char *name)
{
char namebuf[30];
return (ENXIO);
}
if (zv->zv_total_opens != 0) {
return (EBUSY);
}
zvol_minors--;
return (0);
}
int
{
int error;
return (ENXIO);
}
doi.doi_data_block_size)) != 0) {
return (error);
}
return (EROFS);
}
if (error) {
return (error);
}
if (error == 0) {
DMU_OBJECT_END, tx);
}
if (error == 0) {
}
return (error);
}
int
{
int error;
return (ENXIO);
}
return (EROFS);
}
if (error) {
} else {
volblocksize, 0, tx);
}
return (error);
}
/*ARGSUSED*/
int
{
if (minor == 0) /* This is the control device */
return (0);
return (ENXIO);
}
return (EROFS);
}
zv->zv_total_opens++;
}
return (0);
}
/*ARGSUSED*/
int
{
if (minor == 0) /* This is the control device */
return (0);
return (ENXIO);
}
/*
* The next statement is a workaround for the following DDI bug:
* 6343604 specfs race: multiple "last-close" of the same device
*/
if (zv->zv_total_opens == 0) {
return (0);
}
/*
* If the open count is zero, this is a spurious close.
* That indicates a bug in the kernel / DDI framework.
*/
/*
* You may get multiple opens, but only one close.
*/
zv->zv_total_opens--;
return (0);
}
static void
{
}
/*
* Get data to generate a TX_WRITE intent log record.
*/
static int
{
int error;
/*
* Write records come in two flavors: immediate and indirect.
* For small writes it's cheaper to store the data with the
* log record (immediate); for large writes it's cheaper to
* sync the data and get a pointer to it (indirect) so that
* we don't have to write the data twice.
*/
/*
* Lock the range of the block to ensure that when the data is
* written out and it's checksum is being calculated that no other
* thread can change the block.
*/
if (error == 0)
/*
* If we get EINPROGRESS, then we need to wait for a
* write IO initiated by dmu_sync() to complete before
* we can release this dbuf. We will finish everything
* up in the zvol_get_done() callback.
*/
if (error == EINPROGRESS)
return (0);
return (error);
}
/*
* zvol_log_write() handles synchronous writes using TX_WRITE ZIL transactions.
*
* We store data in the log buffers if it's small enough.
* Otherwise we will later flush the data out via dmu_sync().
*/
static void
{
lr_write_t *lr;
while (len) {
itx->itx_wr_state =
}
}
int
{
char *addr;
int error = 0;
int sync;
return (0);
}
return (0);
}
return (0);
}
/*
* There must be no buffer changes when doing a dmu_sync() because
* we can't change the data whilst calculating the checksum.
* A better approach than a per zvol rwlock would be to lock ranges.
*/
if (reading) {
} else {
if (error) {
} else {
}
}
if (error)
break;
}
if (sync)
return (0);
}
/*
* Set the buffer count to the zvol maximum transfer.
* Using our own routine instead of the default minphys()
* means that for larger writes we write bigger buffers on X86
* (128K instead of 56K) and flush the disk write cache less often
* (every zvol_maxphys - currently 1MB) instead of minphys (currently
* 56K on X86 and 128K on sparc).
*/
void
{
}
/*ARGSUSED*/
int
{
int error = 0;
if (error)
break;
}
return (error);
}
/*ARGSUSED*/
int
{
int error = 0;
if (error) {
break;
}
if (error == 0)
if (error)
break;
}
return (error);
}
/*
* Dirtbag ioctls to support mkfs(1M) for UFS filesystems. See dkio(7I).
*/
/*ARGSUSED*/
int
{
struct dk_callback *dkc;
int error = 0;
return (ENXIO);
}
switch (cmd) {
case DKIOCINFO:
return (error);
case DKIOCGMEDIAINFO:
return (error);
case DKIOCGETEFI:
return (EFAULT);
}
/*
* Some clients may attempt to request a PMBR for the
* zvol. Currently this interface will return ENOTTY to
* such requests. These requests could be supported by
* adding a check for lba == 0 and consing up an appropriate
* RMBR.
*/
return (EINVAL);
}
return (EINVAL);
}
} else {
}
return (error);
case DKIOCFLUSHWRITECACHE:
error = 0;
}
break;
case DKIOCGGEOM:
case DKIOCGVTOC:
/* commands using these (like prtvtoc) expect ENOTSUP */
break;
default:
break;
}
return (error);
}
int
zvol_busy(void)
{
return (zvol_minors != 0);
}
void
zvol_init(void)
{
}
void
zvol_fini(void)
{
}