mirror of
https://github.com/freebsd/freebsd-src
synced 2024-07-23 19:28:36 +00:00
zfs: merge openzfs/zfs@1f940de07
Notable upstream pull request merges: #160381f940de07
L2ARC: Cleanup buffer re-compression #16093c183d164a
Parallel pool import #16094cd3e6b4f4
Add zfetch stats in arcstats #1610335bf25848
Fix: FreeBSD Arm64 does not build currently #161044036b8d02
Refactor dbuf_read() for safer decryption #161109f83eec03
Handle FLUSH errors as "expected" #16117c346068e5
zfs get: add '-t fs' and '-t vol' options Obtained from: OpenZFS OpenZFS commit:1f940de072
This commit is contained in:
commit
0d4ad64077
|
@ -6,5 +6,5 @@ Release: 1
|
|||
Release-Tags: relext
|
||||
License: CDDL
|
||||
Author: OpenZFS
|
||||
Linux-Maximum: 6.7
|
||||
Linux-Maximum: 6.8
|
||||
Linux-Minimum: 3.10
|
||||
|
|
|
@ -157,6 +157,16 @@ cols = {
|
|||
"free": [5, 1024, "ARC free memory"],
|
||||
"avail": [5, 1024, "ARC available memory"],
|
||||
"waste": [5, 1024, "Wasted memory due to round up to pagesize"],
|
||||
"ztotal": [6, 1000, "zfetch total prefetcher calls per second"],
|
||||
"zhits": [5, 1000, "zfetch stream hits per second"],
|
||||
"zahead": [6, 1000, "zfetch hits ahead of streams per second"],
|
||||
"zpast": [5, 1000, "zfetch hits behind streams per second"],
|
||||
"zmisses": [7, 1000, "zfetch stream misses per second"],
|
||||
"zmax": [4, 1000, "zfetch limit reached per second"],
|
||||
"zfuture": [7, 1000, "zfetch stream future per second"],
|
||||
"zstride": [7, 1000, "zfetch stream strides per second"],
|
||||
"zissued": [7, 1000, "zfetch prefetches issued per second"],
|
||||
"zactive": [7, 1000, "zfetch prefetches active per second"],
|
||||
}
|
||||
|
||||
v = {}
|
||||
|
@ -164,6 +174,8 @@ hdr = ["time", "read", "ddread", "ddh%", "dmread", "dmh%", "pread", "ph%",
|
|||
"size", "c", "avail"]
|
||||
xhdr = ["time", "mfu", "mru", "mfug", "mrug", "unc", "eskip", "mtxmis",
|
||||
"dread", "pread", "read"]
|
||||
zhdr = ["time", "ztotal", "zhits", "zahead", "zpast", "zmisses", "zmax",
|
||||
"zfuture", "zstride", "zissued", "zactive"]
|
||||
sint = 1 # Default interval is 1 second
|
||||
count = 1 # Default count is 1
|
||||
hdr_intr = 20 # Print header every 20 lines of output
|
||||
|
@ -206,12 +218,17 @@ elif sys.platform.startswith('linux'):
|
|||
def kstat_update():
|
||||
global kstat
|
||||
|
||||
k = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
|
||||
k1 = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
|
||||
|
||||
if not k:
|
||||
k2 = ["zfetch_" + line.strip() for line in
|
||||
open('/proc/spl/kstat/zfs/zfetchstats')]
|
||||
|
||||
if k1 is None or k2 is None:
|
||||
sys.exit(1)
|
||||
|
||||
del k[0:2]
|
||||
del k1[0:2]
|
||||
del k2[0:2]
|
||||
k = k1 + k2
|
||||
kstat = {}
|
||||
|
||||
for s in k:
|
||||
|
@ -239,6 +256,7 @@ def usage():
|
|||
sys.stderr.write("\t -v : List all possible field headers and definitions"
|
||||
"\n")
|
||||
sys.stderr.write("\t -x : Print extended stats\n")
|
||||
sys.stderr.write("\t -z : Print zfetch stats\n")
|
||||
sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
|
||||
sys.stderr.write("\t -o : Redirect output to the specified file\n")
|
||||
sys.stderr.write("\t -s : Override default field separator with custom "
|
||||
|
@ -357,6 +375,7 @@ def init():
|
|||
global count
|
||||
global hdr
|
||||
global xhdr
|
||||
global zhdr
|
||||
global opfile
|
||||
global sep
|
||||
global out
|
||||
|
@ -368,15 +387,17 @@ def init():
|
|||
xflag = False
|
||||
hflag = False
|
||||
vflag = False
|
||||
zflag = False
|
||||
i = 1
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"axo:hvs:f:p",
|
||||
"axzo:hvs:f:p",
|
||||
[
|
||||
"all",
|
||||
"extended",
|
||||
"zfetch",
|
||||
"outfile",
|
||||
"help",
|
||||
"verbose",
|
||||
|
@ -410,13 +431,15 @@ def init():
|
|||
i += 1
|
||||
if opt in ('-p', '--parsable'):
|
||||
pretty_print = False
|
||||
if opt in ('-z', '--zfetch'):
|
||||
zflag = True
|
||||
i += 1
|
||||
|
||||
argv = sys.argv[i:]
|
||||
sint = int(argv[0]) if argv else sint
|
||||
count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)
|
||||
|
||||
if hflag or (xflag and desired_cols):
|
||||
if hflag or (xflag and zflag) or ((zflag or xflag) and desired_cols):
|
||||
usage()
|
||||
|
||||
if vflag:
|
||||
|
@ -425,6 +448,9 @@ def init():
|
|||
if xflag:
|
||||
hdr = xhdr
|
||||
|
||||
if zflag:
|
||||
hdr = zhdr
|
||||
|
||||
update_hdr_intr()
|
||||
|
||||
# check if L2ARC exists
|
||||
|
@ -569,6 +595,17 @@ def calculate():
|
|||
v["el2mru"] = d["evict_l2_eligible_mru"] // sint
|
||||
v["el2inel"] = d["evict_l2_ineligible"] // sint
|
||||
v["mtxmis"] = d["mutex_miss"] // sint
|
||||
v["ztotal"] = (d["zfetch_hits"] + d["zfetch_future"] + d["zfetch_stride"] +
|
||||
d["zfetch_past"] + d["zfetch_misses"]) // sint
|
||||
v["zhits"] = d["zfetch_hits"] // sint
|
||||
v["zahead"] = (d["zfetch_future"] + d["zfetch_stride"]) // sint
|
||||
v["zpast"] = d["zfetch_past"] // sint
|
||||
v["zmisses"] = d["zfetch_misses"] // sint
|
||||
v["zmax"] = d["zfetch_max_streams"] // sint
|
||||
v["zfuture"] = d["zfetch_future"] // sint
|
||||
v["zstride"] = d["zfetch_stride"] // sint
|
||||
v["zissued"] = d["zfetch_io_issued"] // sint
|
||||
v["zactive"] = d["zfetch_io_active"] // sint
|
||||
|
||||
if l2exist:
|
||||
v["l2hits"] = d["l2_hits"] // sint
|
||||
|
|
|
@ -2146,15 +2146,25 @@ found2:;
|
|||
|
||||
for (char *tok; (tok = strsep(&optarg, ",")); ) {
|
||||
static const char *const type_opts[] = {
|
||||
"filesystem", "volume",
|
||||
"snapshot", "snap",
|
||||
"filesystem",
|
||||
"fs",
|
||||
"volume",
|
||||
"vol",
|
||||
"snapshot",
|
||||
"snap",
|
||||
"bookmark",
|
||||
"all" };
|
||||
"all"
|
||||
};
|
||||
static const int type_types[] = {
|
||||
ZFS_TYPE_FILESYSTEM, ZFS_TYPE_VOLUME,
|
||||
ZFS_TYPE_SNAPSHOT, ZFS_TYPE_SNAPSHOT,
|
||||
ZFS_TYPE_FILESYSTEM,
|
||||
ZFS_TYPE_FILESYSTEM,
|
||||
ZFS_TYPE_VOLUME,
|
||||
ZFS_TYPE_VOLUME,
|
||||
ZFS_TYPE_SNAPSHOT,
|
||||
ZFS_TYPE_SNAPSHOT,
|
||||
ZFS_TYPE_BOOKMARK,
|
||||
ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK };
|
||||
ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(type_opts); ++i)
|
||||
if (strcmp(tok, type_opts[i]) == 0) {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
* Copyright (c) 2024, Klara Inc.
|
||||
* Copyright (c) 2023-2024, Klara Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -310,6 +310,11 @@ usage(void)
|
|||
"\t\tcreate 3 lanes on the device; one lane with a latency\n"
|
||||
"\t\tof 10 ms and two lanes with a 25 ms latency.\n"
|
||||
"\n"
|
||||
"\tzinject -P import|export -s <seconds> pool\n"
|
||||
"\t\tAdd an artificial delay to a future pool import or export,\n"
|
||||
"\t\tsuch that the operation takes a minimum of supplied seconds\n"
|
||||
"\t\tto complete.\n"
|
||||
"\n"
|
||||
"\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
|
||||
"\t\tCause the pool to stop writing blocks yet not\n"
|
||||
"\t\treport errors for a duration. Simulates buggy hardware\n"
|
||||
|
@ -392,8 +397,10 @@ print_data_handler(int id, const char *pool, zinject_record_t *record,
|
|||
{
|
||||
int *count = data;
|
||||
|
||||
if (record->zi_guid != 0 || record->zi_func[0] != '\0')
|
||||
if (record->zi_guid != 0 || record->zi_func[0] != '\0' ||
|
||||
record->zi_duration != 0) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (*count == 0) {
|
||||
(void) printf("%3s %-15s %-6s %-6s %-8s %3s %-4s "
|
||||
|
@ -507,6 +514,33 @@ print_panic_handler(int id, const char *pool, zinject_record_t *record,
|
|||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
print_pool_delay_handler(int id, const char *pool, zinject_record_t *record,
|
||||
void *data)
|
||||
{
|
||||
int *count = data;
|
||||
|
||||
if (record->zi_cmd != ZINJECT_DELAY_IMPORT &&
|
||||
record->zi_cmd != ZINJECT_DELAY_EXPORT) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (*count == 0) {
|
||||
(void) printf("%3s %-19s %-11s %s\n",
|
||||
"ID", "POOL", "DELAY (sec)", "COMMAND");
|
||||
(void) printf("--- ------------------- -----------"
|
||||
" -------\n");
|
||||
}
|
||||
|
||||
*count += 1;
|
||||
|
||||
(void) printf("%3d %-19s %-11llu %s\n",
|
||||
id, pool, (u_longlong_t)record->zi_duration,
|
||||
record->zi_cmd == ZINJECT_DELAY_IMPORT ? "import": "export");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print all registered error handlers. Returns the number of handlers
|
||||
* registered.
|
||||
|
@ -537,6 +571,13 @@ print_all_handlers(void)
|
|||
count = 0;
|
||||
}
|
||||
|
||||
(void) iter_handlers(print_pool_delay_handler, &count);
|
||||
if (count > 0) {
|
||||
total += count;
|
||||
(void) printf("\n");
|
||||
count = 0;
|
||||
}
|
||||
|
||||
(void) iter_handlers(print_panic_handler, &count);
|
||||
|
||||
return (count + total);
|
||||
|
@ -609,9 +650,27 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
|
|||
zc.zc_guid = flags;
|
||||
|
||||
if (zfs_ioctl(g_zfs, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
|
||||
(void) fprintf(stderr, "failed to add handler: %s\n",
|
||||
errno == EDOM ? "block level exceeds max level of object" :
|
||||
strerror(errno));
|
||||
const char *errmsg = strerror(errno);
|
||||
|
||||
switch (errno) {
|
||||
case EDOM:
|
||||
errmsg = "block level exceeds max level of object";
|
||||
break;
|
||||
case EEXIST:
|
||||
if (record->zi_cmd == ZINJECT_DELAY_IMPORT)
|
||||
errmsg = "pool already imported";
|
||||
if (record->zi_cmd == ZINJECT_DELAY_EXPORT)
|
||||
errmsg = "a handler already exists";
|
||||
break;
|
||||
case ENOENT:
|
||||
/* import delay injector running on older zfs module */
|
||||
if (record->zi_cmd == ZINJECT_DELAY_IMPORT)
|
||||
errmsg = "import delay injector not supported";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
(void) fprintf(stderr, "failed to add handler: %s\n", errmsg);
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
@ -636,6 +695,9 @@ register_handler(const char *pool, int flags, zinject_record_t *record,
|
|||
} else if (record->zi_duration < 0) {
|
||||
(void) printf(" txgs: %lld \n",
|
||||
(u_longlong_t)-record->zi_duration);
|
||||
} else if (record->zi_timer > 0) {
|
||||
(void) printf(" timer: %lld ms\n",
|
||||
(u_longlong_t)NSEC2MSEC(record->zi_timer));
|
||||
} else {
|
||||
(void) printf("objset: %llu\n",
|
||||
(u_longlong_t)record->zi_objset);
|
||||
|
@ -834,7 +896,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
":aA:b:C:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:")) != -1) {
|
||||
":aA:b:C:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:P:")) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
flags |= ZINJECT_FLUSH_ARC;
|
||||
|
@ -952,6 +1014,19 @@ main(int argc, char **argv)
|
|||
sizeof (record.zi_func));
|
||||
record.zi_cmd = ZINJECT_PANIC;
|
||||
break;
|
||||
case 'P':
|
||||
if (strcasecmp(optarg, "import") == 0) {
|
||||
record.zi_cmd = ZINJECT_DELAY_IMPORT;
|
||||
} else if (strcasecmp(optarg, "export") == 0) {
|
||||
record.zi_cmd = ZINJECT_DELAY_EXPORT;
|
||||
} else {
|
||||
(void) fprintf(stderr, "invalid command '%s': "
|
||||
"must be 'import' or 'export'\n", optarg);
|
||||
usage();
|
||||
libzfs_fini(g_zfs);
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
|
@ -1033,7 +1108,7 @@ main(int argc, char **argv)
|
|||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (record.zi_duration != 0)
|
||||
if (record.zi_duration != 0 && record.zi_cmd == 0)
|
||||
record.zi_cmd = ZINJECT_IGNORED_WRITES;
|
||||
|
||||
if (cancel != NULL) {
|
||||
|
@ -1179,8 +1254,8 @@ main(int argc, char **argv)
|
|||
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
|
||||
level != 0 || device != NULL || record.zi_freq > 0 ||
|
||||
dvas != 0) {
|
||||
(void) fprintf(stderr, "panic (-p) incompatible with "
|
||||
"other options\n");
|
||||
(void) fprintf(stderr, "%s incompatible with other "
|
||||
"options\n", "import|export delay (-P)");
|
||||
usage();
|
||||
libzfs_fini(g_zfs);
|
||||
return (2);
|
||||
|
@ -1198,6 +1273,28 @@ main(int argc, char **argv)
|
|||
if (argv[1] != NULL)
|
||||
record.zi_type = atoi(argv[1]);
|
||||
dataset[0] = '\0';
|
||||
} else if (record.zi_cmd == ZINJECT_DELAY_IMPORT ||
|
||||
record.zi_cmd == ZINJECT_DELAY_EXPORT) {
|
||||
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
|
||||
level != 0 || device != NULL || record.zi_freq > 0 ||
|
||||
dvas != 0) {
|
||||
(void) fprintf(stderr, "%s incompatible with other "
|
||||
"options\n", "import|export delay (-P)");
|
||||
usage();
|
||||
libzfs_fini(g_zfs);
|
||||
return (2);
|
||||
}
|
||||
|
||||
if (argc != 1 || record.zi_duration <= 0) {
|
||||
(void) fprintf(stderr, "import|export delay (-P) "
|
||||
"injection requires a duration (-s) and a single "
|
||||
"pool name\n");
|
||||
usage();
|
||||
libzfs_fini(g_zfs);
|
||||
return (2);
|
||||
}
|
||||
|
||||
(void) strlcpy(pool, argv[0], sizeof (pool));
|
||||
} else if (record.zi_cmd == ZINJECT_IGNORED_WRITES) {
|
||||
if (raw != NULL || range != NULL || type != TYPE_INVAL ||
|
||||
level != 0 || record.zi_freq > 0 || dvas != 0) {
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <thread_pool.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
@ -3444,10 +3445,10 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
|
|||
ms_status = zpool_enable_datasets(zhp, mntopts, 0);
|
||||
if (ms_status == EZFS_SHAREFAILED) {
|
||||
(void) fprintf(stderr, gettext("Import was "
|
||||
"successful, but unable to share some datasets"));
|
||||
"successful, but unable to share some datasets\n"));
|
||||
} else if (ms_status == EZFS_MOUNTFAILED) {
|
||||
(void) fprintf(stderr, gettext("Import was "
|
||||
"successful, but unable to mount some datasets"));
|
||||
"successful, but unable to mount some datasets\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3455,15 +3456,40 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts,
|
|||
return (ret);
|
||||
}
|
||||
|
||||
typedef struct import_parameters {
|
||||
nvlist_t *ip_config;
|
||||
const char *ip_mntopts;
|
||||
nvlist_t *ip_props;
|
||||
int ip_flags;
|
||||
int *ip_err;
|
||||
} import_parameters_t;
|
||||
|
||||
static void
|
||||
do_import_task(void *arg)
|
||||
{
|
||||
import_parameters_t *ip = arg;
|
||||
*ip->ip_err |= do_import(ip->ip_config, NULL, ip->ip_mntopts,
|
||||
ip->ip_props, ip->ip_flags);
|
||||
free(ip);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
import_pools(nvlist_t *pools, nvlist_t *props, char *mntopts, int flags,
|
||||
char *orig_name, char *new_name,
|
||||
boolean_t do_destroyed, boolean_t pool_specified, boolean_t do_all,
|
||||
importargs_t *import)
|
||||
char *orig_name, char *new_name, importargs_t *import)
|
||||
{
|
||||
nvlist_t *config = NULL;
|
||||
nvlist_t *found_config = NULL;
|
||||
uint64_t pool_state;
|
||||
boolean_t pool_specified = (import->poolname != NULL ||
|
||||
import->guid != 0);
|
||||
|
||||
|
||||
tpool_t *tp = NULL;
|
||||
if (import->do_all) {
|
||||
tp = tpool_create(1, 5 * sysconf(_SC_NPROCESSORS_ONLN),
|
||||
0, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we have a list of import candidate configs. Even if
|
||||
|
@ -3480,9 +3506,11 @@ import_pools(nvlist_t *pools, nvlist_t *props, char *mntopts, int flags,
|
|||
|
||||
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
|
||||
&pool_state) == 0);
|
||||
if (!do_destroyed && pool_state == POOL_STATE_DESTROYED)
|
||||
if (!import->do_destroyed &&
|
||||
pool_state == POOL_STATE_DESTROYED)
|
||||
continue;
|
||||
if (do_destroyed && pool_state != POOL_STATE_DESTROYED)
|
||||
if (import->do_destroyed &&
|
||||
pool_state != POOL_STATE_DESTROYED)
|
||||
continue;
|
||||
|
||||
verify(nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY,
|
||||
|
@ -3491,12 +3519,21 @@ import_pools(nvlist_t *pools, nvlist_t *props, char *mntopts, int flags,
|
|||
if (!pool_specified) {
|
||||
if (first)
|
||||
first = B_FALSE;
|
||||
else if (!do_all)
|
||||
else if (!import->do_all)
|
||||
(void) fputc('\n', stdout);
|
||||
|
||||
if (do_all) {
|
||||
err |= do_import(config, NULL, mntopts,
|
||||
props, flags);
|
||||
if (import->do_all) {
|
||||
import_parameters_t *ip = safe_malloc(
|
||||
sizeof (import_parameters_t));
|
||||
|
||||
ip->ip_config = config;
|
||||
ip->ip_mntopts = mntopts;
|
||||
ip->ip_props = props;
|
||||
ip->ip_flags = flags;
|
||||
ip->ip_err = &err;
|
||||
|
||||
(void) tpool_dispatch(tp, do_import_task,
|
||||
(void *)ip);
|
||||
} else {
|
||||
/*
|
||||
* If we're importing from cachefile, then
|
||||
|
@ -3544,6 +3581,10 @@ import_pools(nvlist_t *pools, nvlist_t *props, char *mntopts, int flags,
|
|||
found_config = config;
|
||||
}
|
||||
}
|
||||
if (import->do_all) {
|
||||
tpool_wait(tp);
|
||||
tpool_destroy(tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we were searching for a specific pool, verify that we found a
|
||||
|
@ -3773,7 +3814,6 @@ zpool_do_import(int argc, char **argv)
|
|||
boolean_t xtreme_rewind = B_FALSE;
|
||||
boolean_t do_scan = B_FALSE;
|
||||
boolean_t pool_exists = B_FALSE;
|
||||
boolean_t pool_specified = B_FALSE;
|
||||
uint64_t txg = -1ULL;
|
||||
char *cachefile = NULL;
|
||||
importargs_t idata = { 0 };
|
||||
|
@ -3972,7 +4012,6 @@ zpool_do_import(int argc, char **argv)
|
|||
searchname = argv[0];
|
||||
searchguid = 0;
|
||||
}
|
||||
pool_specified = B_TRUE;
|
||||
|
||||
/*
|
||||
* User specified a name or guid. Ensure it's unique.
|
||||
|
@ -4005,6 +4044,8 @@ zpool_do_import(int argc, char **argv)
|
|||
idata.cachefile = cachefile;
|
||||
idata.scan = do_scan;
|
||||
idata.policy = policy;
|
||||
idata.do_destroyed = do_destroyed;
|
||||
idata.do_all = do_all;
|
||||
|
||||
libpc_handle_t lpch = {
|
||||
.lpc_lib_handle = g_zfs,
|
||||
|
@ -4047,9 +4088,7 @@ zpool_do_import(int argc, char **argv)
|
|||
}
|
||||
|
||||
err = import_pools(pools, props, mntopts, flags,
|
||||
argc >= 1 ? argv[0] : NULL,
|
||||
argc >= 2 ? argv[1] : NULL,
|
||||
do_destroyed, pool_specified, do_all, &idata);
|
||||
argc >= 1 ? argv[0] : NULL, argc >= 2 ? argv[1] : NULL, &idata);
|
||||
|
||||
/*
|
||||
* If we're using the cachefile and we failed to import, then
|
||||
|
@ -4070,9 +4109,8 @@ zpool_do_import(int argc, char **argv)
|
|||
pools = zpool_search_import(&lpch, &idata);
|
||||
|
||||
err = import_pools(pools, props, mntopts, flags,
|
||||
argc >= 1 ? argv[0] : NULL,
|
||||
argc >= 2 ? argv[1] : NULL,
|
||||
do_destroyed, pool_specified, do_all, &idata);
|
||||
argc >= 1 ? argv[0] : NULL, argc >= 2 ? argv[1] : NULL,
|
||||
&idata);
|
||||
}
|
||||
|
||||
error:
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2018, 2024 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZUTIL_H
|
||||
|
@ -79,6 +79,8 @@ typedef struct importargs {
|
|||
boolean_t can_be_active; /* can the pool be active? */
|
||||
boolean_t scan; /* prefer scanning to libblkid cache */
|
||||
nvlist_t *policy; /* load policy (max txg, rewind, etc.) */
|
||||
boolean_t do_destroyed;
|
||||
boolean_t do_all;
|
||||
} importargs_t;
|
||||
|
||||
typedef struct libpc_handle {
|
||||
|
|
|
@ -833,6 +833,8 @@ void spa_select_allocator(zio_t *zio);
|
|||
|
||||
/* spa namespace global mutex */
|
||||
extern kmutex_t spa_namespace_lock;
|
||||
extern avl_tree_t spa_namespace_avl;
|
||||
extern kcondvar_t spa_namespace_cv;
|
||||
|
||||
/*
|
||||
* SPA configuration functions in spa_config.c
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2019 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2024 by Delphix. All rights reserved.
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||
* Copyright 2013 Saso Kiselkov. All rights reserved.
|
||||
|
@ -237,6 +237,7 @@ struct spa {
|
|||
dsl_pool_t *spa_dsl_pool;
|
||||
boolean_t spa_is_initializing; /* true while opening pool */
|
||||
boolean_t spa_is_exporting; /* true while exporting pool */
|
||||
kthread_t *spa_load_thread; /* loading, no namespace lock */
|
||||
metaslab_class_t *spa_normal_class; /* normal data class */
|
||||
metaslab_class_t *spa_log_class; /* intent log data class */
|
||||
metaslab_class_t *spa_embedded_log_class; /* log on normal vdevs */
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2020 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, 2024 by Delphix. All rights reserved.
|
||||
* Copyright 2016 RackTop Systems.
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
*/
|
||||
|
@ -454,6 +454,8 @@ typedef enum zinject_type {
|
|||
ZINJECT_PANIC,
|
||||
ZINJECT_DELAY_IO,
|
||||
ZINJECT_DECRYPT_FAULT,
|
||||
ZINJECT_DELAY_IMPORT,
|
||||
ZINJECT_DELAY_EXPORT,
|
||||
} zinject_type_t;
|
||||
|
||||
typedef struct zfs_share {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2012, 2020 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, 2024 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
|
||||
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
|
||||
* Copyright 2016 Toomas Soome <tsoome@me.com>
|
||||
|
@ -686,6 +686,8 @@ extern int zio_handle_device_injections(vdev_t *vd, zio_t *zio, int err1,
|
|||
extern int zio_handle_label_injection(zio_t *zio, int error);
|
||||
extern void zio_handle_ignored_writes(zio_t *zio);
|
||||
extern hrtime_t zio_handle_io_delay(zio_t *zio);
|
||||
extern void zio_handle_import_delay(spa_t *spa, hrtime_t elapsed);
|
||||
extern void zio_handle_export_delay(spa_t *spa, hrtime_t elapsed);
|
||||
|
||||
/*
|
||||
* Checksum ereport functions
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
.\" Copyright 2018 Nexenta Systems, Inc.
|
||||
.\" Copyright 2019 Joyent, Inc.
|
||||
.\"
|
||||
.Dd March 16, 2022
|
||||
.Dd April 20, 2024
|
||||
.Dt ZFS-SET 8
|
||||
.Os
|
||||
.
|
||||
|
@ -158,6 +158,15 @@ A comma-separated list of types to display, where
|
|||
.Ar type
|
||||
is one of
|
||||
.Sy filesystem , snapshot , volume , bookmark , No or Sy all .
|
||||
.Sy fs ,
|
||||
.Sy snap ,
|
||||
or
|
||||
.Sy vol
|
||||
can be used as aliases for
|
||||
.Sy filesystem ,
|
||||
.Sy snapshot ,
|
||||
or
|
||||
.Sy volume .
|
||||
.El
|
||||
.It Xo
|
||||
.Nm zfs
|
||||
|
|
|
@ -129,6 +129,14 @@ Force a vdev error.
|
|||
.
|
||||
.It Xo
|
||||
.Nm zinject
|
||||
.Fl i Ar seconds
|
||||
.Ar pool
|
||||
.Xc
|
||||
Add an artificial delay during the future import of a pool.
|
||||
This injector is automatically cleared after the import is finished.
|
||||
.
|
||||
.It Xo
|
||||
.Nm zinject
|
||||
.Fl I
|
||||
.Op Fl s Ar seconds Ns | Ns Fl g Ar txgs
|
||||
.Ar pool
|
||||
|
|
|
@ -1015,10 +1015,50 @@ abd_cache_reap_now(void)
|
|||
}
|
||||
|
||||
#if defined(_KERNEL)
|
||||
|
||||
/*
|
||||
* Yield the next page struct and data offset and size within it, without
|
||||
* This is abd_iter_page(), the function underneath abd_iterate_page_func().
|
||||
* It yields the next page struct and data offset and size within it, without
|
||||
* mapping it into the address space.
|
||||
*/
|
||||
|
||||
/*
|
||||
* "Compound pages" are a group of pages that can be referenced from a single
|
||||
* struct page *. Its organised as a "head" page, followed by a series of
|
||||
* "tail" pages.
|
||||
*
|
||||
* In OpenZFS, compound pages are allocated using the __GFP_COMP flag, which we
|
||||
* get from scatter ABDs and SPL vmalloc slabs (ie >16K allocations). So a
|
||||
* great many of the IO buffers we get are going to be of this type.
|
||||
*
|
||||
* The tail pages are just regular PAGESIZE pages, and can be safely used
|
||||
* as-is. However, the head page has length covering itself and all the tail
|
||||
* pages. If the ABD chunk spans multiple pages, then we can use the head page
|
||||
* and a >PAGESIZE length, which is far more efficient.
|
||||
*
|
||||
* Before kernel 4.5 however, compound page heads were refcounted separately
|
||||
* from tail pages, such that moving back to the head page would require us to
|
||||
* take a reference to it and releasing it once we're completely finished with
|
||||
* it. In practice, that means when our caller is done with the ABD, which we
|
||||
* have no insight into from here. Rather than contort this API to track head
|
||||
* page references on such ancient kernels, we disable this special compound
|
||||
* page handling on 4.5, instead just using treating each page within it as a
|
||||
* regular PAGESIZE page (which it is). This is slightly less efficient, but
|
||||
* makes everything far simpler.
|
||||
*
|
||||
* The below test sets/clears ABD_ITER_COMPOUND_PAGES to enable/disable the
|
||||
* special handling, and also defines the ABD_ITER_PAGE_SIZE(page) macro to
|
||||
* understand compound pages, or not, as required.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
|
||||
#define ABD_ITER_COMPOUND_PAGES 1
|
||||
#define ABD_ITER_PAGE_SIZE(page) \
|
||||
(PageCompound(page) ? page_size(page) : PAGESIZE)
|
||||
#else
|
||||
#undef ABD_ITER_COMPOUND_PAGES
|
||||
#define ABD_ITER_PAGE_SIZE(page) (PAGESIZE)
|
||||
#endif
|
||||
|
||||
void
|
||||
abd_iter_page(struct abd_iter *aiter)
|
||||
{
|
||||
|
@ -1032,6 +1072,12 @@ abd_iter_page(struct abd_iter *aiter)
|
|||
struct page *page;
|
||||
size_t doff, dsize;
|
||||
|
||||
/*
|
||||
* Find the page, and the start of the data within it. This is computed
|
||||
* differently for linear and scatter ABDs; linear is referenced by
|
||||
* virtual memory location, while scatter is referenced by page
|
||||
* pointer.
|
||||
*/
|
||||
if (abd_is_linear(aiter->iter_abd)) {
|
||||
ASSERT3U(aiter->iter_pos, ==, aiter->iter_offset);
|
||||
|
||||
|
@ -1044,57 +1090,24 @@ abd_iter_page(struct abd_iter *aiter)
|
|||
|
||||
/* offset of address within the page */
|
||||
doff = offset_in_page(paddr);
|
||||
|
||||
/* total data remaining in abd from this position */
|
||||
dsize = aiter->iter_abd->abd_size - aiter->iter_offset;
|
||||
} else {
|
||||
ASSERT(!abd_is_gang(aiter->iter_abd));
|
||||
|
||||
/* current scatter page */
|
||||
page = sg_page(aiter->iter_sg);
|
||||
page = nth_page(sg_page(aiter->iter_sg),
|
||||
aiter->iter_offset >> PAGE_SHIFT);
|
||||
|
||||
/* position within page */
|
||||
doff = aiter->iter_offset;
|
||||
|
||||
/* remaining data in scatterlist */
|
||||
dsize = MIN(aiter->iter_sg->length - aiter->iter_offset,
|
||||
aiter->iter_abd->abd_size - aiter->iter_pos);
|
||||
doff = aiter->iter_offset & (PAGESIZE - 1);
|
||||
}
|
||||
ASSERT(page);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
|
||||
#ifdef ABD_ITER_COMPOUND_PAGES
|
||||
if (PageTail(page)) {
|
||||
/*
|
||||
* This page is part of a "compound page", which is a group of
|
||||
* pages that can be referenced from a single struct page *.
|
||||
* Its organised as a "head" page, followed by a series of
|
||||
* "tail" pages.
|
||||
*
|
||||
* In OpenZFS, compound pages are allocated using the
|
||||
* __GFP_COMP flag, which we get from scatter ABDs and SPL
|
||||
* vmalloc slabs (ie >16K allocations). So a great many of the
|
||||
* IO buffers we get are going to be of this type.
|
||||
*
|
||||
* The tail pages are just regular PAGE_SIZE pages, and can be
|
||||
* safely used as-is. However, the head page has length
|
||||
* covering itself and all the tail pages. If this ABD chunk
|
||||
* spans multiple pages, then we can use the head page and a
|
||||
* >PAGE_SIZE length, which is far more efficient.
|
||||
*
|
||||
* To do this, we need to adjust the offset to be counted from
|
||||
* the head page. struct page for compound pages are stored
|
||||
* contiguously, so we can just adjust by a simple offset.
|
||||
*
|
||||
* Before kernel 4.5, compound page heads were refcounted
|
||||
* separately, such that moving back to the head page would
|
||||
* require us to take a reference to it and releasing it once
|
||||
* we're completely finished with it. In practice, that means
|
||||
* when our caller is done with the ABD, which we have no
|
||||
* insight into from here. Rather than contort this API to
|
||||
* track head page references on such ancient kernels, we just
|
||||
* compile this block out and use the tail pages directly. This
|
||||
* is slightly less efficient, but makes everything far
|
||||
* simpler.
|
||||
* If this is a compound tail page, move back to the head, and
|
||||
* adjust the offset to match. This may let us yield a much
|
||||
* larger amount of data from a single logical page, and so
|
||||
* leave our caller with fewer pages to process.
|
||||
*/
|
||||
struct page *head = compound_head(page);
|
||||
doff += ((page - head) * PAGESIZE);
|
||||
|
@ -1102,12 +1115,27 @@ abd_iter_page(struct abd_iter *aiter)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* final page and position within it */
|
||||
ASSERT(page);
|
||||
|
||||
/*
|
||||
* Compute the maximum amount of data we can take from this page. This
|
||||
* is the smaller of:
|
||||
* - the remaining space in the page
|
||||
* - the remaining space in this scatterlist entry (which may not cover
|
||||
* the entire page)
|
||||
* - the remaining space in the abd (which may not cover the entire
|
||||
* scatterlist entry)
|
||||
*/
|
||||
dsize = MIN(ABD_ITER_PAGE_SIZE(page) - doff,
|
||||
aiter->iter_abd->abd_size - aiter->iter_pos);
|
||||
if (!abd_is_linear(aiter->iter_abd))
|
||||
dsize = MIN(dsize, aiter->iter_sg->length - aiter->iter_offset);
|
||||
ASSERT3U(dsize, >, 0);
|
||||
|
||||
/* final iterator outputs */
|
||||
aiter->iter_page = page;
|
||||
aiter->iter_page_doff = doff;
|
||||
|
||||
/* amount of data in the chunk, up to the end of the page */
|
||||
aiter->iter_page_dsize = MIN(dsize, page_size(page) - doff);
|
||||
aiter->iter_page_dsize = dsize;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -8902,7 +8902,6 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, uint64_t asize,
|
|||
abd_t **abd_out)
|
||||
{
|
||||
int ret;
|
||||
void *tmp = NULL;
|
||||
abd_t *cabd = NULL, *eabd = NULL, *to_write = hdr->b_l1hdr.b_pabd;
|
||||
enum zio_compress compress = HDR_GET_COMPRESS(hdr);
|
||||
uint64_t psize = HDR_GET_PSIZE(hdr);
|
||||
|
@ -8923,12 +8922,11 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, uint64_t asize,
|
|||
* and copy the data. This may be done to eliminate a dependency on a
|
||||
* shared buffer or to reallocate the buffer to match asize.
|
||||
*/
|
||||
if (HDR_HAS_RABD(hdr) && asize != psize) {
|
||||
ASSERT3U(asize, >=, psize);
|
||||
if (HDR_HAS_RABD(hdr)) {
|
||||
ASSERT3U(asize, >, psize);
|
||||
to_write = abd_alloc_for_io(asize, ismd);
|
||||
abd_copy(to_write, hdr->b_crypt_hdr.b_rabd, psize);
|
||||
if (psize != asize)
|
||||
abd_zero_off(to_write, psize, asize - psize);
|
||||
abd_zero_off(to_write, psize, asize - psize);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -8937,48 +8935,31 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, uint64_t asize,
|
|||
ASSERT3U(size, ==, psize);
|
||||
to_write = abd_alloc_for_io(asize, ismd);
|
||||
abd_copy(to_write, hdr->b_l1hdr.b_pabd, size);
|
||||
if (size != asize)
|
||||
if (asize > size)
|
||||
abd_zero_off(to_write, size, asize - size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (compress != ZIO_COMPRESS_OFF && !HDR_COMPRESSION_ENABLED(hdr)) {
|
||||
/*
|
||||
* In some cases, we can wind up with size > asize, so
|
||||
* we need to opt for the larger allocation option here.
|
||||
*
|
||||
* (We also need abd_return_buf_copy in all cases because
|
||||
* it's an ASSERT() to modify the buffer before returning it
|
||||
* with arc_return_buf(), and all the compressors
|
||||
* write things before deciding to fail compression in nearly
|
||||
* every case.)
|
||||
*/
|
||||
uint64_t bufsize = MAX(size, asize);
|
||||
cabd = abd_alloc_for_io(bufsize, ismd);
|
||||
tmp = abd_borrow_buf(cabd, bufsize);
|
||||
|
||||
psize = zio_compress_data(compress, to_write, &tmp, size,
|
||||
hdr->b_complevel);
|
||||
|
||||
if (psize >= asize) {
|
||||
psize = HDR_GET_PSIZE(hdr);
|
||||
abd_return_buf_copy(cabd, tmp, bufsize);
|
||||
HDR_SET_COMPRESS(hdr, ZIO_COMPRESS_OFF);
|
||||
to_write = cabd;
|
||||
abd_copy(to_write, hdr->b_l1hdr.b_pabd, psize);
|
||||
if (psize != asize)
|
||||
abd_zero_off(to_write, psize, asize - psize);
|
||||
goto encrypt;
|
||||
size_t bufsize = MAX(size, asize);
|
||||
void *buf = zio_buf_alloc(bufsize);
|
||||
uint64_t csize = zio_compress_data(compress, to_write, &buf,
|
||||
size, hdr->b_complevel);
|
||||
if (csize > psize) {
|
||||
/*
|
||||
* We can't re-compress the block into the original
|
||||
* psize. Even if it fits into asize, it does not
|
||||
* matter, since checksum will never match on read.
|
||||
*/
|
||||
zio_buf_free(buf, bufsize);
|
||||
return (SET_ERROR(EIO));
|
||||
}
|
||||
ASSERT3U(psize, <=, HDR_GET_PSIZE(hdr));
|
||||
if (psize < asize)
|
||||
memset((char *)tmp + psize, 0, bufsize - psize);
|
||||
psize = HDR_GET_PSIZE(hdr);
|
||||
abd_return_buf_copy(cabd, tmp, bufsize);
|
||||
to_write = cabd;
|
||||
if (asize > csize)
|
||||
memset((char *)buf + csize, 0, asize - csize);
|
||||
to_write = cabd = abd_get_from_buf(buf, bufsize);
|
||||
abd_take_ownership_of_buf(cabd, B_TRUE);
|
||||
}
|
||||
|
||||
encrypt:
|
||||
if (HDR_ENCRYPTED(hdr)) {
|
||||
eabd = abd_alloc_for_io(asize, ismd);
|
||||
|
||||
|
|
|
@ -161,13 +161,13 @@ struct {
|
|||
} dbuf_sums;
|
||||
|
||||
#define DBUF_STAT_INCR(stat, val) \
|
||||
wmsum_add(&dbuf_sums.stat, val);
|
||||
wmsum_add(&dbuf_sums.stat, val)
|
||||
#define DBUF_STAT_DECR(stat, val) \
|
||||
DBUF_STAT_INCR(stat, -(val));
|
||||
DBUF_STAT_INCR(stat, -(val))
|
||||
#define DBUF_STAT_BUMP(stat) \
|
||||
DBUF_STAT_INCR(stat, 1);
|
||||
DBUF_STAT_INCR(stat, 1)
|
||||
#define DBUF_STAT_BUMPDOWN(stat) \
|
||||
DBUF_STAT_INCR(stat, -1);
|
||||
DBUF_STAT_INCR(stat, -1)
|
||||
#define DBUF_STAT_MAX(stat, v) { \
|
||||
uint64_t _m; \
|
||||
while ((v) > (_m = dbuf_stats.stat.value.ui64) && \
|
||||
|
@ -177,7 +177,6 @@ struct {
|
|||
|
||||
static void dbuf_write(dbuf_dirty_record_t *dr, arc_buf_t *data, dmu_tx_t *tx);
|
||||
static void dbuf_sync_leaf_verify_bonus_dnode(dbuf_dirty_record_t *dr);
|
||||
static int dbuf_read_verify_dnode_crypt(dmu_buf_impl_t *db, uint32_t flags);
|
||||
|
||||
/*
|
||||
* Global data structures and functions for the dbuf cache.
|
||||
|
@ -1418,13 +1417,9 @@ dbuf_read_done(zio_t *zio, const zbookmark_phys_t *zb, const blkptr_t *bp,
|
|||
* a decrypted block. Otherwise success.
|
||||
*/
|
||||
static int
|
||||
dbuf_read_bonus(dmu_buf_impl_t *db, dnode_t *dn, uint32_t flags)
|
||||
dbuf_read_bonus(dmu_buf_impl_t *db, dnode_t *dn)
|
||||
{
|
||||
int bonuslen, max_bonuslen, err;
|
||||
|
||||
err = dbuf_read_verify_dnode_crypt(db, flags);
|
||||
if (err)
|
||||
return (err);
|
||||
int bonuslen, max_bonuslen;
|
||||
|
||||
bonuslen = MIN(dn->dn_bonuslen, dn->dn_phys->dn_bonuslen);
|
||||
max_bonuslen = DN_SLOTS_TO_BONUSLEN(dn->dn_num_slots);
|
||||
|
@ -1509,32 +1504,46 @@ dbuf_read_hole(dmu_buf_impl_t *db, dnode_t *dn, blkptr_t *bp)
|
|||
* decrypt / authenticate them when we need to read an encrypted bonus buffer.
|
||||
*/
|
||||
static int
|
||||
dbuf_read_verify_dnode_crypt(dmu_buf_impl_t *db, uint32_t flags)
|
||||
dbuf_read_verify_dnode_crypt(dmu_buf_impl_t *db, dnode_t *dn, uint32_t flags)
|
||||
{
|
||||
int err = 0;
|
||||
objset_t *os = db->db_objset;
|
||||
arc_buf_t *dnode_abuf;
|
||||
dnode_t *dn;
|
||||
dmu_buf_impl_t *dndb;
|
||||
arc_buf_t *dnbuf;
|
||||
zbookmark_phys_t zb;
|
||||
|
||||
ASSERT(MUTEX_HELD(&db->db_mtx));
|
||||
int err;
|
||||
|
||||
if ((flags & DB_RF_NO_DECRYPT) != 0 ||
|
||||
!os->os_encrypted || os->os_raw_receive)
|
||||
!os->os_encrypted || os->os_raw_receive ||
|
||||
(dndb = dn->dn_dbuf) == NULL)
|
||||
return (0);
|
||||
|
||||
DB_DNODE_ENTER(db);
|
||||
dn = DB_DNODE(db);
|
||||
dnode_abuf = (dn->dn_dbuf != NULL) ? dn->dn_dbuf->db_buf : NULL;
|
||||
|
||||
if (dnode_abuf == NULL || !arc_is_encrypted(dnode_abuf)) {
|
||||
DB_DNODE_EXIT(db);
|
||||
dnbuf = dndb->db_buf;
|
||||
if (!arc_is_encrypted(dnbuf))
|
||||
return (0);
|
||||
}
|
||||
|
||||
mutex_enter(&dndb->db_mtx);
|
||||
|
||||
/*
|
||||
* Since dnode buffer is modified by sync process, there can be only
|
||||
* one copy of it. It means we can not modify (decrypt) it while it
|
||||
* is being written. I don't see how this may happen now, since
|
||||
* encrypted dnode writes by receive should be completed before any
|
||||
* plain-text reads due to txg wait, but better be safe than sorry.
|
||||
*/
|
||||
while (1) {
|
||||
if (!arc_is_encrypted(dnbuf)) {
|
||||
mutex_exit(&dndb->db_mtx);
|
||||
return (0);
|
||||
}
|
||||
dbuf_dirty_record_t *dr = dndb->db_data_pending;
|
||||
if (dr == NULL || dr->dt.dl.dr_data != dnbuf)
|
||||
break;
|
||||
cv_wait(&dndb->db_changed, &dndb->db_mtx);
|
||||
};
|
||||
|
||||
SET_BOOKMARK(&zb, dmu_objset_id(os),
|
||||
DMU_META_DNODE_OBJECT, 0, dn->dn_dbuf->db_blkid);
|
||||
err = arc_untransform(dnode_abuf, os->os_spa, &zb, B_TRUE);
|
||||
DMU_META_DNODE_OBJECT, 0, dndb->db_blkid);
|
||||
err = arc_untransform(dnbuf, os->os_spa, &zb, B_TRUE);
|
||||
|
||||
/*
|
||||
* An error code of EACCES tells us that the key is still not
|
||||
|
@ -1547,7 +1556,7 @@ dbuf_read_verify_dnode_crypt(dmu_buf_impl_t *db, uint32_t flags)
|
|||
!DMU_OT_IS_ENCRYPTED(dn->dn_bonustype))))
|
||||
err = 0;
|
||||
|
||||
DB_DNODE_EXIT(db);
|
||||
mutex_exit(&dndb->db_mtx);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
@ -1573,7 +1582,7 @@ dbuf_read_impl(dmu_buf_impl_t *db, dnode_t *dn, zio_t *zio, uint32_t flags,
|
|||
RW_LOCK_HELD(&db->db_parent->db_rwlock));
|
||||
|
||||
if (db->db_blkid == DMU_BONUS_BLKID) {
|
||||
err = dbuf_read_bonus(db, dn, flags);
|
||||
err = dbuf_read_bonus(db, dn);
|
||||
goto early_unlock;
|
||||
}
|
||||
|
||||
|
@ -1635,10 +1644,6 @@ dbuf_read_impl(dmu_buf_impl_t *db, dnode_t *dn, zio_t *zio, uint32_t flags,
|
|||
goto early_unlock;
|
||||
}
|
||||
|
||||
err = dbuf_read_verify_dnode_crypt(db, flags);
|
||||
if (err != 0)
|
||||
goto early_unlock;
|
||||
|
||||
db->db_state = DB_READ;
|
||||
DTRACE_SET_STATE(db, "read issued");
|
||||
mutex_exit(&db->db_mtx);
|
||||
|
@ -1754,19 +1759,23 @@ dbuf_fix_old_data(dmu_buf_impl_t *db, uint64_t txg)
|
|||
int
|
||||
dbuf_read(dmu_buf_impl_t *db, zio_t *pio, uint32_t flags)
|
||||
{
|
||||
int err = 0;
|
||||
boolean_t prefetch;
|
||||
dnode_t *dn;
|
||||
boolean_t miss = B_TRUE, need_wait = B_FALSE, prefetch;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We don't have to hold the mutex to check db_state because it
|
||||
* can't be freed while we have a hold on the buffer.
|
||||
*/
|
||||
ASSERT(!zfs_refcount_is_zero(&db->db_holds));
|
||||
|
||||
DB_DNODE_ENTER(db);
|
||||
dn = DB_DNODE(db);
|
||||
|
||||
/*
|
||||
* Ensure that this block's dnode has been decrypted if the caller
|
||||
* has requested decrypted data.
|
||||
*/
|
||||
err = dbuf_read_verify_dnode_crypt(db, dn, flags);
|
||||
if (err != 0)
|
||||
goto done;
|
||||
|
||||
prefetch = db->db_level == 0 && db->db_blkid != DMU_BONUS_BLKID &&
|
||||
(flags & DB_RF_NOPREFETCH) == 0;
|
||||
|
||||
|
@ -1775,13 +1784,38 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *pio, uint32_t flags)
|
|||
db->db_partial_read = B_TRUE;
|
||||
else if (!(flags & DB_RF_PARTIAL_MORE))
|
||||
db->db_partial_read = B_FALSE;
|
||||
if (db->db_state == DB_CACHED) {
|
||||
/*
|
||||
* Ensure that this block's dnode has been decrypted if
|
||||
* the caller has requested decrypted data.
|
||||
*/
|
||||
err = dbuf_read_verify_dnode_crypt(db, flags);
|
||||
miss = (db->db_state != DB_CACHED);
|
||||
|
||||
if (db->db_state == DB_READ || db->db_state == DB_FILL) {
|
||||
/*
|
||||
* Another reader came in while the dbuf was in flight between
|
||||
* UNCACHED and CACHED. Either a writer will finish filling
|
||||
* the buffer, sending the dbuf to CACHED, or the first reader's
|
||||
* request will reach the read_done callback and send the dbuf
|
||||
* to CACHED. Otherwise, a failure occurred and the dbuf will
|
||||
* be sent to UNCACHED.
|
||||
*/
|
||||
if (flags & DB_RF_NEVERWAIT) {
|
||||
mutex_exit(&db->db_mtx);
|
||||
DB_DNODE_EXIT(db);
|
||||
goto done;
|
||||
}
|
||||
do {
|
||||
ASSERT(db->db_state == DB_READ ||
|
||||
(flags & DB_RF_HAVESTRUCT) == 0);
|
||||
DTRACE_PROBE2(blocked__read, dmu_buf_impl_t *, db,
|
||||
zio_t *, pio);
|
||||
cv_wait(&db->db_changed, &db->db_mtx);
|
||||
} while (db->db_state == DB_READ || db->db_state == DB_FILL);
|
||||
if (db->db_state == DB_UNCACHED) {
|
||||
err = SET_ERROR(EIO);
|
||||
mutex_exit(&db->db_mtx);
|
||||
DB_DNODE_EXIT(db);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (db->db_state == DB_CACHED) {
|
||||
/*
|
||||
* If the arc buf is compressed or encrypted and the caller
|
||||
* requested uncompressed data, we need to untransform it
|
||||
|
@ -1789,8 +1823,7 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *pio, uint32_t flags)
|
|||
* unauthenticated blocks, which will verify their MAC if
|
||||
* the key is now available.
|
||||
*/
|
||||
if (err == 0 && db->db_buf != NULL &&
|
||||
(flags & DB_RF_NO_DECRYPT) == 0 &&
|
||||
if ((flags & DB_RF_NO_DECRYPT) == 0 && db->db_buf != NULL &&
|
||||
(arc_is_encrypted(db->db_buf) ||
|
||||
arc_is_unauthenticated(db->db_buf) ||
|
||||
arc_get_compression(db->db_buf) != ZIO_COMPRESS_OFF)) {
|
||||
|
@ -1804,17 +1837,10 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *pio, uint32_t flags)
|
|||
dbuf_set_data(db, db->db_buf);
|
||||
}
|
||||
mutex_exit(&db->db_mtx);
|
||||
if (err == 0 && prefetch) {
|
||||
dmu_zfetch(&dn->dn_zfetch, db->db_blkid, 1, B_TRUE,
|
||||
B_FALSE, flags & DB_RF_HAVESTRUCT);
|
||||
}
|
||||
DB_DNODE_EXIT(db);
|
||||
DBUF_STAT_BUMP(hash_hits);
|
||||
} else if (db->db_state == DB_UNCACHED || db->db_state == DB_NOFILL) {
|
||||
boolean_t need_wait = B_FALSE;
|
||||
|
||||
} else {
|
||||
ASSERT(db->db_state == DB_UNCACHED ||
|
||||
db->db_state == DB_NOFILL);
|
||||
db_lock_type_t dblt = dmu_buf_lock_parent(db, RW_READER, FTAG);
|
||||
|
||||
if (pio == NULL && (db->db_state == DB_NOFILL ||
|
||||
(db->db_blkptr != NULL && !BP_IS_HOLE(db->db_blkptr)))) {
|
||||
spa_t *spa = dn->dn_objset->os_spa;
|
||||
|
@ -1822,65 +1848,33 @@ dbuf_read(dmu_buf_impl_t *db, zio_t *pio, uint32_t flags)
|
|||
need_wait = B_TRUE;
|
||||
}
|
||||
err = dbuf_read_impl(db, dn, pio, flags, dblt, FTAG);
|
||||
/*
|
||||
* dbuf_read_impl has dropped db_mtx and our parent's rwlock
|
||||
* for us
|
||||
*/
|
||||
if (!err && prefetch) {
|
||||
dmu_zfetch(&dn->dn_zfetch, db->db_blkid, 1, B_TRUE,
|
||||
db->db_state != DB_CACHED,
|
||||
flags & DB_RF_HAVESTRUCT);
|
||||
}
|
||||
|
||||
DB_DNODE_EXIT(db);
|
||||
DBUF_STAT_BUMP(hash_misses);
|
||||
|
||||
/*
|
||||
* If we created a zio_root we must execute it to avoid
|
||||
* leaking it, even if it isn't attached to any work due
|
||||
* to an error in dbuf_read_impl().
|
||||
*/
|
||||
if (need_wait) {
|
||||
if (err == 0)
|
||||
err = zio_wait(pio);
|
||||
else
|
||||
(void) zio_wait(pio);
|
||||
pio = NULL;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Another reader came in while the dbuf was in flight
|
||||
* between UNCACHED and CACHED. Either a writer will finish
|
||||
* writing the buffer (sending the dbuf to CACHED) or the
|
||||
* first reader's request will reach the read_done callback
|
||||
* and send the dbuf to CACHED. Otherwise, a failure
|
||||
* occurred and the dbuf went to UNCACHED.
|
||||
*/
|
||||
mutex_exit(&db->db_mtx);
|
||||
if (prefetch) {
|
||||
dmu_zfetch(&dn->dn_zfetch, db->db_blkid, 1, B_TRUE,
|
||||
B_TRUE, flags & DB_RF_HAVESTRUCT);
|
||||
}
|
||||
DB_DNODE_EXIT(db);
|
||||
DBUF_STAT_BUMP(hash_misses);
|
||||
|
||||
/* Skip the wait per the caller's request. */
|
||||
if ((flags & DB_RF_NEVERWAIT) == 0) {
|
||||
mutex_enter(&db->db_mtx);
|
||||
while (db->db_state == DB_READ ||
|
||||
db->db_state == DB_FILL) {
|
||||
ASSERT(db->db_state == DB_READ ||
|
||||
(flags & DB_RF_HAVESTRUCT) == 0);
|
||||
DTRACE_PROBE2(blocked__read, dmu_buf_impl_t *,
|
||||
db, zio_t *, pio);
|
||||
cv_wait(&db->db_changed, &db->db_mtx);
|
||||
}
|
||||
if (db->db_state == DB_UNCACHED)
|
||||
err = SET_ERROR(EIO);
|
||||
mutex_exit(&db->db_mtx);
|
||||
}
|
||||
/* dbuf_read_impl drops db_mtx and parent's rwlock. */
|
||||
miss = (db->db_state != DB_CACHED);
|
||||
}
|
||||
|
||||
if (err == 0 && prefetch) {
|
||||
dmu_zfetch(&dn->dn_zfetch, db->db_blkid, 1, B_TRUE, miss,
|
||||
flags & DB_RF_HAVESTRUCT);
|
||||
}
|
||||
DB_DNODE_EXIT(db);
|
||||
|
||||
/*
|
||||
* If we created a zio we must execute it to avoid leaking it, even if
|
||||
* it isn't attached to any work due to an error in dbuf_read_impl().
|
||||
*/
|
||||
if (need_wait) {
|
||||
if (err == 0)
|
||||
err = zio_wait(pio);
|
||||
else
|
||||
(void) zio_wait(pio);
|
||||
pio = NULL;
|
||||
}
|
||||
|
||||
done:
|
||||
if (miss)
|
||||
DBUF_STAT_BUMP(hash_misses);
|
||||
else
|
||||
DBUF_STAT_BUMP(hash_hits);
|
||||
if (pio && err != 0) {
|
||||
zio_t *zio = zio_null(pio, pio->io_spa, NULL, NULL, NULL,
|
||||
ZIO_FLAG_CANFAIL);
|
||||
|
|
|
@ -3273,8 +3273,6 @@ spa_spawn_aux_threads(spa_t *spa)
|
|||
{
|
||||
ASSERT(spa_writeable(spa));
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
|
||||
spa_start_raidz_expansion_thread(spa);
|
||||
spa_start_indirect_condensing_thread(spa);
|
||||
spa_start_livelist_destroy_thread(spa);
|
||||
|
@ -4981,7 +4979,8 @@ spa_ld_read_checkpoint_txg(spa_t *spa)
|
|||
int error = 0;
|
||||
|
||||
ASSERT0(spa->spa_checkpoint_txg);
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock) ||
|
||||
spa->spa_load_thread == curthread);
|
||||
|
||||
error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
|
||||
DMU_POOL_ZPOOL_CHECKPOINT, sizeof (uint64_t),
|
||||
|
@ -5228,6 +5227,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
boolean_t checkpoint_rewind =
|
||||
(spa->spa_import_flags & ZFS_IMPORT_CHECKPOINT);
|
||||
boolean_t update_config_cache = B_FALSE;
|
||||
hrtime_t load_start = gethrtime();
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
ASSERT(spa->spa_config_source != SPA_CONFIG_SRC_NONE);
|
||||
|
@ -5272,13 +5272,19 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop the namespace lock for the rest of the function.
|
||||
*/
|
||||
spa->spa_load_thread = curthread;
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
|
||||
/*
|
||||
* Retrieve the checkpoint txg if the pool has a checkpoint.
|
||||
*/
|
||||
spa_import_progress_set_notes(spa, "Loading checkpoint txg");
|
||||
error = spa_ld_read_checkpoint_txg(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Retrieve the mapping of indirect vdevs. Those vdevs were removed
|
||||
|
@ -5291,7 +5297,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Loading indirect vdev metadata");
|
||||
error = spa_ld_open_indirect_vdev_metadata(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Retrieve the full list of active features from the MOS and check if
|
||||
|
@ -5300,7 +5306,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Checking feature flags");
|
||||
error = spa_ld_check_features(spa, &missing_feat_write);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Load several special directories from the MOS needed by the dsl_pool
|
||||
|
@ -5309,7 +5315,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Loading special MOS directories");
|
||||
error = spa_ld_load_special_directories(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Retrieve pool properties from the MOS.
|
||||
|
@ -5317,7 +5323,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Loading properties");
|
||||
error = spa_ld_get_props(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Retrieve the list of auxiliary devices - cache devices and spares -
|
||||
|
@ -5326,7 +5332,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Loading AUX vdevs");
|
||||
error = spa_ld_open_aux_vdevs(spa, type);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Load the metadata for all vdevs. Also check if unopenable devices
|
||||
|
@ -5335,17 +5341,17 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Loading vdev metadata");
|
||||
error = spa_ld_load_vdev_metadata(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
spa_import_progress_set_notes(spa, "Loading dedup tables");
|
||||
error = spa_ld_load_dedup_tables(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
spa_import_progress_set_notes(spa, "Loading BRT");
|
||||
error = spa_ld_load_brt(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Verify the logs now to make sure we don't have any unexpected errors
|
||||
|
@ -5354,7 +5360,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Verifying Log Devices");
|
||||
error = spa_ld_verify_logs(spa, type, ereport);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
if (missing_feat_write) {
|
||||
ASSERT(spa->spa_load_state == SPA_LOAD_TRYIMPORT);
|
||||
|
@ -5364,8 +5370,9 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
* read-only mode but not read-write mode. We now have enough
|
||||
* information and can return to userland.
|
||||
*/
|
||||
return (spa_vdev_err(spa->spa_root_vdev, VDEV_AUX_UNSUP_FEAT,
|
||||
ENOTSUP));
|
||||
error = spa_vdev_err(spa->spa_root_vdev, VDEV_AUX_UNSUP_FEAT,
|
||||
ENOTSUP);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -5376,7 +5383,7 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_import_progress_set_notes(spa, "Verifying pool data");
|
||||
error = spa_ld_verify_pool_data(spa);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Calculate the deflated space for the pool. This must be done before
|
||||
|
@ -5501,13 +5508,19 @@ spa_load_impl(spa_t *spa, spa_import_type_t type, const char **ereport)
|
|||
spa_config_exit(spa, SCL_CONFIG, FTAG);
|
||||
spa_import_progress_set_notes(spa, "Finished importing");
|
||||
}
|
||||
zio_handle_import_delay(spa, gethrtime() - load_start);
|
||||
|
||||
spa_import_progress_remove(spa_guid(spa));
|
||||
spa_async_request(spa, SPA_ASYNC_L2CACHE_REBUILD);
|
||||
|
||||
spa_load_note(spa, "LOADED");
|
||||
fail:
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
spa->spa_load_thread = NULL;
|
||||
cv_broadcast(&spa_namespace_cv);
|
||||
|
||||
return (error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -6757,9 +6770,14 @@ spa_tryimport(nvlist_t *tryconfig)
|
|||
/*
|
||||
* Create and initialize the spa structure.
|
||||
*/
|
||||
char *name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
|
||||
(void) snprintf(name, MAXPATHLEN, "%s-%llx-%s",
|
||||
TRYIMPORT_NAME, (u_longlong_t)curthread, poolname);
|
||||
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
spa = spa_add(TRYIMPORT_NAME, tryconfig, NULL);
|
||||
spa = spa_add(name, tryconfig, NULL);
|
||||
spa_activate(spa, SPA_MODE_READ);
|
||||
kmem_free(name, MAXPATHLEN);
|
||||
|
||||
/*
|
||||
* Rewind pool if a max txg was provided.
|
||||
|
@ -6874,6 +6892,7 @@ spa_export_common(const char *pool, int new_state, nvlist_t **oldconfig,
|
|||
{
|
||||
int error;
|
||||
spa_t *spa;
|
||||
hrtime_t export_start = gethrtime();
|
||||
|
||||
if (oldconfig)
|
||||
*oldconfig = NULL;
|
||||
|
@ -7018,6 +7037,9 @@ spa_export_common(const char *pool, int new_state, nvlist_t **oldconfig,
|
|||
spa->spa_is_exporting = B_FALSE;
|
||||
}
|
||||
|
||||
if (new_state == POOL_STATE_EXPORTED)
|
||||
zio_handle_export_delay(spa, gethrtime() - export_start);
|
||||
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
return (0);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2019 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2011, 2024 by Delphix. All rights reserved.
|
||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
|
||||
* Copyright 2013 Saso Kiselkov. All rights reserved.
|
||||
|
@ -82,7 +82,8 @@
|
|||
* - Check if spa_refcount is zero
|
||||
* - Rename a spa_t
|
||||
* - add/remove/attach/detach devices
|
||||
* - Held for the duration of create/destroy/import/export
|
||||
* - Held for the duration of create/destroy/export
|
||||
* - Held at the start and end of import
|
||||
*
|
||||
* It does not need to handle recursion. A create or destroy may
|
||||
* reference objects (files or zvols) in other pools, but by
|
||||
|
@ -235,9 +236,9 @@
|
|||
* locking is, always, based on spa_namespace_lock and spa_config_lock[].
|
||||
*/
|
||||
|
||||
static avl_tree_t spa_namespace_avl;
|
||||
avl_tree_t spa_namespace_avl;
|
||||
kmutex_t spa_namespace_lock;
|
||||
static kcondvar_t spa_namespace_cv;
|
||||
kcondvar_t spa_namespace_cv;
|
||||
static const int spa_max_replication_override = SPA_DVAS_PER_BP;
|
||||
|
||||
static kmutex_t spa_spare_lock;
|
||||
|
@ -619,6 +620,7 @@ spa_lookup(const char *name)
|
|||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
|
||||
retry:
|
||||
(void) strlcpy(search.spa_name, name, sizeof (search.spa_name));
|
||||
|
||||
/*
|
||||
|
@ -630,6 +632,14 @@ spa_lookup(const char *name)
|
|||
*cp = '\0';
|
||||
|
||||
spa = avl_find(&spa_namespace_avl, &search, &where);
|
||||
if (spa == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (spa->spa_load_thread != NULL &&
|
||||
spa->spa_load_thread != curthread) {
|
||||
cv_wait(&spa_namespace_cv, &spa_namespace_lock);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return (spa);
|
||||
}
|
||||
|
@ -728,6 +738,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot)
|
|||
spa_config_lock_init(spa);
|
||||
spa_stats_init(spa);
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
avl_add(&spa_namespace_avl, spa);
|
||||
|
||||
/*
|
||||
|
@ -826,7 +837,6 @@ spa_remove(spa_t *spa)
|
|||
nvlist_free(spa->spa_config_splitting);
|
||||
|
||||
avl_remove(&spa_namespace_avl, spa);
|
||||
cv_broadcast(&spa_namespace_cv);
|
||||
|
||||
if (spa->spa_root)
|
||||
spa_strfree(spa->spa_root);
|
||||
|
@ -920,7 +930,8 @@ void
|
|||
spa_open_ref(spa_t *spa, const void *tag)
|
||||
{
|
||||
ASSERT(zfs_refcount_count(&spa->spa_refcount) >= spa->spa_minref ||
|
||||
MUTEX_HELD(&spa_namespace_lock));
|
||||
MUTEX_HELD(&spa_namespace_lock) ||
|
||||
spa->spa_load_thread == curthread);
|
||||
(void) zfs_refcount_add(&spa->spa_refcount, tag);
|
||||
}
|
||||
|
||||
|
@ -932,7 +943,8 @@ void
|
|||
spa_close(spa_t *spa, const void *tag)
|
||||
{
|
||||
ASSERT(zfs_refcount_count(&spa->spa_refcount) > spa->spa_minref ||
|
||||
MUTEX_HELD(&spa_namespace_lock));
|
||||
MUTEX_HELD(&spa_namespace_lock) ||
|
||||
spa->spa_load_thread == curthread);
|
||||
(void) zfs_refcount_remove(&spa->spa_refcount, tag);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016, 2019 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2016, 2024 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/spa.h>
|
||||
|
@ -775,7 +775,8 @@ vdev_initialize_stop_all(vdev_t *vd, vdev_initializing_state_t tgt_state)
|
|||
void
|
||||
vdev_initialize_restart(vdev_t *vd)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock) ||
|
||||
vd->vdev_spa->spa_load_thread == curthread);
|
||||
ASSERT(!spa_config_held(vd->vdev_spa, SCL_ALL, RW_WRITER));
|
||||
|
||||
if (vd->vdev_leaf_zap != 0) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* Copyright (c) 2018, Intel Corporation.
|
||||
* Copyright (c) 2020 by Lawrence Livermore National Security, LLC.
|
||||
* Copyright (c) 2022 Hewlett Packard Enterprise Development LP.
|
||||
* Copyright (c) 2024 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/vdev_impl.h>
|
||||
|
@ -1071,7 +1072,8 @@ vdev_rebuild_restart_impl(vdev_t *vd)
|
|||
void
|
||||
vdev_rebuild_restart(spa_t *spa)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock) ||
|
||||
spa->spa_load_thread == curthread);
|
||||
|
||||
vdev_rebuild_restart_impl(spa->spa_root_vdev);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2016, 2024 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2019 by Lawrence Livermore National Security, LLC.
|
||||
* Copyright (c) 2021 Hewlett Packard Enterprise Development LP
|
||||
* Copyright 2023 RackTop Systems, Inc.
|
||||
|
@ -1148,7 +1148,8 @@ vdev_trim_stop_all(vdev_t *vd, vdev_trim_state_t tgt_state)
|
|||
void
|
||||
vdev_trim_restart(vdev_t *vd)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock) ||
|
||||
vd->vdev_spa->spa_load_thread == curthread);
|
||||
ASSERT(!spa_config_held(vd->vdev_spa, SCL_ALL, RW_WRITER));
|
||||
|
||||
if (vd->vdev_leaf_zap != 0) {
|
||||
|
@ -1568,8 +1569,8 @@ vdev_autotrim_stop_all(spa_t *spa)
|
|||
void
|
||||
vdev_autotrim_restart(spa_t *spa)
|
||||
{
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock));
|
||||
|
||||
ASSERT(MUTEX_HELD(&spa_namespace_lock) ||
|
||||
spa->spa_load_thread == curthread);
|
||||
if (spa->spa_autotrim)
|
||||
vdev_autotrim(spa);
|
||||
}
|
||||
|
|
|
@ -4114,7 +4114,8 @@ zio_vdev_io_done(zio_t *zio)
|
|||
if (zio_injection_enabled && zio->io_error == 0)
|
||||
zio->io_error = zio_handle_label_injection(zio, EIO);
|
||||
|
||||
if (zio->io_error && zio->io_type != ZIO_TYPE_TRIM) {
|
||||
if (zio->io_error && zio->io_type != ZIO_TYPE_FLUSH &&
|
||||
zio->io_type != ZIO_TYPE_TRIM) {
|
||||
if (!vdev_accessible(vd, zio)) {
|
||||
zio->io_error = SET_ERROR(ENXIO);
|
||||
} else {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2017, Intel Corporation.
|
||||
* Copyright (c) 2024, Klara Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -59,6 +60,7 @@ uint32_t zio_injection_enabled = 0;
|
|||
typedef struct inject_handler {
|
||||
int zi_id;
|
||||
spa_t *zi_spa;
|
||||
char *zi_spa_name; /* ZINJECT_DELAY_IMPORT only */
|
||||
zinject_record_t zi_record;
|
||||
uint64_t *zi_lanes;
|
||||
int zi_next_lane;
|
||||
|
@ -703,6 +705,63 @@ zio_handle_io_delay(zio_t *zio)
|
|||
return (min_target);
|
||||
}
|
||||
|
||||
static void
|
||||
zio_handle_pool_delay(spa_t *spa, hrtime_t elapsed, zinject_type_t command)
|
||||
{
|
||||
inject_handler_t *handler;
|
||||
hrtime_t delay = 0;
|
||||
int id = 0;
|
||||
|
||||
rw_enter(&inject_lock, RW_READER);
|
||||
|
||||
for (handler = list_head(&inject_handlers);
|
||||
handler != NULL && handler->zi_record.zi_cmd == command;
|
||||
handler = list_next(&inject_handlers, handler)) {
|
||||
ASSERT3P(handler->zi_spa_name, !=, NULL);
|
||||
if (strcmp(spa_name(spa), handler->zi_spa_name) == 0) {
|
||||
uint64_t pause =
|
||||
SEC2NSEC(handler->zi_record.zi_duration);
|
||||
if (pause > elapsed) {
|
||||
delay = pause - elapsed;
|
||||
}
|
||||
id = handler->zi_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rw_exit(&inject_lock);
|
||||
|
||||
if (delay) {
|
||||
if (command == ZINJECT_DELAY_IMPORT) {
|
||||
spa_import_progress_set_notes(spa, "injecting %llu "
|
||||
"sec delay", (u_longlong_t)NSEC2SEC(delay));
|
||||
}
|
||||
zfs_sleep_until(gethrtime() + delay);
|
||||
}
|
||||
if (id) {
|
||||
/* all done with this one-shot handler */
|
||||
zio_clear_fault(id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For testing, inject a delay during an import
|
||||
*/
|
||||
void
|
||||
zio_handle_import_delay(spa_t *spa, hrtime_t elapsed)
|
||||
{
|
||||
zio_handle_pool_delay(spa, elapsed, ZINJECT_DELAY_IMPORT);
|
||||
}
|
||||
|
||||
/*
|
||||
* For testing, inject a delay during an export
|
||||
*/
|
||||
void
|
||||
zio_handle_export_delay(spa_t *spa, hrtime_t elapsed)
|
||||
{
|
||||
zio_handle_pool_delay(spa, elapsed, ZINJECT_DELAY_EXPORT);
|
||||
}
|
||||
|
||||
static int
|
||||
zio_calculate_range(const char *pool, zinject_record_t *record)
|
||||
{
|
||||
|
@ -760,6 +819,28 @@ zio_calculate_range(const char *pool, zinject_record_t *record)
|
|||
return (0);
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
zio_pool_handler_exists(const char *name, zinject_type_t command)
|
||||
{
|
||||
boolean_t exists = B_FALSE;
|
||||
|
||||
rw_enter(&inject_lock, RW_READER);
|
||||
for (inject_handler_t *handler = list_head(&inject_handlers);
|
||||
handler != NULL; handler = list_next(&inject_handlers, handler)) {
|
||||
if (command != handler->zi_record.zi_cmd)
|
||||
continue;
|
||||
|
||||
const char *pool = (handler->zi_spa_name != NULL) ?
|
||||
handler->zi_spa_name : spa_name(handler->zi_spa);
|
||||
if (strcmp(name, pool) == 0) {
|
||||
exists = B_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rw_exit(&inject_lock);
|
||||
|
||||
return (exists);
|
||||
}
|
||||
/*
|
||||
* Create a new handler for the given record. We add it to the list, adding
|
||||
* a reference to the spa_t in the process. We increment zio_injection_enabled,
|
||||
|
@ -810,16 +891,42 @@ zio_inject_fault(char *name, int flags, int *id, zinject_record_t *record)
|
|||
|
||||
if (!(flags & ZINJECT_NULL)) {
|
||||
/*
|
||||
* spa_inject_ref() will add an injection reference, which will
|
||||
* prevent the pool from being removed from the namespace while
|
||||
* still allowing it to be unloaded.
|
||||
* Pool delays for import or export don't take an
|
||||
* injection reference on the spa. Instead they
|
||||
* rely on matching by name.
|
||||
*/
|
||||
if ((spa = spa_inject_addref(name)) == NULL)
|
||||
return (SET_ERROR(ENOENT));
|
||||
if (record->zi_cmd == ZINJECT_DELAY_IMPORT ||
|
||||
record->zi_cmd == ZINJECT_DELAY_EXPORT) {
|
||||
if (record->zi_duration <= 0)
|
||||
return (SET_ERROR(EINVAL));
|
||||
/*
|
||||
* Only one import | export delay handler per pool.
|
||||
*/
|
||||
if (zio_pool_handler_exists(name, record->zi_cmd))
|
||||
return (SET_ERROR(EEXIST));
|
||||
|
||||
mutex_enter(&spa_namespace_lock);
|
||||
boolean_t has_spa = spa_lookup(name) != NULL;
|
||||
mutex_exit(&spa_namespace_lock);
|
||||
|
||||
if (record->zi_cmd == ZINJECT_DELAY_IMPORT && has_spa)
|
||||
return (SET_ERROR(EEXIST));
|
||||
if (record->zi_cmd == ZINJECT_DELAY_EXPORT && !has_spa)
|
||||
return (SET_ERROR(ENOENT));
|
||||
spa = NULL;
|
||||
} else {
|
||||
/*
|
||||
* spa_inject_ref() will add an injection reference,
|
||||
* which will prevent the pool from being removed
|
||||
* from the namespace while still allowing it to be
|
||||
* unloaded.
|
||||
*/
|
||||
if ((spa = spa_inject_addref(name)) == NULL)
|
||||
return (SET_ERROR(ENOENT));
|
||||
}
|
||||
|
||||
handler = kmem_alloc(sizeof (inject_handler_t), KM_SLEEP);
|
||||
|
||||
handler->zi_spa = spa;
|
||||
handler->zi_spa = spa; /* note: can be NULL */
|
||||
handler->zi_record = *record;
|
||||
|
||||
if (handler->zi_record.zi_cmd == ZINJECT_DELAY_IO) {
|
||||
|
@ -832,6 +939,11 @@ zio_inject_fault(char *name, int flags, int *id, zinject_record_t *record)
|
|||
handler->zi_next_lane = 0;
|
||||
}
|
||||
|
||||
if (handler->zi_spa == NULL)
|
||||
handler->zi_spa_name = spa_strdup(name);
|
||||
else
|
||||
handler->zi_spa_name = NULL;
|
||||
|
||||
rw_enter(&inject_lock, RW_WRITER);
|
||||
|
||||
/*
|
||||
|
@ -891,7 +1003,11 @@ zio_inject_list_next(int *id, char *name, size_t buflen,
|
|||
if (handler) {
|
||||
*record = handler->zi_record;
|
||||
*id = handler->zi_id;
|
||||
(void) strlcpy(name, spa_name(handler->zi_spa), buflen);
|
||||
ASSERT(handler->zi_spa || handler->zi_spa_name);
|
||||
if (handler->zi_spa != NULL)
|
||||
(void) strlcpy(name, spa_name(handler->zi_spa), buflen);
|
||||
else
|
||||
(void) strlcpy(name, handler->zi_spa_name, buflen);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = SET_ERROR(ENOENT);
|
||||
|
@ -941,7 +1057,11 @@ zio_clear_fault(int id)
|
|||
ASSERT3P(handler->zi_lanes, ==, NULL);
|
||||
}
|
||||
|
||||
spa_inject_delref(handler->zi_spa);
|
||||
if (handler->zi_spa_name != NULL)
|
||||
spa_strfree(handler->zi_spa_name);
|
||||
|
||||
if (handler->zi_spa != NULL)
|
||||
spa_inject_delref(handler->zi_spa);
|
||||
kmem_free(handler, sizeof (inject_handler_t));
|
||||
atomic_dec_32(&zio_injection_enabled);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
|||
# by generating a preamble text file which kmodtool can append to the spec file.
|
||||
%(/bin/echo -e "\
|
||||
Requires: @PACKAGE@ = %{version}\n\
|
||||
Conflicts: @PACKAGE@-dkms)
|
||||
Conflicts: @PACKAGE@-dkms" > %{_sourcedir}/kmod-preamble)
|
||||
|
||||
# LDFLAGS are not sanitized by arch/*/Makefile for these architectures.
|
||||
%ifarch ppc ppc64 ppc64le aarch64
|
||||
|
|
|
@ -32,6 +32,7 @@ SCRIPT_COMMON=${SCRIPT_COMMON:-${0%/*}/common.sh}
|
|||
PROG=zfs-tests.sh
|
||||
VERBOSE="no"
|
||||
QUIET=""
|
||||
DEBUG=""
|
||||
CLEANUP="yes"
|
||||
CLEANUPALL="no"
|
||||
KMSG=""
|
||||
|
@ -313,6 +314,7 @@ OPTIONS:
|
|||
-h Show this message
|
||||
-v Verbose zfs-tests.sh output
|
||||
-q Quiet test-runner output
|
||||
-D Debug; show all test output immediately (noisy)
|
||||
-x Remove all testpools, dm, lo, and files (unsafe)
|
||||
-k Disable cleanup after test failure
|
||||
-K Log test names to /dev/kmsg
|
||||
|
@ -351,7 +353,7 @@ $0 -x
|
|||
EOF
|
||||
}
|
||||
|
||||
while getopts 'hvqxkKfScRmn:d:s:r:?t:T:u:I:' OPTION; do
|
||||
while getopts 'hvqxkKfScRmn:d:Ds:r:?t:T:u:I:' OPTION; do
|
||||
case $OPTION in
|
||||
h)
|
||||
usage
|
||||
|
@ -397,6 +399,9 @@ while getopts 'hvqxkKfScRmn:d:s:r:?t:T:u:I:' OPTION; do
|
|||
d)
|
||||
FILEDIR="$OPTARG"
|
||||
;;
|
||||
D)
|
||||
DEBUG="yes"
|
||||
;;
|
||||
I)
|
||||
ITERATIONS="$OPTARG"
|
||||
if [ "$ITERATIONS" -le 0 ]; then
|
||||
|
@ -691,6 +696,7 @@ REPORT_FILE=$(mktemp_file zts-report)
|
|||
#
|
||||
msg "${TEST_RUNNER}" \
|
||||
"${QUIET:+-q}" \
|
||||
"${DEBUG:+-D}" \
|
||||
"${KMEMLEAK:+-m}" \
|
||||
"${KMSG:+-K}" \
|
||||
"-c \"${RUNFILES}\"" \
|
||||
|
@ -700,6 +706,7 @@ msg "${TEST_RUNNER}" \
|
|||
{ PATH=$STF_PATH \
|
||||
${TEST_RUNNER} \
|
||||
${QUIET:+-q} \
|
||||
${DEBUG:+-D} \
|
||||
${KMEMLEAK:+-m} \
|
||||
${KMSG:+-K} \
|
||||
-c "${RUNFILES}" \
|
||||
|
@ -726,6 +733,7 @@ if [ "$RESULT" -eq "2" ] && [ -n "$RERUN" ]; then
|
|||
{ PATH=$STF_PATH \
|
||||
${TEST_RUNNER} \
|
||||
${QUIET:+-q} \
|
||||
${DEBUG:+-D} \
|
||||
${KMEMLEAK:+-m} \
|
||||
-c "${RUNFILES}" \
|
||||
-T "${TAGS}" \
|
||||
|
|
|
@ -466,7 +466,8 @@ tests = ['zpool_import_001_pos', 'zpool_import_002_pos',
|
|||
'import_paths_changed',
|
||||
'import_rewind_config_changed',
|
||||
'import_rewind_device_replaced',
|
||||
'zpool_import_status']
|
||||
'zpool_import_status', 'zpool_import_parallel_pos',
|
||||
'zpool_import_parallel_neg', 'zpool_import_parallel_admin']
|
||||
tags = ['functional', 'cli_root', 'zpool_import']
|
||||
timeout = 1200
|
||||
|
||||
|
|
|
@ -113,8 +113,9 @@ class Output(object):
|
|||
This class is a slightly modified version of the 'Stream' class found
|
||||
here: http://goo.gl/aSGfv
|
||||
"""
|
||||
def __init__(self, stream):
|
||||
def __init__(self, stream, debug=False):
|
||||
self.stream = stream
|
||||
self.debug = debug
|
||||
self._buf = b''
|
||||
self.lines = []
|
||||
|
||||
|
@ -140,6 +141,8 @@ class Output(object):
|
|||
buf = os.read(fd, 4096)
|
||||
if not buf:
|
||||
return None
|
||||
if self.debug:
|
||||
os.write(sys.stderr.fileno(), buf)
|
||||
if b'\n' not in buf:
|
||||
self._buf += buf
|
||||
return []
|
||||
|
@ -238,14 +241,14 @@ User: %s
|
|||
ret = '%s -E -u %s %s' % (SUDO, user, cmd)
|
||||
return ret.split(' ')
|
||||
|
||||
def collect_output(self, proc):
|
||||
def collect_output(self, proc, debug=False):
|
||||
"""
|
||||
Read from stdout/stderr as data becomes available, until the
|
||||
process is no longer running. Return the lines from the stdout and
|
||||
stderr Output objects.
|
||||
"""
|
||||
out = Output(proc.stdout)
|
||||
err = Output(proc.stderr)
|
||||
out = Output(proc.stdout, debug)
|
||||
err = Output(proc.stderr, debug)
|
||||
res = []
|
||||
while proc.returncode is None:
|
||||
proc.poll()
|
||||
|
@ -308,7 +311,10 @@ User: %s
|
|||
|
||||
try:
|
||||
t.start()
|
||||
self.result.stdout, self.result.stderr = self.collect_output(proc)
|
||||
|
||||
out, err = self.collect_output(proc, options.debug)
|
||||
self.result.stdout = out
|
||||
self.result.stderr = err
|
||||
|
||||
if kmemleak:
|
||||
cmd = f'{SUDO} sh -c "echo scan > {KMEMLEAK_FILE}"'
|
||||
|
@ -624,7 +630,7 @@ Tags: %s
|
|||
|
||||
|
||||
class TestRun(object):
|
||||
props = ['quiet', 'outputdir']
|
||||
props = ['quiet', 'outputdir', 'debug']
|
||||
|
||||
def __init__(self, options):
|
||||
self.tests = {}
|
||||
|
@ -644,7 +650,8 @@ class TestRun(object):
|
|||
('post_user', ''),
|
||||
('failsafe', ''),
|
||||
('failsafe_user', ''),
|
||||
('tags', [])
|
||||
('tags', []),
|
||||
('debug', False)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
|
@ -1067,6 +1074,8 @@ def parse_args():
|
|||
help='Specify tests to run via config files.')
|
||||
parser.add_option('-d', action='store_true', default=False, dest='dryrun',
|
||||
help='Dry run. Print tests, but take no other action.')
|
||||
parser.add_option('-D', action='store_true', default=False, dest='debug',
|
||||
help='Write all test output to stdout as it arrives.')
|
||||
parser.add_option('-l', action='callback', callback=options_cb,
|
||||
default=None, dest='logfile', metavar='logfile',
|
||||
type='string',
|
||||
|
|
|
@ -1144,6 +1144,9 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
|||
functional/cli_root/zpool_import/zpool_import_missing_003_pos.ksh \
|
||||
functional/cli_root/zpool_import/zpool_import_rename_001_pos.ksh \
|
||||
functional/cli_root/zpool_import/zpool_import_status.ksh \
|
||||
functional/cli_root/zpool_import/zpool_import_parallel_admin.ksh \
|
||||
functional/cli_root/zpool_import/zpool_import_parallel_neg.ksh \
|
||||
functional/cli_root/zpool_import/zpool_import_parallel_pos.ksh \
|
||||
functional/cli_root/zpool_initialize/cleanup.ksh \
|
||||
functional/cli_root/zpool_initialize/zpool_initialize_attach_detach_add_remove.ksh \
|
||||
functional/cli_root/zpool_initialize/zpool_initialize_fault_export_import_online.ksh \
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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 https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2023 Klara, Inc.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.cfg
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Verify that admin commands to different pool are not blocked by import
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create 2 pools
|
||||
# 2. Export one of the pools
|
||||
# 4. Import the pool with an injected delay
|
||||
# 5. Execute some admin commands against both pools
|
||||
# 6. Verify that the admin commands to the non-imported pool don't stall
|
||||
#
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
function cleanup
|
||||
{
|
||||
zinject -c all
|
||||
destroy_pool $TESTPOOL1
|
||||
destroy_pool $TESTPOOL2
|
||||
}
|
||||
|
||||
function pool_import
|
||||
{
|
||||
typeset dir=$1
|
||||
typeset pool=$2
|
||||
|
||||
SECONDS=0
|
||||
errmsg=$(zpool import -d $dir -f $pool 2>&1 > /dev/null)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo ${pool}: imported in $SECONDS secs
|
||||
echo $SECONDS > ${DEVICE_DIR}/${pool}-import
|
||||
else
|
||||
echo ${pool}: import failed $errmsg in $SECONDS secs
|
||||
fi
|
||||
}
|
||||
|
||||
function pool_add_device
|
||||
{
|
||||
typeset pool=$1
|
||||
typeset device=$2
|
||||
typeset devtype=$3
|
||||
|
||||
SECONDS=0
|
||||
errmsg=$(zpool add $pool $devtype $device 2>&1 > /dev/null)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo ${pool}: added $devtype vdev in $SECONDS secs
|
||||
echo $SECONDS > ${DEVICE_DIR}/${pool}-add
|
||||
else
|
||||
echo ${pool}: add $devtype vdev failed ${errmsg}, in $SECONDS secs
|
||||
fi
|
||||
}
|
||||
|
||||
function pool_stats
|
||||
{
|
||||
typeset stats=$1
|
||||
typeset pool=$2
|
||||
|
||||
SECONDS=0
|
||||
errmsg=$(zpool $stats $pool 2>&1 > /dev/null)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo ${pool}: $stats in $SECONDS secs
|
||||
echo $SECONDS > ${DEVICE_DIR}/${pool}-${stats}
|
||||
else
|
||||
echo ${pool}: $stats failed ${errmsg}, in $SECONDS secs
|
||||
fi
|
||||
}
|
||||
|
||||
function pool_create
|
||||
{
|
||||
typeset pool=$1
|
||||
typeset device=$2
|
||||
|
||||
SECONDS=0
|
||||
errmsg=$(zpool create $pool $device 2>&1 > /dev/null)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo ${pool}: created in $SECONDS secs
|
||||
echo $SECONDS > ${DEVICE_DIR}/${pool}-create
|
||||
else
|
||||
echo ${pool}: create failed ${errmsg}, in $SECONDS secs
|
||||
fi
|
||||
}
|
||||
|
||||
log_assert "Simple admin commands to different pool not blocked by import"
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
#
|
||||
# create two pools and export one
|
||||
#
|
||||
log_must zpool create $TESTPOOL1 $VDEV0
|
||||
log_must zpool export $TESTPOOL1
|
||||
log_must zpool create $TESTPOOL2 $VDEV1
|
||||
|
||||
#
|
||||
# import pool asyncronously with an injected 10 second delay
|
||||
#
|
||||
log_must zinject -P import -s 10 $TESTPOOL1
|
||||
pool_import $DEVICE_DIR $TESTPOOL1 &
|
||||
|
||||
sleep 2
|
||||
|
||||
#
|
||||
# run some admin commands on the pools while the import is in progress
|
||||
#
|
||||
|
||||
pool_add_device $TESTPOOL1 $VDEV2 "log" &
|
||||
pool_add_device $TESTPOOL2 $VDEV3 "cache" &
|
||||
pool_stats "status" $TESTPOOL1 &
|
||||
pool_stats "status" $TESTPOOL2 &
|
||||
pool_stats "list" $TESTPOOL1 &
|
||||
pool_stats "list" $TESTPOOL2 &
|
||||
pool_create $TESTPOOL1 $VDEV4 &
|
||||
wait
|
||||
|
||||
log_must zpool sync $TESTPOOL1 $TESTPOOL2
|
||||
|
||||
zpool history $TESTPOOL1
|
||||
zpool history $TESTPOOL2
|
||||
|
||||
log_must test "5" -lt $(<${DEVICE_DIR}/${TESTPOOL1}-import)
|
||||
|
||||
#
|
||||
# verify that commands to second pool did not wait for import to finish
|
||||
#
|
||||
log_must test "2" -gt $(<${DEVICE_DIR}/${TESTPOOL2}-status)
|
||||
log_must test "2" -gt $(<${DEVICE_DIR}/${TESTPOOL2}-list)
|
||||
log_must test "2" -gt $(<${DEVICE_DIR}/${TESTPOOL2}-add)
|
||||
[[ -e ${DEVICE_DIR}/${TESTPOOL1}-create ]] && log_fail "unexpected pool create"
|
||||
|
||||
log_pass "Simple admin commands to different pool not blocked by import"
|
|
@ -0,0 +1,130 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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 https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2023 Klara, Inc.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.cfg
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Verify that pool imports by same name only have one winner
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create 4 single disk pools with the same name
|
||||
# 2. Generate some ZIL records (for a longer import)
|
||||
# 3. Export the pools
|
||||
# 4. Import the pools in parallel
|
||||
# 5. Repeat with using matching guids
|
||||
#
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
POOLNAME="import_pool"
|
||||
DEV_DIR_PREFIX="$DEVICE_DIR/$POOLNAME"
|
||||
VDEVSIZE=$((512 * 1024 * 1024))
|
||||
|
||||
log_assert "parallel pool imports by same name only have one winner"
|
||||
|
||||
# each pool has its own device directory
|
||||
for i in {0..3}; do
|
||||
log_must mkdir -p ${DEV_DIR_PREFIX}$i
|
||||
log_must truncate -s $VDEVSIZE ${DEV_DIR_PREFIX}$i/${DEVICE_FILE}$i
|
||||
done
|
||||
|
||||
function cleanup
|
||||
{
|
||||
zinject -c all
|
||||
log_must set_tunable64 KEEP_LOG_SPACEMAPS_AT_EXPORT 0
|
||||
log_must set_tunable64 METASLAB_DEBUG_LOAD 0
|
||||
|
||||
destroy_pool $POOLNAME
|
||||
|
||||
log_must rm -rf $DEV_DIR_PREFIX*
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must set_tunable64 KEEP_LOG_SPACEMAPS_AT_EXPORT 1
|
||||
log_must set_tunable64 METASLAB_DEBUG_LOAD 1
|
||||
|
||||
function import_pool
|
||||
{
|
||||
typeset dir=$1
|
||||
typeset pool=$2
|
||||
typeset newname=$3
|
||||
|
||||
SECONDS=0
|
||||
errmsg=$(zpool import -N -d $dir -f $pool $newname 2>&1 > /dev/null)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
touch $dir/imported
|
||||
echo "imported $pool in $SECONDS secs"
|
||||
elif [[ $errmsg == *"cannot import"* ]]; then
|
||||
echo "pool import failed: $errmsg, waited $SECONDS secs"
|
||||
touch $dir/failed
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# create four exported pools with the same name
|
||||
#
|
||||
for i in {0..3}; do
|
||||
log_must zpool create $POOLNAME ${DEV_DIR_PREFIX}$i/${DEVICE_FILE}$i
|
||||
log_must zpool export $POOLNAME
|
||||
done
|
||||
log_must zinject -P import -s 10 $POOLNAME
|
||||
|
||||
#
|
||||
# import the pools in parallel, expecting only one winner
|
||||
#
|
||||
for i in {0..3}; do
|
||||
import_pool ${DEV_DIR_PREFIX}$i $POOLNAME &
|
||||
done
|
||||
wait
|
||||
|
||||
# check the result of background imports
|
||||
typeset num_imports=0
|
||||
typeset num_cannot=0
|
||||
for i in {0..3}; do
|
||||
if [[ -f ${DEV_DIR_PREFIX}$i/imported ]]; then
|
||||
((num_imports += 1))
|
||||
fi
|
||||
if [[ -f ${DEV_DIR_PREFIX}$i/failed ]]; then
|
||||
((num_cannot += 1))
|
||||
loser=$i
|
||||
fi
|
||||
done
|
||||
[[ $num_imports -eq "1" ]] || log_fail "expecting an import"
|
||||
[[ $num_cannot -eq "3" ]] || \
|
||||
log_fail "expecting 3 pool exists errors, found $num_cannot"
|
||||
|
||||
log_note "$num_imports imported and $num_cannot failed (expected)"
|
||||
|
||||
log_pass "parallel pool imports by same name only have one winner"
|
|
@ -0,0 +1,137 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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 https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2023 Klara, Inc.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.cfg
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib
|
||||
|
||||
# test uses 8 vdevs
|
||||
export MAX_NUM=8
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Verify that pool imports can occur in parallel
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Create 8 pools
|
||||
# 2. Generate some ZIL records
|
||||
# 3. Export the pools
|
||||
# 4. Import half of the pools synchronously to baseline sequential cost
|
||||
# 5. Import the other half asynchronously to demonstrate parallel savings
|
||||
# 6. Export 4 pools
|
||||
# 7. Test zpool import -a
|
||||
#
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
#
|
||||
# override the minimum sized vdevs
|
||||
#
|
||||
VDEVSIZE=$((512 * 1024 * 1024))
|
||||
increase_device_sizes $VDEVSIZE
|
||||
|
||||
POOLNAME="import_pool"
|
||||
|
||||
function cleanup
|
||||
{
|
||||
zinject -c all
|
||||
log_must set_tunable64 KEEP_LOG_SPACEMAPS_AT_EXPORT 0
|
||||
log_must set_tunable64 METASLAB_DEBUG_LOAD 0
|
||||
|
||||
for i in {0..$(($MAX_NUM - 1))}; do
|
||||
destroy_pool $POOLNAME-$i
|
||||
done
|
||||
# reset the devices
|
||||
increase_device_sizes 0
|
||||
increase_device_sizes $FILE_SIZE
|
||||
}
|
||||
|
||||
log_assert "Pool imports can occur in parallel"
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must set_tunable64 KEEP_LOG_SPACEMAPS_AT_EXPORT 1
|
||||
log_must set_tunable64 METASLAB_DEBUG_LOAD 1
|
||||
|
||||
|
||||
#
|
||||
# create some exported pools with import delay injectors
|
||||
#
|
||||
for i in {0..$(($MAX_NUM - 1))}; do
|
||||
log_must zpool create $POOLNAME-$i $DEVICE_DIR/${DEVICE_FILE}$i
|
||||
log_must zpool export $POOLNAME-$i
|
||||
log_must zinject -P import -s 12 $POOLNAME-$i
|
||||
done
|
||||
wait
|
||||
|
||||
#
|
||||
# import half of the pools synchronously
|
||||
#
|
||||
SECONDS=0
|
||||
for i in {0..3}; do
|
||||
log_must zpool import -d $DEVICE_DIR -f $POOLNAME-$i
|
||||
done
|
||||
sequential_time=$SECONDS
|
||||
log_note "sequentially imported 4 pools in $sequential_time seconds"
|
||||
|
||||
#
|
||||
# import half of the pools in parallel
|
||||
#
|
||||
SECONDS=0
|
||||
for i in {4..7}; do
|
||||
log_must zpool import -d $DEVICE_DIR -f $POOLNAME-$i &
|
||||
done
|
||||
wait
|
||||
parallel_time=$SECONDS
|
||||
log_note "asyncronously imported 4 pools in $parallel_time seconds"
|
||||
|
||||
log_must test $parallel_time -lt $(($sequential_time / 3))
|
||||
|
||||
#
|
||||
# export pools with import delay injectors
|
||||
#
|
||||
for i in {4..7}; do
|
||||
log_must zpool export $POOLNAME-$i
|
||||
log_must zinject -P import -s 12 $POOLNAME-$i
|
||||
done
|
||||
wait
|
||||
|
||||
#
|
||||
# now test zpool import -a
|
||||
#
|
||||
SECONDS=0
|
||||
log_must zpool import -a -d $DEVICE_DIR -f
|
||||
parallel_time=$SECONDS
|
||||
log_note "asyncronously imported 4 pools in $parallel_time seconds"
|
||||
|
||||
log_must test $parallel_time -lt $(($sequential_time / 3))
|
||||
|
||||
log_pass "Pool imports occur in parallel"
|
|
@ -95,3 +95,10 @@ function exceed_quota
|
|||
log_fail "Returned error code: $zret. Expected: $EDQUOT."
|
||||
return 0
|
||||
}
|
||||
|
||||
function reset_quota
|
||||
{
|
||||
typeset FILESYSTEM="$1"
|
||||
|
||||
log_must zfs set quota=none $FILESYSTEM
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ function cleanup
|
|||
#
|
||||
wait_freeing $TESTPOOL
|
||||
sync_pool $TESTPOOL
|
||||
|
||||
reset_quota $TESTPOOL/$TESTFS
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
|
|
@ -64,6 +64,8 @@ function cleanup
|
|||
|
||||
wait_freeing $TESTPOOL
|
||||
sync_pool $TESTPOOL
|
||||
|
||||
reset_quota $TESTPOOL/$TESTFS
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
|
|
@ -67,6 +67,8 @@ function cleanup
|
|||
#
|
||||
wait_freeing $TESTPOOL
|
||||
sync_pool $TESTPOOL
|
||||
|
||||
reset_quota $TESTPOOL/$TESTCTR/$TESTFS1
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
|
|
@ -65,6 +65,8 @@ function cleanup
|
|||
|
||||
wait_freeing $TESTPOOL
|
||||
sync_pool $TESTPOOL
|
||||
|
||||
reset_quota $TESTPOOL/$TESTCTR/$TESTFS1
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
|
|
@ -50,20 +50,19 @@ function cleanup
|
|||
{
|
||||
datasetexists $fs_child && destroy_dataset $fs_child
|
||||
|
||||
log_must zfs set quota=$quota_val $fs
|
||||
reset_quota $fs
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "Verify that quota doesnot inherit its value from parent."
|
||||
log_onexit cleanup
|
||||
log_assert "Verify that quota does not inherit its value from parent."
|
||||
|
||||
fs=$TESTPOOL/$TESTFS
|
||||
fs_child=$TESTPOOL/$TESTFS/$TESTFS
|
||||
|
||||
space_avail=$(get_prop available $fs)
|
||||
quota_val=$(get_prop quota $fs)
|
||||
typeset -i quotasize=$space_avail
|
||||
typeset -li quotasize=$space_avail
|
||||
((quotasize = quotasize * 2 ))
|
||||
log_must zfs set quota=$quotasize $fs
|
||||
|
||||
|
@ -72,4 +71,4 @@ quota_space=$(get_prop quota $fs_child)
|
|||
[[ $quota_space == $quotasize ]] && \
|
||||
log_fail "The quota of child dataset inherits its value from parent."
|
||||
|
||||
log_pass "quota doesnot inherit its value from parent as expected."
|
||||
log_pass "quota does not inherit its value from parent as expected."
|
||||
|
|
|
@ -50,7 +50,7 @@ log_assert "Verify cannot set quota lower than the space currently in use"
|
|||
|
||||
function cleanup
|
||||
{
|
||||
log_must zfs set quota=none $TESTPOOL/$TESTFS
|
||||
reset_quota $TESTPOOL/$TESTFS
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
|
|
@ -44,8 +44,6 @@ user_ns_cleanup() {
|
|||
log_must zfs destroy -r "$TESTPOOL/userns"
|
||||
}
|
||||
|
||||
log_onexit user_ns_cleanup
|
||||
|
||||
log_assert "Check zfs zone command handling of non-namespace files"
|
||||
|
||||
# Pass if user namespaces are not supported.
|
||||
|
@ -54,6 +52,8 @@ if [ "$?" -ne "0" ]; then
|
|||
log_unsupported "Failed to create user namespace"
|
||||
fi
|
||||
|
||||
log_onexit user_ns_cleanup
|
||||
|
||||
# Create the baseline datasets.
|
||||
log_must zfs create -o zoned=on "$TESTPOOL/userns"
|
||||
|
||||
|
|
|
@ -1179,7 +1179,7 @@
|
|||
/* #undef ZFS_IS_GPL_COMPATIBLE */
|
||||
|
||||
/* Define the project alias string. */
|
||||
#define ZFS_META_ALIAS "zfs-2.2.99-440-FreeBSD_g90ba19eb7"
|
||||
#define ZFS_META_ALIAS "zfs-2.2.99-456-FreeBSD_g1f940de07"
|
||||
|
||||
/* Define the project author. */
|
||||
#define ZFS_META_AUTHOR "OpenZFS"
|
||||
|
@ -1188,7 +1188,7 @@
|
|||
/* #undef ZFS_META_DATA */
|
||||
|
||||
/* Define the maximum compatible kernel version. */
|
||||
#define ZFS_META_KVER_MAX "6.7"
|
||||
#define ZFS_META_KVER_MAX "6.8"
|
||||
|
||||
/* Define the minimum compatible kernel version. */
|
||||
#define ZFS_META_KVER_MIN "3.10"
|
||||
|
@ -1209,7 +1209,7 @@
|
|||
#define ZFS_META_NAME "zfs"
|
||||
|
||||
/* Define the project release. */
|
||||
#define ZFS_META_RELEASE "440-FreeBSD_g90ba19eb7"
|
||||
#define ZFS_META_RELEASE "456-FreeBSD_g1f940de07"
|
||||
|
||||
/* Define the project version. */
|
||||
#define ZFS_META_VERSION "2.2.99"
|
||||
|
|
|
@ -1 +1 @@
|
|||
#define ZFS_META_GITREV "zfs-2.2.99-440-g90ba19eb7"
|
||||
#define ZFS_META_GITREV "zfs-2.2.99-456-g1f940de07"
|
||||
|
|
Loading…
Reference in a new issue