These patches contain the beginnings of discard (a.k.a. trim or unmap) support. This will allow us to change virt-sparsify to work on disk images in-place (instead of using slow & inefficient copying). The approach used is to add an optional 'discard' parameter to add-drive. It has 3 possible settings: - 'disable' : the default, no discard is done - 'besteffort' : try to enable it, but don't fail if it's not supported - 'enable' : enable it, and fail if it is not supported I'm not sure if we should wait for post-1.26 before adding discard support. It depends if the interface above is suitable or if we'll have to change it later. However I discussed how to support this at DevConf and this approach was generally agreed. Rich.
Richard W.M. Jones
2014-Mar-10  17:28 UTC
[Libguestfs] [PATCH 1/3] launch: libvirt: Move the is_kvm flag (derived from libvirt capabilities) to backend data struct.
This is just rearranging the data between structs.  There should be no
functional change.
---
 src/launch-libvirt.c | 41 +++++++++++++++++++++++------------------
 1 file changed, 23 insertions(+), 18 deletions(-)
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
index fd6e6d2..d86e2d2 100644
--- a/src/launch-libvirt.c
+++ b/src/launch-libvirt.c
@@ -106,6 +106,7 @@ struct backend_libvirt_data {
   char *selinux_imagelabel;
   bool selinux_norelabel_disks;
   char name[DOMAIN_NAME_LEN];   /* random name */
+  bool is_kvm;                  /* false = qemu, true = kvm (from
capabilities)*/
 };
 
 /* Parameters passed to construct_libvirt_xml and subfunctions.  We
@@ -122,12 +123,11 @@ struct libvirt_xml_params {
   char guestfsd_path[UNIX_PATH_MAX]; /* paths to sockets */
   char console_path[UNIX_PATH_MAX];
   bool enable_svirt;            /* false if we decided to disable sVirt */
-  bool is_kvm;                  /* false = qemu, true = kvm */
   bool current_proc_is_root;    /* true = euid is root */
 };
 
-static int parse_capabilities (guestfs_h *g, const char *capabilities_xml,
struct libvirt_xml_params *params);
-static xmlChar *construct_libvirt_xml (guestfs_h *g, const struct
libvirt_xml_params *params);
+static int parse_capabilities (guestfs_h *g, const char *capabilities_xml,
struct backend_libvirt_data *data);
+static xmlChar *construct_libvirt_xml (guestfs_h *g, const struct
backend_libvirt_data *data, const struct libvirt_xml_params *params);
 static void debug_appliance_permissions (guestfs_h *g);
 static void debug_socket_permissions (guestfs_h *g);
 static void libvirt_error (guestfs_h *g, const char *fs, ...)
__attribute__((format (printf,2,3)));
@@ -299,7 +299,7 @@ launch_libvirt (guestfs_h *g, void *datav, const char
*libvirt_uri)
   if (g->verbose)
     guestfs___print_timestamped_message (g, "parsing capabilities
XML");
 
-  if (parse_capabilities (g, capabilities_xml, ¶ms) == -1)
+  if (parse_capabilities (g, capabilities_xml, data) == -1)
     goto cleanup;
 
   /* Locate and/or build the appliance. */
@@ -434,7 +434,7 @@ launch_libvirt (guestfs_h *g, void *datav, const char
*libvirt_uri)
   guestfs___drive_name (params.appliance_index, ¶ms.appliance_dev[7]);
   params.enable_svirt = ! is_custom_hv (g);
 
-  xml = construct_libvirt_xml (g, ¶ms);
+  xml = construct_libvirt_xml (g, data, ¶ms);
   if (!xml)
     goto cleanup;
 
@@ -575,7 +575,7 @@ launch_libvirt (guestfs_h *g, void *datav, const char
*libvirt_uri)
 
 static int
 parse_capabilities (guestfs_h *g, const char *capabilities_xml,
-                    struct libvirt_xml_params *params)
+                    struct backend_libvirt_data *data)
 {
   CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL;
   CLEANUP_XMLXPATHFREECONTEXT xmlXPathContextPtr xpathCtx = NULL;
@@ -654,9 +654,9 @@ parse_capabilities (guestfs_h *g, const char
*capabilities_xml,
   force_tcg = guestfs___get_backend_setting_bool (g, "force_tcg");
 
   if (!force_tcg)
-    params->is_kvm = seen_kvm;
+    data->is_kvm = seen_kvm;
   else
-    params->is_kvm = 0;
+    data->is_kvm = 0;
 
   return 0;
 }
@@ -779,10 +779,10 @@ debug_socket_permissions (guestfs_h *g)
   }
 }
 
