svn commit: r364355 - in head: stand/efi/loader stand/i386/loader stand/libsa/zfs stand/lua sys/cddl/boot/zfs sys/cddl/contrib/opensolaris/uts/common/fs/zfs sys/cddl/contrib/opensolaris/uts/common/...

Mariusz Zaborski oshogbo at FreeBSD.org
Tue Aug 18 19:48:06 UTC 2020


Author: oshogbo
Date: Tue Aug 18 19:48:04 2020
New Revision: 364355
URL: https://svnweb.freebsd.org/changeset/base/364355

Log:
  zfs: add an option to the bootloader to rewind the ZFS checkpoint
  
  The checkpoints are another way of keeping the state of ZFS.
  During the rewind, the pool has to be exported.
  This makes checkpoints unusable when using ZFS as root.
  Add the option to rewind the ZFS checkpoint at the boot time.
  If checkpoint exists, a new option for rewinding a checkpoint will appear in
  the bootloader menu.
  We fully support boot environments.
  If the rewind option is selected, the boot loader will show a list of
  boot environments that existed before the checkpoint.
  
  Reviewed by:	tsoome, allanjude, kevans (ok with high-level overview)
  Differential Revision:	https://reviews.freebsd.org/D24920

Modified:
  head/stand/efi/loader/main.c
  head/stand/i386/loader/main.c
  head/stand/libsa/zfs/libzfs.h
  head/stand/libsa/zfs/zfs.c
  head/stand/libsa/zfs/zfsimpl.c
  head/stand/lua/core.lua
  head/stand/lua/menu.lua
  head/sys/cddl/boot/zfs/zfsimpl.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c

Modified: head/stand/efi/loader/main.c
==============================================================================
--- head/stand/efi/loader/main.c	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/efi/loader/main.c	Tue Aug 18 19:48:04 2020	(r364355)
@@ -269,7 +269,7 @@ probe_zfs_currdev(uint64_t guid)
 	currdev.root_guid = 0;
 	set_currdev_devdesc((struct devdesc *)&currdev);
 	devname = efi_fmtdev(&currdev);
-	init_zfs_bootenv(devname);
+	init_zfs_boot_options(devname);
 
 	rv = sanity_check_currdev();
 	if (rv) {

Modified: head/stand/i386/loader/main.c
==============================================================================
--- head/stand/i386/loader/main.c	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/i386/loader/main.c	Tue Aug 18 19:48:04 2020	(r364355)
@@ -363,7 +363,7 @@ extract_currdev(void)
 
 #ifdef LOADER_ZFS_SUPPORT
 	if (new_currdev.dd.d_dev->dv_type == DEVT_ZFS)
-		init_zfs_bootenv(zfs_fmtdev(&new_currdev));
+		init_zfs_boot_options(zfs_fmtdev(&new_currdev));
 #endif
 
 	env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),

Modified: head/stand/libsa/zfs/libzfs.h
==============================================================================
--- head/stand/libsa/zfs/libzfs.h	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/libsa/zfs/libzfs.h	Tue Aug 18 19:48:04 2020	(r364355)
@@ -123,7 +123,7 @@ int	zfs_nextboot(void *vdev, char *buf, size_t size);
 int	zfs_probe_dev(const char *devname, uint64_t *pool_guid);
 int	zfs_list(const char *name);
 uint64_t ldi_get_size(void *);
-void	init_zfs_bootenv(const char *currdev);
+void	init_zfs_boot_options(const char *currdev);
 int	zfs_bootenv(const char *name);
 int	zfs_belist_add(const char *name, uint64_t __unused);
 int	zfs_set_env(void);

Modified: head/stand/libsa/zfs/zfs.c
==============================================================================
--- head/stand/libsa/zfs/zfs.c	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/libsa/zfs/zfs.c	Tue Aug 18 19:48:04 2020	(r364355)
@@ -60,7 +60,10 @@ static off_t	zfs_seek(struct open_file *f, off_t offse
 static int	zfs_stat(struct open_file *f, struct stat *sb);
 static int	zfs_readdir(struct open_file *f, struct dirent *d);
 
-static void	zfs_bootenv_initial(const char *);
+static void	zfs_bootenv_initial(const char *envname, spa_t *spa,
+		    const char *name, const char *dsname, int checkpoint);
+static void	zfs_checkpoints_initial(spa_t *spa, const char *name,
+		    const char *dsname);
 
 struct devsw zfs_dev;
 
@@ -1077,16 +1080,16 @@ zfs_fmtdev(void *vdev)
 	return (buf);
 }
 
