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

Nir Soffer nirsof at gmail.com
Sun Aug 19 16:56:55 UTC 2018


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

For block device, try also to use ioctl(BLKZEROOUT) if offset and count
are aligned to block device sector size.

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/Makefile.am |  3 +-
 plugins/file/file.c      | 70 +++++++++++++++++++++++++++++++++-------
 2 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/plugins/file/Makefile.am b/plugins/file/Makefile.am
index 00eaf59..91adbcb 100644
--- a/plugins/file/Makefile.am
+++ b/plugins/file/Makefile.am
@@ -41,7 +41,8 @@ nbdkit_file_plugin_la_SOURCES = \
 	$(top_srcdir)/include/nbdkit-plugin.h
 
 nbdkit_file_plugin_la_CPPFLAGS = \
-	-I$(top_srcdir)/include
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/common/include
 nbdkit_file_plugin_la_CFLAGS = \
 	$(WARNINGS_CFLAGS)
 nbdkit_file_plugin_la_LDFLAGS = \
diff --git a/plugins/file/file.c b/plugins/file/file.c
index 141a32c..655877e 100644
--- a/plugins/file/file.c
+++ b/plugins/file/file.c
@@ -41,14 +41,21 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <errno.h>
 
 #if defined(__linux__) && !defined(FALLOC_FL_PUNCH_HOLE)
 #include <linux/falloc.h>   /* For FALLOC_FL_*, glibc < 2.18 */
 #endif
 
+#if defined(__linux__)
+#include <linux/fs.h>       /* For BLKZEROOUT */
+#endif
+
 #include <nbdkit-plugin.h>
 
+#include "isaligned.h"
+
 #ifndef O_CLOEXEC
 #define O_CLOEXEC 0
 #endif
@@ -119,9 +126,12 @@ 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;
+  bool can_zeroout;
 };
 
 /* Create the per-connection handle. */
@@ -129,6 +139,7 @@ static void *
 file_open (int readonly)
 {
   struct handle *h;
+  struct stat statbuf;
   int flags;
 
   h = malloc (sizeof *h);
@@ -150,6 +161,22 @@ 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);
+  h->sector_size = 4096;  /* Start with safe guess */
+
+#ifdef BLKSSZGET
+  if (h->is_block_device) {
+    if (ioctl (h->fd, BLKSSZGET, &h->sector_size))
+      nbdkit_debug ("cannot get sector size: %s: %m", filename);
+  }
+#endif
+
 #ifdef FALLOC_FL_PUNCH_HOLE
   h->can_punch_hole = true;
 #else
@@ -163,6 +190,7 @@ file_open (int readonly)
 #endif
 
   h->can_fallocate = true;
+  h->can_zeroout = h->is_block_device;
 
   return h;
 }
@@ -184,27 +212,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;
 
-  /* Else regular file. */
-  return statbuf.st_size;
+    if (fstat (h->fd, &statbuf) == -1) {
+      nbdkit_error ("fstat: %m");
+      return -1;
+    }
+
+    return statbuf.st_size;
+  }
 }
 
 static int
@@ -333,6 +363,24 @@ file_zero (void *handle, uint32_t count, uint64_t offset, int may_trim)
   }
 #endif
 
+#ifdef BLKZEROOUT
+  /* For aligned range and block device, we can use BLKZEROOUT. */
+  if (h->can_zeroout && is_aligned (offset | count, h->sector_size)) {
+    uint64_t range[2] = {offset, count};
+
+    r = ioctl (h->fd, BLKZEROOUT, &range);
+    if (r == 0)
+      return 0;
+
+    if (errno != ENOTTY) {
+      nbdkit_error ("zero: %m");
+      return -1;
+    }
+
+    h->can_zeroout = false;
+  }
+#endif
+
   /* Trigger a fall back to writing */
   errno = EOPNOTSUPP;
   return -1;
-- 
2.17.1




More information about the Libguestfs mailing list