From: "Richard W.M. Jones" <rjones(a)redhat.com>
This requires febootstrap >= 3.15.
---
README | 2 +-
src/guestfs-internal.h | 3 ++
src/launch.c | 141 +++++++++++++++++++++++++++++++++++++++---------
3 files changed, 121 insertions(+), 25 deletions(-)
diff --git a/README b/README
index 4dd2075..a17863f 100644
--- a/README
+++ b/README
@@ -55,7 +55,7 @@ For basic functionality and the C tools:
to make complex changes to the ./configure command line to get it
to work if you don't have virtio)
-- febootstrap >= 3.3 (it is best to use the latest version)
+- febootstrap >= 3.15
Notes: (1) febootstrap 2.x WILL NOT WORK
(2) febootstrap 3.x is distro-independent, and is required on
diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h
index fb4868c..0cdc8f2 100644
--- a/src/guestfs-internal.h
+++ b/src/guestfs-internal.h
@@ -257,6 +257,9 @@ struct guestfs_h
int ml_read_only; /* If mounted read-only. */
int ml_debug_calls; /* Extra debug info on each FUSE call. */
#endif
+
+ /* Used by src/launch.c:qemu_supports_virtio_scsi */
+ int virtio_scsi;
};
/* Per-filesystem data stored for inspect_os. */
diff --git a/src/launch.c b/src/launch.c
index 3db2443..5aed29f 100644
--- a/src/launch.c
+++ b/src/launch.c
@@ -78,7 +78,10 @@ static int64_t timeval_diff (const struct timeval *x, const struct
timeval *y);
static void print_qemu_command_line (guestfs_h *g, char **argv);
static int connect_unix_socket (guestfs_h *g, const char *sock);
static int qemu_supports (guestfs_h *g, const char *option);
-static char *qemu_drive_param (guestfs_h *g, const struct drive *drv);
+static int qemu_supports_device (guestfs_h *g, const char *device_name);
+static int qemu_supports_virtio_scsi (guestfs_h *g);
+static char *qemu_drive_param (guestfs_h *g, const struct drive *drv, size_t index);
+static char *drive_name (size_t index, char *ret);
#if 0
static int qemu_supports_re (guestfs_h *g, const pcre *option_regex);
@@ -248,7 +251,7 @@ guestfs__debug_drives (guestfs_h *g)
ret = safe_malloc (g, sizeof (char *) * (count + 1));
for (i = 0, drv = g->drives; drv; i++, drv = drv->next)
- ret[i] = qemu_drive_param (g, drv);
+ ret[i] = qemu_drive_param (g, drv, i);
ret[count] = NULL;
@@ -620,6 +623,7 @@ launch_appliance (guestfs_h *g)
if (r == 0) { /* Child (qemu). */
char buf[256];
+ int virtio_scsi = qemu_supports_virtio_scsi (g);
/* Set up the full command line. Do this in the subprocess so we
* don't need to worry about cleaning up.
@@ -660,15 +664,60 @@ launch_appliance (guestfs_h *g)
/* Add drives */
struct drive *drv = g->drives;
+ size_t drv_index = 0;
+
+ if (virtio_scsi) {
+ /* Create the virtio-scsi bus. */
+ add_cmdline (g, "-device");
+ add_cmdline (g, "virtio-scsi-pci,id=scsi");
+ }
+
while (drv != NULL) {
/* Construct the final -drive parameter. */
- char *buf = qemu_drive_param (g, drv);
+ char *buf = qemu_drive_param (g, drv, drv_index);
add_cmdline (g, "-drive");
add_cmdline (g, buf);
free (buf);
+ if (qemu_supports_virtio_scsi (g)) {
+ char buf2[64];
+ snprintf (buf2, sizeof buf2, "scsi-hd,drive=hd%zu", drv_index);
+ add_cmdline (g, "-device");
+ add_cmdline (g, buf2);
+ }
+
drv = drv->next;
+ drv_index++;
+ }
+
+ char appliance_root[64] = "";
+
+ /* Add the ext2 appliance drive (after all the drives). */
+ if (appliance) {
+ const char *cachemode = "";
+ if (qemu_supports (g, "cache=")) {
+ if (qemu_supports (g, "unsafe"))
+ cachemode = ",cache=unsafe";
+ else if (qemu_supports (g, "writeback"))
+ cachemode = ",cache=writeback";
+ }
+
+ char buf2[PATH_MAX + 64];
+ int virtio_scsi = qemu_supports_virtio_scsi (g);
+ add_cmdline (g, "-drive");
+ snprintf (buf2, sizeof buf2, "file=%s,snapshot=on,id=appliance,if=%s%s",
+ appliance, virtio_scsi ? "none" : "virtio",
cachemode);
+ add_cmdline (g, buf2);
+
+ if (virtio_scsi) {
+ add_cmdline (g, "-device");
+ add_cmdline (g, "scsi-hd,drive=appliance");
+ }
+
+ snprintf (appliance_root, sizeof appliance_root, "root=/dev/%cd",
+ virtio_scsi ? 's' : 'v');
+ drive_name (drv_index, &appliance_root[12]);
}
if (STRNEQ (QEMU_OPTIONS, "")) {
@@ -792,10 +841,12 @@ launch_appliance (guestfs_h *g)
/* Linux kernel command line. */
snprintf (buf, sizeof buf,
LINUX_CMDLINE
+ "%s " /* (root) */
"%s " /* (selinux) */
"%s " /* (verbose) */
"TERM=%s " /* (TERM environment variable) */
"%s", /* (append) */
+ appliance_root,
g->selinux ? "selinux=1 enforcing=0" : "selinux=0",
g->verbose ? "guestfs_verbose=1" : "",
getenv ("TERM") ? : "linux",
@@ -808,23 +859,6 @@ launch_appliance (guestfs_h *g)
add_cmdline (g, "-append");
add_cmdline (g, buf);
- /* Add the ext2 appliance drive (last of all). */
- if (appliance) {
- const char *cachemode = "";
- if (qemu_supports (g, "cache=")) {
- if (qemu_supports (g, "unsafe"))
- cachemode = ",cache=unsafe";
- else if (qemu_supports (g, "writeback"))
- cachemode = ",cache=writeback";
- }
-
- char buf2[PATH_MAX + 64];
- add_cmdline (g, "-drive");
- snprintf (buf2, sizeof buf2, "file=%s,snapshot=on,if=virtio%s",
- appliance, cachemode);
- add_cmdline (g, buf2);
- }
-
/* Finish off the command line. */
incr_cmdline_size (g);
g->cmdline[g->cmdline_size-1] = NULL;
@@ -1432,6 +1466,20 @@ qemu_supports_re (guestfs_h *g, const pcre *option_regex)
}
#endif
+/* Test if device is supported by qemu (currently just greps the -device ?
+ * output).
+ */
+static int
+qemu_supports_device (guestfs_h *g, const char *device_name)
+{
+ if (!g->qemu_devices) {
+ if (test_qemu (g) == -1)
+ return -1;
+ }
+
+ return strstr (g->qemu_devices, device_name) != NULL;
+}
+
#if defined(__i386__) || defined(__x86_64__)
/* Check if a file can be opened. */
static int
@@ -1447,13 +1495,39 @@ is_openable (guestfs_h *g, const char *path, int flags)
}
#endif
+/* Returns 1 = use virtio-scsi, or 0 = use virtio-blk. */
+static int
+qemu_supports_virtio_scsi (guestfs_h *g)
+{
+ int r;
+
+ /* g->virtio_scsi has these values:
+ * 0 = untested (after handle creation)
+ * 1 = supported
+ * 2 = not supported (use virtio-blk)
+ * 3 = test failed (use virtio-blk)
+ */
+ if (g->virtio_scsi == 0) {
+ r = qemu_supports_device (g, "virtio-scsi-pci");
+ if (r > 0)
+ g->virtio_scsi = 1;
+ else if (r == 0)
+ g->virtio_scsi = 2;
+ else
+ g->virtio_scsi = 3;
+ }
+
+ return g->virtio_scsi == 1;
+}
+
static char *
-qemu_drive_param (guestfs_h *g, const struct drive *drv)
+qemu_drive_param (guestfs_h *g, const struct drive *drv, size_t index)
{
size_t i;
- size_t len = 64;
+ size_t len = 128;
const char *p;
char *r;
+ const char *iface;
len += strlen (drv->path) * 2; /* every "," could become ",,"
*/
if (drv->iface)
@@ -1475,16 +1549,35 @@ qemu_drive_param (guestfs_h *g, const struct drive *drv)
r[i++] = *p;
}
- snprintf (&r[i], len-i, "%s%s%s%s,if=%s",
+ if (drv->iface)
+ iface = drv->iface;
+ else if (qemu_supports_virtio_scsi (g))
+ iface = "none"; /* sic */
+ else
+ iface = "virtio";
+
+ snprintf (&r[i], len-i, "%s%s%s%s,id=hd%zu,if=%s",
drv->readonly ? ",snapshot=on" : "",
drv->use_cache_off ? ",cache=off" : "",
drv->format ? ",format=" : "",
drv->format ? drv->format : "",
- drv->iface ? drv->iface : "virtio");
+ index,
+ iface);
return r; /* caller frees */
}
+/*
https://rwmj.wordpress.com/2011/01/09/how-are-linux-drives-named-beyond-d...
*/
+static char *
+drive_name (size_t index, char *ret)
+{
+ if (index > 26)
+ ret = drive_name (index / 26, ret);
+ index %= 26;
+ *ret++ = 'a' + index;
+ return ret;
+}
+
/* You had to call this function after launch in versions <= 1.0.70,
* but it is now a no-op.
*/
--
1.7.10.1