-int
-zfs_list(const char *name)
+static int
+split_devname(const char *name, char *poolname, size_t size,
+    const char **dsnamep)
 {
-	static char	poolname[ZFS_MAXNAMELEN];
-	uint64_t	objid;
-	spa_t		*spa;
-	const char	*dsname;
-	int		len;
-	int		rv;
+	const char *dsname;
+	size_t len;
 
+	ASSERT(name != NULL);
+	ASSERT(poolname != NULL);
+
 	len = strlen(name);
 	dsname = strchr(name, '/');
 	if (dsname != NULL) {
@@ -1094,9 +1097,30 @@ zfs_list(const char *name)
 		dsname++;
 	} else
 		dsname = "";
-	memcpy(poolname, name, len);
-	poolname[len] = '\0';
 
+	if (len + 1 > size)
+		return (EINVAL);
+
+	strlcpy(poolname, name, len + 1);
+
+	if (dsnamep != NULL)
+		*dsnamep = dsname;
+
+	return (0);
+}
+
+int
+zfs_list(const char *name)
+{
+	static char	poolname[ZFS_MAXNAMELEN];
+	uint64_t	objid;
+	spa_t		*spa;
+	const char	*dsname;
+	int		rv;
+
+	if (split_devname(name, poolname, sizeof(poolname), &dsname) != 0)
+		return (EINVAL);
+
 	spa = spa_find_by_name(poolname);
 	if (!spa)
 		return (ENXIO);
@@ -1108,10 +1132,13 @@ zfs_list(const char *name)
 }
 
 void
-init_zfs_bootenv(const char *currdev_in)
+init_zfs_boot_options(const char *currdev_in)
 {
+	char poolname[ZFS_MAXNAMELEN];
 	char *beroot, *currdev;
+	spa_t *spa;
 	int currdev_len;
+	const char *dsname;
 
 	currdev = NULL;
 	currdev_len = strlen(currdev_in);
@@ -1124,6 +1151,7 @@ init_zfs_bootenv(const char *currdev_in)
 		return;
 	/* Remove the trailing : */
 	currdev[currdev_len - 1] = '\0';
+
 	setenv("zfs_be_active", currdev, 1);
 	setenv("zfs_be_currpage", "1", 1);
 	/* Remove the last element (current bootenv) */
@@ -1132,49 +1160,71 @@ init_zfs_bootenv(const char *currdev_in)
 		beroot[0] = '\0';
 	beroot = strchr(currdev, ':') + 1;
 	setenv("zfs_be_root", beroot, 1);
-	zfs_bootenv_initial(beroot);
+
+	if (split_devname(beroot, poolname, sizeof(poolname), &dsname) != 0)
+		return;
+
+	spa = spa_find_by_name(poolname);
+	if (spa == NULL)
+		return;
+
+	zfs_bootenv_initial("bootenvs", spa, beroot, dsname, 0);
+	zfs_checkpoints_initial(spa, beroot, dsname);
+
 	free(currdev);
 }
 
 static void
