OpenZFS 8592 - ZFS channel programs - rollback

Authored by: Brad Lewis <brad.lewis@delphix.com>
Reviewed by: Chris Williamson <chris.williamson@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Ported-by: Don Brady <don.brady@delphix.com>

ZFS channel programs should be able to perform a rollback.

OpenZFS-issue: https://www.illumos.org/issues/8592
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/d46b5ed6
This commit is contained in:
Brad Lewis 2018-02-08 09:20:33 -07:00 committed by Brian Behlendorf
parent 475eca4908
commit af07368986
8 changed files with 177 additions and 16 deletions

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
*/ */
@ -245,6 +245,13 @@ typedef struct dsl_dataset_promote_arg {
cred_t *cr; cred_t *cr;
} dsl_dataset_promote_arg_t; } dsl_dataset_promote_arg_t;
typedef struct dsl_dataset_rollback_arg {
const char *ddra_fsname;
const char *ddra_tosnap;
void *ddra_owner;
nvlist_t *ddra_result;
} dsl_dataset_rollback_arg_t;
/* /*
* The max length of a temporary tag prefix is the number of hex digits * The max length of a temporary tag prefix is the number of hex digits
* required to express UINT64_MAX plus one for the hyphen. * required to express UINT64_MAX plus one for the hyphen.
@ -394,6 +401,9 @@ void dsl_dataset_set_refreservation_sync_impl(dsl_dataset_t *ds,
void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx); void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx);
boolean_t dsl_dataset_is_zapified(dsl_dataset_t *ds); boolean_t dsl_dataset_is_zapified(dsl_dataset_t *ds);
boolean_t dsl_dataset_has_resume_receive_state(dsl_dataset_t *ds); boolean_t dsl_dataset_has_resume_receive_state(dsl_dataset_t *ds);
int dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx);
void dsl_dataset_rollback_sync(void *arg, dmu_tx_t *tx);
int dsl_dataset_rollback(const char *fsname, const char *tosnap, void *owner, int dsl_dataset_rollback(const char *fsname, const char *tosnap, void *owner,
nvlist_t *result); nvlist_t *result);

View File

@ -8,7 +8,7 @@
.\" http://www.illumos.org/license/CDDL. .\" http://www.illumos.org/license/CDDL.
.\" .\"
.\" .\"
.\" Copyright (c) 2016 by Delphix. All Rights Reserved. .\" Copyright (c) 2016, 2017 by Delphix. All Rights Reserved.
.\" .\"
.Dd January 21, 2016 .Dd January 21, 2016
.Dt ZFS-PROGRAM 8 .Dt ZFS-PROGRAM 8
@ -361,6 +361,17 @@ dataset (string)
.Bd -ragged -compact -offset "xxxx" .Bd -ragged -compact -offset "xxxx"
Clone to be promoted. Clone to be promoted.
.Ed .Ed
.It Em zfs.sync.rollback(filesystem)
Rollback to the previous snapshot for a dataset.
Returns 0 on successful rollback, or a nonzero error code otherwise.
Rollbacks can be performed on filesystems or zvols, but not on snapshots
or mounted datasets.
EBUSY is returned in the case where the filesystem is mounted.
.Pp
filesystem (string)
.Bd -ragged -compact -offset "xxxx"
Filesystem to rollback.
.Ed
.El .El
.It Sy zfs.check submodule .It Sy zfs.check submodule
For each function in the zfs.sync submodule, there is a corresponding zfs.check For each function in the zfs.sync submodule, there is a corresponding zfs.check
@ -380,6 +391,7 @@ The available zfs.check functions are:
.Bl -tag -width "xx" .Bl -tag -width "xx"
.It Em zfs.check.destroy(dataset, [defer=true|false]) .It Em zfs.check.destroy(dataset, [defer=true|false])
.It Em zfs.check.promote(dataset) .It Em zfs.check.promote(dataset)
.It Em zfs.check.rollback(filesystem)
.El .El
.It Sy zfs.list submodule .It Sy zfs.list submodule
The zfs.list submodule provides functions for iterating over datasets and The zfs.list submodule provides functions for iterating over datasets and

View File

@ -21,7 +21,7 @@
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved. * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2014 RackTop Systems. * Copyright (c) 2014 RackTop Systems.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
@ -2526,14 +2526,7 @@ dsl_dataset_handoff_check(dsl_dataset_t *ds, void *owner, dmu_tx_t *tx)
return (0); return (0);
} }
typedef struct dsl_dataset_rollback_arg { int
const char *ddra_fsname;
const char *ddra_tosnap;
void *ddra_owner;
nvlist_t *ddra_result;
} dsl_dataset_rollback_arg_t;
static int
dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx) dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
{ {
dsl_dataset_rollback_arg_t *ddra = arg; dsl_dataset_rollback_arg_t *ddra = arg;
@ -2641,7 +2634,7 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
return (0); return (0);
} }
static void void
dsl_dataset_rollback_sync(void *arg, dmu_tx_t *tx) dsl_dataset_rollback_sync(void *arg, dmu_tx_t *tx)
{ {
dsl_dataset_rollback_arg_t *ddra = arg; dsl_dataset_rollback_arg_t *ddra = arg;

View File

@ -14,7 +14,7 @@
*/ */
/* /*
* Copyright (c) 2016 by Delphix. All rights reserved. * Copyright (c) 2016, 2017 by Delphix. All rights reserved.
*/ */
#include <sys/lua/lua.h> #include <sys/lua/lua.h>
@ -177,6 +177,37 @@ zcp_synctask_promote(lua_State *state, boolean_t sync, nvlist_t *err_details)
return (err); return (err);
} }
static int zcp_synctask_rollback(lua_State *, boolean_t, nvlist_t *err_details);
static zcp_synctask_info_t zcp_synctask_rollback_info = {
.name = "rollback",
.func = zcp_synctask_rollback,
.space_check = ZFS_SPACE_CHECK_RESERVED,
.blocks_modified = 1,
.pargs = {
{.za_name = "filesystem", .za_lua_type = LUA_TSTRING},
{0, 0}
},
.kwargs = {
{0, 0}
}
};
static int
zcp_synctask_rollback(lua_State *state, boolean_t sync, nvlist_t *err_details)
{
int err;
const char *dsname = lua_tostring(state, 1);
dsl_dataset_rollback_arg_t ddra = { 0 };
ddra.ddra_fsname = dsname;
ddra.ddra_result = err_details;
err = zcp_sync_task(state, dsl_dataset_rollback_check,
dsl_dataset_rollback_sync, &ddra, sync, dsname);
return (err);
}
void void
zcp_synctask_wrapper_cleanup(void *arg) zcp_synctask_wrapper_cleanup(void *arg)
{ {
@ -247,6 +278,7 @@ zcp_load_synctask_lib(lua_State *state, boolean_t sync)
zcp_synctask_info_t *zcp_synctask_funcs[] = { zcp_synctask_info_t *zcp_synctask_funcs[] = {
&zcp_synctask_destroy_info, &zcp_synctask_destroy_info,
&zcp_synctask_promote_info, &zcp_synctask_promote_info,
&zcp_synctask_rollback_info,
NULL NULL
}; };

View File

@ -78,7 +78,7 @@ tests = ['tst.destroy_fs', 'tst.destroy_snap', 'tst.get_count_and_limit',
'tst.get_userquota', 'tst.get_written', 'tst.list_children', 'tst.get_userquota', 'tst.get_written', 'tst.list_children',
'tst.list_clones', 'tst.list_snapshots', 'tst.list_system_props', 'tst.list_clones', 'tst.list_snapshots', 'tst.list_system_props',
'tst.parse_args_neg', 'tst.promote_conflict', 'tst.promote_multiple', 'tst.parse_args_neg', 'tst.promote_conflict', 'tst.promote_multiple',
'tst.promote_simple'] 'tst.promote_simple', 'tst.rollback_mult', 'tst.rollback_one']
tags = ['functional', 'channel_program', 'synctask_core'] tags = ['functional', 'channel_program', 'synctask_core']
[tests/functional/chattr] [tests/functional/chattr]

View File

@ -27,4 +27,6 @@ dist_pkgdata_SCRIPTS = \
tst.promote_conflict.ksh \ tst.promote_conflict.ksh \
tst.promote_conflict.zcp \ tst.promote_conflict.zcp \
tst.promote_multiple.ksh \ tst.promote_multiple.ksh \
tst.promote_simple.ksh tst.promote_simple.ksh \
tst.rollback_mult.ksh \
tst.rollback_one.ksh

View File

@ -0,0 +1,61 @@
#!/bin/ksh -p
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
verify_runnable "global"
snap1=$TESTPOOL/$TESTFS@$TESTSNAP1
snap2=$TESTPOOL/$TESTFS@$TESTSNAP2
fs=$TESTPOOL/$TESTFS
file=$TESTDIR/$TESTFILE0
function cleanup
{
datasetexists $snap1 && log_must zfs destroy $snap1 && \
log_must rm $file
}
log_onexit cleanup
log_must mkfile 128b $file
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP1
log_must rm $file
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP2
log_must snapexists $snap1
log_must snapexists $snap2
log_must zfs unmount $fs
log_must_program $TESTPOOL - $fs $snap2 <<-EOF
arg = ...
fs = arg["argv"][1]
snap = arg["argv"][2]
err = zfs.sync.rollback(fs)
if err == 0 then
err = zfs.sync.destroy(snap)
end
if err == 0 then
err = zfs.sync.rollback(fs)
end
msg = "rolling back " .. fs .. " err=" .. err
return msg
EOF
log_must zfs mount $fs
log_must [ -f $file ]
log_mustnot snapexists $snap2
log_pass "Rolling back snapshot with channel program works."

View File

@ -0,0 +1,51 @@
#!/bin/ksh -p
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
verify_runnable "global"
snap=$TESTPOOL/$TESTFS@$TESTSNAP
fs=$TESTPOOL/$TESTFS
file=$TESTDIR/$TESTFILE0
function cleanup
{
datasetexists $snap && log_must zfs destroy $snap && \
log_must rm $file
}
log_onexit cleanup
log_must mkfile 128b $file
create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
log_must rm $file
log_must snapexists $snap
log_must zfs unmount $fs
log_must_program $TESTPOOL - $fs <<-EOF
arg = ...
fs = arg["argv"][1]
err = zfs.sync.rollback(fs)
msg = "rolling back " .. fs .. " err=" .. err
return msg
EOF
log_must zfs mount $fs
log_must [ -f $file ]
log_pass "Rolling back snapshot with channel program works."