qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Justin Ossevoort <justin@quarantainenet.nl>
To: qemu-devel@nongnu.org
Cc: mdroth@linux.vnet.ibm.com, Justin Ossevoort <justin@quarantainenet.nl>
Subject: [Qemu-devel] [PATCH v2] qga/commands-posix: Fix bug in guest-fstrim
Date: Wed,  1 Apr 2015 14:35:51 +0200	[thread overview]
Message-ID: <1427891751-16881-1-git-send-email-justin@quarantainenet.nl> (raw)

The FITRIM ioctl updates the fstrim_range structure it receives. This
way the caller can determine how many bytes were trimmed. The
guest-fstrim logic reuses the same fstrim_range for each filesystem,
effectively limiting each filesystem to trim at most as much as the
previous was able to trim.

If a previous filesystem would have trimmed 0 bytes, than the next
filesystem would report an error 'Invalid argument' because a FITRIM
request with length 0 is not valid.

This change resets the fstrim_range structure for each filesystem. It
returns a list of all trimmed filesystems with their status. If the
trime operation succeeded it returns the amount of bytes trimmed and
the effective minimum chunk size. On error it returns the value of
the errno for that filesystem operation (instead of aborting the
request).

Signed-off-by: Justin Ossevoort <justin@quarantainenet.nl>
---
 qga/commands-posix.c | 106 +++++++++++++++++++++++++++++++++++++++++++--------
 qga/qapi-schema.json |  49 ++++++++++++++++++++++--
 2 files changed, 136 insertions(+), 19 deletions(-)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index ba8de62..01ac801 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1322,21 +1322,52 @@ static void guest_fsfreeze_cleanup(void)
 #endif /* CONFIG_FSFREEZE */
 
 #if defined(CONFIG_FSTRIM)
+/* Add a fstrim result for a filesystem specified by @mount to @response */
+static GuestFilesystemTrimResult *
+qmp_guest_fstrim_add_result(GuestFilesystemTrimResponse *response,
+                            struct FsMount *mount, Error **errp)
+{
+    char *devpath, *syspath;
+    GuestFilesystemTrimResult *result = NULL;
+    GuestFilesystemTrimResultList *list;
+
+    devpath = g_strdup_printf("/sys/dev/block/%u:%u", mount->devmajor,
+                              mount->devminor);
+    syspath = realpath(devpath, NULL);
+    if (!syspath) {
+        error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
+        goto out;
+    }
+
+    result = g_malloc0(sizeof(*result));
+    result->name = g_strdup(basename(syspath));
+    result->type = g_strdup(mount->devtype);
+    result->mountpoint = g_strdup(mount->dirname);
+
+    list = g_malloc0(sizeof(*list));
+    list->value = result;
+    list->next = response->trimmed;
+    response->trimmed = list;
+
+out:
+    free(devpath);
+    return result;
+}
+
 /*
  * Walk list of mounted file systems in the guest, and trim them.
  */
