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 e96aa86..a0c7b0b 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 r;
+
+ if (errno != ENOTTY) {
+ nbdkit_error ("zero: %m");
+ return r;
+ }
+
+ h->can_zeroout = false;
+ }
+#endif
+
/* Trigger a fall back to writing */
errno = EOPNOTSUPP;
return r;
--
2.17.1