2010-08-26 22:45:02 +04:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* or http://www.opensolaris.org/os/licensing.
|
|
|
|
* 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 (C) 2008-2010 Lawrence Livermore National Security, LLC.
|
|
|
|
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
|
|
|
|
* Rewritten for Linux by Brian Behlendorf <behlendorf1@llnl.gov>.
|
|
|
|
* LLNL-CODE-403049.
|
|
|
|
*
|
|
|
|
* 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:
|
|
|
|
*
|
|
|
|
* /dev/<pool_name>/<dataset_name>
|
|
|
|
*
|
|
|
|
* Volumes are persistent through reboot and module load. No user command
|
|
|
|
* needs to be run before opening and using a device.
|
2015-08-02 16:01:14 +03:00
|
|
|
*
|
|
|
|
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
|
2016-02-16 22:52:55 +03:00
|
|
|
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
|
2019-05-05 02:39:10 +03:00
|
|
|
* Copyright (c) 2012, 2019 by Delphix. All rights reserved.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
|
|
|
|
2017-05-10 20:51:29 +03:00
|
|
|
/*
|
|
|
|
* Note on locking of zvol state structures.
|
|
|
|
*
|
|
|
|
* These structures are used to maintain internal state used to emulate block
|
|
|
|
* devices on top of zvols. In particular, management of device minor number
|
|
|
|
* operations - create, remove, rename, and set_snapdev - involves access to
|
|
|
|
* these structures. The zvol_state_lock is primarily used to protect the
|
|
|
|
* zvol_state_list. The zv->zv_state_lock is used to protect the contents
|
|
|
|
* of the zvol_state_t structures, as well as to make sure that when the
|
|
|
|
* time comes to remove the structure from the list, it is not in use, and
|
|
|
|
* therefore, it can be taken off zvol_state_list and freed.
|
|
|
|
*
|
2017-06-13 19:03:44 +03:00
|
|
|
* The zv_suspend_lock was introduced to allow for suspending I/O to a zvol,
|
|
|
|
* e.g. for the duration of receive and rollback operations. This lock can be
|
|
|
|
* held for significant periods of time. Given that it is undesirable to hold
|
|
|
|
* mutexes for long periods of time, the following lock ordering applies:
|
|
|
|
* - take zvol_state_lock if necessary, to protect zvol_state_list
|
|
|
|
* - take zv_suspend_lock if necessary, by the code path in question
|
|
|
|
* - take zv_state_lock to protect zvol_state_t
|
|
|
|
*
|
|
|
|
* The minor operations are issued to spa->spa_zvol_taskq queues, that are
|
2017-05-10 20:51:29 +03:00
|
|
|
* single-threaded (to preserve order of minor operations), and are executed
|
|
|
|
* through the zvol_task_cb that dispatches the specific operations. Therefore,
|
|
|
|
* these operations are serialized per pool. Consequently, we can be certain
|
|
|
|
* that for a given zvol, there is only one operation at a time in progress.
|
|
|
|
* That is why one can be sure that first, zvol_state_t for a given zvol is
|
|
|
|
* allocated and placed on zvol_state_list, and then other minor operations
|
|
|
|
* for this zvol are going to proceed in the order of issue.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-08-20 19:52:37 +03:00
|
|
|
#include <sys/dataset_kstats.h>
|
2013-05-10 23:47:54 +04:00
|
|
|
#include <sys/dbuf.h>
|
2010-08-26 22:45:02 +04:00
|
|
|
#include <sys/dmu_traverse.h>
|
|
|
|
#include <sys/dsl_dataset.h>
|
|
|
|
#include <sys/dsl_prop.h>
|
2014-03-22 13:07:14 +04:00
|
|
|
#include <sys/dsl_dir.h>
|
2010-08-26 22:45:02 +04:00
|
|
|
#include <sys/zap.h>
|
2015-08-25 00:18:48 +03:00
|
|
|
#include <sys/zfeature.h>
|
2010-08-26 22:45:02 +04:00
|
|
|
#include <sys/zil_impl.h>
|
2015-08-02 16:01:14 +03:00
|
|
|
#include <sys/dmu_tx.h>
|
2010-08-26 22:45:02 +04:00
|
|
|
#include <sys/zio.h>
|
|
|
|
#include <sys/zfs_rlock.h>
|
2014-03-22 13:07:14 +04:00
|
|
|
#include <sys/spa_impl.h>
|
2010-08-26 22:45:02 +04:00
|
|
|
#include <sys/zvol.h>
|
2018-08-20 19:52:37 +03:00
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
#include <sys/zvol_impl.h>
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2012-06-02 05:49:10 +04:00
|
|
|
unsigned int zvol_inhibit_dev = 0;
|
2017-07-12 23:05:37 +03:00
|
|
|
unsigned int zvol_volmode = ZFS_VOLMODE_GEOM;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
struct hlist_head *zvol_htable;
|
|
|
|
list_t zvol_state_list;
|
|
|
|
krwlock_t zvol_state_lock;
|
|
|
|
const zvol_platform_ops_t *ops;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
typedef enum {
|
|
|
|
ZVOL_ASYNC_CREATE_MINORS,
|
|
|
|
ZVOL_ASYNC_REMOVE_MINORS,
|
|
|
|
ZVOL_ASYNC_RENAME_MINORS,
|
|
|
|
ZVOL_ASYNC_SET_SNAPDEV,
|
2017-07-12 23:05:37 +03:00
|
|
|
ZVOL_ASYNC_SET_VOLMODE,
|
2014-03-22 13:07:14 +04:00
|
|
|
ZVOL_ASYNC_MAX
|
|
|
|
} zvol_async_op_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
zvol_async_op_t op;
|
|
|
|
char pool[MAXNAMELEN];
|
|
|
|
char name1[MAXNAMELEN];
|
|
|
|
char name2[MAXNAMELEN];
|
|
|
|
zprop_source_t source;
|
2017-07-12 23:05:37 +03:00
|
|
|
uint64_t value;
|
2014-03-22 13:07:14 +04:00
|
|
|
} zvol_task_t;
|
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
uint64_t
|
2016-12-01 00:56:50 +03:00
|
|
|
zvol_name_hash(const char *name)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2016-12-01 00:56:50 +03:00
|
|
|
int i;
|
|
|
|
uint64_t crc = -1ULL;
|
2019-09-25 19:20:30 +03:00
|
|
|
const uint8_t *p = (const uint8_t *)name;
|
2016-12-01 00:56:50 +03:00
|
|
|
ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);
|
|
|
|
for (i = 0; i < MAXNAMELEN - 1 && *p; i++, p++) {
|
|
|
|
crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ (*p)) & 0xFF];
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
2016-12-01 00:56:50 +03:00
|
|
|
return (crc);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-12-01 00:56:50 +03:00
|
|
|
* Find a zvol_state_t given the name and hash generated by zvol_name_hash.
|
2017-06-13 19:03:44 +03:00
|
|
|
* If found, return with zv_suspend_lock and zv_state_lock taken, otherwise,
|
|
|
|
* return (NULL) without the taking locks. The zv_suspend_lock is always taken
|
|
|
|
* before zv_state_lock. The mode argument indicates the mode (including none)
|
|
|
|
* for zv_suspend_lock to be taken.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
zvol_state_t *
|
2017-06-13 19:03:44 +03:00
|
|
|
zvol_find_by_name_hash(const char *name, uint64_t hash, int mode)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
|
|
|
zvol_state_t *zv;
|
2017-11-19 01:08:00 +03:00
|
|
|
struct hlist_node *p = NULL;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_enter(&zvol_state_lock, RW_READER);
|
2016-12-01 00:56:50 +03:00
|
|
|
hlist_for_each(p, ZVOL_HT_HEAD(hash)) {
|
|
|
|
zv = hlist_entry(p, zvol_state_t, zv_hlink);
|
2017-06-13 19:03:44 +03:00
|
|
|
mutex_enter(&zv->zv_state_lock);
|
2016-12-01 00:56:50 +03:00
|
|
|
if (zv->zv_hash == hash &&
|
2017-06-13 19:03:44 +03:00
|
|
|
strncmp(zv->zv_name, name, MAXNAMELEN) == 0) {
|
|
|
|
/*
|
|
|
|
* this is the right zvol, take the locks in the
|
|
|
|
* right order
|
|
|
|
*/
|
|
|
|
if (mode != RW_NONE &&
|
|
|
|
!rw_tryenter(&zv->zv_suspend_lock, mode)) {
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
|
|
|
rw_enter(&zv->zv_suspend_lock, mode);
|
|
|
|
mutex_enter(&zv->zv_state_lock);
|
|
|
|
/*
|
|
|
|
* zvol cannot be renamed as we continue
|
|
|
|
* to hold zvol_state_lock
|
|
|
|
*/
|
|
|
|
ASSERT(zv->zv_hash == hash &&
|
|
|
|
strncmp(zv->zv_name, name, MAXNAMELEN)
|
|
|
|
== 0);
|
|
|
|
}
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_exit(&zvol_state_lock);
|
2013-12-13 01:04:40 +04:00
|
|
|
return (zv);
|
2017-06-13 19:03:44 +03:00
|
|
|
}
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_exit(&zvol_state_lock);
|
2017-06-13 19:03:44 +03:00
|
|
|
|
2013-12-13 01:04:40 +04:00
|
|
|
return (NULL);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2016-12-01 00:56:50 +03:00
|
|
|
/*
|
2017-06-13 19:03:44 +03:00
|
|
|
* Find a zvol_state_t given the name.
|
|
|
|
* If found, return with zv_suspend_lock and zv_state_lock taken, otherwise,
|
|
|
|
* return (NULL) without the taking locks. The zv_suspend_lock is always taken
|
|
|
|
* before zv_state_lock. The mode argument indicates the mode (including none)
|
|
|
|
* for zv_suspend_lock to be taken.
|
2016-12-01 00:56:50 +03:00
|
|
|
*/
|
|
|
|
static zvol_state_t *
|
2017-06-13 19:03:44 +03:00
|
|
|
zvol_find_by_name(const char *name, int mode)
|
2016-12-01 00:56:50 +03:00
|
|
|
{
|
2017-06-13 19:03:44 +03:00
|
|
|
return (zvol_find_by_name_hash(name, zvol_name_hash(name), mode));
|
2016-12-01 00:56:50 +03:00
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
/*
|
|
|
|
* ZFS_IOC_CREATE callback handles dmu zvol and zap object creation.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
|
|
|
|
{
|
|
|
|
zfs_creat_t *zct = arg;
|
|
|
|
nvlist_t *nvprops = zct->zct_props;
|
|
|
|
int error;
|
|
|
|
uint64_t volblocksize, volsize;
|
|
|
|
|
|
|
|
VERIFY(nvlist_lookup_uint64(nvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLSIZE), &volsize) == 0);
|
|
|
|
if (nvlist_lookup_uint64(nvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), &volblocksize) != 0)
|
|
|
|
volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These properties must be removed from the list so the generic
|
|
|
|
* property setting step won't apply to them.
|
|
|
|
*/
|
|
|
|
VERIFY(nvlist_remove_all(nvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLSIZE)) == 0);
|
|
|
|
(void) nvlist_remove_all(nvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE));
|
|
|
|
|
|
|
|
error = dmu_object_claim(os, ZVOL_OBJ, DMU_OT_ZVOL, volblocksize,
|
|
|
|
DMU_OT_NONE, 0, tx);
|
|
|
|
ASSERT(error == 0);
|
|
|
|
|
|
|
|
error = zap_create_claim(os, ZVOL_ZAP_OBJ, DMU_OT_ZVOL_PROP,
|
|
|
|
DMU_OT_NONE, 0, tx);
|
|
|
|
ASSERT(error == 0);
|
|
|
|
|
|
|
|
error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize, tx);
|
|
|
|
ASSERT(error == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ZFS_IOC_OBJSET_STATS entry point.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
zvol_get_stats(objset_t *os, nvlist_t *nv)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
dmu_object_info_t *doi;
|
|
|
|
uint64_t val;
|
|
|
|
|
|
|
|
error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &val);
|
|
|
|
if (error)
|
2013-12-13 01:04:40 +04:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VOLSIZE, val);
|
2013-12-13 01:04:40 +04:00
|
|
|
doi = kmem_alloc(sizeof (dmu_object_info_t), KM_SLEEP);
|
2010-08-26 22:45:02 +04:00
|
|
|
error = dmu_object_info(os, ZVOL_OBJ, doi);
|
|
|
|
|
|
|
|
if (error == 0) {
|
|
|
|
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VOLBLOCKSIZE,
|
|
|
|
doi->doi_data_block_size);
|
|
|
|
}
|
|
|
|
|
2013-12-13 01:04:40 +04:00
|
|
|
kmem_free(doi, sizeof (dmu_object_info_t));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2013-12-13 01:04:40 +04:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sanity check volume size.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
zvol_check_volsize(uint64_t volsize, uint64_t blocksize)
|
|
|
|
{
|
|
|
|
if (volsize == 0)
|
2013-03-08 22:41:28 +04:00
|
|
|
return (SET_ERROR(EINVAL));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
if (volsize % blocksize != 0)
|
2013-03-08 22:41:28 +04:00
|
|
|
return (SET_ERROR(EINVAL));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
#ifdef _ILP32
|
2016-10-29 02:53:24 +03:00
|
|
|
if (volsize - 1 > SPEC_MAXOFFSET_T)
|
2013-03-08 22:41:28 +04:00
|
|
|
return (SET_ERROR(EOVERFLOW));
|
2010-08-26 22:45:02 +04:00
|
|
|
#endif
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure the zap is flushed then inform the VFS of the capacity change.
|
|
|
|
*/
|
|
|
|
static int
|
2014-01-14 02:27:33 +04:00
|
|
|
zvol_update_volsize(uint64_t volsize, objset_t *os)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
|
|
|
dmu_tx_t *tx;
|
|
|
|
int error;
|
2016-02-26 10:33:44 +03:00
|
|
|
uint64_t txg;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2011-02-25 10:36:01 +03:00
|
|
|
tx = dmu_tx_create(os);
|
2010-08-26 22:45:02 +04:00
|
|
|
dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL);
|
2014-07-07 23:49:36 +04:00
|
|
|
dmu_tx_mark_netfree(tx);
|
2010-08-26 22:45:02 +04:00
|
|
|
error = dmu_tx_assign(tx, TXG_WAIT);
|
|
|
|
if (error) {
|
|
|
|
dmu_tx_abort(tx);
|
2013-12-13 01:04:40 +04:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
2016-02-26 10:33:44 +03:00
|
|
|
txg = dmu_tx_get_txg(tx);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2011-02-25 10:36:01 +03:00
|
|
|
error = zap_update(os, ZVOL_ZAP_OBJ, "size", 8, 1,
|
2010-08-26 22:45:02 +04:00
|
|
|
&volsize, tx);
|
|
|
|
dmu_tx_commit(tx);
|
|
|
|
|
2016-02-26 10:33:44 +03:00
|
|
|
txg_wait_synced(dmu_objset_pool(os), txg);
|
|
|
|
|
2014-01-14 02:27:33 +04:00
|
|
|
if (error == 0)
|
|
|
|
error = dmu_free_long_range(os,
|
|
|
|
ZVOL_OBJ, volsize, DMU_OBJECT_END);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2014-01-14 02:27:33 +04:00
|
|
|
return (error);
|
|
|
|
}
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
/*
|
2018-06-16 01:05:21 +03:00
|
|
|
* Set ZFS_PROP_VOLSIZE set entry point. Note that modifying the volume
|
|
|
|
* size will result in a udev "change" event being generated.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
zvol_set_volsize(const char *name, uint64_t volsize)
|
|
|
|
{
|
|
|
|
objset_t *os = NULL;
|
2014-01-14 02:27:33 +04:00
|
|
|
uint64_t readonly;
|
2018-06-16 01:05:21 +03:00
|
|
|
int error;
|
2014-01-14 02:27:33 +04:00
|
|
|
boolean_t owned = B_FALSE;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2013-09-04 16:00:57 +04:00
|
|
|
error = dsl_prop_get_integer(name,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_READONLY), &readonly, NULL);
|
|
|
|
if (error != 0)
|
2013-12-13 01:04:40 +04:00
|
|
|
return (SET_ERROR(error));
|
2013-09-04 16:00:57 +04:00
|
|
|
if (readonly)
|
2013-03-08 22:41:28 +04:00
|
|
|
return (SET_ERROR(EROFS));
|
2013-09-04 16:00:57 +04:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
zvol_state_t *zv = zvol_find_by_name(name, RW_READER);
|
2017-06-13 19:03:44 +03:00
|
|
|
|
|
|
|
ASSERT(zv == NULL || (MUTEX_HELD(&zv->zv_state_lock) &&
|
|
|
|
RW_READ_HELD(&zv->zv_suspend_lock)));
|
2014-01-14 02:27:33 +04:00
|
|
|
|
|
|
|
if (zv == NULL || zv->zv_objset == NULL) {
|
2017-06-13 19:03:44 +03:00
|
|
|
if (zv != NULL)
|
|
|
|
rw_exit(&zv->zv_suspend_lock);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 20:36:48 +03:00
|
|
|
if ((error = dmu_objset_own(name, DMU_OST_ZVOL, B_FALSE, B_TRUE,
|
2014-01-14 02:27:33 +04:00
|
|
|
FTAG, &os)) != 0) {
|
2017-05-10 20:51:29 +03:00
|
|
|
if (zv != NULL)
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2014-01-14 02:27:33 +04:00
|
|
|
return (SET_ERROR(error));
|
|
|
|
}
|
|
|
|
owned = B_TRUE;
|
|
|
|
if (zv != NULL)
|
|
|
|
zv->zv_objset = os;
|
|
|
|
} else {
|
|
|
|
os = zv->zv_objset;
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
dmu_object_info_t *doi = kmem_alloc(sizeof (*doi), KM_SLEEP);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2013-12-13 01:04:40 +04:00
|
|
|
if ((error = dmu_object_info(os, ZVOL_OBJ, doi)) ||
|
|
|
|
(error = zvol_check_volsize(volsize, doi->doi_data_block_size)))
|
2014-01-14 02:27:33 +04:00
|
|
|
goto out;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2014-01-14 02:27:33 +04:00
|
|
|
error = zvol_update_volsize(volsize, os);
|
2018-06-16 01:05:21 +03:00
|
|
|
if (error == 0 && zv != NULL) {
|
|
|
|
zv->zv_volsize = volsize;
|
|
|
|
zv->zv_changed = 1;
|
|
|
|
}
|
2014-01-14 02:27:33 +04:00
|
|
|
out:
|
2017-05-31 22:52:12 +03:00
|
|
|
kmem_free(doi, sizeof (dmu_object_info_t));
|
|
|
|
|
2014-01-14 02:27:33 +04:00
|
|
|
if (owned) {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 20:36:48 +03:00
|
|
|
dmu_objset_disown(os, B_TRUE, FTAG);
|
2014-01-14 02:27:33 +04:00
|
|
|
if (zv != NULL)
|
|
|
|
zv->zv_objset = NULL;
|
2017-01-20 00:56:36 +03:00
|
|
|
} else {
|
|
|
|
rw_exit(&zv->zv_suspend_lock);
|
2014-01-14 02:27:33 +04:00
|
|
|
}
|
2017-05-10 20:51:29 +03:00
|
|
|
|
|
|
|
if (zv != NULL)
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2017-06-13 19:03:44 +03:00
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
if (error == 0 && zv != NULL)
|
|
|
|
ops->zv_update_volsize(zv, volsize);
|
2018-06-16 01:05:21 +03:00
|
|
|
|
2017-05-10 20:51:29 +03:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sanity check volume block size.
|
|
|
|
*/
|
|
|
|
int
|
2015-08-25 00:18:48 +03:00
|
|
|
zvol_check_volblocksize(const char *name, uint64_t volblocksize)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2015-08-25 00:18:48 +03:00
|
|
|
/* Record sizes above 128k need the feature to be enabled */
|
|
|
|
if (volblocksize > SPA_OLD_MAXBLOCKSIZE) {
|
|
|
|
spa_t *spa;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if ((error = spa_open(name, &spa, FTAG)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
if (!spa_feature_is_enabled(spa, SPA_FEATURE_LARGE_BLOCKS)) {
|
|
|
|
spa_close(spa, FTAG);
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't allow setting the property above 1MB,
|
|
|
|
* unless the tunable has been changed.
|
|
|
|
*/
|
|
|
|
if (volblocksize > zfs_max_recordsize)
|
|
|
|
return (SET_ERROR(EDOM));
|
|
|
|
|
|
|
|
spa_close(spa, FTAG);
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
if (volblocksize < SPA_MINBLOCKSIZE ||
|
|
|
|
volblocksize > SPA_MAXBLOCKSIZE ||
|
|
|
|
!ISP2(volblocksize))
|
2013-03-08 22:41:28 +04:00
|
|
|
return (SET_ERROR(EDOM));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set ZFS_PROP_VOLBLOCKSIZE set entry point.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
zvol_set_volblocksize(const char *name, uint64_t volblocksize)
|
|
|
|
{
|
|
|
|
zvol_state_t *zv;
|
|
|
|
dmu_tx_t *tx;
|
|
|
|
int error;
|
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
zv = zvol_find_by_name(name, RW_READER);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
if (zv == NULL)
|
2017-05-10 20:51:29 +03:00
|
|
|
return (SET_ERROR(ENXIO));
|
2017-06-13 19:03:44 +03:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
|
|
|
|
ASSERT(RW_READ_HELD(&zv->zv_suspend_lock));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2013-12-07 02:20:22 +04:00
|
|
|
if (zv->zv_flags & ZVOL_RDONLY) {
|
2017-05-10 20:51:29 +03:00
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2017-06-13 19:03:44 +03:00
|
|
|
rw_exit(&zv->zv_suspend_lock);
|
2017-05-10 20:51:29 +03:00
|
|
|
return (SET_ERROR(EROFS));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
tx = dmu_tx_create(zv->zv_objset);
|
|
|
|
dmu_tx_hold_bonus(tx, ZVOL_OBJ);
|
|
|
|
error = dmu_tx_assign(tx, TXG_WAIT);
|
|
|
|
if (error) {
|
|
|
|
dmu_tx_abort(tx);
|
|
|
|
} else {
|
|
|
|
error = dmu_object_set_blocksize(zv->zv_objset, ZVOL_OBJ,
|
|
|
|
volblocksize, 0, tx);
|
|
|
|
if (error == ENOTSUP)
|
2013-03-08 22:41:28 +04:00
|
|
|
error = SET_ERROR(EBUSY);
|
2010-08-26 22:45:02 +04:00
|
|
|
dmu_tx_commit(tx);
|
|
|
|
if (error == 0)
|
|
|
|
zv->zv_volblocksize = volblocksize;
|
|
|
|
}
|
2017-05-10 20:51:29 +03:00
|
|
|
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2017-06-13 19:03:44 +03:00
|
|
|
rw_exit(&zv->zv_suspend_lock);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2013-12-13 01:04:40 +04:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2015-08-02 16:01:14 +03:00
|
|
|
/*
|
|
|
|
* Replay a TX_TRUNCATE ZIL transaction if asked. TX_TRUNCATE is how we
|
|
|
|
* implement DKIOCFREE/free-long-range.
|
|
|
|
*/
|
|
|
|
static int
|
2017-10-27 22:46:35 +03:00
|
|
|
zvol_replay_truncate(void *arg1, void *arg2, boolean_t byteswap)
|
2015-08-02 16:01:14 +03:00
|
|
|
{
|
2017-10-27 22:46:35 +03:00
|
|
|
zvol_state_t *zv = arg1;
|
|
|
|
lr_truncate_t *lr = arg2;
|
2015-08-02 16:01:14 +03:00
|
|
|
uint64_t offset, length;
|
|
|
|
|
|
|
|
if (byteswap)
|
|
|
|
byteswap_uint64_array(lr, sizeof (*lr));
|
|
|
|
|
|
|
|
offset = lr->lr_offset;
|
|
|
|
length = lr->lr_length;
|
|
|
|
|
|
|
|
return (dmu_free_long_range(zv->zv_objset, ZVOL_OBJ, offset, length));
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
/*
|
|
|
|
* Replay a TX_WRITE ZIL transaction that didn't get committed
|
|
|
|
* after a system failure
|
|
|
|
*/
|
|
|
|
static int
|
2017-10-27 22:46:35 +03:00
|
|
|
zvol_replay_write(void *arg1, void *arg2, boolean_t byteswap)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2017-10-27 22:46:35 +03:00
|
|
|
zvol_state_t *zv = arg1;
|
|
|
|
lr_write_t *lr = arg2;
|
2010-08-26 22:45:02 +04:00
|
|
|
objset_t *os = zv->zv_objset;
|
2017-09-09 01:07:00 +03:00
|
|
|
char *data = (char *)(lr + 1); /* data follows lr_write_t */
|
|
|
|
uint64_t offset, length;
|
2010-08-26 22:45:02 +04:00
|
|
|
dmu_tx_t *tx;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (byteswap)
|
|
|
|
byteswap_uint64_array(lr, sizeof (*lr));
|
|
|
|
|
2017-09-09 01:07:00 +03:00
|
|
|
offset = lr->lr_offset;
|
|
|
|
length = lr->lr_length;
|
|
|
|
|
|
|
|
/* If it's a dmu_sync() block, write the whole block */
|
|
|
|
if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
|
|
|
|
uint64_t blocksize = BP_GET_LSIZE(&lr->lr_blkptr);
|
|
|
|
if (length < blocksize) {
|
|
|
|
offset -= offset % blocksize;
|
|
|
|
length = blocksize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
tx = dmu_tx_create(os);
|
2017-09-09 01:07:00 +03:00
|
|
|
dmu_tx_hold_write(tx, ZVOL_OBJ, offset, length);
|
2010-08-26 22:45:02 +04:00
|
|
|
error = dmu_tx_assign(tx, TXG_WAIT);
|
|
|
|
if (error) {
|
|
|
|
dmu_tx_abort(tx);
|
|
|
|
} else {
|
2017-09-09 01:07:00 +03:00
|
|
|
dmu_write(os, ZVOL_OBJ, offset, length, data, tx);
|
2010-08-26 22:45:02 +04:00
|
|
|
dmu_tx_commit(tx);
|
|
|
|
}
|
|
|
|
|
2017-09-09 01:07:00 +03:00
|
|
|
return (error);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-10-27 22:46:35 +03:00
|
|
|
zvol_replay_err(void *arg1, void *arg2, boolean_t byteswap)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2013-03-08 22:41:28 +04:00
|
|
|
return (SET_ERROR(ENOTSUP));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Callback vectors for replaying records.
|
2015-08-02 16:01:14 +03:00
|
|
|
* Only TX_WRITE and TX_TRUNCATE are needed for zvol.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
2017-10-27 22:46:35 +03:00
|
|
|
zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = {
|
|
|
|
zvol_replay_err, /* 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_truncate, /* TX_TRUNCATE */
|
|
|
|
zvol_replay_err, /* TX_SETATTR */
|
|
|
|
zvol_replay_err, /* TX_ACL */
|
|
|
|
zvol_replay_err, /* TX_CREATE_ATTR */
|
|
|
|
zvol_replay_err, /* TX_CREATE_ACL_ATTR */
|
|
|
|
zvol_replay_err, /* TX_MKDIR_ACL */
|
|
|
|
zvol_replay_err, /* TX_MKDIR_ATTR */
|
|
|
|
zvol_replay_err, /* TX_MKDIR_ACL_ATTR */
|
|
|
|
zvol_replay_err, /* TX_WRITE2 */
|
2010-08-26 22:45:02 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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().
|
|
|
|
*/
|
|
|
|
ssize_t zvol_immediate_write_sz = 32768;
|
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
void
|
2013-12-13 01:04:40 +04:00
|
|
|
zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, uint64_t offset,
|
|
|
|
uint64_t size, int sync)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
|
|
|
uint32_t blocksize = zv->zv_volblocksize;
|
|
|
|
zilog_t *zilog = zv->zv_zilog;
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
itx_wr_state_t write_state;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
if (zil_replaying(zilog, tx))
|
|
|
|
return;
|
|
|
|
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
if (zilog->zl_logbias == ZFS_LOGBIAS_THROUGHPUT)
|
|
|
|
write_state = WR_INDIRECT;
|
|
|
|
else if (!spa_has_slogs(zilog->zl_spa) &&
|
|
|
|
size >= blocksize && blocksize > zvol_immediate_write_sz)
|
|
|
|
write_state = WR_INDIRECT;
|
|
|
|
else if (sync)
|
|
|
|
write_state = WR_COPIED;
|
|
|
|
else
|
|
|
|
write_state = WR_NEED_COPY;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
while (size) {
|
|
|
|
itx_t *itx;
|
|
|
|
lr_write_t *lr;
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
itx_wr_state_t wr_state = write_state;
|
|
|
|
ssize_t len = size;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2019-06-10 21:48:42 +03:00
|
|
|
if (wr_state == WR_COPIED && size > zil_max_copied_data(zilog))
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
wr_state = WR_NEED_COPY;
|
|
|
|
else if (wr_state == WR_INDIRECT)
|
|
|
|
len = MIN(blocksize - P2PHASE(offset, blocksize), size);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
itx = zil_itx_create(TX_WRITE, sizeof (*lr) +
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
(wr_state == WR_COPIED ? len : 0));
|
2010-08-26 22:45:02 +04:00
|
|
|
lr = (lr_write_t *)&itx->itx_lr;
|
2017-06-13 19:18:08 +03:00
|
|
|
if (wr_state == WR_COPIED && dmu_read_by_dnode(zv->zv_dn,
|
|
|
|
offset, len, lr+1, DMU_READ_NO_PREFETCH) != 0) {
|
2010-08-26 22:45:02 +04:00
|
|
|
zil_itx_destroy(itx);
|
|
|
|
itx = zil_itx_create(TX_WRITE, sizeof (*lr));
|
|
|
|
lr = (lr_write_t *)&itx->itx_lr;
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
wr_state = WR_NEED_COPY;
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
OpenZFS 7578 - Fix/improve some aspects of ZIL writing
- After some ZIL changes 6 years ago zil_slog_limit got partially broken
due to zl_itx_list_sz not updated when async itx'es upgraded to sync.
Actually because of other changes about that time zl_itx_list_sz is not
really required to implement the functionality, so this patch removes
some unneeded broken code and variables.
- Original idea of zil_slog_limit was to reduce chance of SLOG abuse by
single heavy logger, that increased latency for other (more latency critical)
loggers, by pushing heavy log out into the main pool instead of SLOG. Beside
huge latency increase for heavy writers, this implementation caused double
write of all data, since the log records were explicitly prepared for SLOG.
Since we now have I/O scheduler, I've found it can be much more efficient
to reduce priority of heavy logger SLOG writes from ZIO_PRIORITY_SYNC_WRITE
to ZIO_PRIORITY_ASYNC_WRITE, while still leave them on SLOG.
- Existing ZIL implementation had problem with space efficiency when it
has to write large chunks of data into log blocks of limited size. In some
cases efficiency stopped to almost as low as 50%. In case of ZIL stored on
spinning rust, that also reduced log write speed in half, since head had to
uselessly fly over allocated but not written areas. This change improves
the situation by offloading problematic operations from z*_log_write() to
zil_lwb_commit(), which knows real situation of log blocks allocation and
can split large requests into pieces much more efficiently. Also as side
effect it removes one of two data copy operations done by ZIL code WR_COPIED
case.
- While there, untangle and unify code of z*_log_write() functions.
Also zfs_log_write() alike to zvol_log_write() can now handle writes crossing
block boundary, that may also improve efficiency if ZPL is made to do that.
Sponsored by: iXsystems, Inc.
Authored by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Andriy Gapon <avg@FreeBSD.org>
Reviewed by: Steven Hartland <steven.hartland@multiplay.co.uk>
Reviewed by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Richard Yao <ryao@gentoo.org>
Ported-by: Giuseppe Di Natale <dinatale2@llnl.gov>
OpenZFS-issue: https://www.illumos.org/issues/7578
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/aeb13ac
Closes #6191
2017-06-09 19:15:37 +03:00
|
|
|
itx->itx_wr_state = wr_state;
|
2010-08-26 22:45:02 +04:00
|
|
|
lr->lr_foid = ZVOL_OBJ;
|
|
|
|
lr->lr_offset = offset;
|
|
|
|
lr->lr_length = len;
|
|
|
|
lr->lr_blkoff = 0;
|
|
|
|
BP_ZERO(&lr->lr_blkptr);
|
|
|
|
|
|
|
|
itx->itx_private = zv;
|
|
|
|
itx->itx_sync = sync;
|
|
|
|
|
|
|
|
(void) zil_itx_assign(zilog, itx, tx);
|
|
|
|
|
|
|
|
offset += len;
|
|
|
|
size -= len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-02 16:01:14 +03:00
|
|
|
/*
|
|
|
|
* Log a DKIOCFREE/free-long-range to the ZIL with TX_TRUNCATE.
|
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
void
|
2015-08-02 16:01:14 +03:00
|
|
|
zvol_log_truncate(zvol_state_t *zv, dmu_tx_t *tx, uint64_t off, uint64_t len,
|
|
|
|
boolean_t sync)
|
|
|
|
{
|
|
|
|
itx_t *itx;
|
|
|
|
lr_truncate_t *lr;
|
|
|
|
zilog_t *zilog = zv->zv_zilog;
|
|
|
|
|
|
|
|
if (zil_replaying(zilog, tx))
|
|
|
|
return;
|
|
|
|
|
|
|
|
itx = zil_itx_create(TX_TRUNCATE, sizeof (*lr));
|
|
|
|
lr = (lr_truncate_t *)&itx->itx_lr;
|
|
|
|
lr->lr_foid = ZVOL_OBJ;
|
|
|
|
lr->lr_offset = off;
|
|
|
|
lr->lr_length = len;
|
|
|
|
|
|
|
|
itx->itx_sync = sync;
|
|
|
|
zil_itx_assign(zilog, itx, tx);
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2019-05-05 02:39:10 +03:00
|
|
|
/* ARGSUSED */
|
|
|
|
static void
|
|
|
|
zvol_get_done(zgd_t *zgd, int error)
|
|
|
|
{
|
|
|
|
if (zgd->zgd_db)
|
|
|
|
dmu_buf_rele(zgd->zgd_db, zgd);
|
|
|
|
|
2019-10-04 01:54:29 +03:00
|
|
|
zfs_rangelock_exit(zgd->zgd_lr);
|
2019-05-05 02:39:10 +03:00
|
|
|
|
|
|
|
kmem_free(zgd, sizeof (zgd_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get data to generate a TX_WRITE intent log record.
|
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
int
|
2019-05-05 02:39:10 +03:00
|
|
|
zvol_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
|
|
|
|
{
|
|
|
|
zvol_state_t *zv = arg;
|
|
|
|
uint64_t offset = lr->lr_offset;
|
|
|
|
uint64_t size = lr->lr_length;
|
|
|
|
dmu_buf_t *db;
|
|
|
|
zgd_t *zgd;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
ASSERT3P(lwb, !=, NULL);
|
|
|
|
ASSERT3P(zio, !=, NULL);
|
|
|
|
ASSERT3U(size, !=, 0);
|
|
|
|
|
|
|
|
zgd = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_SLEEP);
|
|
|
|
zgd->zgd_lwb = lwb;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (buf != NULL) { /* immediate write */
|
2019-10-04 01:54:29 +03:00
|
|
|
zgd->zgd_lr = zfs_rangelock_enter(&zv->zv_rangelock, offset,
|
|
|
|
size, RL_READER);
|
2019-05-05 02:39:10 +03:00
|
|
|
error = dmu_read_by_dnode(zv->zv_dn, offset, size, buf,
|
|
|
|
DMU_READ_NO_PREFETCH);
|
|
|
|
} else { /* indirect write */
|
|
|
|
/*
|
|
|
|
* Have to lock the whole block to ensure when it's written out
|
|
|
|
* and its checksum is being calculated that no one can change
|
|
|
|
* the data. Contrarily to zfs_get_data we need not re-check
|
|
|
|
* blocksize after we get the lock because it cannot be changed.
|
|
|
|
*/
|
|
|
|
size = zv->zv_volblocksize;
|
|
|
|
offset = P2ALIGN_TYPED(offset, size, uint64_t);
|
2019-10-04 01:54:29 +03:00
|
|
|
zgd->zgd_lr = zfs_rangelock_enter(&zv->zv_rangelock, offset,
|
|
|
|
size, RL_READER);
|
2019-05-05 02:39:10 +03:00
|
|
|
error = dmu_buf_hold_by_dnode(zv->zv_dn, offset, zgd, &db,
|
|
|
|
DMU_READ_NO_PREFETCH);
|
|
|
|
if (error == 0) {
|
|
|
|
blkptr_t *bp = &lr->lr_blkptr;
|
|
|
|
|
|
|
|
zgd->zgd_db = db;
|
|
|
|
zgd->zgd_bp = bp;
|
|
|
|
|
|
|
|
ASSERT(db != NULL);
|
|
|
|
ASSERT(db->db_offset == offset);
|
|
|
|
ASSERT(db->db_size == size);
|
|
|
|
|
|
|
|
error = dmu_sync(zio, lr->lr_common.lrc_txg,
|
|
|
|
zvol_get_done, zgd);
|
|
|
|
|
|
|
|
if (error == 0)
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zvol_get_done(zgd, error);
|
|
|
|
|
|
|
|
return (SET_ERROR(error));
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
/*
|
2016-12-01 00:56:50 +03:00
|
|
|
* The zvol_state_t's are inserted into zvol_state_list and zvol_htable.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
void
|
2016-12-01 00:56:50 +03:00
|
|
|
zvol_insert(zvol_state_t *zv)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2018-06-16 01:05:21 +03:00
|
|
|
ASSERT(RW_WRITE_HELD(&zvol_state_lock));
|
2016-12-01 00:56:50 +03:00
|
|
|
list_insert_head(&zvol_state_list, zv);
|
|
|
|
hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash));
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simply remove the zvol from to list of zvols.
|
|
|
|
*/
|
|
|
|
static void
|
2016-12-01 00:56:50 +03:00
|
|
|
zvol_remove(zvol_state_t *zv)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2018-06-16 01:05:21 +03:00
|
|
|
ASSERT(RW_WRITE_HELD(&zvol_state_lock));
|
2016-12-01 00:56:50 +03:00
|
|
|
list_remove(&zvol_state_list, zv);
|
|
|
|
hlist_del(&zv->zv_hlink);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2017-01-20 00:56:36 +03:00
|
|
|
/*
|
|
|
|
* Setup zv after we just own the zv->objset
|
|
|
|
*/
|
2010-08-26 22:45:02 +04:00
|
|
|
static int
|
2017-01-20 00:56:36 +03:00
|
|
|
zvol_setup_zv(zvol_state_t *zv)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
|
|
|
uint64_t volsize;
|
|
|
|
int error;
|
|
|
|
uint64_t ro;
|
2017-01-20 00:56:36 +03:00
|
|
|
objset_t *os = zv->zv_objset;
|
2015-09-23 19:34:51 +03:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
|
|
|
|
ASSERT(RW_LOCK_HELD(&zv->zv_suspend_lock));
|
2017-06-13 19:03:44 +03:00
|
|
|
|
2019-05-05 02:39:10 +03:00
|
|
|
zv->zv_zilog = NULL;
|
|
|
|
zv->zv_flags &= ~ZVOL_WRITTEN_TO;
|
|
|
|
|
2015-09-23 19:34:51 +03:00
|
|
|
error = dsl_prop_get_integer(zv->zv_name, "readonly", &ro, NULL);
|
|
|
|
if (error)
|
2017-01-20 00:56:36 +03:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
|
|
|
error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize);
|
2015-09-23 19:34:51 +03:00
|
|
|
if (error)
|
2017-01-20 00:56:36 +03:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2017-06-13 19:18:08 +03:00
|
|
|
error = dnode_hold(os, ZVOL_OBJ, FTAG, &zv->zv_dn);
|
2015-09-23 19:34:51 +03:00
|
|
|
if (error)
|
2017-01-20 00:56:36 +03:00
|
|
|
return (SET_ERROR(error));
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_set_capacity(zv, volsize >> 9);
|
2010-08-26 22:45:02 +04:00
|
|
|
zv->zv_volsize = volsize;
|
|
|
|
|
2013-03-03 09:57:39 +04:00
|
|
|
if (ro || dmu_objset_is_snapshot(os) ||
|
|
|
|
!spa_writeable(dmu_objset_spa(os))) {
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_set_disk_ro(zv, 1);
|
2013-01-18 21:44:09 +04:00
|
|
|
zv->zv_flags |= ZVOL_RDONLY;
|
2010-08-26 22:45:02 +04:00
|
|
|
} else {
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_set_disk_ro(zv, 0);
|
2013-01-18 21:44:09 +04:00
|
|
|
zv->zv_flags &= ~ZVOL_RDONLY;
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
2017-01-20 00:56:36 +03:00
|
|
|
return (0);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2017-01-20 00:56:36 +03:00
|
|
|
/*
|
|
|
|
* Shutdown every zv_objset related stuff except zv_objset itself.
|
|
|
|
* The is the reverse of zvol_setup_zv.
|
|
|
|
*/
|
2010-08-26 22:45:02 +04:00
|
|
|
static void
|
2017-01-20 00:56:36 +03:00
|
|
|
zvol_shutdown_zv(zvol_state_t *zv)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2017-06-13 19:03:44 +03:00
|
|
|
ASSERT(MUTEX_HELD(&zv->zv_state_lock) &&
|
|
|
|
RW_LOCK_HELD(&zv->zv_suspend_lock));
|
|
|
|
|
2019-05-05 02:39:10 +03:00
|
|
|
if (zv->zv_flags & ZVOL_WRITTEN_TO) {
|
|
|
|
ASSERT(zv->zv_zilog != NULL);
|
|
|
|
zil_close(zv->zv_zilog);
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
zv->zv_zilog = NULL;
|
2012-08-24 18:12:46 +04:00
|
|
|
|
2017-06-13 19:18:08 +03:00
|
|
|
dnode_rele(zv->zv_dn, FTAG);
|
|
|
|
zv->zv_dn = NULL;
|
2012-08-24 18:12:46 +04:00
|
|
|
|
|
|
|
/*
|
Always wait for txg sync when umounting dataset
Currently, when unmounting a filesystem, ZFS will only wait for
a txg sync if the dataset is dirty and not readonly. However, this
can be problematic in cases where a dataset is remounted readonly
immediately before being unmounted, which often happens when the
system is being shut down. Since encrypted datasets require that
all I/O is completed before the dataset is disowned, this issue
causes problems when write I/Os leak into the txgs after the
dataset is disowned, which can happen when sync=disabled.
While looking into fixes for this issue, it was discovered that
dsl_dataset_is_dirty() does not return B_TRUE when the dataset has
been removed from the txg dirty datasets list, but has not actually
been processed yet. Furthermore, the implementation is comletely
different from dmu_objset_is_dirty(), adding to the confusion.
Rather than relying on this function, this patch forces the umount
code path (and the remount readonly code path) to always perform a
txg sync on read-write datasets and removes the function altogether.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #7753
Closes #7795
2018-08-20 23:42:17 +03:00
|
|
|
* Evict cached data. We must write out any dirty data before
|
|
|
|
* disowning the dataset.
|
2012-08-24 18:12:46 +04:00
|
|
|
*/
|
2019-05-05 02:39:10 +03:00
|
|
|
if (zv->zv_flags & ZVOL_WRITTEN_TO)
|
2012-08-24 18:12:46 +04:00
|
|
|
txg_wait_synced(dmu_objset_pool(zv->zv_objset), 0);
|
|
|
|
(void) dmu_objset_evict_dbufs(zv->zv_objset);
|
2017-01-20 00:56:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* return the proper tag for rollback and recv
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
zvol_tag(zvol_state_t *zv)
|
|
|
|
{
|
|
|
|
ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock));
|
|
|
|
return (zv->zv_open_count > 0 ? zv : NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Suspend the zvol for recv and rollback.
|
|
|
|
*/
|
|
|
|
zvol_state_t *
|
|
|
|
zvol_suspend(const char *name)
|
|
|
|
{
|
|
|
|
zvol_state_t *zv;
|
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
zv = zvol_find_by_name(name, RW_WRITER);
|
|
|
|
|
|
|
|
if (zv == NULL)
|
2017-05-10 20:51:29 +03:00
|
|
|
return (NULL);
|
2012-08-24 18:12:46 +04:00
|
|
|
|
2017-01-20 00:56:36 +03:00
|
|
|
/* block all I/O, release in zvol_resume. */
|
2018-06-16 01:05:21 +03:00
|
|
|
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
|
|
|
|
ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock));
|
2017-01-20 00:56:36 +03:00
|
|
|
|
|
|
|
atomic_inc(&zv->zv_suspend_ref);
|
|
|
|
|
|
|
|
if (zv->zv_open_count > 0)
|
|
|
|
zvol_shutdown_zv(zv);
|
2017-05-10 20:51:29 +03:00
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
/*
|
|
|
|
* do not hold zv_state_lock across suspend/resume to
|
|
|
|
* avoid locking up zvol lookups
|
|
|
|
*/
|
2017-05-10 20:51:29 +03:00
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2017-06-13 19:03:44 +03:00
|
|
|
|
|
|
|
/* zv_suspend_lock is released in zvol_resume() */
|
2017-01-20 00:56:36 +03:00
|
|
|
return (zv);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
zvol_resume(zvol_state_t *zv)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
ASSERT(RW_WRITE_HELD(&zv->zv_suspend_lock));
|
2017-05-27 03:50:25 +03:00
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
mutex_enter(&zv->zv_state_lock);
|
|
|
|
|
2017-01-20 00:56:36 +03:00
|
|
|
if (zv->zv_open_count > 0) {
|
|
|
|
VERIFY0(dmu_objset_hold(zv->zv_name, zv, &zv->zv_objset));
|
|
|
|
VERIFY3P(zv->zv_objset->os_dsl_dataset->ds_owner, ==, zv);
|
|
|
|
VERIFY(dsl_dataset_long_held(zv->zv_objset->os_dsl_dataset));
|
|
|
|
dmu_objset_rele(zv->zv_objset, zv);
|
|
|
|
|
|
|
|
error = zvol_setup_zv(zv);
|
|
|
|
}
|
2017-06-13 19:03:44 +03:00
|
|
|
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
|
|
|
|
2017-01-20 00:56:36 +03:00
|
|
|
rw_exit(&zv->zv_suspend_lock);
|
|
|
|
/*
|
|
|
|
* We need this because we don't hold zvol_state_lock while releasing
|
|
|
|
* zv_suspend_lock. zvol_remove_minors_impl thus cannot check
|
|
|
|
* zv_suspend_lock to determine it is safe to free because rwlock is
|
|
|
|
* not inherent atomic.
|
|
|
|
*/
|
|
|
|
atomic_dec(&zv->zv_suspend_ref);
|
|
|
|
|
|
|
|
return (SET_ERROR(error));
|
|
|
|
}
|
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
int
|
2018-02-21 03:27:31 +03:00
|
|
|
zvol_first_open(zvol_state_t *zv, boolean_t readonly)
|
2017-01-20 00:56:36 +03:00
|
|
|
{
|
|
|
|
objset_t *os;
|
2017-05-11 23:40:33 +03:00
|
|
|
int error, locked = 0;
|
2018-02-21 03:27:31 +03:00
|
|
|
boolean_t ro;
|
2017-05-11 23:40:33 +03:00
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
ASSERT(RW_READ_HELD(&zv->zv_suspend_lock));
|
|
|
|
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
|
|
|
|
|
2017-05-11 23:40:33 +03:00
|
|
|
/*
|
|
|
|
* In all other cases the spa_namespace_lock is taken before the
|
|
|
|
* bdev->bd_mutex lock. But in this case the Linux __blkdev_get()
|
|
|
|
* function calls fops->open() with the bdev->bd_mutex lock held.
|
|
|
|
* This deadlock can be easily observed with zvols used as vdevs.
|
|
|
|
*
|
|
|
|
* To avoid a potential lock inversion deadlock we preemptively
|
|
|
|
* try to take the spa_namespace_lock(). Normally it will not
|
|
|
|
* be contended and this is safe because spa_open_common() handles
|
|
|
|
* the case where the caller already holds the spa_namespace_lock.
|
|
|
|
*
|
|
|
|
* When it is contended we risk a lock inversion if we were to
|
|
|
|
* block waiting for the lock. Luckily, the __blkdev_get()
|
|
|
|
* function allows us to return -ERESTARTSYS which will result in
|
|
|
|
* bdev->bd_mutex being dropped, reacquired, and fops->open() being
|
|
|
|
* called again. This process can be repeated safely until both
|
|
|
|
* locks are acquired.
|
|
|
|
*/
|
|
|
|
if (!mutex_owned(&spa_namespace_lock)) {
|
|
|
|
locked = mutex_tryenter(&spa_namespace_lock);
|
|
|
|
if (!locked)
|
2019-09-25 19:20:30 +03:00
|
|
|
return (SET_ERROR(EINTR));
|
2017-05-11 23:40:33 +03:00
|
|
|
}
|
2017-01-20 00:56:36 +03:00
|
|
|
|
2018-02-21 03:27:31 +03:00
|
|
|
ro = (readonly || (strchr(zv->zv_name, '@') != NULL));
|
|
|
|
error = dmu_objset_own(zv->zv_name, DMU_OST_ZVOL, ro, B_TRUE, zv, &os);
|
2017-01-20 00:56:36 +03:00
|
|
|
if (error)
|
2017-05-11 23:40:33 +03:00
|
|
|
goto out_mutex;
|
2017-01-20 00:56:36 +03:00
|
|
|
|
|
|
|
zv->zv_objset = os;
|
|
|
|
|
|
|
|
error = zvol_setup_zv(zv);
|
|
|
|
|
|
|
|
if (error) {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 20:36:48 +03:00
|
|
|
dmu_objset_disown(os, 1, zv);
|
2017-01-20 00:56:36 +03:00
|
|
|
zv->zv_objset = NULL;
|
|
|
|
}
|
|
|
|
|
2017-05-11 23:40:33 +03:00
|
|
|
out_mutex:
|
|
|
|
if (locked)
|
|
|
|
mutex_exit(&spa_namespace_lock);
|
2019-09-25 19:20:30 +03:00
|
|
|
return (SET_ERROR(error));
|
2017-01-20 00:56:36 +03:00
|
|
|
}
|
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
void
|
2017-01-20 00:56:36 +03:00
|
|
|
zvol_last_close(zvol_state_t *zv)
|
|
|
|
{
|
2017-06-13 19:03:44 +03:00
|
|
|
ASSERT(RW_READ_HELD(&zv->zv_suspend_lock));
|
|
|
|
ASSERT(MUTEX_HELD(&zv->zv_state_lock));
|
|
|
|
|
2017-01-20 00:56:36 +03:00
|
|
|
zvol_shutdown_zv(zv);
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 20:36:48 +03:00
|
|
|
dmu_objset_disown(zv->zv_objset, 1, zv);
|
2010-08-26 22:45:02 +04:00
|
|
|
zv->zv_objset = NULL;
|
|
|
|
}
|
|
|
|
|
2016-12-01 00:56:50 +03:00
|
|
|
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;
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 20:36:48 +03:00
|
|
|
job->error = dmu_objset_own(dsname, DMU_OST_ZVOL, B_TRUE, B_TRUE,
|
|
|
|
FTAG, &os);
|
2016-12-01 00:56:50 +03:00
|
|
|
if (job->error == 0) {
|
|
|
|
dmu_prefetch(os, ZVOL_OBJ, 0, 0, 0, ZIO_PRIORITY_SYNC_READ);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 20:36:48 +03:00
|
|
|
dmu_objset_disown(os, B_TRUE, FTAG);
|
2016-12-01 00:56:50 +03:00
|
|
|
}
|
|
|
|
}
|
2014-03-22 13:07:14 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mask errors to continue dmu_objset_find() traversal
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zvol_create_snap_minor_cb(const char *dsname, void *arg)
|
|
|
|
{
|
2016-12-01 00:56:50 +03:00
|
|
|
minors_job_t *j = arg;
|
|
|
|
list_t *minors_list = j->list;
|
|
|
|
const char *name = j->name;
|
2014-03-22 13:07:14 +04:00
|
|
|
|
2015-09-23 19:34:51 +03:00
|
|
|
ASSERT0(MUTEX_HELD(&spa_namespace_lock));
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
/* skip the designated dataset */
|
|
|
|
if (name && strcmp(dsname, name) == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* at this point, the dsname should name a snapshot */
|
|
|
|
if (strchr(dsname, '@') == 0) {
|
|
|
|
dprintf("zvol_create_snap_minor_cb(): "
|
2019-09-03 03:56:41 +03:00
|
|
|
"%s is not a snapshot name\n", dsname);
|
2014-03-22 13:07:14 +04:00
|
|
|
} else {
|
2016-12-01 00:56:50 +03:00
|
|
|
minors_job_t *job;
|
2019-10-10 19:47:06 +03:00
|
|
|
char *n = kmem_strdup(dsname);
|
2016-12-01 00:56:50 +03:00
|
|
|
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);
|
2014-03-22 13:07:14 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mask errors to continue dmu_objset_find() traversal
|
|
|
|
*/
|
2010-08-26 22:45:02 +04:00
|
|
|
static int
|
2013-09-04 16:00:57 +04:00
|
|
|
zvol_create_minors_cb(const char *dsname, void *arg)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2014-03-22 13:07:14 +04:00
|
|
|
uint64_t snapdev;
|
|
|
|
int error;
|
2016-12-01 00:56:50 +03:00
|
|
|
list_t *minors_list = arg;
|
2014-03-22 13:07:14 +04:00
|
|
|
|
2015-09-23 19:34:51 +03:00
|
|
|
ASSERT0(MUTEX_HELD(&spa_namespace_lock));
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
error = dsl_prop_get_integer(dsname, "snapdev", &snapdev, NULL);
|
|
|
|
if (error)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given the name and the 'snapdev' property, create device minor nodes
|
|
|
|
* with the linkages to zvols/snapshots as needed.
|
|
|
|
* If the name represents a zvol, create a minor node for the zvol, then
|
|
|
|
* check if its snapshots are 'visible', and if so, iterate over the
|
|
|
|
* snapshots and create device minor nodes for those.
|
|
|
|
*/
|
|
|
|
if (strchr(dsname, '@') == 0) {
|
2016-12-01 00:56:50 +03:00
|
|
|
minors_job_t *job;
|
2019-10-10 19:47:06 +03:00
|
|
|
char *n = kmem_strdup(dsname);
|
2016-12-01 00:56:50 +03:00
|
|
|
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) {
|
2014-03-22 13:07:14 +04:00
|
|
|
/*
|
|
|
|
* traverse snapshots only, do not traverse children,
|
|
|
|
* and skip the 'dsname'
|
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
error = dmu_objset_find(dsname,
|
2016-12-01 00:56:50 +03:00
|
|
|
zvol_create_snap_minor_cb, (void *)job,
|
2014-03-22 13:07:14 +04:00
|
|
|
DS_FIND_SNAPSHOTS);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dprintf("zvol_create_minors_cb(): %s is not a zvol name\n",
|
2016-12-12 21:46:26 +03:00
|
|
|
dsname);
|
2014-03-22 13:07:14 +04:00
|
|
|
}
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2011-02-16 20:40:29 +03:00
|
|
|
return (0);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-03-22 13:07:14 +04:00
|
|
|
* Create minors for the specified dataset, including children and snapshots.
|
|
|
|
* Pay attention to the 'snapdev' property and iterate over the snapshots
|
|
|
|
* only if they are 'visible'. This approach allows one to assure that the
|
|
|
|
* snapshot metadata is read from disk only if it is needed.
|
|
|
|
*
|
|
|
|
* The name can represent a dataset to be recursively scanned for zvols and
|
|
|
|
* their snapshots, or a single zvol snapshot. If the name represents a
|
|
|
|
* dataset, the scan is performed in two nested stages:
|
|
|
|
* - scan the dataset for zvols, and
|
|
|
|
* - for each zvol, create a minor node, then check if the zvol's snapshots
|
|
|
|
* are 'visible', and only then iterate over the snapshots if needed
|
|
|
|
*
|
2017-01-03 20:31:18 +03:00
|
|
|
* If the name represents a snapshot, a check is performed if the snapshot is
|
2014-03-22 13:07:14 +04:00
|
|
|
* 'visible' (which also verifies that the parent is a zvol), and if so,
|
|
|
|
* a minor node for that snapshot is created.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
2014-03-22 13:07:14 +04:00
|
|
|
static int
|
|
|
|
zvol_create_minors_impl(const char *name)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
|
|
|
int error = 0;
|
2016-02-16 22:52:55 +03:00
|
|
|
fstrans_cookie_t cookie;
|
2014-03-22 13:07:14 +04:00
|
|
|
char *atp, *parent;
|
2016-12-01 00:56:50 +03:00
|
|
|
list_t minors_list;
|
|
|
|
minors_job_t *job;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2016-02-16 22:52:55 +03:00
|
|
|
if (zvol_inhibit_dev)
|
|
|
|
return (0);
|
|
|
|
|
2016-12-01 00:56:50 +03:00
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
parent = kmem_alloc(MAXPATHLEN, KM_SLEEP);
|
|
|
|
(void) strlcpy(parent, name, MAXPATHLEN);
|
|
|
|
|
|
|
|
if ((atp = strrchr(parent, '@')) != NULL) {
|
|
|
|
uint64_t snapdev;
|
|
|
|
|
|
|
|
*atp = '\0';
|
|
|
|
error = dsl_prop_get_integer(parent, "snapdev",
|
|
|
|
&snapdev, NULL);
|
|
|
|
|
|
|
|
if (error == 0 && snapdev == ZFS_SNAPDEV_VISIBLE)
|
2019-09-25 19:20:30 +03:00
|
|
|
error = ops->zv_create_minor(name);
|
2014-03-22 13:07:14 +04:00
|
|
|
} else {
|
|
|
|
cookie = spl_fstrans_mark();
|
|
|
|
error = dmu_objset_find(parent, zvol_create_minors_cb,
|
2016-12-01 00:56:50 +03:00
|
|
|
&minors_list, DS_FIND_CHILDREN);
|
2014-03-22 13:07:14 +04:00
|
|
|
spl_fstrans_unmark(cookie);
|
|
|
|
}
|
|
|
|
|
|
|
|
kmem_free(parent, MAXPATHLEN);
|
2016-12-01 00:56:50 +03:00
|
|
|
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)
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_create_minor(job->name);
|
2019-10-10 19:47:06 +03:00
|
|
|
kmem_strfree(job->name);
|
2016-12-01 00:56:50 +03:00
|
|
|
kmem_free(job, sizeof (minors_job_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
list_destroy(&minors_list);
|
2013-12-07 02:20:22 +04:00
|
|
|
|
|
|
|
return (SET_ERROR(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove minors for specified dataset including children and snapshots.
|
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
|
|
|
|
void
|
2014-03-22 13:07:14 +04:00
|
|
|
zvol_remove_minors_impl(const char *name)
|
2013-12-07 02:20:22 +04:00
|
|
|
{
|
|
|
|
zvol_state_t *zv, *zv_next;
|
|
|
|
int namelen = ((name) ? strlen(name) : 0);
|
Timeout waiting for ZVOL device to be created
We've seen cases where after creating a ZVOL, the ZVOL device node in
"/dev" isn't generated after 20 seconds of waiting, which is the point
at which our applications gives up on waiting and reports an error.
The workload when this occurs is to "refresh" 400+ ZVOLs roughly at the
same time, based on a policy set by the user. This refresh operation
will destroy the ZVOL, and re-create it based on a snapshot.
When this occurs, we see many hundreds of entries on the "z_zvol" taskq
(based on inspection of the /proc/spl/taskq-all file). Many of the
entries on the taskq end up in the "zvol_remove_minors_impl" function,
and I've measured the latency of that function:
Function = zvol_remove_minors_impl
msecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 1 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 0 | |
64 -> 127 : 1 | |
128 -> 255 : 45 |****************************************|
256 -> 511 : 5 |**** |
That data is from a 10 second sample, using the BCC "funclatency" tool.
As we can see, in this 10 second sample, most calls took 128ms at a
minimum. Thus, some basic math tells us that in any 20 second interval,
we could only process at most about 150 removals, which is much less
than the 400+ that'll occur based on the workload.
As a result of this, and since all ZVOL minor operations will go through
the single threaded "z_zvol" taskq, the latency for creating a single
ZVOL device can be unreasonably large due to other ZVOL activity on the
system. In our case, it's large enough to cause the application to
generate an error and fail the operation.
When profiling the "zvol_remove_minors_impl" function, I saw that most
of the time in the function was spent off-cpu, blocked in the function
"taskq_wait_outstanding". How this works, is "zvol_remove_minors_impl"
will dispatch calls to "zvol_free" using the "system_taskq", and then
the "taskq_wait_outstanding" function is used to wait for all of those
dispatched calls to occur before "zvol_remove_minors_impl" will return.
As far as I can tell, "zvol_remove_minors_impl" doesn't necessarily have
to wait for all calls to "zvol_free" to occur before it returns. Thus,
this change removes the call to "taskq_wait_oustanding", so that calls
to "zvol_free" don't affect the latency of "zvol_remove_minors_impl".
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: John Gallagher <john.gallagher@delphix.com>
Signed-off-by: Prakash Surya <prakash.surya@delphix.com>
Closes #9380
2019-10-01 22:33:12 +03:00
|
|
|
taskqid_t t;
|
2017-05-10 20:51:29 +03:00
|
|
|
list_t free_list;
|
2013-12-07 02:20:22 +04:00
|
|
|
|
2012-06-02 05:49:10 +04:00
|
|
|
if (zvol_inhibit_dev)
|
2013-12-07 02:20:22 +04:00
|
|
|
return;
|
2012-06-02 05:49:10 +04:00
|
|
|
|
2017-05-10 20:51:29 +03:00
|
|
|
list_create(&free_list, sizeof (zvol_state_t),
|
|
|
|
offsetof(zvol_state_t, zv_next));
|
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_enter(&zvol_state_lock, RW_WRITER);
|
2013-12-07 02:20:22 +04:00
|
|
|
|
|
|
|
for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) {
|
|
|
|
zv_next = list_next(&zvol_state_list, zv);
|
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
mutex_enter(&zv->zv_state_lock);
|
2013-12-07 02:20:22 +04:00
|
|
|
if (name == NULL || strcmp(zv->zv_name, name) == 0 ||
|
|
|
|
(strncmp(zv->zv_name, name, namelen) == 0 &&
|
2016-02-16 22:52:55 +03:00
|
|
|
(zv->zv_name[namelen] == '/' ||
|
|
|
|
zv->zv_name[namelen] == '@'))) {
|
2017-05-10 20:51:29 +03:00
|
|
|
/*
|
2017-06-13 19:03:44 +03:00
|
|
|
* By holding zv_state_lock here, we guarantee that no
|
2017-05-10 20:51:29 +03:00
|
|
|
* one is currently using this zv
|
|
|
|
*/
|
2017-06-15 21:08:45 +03:00
|
|
|
|
|
|
|
/* If in use, leave alone */
|
|
|
|
if (zv->zv_open_count > 0 ||
|
|
|
|
atomic_read(&zv->zv_suspend_ref)) {
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-12-07 02:20:22 +04:00
|
|
|
zvol_remove(zv);
|
2016-12-01 00:56:50 +03:00
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
/*
|
2018-06-16 01:05:21 +03:00
|
|
|
* Cleared while holding zvol_state_lock as a writer
|
|
|
|
* which will prevent zvol_open() from opening it.
|
2017-06-13 19:03:44 +03:00
|
|
|
*/
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_clear_private(zv);
|
2016-12-01 00:56:50 +03:00
|
|
|
|
2017-06-15 21:08:45 +03:00
|
|
|
/* Drop zv_state_lock before zvol_free() */
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
/* Try parallel zv_free, if failed do it in place */
|
2019-09-25 19:20:30 +03:00
|
|
|
t = taskq_dispatch(system_taskq,
|
|
|
|
(task_func_t *)ops->zv_free, zv, TQ_SLEEP);
|
2016-12-01 00:56:50 +03:00
|
|
|
if (t == TASKQID_INVALID)
|
2017-05-10 20:51:29 +03:00
|
|
|
list_insert_head(&free_list, zv);
|
2017-06-13 19:03:44 +03:00
|
|
|
} else {
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
}
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_exit(&zvol_state_lock);
|
2017-05-10 20:51:29 +03:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
/* Drop zvol_state_lock before calling zvol_free() */
|
2017-05-10 20:51:29 +03:00
|
|
|
while ((zv = list_head(&free_list)) != NULL) {
|
|
|
|
list_remove(&free_list, zv);
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_free(zv);
|
2017-05-10 20:51:29 +03:00
|
|
|
}
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2017-07-12 23:05:37 +03:00
|
|
|
/* Remove minor for this specific volume only */
|
2014-03-22 13:07:14 +04:00
|
|
|
static void
|
|
|
|
zvol_remove_minor_impl(const char *name)
|
|
|
|
{
|
2017-06-02 17:17:00 +03:00
|
|
|
zvol_state_t *zv = NULL, *zv_next;
|
2014-03-22 13:07:14 +04:00
|
|
|
|
|
|
|
if (zvol_inhibit_dev)
|
|
|
|
return;
|
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_enter(&zvol_state_lock, RW_WRITER);
|
2014-03-22 13:07:14 +04:00
|
|
|
|
|
|
|
for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) {
|
|
|
|
zv_next = list_next(&zvol_state_list, zv);
|
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
mutex_enter(&zv->zv_state_lock);
|
2014-03-22 13:07:14 +04:00
|
|
|
if (strcmp(zv->zv_name, name) == 0) {
|
2017-05-10 20:51:29 +03:00
|
|
|
/*
|
2017-06-13 19:03:44 +03:00
|
|
|
* By holding zv_state_lock here, we guarantee that no
|
2017-05-10 20:51:29 +03:00
|
|
|
* one is currently using this zv
|
|
|
|
*/
|
2017-06-15 21:08:45 +03:00
|
|
|
|
|
|
|
/* If in use, leave alone */
|
|
|
|
if (zv->zv_open_count > 0 ||
|
|
|
|
atomic_read(&zv->zv_suspend_ref)) {
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
|
|
|
continue;
|
|
|
|
}
|
2014-03-22 13:07:14 +04:00
|
|
|
zvol_remove(zv);
|
2017-06-15 21:08:45 +03:00
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_clear_private(zv);
|
2017-06-15 21:08:45 +03:00
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2014-03-22 13:07:14 +04:00
|
|
|
break;
|
2017-06-13 19:03:44 +03:00
|
|
|
} else {
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2014-03-22 13:07:14 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-02 17:17:00 +03:00
|
|
|
/* Drop zvol_state_lock before calling zvol_free() */
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_exit(&zvol_state_lock);
|
2017-05-10 20:51:29 +03:00
|
|
|
|
2017-06-02 17:17:00 +03:00
|
|
|
if (zv != NULL)
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_free(zv);
|
2014-03-22 13:07:14 +04:00
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
/*
|
2013-12-07 02:20:22 +04:00
|
|
|
* Rename minors for specified dataset including children and snapshots.
|
2010-08-26 22:45:02 +04:00
|
|
|
*/
|
2014-03-22 13:07:14 +04:00
|
|
|
static void
|
|
|
|
zvol_rename_minors_impl(const char *oldname, const char *newname)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
|
|
|
zvol_state_t *zv, *zv_next;
|
2013-12-07 02:20:22 +04:00
|
|
|
int oldnamelen, newnamelen;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2012-06-02 05:49:10 +04:00
|
|
|
if (zvol_inhibit_dev)
|
|
|
|
return;
|
|
|
|
|
2013-12-07 02:20:22 +04:00
|
|
|
oldnamelen = strlen(oldname);
|
|
|
|
newnamelen = strlen(newname);
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_enter(&zvol_state_lock, RW_READER);
|
2013-12-07 02:20:22 +04:00
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) {
|
|
|
|
zv_next = list_next(&zvol_state_list, zv);
|
|
|
|
|
2017-06-13 19:03:44 +03:00
|
|
|
mutex_enter(&zv->zv_state_lock);
|
|
|
|
|
2013-12-07 02:20:22 +04:00
|
|
|
if (strcmp(zv->zv_name, oldname) == 0) {
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_rename_minor(zv, newname);
|
2013-12-07 02:20:22 +04:00
|
|
|
} else if (strncmp(zv->zv_name, oldname, oldnamelen) == 0 &&
|
|
|
|
(zv->zv_name[oldnamelen] == '/' ||
|
|
|
|
zv->zv_name[oldnamelen] == '@')) {
|
2017-06-28 20:05:16 +03:00
|
|
|
char *name = kmem_asprintf("%s%c%s", newname,
|
2013-12-07 02:20:22 +04:00
|
|
|
zv->zv_name[oldnamelen],
|
|
|
|
zv->zv_name + oldnamelen + 1);
|
2019-09-25 19:20:30 +03:00
|
|
|
ops->zv_rename_minor(zv, name);
|
2019-10-10 19:47:06 +03:00
|
|
|
kmem_strfree(name);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
2017-06-13 19:03:44 +03:00
|
|
|
|
|
|
|
mutex_exit(&zv->zv_state_lock);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
2013-12-07 02:20:22 +04:00
|
|
|
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_exit(&zvol_state_lock);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
typedef struct zvol_snapdev_cb_arg {
|
|
|
|
uint64_t snapdev;
|
|
|
|
} zvol_snapdev_cb_arg_t;
|
|
|
|
|
2013-02-14 03:11:59 +04:00
|
|
|
static int
|
2017-01-21 00:17:55 +03:00
|
|
|
zvol_set_snapdev_cb(const char *dsname, void *param)
|
|
|
|
{
|
2014-03-22 13:07:14 +04:00
|
|
|
zvol_snapdev_cb_arg_t *arg = param;
|
2013-02-14 03:11:59 +04:00
|
|
|
|
|
|
|
if (strchr(dsname, '@') == NULL)
|
2013-12-07 02:20:22 +04:00
|
|
|
return (0);
|
2013-02-14 03:11:59 +04:00
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
switch (arg->snapdev) {
|
2013-02-14 03:11:59 +04:00
|
|
|
case ZFS_SNAPDEV_VISIBLE:
|
2019-09-25 19:20:30 +03:00
|
|
|
(void) ops->zv_create_minor(dsname);
|
2013-02-14 03:11:59 +04:00
|
|
|
break;
|
|
|
|
case ZFS_SNAPDEV_HIDDEN:
|
2014-03-22 13:07:14 +04:00
|
|
|
(void) zvol_remove_minor_impl(dsname);
|
2013-02-14 03:11:59 +04:00
|
|
|
break;
|
|
|
|
}
|
2013-12-07 02:20:22 +04:00
|
|
|
|
|
|
|
return (0);
|
2013-02-14 03:11:59 +04:00
|
|
|
}
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
static void
|
|
|
|
zvol_set_snapdev_impl(char *name, uint64_t snapdev)
|
|
|
|
{
|
|
|
|
zvol_snapdev_cb_arg_t arg = {snapdev};
|
|
|
|
fstrans_cookie_t cookie = spl_fstrans_mark();
|
|
|
|
/*
|
|
|
|
* The zvol_set_snapdev_sync() sets snapdev appropriately
|
|
|
|
* in the dataset hierarchy. Here, we only scan snapshots.
|
|
|
|
*/
|
|
|
|
dmu_objset_find(name, zvol_set_snapdev_cb, &arg, DS_FIND_SNAPSHOTS);
|
|
|
|
spl_fstrans_unmark(cookie);
|
|
|
|
}
|
|
|
|
|
2017-07-12 23:05:37 +03:00
|
|
|
typedef struct zvol_volmode_cb_arg {
|
|
|
|
uint64_t volmode;
|
|
|
|
} zvol_volmode_cb_arg_t;
|
|
|
|
|
|
|
|
static void
|
|
|
|
zvol_set_volmode_impl(char *name, uint64_t volmode)
|
|
|
|
{
|
|
|
|
fstrans_cookie_t cookie = spl_fstrans_mark();
|
|
|
|
|
|
|
|
if (strchr(name, '@') != NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It's unfortunate we need to remove minors before we create new ones:
|
|
|
|
* this is necessary because our backing gendisk (zvol_state->zv_disk)
|
|
|
|
* coule be different when we set, for instance, volmode from "geom"
|
|
|
|
* to "dev" (or vice versa).
|
|
|
|
* A possible optimization is to modify our consumers so we don't get
|
|
|
|
* called when "volmode" does not change.
|
|
|
|
*/
|
|
|
|
switch (volmode) {
|
|
|
|
case ZFS_VOLMODE_NONE:
|
|
|
|
(void) zvol_remove_minor_impl(name);
|
|
|
|
break;
|
|
|
|
case ZFS_VOLMODE_GEOM:
|
|
|
|
case ZFS_VOLMODE_DEV:
|
|
|
|
(void) zvol_remove_minor_impl(name);
|
2019-09-25 19:20:30 +03:00
|
|
|
(void) ops->zv_create_minor(name);
|
2017-07-12 23:05:37 +03:00
|
|
|
break;
|
|
|
|
case ZFS_VOLMODE_DEFAULT:
|
|
|
|
(void) zvol_remove_minor_impl(name);
|
|
|
|
if (zvol_volmode == ZFS_VOLMODE_NONE)
|
|
|
|
break;
|
|
|
|
else /* if zvol_volmode is invalid defaults to "geom" */
|
2019-09-25 19:20:30 +03:00
|
|
|
(void) ops->zv_create_minor(name);
|
2017-07-12 23:05:37 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
spl_fstrans_unmark(cookie);
|
|
|
|
}
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
static zvol_task_t *
|
|
|
|
zvol_task_alloc(zvol_async_op_t op, const char *name1, const char *name2,
|
2017-07-12 23:05:37 +03:00
|
|
|
uint64_t value)
|
2014-03-22 13:07:14 +04:00
|
|
|
{
|
|
|
|
zvol_task_t *task;
|
|
|
|
char *delim;
|
|
|
|
|
|
|
|
/* Never allow tasks on hidden names. */
|
|
|
|
if (name1[0] == '$')
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
task = kmem_zalloc(sizeof (zvol_task_t), KM_SLEEP);
|
|
|
|
task->op = op;
|
2017-07-12 23:05:37 +03:00
|
|
|
task->value = value;
|
2014-03-22 13:07:14 +04:00
|
|
|
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;
|
|
|
|
case ZVOL_ASYNC_SET_SNAPDEV:
|
2017-07-12 23:05:37 +03:00
|
|
|
zvol_set_snapdev_impl(task->name1, task->value);
|
|
|
|
break;
|
|
|
|
case ZVOL_ASYNC_SET_VOLMODE:
|
|
|
|
zvol_set_volmode_impl(task->name1, task->value);
|
2014-03-22 13:07:14 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
VERIFY(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
zvol_task_free(task);
|
|
|
|
}
|
|
|
|
|
2017-07-12 23:05:37 +03:00
|
|
|
typedef struct zvol_set_prop_int_arg {
|
2014-03-22 13:07:14 +04:00
|
|
|
const char *zsda_name;
|
|
|
|
uint64_t zsda_value;
|
|
|
|
zprop_source_t zsda_source;
|
|
|
|
dmu_tx_t *zsda_tx;
|
2017-07-12 23:05:37 +03:00
|
|
|
} zvol_set_prop_int_arg_t;
|
2014-03-22 13:07:14 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Sanity check the dataset for safe use by the sync task. No additional
|
|
|
|
* conditions are imposed.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zvol_set_snapdev_check(void *arg, dmu_tx_t *tx)
|
|
|
|
{
|
2017-07-12 23:05:37 +03:00
|
|
|
zvol_set_prop_int_arg_t *zsda = arg;
|
2014-03-22 13:07:14 +04:00
|
|
|
dsl_pool_t *dp = dmu_tx_pool(tx);
|
|
|
|
dsl_dir_t *dd;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = dsl_dir_hold(dp, zsda->zsda_name, FTAG, &dd, NULL);
|
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
dsl_dir_rele(dd, FTAG);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2017-06-02 17:17:00 +03:00
|
|
|
/* ARGSUSED */
|
2014-03-22 13:07:14 +04:00
|
|
|
static int
|
|
|
|
zvol_set_snapdev_sync_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
|
|
|
|
{
|
|
|
|
char dsname[MAXNAMELEN];
|
|
|
|
zvol_task_t *task;
|
2017-06-02 17:17:00 +03:00
|
|
|
uint64_t snapdev;
|
2014-03-22 13:07:14 +04:00
|
|
|
|
|
|
|
dsl_dataset_name(ds, dsname);
|
2017-06-02 17:17:00 +03:00
|
|
|
if (dsl_prop_get_int_ds(ds, "snapdev", &snapdev) != 0)
|
|
|
|
return (0);
|
|
|
|
task = zvol_task_alloc(ZVOL_ASYNC_SET_SNAPDEV, dsname, NULL, snapdev);
|
2014-03-22 13:07:14 +04:00
|
|
|
if (task == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
(void) taskq_dispatch(dp->dp_spa->spa_zvol_taskq, zvol_task_cb,
|
2016-12-12 21:46:26 +03:00
|
|
|
task, TQ_SLEEP);
|
2014-03-22 13:07:14 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-06-02 17:17:00 +03:00
|
|
|
* Traverse all child datasets and apply snapdev appropriately.
|
|
|
|
* We call dsl_prop_set_sync_impl() here to set the value only on the toplevel
|
|
|
|
* dataset and read the effective "snapdev" on every child in the callback
|
|
|
|
* function: this is because the value is not guaranteed to be the same in the
|
|
|
|
* whole dataset hierarchy.
|
2014-03-22 13:07:14 +04:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
zvol_set_snapdev_sync(void *arg, dmu_tx_t *tx)
|
|
|
|
{
|
2017-07-12 23:05:37 +03:00
|
|
|
zvol_set_prop_int_arg_t *zsda = arg;
|
2014-03-22 13:07:14 +04:00
|
|
|
dsl_pool_t *dp = dmu_tx_pool(tx);
|
|
|
|
dsl_dir_t *dd;
|
2017-06-02 17:17:00 +03:00
|
|
|
dsl_dataset_t *ds;
|
|
|
|
int error;
|
2014-03-22 13:07:14 +04:00
|
|
|
|
|
|
|
VERIFY0(dsl_dir_hold(dp, zsda->zsda_name, FTAG, &dd, NULL));
|
|
|
|
zsda->zsda_tx = tx;
|
|
|
|
|
2017-06-02 17:17:00 +03:00
|
|
|
error = dsl_dataset_hold(dp, zsda->zsda_name, FTAG, &ds);
|
|
|
|
if (error == 0) {
|
|
|
|
dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_SNAPDEV),
|
|
|
|
zsda->zsda_source, sizeof (zsda->zsda_value), 1,
|
|
|
|
&zsda->zsda_value, zsda->zsda_tx);
|
|
|
|
dsl_dataset_rele(ds, FTAG);
|
|
|
|
}
|
2014-03-22 13:07:14 +04:00
|
|
|
dmu_objset_find_dp(dp, dd->dd_object, zvol_set_snapdev_sync_cb,
|
|
|
|
zsda, DS_FIND_CHILDREN);
|
|
|
|
|
|
|
|
dsl_dir_rele(dd, FTAG);
|
|
|
|
}
|
|
|
|
|
2013-02-14 03:11:59 +04:00
|
|
|
int
|
2014-03-22 13:07:14 +04:00
|
|
|
zvol_set_snapdev(const char *ddname, zprop_source_t source, uint64_t snapdev)
|
|
|
|
{
|
2017-07-12 23:05:37 +03:00
|
|
|
zvol_set_prop_int_arg_t zsda;
|
2016-02-16 22:52:55 +03:00
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
zsda.zsda_name = ddname;
|
|
|
|
zsda.zsda_source = source;
|
|
|
|
zsda.zsda_value = snapdev;
|
2016-02-16 22:52:55 +03:00
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
return (dsl_sync_task(ddname, zvol_set_snapdev_check,
|
|
|
|
zvol_set_snapdev_sync, &zsda, 0, ZFS_SPACE_CHECK_NONE));
|
|
|
|
}
|
|
|
|
|
2017-07-12 23:05:37 +03:00
|
|
|
/*
|
|
|
|
* Sanity check the dataset for safe use by the sync task. No additional
|
|
|
|
* conditions are imposed.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zvol_set_volmode_check(void *arg, dmu_tx_t *tx)
|
|
|
|
{
|
|
|
|
zvol_set_prop_int_arg_t *zsda = arg;
|
|
|
|
dsl_pool_t *dp = dmu_tx_pool(tx);
|
|
|
|
dsl_dir_t *dd;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = dsl_dir_hold(dp, zsda->zsda_name, FTAG, &dd, NULL);
|
|
|
|
if (error != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
dsl_dir_rele(dd, FTAG);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
static int
|
|
|
|
zvol_set_volmode_sync_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg)
|
|
|
|
{
|
|
|
|
char dsname[MAXNAMELEN];
|
|
|
|
zvol_task_t *task;
|
|
|
|
uint64_t volmode;
|
|
|
|
|
|
|
|
dsl_dataset_name(ds, dsname);
|
|
|
|
if (dsl_prop_get_int_ds(ds, "volmode", &volmode) != 0)
|
|
|
|
return (0);
|
|
|
|
task = zvol_task_alloc(ZVOL_ASYNC_SET_VOLMODE, dsname, NULL, volmode);
|
|
|
|
if (task == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
(void) taskq_dispatch(dp->dp_spa->spa_zvol_taskq, zvol_task_cb,
|
|
|
|
task, TQ_SLEEP);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse all child datasets and apply volmode appropriately.
|
|
|
|
* We call dsl_prop_set_sync_impl() here to set the value only on the toplevel
|
|
|
|
* dataset and read the effective "volmode" on every child in the callback
|
|
|
|
* function: this is because the value is not guaranteed to be the same in the
|
|
|
|
* whole dataset hierarchy.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
zvol_set_volmode_sync(void *arg, dmu_tx_t *tx)
|
|
|
|
{
|
|
|
|
zvol_set_prop_int_arg_t *zsda = arg;
|
|
|
|
dsl_pool_t *dp = dmu_tx_pool(tx);
|
|
|
|
dsl_dir_t *dd;
|
|
|
|
dsl_dataset_t *ds;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
VERIFY0(dsl_dir_hold(dp, zsda->zsda_name, FTAG, &dd, NULL));
|
|
|
|
zsda->zsda_tx = tx;
|
|
|
|
|
|
|
|
error = dsl_dataset_hold(dp, zsda->zsda_name, FTAG, &ds);
|
|
|
|
if (error == 0) {
|
|
|
|
dsl_prop_set_sync_impl(ds, zfs_prop_to_name(ZFS_PROP_VOLMODE),
|
|
|
|
zsda->zsda_source, sizeof (zsda->zsda_value), 1,
|
|
|
|
&zsda->zsda_value, zsda->zsda_tx);
|
|
|
|
dsl_dataset_rele(ds, FTAG);
|
|
|
|
}
|
|
|
|
|
|
|
|
dmu_objset_find_dp(dp, dd->dd_object, zvol_set_volmode_sync_cb,
|
|
|
|
zsda, DS_FIND_CHILDREN);
|
|
|
|
|
|
|
|
dsl_dir_rele(dd, FTAG);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
zvol_set_volmode(const char *ddname, zprop_source_t source, uint64_t volmode)
|
|
|
|
{
|
|
|
|
zvol_set_prop_int_arg_t zsda;
|
|
|
|
|
|
|
|
zsda.zsda_name = ddname;
|
|
|
|
zsda.zsda_source = source;
|
|
|
|
zsda.zsda_value = volmode;
|
|
|
|
|
|
|
|
return (dsl_sync_task(ddname, zvol_set_volmode_check,
|
|
|
|
zvol_set_volmode_sync, &zsda, 0, ZFS_SPACE_CHECK_NONE));
|
|
|
|
}
|
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
void
|
|
|
|
zvol_create_minors(spa_t *spa, const char *name, boolean_t async)
|
|
|
|
{
|
|
|
|
zvol_task_t *task;
|
|
|
|
taskqid_t id;
|
|
|
|
|
|
|
|
task = zvol_task_alloc(ZVOL_ASYNC_CREATE_MINORS, name, NULL, ~0ULL);
|
|
|
|
if (task == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
id = taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP);
|
2016-10-29 01:40:14 +03:00
|
|
|
if ((async == B_FALSE) && (id != TASKQID_INVALID))
|
2014-03-22 13:07:14 +04:00
|
|
|
taskq_wait_id(spa->spa_zvol_taskq, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
zvol_remove_minors(spa_t *spa, const char *name, boolean_t async)
|
|
|
|
{
|
|
|
|
zvol_task_t *task;
|
|
|
|
taskqid_t id;
|
|
|
|
|
|
|
|
task = zvol_task_alloc(ZVOL_ASYNC_REMOVE_MINORS, name, NULL, ~0ULL);
|
|
|
|
if (task == NULL)
|
|
|
|
return;
|
2016-02-16 22:52:55 +03:00
|
|
|
|
2014-03-22 13:07:14 +04:00
|
|
|
id = taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP);
|
2016-10-29 01:40:14 +03:00
|
|
|
if ((async == B_FALSE) && (id != TASKQID_INVALID))
|
2014-03-22 13:07:14 +04:00
|
|
|
taskq_wait_id(spa->spa_zvol_taskq, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
zvol_rename_minors(spa_t *spa, const char *name1, const char *name2,
|
|
|
|
boolean_t async)
|
|
|
|
{
|
|
|
|
zvol_task_t *task;
|
|
|
|
taskqid_t id;
|
|
|
|
|
|
|
|
task = zvol_task_alloc(ZVOL_ASYNC_RENAME_MINORS, name1, name2, ~0ULL);
|
|
|
|
if (task == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
id = taskq_dispatch(spa->spa_zvol_taskq, zvol_task_cb, task, TQ_SLEEP);
|
2016-10-29 01:40:14 +03:00
|
|
|
if ((async == B_FALSE) && (id != TASKQID_INVALID))
|
2014-03-22 13:07:14 +04:00
|
|
|
taskq_wait_id(spa->spa_zvol_taskq, id);
|
2013-02-14 03:11:59 +04:00
|
|
|
}
|
|
|
|
|
2019-09-25 19:20:30 +03:00
|
|
|
boolean_t
|
|
|
|
zvol_is_zvol(const char *name)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (ops->zv_is_zvol(name));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
zvol_register_ops(const zvol_platform_ops_t *zvol_ops)
|
|
|
|
{
|
|
|
|
ops = zvol_ops;
|
|
|
|
}
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
int
|
2019-09-25 19:20:30 +03:00
|
|
|
zvol_init_impl(void)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2019-09-25 19:20:30 +03:00
|
|
|
int i;
|
2010-08-26 22:45:02 +04:00
|
|
|
|
2013-07-02 22:59:10 +04:00
|
|
|
list_create(&zvol_state_list, sizeof (zvol_state_t),
|
2013-12-13 01:04:40 +04:00
|
|
|
offsetof(zvol_state_t, zv_next));
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_init(&zvol_state_lock, NULL, RW_DEFAULT, NULL);
|
2017-02-23 03:08:04 +03:00
|
|
|
|
2016-12-01 00:56:50 +03:00
|
|
|
zvol_htable = kmem_alloc(ZVOL_HT_SIZE * sizeof (struct hlist_head),
|
|
|
|
KM_SLEEP);
|
|
|
|
for (i = 0; i < ZVOL_HT_SIZE; i++)
|
|
|
|
INIT_HLIST_HEAD(&zvol_htable[i]);
|
|
|
|
|
2010-08-26 22:45:02 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-09-25 19:20:30 +03:00
|
|
|
zvol_fini_impl(void)
|
2010-08-26 22:45:02 +04:00
|
|
|
{
|
2019-10-25 23:42:54 +03:00
|
|
|
zvol_remove_minors_impl(NULL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The call to "zvol_remove_minors_impl" may dispatch entries to
|
|
|
|
* the system_taskq, but it doesn't wait for those entires to
|
|
|
|
* complete before it returns. Thus, we must wait for all of the
|
|
|
|
* removals to finish, before we can continue.
|
|
|
|
*/
|
|
|
|
taskq_wait_outstanding(system_taskq, 0);
|
|
|
|
|
2016-12-01 00:56:50 +03:00
|
|
|
kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));
|
2010-08-26 22:45:02 +04:00
|
|
|
list_destroy(&zvol_state_list);
|
2018-06-16 01:05:21 +03:00
|
|
|
rw_destroy(&zvol_state_lock);
|
2010-08-26 22:45:02 +04:00
|
|
|
}
|