-void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
+GuestFilesystemTrimResponse *
+qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
 {
-    int ret = 0;
+    GuestFilesystemTrimResponse *response = NULL;
+    GuestFilesystemTrimResult *result;
+    int ret = 0, error;
     FsMountList mounts;
     struct FsMount *mount;
     int fd;
     Error *local_err = NULL;
-    struct fstrim_range r = {
-        .start = 0,
-        .len = -1,
-        .minlen = has_minimum ? minimum : 0,
-    };
+    struct fstrim_range r;
 
     slog("guest-fstrim called");
 
@@ -1344,14 +1375,24 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
     build_fs_mount_list(&mounts, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
-        return;
+        return NULL;
     }
 
+    response = g_malloc0(sizeof(*response));
+
     QTAILQ_FOREACH(mount, &mounts, next) {
         fd = qemu_open(mount->dirname, O_RDONLY);
         if (fd == -1) {
-            error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
-            goto error;
+            error = errno;
+            result = qmp_guest_fstrim_add_result(response, mount, &local_err);
+            if (local_err) {
+                goto abort;
+            }
+            result->result =
+                GUEST_FILESYSTEM_TRIM_RESULT_TYPE_OPERATION_FAILED;
+            result->has_error_code = true;
+            result->error_code = error;
+            continue;
         }
 
         /* We try to cull filesytems we know won't work in advance, but other
@@ -1360,20 +1401,51 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
          * error means an unexpected error, so return it in those cases.  In
          * some other cases ENOTTY will be reported (e.g. CD-ROMs).
          */
+        r.start = 0;
+        r.len = -1;
+        r.minlen = has_minimum ? minimum : 0;
         ret = ioctl(fd, FITRIM, &r);
         if (ret == -1) {
             if (errno != ENOTTY && errno != EOPNOTSUPP) {
-                error_setg_errno(errp, errno, "failed to trim %s",
-                                 mount->dirname);
-                close(fd);
-                goto error;
+                error = errno;
+                result = qmp_guest_fstrim_add_result(response, mount,
+                                                     &local_err);
+                if (local_err) {
+                    goto abort;
+                }
+                result->result =
+                    GUEST_FILESYSTEM_TRIM_RESULT_TYPE_OPERATION_FAILED;
+                result->has_error_code = true;
+                result->error_code = error;
             }
+            close(fd);
+            continue;
         }
+
+        result = qmp_guest_fstrim_add_result(response, mount, &local_err);
+        if (local_err) {
+            goto abort;
+        }
+        result->result = GUEST_FILESYSTEM_TRIM_RESULT_TYPE_SUCCESS;
+        result->has_minimum = true;
+        result->minimum = r.minlen;
+        result->has_trimmed = true;
+        result->trimmed = r.len;
         close(fd);
     }
+    goto out;
 
-error:
+abort:
+    error_propagate(errp, local_err);
+    if (fd > 0) {
+        close(fd);
+    }
+    qapi_free_GuestFilesystemTrimResponse(response);
+    response = NULL;
+
+out:
     free_fs_mount_list(&mounts);
+    return response;
 }
 #endif /* CONFIG_FSTRIM */
 
@@ -2402,9 +2474,11 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
 #endif /* CONFIG_FSFREEZE */
 
 #if !defined(CONFIG_FSTRIM)
-void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
+GuestFilesystemTrimResponse *
+qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
 {
     error_set(errp, QERR_UNSUPPORTED);
+    return NULL;
 }
 #endif
 
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 95f49e3..cc96742 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -425,6 +425,48 @@
   'returns': 'int' }
 
 ##
+# @GuestFilesystemTrimResultType
+#
+# An enumeration of filesystem trim operation result.
+#
+# @success: the operation of was succesful
+# @operation-failed: the filesystem reported a failure
+#
+# Since: 2.4
+##
+{ 'enum': 'GuestFilesystemTrimResultType',
+  'data': ['success', 'operation-failed'] }
+
+##
+# @GuestFilesystemTrimResult
+#
+# @name: disk name
+# @mountpoint: mount point path
+# @type: file system type string
+# @result: the result of the fstrim operation for this filesystem
+# @trimmed: bytes trimmed when result is success
+# @minimum: reported minimum considered for trimming when
+#           result is success
+# @error-code: the value of errno when response is not success
+#
+# Since: 2.4
+##
+{ 'type': 'GuestFilesystemTrimResult',
+  'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
+           'result': 'GuestFilesystemTrimResultType',
+           '*trimmed': 'int', '*minimum': 'int', '*error-code': 'int'} }
+
+##
+# @GuestFilesystemTrimResponse
+#
+# @trimmed: list of filesystem trim results
+#
+# Since: 2.4
+##
+{ 'type': 'GuestFilesystemTrimResponse',
+  'data': {'trimmed': ['GuestFilesystemTrimResult']} }
+
+##
 # @guest-fstrim:
 #
 # Discard (or "trim") blocks which are not in use by the filesystem.
@@ -437,12 +479,13 @@
 #       fragmented free space, although not all blocks will be discarded.
 #       The default value is zero, meaning "discard every free block".
 #
-# Returns: Nothing.
+# Returns: Number of bytes trimmed by this call.
 #
-# Since: 1.2
+# Since: 2.4
 ##
 { 'command': 'guest-fstrim',
-  'data': { '*minimum': 'int' } }
+  'data': { '*minimum': 'int' },
+  'returns': 'GuestFilesystemTrimResponse' }
 
 ##
 # @guest-suspend-disk
-- 
2.1.0

             reply	other threads:[~2015-04-01 12:36 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-01 12:35 Justin Ossevoort [this message]
2015-04-29 10:42 ` [Qemu-devel] [PATCH v2] qga/commands-posix: Fix bug in guest-fstrim Thomas Huth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1427891751-16881-1-git-send-email-justin@quarantainenet.nl \
    --to=justin@quarantainenet.nl \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).