Nir Soffer
2018-Aug-19 16:56 UTC
[Libguestfs] [PATCH v3 0/4] file: Zero for block devices and older file systems
This version addresses comments on v3. Changes since v3: - Finally got spacing right (Eric) - Reorder includes (Richard) - Return 0 or -1 instead of r (Richard) - Add common/include/isaligned.h to Makefile.am (Richard) v3 was here: https://www.redhat.com/archives/libguestfs/2018-August/msg00177.html Nir Soffer (4): file: Avoid unsupported fallocate() calls file: Support zero without ZERO_RANGE common: Add isaligned helper module file: Zero for block devices on old kernels common/include/Makefile.am | 1 + common/include/isaligned.h | 51 ++++++++++ plugins/file/Makefile.am | 3 +- plugins/file/file.c | 186 +++++++++++++++++++++++++++++-------- 4 files changed, 200 insertions(+), 41 deletions(-) create mode 100644 common/include/isaligned.h -- 2.17.1
Nir Soffer
2018-Aug-19 16:56 UTC
[Libguestfs] [PATCH v4 1/4] file: Avoid unsupported fallocate() calls
When using file systems not supporting ZERO_RANGE (e.g. NFS 4.2) or block device on kernel < 4.9, we used to call fallocate() for every zero, fail with EOPNOTSUPP, and fallback to manual zeroing. When trimming, we used to try unsupported fallocate() on every call. Change file handle to remember if punching holes or zeroing range are supported, and avoid unsupported calls. - zero changed to: 1. If we can punch hole and may trim, try PUNCH_HOLE 2. If we can zero range, try ZERO_RANGE 3. Fall back to manual writing - trim changed to: 1. If we can punch hole, try PUNCH_HOLE 2. Succeed --- plugins/file/file.c | 84 +++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/plugins/file/file.c b/plugins/file/file.c index 3bb4d17..72f51a4 100644 --- a/plugins/file/file.c +++ b/plugins/file/file.c @@ -35,6 +35,7 @@ #include <stdio.h> #include <stdlib.h> +#include <stdbool.h> #include <string.h> #include <fcntl.h> #include <unistd.h> @@ -118,6 +119,8 @@ file_config_complete (void) /* The per-connection handle. */ struct handle { int fd; + bool can_punch_hole; + bool can_zero_range; }; /* Create the per-connection handle. */ @@ -146,6 +149,18 @@ file_open (int readonly) return NULL; } +#ifdef FALLOC_FL_PUNCH_HOLE + h->can_punch_hole = true; +#else + h->can_punch_hole = false; +#endif + +#ifdef FALLOC_FL_ZERO_RANGE + h->can_zero_range = true; +#else + h->can_zero_range = false; +#endif + return h; } @@ -252,35 +267,43 @@ file_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset) static int file_zero (void *handle, uint32_t count, uint64_t offset, int may_trim) { -#if defined(FALLOC_FL_PUNCH_HOLE) || defined(FALLOC_FL_ZERO_RANGE) struct handle *h = handle; -#endif - int r = -1; + int r; #ifdef FALLOC_FL_PUNCH_HOLE - if (may_trim) { + if (h->can_punch_hole && may_trim) { r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, count); - if (r == -1 && errno != EOPNOTSUPP) { + if (r == 0) + return 0; + + if (errno != EOPNOTSUPP) { nbdkit_error ("zero: %m"); + return -1; } - /* PUNCH_HOLE is older; if it is not supported, it is likely that - ZERO_RANGE will not work either, so fall back to write. */ - return r; + + h->can_punch_hole = false; } #endif #ifdef FALLOC_FL_ZERO_RANGE - r = do_fallocate (h->fd, FALLOC_FL_ZERO_RANGE, offset, count); - if (r == -1 && errno != EOPNOTSUPP) { - nbdkit_error ("zero: %m"); + if (h->can_zero_range) { + r = do_fallocate (h->fd, FALLOC_FL_ZERO_RANGE, offset, count); + if (r == 0) + return 0; + + if (errno != EOPNOTSUPP) { + nbdkit_error ("zero: %m"); + return -1; + } + + h->can_zero_range = false; } -#else - /* Trigger a fall back to writing */ - errno = EOPNOTSUPP; #endif - return r; + /* Trigger a fall back to writing */ + errno = EOPNOTSUPP; + return -1; } /* Flush the file to disk. */ @@ -301,27 +324,30 @@ file_flush (void *handle) static int file_trim (void *handle, uint32_t count, uint64_t offset) { - int r = -1; #ifdef FALLOC_FL_PUNCH_HOLE struct handle *h = handle; + int r; + + if (h->can_punch_hole) { + r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + offset, count); + if (r == -1) { + /* Trim is advisory; we don't care if it fails for anything other + * than EIO or EPERM. */ + if (errno == EPERM || errno == EIO) { + nbdkit_error ("fallocate: %m"); + return -1; + } + + if (errno == EOPNOTSUPP) + h->can_punch_hole = false; - /* Trim is advisory; we don't care if it fails for anything other - * than EIO or EPERM. */ - r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - offset, count); - if (r < 0) { - if (errno != EPERM && errno != EIO) { nbdkit_debug ("ignoring failed fallocate during trim: %m"); - r = 0; } - else - nbdkit_error ("fallocate: %m"); } -#else - /* Based on .can_trim, this should not be reached. */ - errno = EOPNOTSUPP; #endif - return r; + + return 0; } static struct nbdkit_plugin plugin = { -- 2.17.1
Nir Soffer
2018-Aug-19 16:56 UTC
[Libguestfs] [PATCH v4 2/4] file: Support zero without ZERO_RANGE
File systems not supporting FALLOC_FL_ZERO_RANGE yet fall back to manual zeroing. We can avoid this by combining two fallocate calls: fallocate(FALLOC_FL_PUNCH_HOLE) fallocate(0) Based on my tests this is much more efficient compared to manual zeroing. The idea came from this qemu patch: https://github.com/qemu/qemu/commit/1cdc3239f1bb Here is an example run with NFS 4.2 without this change, converting fedora 27 image created with virt-builder: $ export SOCK=/tmp/nbd.sock $ export FILE=/nfs-mount/fedora-27.img $ src/nbdkit plugins/file/.libs/nbdkit-file-plugin.so file=$FILE -U $SOCK $ time qemu-img convert -n -f raw -O raw /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock real 0m17.481s user 0m0.199s sys 0m0.691s $ time qemu-img convert -n -f raw -O raw -W /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock real 0m17.072s user 0m0.191s sys 0m0.738s With this change: $ time qemu-img convert -n -f raw -O raw /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock real 0m6.285s user 0m0.217s sys 0m0.829s $ time qemu-img convert -n -f raw -O raw -W /var/tmp/fedora-27.img nbd:unix:/tmp/nbd.sock real 0m3.967s user 0m0.193s sys 0m0.702s Note: the image is sparse, but nbdkit creates a fully allocated image. This may be a bug in nbdkit or qemu-img. --- plugins/file/file.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/plugins/file/file.c b/plugins/file/file.c index 72f51a4..141a32c 100644 --- a/plugins/file/file.c +++ b/plugins/file/file.c @@ -121,6 +121,7 @@ struct handle { int fd; bool can_punch_hole; bool can_zero_range; + bool can_fallocate; }; /* Create the per-connection handle. */ @@ -161,6 +162,8 @@ file_open (int readonly) h->can_zero_range = false; #endif + h->can_fallocate = true; + return h; } @@ -301,6 +304,35 @@ file_zero (void *handle, uint32_t count, uint64_t offset, int may_trim) } #endif +#ifdef FALLOC_FL_PUNCH_HOLE + /* If we can punch hole but may not trim, we can combine punching hole and + * fallocate to zero a range. This is expected to be more efficient than + * writing zeros manually. */ + if (h->can_punch_hole && h->can_fallocate) { + r = do_fallocate (h->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + offset, count); + if (r == 0) { + r = do_fallocate (h->fd, 0, offset, count); + if (r == 0) + return 0; + + if (errno != EOPNOTSUPP) { + nbdkit_error ("zero: %m"); + return -1; + } + + h->can_fallocate = false; + } else { + if (errno != EOPNOTSUPP) { + nbdkit_error ("zero: %m"); + return -1; + } + + h->can_punch_hole = false; + } + } +#endif + /* Trigger a fall back to writing */ errno = EOPNOTSUPP; return -1; -- 2.17.1
Nir Soffer
2018-Aug-19 16:56 UTC
[Libguestfs] [PATCH v4 3/4] common: Add isaligned helper module
is_aligned (size, align) returns true if size is aligned to align, assuming that align is power of 2. Suggested by Eric Blake in https://www.redhat.com/archives/libguestfs/2018-August/msg00036.html --- common/include/Makefile.am | 1 + common/include/isaligned.h | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 common/include/isaligned.h diff --git a/common/include/Makefile.am b/common/include/Makefile.am index 07f28b3..6b3e75f 100644 --- a/common/include/Makefile.am +++ b/common/include/Makefile.am @@ -37,5 +37,6 @@ include $(top_srcdir)/common-rules.mk EXTRA_DIST = \ byte-swapping.h \ exit-with-parent.h \ + isaligned.h \ ispowerof2.h \ iszero.h diff --git a/common/include/isaligned.h b/common/include/isaligned.h new file mode 100644 index 0000000..e693820 --- /dev/null +++ b/common/include/isaligned.h @@ -0,0 +1,51 @@ +/* nbdkit + * Copyright (C) 2018 Red Hat Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef NBDKIT_ISALIGNED_H +#define NBDKIT_ISALIGNED_H + +#include <assert.h> +#include <stdbool.h> + +#include "ispowerof2.h" + +/* Return true if size is a multiple of align. align must be power of 2. + */ +static inline bool +is_aligned (unsigned int size, unsigned int align) +{ + assert (is_power_of_2 (align)); + return !(size & (align - 1)); +} + +#endif /* NBDKIT_ISALIGNED_H */ -- 2.17.1
Nir Soffer
2018-Aug-19 16:56 UTC
[Libguestfs] [PATCH v4 4/4] file: Zero for block devices on old kernels
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
Richard W.M. Jones
2018-Aug-19 18:37 UTC
Re: [Libguestfs] [PATCH v3 0/4] file: Zero for block devices and older file systems
On Sun, Aug 19, 2018 at 07:56:51PM +0300, Nir Soffer wrote:> This version addresses comments on v3. > > Changes since v3: > - Finally got spacing right (Eric) > - Reorder includes (Richard) > - Return 0 or -1 instead of r (Richard) > - Add common/include/isaligned.h to Makefile.am (Richard) > > v3 was here: > https://www.redhat.com/archives/libguestfs/2018-August/msg00177.html > > Nir Soffer (4): > file: Avoid unsupported fallocate() calls > file: Support zero without ZERO_RANGE > common: Add isaligned helper module > file: Zero for block devices on old kernelsI pushed the isaligned change. The others all look fine to me now, and I would push them but I think it's better to get Eric to check them over first. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
Eric Blake
2018-Aug-20 17:29 UTC
Re: [Libguestfs] [PATCH v4 1/4] file: Avoid unsupported fallocate() calls
On 08/19/2018 11:56 AM, Nir Soffer wrote:> When using file systems not supporting ZERO_RANGE (e.g. NFS 4.2) or > block device on kernel < 4.9, we used to call fallocate() for every > zero, fail with EOPNOTSUPP, and fallback to manual zeroing. When > trimming, we used to try unsupported fallocate() on every call. > > Change file handle to remember if punching holes or zeroing range are > supported, and avoid unsupported calls. > > - zero changed to: > 1. If we can punch hole and may trim, try PUNCH_HOLE > 2. If we can zero range, try ZERO_RANGE > 3. Fall back to manual writing > > - trim changed to: > 1. If we can punch hole, try PUNCH_HOLE > 2. Succeed > --- > plugins/file/file.c | 84 +++++++++++++++++++++++++++++---------------- > 1 file changed, 55 insertions(+), 29 deletions(-)> @@ -146,6 +149,18 @@ file_open (int readonly) > return NULL; > } > > +#ifdef FALLOC_FL_PUNCH_HOLE > + h->can_punch_hole = true; > +#else > + h->can_punch_hole = false; > +#endifh was allocated with malloc(). If we switched to allocating with calloc(), we could get rid of the '= false' branches, for fewer lines. Not a big enough deal for me to care (so I didn't modify anything). -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Eric Blake
2018-Aug-20 17:38 UTC
Re: [Libguestfs] [PATCH v4 4/4] file: Zero for block devices on old kernels
On 08/19/2018 11:56 AM, Nir Soffer wrote:> 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. >> @@ -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;I generally try to group members by size, to avoid holes that push struct sizes beyond a cache line size. Thankfully, it looks like this struct is now 16 bytes on both 32- and 64-bit architectures, whether or not you sink 'is_block_device' so that all 5 bools are contiguous. If we were worried about it, we could use ':1' for the bools to make the struct occupy 12 bytes instead, but I doubt this code is in the hotspot for it to make a difference. So I'm not changing it. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Eric Blake
2018-Aug-20 17:39 UTC
Re: [Libguestfs] [PATCH v3 0/4] file: Zero for block devices and older file systems
On 08/19/2018 01:37 PM, Richard W.M. Jones wrote:> On Sun, Aug 19, 2018 at 07:56:51PM +0300, Nir Soffer wrote: >> This version addresses comments on v3. >> >> Changes since v3: >> - Finally got spacing right (Eric) >> - Reorder includes (Richard) >> - Return 0 or -1 instead of r (Richard) >> - Add common/include/isaligned.h to Makefile.am (Richard) >> >> v3 was here: >> https://www.redhat.com/archives/libguestfs/2018-August/msg00177.html >> >> Nir Soffer (4): >> file: Avoid unsupported fallocate() calls >> file: Support zero without ZERO_RANGE >> common: Add isaligned helper module >> file: Zero for block devices on old kernels > > I pushed the isaligned change. > > The others all look fine to me now, and I would push them but I think > it's better to get Eric to check them over first.I'm okay with the rest, so I've pushed them. Thanks again for bearing with our delays in reviewing this. -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Richard W.M. Jones
2018-Aug-21 13:06 UTC
Re: [Libguestfs] [PATCH v3 0/4] file: Zero for block devices and older file systems
FYI I added a few enhancements to allow easier tracing inside the file plugin: https://github.com/libguestfs/nbdkit/commit/10d4dcfb30cb0ba626a2ec503c7c96f72c6ca88e https://github.com/libguestfs/nbdkit/commit/96a41cd798ec0dce07d97c2a4a04ee6284e1552f I learned from this that the file_zero function does successfully use fallocate to zero ranges, at least on my very recent Linux system. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into KVM guests. http://libguestfs.org/virt-v2v
Possibly Parallel Threads
- [PATCH v2 0/4] file: Zero for block devices and older file systems
- [PATCH v3 0/4] file: Zero for block devices and older file systems
- [PATCH 0/3] file: Zero for block devices and older file systems
- [PATCH nbdkit PRELIMINARY] file: Move file operators to a new fileops mini-library
- [PATCH nbdkit v2 0/3] Implement fileops.