-zfs_bootenv_initial(const char *name)
+zfs_checkpoints_initial(spa_t *spa, const char *name, const char *dsname)
 {
-	char		poolname[ZFS_MAXNAMELEN], *dsname;
-	char envname[32], envval[256];
+	char envname[32];
+
+	if (spa->spa_uberblock_checkpoint.ub_checkpoint_txg != 0) {
+		snprintf(envname, sizeof(envname), "zpool_checkpoint");
+		setenv(envname, name, 1);
+
+		spa->spa_uberblock = &spa->spa_uberblock_checkpoint;
+		spa->spa_mos = &spa->spa_mos_checkpoint;
+
+		zfs_bootenv_initial("bootenvs_check", spa, name, dsname, 1);
+
+		spa->spa_uberblock = &spa->spa_uberblock_master;
+		spa->spa_mos = &spa->spa_mos_master;
+	}
+}
+
+static void
+zfs_bootenv_initial(const char *envprefix, spa_t *spa, const char *rootname,
+   const char *dsname, int checkpoint)
+{
+	char		envname[32], envval[256];
 	uint64_t	objid;
-	spa_t		*spa;
-	int		bootenvs_idx, len, rv;
+	int		bootenvs_idx, rv;
 
 	SLIST_INIT(&zfs_be_head);
 	zfs_env_count = 0;
-	len = strlen(name);
-	dsname = strchr(name, '/');
-	if (dsname != NULL) {
-		len = dsname - name;
-		dsname++;
-	} else
-		dsname = "";
-	strlcpy(poolname, name, len + 1);
-	spa = spa_find_by_name(poolname);
-	if (spa == NULL)
-		return;
+
 	rv = zfs_lookup_dataset(spa, dsname, &objid);
 	if (rv != 0)
 		return;
+
 	rv = zfs_callback_dataset(spa, objid, zfs_belist_add);
 	bootenvs_idx = 0;
 	/* Populate the initial environment variables */
 	SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) {
 		/* Enumerate all bootenvs for general usage */
-		snprintf(envname, sizeof(envname), "bootenvs[%d]", bootenvs_idx);
-		snprintf(envval, sizeof(envval), "zfs:%s/%s", name, zfs_be->name);
+		snprintf(envname, sizeof(envname), "%s[%d]",
+		    envprefix, bootenvs_idx);
+		snprintf(envval, sizeof(envval), "zfs:%s%s/%s",
+		    checkpoint ? "!" : "", rootname, zfs_be->name);
 		rv = setenv(envname, envval, 1);
 		if (rv != 0)
 			break;
 		bootenvs_idx++;
 	}
+	snprintf(envname, sizeof(envname), "%s_count", envprefix);
 	snprintf(envval, sizeof(envval), "%d", bootenvs_idx);
-	setenv("bootenvs_count", envval, 1);
+	setenv(envname, envval, 1);
 
 	/* Clean up the SLIST of ZFS BEs */
 	while (!SLIST_EMPTY(&zfs_be_head)) {
@@ -1183,19 +1233,17 @@ zfs_bootenv_initial(const char *name)
 		free(zfs_be->name);
 		free(zfs_be);
 	}
-
-	return;
-
 }
 
 int
 zfs_bootenv(const char *name)
 {
-	static char	poolname[ZFS_MAXNAMELEN], *dsname, *root;
+	char		poolname[ZFS_MAXNAMELEN], *root;
+	const char	*dsname;
 	char		becount[4];
 	uint64_t	objid;
 	spa_t		*spa;
-	int		len, rv, pages, perpage, currpage;
+	int		rv, pages, perpage, currpage;
 
 	if (name == NULL)
 		return (EINVAL);
@@ -1209,16 +1257,10 @@ zfs_bootenv(const char *name)
 
 	SLIST_INIT(&zfs_be_head);
 	zfs_env_count = 0;
-	len = strlen(name);
-	dsname = strchr(name, '/');
-	if (dsname != NULL) {
-		len = dsname - name;
-		dsname++;
-	} else
-		dsname = "";
-	memcpy(poolname, name, len);
-	poolname[len] = '\0';
 
+	if (split_devname(name, poolname, sizeof(poolname), &dsname) != 0)
+		return (EINVAL);
+
 	spa = spa_find_by_name(poolname);
 	if (!spa)
 		return (ENXIO);
@@ -1307,7 +1349,7 @@ zfs_set_env(void)
 			ctr++;
 			continue;
 		}
-		
+
 		snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
 		snprintf(envval, sizeof(envval), "%s", zfs_be->name);
 		rv = setenv(envname, envval, 1);
@@ -1340,7 +1382,7 @@ zfs_set_env(void)
 		}
 
 	}
