[Libguestfs] [PATCH 3/3] file: Zero for block devices on old kernels

Nir Soffer nirsof at gmail.com
Thu Aug 2 19:05:29 UTC 2018


fallocate(FALLOC_FL_ZERO_RANGE) is supportd for block devices with
modern kernel, but when it is not, we fall back to manual zeroing.

Check if the underlying file is a block device when opening the file,
and fall back to ioctl(BLKZEROOUT) for aligned zero requests for a
block device.

Here is an example run without this change on RHEL 7.5:

$ export SOCK=/tmp/nbd.sock
$ export BLOCK=/dev/e30bfac2-8e13-479d-8cd6-c6da5e306c4e/c9864222-bc52-4359-80d7-76e47d619b15
$ src/nbdkit plugins/file/.libs/nbdkit-file-plugin.so file=$BLOCK -U $SOCK

$ time qemu-img convert -n -f raw -O raw /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock

real	0m11.563s
user	0m0.219s
sys	0m0.746s

$ time qemu-img convert -n -f raw -O raw -W /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock

real	0m9.841s
user	0m0.167s
sys	0m0.715s

With this change:

$ time qemu-img convert -n -f raw -O raw /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock

real	0m2.796s
user	0m0.202s
sys	0m0.700s

$ time qemu-img convert -n -f raw -O raw -W /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock

real	0m1.908s
user	0m0.166s
sys	0m0.728s
---
 plugins/file/file.c | 68 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 57 insertions(+), 11 deletions(-)

diff --git a/plugins/file/file.c b/plugins/file/file.c
index aa05492..8877db2 100644
--- a/plugins/file/file.c
+++ b/plugins/file/file.c
@@ -45,6 +45,7 @@
 
 #if defined(__linux__) && !defined(FALLOC_FL_PUNCH_HOLE)
 #include <linux/falloc.h>   /* For FALLOC_FL_*, glibc < 2.18 */
+#include <linux/fs.h>       /* For BLKZEROOUT */
 #endif
 
 #include <nbdkit-plugin.h>
@@ -119,16 +120,25 @@ file_config_complete (void)
 /* The per-connection handle. */
 struct handle {
   int fd;
+  bool is_block_device;
+  int sector_size;
   bool can_punch_hole;
   bool can_zero_range;
   bool can_fallocate;
 };
 
+static bool
+is_aligned(struct handle *h, uint64_t n)
+{
+  return n % h->sector_size == 0;
+}
+
 /* Create the per-connection handle. */
 static void *
 file_open (int readonly)
 {
   struct handle *h;
+  struct stat statbuf;
   int flags;
 
   h = malloc (sizeof *h);
@@ -150,6 +160,26 @@ file_open (int readonly)
     return NULL;
   }
 
+  if (fstat (h->fd, &statbuf) == -1) {
+    nbdkit_error ("fstat: %s: %m", filename);
+    free (h);
+    return NULL;
+  }
+
+  h->is_block_device = S_ISBLK(statbuf.st_mode);
+
+#ifdef BLKSSZGET
+  if (h->is_block_device) {
+    if (ioctl (h->fd, BLKSSZGET, &h->sector_size)) {
+      nbdkit_error ("ioctl(BLKSSZGET): %s: %m", filename);
+      free (h);
+      return NULL;
+    }
+  }
+#else
+  h->sector_size = 4096;  /* Safe guess */
+#endif
+
 #ifdef FALLOC_FL_PUNCH_HOLE
   h->can_punch_hole = true;
 #else
@@ -184,27 +214,29 @@ static int64_t
 file_get_size (void *handle)
 {
   struct handle *h = handle;
-  struct stat statbuf;
 
-  if (fstat (h->fd, &statbuf) == -1) {
-    nbdkit_error ("stat: %m");
-    return -1;
-  }
-
-  if (S_ISBLK (statbuf.st_mode)) {
+  if (h->is_block_device) {
+    /* Block device, so st_size will not be the true size. */
     off_t size;
 
-    /* Block device, so st_size will not be the true size. */
     size = lseek (h->fd, 0, SEEK_END);
     if (size == -1) {
       nbdkit_error ("lseek (to find device size): %m");
       return -1;
     }
+
     return size;
-  }
+  } else {
+    /* Regular file. */
+    struct stat statbuf;
+
+    if (fstat (h->fd, &statbuf) == -1) {
+      nbdkit_error ("fstat: %m");
+      return -1;
+    }
 
-  /* Else regular file. */
-  return statbuf.st_size;
+    return statbuf.st_size;
+  }
 }
 
 /* Trim is advisory, but we prefer to advertise it only when we can actually
@@ -329,6 +361,20 @@ file_zero (void *handle, uint32_t count, uint64_t offset, int may_trim)
   }
 #endif
 
+#ifdef BLKZEROOUT
+  /* For aligned range and block devices, we can use BLKZEROOUT. */
+  if (h->is_block_device && is_aligned (h, offset) && is_aligned (h, count)) {
+    uint64_t range[2] = {offset, count};
+
+    r = ioctl (h->fd, BLKZEROOUT, &range);
+    if (r == 0)
+      return r;
+
+    nbdkit_error ("zero: %m");
+    return r;
+  }
+#endif
+
   /* Trigger a fall back to writing */
   errno = EOPNOTSUPP;
   return r;
-- 
2.17.1




More information about the Libguestfs mailing list