On Tue, 12 Jun 2012 12:50:08 +0200 Paolo Bonzini wrote: > 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 > --- > qapi-schema-guest.json | 20 ++++++++++++++ > qga/commands-posix.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++- > qga/commands-win32.c | 11 ++++++++ > 3 files changed, 102 insertions(+), 1 deletion(-) > > diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json > index d4055d2..72bedac 100644 > --- a/qapi-schema-guest.json > +++ b/qapi-schema-guest.json > @@ -351,6 +351,26 @@ > '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: 0.15.0 1.2. Otherwise looks good to me. > +## > +{ 'command': 'guest-fstrim', > + 'data': { '*minimum': 'int' } } > + > +## > # @guest-suspend-disk > # > # Suspend guest to disk. > diff --git a/qga/commands-posix.c b/qga/commands-posix.c > index b1a7ce6..802d13a 100644 > --- a/qga/commands-posix.c > +++ b/qga/commands-posix.c > @@ -38,9 +38,12 @@ extern char **environ; > #include > #include > > -#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) > @@ -525,6 +528,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 +980,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 */ > diff --git a/qga/commands-win32.c b/qga/commands-win32.c > index eb8d140..54bc546 100644 > --- a/qga/commands-win32.c > +++ b/qga/commands-win32.c > @@ -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