-	
+
 	for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) {
 		snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
 		(void)unsetenv(envname);

Modified: head/stand/libsa/zfs/zfsimpl.c
==============================================================================
--- head/stand/libsa/zfs/zfsimpl.c	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/libsa/zfs/zfsimpl.c	Tue Aug 18 19:48:04 2020	(r364355)
@@ -699,7 +699,7 @@ vdev_indirect_read(vdev_t *vdev, const blkptr_t *bp, v
 
 		vic = &vdev->vdev_indirect_config;
 		vdev->v_mapping = vdev_indirect_mapping_open(spa,
-		    &spa->spa_mos, vic->vic_mapping_object);
+		    spa->spa_mos, vic->vic_mapping_object);
 	}
 
 	vdev_indirect_remap(vdev, offset, bytes, &zio);
@@ -1347,6 +1347,8 @@ spa_create(uint64_t guid, const char *name)
 		free(spa);
 		return (NULL);
 	}
+	spa->spa_uberblock = &spa->spa_uberblock_master;
+	spa->spa_mos = &spa->spa_mos_master;
 	spa->spa_guid = guid;
 	spa->spa_root_vdev = vdev_create(guid, NULL);
 	if (spa->spa_root_vdev == NULL) {
@@ -1883,7 +1885,7 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, s
 	 * the best uberblock and then we can actually access
 	 * the contents of the pool.
 	 */
-	vdev_uberblock_load(vdev, &spa->spa_uberblock);
+	vdev_uberblock_load(vdev, spa->spa_uberblock);
 
 	if (spap != NULL)
 		*spap = spa;
@@ -2409,8 +2411,9 @@ fzap_lookup(const spa_t *spa, const dnode_phys_t *dnod
 	if (zh->zap_magic != ZAP_MAGIC)
 		return (EIO);
 
-	if ((rc = fzap_check_size(integer_size, num_integers)) != 0)
+	if ((rc = fzap_check_size(integer_size, num_integers)) != 0) {
 		return (rc);
+	}
 
 	z.zap_block_shift = ilog2(bsize);
 	z.zap_phys = zh;
@@ -2766,7 +2769,7 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char *r
 	p = &name[sizeof(name) - 1];
 	*p = '\0';
 
-	if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+	if (objset_get_dnode(spa, spa->spa_mos, objnum, &dataset)) {
 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
 		return (EIO);
 	}
@@ -2774,7 +2777,7 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char *r
 	dir_obj = ds->ds_dir_obj;
 
 	for (;;) {
-		if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0)
+		if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir) != 0)
 			return (EIO);
 		dd = (dsl_dir_phys_t *)&dir.dn_bonus;
 
@@ -2783,12 +2786,12 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char *r
 		if (parent_obj == 0)
 			break;
 
-		if (objset_get_dnode(spa, &spa->spa_mos, parent_obj,
+		if (objset_get_dnode(spa, spa->spa_mos, parent_obj,
 		    &parent) != 0)
 			return (EIO);
 		dd = (dsl_dir_phys_t *)&parent.dn_bonus;
 		child_dir_zapobj = dd->dd_child_dir_zapobj;
-		if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
+		if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
 		    &child_dir_zap) != 0)
 			return (EIO);
 		if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0)
@@ -2820,7 +2823,7 @@ zfs_lookup_dataset(const spa_t *spa, const char *name,
 	dsl_dir_phys_t *dd;
 	const char *p, *q;
 
-	if (objset_get_dnode(spa, &spa->spa_mos,
+	if (objset_get_dnode(spa, spa->spa_mos,
 	    DMU_POOL_DIRECTORY_OBJECT, &dir))
 		return (EIO);
 	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (dir_obj),
@@ -2829,7 +2832,7 @@ zfs_lookup_dataset(const spa_t *spa, const char *name,
 
 	p = name;
 	for (;;) {
-		if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir))
+		if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir))
 			return (EIO);
 		dd = (dsl_dir_phys_t *)&dir.dn_bonus;
 
@@ -2850,7 +2853,7 @@ zfs_lookup_dataset(const spa_t *spa, const char *name,
 		}
 
 		child_dir_zapobj = dd->dd_child_dir_zapobj;
-		if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
+		if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
 		    &child_dir_zap) != 0)
 			return (EIO);
 
