qemu-ga: add guest-fstrim command
FITRIM is a mounted filesystem feature to discard (or "trim") blocks which are not in use by the filesystem. This is useful for solid-state drives (SSDs) and thinly-provisioned storage. Provide access to the feature from the host so that filesystems can be trimmed periodically or before migration. Here is an example using scsi_debug: # modprobe scsi_debug lbpu=1 lbpws=1 # sg_vpd -p0xb2 /dev/sdb Logical block provisioning VPD page (SBC): Unmap command supported (LBPU): 1 Write same (16) with unmap bit supported (LBWS): 1 Write same (10) with unmap bit supported (LBWS10): 0 # mke2fs /dev/sdb # cat /sys/bus/pseudo/drivers/scsi_debug/map 1-616,16257-16383 # mount /dev/sdb /run/media/pbonzini/test # dd if=/dev/zero of=/run/media/pbonzini/test/file # cat map 1-616,645-1588,1599-4026,4029-16383 # rm /run/media/pbonzini/test/file # ./qemu-ga /dev/fd/0 {"execute":"guest-fstrim"} {"return": {}} # cat map 1-612 Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
This commit is contained in:
parent
af02203fbe
commit
eab5fd5989
@ -350,6 +350,26 @@
|
||||
{ 'command': 'guest-fsfreeze-thaw',
|
||||
'returns': 'int' }
|
||||
|
||||
##
|
||||
# @guest-fstrim:
|
||||
#
|
||||
# Discard (or "trim") blocks which are not in use by the filesystem.
|
||||
#
|
||||
# @minimum:
|
||||
# Minimum contiguous free range to discard, in bytes. Free ranges
|
||||
# smaller than this may be ignored (this is a hint and the guest
|
||||
# may not respect it). By increasing this value, the fstrim
|
||||
# operation will complete more quickly for filesystems with badly
|
||||
# fragmented free space, although not all blocks will be discarded.
|
||||
# The default value is zero, meaning "discard every free block".
|
||||
#
|
||||
# Returns: Nothing.
|
||||
#
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'command': 'guest-fstrim',
|
||||
'data': { '*minimum': 'int' } }
|
||||
|
||||
##
|
||||
# @guest-suspend-disk
|
||||
#
|
||||
|
@ -38,9 +38,12 @@ extern char **environ;
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#if defined(__linux__) && defined(FIFREEZE)
|
||||
#ifdef FIFREEZE
|
||||
#define CONFIG_FSFREEZE
|
||||
#endif
|
||||
#ifdef FITRIM
|
||||
#define CONFIG_FSTRIM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
|
||||
@ -312,8 +315,7 @@ static void guest_file_init(void)
|
||||
/* linux-specific implementations. avoid this if at all possible. */
|
||||
#if defined(__linux__)
|
||||
|
||||
#if defined(CONFIG_FSFREEZE)
|
||||
|
||||
#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
|
||||
typedef struct FsMount {
|
||||
char *dirname;
|
||||
char *devtype;
|
||||
@ -378,6 +380,9 @@ static int build_fs_mount_list(FsMountList *mounts)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_FSFREEZE)
|
||||
|
||||
/*
|
||||
* Return status of freeze/thaw
|
||||
@ -525,6 +530,65 @@ static void guest_fsfreeze_cleanup(void)
|
||||
}
|
||||
#endif /* CONFIG_FSFREEZE */
|
||||
|
||||
#if defined(CONFIG_FSTRIM)
|
||||
/*
|
||||
* Walk list of mounted file systems in the guest, and trim them.
|
||||
*/
|
||||
void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
|
||||
{
|
||||
int ret = 0;
|
||||
FsMountList mounts;
|
||||
struct FsMount *mount;
|
||||
int fd;
|
||||
char err_msg[512];
|
||||
struct fstrim_range r = {
|
||||
.start = 0,
|
||||
.len = -1,
|
||||
.minlen = has_minimum ? minimum : 0,
|
||||
};
|
||||
|
||||
slog("guest-fstrim called");
|
||||
|
||||
QTAILQ_INIT(&mounts);
|
||||
ret = build_fs_mount_list(&mounts);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(mount, &mounts, next) {
|
||||
fd = qemu_open(mount->dirname, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
sprintf(err_msg, "failed to open %s, %s", mount->dirname,
|
||||
strerror(errno));
|
||||
error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* We try to cull filesytems we know won't work in advance, but other
|
||||
* filesytems may not implement fstrim for less obvious reasons. These
|
||||
* will report EOPNOTSUPP; we simply ignore these errors. Any other
|
||||
* error means an unexpected error, so return it in those cases. In
|
||||
* some other cases ENOTTY will be reported (e.g. CD-ROMs).
|
||||
*/
|
||||
ret = ioctl(fd, FITRIM, &r);
|
||||
if (ret == -1) {
|
||||
if (errno != ENOTTY && errno != EOPNOTSUPP) {
|
||||
sprintf(err_msg, "failed to trim %s, %s",
|
||||
mount->dirname, strerror(errno));
|
||||
error_set(err, QERR_QGA_COMMAND_FAILED, err_msg);
|
||||
close(fd);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
error:
|
||||
free_fs_mount_list(&mounts);
|
||||
}
|
||||
#endif /* CONFIG_FSTRIM */
|
||||
|
||||
|
||||
#define LINUX_SYS_STATE_FILE "/sys/power/state"
|
||||
#define SUSPEND_SUPPORTED 0
|
||||
#define SUSPEND_NOT_SUPPORTED 1
|
||||
@ -918,7 +982,15 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_FSFREEZE */
|
||||
|
||||
#if !defined(CONFIG_FSTRIM)
|
||||
void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
|
||||
{
|
||||
error_set(err, QERR_UNSUPPORTED);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* register init/cleanup routines for stateful command groups */
|
||||
|
@ -173,6 +173,17 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk list of mounted file systems in the guest, and discard unused
|
||||
* areas.
|
||||
*/
|
||||
void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err)
|
||||
{
|
||||
error_set(err, QERR_UNSUPPORTED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
GUEST_SUSPEND_MODE_DISK,
|
||||
GUEST_SUSPEND_MODE_RAM
|
||||
|
Loading…
Reference in New Issue
Block a user