提交 9e8aded4 编写于 作者: M Michael Roth

qemu-ga: improve recovery options for fsfreeze

guest-fsfreeze-thaw relies on state information obtained from
guest-fsfreeze-freeze to determine what filesystems to unfreeze.
This is unreliable due to the fact that that state does not account
for FIFREEZE being issued by other processes, or previous instances
of qemu-ga. This means in certain situations we cannot thaw
filesystems even with a responsive qemu-ga instance at our disposal.

This patch allows guest-fsfreeze-thaw to be issued unconditionally.
It also adds some additional logic to allow us to thaw filesystems
regardless of how many times the filesystem's "frozen" refcount has
been incremented by any guest processes.

Also, guest-fsfreeze-freeze now operates atomically: on success all
freezable filesystems are frozen, and on error all filesystems are
thawed. The ambiguous "GUEST_FSFREEZE_STATUS_ERROR" state is no
longer entered.
Signed-off-by: NMichael Roth <mdroth@linux.vnet.ibm.com>
上级 a8b69b8e
...@@ -296,14 +296,10 @@ ...@@ -296,14 +296,10 @@
# #
# @frozen: all non-network guest filesystems frozen # @frozen: all non-network guest filesystems frozen
# #
# @error: failure to thaw 1 or more
# previously frozen filesystems, or failure to open a previously
# cached filesytem (filesystem unmounted/directory changes, etc).
#
# Since: 0.15.0 # Since: 0.15.0
## ##
{ 'enum': 'GuestFsfreezeStatus', { 'enum': 'GuestFsfreezeStatus',
'data': [ 'thawed', 'frozen', 'error' ] } 'data': [ 'thawed', 'frozen' ] }
## ##
# @guest-fsfreeze-status: # @guest-fsfreeze-status:
...@@ -312,6 +308,10 @@ ...@@ -312,6 +308,10 @@
# #
# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below) # Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
# #
# Note: This may fail to properly report the current state as a result of
# qemu-ga having been restarted, or other guest processes having issued
# an fs freeze/thaw.
#
# Since: 0.15.0 # Since: 0.15.0
## ##
{ 'command': 'guest-fsfreeze-status', { 'command': 'guest-fsfreeze-status',
...@@ -320,9 +320,10 @@ ...@@ -320,9 +320,10 @@
## ##
# @guest-fsfreeze-freeze: # @guest-fsfreeze-freeze:
# #
# Sync and freeze all non-network guest filesystems # Sync and freeze all freezable, local guest filesystems
# #
# Returns: Number of file systems frozen on success # Returns: Number of file systems currently frozen. On error, all filesystems
# will be thawed.
# #
# Since: 0.15.0 # Since: 0.15.0
## ##
...@@ -332,10 +333,15 @@ ...@@ -332,10 +333,15 @@
## ##
# @guest-fsfreeze-thaw: # @guest-fsfreeze-thaw:
# #
# Unfreeze frozen guest fileystems # Unfreeze all frozen guest filesystems
#
# Returns: Number of file systems thawed by this call
# #
# Returns: Number of file systems thawed # Note: if return value does not match the previous call to
# If error, -1 (unknown error) or -errno # guest-fsfreeze-freeze, this likely means some freezable
# filesystems were unfrozen before this call, and that the
# filesystem state may have changed before issuing this
# command.
# #
# Since: 0.15.0 # Since: 0.15.0
## ##
......
...@@ -332,28 +332,38 @@ typedef struct GuestFsfreezeMount { ...@@ -332,28 +332,38 @@ typedef struct GuestFsfreezeMount {
QTAILQ_ENTRY(GuestFsfreezeMount) next; QTAILQ_ENTRY(GuestFsfreezeMount) next;
} GuestFsfreezeMount; } GuestFsfreezeMount;
typedef QTAILQ_HEAD(, GuestFsfreezeMount) GuestFsfreezeMountList;
struct { struct {
GuestFsfreezeStatus status; GuestFsfreezeStatus status;
QTAILQ_HEAD(, GuestFsfreezeMount) mount_list;
} guest_fsfreeze_state; } guest_fsfreeze_state;
static void guest_fsfreeze_free_mount_list(GuestFsfreezeMountList *mounts)
{
GuestFsfreezeMount *mount, *temp;
if (!mounts) {
return;
}
QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
QTAILQ_REMOVE(mounts, mount, next);
g_free(mount->dirname);
g_free(mount->devtype);
g_free(mount);
}
}
/* /*
* Walk the mount table and build a list of local file systems * Walk the mount table and build a list of local file systems
*/ */
static int guest_fsfreeze_build_mount_list(void) static int guest_fsfreeze_build_mount_list(GuestFsfreezeMountList *mounts)
{ {
struct mntent *ment; struct mntent *ment;
GuestFsfreezeMount *mount, *temp; GuestFsfreezeMount *mount;
char const *mtab = MOUNTED; char const *mtab = MOUNTED;
FILE *fp; FILE *fp;
QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) {
QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next);
g_free(mount->dirname);
g_free(mount->devtype);
g_free(mount);
}
fp = setmntent(mtab, "r"); fp = setmntent(mtab, "r");
if (!fp) { if (!fp) {
g_warning("fsfreeze: unable to read mtab"); g_warning("fsfreeze: unable to read mtab");
...@@ -377,7 +387,7 @@ static int guest_fsfreeze_build_mount_list(void) ...@@ -377,7 +387,7 @@ static int guest_fsfreeze_build_mount_list(void)
mount->dirname = g_strdup(ment->mnt_dir); mount->dirname = g_strdup(ment->mnt_dir);
mount->devtype = g_strdup(ment->mnt_type); mount->devtype = g_strdup(ment->mnt_type);
QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next); QTAILQ_INSERT_TAIL(mounts, mount, next);
} }
endmntent(fp); endmntent(fp);
...@@ -400,17 +410,15 @@ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) ...@@ -400,17 +410,15 @@ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
int64_t qmp_guest_fsfreeze_freeze(Error **err) int64_t qmp_guest_fsfreeze_freeze(Error **err)
{ {
int ret = 0, i = 0; int ret = 0, i = 0;
struct GuestFsfreezeMount *mount, *temp; GuestFsfreezeMountList mounts;
struct GuestFsfreezeMount *mount;
int fd; int fd;
char err_msg[512]; char err_msg[512];
slog("guest-fsfreeze called"); slog("guest-fsfreeze called");
if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) { QTAILQ_INIT(&mounts);
return 0; ret = guest_fsfreeze_build_mount_list(&mounts);
}
ret = guest_fsfreeze_build_mount_list();
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
...@@ -418,43 +426,46 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err) ...@@ -418,43 +426,46 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err)
/* cannot risk guest agent blocking itself on a write in this state */ /* cannot risk guest agent blocking itself on a write in this state */
disable_logging(); disable_logging();
QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { QTAILQ_FOREACH(mount, &mounts, next) {
fd = qemu_open(mount->dirname, O_RDONLY); fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) { if (fd == -1) {
sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno)); sprintf(err_msg, "failed to open %s, %s", mount->dirname,
strerror(errno));
error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
goto error; goto error;
} }
/* we try to cull filesytems we know won't work in advance, but other /* we try to cull filesytems we know won't work in advance, but other
* filesytems may not implement fsfreeze for less obvious reasons. * filesytems may not implement fsfreeze for less obvious reasons.
* these will report EOPNOTSUPP, so we simply ignore them. when * these will report EOPNOTSUPP. we simply ignore these when tallying
* thawing, these filesystems will return an EINVAL instead, due to * the number of frozen filesystems.
* not being in a frozen state. Other filesystem-specific *
* errors may result in EINVAL, however, so the user should check the * any other error means a failure to freeze a filesystem we
* number * of filesystems returned here against those returned by the * expect to be freezable, so return an error in those cases
* thaw operation to determine whether everything completed * and return system to thawed state.
* successfully
*/ */
ret = ioctl(fd, FIFREEZE); ret = ioctl(fd, FIFREEZE);
if (ret < 0 && errno != EOPNOTSUPP) { if (ret == -1) {
sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno)); if (errno != EOPNOTSUPP) {
error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); sprintf(err_msg, "failed to freeze %s, %s",
close(fd); mount->dirname, strerror(errno));
goto error; error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
close(fd);
goto error;
}
} else {
i++;
} }
close(fd); close(fd);
i++;
} }
guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN; guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
guest_fsfreeze_free_mount_list(&mounts);
return i; return i;
error: error:
if (i > 0) { guest_fsfreeze_free_mount_list(&mounts);
qmp_guest_fsfreeze_thaw(NULL); qmp_guest_fsfreeze_thaw(NULL);
}
return 0; return 0;
} }
...@@ -464,39 +475,59 @@ error: ...@@ -464,39 +475,59 @@ error:
int64_t qmp_guest_fsfreeze_thaw(Error **err) int64_t qmp_guest_fsfreeze_thaw(Error **err)
{ {
int ret; int ret;
GuestFsfreezeMount *mount, *temp; GuestFsfreezeMountList mounts;
int fd, i = 0; GuestFsfreezeMount *mount;
bool has_error = false; int fd, i = 0, logged;
QTAILQ_INIT(&mounts);
ret = guest_fsfreeze_build_mount_list(&mounts);
if (ret) {
error_set(err, QERR_QGA_COMMAND_FAILED,
"failed to enumerate filesystems");
return 0;
}
QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { QTAILQ_FOREACH(mount, &mounts, next) {
logged = false;
fd = qemu_open(mount->dirname, O_RDONLY); fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) { if (fd == -1) {
has_error = true;
continue;
}
ret = ioctl(fd, FITHAW);
if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) {
has_error = true;
close(fd);
continue; continue;
} }
/* we have no way of knowing whether a filesystem was actually unfrozen
* as a result of a successful call to FITHAW, only that if an error
* was returned the filesystem was *not* unfrozen by that particular
* call.
*
* since multiple preceeding FIFREEZEs require multiple calls to FITHAW
* to unfreeze, continuing issuing FITHAW until an error is returned,
* in which case either the filesystem is in an unfreezable state, or,
* more likely, it was thawed previously (and remains so afterward).
*
* also, since the most recent successful call is the one that did
* the actual unfreeze, we can use this to provide an accurate count
* of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
* may * be useful for determining whether a filesystem was unfrozen
* during the freeze/thaw phase by a process other than qemu-ga.
*/
do {
ret = ioctl(fd, FITHAW);
if (ret == 0 && !logged) {
i++;
logged = true;
}
} while (ret == 0);
close(fd); close(fd);
i++;
} }
if (has_error) { guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
} else {
guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
}
enable_logging(); enable_logging();
guest_fsfreeze_free_mount_list(&mounts);
return i; return i;
} }
static void guest_fsfreeze_init(void) static void guest_fsfreeze_init(void)
{ {
guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
QTAILQ_INIT(&guest_fsfreeze_state.mount_list);
} }
static void guest_fsfreeze_cleanup(void) static void guest_fsfreeze_cleanup(void)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册