@@ -2873,21 +2876,21 @@ zfs_list_dataset(const spa_t *spa, uint64_t objnum/*, 
 	dsl_dataset_phys_t *ds;
 	dsl_dir_phys_t *dd;
 
-	if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+	if (objset_get_dnode(spa, spa->spa_mos, objnum, &dataset)) {
 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
 		return (EIO);
 	}
 	ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
 	dir_obj = ds->ds_dir_obj;
 
-	if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) {
+	if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir)) {
 		printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
 		return (EIO);
 	}
 	dd = (dsl_dir_phys_t *)&dir.dn_bonus;
 
 	child_dir_zapobj = dd->dd_child_dir_zapobj;
-	if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
+	if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
 	    &child_dir_zap) != 0) {
 		printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
 		return (EIO);
@@ -2908,7 +2911,7 @@ zfs_callback_dataset(const spa_t *spa, uint64_t objnum
 	size_t size;
 	int err;
 
-	err = objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset);
+	err = objset_get_dnode(spa, spa->spa_mos, objnum, &dataset);
 	if (err != 0) {
 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
 		return (err);
@@ -2916,7 +2919,7 @@ zfs_callback_dataset(const spa_t *spa, uint64_t objnum
 	ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
 	dir_obj = ds->ds_dir_obj;
 
-	err = objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir);
+	err = objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir);
 	if (err != 0) {
 		printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
 		return (err);
@@ -2924,7 +2927,7 @@ zfs_callback_dataset(const spa_t *spa, uint64_t objnum
 	dd = (dsl_dir_phys_t *)&dir.dn_bonus;
 
 	child_dir_zapobj = dd->dd_child_dir_zapobj;
-	err = objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
+	err = objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
 	    &child_dir_zap);
 	if (err != 0) {
 		printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
@@ -2962,7 +2965,7 @@ zfs_mount_dataset(const spa_t *spa, uint64_t objnum, o
 	dnode_phys_t dataset;
 	dsl_dataset_phys_t *ds;
 
-	if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
+	if (objset_get_dnode(spa, spa->spa_mos, objnum, &dataset)) {
 		printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
 		return (EIO);
 	}
@@ -2992,7 +2995,7 @@ zfs_get_root(const spa_t *spa, uint64_t *objid)
 	/*
 	 * Start with the MOS directory object.
 	 */
-	if (objset_get_dnode(spa, &spa->spa_mos,
+	if (objset_get_dnode(spa, spa->spa_mos,
 	    DMU_POOL_DIRECTORY_OBJECT, &dir)) {
 		printf("ZFS: can't read MOS object directory\n");
 		return (EIO);
@@ -3003,7 +3006,7 @@ zfs_get_root(const spa_t *spa, uint64_t *objid)
 	 */
 	if (zap_lookup(spa, &dir, DMU_POOL_PROPS,
 	    sizeof(props), 1, &props) == 0 &&
-	    objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0 &&
+	    objset_get_dnode(spa, spa->spa_mos, props, &propdir) == 0 &&
 	    zap_lookup(spa, &propdir, "bootfs",
 	    sizeof(bootfs), 1, &bootfs) == 0 && bootfs != 0) {
 		*objid = bootfs;
@@ -3014,7 +3017,7 @@ zfs_get_root(const spa_t *spa, uint64_t *objid)
 	 */
 	if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET,
 	    sizeof(root), 1, &root) ||
-	    objset_get_dnode(spa, &spa->spa_mos, root, &dir)) {
+	    objset_get_dnode(spa, spa->spa_mos, root, &dir)) {
 		printf("ZFS: can't find root dsl_dir\n");
 		return (EIO);
 	}
@@ -3085,7 +3088,7 @@ check_mos_features(const spa_t *spa)
 	size_t size;
 	int rc;
 
-	if ((rc = objset_get_dnode(spa, &spa->spa_mos, DMU_OT_OBJECT_DIRECTORY,
+	if ((rc = objset_get_dnode(spa, spa->spa_mos, DMU_OT_OBJECT_DIRECTORY,
 	    &dir)) != 0)
 		return (rc);
 	if ((rc = zap_lookup(spa, &dir, DMU_POOL_FEATURES_FOR_READ,
@@ -3097,7 +3100,7 @@ check_mos_features(const spa_t *spa)
 		return (0);
 	}
 
-	if ((rc = objset_get_dnode(spa, &spa->spa_mos, objnum, &dir)) != 0)
+	if ((rc = objset_get_dnode(spa, spa->spa_mos, objnum, &dir)) != 0)
 		return (rc);
 
 	if (dir.dn_type != DMU_OTN_ZAP_METADATA)
@@ -3131,7 +3134,7 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value
 	unsigned char *nv;
 
 	*value = NULL;
-	if ((rc = objset_get_dnode(spa, &spa->spa_mos, obj, &dir)) != 0)
+	if ((rc = objset_get_dnode(spa, spa->spa_mos, obj, &dir)) != 0)
 		return (rc);
 	if (dir.dn_type != DMU_OT_PACKED_NVLIST &&
 	    dir.dn_bonustype != DMU_OT_PACKED_NVLIST_SIZE) {
@@ -3160,22 +3163,23 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value
 static int
 zfs_spa_init(spa_t *spa)
 {
+	struct uberblock checkpoint;
 	dnode_phys_t dir;
 	uint64_t config_object;
 	nvlist_t *nvlist;
 	int rc;
 
-	if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
+	if (zio_read(spa, &spa->spa_uberblock->ub_rootbp, spa->spa_mos)) {
 		printf("ZFS: can't read MOS of pool %s\n", spa->spa_name);
 		return (EIO);
 	}
-	if (spa->spa_mos.os_type != DMU_OST_META) {
+	if (spa->spa_mos->os_type != DMU_OST_META) {
 		printf("ZFS: corrupted MOS of pool %s\n", spa->spa_name);
 		return (EIO);
 	}
 
-	if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT,
-	    &dir)) {
+	if (objset_get_dnode(spa, &spa->spa_mos_master,
+	    DMU_POOL_DIRECTORY_OBJECT, &dir)) {
 		printf("ZFS: failed to read pool %s directory object\n",
 		    spa->spa_name);
 		return (EIO);
@@ -3200,6 +3204,20 @@ zfs_spa_init(spa_t *spa)
 	rc = load_nvlist(spa, config_object, &nvlist);
 	if (rc != 0)
 		return (rc);
+
+	rc = zap_lookup(spa, &dir, DMU_POOL_ZPOOL_CHECKPOINT,
+	    sizeof(uint64_t), sizeof(checkpoint) / sizeof(uint64_t),
+	    &checkpoint);
+	if (rc == 0 && checkpoint.ub_checkpoint_txg != 0) {
+		memcpy(&spa->spa_uberblock_checkpoint, &checkpoint,
+		    sizeof(checkpoint));
+		if (zio_read(spa, &spa->spa_uberblock_checkpoint.ub_rootbp,
+		    &spa->spa_mos_checkpoint)) {
+			printf("ZFS: can not read checkpoint data.\n");
+			return (EIO);
+		}
+	}
+
 	/*
 	 * Update vdevs from MOS config. Note, we do skip encoding bytes
 	 * here. See also vdev_label_read_config().

Modified: head/stand/lua/core.lua
==============================================================================
--- head/stand/lua/core.lua	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/lua/core.lua	Tue Aug 18 19:48:04 2020	(r364355)
@@ -38,6 +38,8 @@ local default_safe_mode = false
 local default_single_user = false
 local default_verbose = false
 
+local bootenv_list = "bootenvs"
+
 local function composeLoaderCmd(cmd_name, argstr)
 	if argstr ~= nil then
 		cmd_name = cmd_name .. " " .. argstr
@@ -270,7 +272,7 @@ function core.bootenvDefault()
 end
 
 function core.bootenvList()
-	local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
+	local bootenv_count = tonumber(loader.getenv(bootenv_list .. "_count"))
 	local bootenvs = {}
 	local curenv
 	local envcount = 0
@@ -281,7 +283,12 @@ function core.bootenvList()
 	end
 
 	-- Currently selected bootenv is always first/default
-	curenv = core.bootenvDefault()
+	-- On the rewinded list the bootenv may not exists
+	if core.isRewinded() then
+		curenv = core.bootenvDefaultRewinded()
+	else
+		curenv = core.bootenvDefault()
+	end
 	if curenv ~= nil then
 		envcount = envcount + 1
 		bootenvs[envcount] = curenv
@@ -289,7 +296,7 @@ function core.bootenvList()
 	end
 
 	for curenv_idx = 0, bootenv_count - 1 do
-		curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
+		curenv = loader.getenv(bootenv_list .. "[" .. curenv_idx .. "]")
 		if curenv ~= nil and unique[curenv] == nil then
 			envcount = envcount + 1
 			bootenvs[envcount] = curenv
@@ -297,6 +304,40 @@ function core.bootenvList()
 		end
 	end
 	return bootenvs
+end
+
+function core.isCheckpointed()
+	return loader.getenv("zpool_checkpoint") ~= nil
+end
+
+function core.bootenvDefaultRewinded()
+	local defname =  "zfs:!" .. string.sub(core.bootenvDefault(), 5)
+	local bootenv_count = tonumber("bootenvs_check_count")
+
+	if bootenv_count == nil or bootenv_count <= 0 then
+		return defname
+	end
+
+	for curenv_idx = 0, bootenv_count - 1 do
+		curenv = loader.getenv("bootenvs_check[" .. curenv_idx .. "]")
+		if curenv == defname then
+			return defname
+		end
+	end
+
+	return loader.getenv("bootenvs_check[0]")
+end
+
+function core.isRewinded()
+	return bootenv_list == "bootenvs_check"
+end
+
+function core.changeRewindCheckpoint()
+	if core.isRewinded() then
+		bootenv_list = "bootenvs"
+	else
+		bootenv_list = "bootenvs_check"
+	end
 end
 
 function core.setDefaults()

Modified: head/stand/lua/menu.lua
==============================================================================
--- head/stand/lua/menu.lua	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/stand/lua/menu.lua	Tue Aug 18 19:48:04 2020	(r364355)
@@ -132,6 +132,9 @@ menu.boot_environments = {
 		},
 		{
 			entry_type = core.MENU_ENTRY,
+			visible = function()
+				return core.isRewinded() == false
+			end,
 			name = function()
 				return color.highlight("b") .. "ootfs: " ..
 				    core.bootenvDefault()
@@ -250,6 +253,7 @@ menu.welcome = {
 			},
 			menu_entries.kernel_options,
 			menu_entries.boot_options,
+			menu_entries.zpool_checkpoints,
 			menu_entries.boot_envs,
 			menu_entries.chainload,
 		}
@@ -333,6 +337,32 @@ menu.welcome = {
 			name = "Boot " .. color.highlight("O") .. "ptions",
 			submenu = menu.boot_options,
 			alias = {"o", "O"},
+		},
+		zpool_checkpoints = {
+			entry_type = core.MENU_ENTRY,
+			name = function()
+				rewind = "No"
+				if core.isRewinded() then
+					rewind = "Yes"
+				end
+				return "Rewind ZFS " .. color.highlight("C") ..
+					"heckpoint: " .. rewind
+			end,
+			func = function()
+				core.changeRewindCheckpoint()
+				if core.isRewinded() then
+					bootenvSet(
+					    core.bootenvDefaultRewinded())
+				else
+					bootenvSet(core.bootenvDefault())
+				end
+				config.setCarouselIndex("be_active", 1)
+			end,
+			visible = function()
+				return core.isZFSBoot() and
+				    core.isCheckpointed()
+			end,
+			alias = {"c", "C"},
 		},
 		boot_envs = {
 			entry_type = core.MENU_SUBMENU,

Modified: head/sys/cddl/boot/zfs/zfsimpl.h
==============================================================================
--- head/sys/cddl/boot/zfs/zfsimpl.h	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/sys/cddl/boot/zfs/zfsimpl.h	Tue Aug 18 19:48:04 2020	(r364355)
@@ -1351,6 +1351,7 @@ typedef struct dsl_dataset_phys {
 #define	DMU_POOL_REMOVING		"com.delphix:removing"
 #define	DMU_POOL_OBSOLETE_BPOBJ		"com.delphix:obsolete_bpobj"
 #define	DMU_POOL_CONDENSING_INDIRECT	"com.delphix:condensing_indirect"
+#define	DMU_POOL_ZPOOL_CHECKPOINT       "com.delphix:zpool_checkpoint"
 
 #define	ZAP_MAGIC 0x2F52AB2ABULL
 
@@ -1814,12 +1815,17 @@ typedef struct spa {
 	char		*spa_name;	/* pool name */
 	uint64_t	spa_guid;	/* pool guid */
 	uint64_t	spa_txg;	/* most recent transaction */
-	struct uberblock spa_uberblock;	/* best uberblock so far */
+	struct uberblock *spa_uberblock;	/* best uberblock so far */
 	vdev_t		*spa_root_vdev;	/* toplevel vdev container */
-	objset_phys_t	spa_mos;	/* MOS for this pool */
+	objset_phys_t	*spa_mos;	/* MOS for this pool */
 	zio_cksum_salt_t spa_cksum_salt;	/* secret salt for cksum */
 	void		*spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
 	boolean_t	spa_with_log;	/* this pool has log */
+
+	struct uberblock spa_uberblock_master;  /* best uberblock so far */
+	objset_phys_t    spa_mos_master;        /* MOS for this pool */
+	struct uberblock spa_uberblock_checkpoint;      /* checkpoint uberblock */
+	objset_phys_t    spa_mos_checkpoint;    /* Checkpoint MOS */
 } spa_t;
 
 /* IO related arguments. */

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c	Tue Aug 18 19:48:04 2020	(r364355)
@@ -5719,7 +5719,7 @@ spa_generate_rootconf(const char *name)
 }
 
 int
-spa_import_rootpool(const char *name)
+spa_import_rootpool(const char *name, bool checkpointrewind)
 {
 	spa_t *spa;
 	vdev_t *rvd, *bvd, *avd = NULL;
@@ -5778,6 +5778,9 @@ spa_import_rootpool(const char *name)
 	}
 	spa->spa_is_root = B_TRUE;
 	spa->spa_import_flags = ZFS_IMPORT_VERBATIM;
+	if (checkpointrewind) {
+		spa->spa_import_flags |= ZFS_IMPORT_CHECKPOINT;
+	}
 
 	/*
 	 * Build up a vdev tree based on the boot device's label config.

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h	Tue Aug 18 19:48:04 2020	(r364355)
@@ -643,7 +643,7 @@ extern int spa_create(const char *pool, nvlist_t *conf
 #ifdef illumos
 extern int spa_import_rootpool(char *devpath, char *devid);
 #else
-extern int spa_import_rootpool(const char *name);
+extern int spa_import_rootpool(const char *name, bool checkpointrewind);
 #endif
 extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props,
     uint64_t flags);

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c	Tue Aug 18 19:34:31 2020	(r364354)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c	Tue Aug 18 19:48:04 2020	(r364355)
@@ -1783,6 +1783,18 @@ getpoolname(const char *osname, char *poolname)
 	return (0);
 }
 
+static void
+fetch_osname_options(char *name, bool *checkpointrewind)
+{
+
+	if (name[0] == '!') {
+		*checkpointrewind = true;
+		memmove(name, name + 1, strlen(name));
+	} else {
+		*checkpointrewind = false;
+	}
+}
+
 /*ARGSUSED*/
 static int
 zfs_mount(vfs_t *vfsp)
@@ -1793,6 +1805,7 @@ zfs_mount(vfs_t *vfsp)
 	char		*osname;
 	int		error = 0;
 	int		canwrite;
+	bool		checkpointrewind;
 
 #ifdef illumos
 	if (mvp->v_type != VDIR)
@@ -1836,6 +1849,7 @@ zfs_mount(vfs_t *vfsp)
 		secpolicy_fs_mount_clearopts(cr, vfsp);
 	}
 #endif	/* illumos */
+	fetch_osname_options(osname, &checkpointrewind);
 
 	/*
 	 * Check for mount privilege?
@@ -1921,7 +1935,7 @@ zfs_mount(vfs_t *vfsp)
 
 		error = getpoolname(osname, pname);
 		if (error == 0)
-			error = spa_import_rootpool(pname);
+			error = spa_import_rootpool(pname, checkpointrewind);
 		if (error)
 			goto out;
 	}


More information about the svn-src-head mailing list