Skip to content

Commit

Permalink
qemu-ga: add guest-fstrim command
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Luiz Capitulino <[email protected]>
Reviewed-by: Michal Privoznik <[email protected]>
Signed-off-by: Michael Roth <[email protected]>
  • Loading branch information
bonzini authored and mdroth committed Jun 21, 2012
1 parent af02203 commit eab5fd5
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 3 deletions.
20 changes: 20 additions & 0 deletions qapi-schema-guest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down
78 changes: 75 additions & 3 deletions qga/commands-posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -378,6 +380,9 @@ static int build_fs_mount_list(FsMountList *mounts)

return 0;
}
#endif

#if defined(CONFIG_FSFREEZE)

/*
* Return status of freeze/thaw
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down
11 changes: 11 additions & 0 deletions qga/commands-win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit eab5fd5

Please sign in to comment.