-static int construct_libvirt_xml_domain (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
+static int construct_libvirt_xml_domain (guestfs_h *g, const struct
backend_libvirt_data *data, const struct libvirt_xml_params *params,
xmlTextWriterPtr xo);
 static int construct_libvirt_xml_name (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
-static int construct_libvirt_xml_cpu (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
-static int construct_libvirt_xml_boot (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
+static int construct_libvirt_xml_cpu (guestfs_h *g, const struct
backend_libvirt_data *data, const struct libvirt_xml_params *params,
xmlTextWriterPtr xo);
+static int construct_libvirt_xml_boot (guestfs_h *g, const struct
backend_libvirt_data *data, const struct libvirt_xml_params *params,
xmlTextWriterPtr xo);
 static int construct_libvirt_xml_seclabel (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
 static int construct_libvirt_xml_lifecycle (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
 static int construct_libvirt_xml_devices (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
@@ -872,7 +872,9 @@ static int construct_libvirt_xml_appliance (guestfs_h *g,
const struct libvirt_x
              __FILE__, __LINE__, (fn));
 
 static xmlChar *
-construct_libvirt_xml (guestfs_h *g, const struct libvirt_xml_params *params)
+construct_libvirt_xml (guestfs_h *g,
+                       const struct backend_libvirt_data *data,
+                       const struct libvirt_xml_params *params)
 {
   xmlChar *ret = NULL;
   CLEANUP_XMLBUFFERFREE xmlBufferPtr xb = NULL;
@@ -905,7 +907,7 @@ construct_libvirt_xml (guestfs_h *g, const struct
libvirt_xml_params *params)
     return NULL;
   }
 
-  if (construct_libvirt_xml_domain (g, params, xo) == -1)
+  if (construct_libvirt_xml_domain (g, data, params, xo) == -1)
     return NULL;
 
   if (xmlTextWriterEndDocument (xo) == -1) {
@@ -925,19 +927,20 @@ construct_libvirt_xml (guestfs_h *g, const struct
libvirt_xml_params *params)
 
 static int
 construct_libvirt_xml_domain (guestfs_h *g,
+                              const struct backend_libvirt_data *data,
                               const struct libvirt_xml_params *params,
                               xmlTextWriterPtr xo)
 {
   start_element ("domain") {
-    attribute ("type", params->is_kvm ? "kvm" :
"qemu");
+    attribute ("type", data->is_kvm ? "kvm" :
"qemu");
     attribute_ns ("xmlns", "qemu", NULL,
                   "http://libvirt.org/schemas/domain/qemu/1.0");
 
     if (construct_libvirt_xml_name (g, params, xo) == -1)
       return -1;
-    if (construct_libvirt_xml_cpu (g, params, xo) == -1)
+    if (construct_libvirt_xml_cpu (g, data, params, xo) == -1)
       return -1;
-    if (construct_libvirt_xml_boot (g, params, xo) == -1)
+    if (construct_libvirt_xml_boot (g, data, params, xo) == -1)
       return -1;
     if (construct_libvirt_xml_seclabel (g, params, xo) == -1)
       return -1;
@@ -968,6 +971,7 @@ construct_libvirt_xml_name (guestfs_h *g,
 /* CPU and memory features. */
 static int
 construct_libvirt_xml_cpu (guestfs_h *g,
+                           const struct backend_libvirt_data *data,
                            const struct libvirt_xml_params *params,
                            xmlTextWriterPtr xo)
 {
@@ -987,7 +991,7 @@ construct_libvirt_xml_cpu (guestfs_h *g,
    * Only do this with KVM.  It is broken in subtle ways on TCG, and
    * fairly pointless anyway.
    */
-  if (params->is_kvm) {
+  if (data->is_kvm) {
     start_element ("cpu") {
       attribute ("mode", "host-passthrough");
       start_element ("model") {
@@ -1031,6 +1035,7 @@ construct_libvirt_xml_cpu (guestfs_h *g,
 /* Boot parameters. */
 static int
 construct_libvirt_xml_boot (guestfs_h *g,
+                            const struct backend_libvirt_data *data,
                             const struct libvirt_xml_params *params,
                             xmlTextWriterPtr xo)
 {
@@ -1039,7 +1044,7 @@ construct_libvirt_xml_boot (guestfs_h *g,
 
   /* Linux kernel command line. */
   flags = 0;
-  if (!params->is_kvm)
+  if (!data->is_kvm)
     flags |= APPLIANCE_COMMAND_LINE_IS_TCG;
   cmdline = guestfs___appliance_command_line (g, params->appliance_dev,
flags);
 
-- 
1.8.5.3
Richard W.M. Jones
2014-Mar-10  17:28 UTC
[Libguestfs] [PATCH 2/3] New API parameter: Add discard parameter to guestfs_add_drive_opts.
This adds a discard parameter to the guestfs_add_drive_opts which
approximately maps to the discard=ignore|unmap parameter supported by
qemu.
If discard is set to "enable" then we force discard=unmap (and try to
fail if it is not possible).  If discard is set to the more useful
"besteffort" option, then we enable discard if possible.  The default
is "disable".
---
 generator/actions.ml   |  32 +++++++++++++-
 src/drives.c           | 107 +++++++++++++++++++++++++++++++----------------
 src/guestfs-internal.h |   9 ++++
 src/launch-direct.c    | 110 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/launch-libvirt.c   |  96 ++++++++++++++++++++++++++++++++++++++----
 src/launch-uml.c       |   6 +++
 6 files changed, 316 insertions(+), 44 deletions(-)
diff --git a/generator/actions.ml b/generator/actions.ml
index a6ef194..b25194c 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -1286,7 +1286,7 @@ not all belong to a single logical operating system
 
   { defaults with
     name = "add_drive";
-    style = RErr, [String "filename"], [OBool "readonly";
OString "format"; OString "iface"; OString "name";
OString "label"; OString "protocol"; OStringList
"server"; OString "username"; OString "secret";
OString "cachemode"];
+    style = RErr, [String "filename"], [OBool "readonly";
OString "format"; OString "iface"; OString "name";
OString "label"; OString "protocol"; OStringList
"server"; OString "username"; OString "secret";
OString "cachemode"; OString "discard"];
     once_had_no_optargs = true;
     blocking = false;
     fish_alias = ["add"];
@@ -1504,6 +1504,36 @@ for scratch or temporary disks.
 
 =back
 
+=item C<discard>
+
+Enable or disable discard (a.k.a. trim or unmap) support on this
+drive.  If enabled, operations such as C<guestfs_fstrim> will be able
+to discard / make thin / punch holes in the underlying host file
+or device.
+
+Possible discard settings are:
+
+=over 4
+
+=item C<discard = \"disable\">
+
+Disable discard support.  This is the default.
+
+=item C<discard = \"enable\">
+
+Enable discard support.  Fail if discard is not possible.
+
+=item C<discard = \"besteffort\">
+
+Enable discard support if possible, but don't fail if it is not
+supported.
+
+Since not all backends and not all underlying systems support
+discard, this is a good choice if you want to use discard if
+possible, but don't mind if it doesn't work.
+
+=back
+
 =back" };
 
   { defaults with
diff --git a/src/drives.c b/src/drives.c
index 2c85b52..68e37f7 100644
--- a/src/drives.c
+++ b/src/drives.c
@@ -115,7 +115,8 @@ static struct drive *
 create_drive_file (guestfs_h *g, const char *path,
                    bool readonly, const char *format,
                    const char *iface, const char *name,
-                   const char *disk_label, const char *cachemode)
+                   const char *disk_label, const char *cachemode,
+                   enum discard discard)
 {
   struct drive *drv = safe_calloc (g, 1, sizeof *drv);
 
@@ -128,6 +129,7 @@ create_drive_file (guestfs_h *g, const char *path,
   drv->name = name ? safe_strdup (g, name) : NULL;
   drv->disk_label = disk_label ? safe_strdup (g, disk_label) : NULL;
   drv->cachemode = cachemode ? safe_strdup (g, cachemode) : NULL;
+  drv->discard = discard;
 
   if (readonly) {
     if (create_overlay (g, drv) == -1) {
@@ -151,7 +153,8 @@ create_drive_non_file (guestfs_h *g,
                        const char *username, const char *secret,
                        bool readonly, const char *format,
                        const char *iface, const char *name,
-                       const char *disk_label, const char *cachemode)
+                       const char *disk_label, const char *cachemode,
+                       enum discard discard)
 {
   struct drive *drv = safe_calloc (g, 1, sizeof *drv);
 
@@ -168,6 +171,7 @@ create_drive_non_file (guestfs_h *g,
   drv->name = name ? safe_strdup (g, name) : NULL;
   drv->disk_label = disk_label ? safe_strdup (g, disk_label) : NULL;
   drv->cachemode = cachemode ? safe_strdup (g, cachemode) : NULL;
+  drv->discard = discard;
 
   if (readonly) {
     if (create_overlay (g, drv) == -1) {
@@ -191,7 +195,8 @@ create_drive_curl (guestfs_h *g,
                    const char *username, const char *secret,
                    bool readonly, const char *format,
                    const char *iface, const char *name,
-                   const char *disk_label, const char *cachemode)
+                   const char *disk_label, const char *cachemode,
+                   enum discard discard)
 {
   if (secret != NULL) {
     error (g, _("curl: you cannot specify a secret with this
protocol"));
@@ -223,7 +228,7 @@ create_drive_curl (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 static struct drive *
@@ -233,7 +238,8 @@ create_drive_gluster (guestfs_h *g,
                       const char *username, const char *secret,
                       bool readonly, const char *format,
                       const char *iface, const char *name,
-                      const char *disk_label, const char *cachemode)
+                      const char *disk_label, const char *cachemode,
+                      enum discard discard)
 {
   if (username != NULL) {
     error (g, _("gluster: you cannot specify a username with this
protocol"));
@@ -263,7 +269,7 @@ create_drive_gluster (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 static int
@@ -285,7 +291,8 @@ create_drive_nbd (guestfs_h *g,
                   const char *username, const char *secret,
                   bool readonly, const char *format,
                   const char *iface, const char *name,
-                  const char *disk_label, const char *cachemode)
+                  const char *disk_label, const char *cachemode,
+                  enum discard discard)
 {
   if (username != NULL) {
     error (g, _("nbd: you cannot specify a username with this
protocol"));
@@ -308,7 +315,7 @@ create_drive_nbd (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 static struct drive *
@@ -318,7 +325,8 @@ create_drive_rbd (guestfs_h *g,
                   const char *username, const char *secret,
                   bool readonly, const char *format,
                   const char *iface, const char *name,
-                  const char *disk_label, const char *cachemode)
+                  const char *disk_label, const char *cachemode,
+                  enum discard discard)
 {
   size_t i;
 
@@ -348,7 +356,7 @@ create_drive_rbd (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 static struct drive *
@@ -358,7 +366,8 @@ create_drive_sheepdog (guestfs_h *g,
                        const char *username, const char *secret,
                        bool readonly, const char *format,
                        const char *iface, const char *name,
-                       const char *disk_label, const char *cachemode)
+                       const char *disk_label, const char *cachemode,
+                       enum discard discard)
 {
   size_t i;
 
@@ -397,7 +406,7 @@ create_drive_sheepdog (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 static struct drive *
@@ -407,7 +416,8 @@ create_drive_ssh (guestfs_h *g,
                   const char *username, const char *secret,
                   bool readonly, const char *format,
                   const char *iface, const char *name,
-                  const char *disk_label, const char *cachemode)
+                  const char *disk_label, const char *cachemode,
+                  enum discard discard)
 {
   if (secret != NULL) {
     error (g, _("ssh: you cannot specify a secret with this
protocol"));
@@ -444,7 +454,7 @@ create_drive_ssh (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 static struct drive *
@@ -454,7 +464,8 @@ create_drive_iscsi (guestfs_h *g,
                     const char *username, const char *secret,
                     bool readonly, const char *format,
                     const char *iface, const char *name,
-                    const char *disk_label, const char *cachemode)
+                    const char *disk_label, const char *cachemode,
+                    enum discard discard)
 {
   if (username != NULL) {
     error (g, _("iscsi: you cannot specify a username with this
protocol"));
@@ -491,7 +502,7 @@ create_drive_iscsi (guestfs_h *g,
                                 servers, nr_servers, exportname,
                                 username, secret,
                                 readonly, format, iface, name, disk_label,
-                                cachemode);
+                                cachemode, discard);
 }
 
 /* Traditionally you have been able to use /dev/null as a filename, as
@@ -528,14 +539,15 @@ create_drive_dev_null (guestfs_h *g, bool readonly, const
char *format,
     return NULL;
 
   return create_drive_file (g, tmpfile, readonly, format, iface, name,
-                            disk_label, 0);
+                            disk_label, NULL, discard_disable);
 }
 
 static struct drive *
 create_drive_dummy (guestfs_h *g)
 {
   /* A special drive struct that is used as a dummy slot for the appliance. */
-  return create_drive_file (g, "", 0, NULL, NULL, NULL, NULL, 0);
+  return create_drive_file (g, "", 0, NULL, NULL, NULL, NULL, NULL,
+                            discard_disable);
 }
 
 static void
@@ -563,8 +575,8 @@ free_drive_struct (struct drive *drv)
   free (drv);
 }
 
-static const char *
-protocol_to_string (enum drive_protocol protocol)
+const char *
+guestfs___drive_protocol_to_string (enum drive_protocol protocol)
 {
   switch (protocol) {
   case drive_protocol_file: return "file";
@@ -590,12 +602,12 @@ static char *
 drive_to_string (guestfs_h *g, const struct drive *drv)
 {
   return safe_asprintf
-    (g, "%s%s%s%s protocol=%s%s%s%s%s%s%s%s%s",
+    (g, "%s%s%s%s protocol=%s%s%s%s%s%s%s%s%s%s",
      drv->src.u.path,
      drv->readonly ? " readonly" : "",
      drv->src.format ? " format=" : "",
      drv->src.format ? : "",
-     protocol_to_string (drv->src.protocol),
+     guestfs___drive_protocol_to_string (drv->src.protocol),
      drv->iface ? " iface=" : "",
      drv->iface ? : "",
      drv->name ? " name=" : "",
@@ -603,7 +615,9 @@ drive_to_string (guestfs_h *g, const struct drive *drv)
      drv->disk_label ? " label=" : "",
      drv->disk_label ? : "",
      drv->cachemode ? " cache=" : "",
-     drv->cachemode ? : "");
+     drv->cachemode ? : "",
+     drv->discard == discard_disable ? "" :
+     drv->discard == discard_enable ? " discard=enable" : "
discard=besteffort");
 }
 
 /* Add struct drive to the g->drives vector at the given index. */
@@ -824,6 +838,7 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
   const char *username;
   const char *secret;
   const char *cachemode;
+  enum discard discard;
   struct drive *drv;
   size_t i, drv_index;
 
@@ -852,6 +867,28 @@ guestfs__add_drive_opts (guestfs_h *g, const char
*filename,
   cachemode = optargs->bitmask &
GUESTFS_ADD_DRIVE_OPTS_CACHEMODE_BITMASK
     ? optargs->cachemode : NULL;
 
+  if (optargs->bitmask & GUESTFS_ADD_DRIVE_OPTS_DISCARD_BITMASK) {
+    if (STREQ (optargs->discard, "disable"))
+      discard = discard_disable;
+    else if (STREQ (optargs->discard, "enable"))
+      discard = discard_enable;
+    else if (STREQ (optargs->discard, "besteffort"))
+      discard = discard_besteffort;
+    else {
+      error (g, _("discard parameter must be 'disable',
'enable' or 'besteffort'"));
+      free_drive_servers (servers, nr_servers);
+      return -1;
+    }
+  }
+  else
+    discard = discard_disable;
+
+  if (readonly && discard == discard_enable) {
+    error (g, _("discard support cannot be enabled on read-only
drives"));
+    free_drive_servers (servers, nr_servers);
+    return -1;
+  }
+
   if (format && !valid_format_iface (format)) {
     error (g, _("%s parameter is empty or contains disallowed
characters"),
            "format");
@@ -904,7 +941,7 @@ guestfs__add_drive_opts (guestfs_h *g, const char *filename,
       }
 
       drv = create_drive_file (g, filename, readonly, format, iface, name,
-                               disk_label, cachemode);
+                               disk_label, cachemode, discard);
     }
   }
   else if (STREQ (protocol, "ftp")) {
@@ -912,71 +949,71 @@ guestfs__add_drive_opts (guestfs_h *g, const char
*filename,
                              servers, nr_servers, filename,
                              username, secret,
                              readonly, format, iface, name,
-                             disk_label, cachemode);
+                             disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "ftps")) {
     drv = create_drive_curl (g, drive_protocol_ftps,
                              servers, nr_servers, filename,
                              username, secret,
                              readonly, format, iface, name,
-                             disk_label, cachemode);
+                             disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "gluster")) {
     drv = create_drive_gluster (g, servers, nr_servers, filename,
                                 username, secret,
                                 readonly, format, iface, name,
-                                disk_label, cachemode);
+                                disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "http")) {
     drv = create_drive_curl (g, drive_protocol_http,
                              servers, nr_servers, filename,
                              username, secret,
                              readonly, format, iface, name,
-                             disk_label, cachemode);
+                             disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "https")) {
     drv = create_drive_curl (g, drive_protocol_https,
                              servers, nr_servers, filename,
                              username, secret,
                              readonly, format, iface, name,
-                             disk_label, cachemode);
+                             disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "iscsi")) {
     drv = create_drive_iscsi (g, servers, nr_servers, filename,
                               username, secret,
                               readonly, format, iface, name,
-                              disk_label, cachemode);
+                              disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "nbd")) {
     drv = create_drive_nbd (g, servers, nr_servers, filename,
                             username, secret,
                             readonly, format, iface, name,
-                            disk_label, cachemode);
+                            disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "rbd")) {
     drv = create_drive_rbd (g, servers, nr_servers, filename,
                             username, secret,
                             readonly, format, iface, name,
-                            disk_label, cachemode);
+                            disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "sheepdog")) {
     drv = create_drive_sheepdog (g, servers, nr_servers, filename,
                                  username, secret,
                                  readonly, format, iface, name,
-                                 disk_label, cachemode);
+                                 disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "ssh")) {
     drv = create_drive_ssh (g, servers, nr_servers, filename,
                             username, secret,
                             readonly, format, iface, name,
-                            disk_label, cachemode);
+                            disk_label, cachemode, discard);
   }
   else if (STREQ (protocol, "tftp")) {
     drv = create_drive_curl (g, drive_protocol_tftp,
                              servers, nr_servers, filename,
                              username, secret,
                              readonly, format, iface, name,
-                             disk_label, cachemode);
+                             disk_label, cachemode, discard);
   }
   else {
     error (g, _("unknown protocol '%s'"), protocol);
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index 545146f..df481a7 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -231,6 +231,12 @@ struct drive_source {
   char *secret;
 };
 
+enum discard {
+  discard_disable = 0,
+  discard_enable = 1,
+  discard_besteffort = 2,
+};
+
 /* There is one 'struct drive' per drive, including hot-plugged drives.
*/
 struct drive {
   /* Original source of the drive, eg. file:..., http:... */
@@ -252,6 +258,7 @@ struct drive {
   char *name;
   char *disk_label;
   char *cachemode;
+  enum discard discard;
 };
 
 /* Extra hv parameters (from guestfs_config). */
@@ -713,6 +720,7 @@ extern size_t guestfs___checkpoint_drives (guestfs_h *g);
 extern void guestfs___rollback_drives (guestfs_h *g, size_t);
 extern void guestfs___add_dummy_appliance_drive (guestfs_h *g);
 extern void guestfs___free_drives (guestfs_h *g);
+extern const char *guestfs___drive_protocol_to_string (enum drive_protocol
protocol);
 
 /* appliance.c */
 extern int guestfs___build_appliance (guestfs_h *g, char **kernel, char **dtb,
char **initrd, char **appliance);
@@ -832,6 +840,7 @@ extern void guestfs___cleanup_cmd_close (struct command **);
 
 /* launch-direct.c */
 extern char *guestfs___drive_source_qemu_param (guestfs_h *g, const struct
drive_source *src);
+extern bool guestfs___discard_possible (guestfs_h *g, struct drive *drv, bool
qemu15, bool qemu16);
 
 /* utils.c */
 extern int guestfs___validate_guid (const char *);
diff --git a/src/launch-direct.c b/src/launch-direct.c
index 1e84061..584c4de 100644
--- a/src/launch-direct.c
+++ b/src/launch-direct.c
@@ -509,6 +509,29 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
     CLEANUP_FREE char *file = NULL, *escaped_file = NULL, *param = NULL;
 
     if (!drv->overlay) {
+      const char *discard_mode = "";
+      int major = data->qemu_version_major, minor =
data->qemu_version_minor;
+
+      switch (drv->discard) {
+      case discard_disable:
+        /* Since the default is always discard=ignore, don't specify it
+         * on the command line.  This also avoids unnecessary breakage
+         * with qemu < 1.5 which didn't have the option at all.
+         */
+        break;
+      case discard_enable:
+        if (!guestfs___discard_possible (g, drv, major, minor))
+          goto cleanup0;
+        /*FALLTHROUGH*/
+      case discard_besteffort:
+        /* I believe from reading the code that this is always safe as
+         * long as qemu >= 1.5.
+         */
+        if (major > 1 || (major == 1 && minor >= 5))
+          discard_mode = ",discard=unmap";
+        break;
+      }
+
       /* Make the file= parameter. */
       file = guestfs___drive_source_qemu_param (g, &drv->src);
       escaped_file = qemu_escape_param (g, file);
@@ -517,10 +540,11 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
        * the if=... at the end.
        */
       param = safe_asprintf
-        (g, "file=%s%s,cache=%s%s%s%s%s,id=hd%zu",
+        (g, "file=%s%s,cache=%s%s%s%s%s%s,id=hd%zu",
          escaped_file,
          drv->readonly ? ",snapshot=on" : "",
          drv->cachemode ? drv->cachemode : "writeback",
+         discard_mode,
          drv->src.format ? ",format=" : "",
          drv->src.format ? drv->src.format : "",
          drv->disk_label ? ",serial=" : "",
@@ -1370,6 +1394,90 @@ guestfs___drive_source_qemu_param (guestfs_h *g, const
struct drive_source *src)
   abort ();
 }
 
+/* Test if discard is both supported by qemu AND possible with the
+ * underlying file or device.  This returns 1 if discard is possible.
+ * It returns 0 if not possible and sets the error to the reason why.
+ *
+ * This function is called when the user set discard == "enable".
+ *
+ * qemu15: qemu >= 1.5.  This was the first version that supported
+ * the discard option on -drive at all.
+ *
+ * qemu16: qemu >= 1.6.  This was the first version that supported
+ * unmap on qcow2 backing files.
+ */
+bool
+guestfs___discard_possible (guestfs_h *g, struct drive *drv,
+                            bool qemu15, bool qemu16)
+{
+  if (!qemu15) {
+    error (g, _("discard cannot be enabled on this drive: "
+                "qemu < 1.5"));
+    return false;
+  }
+
+  /* If it's an overlay, discard is not possible (on the underlying
+   * file).  This has probably been caught earlier since we already
+   * checked that the drive is !readonly.  Nevertheless ...
+   */
+  if (drv->overlay) {
+    error (g, _("discard cannot be enabled on this drive: "
+                "the drive has a read-only overlay"));
+    return false;
+  }
+
+  /* Look at the source format. */
+  if (drv->src.format == NULL) {
+    /* We could autodetect the format, but we don't ... yet. XXX */
+    error (g, _("discard cannot be enabled on this drive: "
+                "you have to specify the format of the file"));
+    return false;
+  }
+  else if (STREQ (drv->src.format, "raw"))
+    /* OK */ ;
+  else if (STREQ (drv->src.format, "qcow2")) {
+    if (!qemu16) {
+      error (g, _("discard cannot be enabled on this drive: "
+                  "qemu < 1.6 cannot do discard on qcow2 files"));
+    return false;
+    }
+  }
+  else {
+    /* It's possible in future other formats will support discard, but
+     * currently (qemu 1.7) none of them do.
+     */
+    error (g, _("discard cannot be enabled on this drive: "
+                "qemu does not support discard for '%s' format
files"),
+           drv->src.format);
+    return false;
+  }
+
+  switch (drv->src.protocol) {
+    /* Protocols which support discard. */
+  case drive_protocol_file:
+  case drive_protocol_gluster:
+  case drive_protocol_iscsi:
+  case drive_protocol_nbd:
+  case drive_protocol_rbd:
+  case drive_protocol_sheepdog: /* XXX depends on server version */
+    break;
+
+    /* Protocols which don't support discard. */
+  case drive_protocol_ftp:
+  case drive_protocol_ftps:
+  case drive_protocol_http:
+  case drive_protocol_https:
+  case drive_protocol_ssh:
+  case drive_protocol_tftp:
+    error (g, _("discard cannot be enabled on this drive: "
+                "protocol '%s' does not support discard"),
+           guestfs___drive_protocol_to_string (drv->src.protocol));
+    return false;
+  }
+
+  return true;
+}
+
 static int
 shutdown_direct (guestfs_h *g, void *datav, int check_for_errors)
 {
diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c
index d86e2d2..1c46b57 100644
--- a/src/launch-libvirt.c
+++ b/src/launch-libvirt.c
@@ -107,6 +107,7 @@ struct backend_libvirt_data {
   bool selinux_norelabel_disks;
   char name[DOMAIN_NAME_LEN];   /* random name */
   bool is_kvm;                  /* false = qemu, true = kvm (from
capabilities)*/
+  unsigned long qemu_version;   /* qemu version (from libvirt) */
 };
 
 /* Parameters passed to construct_libvirt_xml and subfunctions.  We
@@ -131,6 +132,7 @@ static xmlChar *construct_libvirt_xml (guestfs_h *g, const
struct backend_libvir
 static void debug_appliance_permissions (guestfs_h *g);
 static void debug_socket_permissions (guestfs_h *g);
 static void libvirt_error (guestfs_h *g, const char *fs, ...)
__attribute__((format (printf,2,3)));
+static void libvirt_debug (guestfs_h *g, const char *fs, ...)
__attribute__((format (printf,2,3)));
 static int is_custom_hv (guestfs_h *g);
 static int is_blk (const char *path);
 static void ignore_errors (void *ignore, virErrorPtr ignore2);
@@ -283,6 +285,19 @@ launch_libvirt (guestfs_h *g, void *datav, const char
*libvirt_uri)
    */
   virConnSetErrorFunc (conn, NULL, ignore_errors);
 
+  /* Get hypervisor (hopefully qemu) version. */
+  if (virConnectGetVersion (conn, &data->qemu_version) == 0) {
+    debug (g, "qemu version (reported by libvirt) = %lu
(%lu.%lu.%lu)",
+           data->qemu_version,
+           data->qemu_version / 1000000UL,
+           data->qemu_version / 1000UL % 1000UL,
+           data->qemu_version % 1000UL);
+  }
+  else {
+    libvirt_debug (g, "unable to read qemu version from libvirt");
+    data->qemu_version = 0;
+  }
+
   if (g->verbose)
     guestfs___print_timestamped_message (g, "get libvirt
capabilities");
 
@@ -789,7 +804,7 @@ static int construct_libvirt_xml_devices (guestfs_h *g,
const struct libvirt_xml
 static int construct_libvirt_xml_qemu_cmdline (guestfs_h *g, const struct
libvirt_xml_params *params, xmlTextWriterPtr xo);
 static int construct_libvirt_xml_disk (guestfs_h *g, const struct
backend_libvirt_data *data, xmlTextWriterPtr xo, struct drive *drv, size_t
drv_index);
 static int construct_libvirt_xml_disk_target (guestfs_h *g, xmlTextWriterPtr
xo, size_t drv_index);
-static int construct_libvirt_xml_disk_driver_qemu (guestfs_h *g,
xmlTextWriterPtr xo, const char *format, const char *cachemode);
+static int construct_libvirt_xml_disk_driver_qemu (guestfs_h *g, const struct
backend_libvirt_data *data, struct drive *drv, xmlTextWriterPtr xo, const char
*format, const char *cachemode, enum discard discard);
 static int construct_libvirt_xml_disk_address (guestfs_h *g, xmlTextWriterPtr
xo, size_t drv_index);
 static int construct_libvirt_xml_disk_source_hosts (guestfs_h *g,
xmlTextWriterPtr xo, const struct drive_source *src);
 static int construct_libvirt_xml_disk_source_seclabel (guestfs_h *g, const
struct backend_libvirt_data *data, xmlTextWriterPtr xo);
@@ -1238,7 +1253,9 @@ construct_libvirt_xml_disk (guestfs_h *g,
       if (construct_libvirt_xml_disk_target (g, xo, drv_index) == -1)
         return -1;
 
-      if (construct_libvirt_xml_disk_driver_qemu (g, xo, "qcow2",
"unsafe")
+      if (construct_libvirt_xml_disk_driver_qemu (g, NULL, NULL,
+                                                  xo, "qcow2",
"unsafe",
+                                                  discard_disable)
           == -1)
         return -1;
     }
@@ -1375,8 +1392,9 @@ construct_libvirt_xml_disk (guestfs_h *g,
         return -1;
       }
 
-      if (construct_libvirt_xml_disk_driver_qemu (g, xo, format,
-                                                  drv->cachemode ? :
"writeback")
+      if (construct_libvirt_xml_disk_driver_qemu (g, data, drv, xo, format,
+                                                  drv->cachemode ? :
"writeback",
+                                                  drv->discard)
           == -1)
         return -1;
     }
@@ -1412,14 +1430,43 @@ construct_libvirt_xml_disk_target (guestfs_h *g,
xmlTextWriterPtr xo,
 }
 
 static int
-construct_libvirt_xml_disk_driver_qemu (guestfs_h *g, xmlTextWriterPtr xo,
+construct_libvirt_xml_disk_driver_qemu (guestfs_h *g,
+                                        const struct backend_libvirt_data
*data,
+                                        struct drive *drv,
+                                        xmlTextWriterPtr xo,
                                         const char *format,
-                                        const char *cachemode)
+                                        const char *cachemode,
+                                        enum discard discard)
 {
+  bool discard_unmap = false;
+
+  switch (discard) {
+  case discard_disable:
+    /* Since the default is always discard=ignore, don't specify it
+     * in the XML.
+     */
+    break;
+  case discard_enable:
+    if (!guestfs___discard_possible (g, drv,
+                                     data->qemu_version >= 1005000,
+                                     data->qemu_version >= 1006000))
+      return -1;
+    /*FALLTHROUGH*/
+  case discard_besteffort:
+    /* I believe from reading the code that this is always safe as
+     * long as qemu >= 1.5.
+     */
+    if (data->qemu_version >= 1005000)
+      discard_unmap = true;
+    break;
+  }
+
   start_element ("driver") {
     attribute ("name", "qemu");
     attribute ("type", format);
     attribute ("cache", cachemode);
+    if (discard_unmap)
+      attribute ("discard", "unmap");
   } end_element ();
 
   return 0;
@@ -1504,7 +1551,9 @@ construct_libvirt_xml_appliance (guestfs_h *g,
       attribute ("bus", "scsi");
     } end_element ();
 
-    if (construct_libvirt_xml_disk_driver_qemu (g, xo, "qcow2",
"unsafe") == -1)
+    if (construct_libvirt_xml_disk_driver_qemu (g, NULL, NULL, xo,
+                                                "qcow2",
"unsafe",
+                                                discard_disable) == -1)
       return -1;
 
     if (construct_libvirt_xml_disk_address (g, xo, params->appliance_index)
@@ -1664,6 +1713,39 @@ libvirt_error (guestfs_h *g, const char *fs, ...)
   free (msg);
 }
 
+/* Same as 'libvirt_error' but calls debug instead. */
+static void
+libvirt_debug (guestfs_h *g, const char *fs, ...)
+{
+  va_list args;
+  char *msg;
+  int len;
+  virErrorPtr err;
+
+  if (!g->verbose)
+    return;
+
+  va_start (args, fs);
+  len = vasprintf (&msg, fs, args);
+  va_end (args);
+
+  if (len < 0)
+    msg = safe_asprintf (g,
+                         _("%s: internal error forming error
message"),
+                         __func__);
+
+  /* In all recent libvirt, this retrieves the thread-local error. */
+  err = virGetLastError ();
+  if (err)
+    debug (g, "%s: %s [code=%d domain=%d]",
+           msg, err->message, err->code, err->domain);
+  else
+    debug (g, "%s", msg);
+
+  /* NB. 'err' must not be freed! */
+  free (msg);
+}
+
 #if HAVE_LIBSELINUX
 static void
 selinux_warning (guestfs_h *g, const char *func,
diff --git a/src/launch-uml.c b/src/launch-uml.c
index deda366..2a6ddaf 100644
--- a/src/launch-uml.c
+++ b/src/launch-uml.c
@@ -122,6 +122,12 @@ uml_supported (guestfs_h *g)
              _("uml backend does not support drives with 'label'
parameter"));
       return false;
     }
+    /* Note that discard == "besteffort" is fine. */
+    if (drv->discard == discard_enable) {
+      error (g,
+             _("uml backend does not support drives with 'discard'
parameter set to 'enable'"));
+      return false;
+    }
   }
 
   return true;
-- 
1.8.5.3
Richard W.M. Jones
2014-Mar-10  17:28 UTC
[Libguestfs] [PATCH 3/3] tests: Add a test of discard support, using fstrim.
Test that fstrim actually works, end-to-end.
---
 Makefile.am                   |   1 +
 configure.ac                  |   1 +
 tests/discard/Makefile.am     |  26 ++++++++++
 tests/discard/test-discard.pl | 116 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 144 insertions(+)
 create mode 100644 tests/discard/Makefile.am
 create mode 100755 tests/discard/test-discard.pl
diff --git a/Makefile.am b/Makefile.am
index 020628e..11b12c2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -46,6 +46,7 @@ SUBDIRS += tests/events
 SUBDIRS += tests/parallel
 SUBDIRS += tests/create
 SUBDIRS += tests/disks
+SUBDIRS += tests/discard
 SUBDIRS += tests/mountable
 SUBDIRS += tests/network
 SUBDIRS += tests/lvm
diff --git a/configure.ac b/configure.ac
index 35460e2..3785ea8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1673,6 +1673,7 @@ AC_CONFIG_FILES([Makefile
                  tests/charsets/Makefile
                  tests/create/Makefile
                  tests/data/Makefile
+                 tests/discard/Makefile
                  tests/disks/Makefile
                  tests/disks/test-qemu-drive-libvirt.xml
                  tests/disk-labels/Makefile
diff --git a/tests/discard/Makefile.am b/tests/discard/Makefile.am
new file mode 100644
index 0000000..f159287
--- /dev/null
+++ b/tests/discard/Makefile.am
@@ -0,0 +1,26 @@
+# libguestfs
+# Copyright (C) 2014 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+TESTS = \
+	test-discard.pl
+
+TESTS_ENVIRONMENT = $(top_builddir)/run --test
+
+EXTRA_DIST = \
+	$(TESTS)
diff --git a/tests/discard/test-discard.pl b/tests/discard/test-discard.pl
new file mode 100755
index 0000000..770049c
--- /dev/null
+++ b/tests/discard/test-discard.pl
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+# Copyright (C) 2014 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+# Test that discard support works.
+
+use strict;
+use warnings;
+
+use Sys::Guestfs;
+
+# Since we read error messages, we want to ensure they are printed
+# in English, hence:
+$ENV{"LANG"} = "C";
+
+$| = 1;
+
+my $g = Sys::Guestfs->new ();
+
+# Discard is only supported when using qemu.
+if ($g->get_backend () ne "libvirt" &&
+    $g->get_backend () !~ /^libvirt:/ &&
+    $g->get_backend () ne "direct") {
+    print "$0: skipped test because discard is only supported when using
qemu\n";
+    exit 77;
+}
+
+# You can set this to "raw" or "qcow2".
+my $format = "raw";
+
+my $disk;
+my @args;
+if ($format eq "raw") {
+    $disk = "test-discard.img";
+    @args = ( preallocation => "sparse" );
+} elsif ($format eq "qcow2") {
+    $disk = "test-discard.qcow2";
+    @args = ( preallocation => "off", compat => "1.1"
);
+} else {
+    die "$0: invalid disk format: $format\n";
+}
+
+# Create a disk and add it with discard enabled.  This is allowed to
+# fail, eg because qemu is too old, but libguestfs must tell us that
+# it failed (since we're using 'enable', not 'besteffort').
+$g->disk_create ($disk, $format, 100*1024*1024, @args);
+END { unlink ($disk); };
+
+eval {
+    $g->add_drive ($disk, format => $format, readonly => 0, discard
=> "enable");
+    $g->launch ();
+};
+if ($@) {
+    if ($@ =~ /discard cannot be enabled on this drive/) {
+        # This is OK.  Libguestfs says it's not possible to enable
+        # discard on this drive (eg. because qemu is too old).  Print
+        # the reason and skip the test.
+        print "$0: skipped test: $@\n";
+        exit 77;
+    }
+    die # propagate the unexpected error
+}
+
+# Is fstrim available in the appliance?
+unless ($g->feature_available (["fstrim"])) {
+    print "$0: skipped test because fstrim is not available\n";
+    exit 77;
+}
+
+# At this point we've got a disk which claims to support discard.
+# Let's test that theory.
+
+my $orig_size = (stat ($disk))[12];
+print "original size:\t$orig_size (blocks)\n";
+#system "du -sh $disk";
+
+# Write a filesystem onto the disk and fill it with data.
+
+$g->mkfs ("ext4", "/dev/sda");
+$g->mount_options ("discard", "/dev/sda",
"/");
+$g->fill (33, 10000000, "/data");
+$g->sync ();
+
+my $full_size = (stat ($disk))[12];
+print "full size:\t$full_size (blocks)\n";
+#system "du -sh $disk";
+
+die "surprising result: full size <= original size"
+    if $full_size <= $orig_size;
+
+# Remove the file and then try to trim the filesystem.
+
+$g->rm ("/data");
+$g->fstrim ("/");
+$g->sync ();
+$g->close ();
+
+my $trimmed_size = (stat ($disk))[12];
+print "trimmed size:\t$trimmed_size (blocks)\n";
+#system "du -sh $disk";
+
+die "looks like the fstrim operation did not work"
+    if $trimmed_size >= $full_size;
-- 
1.8.5.3
Pino Toscano
2014-Mar-10  17:50 UTC
Re: [Libguestfs] [PATCH 2/3] New API parameter: Add discard parameter to guestfs_add_drive_opts.
Hi, On Monday 10 March 2014 17:28:53 Richard W.M. Jones wrote:> index 1e84061..584c4de 100644 > --- a/src/launch-direct.c > +++ b/src/launch-direct.c > @@ -509,6 +509,29 @@ launch_direct (guestfs_h *g, void *datav, const char *arg) > CLEANUP_FREE char *file = NULL, *escaped_file = NULL, *param = NULL; > > if (!drv->overlay) { > + const char *discard_mode = ""; > + int major = data->qemu_version_major, minor = data->qemu_version_minor; > + > + switch (drv->discard) { > + case discard_disable: > + /* Since the default is always discard=ignore, don't specify it > + * on the command line. This also avoids unnecessary breakage > + * with qemu < 1.5 which didn't have the option at all. > + */ > + break; > + case discard_enable: > + if (!guestfs___discard_possible (g, drv, major, minor))There is an argument mismatch in this call to guestfs___discard_possible (int's vs bool's). What about turn guestfs___discard_possible to just get the qemu version, and do the checks in it rather than asking to prefill the two "is qemu >= 1.5" and "is qemu >= 1.6" bool's? -- Pino Toscano
Pino Toscano
2014-Mar-10  17:59 UTC
Re: [Libguestfs] [PATCH 1/3] launch: libvirt: Move the is_kvm flag (derived from libvirt capabilities) to backend data struct.
Hi, On Monday 10 March 2014 17:28:52 Richard W.M. Jones wrote:> diff --git a/src/launch-libvirt.c b/src/launch-libvirt.c > index fd6e6d2..d86e2d2 100644 > --- a/src/launch-libvirt.c > +++ b/src/launch-libvirt.c > @@ -106,6 +106,7 @@ struct backend_libvirt_data { > char *selinux_imagelabel; > bool selinux_norelabel_disks; > char name[DOMAIN_NAME_LEN]; /* random name */ > + bool is_kvm; /* false = qemu, true = kvm (from capabilities)*/ }; > > /* Parameters passed to construct_libvirt_xml and subfunctions. We > @@ -122,12 +123,11 @@ struct libvirt_xml_params { > char guestfsd_path[UNIX_PATH_MAX]; /* paths to sockets */ > char console_path[UNIX_PATH_MAX]; > bool enable_svirt; /* false if we decided to disable sVirt */ > - bool is_kvm; /* false = qemu, true = kvm */ > bool current_proc_is_root; /* true = euid is root */ > }; > > -static int parse_capabilities (guestfs_h *g, const char *capabilities_xml, struct libvirt_xml_params *params); > -static xmlChar *construct_libvirt_xml (guestfs_h *g, const struct libvirt_xml_params *params); > +static int parse_capabilities (guestfs_h *g, const char *capabilities_xml, struct backend_libvirt_data *data); > +static xmlChar *construct_libvirt_xml (guestfs_h *g, const struct backend_libvirt_data *data, const struct libvirt_xml_params *params);Considering the libvirt_xml_params struct has already a 'struct backend_libvirt_data *data' member, I guess these (and the others) functions could just access params->data->is_kvm with no need to pass also backend_libvirt_data around, no? After all, construct_libvirt_xml_name does this already. -- Pino Toscano
Seemingly Similar Threads
- [PATCH v2 00/18] Add discard support.
- [PATCH 0/2] Don't use snapshot=on
- [PATCH v3 00/10] Add discard support.
- Re: [PATCH v2 03/18] New API parameter: Add discard parameter to guestfs_add_drive_opts.
- [RFC] lib: allow to specify physical/logical block size for disks