[lxc-devel] [lxd/master] VM: Switch to unsafe async I/O mode on ZFS backed disk files

tomponline on Github lxc-bot at linuxcontainers.org
Wed Jan 22 11:03:35 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 669 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200122/6165461d/attachment.bin>
-------------- next part --------------
From 5d30eaf34ad46fbc93dd8723582de37bbb545976 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 22 Jan 2020 11:00:35 +0000
Subject: [PATCH] lxd/instance/drivers/driver/qemu: Switch to unsafe async I/O
 mode on ZFS backed disk files

Detects if the storage root disk is running on a ZFS pool backed by a loop file or if the attached disk is a file stored on a ZFS partition.

In both cases we attached the disk to qemu using unsafe async I/O mode that disables flushing to avoid kernel hangs and qemu direct I/O errors with ZFS pre 0.8.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/drivers/driver_qemu.go | 47 ++++++++++++++++++++++++++---
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index 0639b907b2..9d7bdc1424 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -52,6 +52,9 @@ import (
 	"github.com/lxc/lxd/shared/units"
 )
 
+// qemuAsyncIO is used to indicate disk should use async I/O.
+const qemuAsyncIO = "asyncio"
+
 var errQemuAgentOffline = fmt.Errorf("LXD VM agent isn't currently running")
 
 var vmConsole = map[int]bool{}
@@ -1512,12 +1515,44 @@ func (vm *qemu) addRootDriveConfig(sb *strings.Builder, bootIndexes map[string]i
 		DevPath: rootDrivePath,
 	}
 
+	// If the storage pool is on ZFS and backed by a loop file then indicate to qemu we need to use asyncio.
+	// We use async I/O to avoid kernel hangs and qemu direct I/O errors with pre ZFS 0.8.
+	if pool.Driver().Info().Name == "zfs" && !shared.IsBlockdevPath(pool.Driver().Config()["source"]) {
+		driveConf.Opts = append(driveConf.Opts, qemuAsyncIO)
+	}
+
 	return vm.addDriveConfig(sb, bootIndexes, driveConf)
 }
 
 // addDriveConfig adds the qemu config required for adding a supplementary drive.
 func (vm *qemu) addDriveConfig(sb *strings.Builder, bootIndexes map[string]int, driveConf deviceConfig.MountEntryItem) error {
-	devName := fmt.Sprintf(driveConf.DevName)
+	asyncIOMode := false
+	backingFile := driveConf.DevPath
+
+	// If drive config indicates we need to use async I/O then enable it.
+	if shared.StringInSlice(qemuAsyncIO, driveConf.Opts) {
+		asyncIOMode = true
+	} else if !shared.IsBlockdevPath(backingFile) {
+		fsType, err := util.FilesystemDetect(backingFile)
+		if err != nil {
+			return errors.Wrapf(err, "Failed detecting filesystem type of %q", backingFile)
+		}
+
+		if fsType == "zfs" {
+			// Use async I/O to avoid kernel hangs and qemu direct I/O errors with pre ZFS 0.8.
+			asyncIOMode = true
+		}
+	}
+
+	// Use direct I/O by default.
+	cacheMode := "none"
+	aioMode := "native"
+
+	if asyncIOMode {
+		logger.Warnf("Using async I/O with %s", driveConf.DevPath)
+		cacheMode = "unsafe"
+		aioMode = "threads"
+	}
 
 	// Devices use "lxd_" prefix indicating that this is a user named device.
 	t := template.Must(template.New("").Parse(`
@@ -1526,8 +1561,8 @@ func (vm *qemu) addDriveConfig(sb *strings.Builder, bootIndexes map[string]int,
 file = "{{.devPath}}"
 format = "raw"
 if = "none"
-cache = "none"
-aio = "native"
+cache = "{{.cacheMode}}"
+aio = "{{.aioMode}}"
 
 [device "dev-lxd_{{.devName}}"]
 driver = "scsi-hd"
@@ -1540,9 +1575,11 @@ bootindex = "{{.bootIndex}}"
 `))
 
 	m := map[string]interface{}{
-		"devName":   devName,
+		"devName":   driveConf.DevName,
 		"devPath":   driveConf.DevPath,
-		"bootIndex": bootIndexes[devName],
+		"bootIndex": bootIndexes[driveConf.DevName],
+		"cacheMode": cacheMode,
+		"aioMode":   aioMode,
 	}
 	return t.Execute(sb, m)
 }


More information about the lxc-devel mailing list