diff options
author | gw25295 <none@none> | 2008-04-11 18:36:28 -0700 |
---|---|---|
committer | gw25295 <none@none> | 2008-04-11 18:36:28 -0700 |
commit | e7cbe64f7a72dae5cb44f100db60ca88f3313c65 (patch) | |
tree | 778467a6522111f338e4644cc2cb895dcecacee4 /usr/src/uts | |
parent | f635d46a9872dc5a02bbbd736f2bf18685c2c221 (diff) | |
download | illumos-joyent-e7cbe64f7a72dae5cb44f100db60ca88f3313c65.tar.gz |
PSARC 2006/370 ZFS Boot Support
5008936 ZFS and/or zvol should support dumps
5070124 dumpadm -d /dev/... does not enforce block device requirement for savecore
6521468 ZFS Boot support Phase 2
6553503 bfu can't find 'rootdev' from /etc/vfstab on a zfs root filesystem
6574993 zfs_mountroot() may need to call clkset() to set the boot_time kstat
6633197 zvol should not permit newfs or createpool while it's in use by swap or dump
6661127 zfs_name_valid() does not support ZFS_TYPE_POOL
6684121 The changes to smf scripts for supporting canmount=noauto will cause a boot failure.
--HG--
rename : usr/src/psm/stand/bootblks/zfs/common/debug-zfs.fth => deleted_files/usr/src/psm/stand/bootblks/zfs/common/debug-zfs.fth
rename : usr/src/psm/stand/bootblks/zfs/common/big-zfs.fth => usr/src/psm/stand/bootblks/zfs/common/fs-zfs.fth
Diffstat (limited to 'usr/src/uts')
43 files changed, 1761 insertions, 178 deletions
diff --git a/usr/src/uts/common/cpr/cpr_main.c b/usr/src/uts/common/cpr/cpr_main.c index 22e1b702f7..31295e1b2c 100644 --- a/usr/src/uts/common/cpr/cpr_main.c +++ b/usr/src/uts/common/cpr/cpr_main.c @@ -70,6 +70,7 @@ extern void (*srn_signal)(int, int); extern void init_cpu_syscall(struct cpu *); extern void i_cpr_pre_resume_cpus(); extern void i_cpr_post_resume_cpus(); +extern int cpr_is_ufs(struct vfs *); extern int pm_powering_down; extern kmutex_t srn_clone_lock; @@ -336,8 +337,14 @@ cpr_ufs_logging(int enable) if (error = cpr_open_deffile(FREAD, &vp)) return (error); - cpr_log_status(enable, &def_status, vp); vfsp = vp->v_vfsp; + if (!cpr_is_ufs(vfsp)) { + (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); + VN_RELE(vp); + return (0); + } + + cpr_log_status(enable, &def_status, vp); (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); diff --git a/usr/src/uts/common/cpr/cpr_misc.c b/usr/src/uts/common/cpr/cpr_misc.c index e35789b4a9..bf5ccf9fcf 100644 --- a/usr/src/uts/common/cpr/cpr_misc.c +++ b/usr/src/uts/common/cpr/cpr_misc.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -71,6 +71,7 @@ static void cpr_save_mp_state(void); #endif int cpr_is_ufs(struct vfs *); +int cpr_is_zfs(struct vfs *); char cpr_default_path[] = CPR_DEFAULT; @@ -240,8 +241,8 @@ cpr_cprconfig_to_path(void) * mounted on the same device as when pmconfig was last run, * and the translation of that device to a node in the prom's * device tree must be the same as when pmconfig was last run. - * for CFT_SPEC, cf_path must be the path to a block special file, - * it must have no file system mounted on it, + * for CFT_SPEC and CFT_ZVOL, cf_path must be the path to a block + * special file, it must have no file system mounted on it, * and the translation of that device to a node in the prom's * device tree must be the same as when pmconfig was last run. */ @@ -278,6 +279,15 @@ cpr_verify_statefile_path(void) switch (cf->cf_type) { case CFT_SPEC: + error = i_devname_to_promname(cf->cf_devfs, devpath, + OBP_MAXPATHLEN); + if (error || strcmp(devpath, cf->cf_dev_prom)) { + cpr_err(CE_CONT, path_chg_fmt, + cf->cf_dev_prom, devpath, rerun); + return (error); + } + /*FALLTHROUGH*/ + case CFT_ZVOL: if (strlen(cf->cf_path) > sizeof (sfpath)) { cpr_err(CE_CONT, long_name); return (ENAMETOOLONG); @@ -304,12 +314,6 @@ cpr_verify_statefile_path(void) return (ENOTSUP); } - error = i_devname_to_promname(cf->cf_devfs, devpath, - OBP_MAXPATHLEN); - if (error || strcmp(devpath, cf->cf_dev_prom)) { - cpr_err(CE_CONT, path_chg_fmt, - cf->cf_dev_prom, devpath, rerun); - } return (error); case CFT_UFS: break; /* don't indent all the original code */ @@ -430,7 +434,8 @@ cpr_check_spec_statefile(void) if (err = cpr_get_config()) return (err); - ASSERT(cprconfig.cf_type == CFT_SPEC); + ASSERT(cprconfig.cf_type == CFT_SPEC || + cprconfig.cf_type == CFT_ZVOL); if (cprconfig.cf_devfs == NULL) return (ENXIO); @@ -1025,6 +1030,8 @@ cpr_build_statefile_path(void) return (NULL); } return (cpr_cprconfig_to_path()); + case CFT_ZVOL: + /*FALLTHROUGH*/ case CFT_SPEC: return (cf->cf_devfs); default: @@ -1049,7 +1056,7 @@ cpr_get_statefile_prom_path(void) ASSERT(cprconfig_loaded); ASSERT(cf->cf_magic == CPR_CONFIG_MAGIC); - ASSERT(cf->cf_type == CFT_SPEC); + ASSERT(cf->cf_type == CFT_SPEC || cf->cf_type == CFT_ZVOL); return (cf->cf_dev_prom); } @@ -1067,6 +1074,15 @@ cpr_is_ufs(struct vfs *vfsp) return (strcmp(fsname, "ufs") == 0); } +int +cpr_is_zfs(struct vfs *vfsp) +{ + char *fsname; + + fsname = vfssw[vfsp->vfs_fstype].vsw_name; + return (strcmp(fsname, "zfs") == 0); +} + /* * This is a list of file systems that are allowed to be writeable when a * reusable statefile checkpoint is taken. They must not have any state that @@ -1121,7 +1137,7 @@ cpr_reusable_mount_check(void) int cpr_statefile_offset(void) { - return (cpr_statefile_is_spec() ? btod(CPR_SPEC_OFFSET) : 0); + return (cprconfig.cf_type != CFT_UFS ? btod(CPR_SPEC_OFFSET) : 0); } /* diff --git a/usr/src/uts/common/cpr/cpr_mod.c b/usr/src/uts/common/cpr/cpr_mod.c index 008cf5d73c..1b26cf38f1 100644 --- a/usr/src/uts/common/cpr/cpr_mod.c +++ b/usr/src/uts/common/cpr/cpr_mod.c @@ -44,6 +44,7 @@ extern int i_cpr_is_supported(int sleeptype); extern int cpr_is_ufs(struct vfs *); +extern int cpr_is_zfs(struct vfs *); extern int cpr_check_spec_statefile(void); extern int cpr_reusable_mount_check(void); extern int i_cpr_reusable_supported(void); @@ -341,7 +342,8 @@ cpr(int fcn, void *mdep) } if (!i_cpr_is_supported(cpr_sleeptype) || - (cpr_sleeptype == CPR_TODISK && !cpr_is_ufs(rootvfs))) + (cpr_sleeptype == CPR_TODISK && + !cpr_is_ufs(rootvfs)&& !cpr_is_zfs(rootvfs))) return (ENOTSUP); if (fcn == AD_CHECK_SUSPEND_TO_RAM || diff --git a/usr/src/uts/common/fs/specfs/specsubr.c b/usr/src/uts/common/fs/specfs/specsubr.c index de5bf62e44..b7158425b7 100644 --- a/usr/src/uts/common/fs/specfs/specsubr.c +++ b/usr/src/uts/common/fs/specfs/specsubr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -820,7 +820,7 @@ specinit(int fstype, char *name) * Create snode cache */ snode_cache = kmem_cache_create("snode_cache", sizeof (struct snode), - 0, snode_constructor, snode_destructor, NULL, NULL, NULL, 0); + 0, snode_constructor, snode_destructor, NULL, NULL, NULL, 0); /* * Associate vfs operations with spec_vfs @@ -1050,3 +1050,19 @@ spec_unfence_snode(dev_info_t *dip) return (0); } + +void +spec_size_invalidate(dev_t dev, vtype_t type) +{ + + struct snode *csp; + + mutex_enter(&stable_lock); + if ((csp = sfind(dev, type, NULL)) != NULL) { + mutex_enter(&csp->s_lock); + csp->s_flag &= ~SSIZEVALID; + VN_RELE(STOV(csp)); + mutex_exit(&csp->s_lock); + } + mutex_exit(&stable_lock); +} diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c index c9d154327e..3c2008f599 100644 --- a/usr/src/uts/common/fs/vfs.c +++ b/usr/src/uts/common/fs/vfs.c @@ -84,6 +84,7 @@ #include <sys/console.h> #include <sys/reboot.h> #include <sys/attr.h> +#include <sys/spa.h> #include <vm/page.h> @@ -353,6 +354,13 @@ fs_copyfsops(const fs_operation_def_t *template, vfsops_t *actual, return (fs_build_vector(actual, unused_ops, vfs_ops_table, template)); } +void +zfs_boot_init() { + + if (strcmp(rootfs.bo_fstype, MNTTYPE_ZFS) == 0) + spa_boot_init(); +} + int vfs_setfsops(int fstype, const fs_operation_def_t *template, vfsops_t **actual) { @@ -842,6 +850,12 @@ vfs_mountroot(void) * root filesystem instead of the boot program's services. */ modrootloaded = 1; + + /* + * Special handling for a ZFS root file system. + */ + zfs_boot_init(); + /* * Set up mnttab information for root */ diff --git a/usr/src/uts/common/fs/zfs/dmu_traverse.c b/usr/src/uts/common/fs/zfs/dmu_traverse.c index 3d2bc3e476..cdfb442f67 100644 --- a/usr/src/uts/common/fs/zfs/dmu_traverse.c +++ b/usr/src/uts/common/fs/zfs/dmu_traverse.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -35,6 +35,7 @@ #include <sys/spa.h> #include <sys/zio.h> #include <sys/dmu_impl.h> +#include <sys/zvol.h> #define BP_SPAN_SHIFT(level, width) ((level) * (width)) @@ -261,6 +262,16 @@ advance_block(zseg_t *zseg, dnode_phys_t *dnp, int rc, int advance) return (EAGAIN); } +/* + * The traverse_callback function will call the function specified in th_func. + * In the event of an error the callee, specified by th_func, must return + * one of the following errors: + * + * EINTR - Indicates that the callee wants the traversal to + * abort immediately. + * ERESTART - The callee has acknowledged the error and would + * like to continue. + */ static int traverse_callback(traverse_handle_t *th, zseg_t *zseg, traverse_blk_cache_t *bc) { @@ -722,6 +733,24 @@ traverse_dsl_dataset(dsl_dataset_t *ds, uint64_t txg_start, int advance, } int +traverse_zvol(objset_t *os, int advance, blkptr_cb_t func, void *arg) +{ + spa_t *spa = dmu_objset_spa(os); + traverse_handle_t *th; + int err; + + th = traverse_init(spa, func, arg, advance, ZIO_FLAG_CANFAIL); + + traverse_add_dnode(th, 0, -1ULL, dmu_objset_id(os), ZVOL_OBJ); + + while ((err = traverse_more(th)) == EAGAIN) + continue; + + traverse_fini(th); + return (err); +} + +int traverse_more(traverse_handle_t *th) { zseg_t *zseg = list_head(&th->th_seglist); diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c index 81c52ce9de..733817fd54 100644 --- a/usr/src/uts/common/fs/zfs/spa.c +++ b/usr/src/uts/common/fs/zfs/spa.c @@ -60,6 +60,7 @@ #include <sys/callb.h> #include <sys/systeminfo.h> #include <sys/sunddi.h> +#include <sys/spa_boot.h> #include "zfs_prop.h" #include "zfs_comutil.h" @@ -2033,8 +2034,9 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, * Import the given pool into the system. We set up the necessary spa_t and * then call spa_load() to do the dirty work. */ -int -spa_import(const char *pool, nvlist_t *config, nvlist_t *props) +static int +spa_import_common(const char *pool, nvlist_t *config, nvlist_t *props, + boolean_t isroot) { spa_t *spa; char *altroot = NULL; @@ -2042,6 +2044,7 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props) nvlist_t *nvroot; nvlist_t **spares, **l2cache; uint_t nspares, nl2cache; + int mosconfig = isroot? B_FALSE : B_TRUE; /* * If a pool with this name exists, return failure. @@ -2065,19 +2068,19 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props) * Pass TRUE for mosconfig because the user-supplied config * is actually the one to trust when doing an import. */ - error = spa_load(spa, config, SPA_LOAD_IMPORT, B_TRUE); + error = spa_load(spa, config, SPA_LOAD_IMPORT, mosconfig); spa_config_enter(spa, RW_WRITER, FTAG); /* * Toss any existing sparelist, as it doesn't have any validity anymore, * and conflicts with spa_has_spare(). */ - if (spa->spa_spares.sav_config) { + if (!isroot && spa->spa_spares.sav_config) { nvlist_free(spa->spa_spares.sav_config); spa->spa_spares.sav_config = NULL; spa_load_spares(spa); } - if (spa->spa_l2cache.sav_config) { + if (!isroot && spa->spa_l2cache.sav_config) { nvlist_free(spa->spa_l2cache.sav_config); spa->spa_l2cache.sav_config = NULL; spa_load_l2cache(spa); @@ -2139,12 +2142,12 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props) * Update the config cache to include the newly-imported pool. */ if (spa_mode & FWRITE) - spa_config_update(spa, SPA_CONFIG_UPDATE_POOL); + spa_config_update_common(spa, SPA_CONFIG_UPDATE_POOL, isroot); /* * Resilver anything that's out of date. */ - if (spa_mode & FWRITE) + if (!isroot && (spa_mode & FWRITE)) VERIFY(spa_scrub(spa, POOL_SCRUB_RESILVER, B_TRUE) == 0); mutex_exit(&spa_namespace_lock); @@ -2152,6 +2155,148 @@ spa_import(const char *pool, nvlist_t *config, nvlist_t *props) return (0); } +#ifdef _KERNEL +/* + * Build a "root" vdev for a top level vdev read in from a rootpool + * device label. + */ +static void +spa_build_rootpool_config(nvlist_t *config) +{ + nvlist_t *nvtop, *nvroot; + uint64_t pgid; + + /* + * Add this top-level vdev to the child array. + */ + VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvtop) + == 0); + VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pgid) + == 0); + + /* + * Put this pool's top-level vdevs into a root vdev. + */ + VERIFY(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, KM_SLEEP) == 0); + VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) + == 0); + VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0); + VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, pgid) == 0); + VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + &nvtop, 1) == 0); + + /* + * Replace the existing vdev_tree with the new root vdev in + * this pool's configuration (remove the old, add the new). + */ + VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0); + nvlist_free(nvroot); +} + +/* + * Get the root pool information from the root disk, then import the root pool + * during the system boot up time. + */ +extern nvlist_t *vdev_disk_read_rootlabel(char *); + +void +spa_check_rootconf(char *devpath, char **bestdev, nvlist_t **bestconf, + uint64_t *besttxg) +{ + nvlist_t *config; + uint64_t txg; + + if ((config = vdev_disk_read_rootlabel(devpath)) == NULL) + return; + + VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0); + + if (txg > *besttxg) { + *besttxg = txg; + if (*bestconf != NULL) + nvlist_free(*bestconf); + *bestconf = config; + *bestdev = devpath; + } +} + +boolean_t +spa_rootdev_validate(nvlist_t *nv) +{ + uint64_t ival; + + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 || + nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 || + nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DEGRADED, &ival) == 0 || + nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * Import a root pool. + * + * For x86. devpath_list will consist the physpath name of the vdev in a single + * disk root pool or a list of physnames for the vdevs in a mirrored rootpool. + * e.g. + * "/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a" + * + * For Sparc, devpath_list consists the physpath name of the booting device + * no matter the rootpool is a single device pool or a mirrored pool. + * e.g. + * "/pci@1f,0/ide@d/disk@0,0:a" + */ +int +spa_import_rootpool(char *devpath_list) +{ + nvlist_t *conf = NULL; + char *dev = NULL; + char *pname; + int error; + + /* + * Get the vdev pathname and configuation from the most + * recently updated vdev (highest txg). + */ + if (error = spa_get_rootconf(devpath_list, &dev, &conf)) + goto msg_out; + + /* + * Add type "root" vdev to the config. + */ + spa_build_rootpool_config(conf); + + VERIFY(nvlist_lookup_string(conf, ZPOOL_CONFIG_POOL_NAME, &pname) == 0); + + error = spa_import_common(pname, conf, NULL, TRUE); + if (error == EEXIST) + error = 0; + + nvlist_free(conf); + return (error); + +msg_out: + cmn_err(CE_NOTE, "\n\n" + " *************************************************** \n" + " * This device is not bootable! * \n" + " * It is either offlined or detached or faulted. * \n" + " * Please try to boot from a different device. * \n" + " *************************************************** \n\n"); + + return (error); +} +#endif + +/* + * Import a non-root pool into the system. + */ +int +spa_import(const char *pool, nvlist_t *config, nvlist_t *props) +{ + return (spa_import_common(pool, config, props, FALSE)); +} + /* * This (illegal) pool name is used when temporarily importing a spa_t in order * to get the vdev stats associated with the imported devices. @@ -2201,6 +2346,38 @@ spa_tryimport(nvlist_t *tryconfig) spa->spa_uberblock.ub_timestamp) == 0); /* + * If the bootfs property exists on this pool then we + * copy it out so that external consumers can tell which + * pools are bootable. + */ + if (spa->spa_bootfs) { + char *tmpname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + /* + * We have to play games with the name since the + * pool was opened as TRYIMPORT_NAME. + */ + if (dsl_dsobj_to_dsname(spa->spa_name, + spa->spa_bootfs, tmpname) == 0) { + char *cp; + char *dsname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + cp = strchr(tmpname, '/'); + if (cp == NULL) { + (void) strlcpy(dsname, tmpname, + MAXPATHLEN); + } else { + (void) snprintf(dsname, MAXPATHLEN, + "%s/%s", poolname, ++cp); + } + VERIFY(nvlist_add_string(config, + ZPOOL_CONFIG_BOOTFS, dsname) == 0); + kmem_free(dsname, MAXPATHLEN); + } + kmem_free(tmpname, MAXPATHLEN); + } + + /* * Add the list of hot spares and level 2 cache devices. */ spa_add_spares(spa, config); diff --git a/usr/src/uts/common/fs/zfs/spa_config.c b/usr/src/uts/common/fs/zfs/spa_config.c index 17978ccc25..d83553c713 100644 --- a/usr/src/uts/common/fs/zfs/spa_config.c +++ b/usr/src/uts/common/fs/zfs/spa_config.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -430,12 +430,24 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) } /* - * Update all disk labels, generate a fresh config based on the current - * in-core state, and sync the global config cache. + * For a pool that's not currently a booting rootpool, update all disk labels, + * generate a fresh config based on the current in-core state, and sync the + * global config cache. */ void spa_config_update(spa_t *spa, int what) { + spa_config_update_common(spa, what, FALSE); +} + +/* + * Update all disk labels, generate a fresh config based on the current + * in-core state, and sync the global config cache (do not sync the config + * cache if this is a booting rootpool). + */ +void +spa_config_update_common(spa_t *spa, int what, boolean_t isroot) +{ vdev_t *rvd = spa->spa_root_vdev; uint64_t txg; int c; @@ -472,8 +484,9 @@ spa_config_update(spa_t *spa, int what) /* * Update the global config cache to reflect the new mosconfig. */ - spa_config_sync(); + if (!isroot) + spa_config_sync(); if (what == SPA_CONFIG_UPDATE_POOL) - spa_config_update(spa, SPA_CONFIG_UPDATE_VDEVS); + spa_config_update_common(spa, SPA_CONFIG_UPDATE_VDEVS, isroot); } diff --git a/usr/src/uts/common/fs/zfs/spa_misc.c b/usr/src/uts/common/fs/zfs/spa_misc.c index 311bec6da7..375bce75ee 100644 --- a/usr/src/uts/common/fs/zfs/spa_misc.c +++ b/usr/src/uts/common/fs/zfs/spa_misc.c @@ -1211,6 +1211,12 @@ spa_busy(void) } void +spa_boot_init() +{ + spa_config_load(); +} + +void spa_init(int mode) { mutex_init(&spa_namespace_lock, NULL, MUTEX_DEFAULT, NULL); diff --git a/usr/src/uts/common/fs/zfs/sys/dmu_traverse.h b/usr/src/uts/common/fs/zfs/sys/dmu_traverse.h index ea9fa6c1e3..05e5ffdbff 100644 --- a/usr/src/uts/common/fs/zfs/sys/dmu_traverse.h +++ b/usr/src/uts/common/fs/zfs/sys/dmu_traverse.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -100,6 +100,7 @@ struct traverse_handle { int traverse_dsl_dataset(struct dsl_dataset *ds, uint64_t txg_start, int advance, blkptr_cb_t func, void *arg); +int traverse_zvol(objset_t *os, int advance, blkptr_cb_t func, void *arg); traverse_handle_t *traverse_init(spa_t *spa, blkptr_cb_t *func, void *arg, int advance, int zio_flags); diff --git a/usr/src/uts/common/fs/zfs/sys/spa.h b/usr/src/uts/common/fs/zfs/sys/spa.h index 713817d89c..2cf4fbb6fa 100644 --- a/usr/src/uts/common/fs/zfs/sys/spa.h +++ b/usr/src/uts/common/fs/zfs/sys/spa.h @@ -326,6 +326,10 @@ extern int spa_get_stats(const char *pool, nvlist_t **config, char *altroot, size_t buflen); extern int spa_create(const char *pool, nvlist_t *config, nvlist_t *props, const char *history_str); +extern void spa_check_rootconf(char *devpath, char **the_dev_p, + nvlist_t **the_conf_p, uint64_t *the_txg_p); +extern boolean_t spa_rootdev_validate(nvlist_t *nv); +extern int spa_import_rootpool(char *devpath); extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props); extern nvlist_t *spa_tryimport(nvlist_t *tryconfig); extern int spa_destroy(char *pool); @@ -390,6 +394,7 @@ extern void spa_config_set(spa_t *spa, nvlist_t *config); extern nvlist_t *spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats); extern void spa_config_update(spa_t *spa, int what); +extern void spa_config_update_common(spa_t *spa, int what, boolean_t isroot); /* * Miscellaneous SPA routines in spa_misc.c @@ -430,7 +435,6 @@ extern uint64_t spa_first_txg(spa_t *spa); extern uint64_t spa_version(spa_t *spa); extern int spa_state(spa_t *spa); extern uint64_t spa_freeze_txg(spa_t *spa); -struct metaslab_class; extern uint64_t spa_get_alloc(spa_t *spa); extern uint64_t spa_get_space(spa_t *spa); extern uint64_t spa_get_dspace(spa_t *spa); @@ -502,6 +506,7 @@ extern void vdev_cache_stat_fini(void); /* Initialization and termination */ extern void spa_init(int flags); extern void spa_fini(void); +extern void spa_boot_init(); /* properties */ extern int spa_prop_set(spa_t *spa, nvlist_t *nvp); diff --git a/usr/src/uts/common/fs/zfs/sys/spa_boot.h b/usr/src/uts/common/fs/zfs/sys/spa_boot.h new file mode 100644 index 0000000000..b178ae0ac2 --- /dev/null +++ b/usr/src/uts/common/fs/zfs/sys/spa_boot.h @@ -0,0 +1,46 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_SPA_BOOT_H +#define _SYS_SPA_BOOT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/nvpair.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *spa_get_bootfs(); +extern void spa_free_bootfs(char *bootfs); +extern int spa_get_rootconf(char *devpath, char **bestdev_p, + nvlist_t **bestconf_p); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_SPA_BOOT_H */ diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_disk.h b/usr/src/uts/common/fs/zfs/sys/vdev_disk.h index 95536a77db..b748571ea0 100644 --- a/usr/src/uts/common/fs/zfs/sys/vdev_disk.h +++ b/usr/src/uts/common/fs/zfs/sys/vdev_disk.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -31,6 +30,8 @@ #include <sys/vdev.h> #ifdef _KERNEL +#include <sys/buf.h> +#include <sys/ddi.h> #include <sys/sunldi.h> #include <sys/sunddi.h> #endif @@ -45,6 +46,9 @@ typedef struct vdev_disk { ldi_handle_t vd_lh; } vdev_disk_t; +#ifdef _KERNEL +extern int vdev_disk_physio(ldi_handle_t, caddr_t, size_t, uint64_t, int); +#endif #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/fs/zfs/sys/zio.h b/usr/src/uts/common/fs/zfs/sys/zio.h index a62551ca9c..5c14ae5d0a 100644 --- a/usr/src/uts/common/fs/zfs/sys/zio.h +++ b/usr/src/uts/common/fs/zfs/sys/zio.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -178,10 +178,11 @@ enum zio_compress { #define ZIO_PIPELINE_STOP 0x101 /* - * We'll take the unused errno 'EBADE' (from the Convergent graveyard) - * to indicate checksum errors. + * We'll take the unused errnos, 'EBADE' and 'EBADR' (from the Convergent + * graveyard) to indicate checksum errors and fragmentation. */ #define ECKSUM EBADE +#define EFRAGS EBADR typedef struct zio zio_t; typedef void zio_done_func_t(zio_t *zio); diff --git a/usr/src/uts/common/fs/zfs/sys/zvol.h b/usr/src/uts/common/fs/zfs/sys/zvol.h index f7a0f8fd4e..06adc667e1 100644 --- a/usr/src/uts/common/fs/zfs/sys/zvol.h +++ b/usr/src/uts/common/fs/zfs/sys/zvol.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -35,6 +35,9 @@ extern "C" { #endif +#define ZVOL_OBJ 1ULL +#define ZVOL_ZAP_OBJ 2ULL + #ifdef _KERNEL extern int zvol_check_volsize(uint64_t volsize, uint64_t blocksize); extern int zvol_check_volblocksize(uint64_t volblocksize); @@ -46,6 +49,7 @@ extern int zvol_set_volsize(const char *, major_t, uint64_t); extern int zvol_set_volblocksize(const char *, uint64_t); extern int zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr); +extern int zvol_dump(dev_t dev, caddr_t addr, daddr_t offset, int nblocks); extern int zvol_close(dev_t dev, int flag, int otyp, cred_t *cr); extern int zvol_strategy(buf_t *bp); extern int zvol_read(dev_t dev, uio_t *uiop, cred_t *cr); diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c index 933ed3e2bf..b586e23f71 100644 --- a/usr/src/uts/common/fs/zfs/vdev_disk.c +++ b/usr/src/uts/common/fs/zfs/vdev_disk.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -27,6 +27,7 @@ #include <sys/zfs_context.h> #include <sys/spa.h> +#include <sys/refcount.h> #include <sys/vdev_disk.h> #include <sys/vdev_impl.h> #include <sys/fs/zfs.h> @@ -266,29 +267,45 @@ vdev_disk_close(vdev_t *vd) vd->vdev_tsd = NULL; } +int +vdev_disk_physio(ldi_handle_t vd_lh, caddr_t data, size_t size, + uint64_t offset, int flags) +{ + buf_t *bp; + int error = 0; + + if (vd_lh == NULL) + return (EINVAL); + + ASSERT(flags & B_READ || flags & B_WRITE); + + bp = getrbuf(KM_SLEEP); + bp->b_flags = flags | B_BUSY | B_NOCACHE | B_FAILFAST; + bp->b_bcount = size; + bp->b_un.b_addr = (void *)data; + bp->b_lblkno = lbtodb(offset); + bp->b_bufsize = size; + + error = ldi_strategy(vd_lh, bp); + ASSERT(error == 0); + if ((error = biowait(bp)) == 0 && bp->b_resid != 0) + error = EIO; + freerbuf(bp); + + return (error); +} + static int vdev_disk_probe_io(vdev_t *vd, caddr_t data, size_t size, uint64_t offset, int flags) { - buf_t buf; int error = 0; vdev_disk_t *dvd = vd->vdev_tsd; if (vd == NULL || dvd == NULL || dvd->vd_lh == NULL) return (EINVAL); - ASSERT(flags & B_READ || flags & B_WRITE); - - bioinit(&buf); - buf.b_flags = flags | B_BUSY | B_NOCACHE | B_FAILFAST; - buf.b_bcount = size; - buf.b_un.b_addr = (void *)data; - buf.b_lblkno = lbtodb(offset); - buf.b_bufsize = size; - - error = ldi_strategy(dvd->vd_lh, &buf); - ASSERT(error == 0); - error = biowait(&buf); + error = vdev_disk_physio(dvd->vd_lh, data, size, offset, flags); if (zio_injection_enabled && error == 0) error = zio_handle_device_injection(vd, EIO); @@ -558,3 +575,65 @@ vdev_ops_t vdev_disk_ops = { VDEV_TYPE_DISK, /* name of this vdev type */ B_TRUE /* leaf vdev */ }; + +/* + * Given the root disk device pathname, read the label from the device, + * and construct a configuration nvlist. + */ +nvlist_t * +vdev_disk_read_rootlabel(char *devpath) +{ + nvlist_t *config = NULL; + ldi_handle_t vd_lh; + vdev_label_t *label; + uint64_t s, size; + int l; + + /* + * Read the device label and build the nvlist. + */ + if (ldi_open_by_name(devpath, FREAD, kcred, &vd_lh, zfs_li)) + return (NULL); + + if (ldi_get_size(vd_lh, &s)) + return (NULL); + + size = P2ALIGN_TYPED(s, sizeof (vdev_label_t), uint64_t); + label = kmem_alloc(sizeof (vdev_label_t), KM_SLEEP); + + for (l = 0; l < VDEV_LABELS; l++) { + uint64_t offset, state, txg = 0; + + /* read vdev label */ + offset = vdev_label_offset(size, l, 0); + if (vdev_disk_physio(vd_lh, (caddr_t)label, + VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE + + VDEV_PHYS_SIZE, offset, B_READ) != 0) + continue; + + if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, + sizeof (label->vl_vdev_phys.vp_nvlist), &config, 0) != 0) { + config = NULL; + continue; + } + + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, + &state) != 0 || state >= POOL_STATE_DESTROYED) { + nvlist_free(config); + config = NULL; + continue; + } + + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, + &txg) != 0 || txg == 0) { + nvlist_free(config); + config = NULL; + continue; + } + + break; + } + + kmem_free(label, sizeof (vdev_label_t)); + return (config); +} diff --git a/usr/src/uts/common/fs/zfs/zfs_fm.c b/usr/src/uts/common/fs/zfs/zfs_fm.c index c5fcb89633..654178a57b 100644 --- a/usr/src/uts/common/fs/zfs/zfs_fm.c +++ b/usr/src/uts/common/fs/zfs/zfs_fm.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -263,6 +263,9 @@ zfs_ereport_post(const char *subclass, spa_t *spa, vdev_t *vd, zio_t *zio, */ if (zio->io_logical != NULL) fm_payload_set(ereport, + FM_EREPORT_PAYLOAD_ZFS_ZIO_OBJSET, + DATA_TYPE_UINT64, + zio->io_logical->io_bookmark.zb_objset, FM_EREPORT_PAYLOAD_ZFS_ZIO_OBJECT, DATA_TYPE_UINT64, zio->io_logical->io_bookmark.zb_object, diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index f559dba7e0..8915ae53f0 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -984,8 +984,8 @@ zfs_ioc_vdev_add(zfs_cmd_t *zc) { spa_t *spa; int error; - nvlist_t *config, **l2cache; - uint_t nl2cache; + nvlist_t *config, **l2cache, **spares; + uint_t nl2cache = 0, nspares = 0; error = spa_open(zc->zc_name, &spa, FTAG); if (error != 0) @@ -996,13 +996,20 @@ zfs_ioc_vdev_add(zfs_cmd_t *zc) (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache); + (void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES, + &spares, &nspares); + /* * A root pool with concatenated devices is not supported. - * Thus, can not add a device to a root pool with one device. - * Allow for l2cache devices to be added. + * Thus, can not add a device to a root pool. + * + * Intent log device can not be added to a rootpool because + * during mountroot, zil is replayed, a seperated log device + * can not be accessed during the mountroot time. + * + * l2cache and spare devices are ok to be added to a rootpool. */ - if (spa->spa_root_vdev->vdev_children == 1 && spa->spa_bootfs != 0 && - nl2cache == 0) { + if (spa->spa_bootfs != 0 && nl2cache == 0 && nspares == 0) { spa_close(spa, FTAG); return (EDOM); } @@ -1348,7 +1355,7 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc) return (error); } -static int +int zfs_set_prop_nvlist(const char *name, nvlist_t *nvl) { nvpair_t *elem; @@ -1919,6 +1926,7 @@ zfs_ioc_create(zfs_cmd_t *zc) default: cbfunc = NULL; + break; } if (strchr(zc->zc_name, '@') || strchr(zc->zc_name, '%')) @@ -2040,6 +2048,7 @@ zfs_ioc_create(zfs_cmd_t *zc) error = dmu_objset_create(zc->zc_name, type, NULL, cbfunc, &zct); nvlist_free(zct.zct_zplprops); + } /* @@ -2049,7 +2058,6 @@ zfs_ioc_create(zfs_cmd_t *zc) if ((error = zfs_set_prop_nvlist(zc->zc_name, nvprops)) != 0) (void) dmu_objset_destroy(zc->zc_name); } - nvlist_free(nvprops); return (error); } @@ -2934,7 +2942,7 @@ static struct cb_ops zfs_cb_ops = { zvol_close, /* close */ zvol_strategy, /* strategy */ nodev, /* print */ - nodev, /* dump */ + zvol_dump, /* dump */ zvol_read, /* read */ zvol_write, /* write */ zfsdev_ioctl, /* ioctl */ diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index 07676602bd..c524cb5eaa 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -61,6 +61,7 @@ #include <sys/sunddi.h> #include <sys/dnlc.h> #include <sys/dmu_objset.h> +#include <sys/spa_boot.h> int zfsfstype; vfsops_t *zfs_vfsops = NULL; @@ -830,7 +831,7 @@ str_to_uint64(char *str, uint64_t *objnum) * string to a dataset name: "rootpool-name/root-filesystem-name". */ static int -parse_bootpath(char *bpath, char *outpath) +zfs_parse_bootfs(char *bpath, char *outpath) { char *slashp; uint64_t objnum; @@ -861,60 +862,66 @@ static int zfs_mountroot(vfs_t *vfsp, enum whymountroot why) { int error = 0; - int ret = 0; static int zfsrootdone = 0; zfsvfs_t *zfsvfs = NULL; znode_t *zp = NULL; vnode_t *vp = NULL; - char *zfs_bootpath; -#if defined(_OBP) - int proplen; -#endif + char *zfs_bootfs; ASSERT(vfsp); /* * The filesystem that we mount as root is defined in the - * "zfs-bootfs" property. + * boot property "zfs-bootfs" with a format of + * "poolname/root-dataset-objnum". */ if (why == ROOT_INIT) { if (zfsrootdone++) return (EBUSY); + /* + * the process of doing a spa_load will require the + * clock to be set before we could (for example) do + * something better by looking at the timestamp on + * an uberblock, so just set it to -1. + */ + clkset(-1); + + if ((zfs_bootfs = spa_get_bootfs()) == NULL) { + cmn_err(CE_NOTE, "\nspa_get_bootfs: can not get " + "bootfs name \n"); + return (EINVAL); + } -#if defined(_OBP) - proplen = BOP_GETPROPLEN(bootops, "zfs-bootfs"); - if (proplen == 0) - return (EIO); - zfs_bootpath = kmem_zalloc(proplen, KM_SLEEP); - if (BOP_GETPROP(bootops, "zfs-bootfs", zfs_bootpath) == -1) { - kmem_free(zfs_bootpath, proplen); - return (EIO); + if (error = spa_import_rootpool(rootfs.bo_name)) { + spa_free_bootfs(zfs_bootfs); + cmn_err(CE_NOTE, "\nspa_import_rootpool: error %d\n", + error); + return (error); } - error = parse_bootpath(zfs_bootpath, rootfs.bo_name); - kmem_free(zfs_bootpath, proplen); -#else - if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), - DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bootpath) != - DDI_SUCCESS) - return (EIO); - - error = parse_bootpath(zfs_bootpath, rootfs.bo_name); - ddi_prop_free(zfs_bootpath); -#endif - - if (error) + + if (error = zfs_parse_bootfs(zfs_bootfs, rootfs.bo_name)) { + spa_free_bootfs(zfs_bootfs); + cmn_err(CE_NOTE, "\nzfs_parse_bootfs: error %d\n", + error); return (error); + } + + spa_free_bootfs(zfs_bootfs); if (error = vfs_lock(vfsp)) return (error); - if (error = zfs_domount(vfsp, rootfs.bo_name, CRED())) + if (error = zfs_domount(vfsp, rootfs.bo_name, CRED())) { + cmn_err(CE_NOTE, "\nzfs_domount: error %d\n", error); goto out; + } zfsvfs = (zfsvfs_t *)vfsp->vfs_data; ASSERT(zfsvfs); - if (error = zfs_zget(zfsvfs, zfsvfs->z_root, &zp)) + if (error = zfs_zget(zfsvfs, zfsvfs->z_root, &zp)) { + cmn_err(CE_NOTE, "\nzfs_zget: error %d\n", error); goto out; + } vp = ZTOV(zp); mutex_enter(&vp->v_lock); @@ -928,17 +935,11 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why) */ VN_RELE(vp); - /* - * Mount root as readonly initially, it will be remouted - * read/write by /lib/svc/method/fs-usr. - */ - readonly_changed_cb(vfsp->vfs_data, B_TRUE); vfs_add((struct vnode *)0, vfsp, (vfsp->vfs_flag & VFS_RDONLY) ? MS_RDONLY : 0); out: vfs_unlock(vfsp); - ret = (error) ? error : 0; - return (ret); + return (error); } else if (why == ROOT_REMOUNT) { readonly_changed_cb(vfsp->vfs_data, B_FALSE); vfsp->vfs_flag |= VFS_REMOUNT; diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c index 171932cc6b..5140e43966 100644 --- a/usr/src/uts/common/fs/zfs/zvol.c +++ b/usr/src/uts/common/fs/zfs/zvol.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -53,6 +53,9 @@ #include <sys/zap.h> #include <sys/spa.h> #include <sys/zio.h> +#include <sys/dmu_traverse.h> +#include <sys/dnode.h> +#include <sys/dsl_dataset.h> #include <sys/dsl_prop.h> #include <sys/dkio.h> #include <sys/efi_partition.h> @@ -70,14 +73,17 @@ #include <sys/refcount.h> #include <sys/zfs_znode.h> #include <sys/zfs_rlock.h> +#include <sys/vdev_disk.h> +#include <sys/vdev_impl.h> +#include <sys/zvol.h> +#include <sys/dumphdr.h> #include "zfs_namecheck.h" -#define ZVOL_OBJ 1ULL -#define ZVOL_ZAP_OBJ 2ULL - static void *zvol_state; +#define ZVOL_DUMPSIZE "dumpsize" + /* * This lock protects the zvol_state structure from being modified * while it's being used, e.g. an open that comes in before a create @@ -87,6 +93,22 @@ static void *zvol_state; static kmutex_t zvol_state_lock; static uint32_t zvol_minors; +#define NUM_EXTENTS ((SPA_MAXBLOCKSIZE) / sizeof (zvol_extent_t)) + +typedef struct zvol_extent { + dva_t ze_dva; /* dva associated with this extent */ + uint64_t ze_stride; /* extent stride */ + uint64_t ze_size; /* number of blocks in extent */ +} zvol_extent_t; + +/* + * The list of extents associated with the dump device + */ +typedef struct zvol_ext_list { + zvol_extent_t zl_extents[NUM_EXTENTS]; + struct zvol_ext_list *zl_next; +} zvol_ext_list_t; + /* * The in-core state of each volume. */ @@ -96,22 +118,33 @@ typedef struct zvol_state { uint64_t zv_volblocksize; /* volume block size */ minor_t zv_minor; /* minor number */ uint8_t zv_min_bs; /* minimum addressable block shift */ - uint8_t zv_readonly; /* hard readonly; like write-protect */ + uint8_t zv_flags; /* readonly; dumpified */ objset_t *zv_objset; /* objset handle */ uint32_t zv_mode; /* DS_MODE_* flags at open time */ uint32_t zv_open_count[OTYPCNT]; /* open counts */ uint32_t zv_total_opens; /* total open count */ zilog_t *zv_zilog; /* ZIL handle */ + zvol_ext_list_t *zv_list; /* List of extents for dump */ uint64_t zv_txg_assign; /* txg to assign during ZIL replay */ znode_t zv_znode; /* for range locking */ } zvol_state_t; /* + * zvol specific flags + */ +#define ZVOL_RDONLY 0x1 +#define ZVOL_DUMPIFIED 0x2 + +/* * zvol maximum transfer in one DMU tx. */ int zvol_maxphys = DMU_MAX_ACCESS/2; +extern int zfs_set_prop_nvlist(const char *, nvlist_t *); static int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio); +static int zvol_dumpify(zvol_state_t *zv); +static int zvol_dump_fini(zvol_state_t *zv); +static int zvol_dump_init(zvol_state_t *zv, boolean_t resize); static void zvol_size_changed(zvol_state_t *zv, major_t maj) @@ -122,6 +155,10 @@ zvol_size_changed(zvol_state_t *zv, major_t maj) "Size", zv->zv_volsize) == DDI_SUCCESS); VERIFY(ddi_prop_update_int64(dev, zfs_dip, "Nblocks", lbtodb(zv->zv_volsize)) == DDI_SUCCESS); + + /* Notify specfs to invalidate the cached size */ + spec_size_invalidate(dev, VBLK); + spec_size_invalidate(dev, VCHR); } int @@ -156,7 +193,10 @@ zvol_readonly_changed_cb(void *arg, uint64_t newval) { zvol_state_t *zv = arg; - zv->zv_readonly = (uint8_t)newval; + if (newval) + zv->zv_flags |= ZVOL_RDONLY; + else + zv->zv_flags &= ~ZVOL_RDONLY; } int @@ -219,6 +259,131 @@ zvol_minor_lookup(const char *name) return (zv); } +void +zvol_init_extent(zvol_extent_t *ze, blkptr_t *bp) +{ + ze->ze_dva = bp->blk_dva[0]; /* structure assignment */ + ze->ze_stride = 0; + ze->ze_size = 1; +} + +/* extent mapping arg */ +struct maparg { + zvol_ext_list_t *ma_list; + zvol_extent_t *ma_extent; + int ma_gang; +}; + +/*ARGSUSED*/ +static int +zvol_map_block(traverse_blk_cache_t *bc, spa_t *spa, void *arg) +{ + zbookmark_t *zb = &bc->bc_bookmark; + blkptr_t *bp = &bc->bc_blkptr; + void *data = bc->bc_data; + dnode_phys_t *dnp = bc->bc_dnode; + struct maparg *ma = (struct maparg *)arg; + uint64_t stride; + + /* If there is an error, then keep trying to make progress */ + if (bc->bc_errno) + return (ERESTART); + +#ifdef ZFS_DEBUG + if (zb->zb_level == -1) { + ASSERT3U(BP_GET_TYPE(bp), ==, DMU_OT_OBJSET); + ASSERT3U(BP_GET_LEVEL(bp), ==, 0); + } else { + ASSERT3U(BP_GET_TYPE(bp), ==, dnp->dn_type); + ASSERT3U(BP_GET_LEVEL(bp), ==, zb->zb_level); + } + + if (zb->zb_level > 0) { + uint64_t fill = 0; + blkptr_t *bpx, *bpend; + + for (bpx = data, bpend = bpx + BP_GET_LSIZE(bp) / sizeof (*bpx); + bpx < bpend; bpx++) { + if (bpx->blk_birth != 0) { + fill += bpx->blk_fill; + } else { + ASSERT(bpx->blk_fill == 0); + } + } + ASSERT3U(fill, ==, bp->blk_fill); + } + + if (zb->zb_level == 0 && dnp->dn_type == DMU_OT_DNODE) { + uint64_t fill = 0; + dnode_phys_t *dnx, *dnend; + + for (dnx = data, dnend = dnx + (BP_GET_LSIZE(bp)>>DNODE_SHIFT); + dnx < dnend; dnx++) { + if (dnx->dn_type != DMU_OT_NONE) + fill++; + } + ASSERT3U(fill, ==, bp->blk_fill); + } +#endif + + if (zb->zb_level || dnp->dn_type == DMU_OT_DNODE) + return (0); + + /* Abort immediately if we have encountered gang blocks */ + if (BP_IS_GANG(bp)) { + ma->ma_gang++; + return (EINTR); + } + + /* first time? */ + if (ma->ma_extent->ze_size == 0) { + zvol_init_extent(ma->ma_extent, bp); + return (0); + } + + stride = (DVA_GET_OFFSET(&bp->blk_dva[0])) - + ((DVA_GET_OFFSET(&ma->ma_extent->ze_dva)) + + (ma->ma_extent->ze_size - 1) * (ma->ma_extent->ze_stride)); + if (DVA_GET_VDEV(BP_IDENTITY(bp)) == + DVA_GET_VDEV(&ma->ma_extent->ze_dva)) { + if (ma->ma_extent->ze_stride == 0) { + /* second block in this extent */ + ma->ma_extent->ze_stride = stride; + ma->ma_extent->ze_size++; + return (0); + } else if (ma->ma_extent->ze_stride == stride) { + /* + * the block we allocated has the same + * stride + */ + ma->ma_extent->ze_size++; + return (0); + } + } + + /* + * dtrace -n 'zfs-dprintf + * /stringof(arg0) == "zvol.c"/ + * { + * printf("%s: %s", stringof(arg1), stringof(arg3)) + * } ' + */ + dprintf("ma_extent 0x%lx mrstride 0x%lx stride %lx\n", + ma->ma_extent->ze_size, ma->ma_extent->ze_stride, stride); + dprintf_bp(bp, "%s", "next blkptr:"); + /* start a new extent */ + if (ma->ma_extent == &ma->ma_list->zl_extents[NUM_EXTENTS - 1]) { + ma->ma_list->zl_next = kmem_zalloc(sizeof (zvol_ext_list_t), + KM_SLEEP); + ma->ma_list = ma->ma_list->zl_next; + ma->ma_extent = &ma->ma_list->zl_extents[0]; + } else { + ma->ma_extent++; + } + zvol_init_extent(ma->ma_extent, bp); + return (0); +} + /* ARGSUSED */ void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) @@ -235,7 +400,7 @@ zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx) volblocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE); /* - * These properites must be removed from the list so the generic + * These properties must be removed from the list so the generic * property setting step won't apply to them. */ VERIFY(nvlist_remove_all(nvprops, @@ -313,7 +478,107 @@ zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = { }; /* - * Create a minor node for the specified volume. + * reconstruct dva that gets us to the desired offset (offset + * is in bytes) + */ +int +zvol_get_dva(zvol_state_t *zv, uint64_t offset, dva_t *dva) +{ + zvol_ext_list_t *zl; + zvol_extent_t *ze; + int idx; + uint64_t tmp; + + if ((zl = zv->zv_list) == NULL) + return (EIO); + idx = 0; + ze = &zl->zl_extents[0]; + while (offset >= ze->ze_size * zv->zv_volblocksize) { + offset -= ze->ze_size * zv->zv_volblocksize; + + if (idx == NUM_EXTENTS - 1) { + /* we've reached the end of this array */ + ASSERT(zl->zl_next != NULL); + if (zl->zl_next == NULL) + return (-1); + zl = zl->zl_next; + ze = &zl->zl_extents[0]; + idx = 0; + } else { + ze++; + idx++; + } + } + DVA_SET_VDEV(dva, DVA_GET_VDEV(&ze->ze_dva)); + tmp = DVA_GET_OFFSET((&ze->ze_dva)); + tmp += (ze->ze_stride * (offset / zv->zv_volblocksize)); + DVA_SET_OFFSET(dva, tmp); + return (0); +} + +static void +zvol_free_extents(zvol_state_t *zv) +{ + zvol_ext_list_t *zl; + zvol_ext_list_t *tmp; + + if (zv->zv_list != NULL) { + zl = zv->zv_list; + while (zl != NULL) { + tmp = zl->zl_next; + kmem_free(zl, sizeof (zvol_ext_list_t)); + zl = tmp; + } + zv->zv_list = NULL; + } +} + +int +zvol_get_lbas(zvol_state_t *zv) +{ + struct maparg ma; + zvol_ext_list_t *zl; + zvol_extent_t *ze; + uint64_t blocks = 0; + int err; + + ma.ma_list = zl = kmem_zalloc(sizeof (zvol_ext_list_t), KM_SLEEP); + ma.ma_extent = &ma.ma_list->zl_extents[0]; + ma.ma_gang = 0; + zv->zv_list = ma.ma_list; + + err = traverse_zvol(zv->zv_objset, ADVANCE_PRE, zvol_map_block, &ma); + if (err == EINTR && ma.ma_gang) { + /* + * We currently don't support dump devices when the pool + * is so fragmented that our allocation has resulted in + * gang blocks. + */ + zvol_free_extents(zv); + return (EFRAGS); + } + ASSERT3U(err, ==, 0); + + ze = &zl->zl_extents[0]; + while (ze) { + blocks += ze->ze_size; + if (ze == &zl->zl_extents[NUM_EXTENTS - 1]) { + zl = zl->zl_next; + ze = &zl->zl_extents[0]; + } else { + ze++; + } + } + if (blocks != (zv->zv_volsize / zv->zv_volblocksize)) { + zvol_free_extents(zv); + return (EIO); + } + + return (0); +} + +/* + * Create a minor node (plus a whole lot more) for the specified volume. */ int zvol_create_minor(const char *name, major_t maj) @@ -327,7 +592,7 @@ zvol_create_minor(const char *name, major_t maj) int ds_mode = DS_MODE_PRIMARY; vnode_t *vp = NULL; char *devpath; - size_t devpathlen = strlen(ZVOL_FULL_DEV_DIR) + 1 + strlen(name) + 1; + size_t devpathlen = strlen(ZVOL_FULL_DEV_DIR) + strlen(name) + 1; char chrbuf[30], blkbuf[30]; int error; @@ -362,7 +627,7 @@ zvol_create_minor(const char *name, major_t maj) */ devpath = kmem_alloc(devpathlen, KM_SLEEP); - (void) sprintf(devpath, "%s/%s", ZVOL_FULL_DEV_DIR, name); + (void) sprintf(devpath, "%s%s", ZVOL_FULL_DEV_DIR, name); error = lookupname(devpath, UIO_SYSSPACE, NO_FOLLOW, NULL, &vp); @@ -444,15 +709,12 @@ zvol_create_minor(const char *name, major_t maj) mutex_init(&zv->zv_znode.z_range_lock, NULL, MUTEX_DEFAULT, NULL); avl_create(&zv->zv_znode.z_range_avl, zfs_range_compare, sizeof (rl_t), offsetof(rl_t, r_node)); - - /* get and cache the blocksize */ error = dmu_object_info(os, ZVOL_OBJ, &doi); ASSERT(error == 0); zv->zv_volblocksize = doi.doi_data_block_size; zil_replay(os, zv, &zv->zv_txg_assign, zvol_replay_vector); - zvol_size_changed(zv, maj); /* XXX this should handle the possible i/o error */ @@ -512,13 +774,107 @@ zvol_remove_minor(const char *name) return (0); } +static int +zvol_truncate(zvol_state_t *zv, uint64_t offset, uint64_t size) +{ + dmu_tx_t *tx; + int error; + + tx = dmu_tx_create(zv->zv_objset); + dmu_tx_hold_free(tx, ZVOL_OBJ, offset, size); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + return (error); + } + error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, offset, size, tx); + dmu_tx_commit(tx); + return (0); +} + +int +zvol_prealloc(zvol_state_t *zv) +{ + objset_t *os = zv->zv_objset; + dmu_tx_t *tx; + void *data; + uint64_t refd, avail, usedobjs, availobjs; + uint64_t resid = zv->zv_volsize; + uint64_t off = 0; + + /* Check the space usage before attempting to allocate the space */ + dmu_objset_space(os, &refd, &avail, &usedobjs, &availobjs); + if (avail < zv->zv_volsize) + return (ENOSPC); + + /* Free old extents if they exist */ + zvol_free_extents(zv); + + /* allocate the blocks by writing each one */ + data = kmem_zalloc(SPA_MAXBLOCKSIZE, KM_SLEEP); + + while (resid != 0) { + int error; + uint64_t bytes = MIN(resid, SPA_MAXBLOCKSIZE); + + tx = dmu_tx_create(os); + dmu_tx_hold_write(tx, ZVOL_OBJ, off, bytes); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + kmem_free(data, SPA_MAXBLOCKSIZE); + (void) zvol_truncate(zv, 0, off); + return (error); + } + dmu_write(os, ZVOL_OBJ, off, bytes, data, tx); + dmu_tx_commit(tx); + off += bytes; + resid -= bytes; + } + kmem_free(data, SPA_MAXBLOCKSIZE); + txg_wait_synced(dmu_objset_pool(os), 0); + + return (0); +} + +int +zvol_update_volsize(zvol_state_t *zv, major_t maj, uint64_t volsize) +{ + dmu_tx_t *tx; + int error; + + ASSERT(MUTEX_HELD(&zvol_state_lock)); + + tx = dmu_tx_create(zv->zv_objset); + dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); + dmu_tx_hold_free(tx, ZVOL_OBJ, volsize, DMU_OBJECT_END); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + return (error); + } + + error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1, + &volsize, tx); + dmu_tx_commit(tx); + + if (error == 0) + error = zvol_truncate(zv, volsize, DMU_OBJECT_END); + + if (error == 0) { + zv->zv_volsize = volsize; + zvol_size_changed(zv, maj); + } + return (error); +} + int zvol_set_volsize(const char *name, major_t maj, uint64_t volsize) { zvol_state_t *zv; - dmu_tx_t *tx; int error; dmu_object_info_t doi; + uint64_t old_volsize = 0ULL; mutex_enter(&zvol_state_lock); @@ -526,6 +882,7 @@ zvol_set_volsize(const char *name, major_t maj, uint64_t volsize) mutex_exit(&zvol_state_lock); return (ENXIO); } + old_volsize = zv->zv_volsize; if ((error = dmu_object_info(zv->zv_objset, ZVOL_OBJ, &doi)) != 0 || (error = zvol_check_volsize(volsize, @@ -534,33 +891,24 @@ zvol_set_volsize(const char *name, major_t maj, uint64_t volsize) return (error); } - if (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) { + if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) { mutex_exit(&zvol_state_lock); return (EROFS); } - tx = dmu_tx_create(zv->zv_objset); - dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); - dmu_tx_hold_free(tx, ZVOL_OBJ, volsize, DMU_OBJECT_END); - error = dmu_tx_assign(tx, TXG_WAIT); - if (error) { - dmu_tx_abort(tx); - mutex_exit(&zvol_state_lock); - return (error); - } - - error = zap_update(zv->zv_objset, ZVOL_ZAP_OBJ, "size", 8, 1, - &volsize, tx); - if (error == 0) { - error = dmu_free_range(zv->zv_objset, ZVOL_OBJ, volsize, - DMU_OBJECT_END, tx); - } + error = zvol_update_volsize(zv, maj, volsize); - dmu_tx_commit(tx); - - if (error == 0) { - zv->zv_volsize = volsize; - zvol_size_changed(zv, maj); + /* + * Reinitialize the dump area to the new size. If we + * failed to resize the dump area then restore the it back to + * it's original size. + */ + if (error == 0 && zv->zv_flags & ZVOL_DUMPIFIED) { + if ((error = zvol_dumpify(zv)) != 0 || + (error = dumpvp_resize()) != 0) { + (void) zvol_update_volsize(zv, maj, old_volsize); + error = zvol_dumpify(zv); + } } mutex_exit(&zvol_state_lock); @@ -581,8 +929,7 @@ zvol_set_volblocksize(const char *name, uint64_t volblocksize) mutex_exit(&zvol_state_lock); return (ENXIO); } - - if (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) { + if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) { mutex_exit(&zvol_state_lock); return (EROFS); } @@ -626,7 +973,7 @@ zvol_open(dev_t *devp, int flag, int otyp, cred_t *cr) ASSERT(zv->zv_objset != NULL); if ((flag & FWRITE) && - (zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY))) { + (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY))) { mutex_exit(&zvol_state_lock); return (EROFS); } @@ -732,7 +1079,7 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) /* * Lock the range of the block to ensure that when the data is - * written out and it's checksum is being calculated that no other + * written out and its checksum is being calculated that no other * thread can change the block. */ boff = P2ALIGN_TYPED(lr->lr_offset, zv->zv_volblocksize, uint64_t); @@ -794,6 +1141,76 @@ zvol_log_write(zvol_state_t *zv, dmu_tx_t *tx, offset_t off, ssize_t len) } int +zvol_dumpio(vdev_t *vd, uint64_t size, uint64_t offset, void *addr, + int bflags, int isdump) +{ + vdev_disk_t *dvd; + int direction; + int c; + int numerrors = 0; + + for (c = 0; c < vd->vdev_children; c++) { + if (zvol_dumpio(vd->vdev_child[c], size, offset, + addr, bflags, isdump) != 0) { + numerrors++; + } else if (bflags & B_READ) { + break; + } + } + + if (!vd->vdev_ops->vdev_op_leaf) + return (numerrors < vd->vdev_children ? 0 : EIO); + + if (!vdev_writeable(vd)) + return (EIO); + + dvd = vd->vdev_tsd; + ASSERT3P(dvd, !=, NULL); + direction = bflags & (B_WRITE | B_READ); + ASSERT(ISP2(direction)); + offset += VDEV_LABEL_START_SIZE; + + if (ddi_in_panic() || isdump) { + if (direction & B_READ) + return (EIO); + return (ldi_dump(dvd->vd_lh, addr, lbtodb(offset), + lbtodb(size))); + } else { + return (vdev_disk_physio(dvd->vd_lh, addr, size, offset, + direction)); + } +} + +int +zvol_physio(zvol_state_t *zv, int bflags, uint64_t off, + uint64_t size, void *addr, int isdump) +{ + dva_t dva; + vdev_t *vd; + int error; + spa_t *spa = dmu_objset_spa(zv->zv_objset); + + ASSERT(size <= zv->zv_volblocksize); + + /* restrict requests to multiples of the system block size */ + if (P2PHASE(off, DEV_BSIZE) || P2PHASE(size, DEV_BSIZE)) + return (EINVAL); + + if (zvol_get_dva(zv, off, &dva) != 0) + return (EIO); + + spa_config_enter(spa, RW_READER, FTAG); + vd = vdev_lookup_top(spa, DVA_GET_VDEV(&dva)); + + error = zvol_dumpio(vd, size, + DVA_GET_OFFSET(&dva) + (off % zv->zv_volblocksize), + addr, bflags & (B_READ | B_WRITE | B_PHYS), isdump); + + spa_config_exit(spa, FTAG); + return (error); +} + +int zvol_strategy(buf_t *bp) { zvol_state_t *zv = ddi_get_soft_state(zvol_state, getminor(bp->b_edev)); @@ -803,7 +1220,7 @@ zvol_strategy(buf_t *bp) objset_t *os; rl_t *rl; int error = 0; - boolean_t reading; + boolean_t reading, is_dump = zv->zv_flags & ZVOL_DUMPIFIED; if (zv == NULL) { bioerror(bp, ENXIO); @@ -817,8 +1234,9 @@ zvol_strategy(buf_t *bp) return (0); } - if ((zv->zv_readonly || (zv->zv_mode & DS_MODE_READONLY)) && - !(bp->b_flags & B_READ)) { + if (!(bp->b_flags & B_READ) && + (zv->zv_flags & ZVOL_RDONLY || + zv->zv_mode & DS_MODE_READONLY)) { bioerror(bp, EROFS); biodone(bp); return (0); @@ -842,14 +1260,18 @@ zvol_strategy(buf_t *bp) rl = zfs_range_lock(&zv->zv_znode, off, resid, reading ? RL_READER : RL_WRITER); - while (resid != 0 && off < volsize) { - - size = MIN(resid, zvol_maxphys); /* zvol_maxphys per tx */ + if (resid > volsize - off) /* don't write past the end */ + resid = volsize - off; - if (size > volsize - off) /* don't write past the end */ - size = volsize - off; + while (resid != 0 && off < volsize) { - if (reading) { + size = MIN(resid, zvol_maxphys); + if (is_dump) { + /* can't straddle a block boundary */ + size = MIN(size, P2END(off, zv->zv_volblocksize) - off); + error = zvol_physio(zv, bp->b_flags, off, size, + addr, 0); + } else if (reading) { error = dmu_read(os, ZVOL_OBJ, off, size, addr); } else { dmu_tx_t *tx = dmu_tx_create(os); @@ -874,9 +1296,8 @@ zvol_strategy(buf_t *bp) if ((bp->b_resid = resid) == bp->b_bcount) bioerror(bp, off > volsize ? EINVAL : error); - if (!(bp->b_flags & B_ASYNC) && !reading && !zil_disable) + if (!(bp->b_flags & B_ASYNC) && !reading && !zil_disable && !is_dump) zil_commit(zv->zv_zilog, UINT64_MAX, ZVOL_OBJ); - biodone(bp); return (0); @@ -897,6 +1318,45 @@ zvol_minphys(struct buf *bp) bp->b_bcount = zvol_maxphys; } +int +zvol_dump(dev_t dev, caddr_t addr, daddr_t blkno, int nblocks) +{ + minor_t minor = getminor(dev); + zvol_state_t *zv; + int error = 0; + uint64_t size; + uint64_t boff; + uint64_t resid; + + if (minor == 0) /* This is the control device */ + return (ENXIO); + + zv = ddi_get_soft_state(zvol_state, minor); + if (zv == NULL) + return (ENXIO); + + boff = ldbtob(blkno); + resid = ldbtob(nblocks); + if (boff + resid > zv->zv_volsize) { + /* dump should know better than to write here */ + ASSERT(blkno + resid <= zv->zv_volsize); + return (EIO); + } + while (resid) { + /* can't straddle a block boundary */ + size = MIN(resid, P2END(boff, zv->zv_volblocksize) - boff); + + error = zvol_physio(zv, B_WRITE, boff, size, addr, 1); + if (error) + break; + boff += size; + addr += size; + resid -= size; + } + + return (error); +} + /*ARGSUSED*/ int zvol_read(dev_t dev, uio_t *uio, cred_t *cr) @@ -942,6 +1402,12 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr) if (zv == NULL) return (ENXIO); + if (zv->zv_flags & ZVOL_DUMPIFIED) { + error = physio(zvol_strategy, NULL, dev, B_WRITE, + zvol_minphys, uio); + return (error); + } + rl = zfs_range_lock(&zv->zv_znode, uio->uio_loffset, uio->uio_resid, RL_WRITER); while (uio->uio_resid > 0) { @@ -982,6 +1448,7 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) struct uuid uuid = EFI_RESERVED; uint32_t crc; int error = 0; + rl_t *rl; mutex_enter(&zvol_state_lock); @@ -1027,7 +1494,7 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) * zvol. Currently this interface will return ENOTTY to * such requests. These requests could be supported by * adding a check for lba == 0 and consing up an appropriate - * RMBR. + * PMBR. */ if (efi.dki_lba == 1) { efi_gpt_t gpt; @@ -1099,10 +1566,27 @@ zvol_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) case DKIOCGGEOM: case DKIOCGVTOC: - /* commands using these (like prtvtoc) expect ENOTSUP */ + /* + * commands using these (like prtvtoc) expect ENOTSUP + * since we're emulating an EFI label + */ error = ENOTSUP; break; + case DKIOCDUMPINIT: + rl = zfs_range_lock(&zv->zv_znode, 0, zv->zv_volsize, + RL_WRITER); + error = zvol_dumpify(zv); + zfs_range_unlock(rl); + break; + + case DKIOCDUMPFINI: + rl = zfs_range_lock(&zv->zv_znode, 0, zv->zv_volsize, + RL_WRITER); + error = zvol_dump_fini(zv); + zfs_range_unlock(rl); + break; + default: error = ENOTTY; break; @@ -1131,3 +1615,216 @@ zvol_fini(void) mutex_destroy(&zvol_state_lock); ddi_soft_state_fini(&zvol_state); } + +static boolean_t +zvol_is_swap(zvol_state_t *zv) +{ + vnode_t *vp; + boolean_t ret = B_FALSE; + char *devpath; + size_t devpathlen; + int error; + + devpathlen = strlen(ZVOL_FULL_DEV_DIR) + strlen(zv->zv_name) + 1; + devpath = kmem_alloc(devpathlen, KM_SLEEP); + (void) sprintf(devpath, "%s%s", ZVOL_FULL_DEV_DIR, zv->zv_name); + error = lookupname(devpath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp); + kmem_free(devpath, devpathlen); + + ret = !error && IS_SWAPVP(common_specvp(vp)); + + if (vp != NULL) + VN_RELE(vp); + + return (ret); +} + +static int +zvol_dump_init(zvol_state_t *zv, boolean_t resize) +{ + dmu_tx_t *tx; + int error = 0; + objset_t *os = zv->zv_objset; + nvlist_t *nv = NULL; + uint64_t checksum, compress, refresrv; + + ASSERT(MUTEX_HELD(&zvol_state_lock)); + + tx = dmu_tx_create(os); + dmu_tx_hold_free(tx, ZVOL_OBJ, 0, DMU_OBJECT_END); + dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + return (error); + } + + /* + * If we are resizing the dump device then we only need to + * update the refreservation to match the newly updated + * zvolsize. Otherwise, we save off the original state of the + * zvol so that we can restore them if the zvol is ever undumpified. + */ + if (resize) { + error = zap_update(os, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 8, 1, + &zv->zv_volsize, tx); + } else { + error = dsl_prop_get_integer(zv->zv_name, + zfs_prop_to_name(ZFS_PROP_COMPRESSION), &compress, NULL); + error = error ? error : dsl_prop_get_integer(zv->zv_name, + zfs_prop_to_name(ZFS_PROP_CHECKSUM), &checksum, NULL); + error = error ? error : dsl_prop_get_integer(zv->zv_name, + zfs_prop_to_name(ZFS_PROP_REFRESERVATION), &refresrv, NULL); + + error = error ? error : zap_update(os, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_COMPRESSION), 8, 1, + &compress, tx); + error = error ? error : zap_update(os, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_CHECKSUM), 8, 1, &checksum, tx); + error = error ? error : zap_update(os, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 8, 1, + &refresrv, tx); + } + dmu_tx_commit(tx); + + /* Truncate the file */ + if (!error) + error = zvol_truncate(zv, 0, DMU_OBJECT_END); + + if (error) + return (error); + + /* + * We only need update the zvol's property if we are initializing + * the dump area for the first time. + */ + if (!resize) { + VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); + VERIFY(nvlist_add_uint64(nv, + zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 0) == 0); + VERIFY(nvlist_add_uint64(nv, + zfs_prop_to_name(ZFS_PROP_COMPRESSION), + ZIO_COMPRESS_OFF) == 0); + VERIFY(nvlist_add_uint64(nv, + zfs_prop_to_name(ZFS_PROP_CHECKSUM), + ZIO_CHECKSUM_OFF) == 0); + + error = zfs_set_prop_nvlist(zv->zv_name, nv); + nvlist_free(nv); + + if (error) + return (error); + } + + /* Allocate the space for the dump */ + error = zvol_prealloc(zv); + return (error); +} + +static int +zvol_dumpify(zvol_state_t *zv) +{ + int error = 0; + uint64_t dumpsize = 0; + dmu_tx_t *tx; + objset_t *os = zv->zv_objset; + + if (zv->zv_flags & ZVOL_RDONLY || (zv->zv_mode & DS_MODE_READONLY)) + return (EROFS); + + /* + * We do not support swap devices acting as dump devices. + */ + if (zvol_is_swap(zv)) + return (ENOTSUP); + + if (zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ, ZVOL_DUMPSIZE, + 8, 1, &dumpsize) != 0 || dumpsize != zv->zv_volsize) { + boolean_t resize = (dumpsize > 0) ? B_TRUE : B_FALSE; + + if ((error = zvol_dump_init(zv, resize)) != 0) { + (void) zvol_dump_fini(zv); + return (error); + } + } + + /* + * Build up our lba mapping. + */ + error = zvol_get_lbas(zv); + if (error) { + (void) zvol_dump_fini(zv); + return (error); + } + + tx = dmu_tx_create(os); + dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + (void) zvol_dump_fini(zv); + return (error); + } + + zv->zv_flags |= ZVOL_DUMPIFIED; + error = zap_update(os, ZVOL_ZAP_OBJ, ZVOL_DUMPSIZE, 8, 1, + &zv->zv_volsize, tx); + dmu_tx_commit(tx); + + if (error) { + (void) zvol_dump_fini(zv); + return (error); + } + + txg_wait_synced(dmu_objset_pool(os), 0); + return (0); +} + +static int +zvol_dump_fini(zvol_state_t *zv) +{ + dmu_tx_t *tx; + objset_t *os = zv->zv_objset; + nvlist_t *nv; + int error = 0; + uint64_t checksum, compress, refresrv; + + tx = dmu_tx_create(os); + dmu_tx_hold_zap(tx, ZVOL_ZAP_OBJ, TRUE, NULL); + error = dmu_tx_assign(tx, TXG_WAIT); + if (error) { + dmu_tx_abort(tx); + return (error); + } + + /* + * Attempt to restore the zvol back to its pre-dumpified state. + * This is a best-effort attempt as it's possible that not all + * of these properties were initialized during the dumpify process + * (i.e. error during zvol_dump_init). + */ + (void) zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_CHECKSUM), 8, 1, &checksum); + (void) zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_COMPRESSION), 8, 1, &compress); + (void) zap_lookup(zv->zv_objset, ZVOL_ZAP_OBJ, + zfs_prop_to_name(ZFS_PROP_REFRESERVATION), 8, 1, &refresrv); + + (void) zap_remove(os, ZVOL_ZAP_OBJ, ZVOL_DUMPSIZE, tx); + zvol_free_extents(zv); + zv->zv_flags &= ~ZVOL_DUMPIFIED; + dmu_tx_commit(tx); + + VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0); + (void) nvlist_add_uint64(nv, + zfs_prop_to_name(ZFS_PROP_CHECKSUM), checksum); + (void) nvlist_add_uint64(nv, + zfs_prop_to_name(ZFS_PROP_COMPRESSION), compress); + (void) nvlist_add_uint64(nv, + zfs_prop_to_name(ZFS_PROP_REFRESERVATION), refresrv); + (void) zfs_set_prop_nvlist(zv->zv_name, nv); + nvlist_free(nv); + + return (0); +} diff --git a/usr/src/uts/common/io/dump.c b/usr/src/uts/common/io/dump.c index f2cfd9dfda..6498463087 100644 --- a/usr/src/uts/common/io/dump.c +++ b/usr/src/uts/common/io/dump.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -173,7 +173,10 @@ dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp) FOLLOW, NULLVPP, &vp)) != 0) break; mutex_enter(&dump_lock); - error = dumpinit(vp, pathbuf, cmd == DIOCTRYDEV); + if (vp->v_type == VBLK) + error = dumpinit(vp, pathbuf, cmd == DIOCTRYDEV); + else + error = ENOTBLK; mutex_exit(&dump_lock); VN_RELE(vp); break; diff --git a/usr/src/uts/common/os/dumpsubr.c b/usr/src/uts/common/os/dumpsubr.c index b8dba61893..b1c90e0eac 100644 --- a/usr/src/uts/common/os/dumpsubr.c +++ b/usr/src/uts/common/os/dumpsubr.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -62,6 +62,7 @@ #include <sys/vtoc.h> #include <sys/errorq.h> #include <sys/fm/util.h> +#include <sys/fs/zfs.h> #include <vm/hat.h> #include <vm/as.h> @@ -269,6 +270,17 @@ dumpinit(vnode_t *vp, char *name, int justchecking) dump_iosize = dki.dki_maxtransfer * blk_size; dumpbuf_resize(); } + /* + * If we are working with a zvol then call into + * it to dumpify itself. + */ + if (strcmp(dki.dki_dname, ZVOL_DRIVER) == 0) { + if ((error = VOP_IOCTL(cdev_vp, + DKIOCDUMPINIT, NULL, FKIOCTL, kcred, + NULL, NULL)) != 0) { + dumpfini(); + } + } (void) VOP_CLOSE(cdev_vp, FREAD | FWRITE, 1, 0, kcred, NULL); @@ -279,16 +291,43 @@ dumpinit(vnode_t *vp, char *name, int justchecking) cmn_err(CE_CONT, "?dump on %s size %llu MB\n", name, dumpvp_size >> 20); - return (0); + return (error); } void dumpfini(void) { + vattr_t vattr; + boolean_t is_zfs = B_FALSE; + vnode_t *cdev_vp; ASSERT(MUTEX_HELD(&dump_lock)); kmem_free(dumppath, strlen(dumppath) + 1); + /* + * Determine if we are using zvols for our dump device + */ + vattr.va_mask = AT_RDEV; + if (VOP_GETATTR(dumpvp, &vattr, 0, kcred, NULL) == 0) { + is_zfs = (getmajor(vattr.va_rdev) == + ddi_name_to_major(ZFS_DRIVER)) ? B_TRUE : B_FALSE; + } + + /* + * If we have a zvol dump device then we call into zfs so + * that it may have a chance to cleanup. + */ + if (is_zfs && + (cdev_vp = makespecvp(VTOS(dumpvp)->s_dev, VCHR)) != NULL) { + if (VOP_OPEN(&cdev_vp, FREAD | FWRITE, kcred, NULL) == 0) { + (void) VOP_IOCTL(cdev_vp, DKIOCDUMPFINI, NULL, FKIOCTL, + kcred, NULL, NULL); + (void) VOP_CLOSE(cdev_vp, FREAD | FWRITE, 1, 0, + kcred, NULL); + } + VN_RELE(cdev_vp); + } + (void) VOP_CLOSE(dumpvp, FREAD | FWRITE, 1, (offset_t)0, kcred, NULL); VN_RELE(dumpvp); @@ -798,3 +837,30 @@ dump_resize() dumpbuf_resize(); mutex_exit(&dump_lock); } + +/* + * This function allows for dynamic resizing of a dump area. It assumes that + * the underlying device has update its appropriate size(9P). + */ +int +dumpvp_resize() +{ + int error; + vattr_t vattr; + + mutex_enter(&dump_lock); + vattr.va_mask = AT_SIZE; + if ((error = VOP_GETATTR(dumpvp, &vattr, 0, kcred, NULL)) != 0) { + mutex_exit(&dump_lock); + return (error); + } + + if (error == 0 && vattr.va_size < 2 * DUMP_LOGSIZE + DUMP_ERPTSIZE) { + mutex_exit(&dump_lock); + return (ENOSPC); + } + + dumpvp_size = vattr.va_size & -DUMP_OFFSET; + mutex_exit(&dump_lock); + return (0); +} diff --git a/usr/src/uts/common/sys/cpr.h b/usr/src/uts/common/sys/cpr.h index 6fd5438cc0..13230543f2 100644 --- a/usr/src/uts/common/sys/cpr.h +++ b/usr/src/uts/common/sys/cpr.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -113,6 +113,15 @@ typedef struct cpr_default_info cdef_t; * cf_dev_prom (prom device path of the above special file) * "/sbus/espdma/dma/sd@1:h" * + * If the statefile is on a zvol, the fields would have these values: + * + * cf_type CFT_ZVOL + * cf_path ignored + * cf_fs (the zvol name e.g. "dump" portion of rootpool/dump) + * cf_devfs (devfs path) "/dev/zvol/dsk/<pool>/<zvol>" + * cf_dev_prom (prom device path of the above special file) + * e.g. "/sbus/espdma/dma/sd@1:h" + * * The rest of the fields are autoshutdown and autopm configuration related. * They are updated by pmconfig and consumed by both powerd and dtpower. */ @@ -163,6 +172,7 @@ struct cprconfig { */ #define CFT_UFS 1 /* statefile is ufs file */ #define CFT_SPEC 2 /* statefile is special file */ +#define CFT_ZVOL 3 /* statefile is a zvol */ /* diff --git a/usr/src/uts/common/sys/dkio.h b/usr/src/uts/common/sys/dkio.h index 043c9d58d8..13edae7368 100644 --- a/usr/src/uts/common/sys/dkio.h +++ b/usr/src/uts/common/sys/dkio.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -358,6 +358,9 @@ struct dk_minfo { #define DKIOCSETVOLCAP (DKIOC | 26) /* Set volume capabilities */ #define DKIOCDMR (DKIOC | 27) /* Issue a directed read */ +#define DKIOCDUMPINIT (DKIOC | 28) /* Dumpify a zvol */ +#define DKIOCDUMPFINI (DKIOC | 29) /* Un-Dumpify a zvol */ + typedef uint_t volcapinfo_t; typedef uint_t volcapset_t; diff --git a/usr/src/uts/common/sys/dumphdr.h b/usr/src/uts/common/sys/dumphdr.h index 81e2c7ccb8..72c6e41c71 100644 --- a/usr/src/uts/common/sys/dumphdr.h +++ b/usr/src/uts/common/sys/dumphdr.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -134,6 +134,7 @@ extern void dumpsys(void); extern void dump_messages(void); extern void dump_ereports(void); extern void dumpvp_write(const void *, size_t); +extern int dumpvp_resize(void); extern int dump_plat_addr(void); extern void dump_plat_pfn(void); extern int dump_plat_data(void *); diff --git a/usr/src/uts/common/sys/fs/snode.h b/usr/src/uts/common/sys/fs/snode.h index d0176af293..7f109f5682 100644 --- a/usr/src/uts/common/sys/fs/snode.h +++ b/usr/src/uts/common/sys/fs/snode.h @@ -23,7 +23,7 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -177,6 +177,7 @@ int spec_is_clone(struct vnode *); int spec_is_selfclone(struct vnode *); int spec_fence_snode(dev_info_t *dip, struct vnode *vp); int spec_unfence_snode(dev_info_t *dip); +void spec_size_invalidate(dev_t, vtype_t); /* diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h index 98a2b5c546..9933839820 100644 --- a/usr/src/uts/common/sys/fs/zfs.h +++ b/usr/src/uts/common/sys/fs/zfs.h @@ -228,7 +228,7 @@ typedef enum zfs_share_op { #define SPA_VERSION_10 10ULL /* - * When bumping up SPA_VERSION, make sure GRUB ZFS understand the on-disk + * When bumping up SPA_VERSION, make sure GRUB ZFS understands the on-disk * format change. Go to usr/src/grub/grub-0.95/stage2/{zfs-include/, fsys_zfs*}, * and do the appropriate changes. */ @@ -316,11 +316,12 @@ typedef enum zfs_share_op { #define ZPOOL_CONFIG_NPARITY "nparity" #define ZPOOL_CONFIG_HOSTID "hostid" #define ZPOOL_CONFIG_HOSTNAME "hostname" -#define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */ #define ZPOOL_CONFIG_UNSPARE "unspare" #define ZPOOL_CONFIG_PHYS_PATH "phys_path" #define ZPOOL_CONFIG_IS_LOG "is_log" #define ZPOOL_CONFIG_L2CACHE "l2cache" +#define ZPOOL_CONFIG_TIMESTAMP "timestamp" /* not stored on disk */ +#define ZPOOL_CONFIG_BOOTFS "bootfs" /* not stored on disk */ /* * The persistent vdev state is stored as separate values rather than a single * 'vdev_state' entry. This is because a device can be in multiple states, such @@ -460,6 +461,7 @@ typedef struct vdev_stat { uint64_t vs_scrub_end; /* UTC scrub end time */ } vdev_stat_t; +#define ZVOL_DRIVER "zvol" #define ZFS_DRIVER "zfs" #define ZFS_DEV "/dev/zfs" @@ -475,7 +477,7 @@ typedef struct vdev_stat { * And here are the things we need with /dev, etc. in front of them. */ #define ZVOL_PSEUDO_DEV "/devices/pseudo/zvol@0:" -#define ZVOL_FULL_DEV_DIR "/dev/" ZVOL_DEV_DIR +#define ZVOL_FULL_DEV_DIR "/dev/" ZVOL_DEV_DIR "/" #define ZVOL_PROP_NAME "name" diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files index 824e43d36c..9a756bd90d 100644 --- a/usr/src/uts/intel/Makefile.files +++ b/usr/src/uts/intel/Makefile.files @@ -108,6 +108,12 @@ LX_AUTOFS_OBJS += \ lx_autofs.o # +# ZFS file system module +# +ZFS_OBJS += \ + spa_boot.o + +# # Decompression code # CORE_OBJS += decompress.o diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules index 8d88d008fd..ea70b357fb 100644 --- a/usr/src/uts/intel/Makefile.rules +++ b/usr/src/uts/intel/Makefile.rules @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -235,6 +235,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/kdi/%.s $(OBJS_DIR)/%.o: $(UTSBASE)/intel/kdi/$(SUBARCH_DIR)/%.s $(COMPILE.s) -o $@ $< +$(OBJS_DIR)/%.o: $(UTSBASE)/intel/zfs/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + # # krtld compiled into unix # @@ -286,6 +290,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/intel/dtrace/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/intel/dtrace/%.s @($(LHEAD) $(LINT.s) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/intel/zfs/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/intel/fs/proc/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/intel/genunix/Makefile b/usr/src/uts/intel/genunix/Makefile index 10802679c5..f6ac2cf2c5 100644 --- a/usr/src/uts/intel/genunix/Makefile +++ b/usr/src/uts/intel/genunix/Makefile @@ -20,7 +20,7 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -84,6 +84,7 @@ IPCTF_TARGET = $(IPCTF) $(PATCH_BUILD)IPCTF_TARGET = CPPFLAGS += -I$(SRC)/common +CPPFLAGS += -I$(SRC)/uts/common/fs/zfs # # For now, disable these lint checks; maintainers should endeavor diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s index 0f99792ed6..fd7a606594 100644 --- a/usr/src/uts/intel/ia32/ml/modstubs.s +++ b/usr/src/uts/intel/ia32/ml/modstubs.s @@ -739,6 +739,15 @@ fcnname/**/_info: \ #endif /* + * Stubs for zfs + */ +#ifndef ZFS_MODULE + MODULE(zfs,fs); + STUB(zfs, spa_boot_init, nomod_minus_one); + END_MODULE(zfs); +#endif + +/* * Stubs for dcfs */ #ifndef DCFS_MODULE diff --git a/usr/src/uts/intel/specfs/Makefile b/usr/src/uts/intel/specfs/Makefile index b5d9435319..23a13db05e 100644 --- a/usr/src/uts/intel/specfs/Makefile +++ b/usr/src/uts/intel/specfs/Makefile @@ -21,7 +21,7 @@ # # uts/intel/specfs/Makefile # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -63,6 +63,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) MODSTUBS_DIR = $(OBJS_DIR) $(MODSTUBS_O) := AS_CPPFLAGS += -DSPEC_MODULE CLEANFILES += $(MODSTUBS_O) +LDFLAGS += -dy -Nfs/fifofs # # For now, disable these lint checks; maintainers should endeavor diff --git a/usr/src/uts/intel/zfs/Makefile b/usr/src/uts/intel/zfs/Makefile index c9596a4eef..05ab541d5d 100644 --- a/usr/src/uts/intel/zfs/Makefile +++ b/usr/src/uts/intel/zfs/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) # Overrides and depends_on # MODSTUBS_DIR = $(OBJS_DIR) -LDFLAGS += -dy -Nfs/specfs -Ndrv/random -Nmisc/idmap +LDFLAGS += -dy -Nfs/specfs -Ncrypto/swrand -Nmisc/idmap INC_PATH += -I$(UTSBASE)/common/fs/zfs INC_PATH += -I$(SRC)/common diff --git a/usr/src/uts/intel/zfs/spa_boot.c b/usr/src/uts/intel/zfs/spa_boot.c new file mode 100644 index 0000000000..9407f52353 --- /dev/null +++ b/usr/src/uts/intel/zfs/spa_boot.c @@ -0,0 +1,198 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/spa.h> +#include <sys/sunddi.h> + +char * +spa_get_bootfs() +{ + char *zfs_bp; + + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), + DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bp) != + DDI_SUCCESS) + return (NULL); + return (zfs_bp); +} + +void +spa_free_bootfs(char *bootfs) +{ + ddi_prop_free(bootfs); +} + +/* + * Calculate how many device pathnames are in devpath_list. + * The devpath_list could look like this: + * + * "/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a" + */ +static int +spa_count_devpath(char *devpath_list) +{ + int numpath; + char *tmp_path, *blank; + + numpath = 0; + tmp_path = devpath_list; + + /* skip leading blanks */ + while (*tmp_path == ' ') + tmp_path++; + + while ((blank = strchr(tmp_path, ' ')) != NULL) { + + numpath++; + /* skip contiguous blanks */ + while (*blank == ' ') + blank++; + tmp_path = blank; + } + + if (strlen(tmp_path) > 0) + numpath++; + + return (numpath); +} + +/* + * Only allow booting the device if it has the same vdev information as + * the most recently updated vdev (highest txg) and is in a valid state. + * + * GRUB passes online/active device path names, e.g. + * "/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a" + * to the kernel. The best vdev should have the same matching online/active + * list as what GRUB passes in. + */ +static int +spa_check_devstate(char *devpath_list, char *dev, nvlist_t *conf) +{ + nvlist_t *nvtop, **child; + uint_t label_path, grub_path, c, children; + char *type; + + VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE, + &nvtop) == 0); + VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0); + + if (strcmp(type, VDEV_TYPE_DISK) == 0) + return (spa_rootdev_validate(nvtop)? 0 : EINVAL); + + ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0); + + VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0); + + /* + * Check if the devpath_list is the same as the path list in conf. + * If these two lists are different, then the booting device is not an + * up-to-date device that can be booted. + */ + label_path = 0; + for (c = 0; c < children; c++) { + char *physpath; + + if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH, + &physpath) != 0) + return (EINVAL); + + if (spa_rootdev_validate(child[c])) { + if (strstr(devpath_list, physpath) == NULL) + return (EINVAL); + label_path++; + } else { + char *blank; + + if (blank = strchr(dev, ' ')) + *blank = '\0'; + if (strcmp(physpath, dev) == 0) + return (EINVAL); + if (blank) + *blank = ' '; + } + } + + grub_path = spa_count_devpath(devpath_list); + + if (label_path != grub_path) + return (EINVAL); + + return (0); +} + +/* + * Given a list of vdev physpath names, pick the vdev with the most recent txg, + * and return the point of the device's physpath in the list and the device's + * label configuration. The content of the label would be the most recent + * updated information. + */ +int +spa_get_rootconf(char *devpath_list, char **bestdev, nvlist_t **bestconf) +{ + nvlist_t *conf = NULL; + char *dev = NULL; + uint64_t txg = 0; + char *devpath, *blank; + + devpath = devpath_list; + dev = devpath; + + while (devpath[0] == ' ') + devpath++; + + while ((blank = strchr(devpath, ' ')) != NULL) { + *blank = '\0'; + spa_check_rootconf(devpath, &dev, &conf, &txg); + *blank = ' '; + + while (*blank == ' ') + blank++; + devpath = blank; + } + + /* for the only or the last devpath in the devpath_list */ + if (strlen(devpath) > 0) + spa_check_rootconf(devpath, &dev, &conf, &txg); + + if (conf == NULL) + return (EINVAL); + + /* + * dev/conf is the vdev with the most recent txg. + * Check if the device is in a bootable state. + * dev may have a trailing blank since it points to a string + * in the devpath_list. + */ + if (spa_check_devstate(devpath_list, dev, conf) != 0) + return (EINVAL); + + *bestdev = dev; + *bestconf = conf; + return (0); +} diff --git a/usr/src/uts/sparc/Makefile.files b/usr/src/uts/sparc/Makefile.files index fccd7341e3..cfca9e03ba 100644 --- a/usr/src/uts/sparc/Makefile.files +++ b/usr/src/uts/sparc/Makefile.files @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -76,6 +76,10 @@ FCODE_OBJS += fcode.o #PROC_OBJS += CORE_OBJS += prmachdep.o +# ZFS file system module +ZFS_OBJS += \ + spa_boot.o + # # misc modules # diff --git a/usr/src/uts/sparc/Makefile.rules b/usr/src/uts/sparc/Makefile.rules index b7978200ad..a9ab9e0dba 100644 --- a/usr/src/uts/sparc/Makefile.rules +++ b/usr/src/uts/sparc/Makefile.rules @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -62,6 +62,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/sparc/krtld/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/sparc/zfs/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + # # _RELSEG indicates that the dynamic syms are put in a separate ELF # section so they can be freed later. @@ -99,6 +103,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/sparc/io/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/sparc/fpu/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/sparc/zfs/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/sparc/fs/proc/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/sparc/ml/modstubs.s b/usr/src/uts/sparc/ml/modstubs.s index 22be0e8a44..8e4e06a008 100644 --- a/usr/src/uts/sparc/ml/modstubs.s +++ b/usr/src/uts/sparc/ml/modstubs.s @@ -627,6 +627,15 @@ stubs_base: #endif /* + * Stubs for zfs + */ +#ifndef ZFS_MODULE + MODULE(zfs,fs); + STUB(zfs, spa_boot_init, nomod_minus_one); + END_MODULE(zfs); +#endif + +/* * Stubs for dcfs */ #ifndef DCFS_MODULE diff --git a/usr/src/uts/sparc/specfs/Makefile b/usr/src/uts/sparc/specfs/Makefile index 6c5344719b..d19a549d3f 100644 --- a/usr/src/uts/sparc/specfs/Makefile +++ b/usr/src/uts/sparc/specfs/Makefile @@ -2,9 +2,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# 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. @@ -21,7 +20,7 @@ # # # uts/sparc/specfs/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -64,6 +63,7 @@ MODSTUBS_DIR = $(OBJS_DIR) $(MODSTUBS_O) := AS_CPPFLAGS += -DSPEC_MODULE CLEANFILES += $(MODSTUBS_O) CFLAGS += $(CCVERBOSE) +LDFLAGS += -dy -Nfs/fifofs # # Default build targets. diff --git a/usr/src/uts/sparc/zfs/Makefile b/usr/src/uts/sparc/zfs/Makefile index c9596a4eef..05ab541d5d 100644 --- a/usr/src/uts/sparc/zfs/Makefile +++ b/usr/src/uts/sparc/zfs/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE) # Overrides and depends_on # MODSTUBS_DIR = $(OBJS_DIR) -LDFLAGS += -dy -Nfs/specfs -Ndrv/random -Nmisc/idmap +LDFLAGS += -dy -Nfs/specfs -Ncrypto/swrand -Nmisc/idmap INC_PATH += -I$(UTSBASE)/common/fs/zfs INC_PATH += -I$(SRC)/common diff --git a/usr/src/uts/sparc/zfs/spa_boot.c b/usr/src/uts/sparc/zfs/spa_boot.c new file mode 100644 index 0000000000..6a20081bd6 --- /dev/null +++ b/usr/src/uts/sparc/zfs/spa_boot.c @@ -0,0 +1,123 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/spa.h> +#include <sys/bootconf.h> + +char * +spa_get_bootfs() +{ + int proplen; + char *zfs_bp; + + proplen = BOP_GETPROPLEN(bootops, "zfs-bootfs"); + if (proplen == 0) + return (NULL); + + zfs_bp = kmem_zalloc(proplen, KM_SLEEP); + if (BOP_GETPROP(bootops, "zfs-bootfs", zfs_bp) == -1) { + kmem_free(zfs_bp, proplen); + return (NULL); + } + + return (zfs_bp); +} + +void +spa_free_bootfs(char *bootfs) +{ + kmem_free(bootfs, strlen(bootfs) + 1); +} + +/* + * Given the boot device physpath, check if the device is in a valid state. + * If so, return the configuration from the vdev label. + */ +int +spa_get_rootconf(char *devpath, char **bestdev, nvlist_t **bestconf) +{ + nvlist_t *conf = NULL; + char *dev = NULL; + uint64_t txg = 0; + nvlist_t *nvtop, **child; + char *type; + uint_t children, c; + + spa_check_rootconf(devpath, &dev, &conf, &txg); + if (txg == 0 || conf == NULL) + return (EINVAL); + + VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE, + &nvtop) == 0); + VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0); + + if (strcmp(type, VDEV_TYPE_DISK) == 0) { + if (spa_rootdev_validate(nvtop)) + goto out; + else + return (EINVAL); + } + + ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0); + + VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN, + &child, &children) == 0); + + /* + * Go thru vdevs in the mirror to see if the given device (devpath) + * is in a healthy state. Also check if the given device has the most + * recent txg. Only the device with the most recent txg has valid + * information and can be booted. + */ + for (c = 0; c < children; c++) { + char *physpath; + + if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH, + &physpath) != 0) + return (EINVAL); + + if (strcmp(devpath, physpath) == 0) { + if (!spa_rootdev_validate(child[c])) + return (EINVAL); + } else { + /* get dev with the highest txg */ + if (spa_rootdev_validate(child[c])) { + spa_check_rootconf(physpath, &dev, + &conf, &txg); + } + } + } + + /* Does the given device have the most recent txg? */ + if (strcmp(devpath, dev) != 0) + return (EINVAL); +out: + *bestdev = dev; + *bestconf = conf; + return (0); +} diff --git a/usr/src/uts/sun/sys/promif.h b/usr/src/uts/sun/sys/promif.h index bd761e6fa0..cfb51d55f6 100644 --- a/usr/src/uts/sun/sys/promif.h +++ b/usr/src/uts/sun/sys/promif.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -316,6 +316,7 @@ extern pnode_t prom_findnode_bydevtype(pnode_t id, char *devtype); * file IO */ extern int prom_fopen(ihandle_t, char *); +extern int prom_volopen(ihandle_t, char *); extern int prom_fseek(ihandle_t, int, unsigned long long); extern int prom_fread(ihandle_t, int, caddr_t, size_t); extern int prom_fsize(ihandle_t, int, size_t *); diff --git a/usr/src/uts/sun4u/genunix/Makefile b/usr/src/uts/sun4u/genunix/Makefile index d3968bb247..9d86cdb173 100644 --- a/usr/src/uts/sun4u/genunix/Makefile +++ b/usr/src/uts/sun4u/genunix/Makefile @@ -96,6 +96,7 @@ $(PATCH_BUILD)IPCTF_TARGET = # CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(SRC)/common +CPPFLAGS += -I$(SRC)/uts/common/fs/zfs # # For now, disable these lint checks; maintainers should endeavor diff --git a/usr/src/uts/sun4v/genunix/Makefile b/usr/src/uts/sun4v/genunix/Makefile index dccdd6dcbb..c2111f62b2 100644 --- a/usr/src/uts/sun4v/genunix/Makefile +++ b/usr/src/uts/sun4v/genunix/Makefile @@ -96,6 +96,7 @@ $(PATCH_BUILD)IPCTF_TARGET = # CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(SRC)/common +CPPFLAGS += -I$(SRC)/uts/common/fs/zfs # # For now, disable these lint checks; maintainers should endeavor |