This change is almost completely code motion.
The only part which is not simple movement of code is that I changed
‘cache_flush’ so that it does not lazily allocate the bounce buffer.
This change considerably simplifies the code and the performance
impact should be minimal.
---
filters/cache/blk.h | 66 +++++++++
filters/cache/cache.h | 54 ++++++++
filters/cache/blk.c | 244 ++++++++++++++++++++++++++++++++
filters/cache/cache.c | 285 +++++++++-----------------------------
filters/cache/Makefile.am | 3 +
5 files changed, 429 insertions(+), 223 deletions(-)
diff --git a/filters/cache/blk.h b/filters/cache/blk.h
new file mode 100644
index 0000000..24bf6a1
--- /dev/null
+++ b/filters/cache/blk.h
@@ -0,0 +1,66 @@
+/* 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_BLK_H
+#define NBDKIT_BLK_H
+
+/* Initialize the cache and bitmap. */
+extern int blk_init (void);
+
+/* Close the cache, free the bitmap. */
+extern void blk_free (void);
+
+/* Allocate or resize the cache file and bitmap. */
+extern int blk_set_size (uint64_t new_size);
+
+/* Read a single block from the cache or plugin. */
+extern int blk_read (struct nbdkit_next_ops *next_ops, void *nxdata, uint64_t blknum,
uint8_t *block, int *err);
+
+/* Write to the cache and the plugin. */
+extern int blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata, uint64_t
blknum, const uint8_t *block, uint32_t flags, int *err);
+
+/* Write a whole block.
+ *
+ * If the cache is in writethrough mode, or the FUA flag is set, then
+ * this calls blk_writethrough above which will write both to the
+ * cache and through to the underlying device.
+ *
+ * Otherwise it will only write to the cache.
+ */
+extern int blk_write (struct nbdkit_next_ops *next_ops, void *nxdata, uint64_t blknum,
const uint8_t *block, uint32_t flags, int *err);
+
+/* Iterates over each dirty block in the cache. */
+typedef int (*block_callback) (uint64_t blknum, void *vp);
+extern int for_each_dirty_block (block_callback f, void *vp);
+
+#endif /* NBDKIT_BLK_H */
diff --git a/filters/cache/cache.h b/filters/cache/cache.h
new file mode 100644
index 0000000..50416b1
--- /dev/null
+++ b/filters/cache/cache.h
@@ -0,0 +1,54 @@
+/* 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_CACHE_H
+#define NBDKIT_CACHE_H
+
+/* Size of a block in the cache. A 4K block size means that we need
+ * 64 MB of memory to store the bitmaps for a 1 TB underlying image.
+ * It is also smaller than the usual hole size for sparse files, which
+ * means we have no reason to call next_ops->zero.
+ */
+#define BLKSIZE 4096
+
+/* Caching mode. */
+extern enum cache_mode {
+ CACHE_MODE_WRITEBACK,
+ CACHE_MODE_WRITETHROUGH,
+ CACHE_MODE_UNSAFE,
+} cache_mode;
+
+/* Cache read requests. */
+extern bool cache_on_read;
+
+#endif /* NBDKIT_CACHE_H */
diff --git a/filters/cache/blk.c b/filters/cache/blk.c
new file mode 100644
index 0000000..4000276
--- /dev/null
+++ b/filters/cache/blk.c
@@ -0,0 +1,244 @@
+/* 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.
+ */
+
+/* These are the block operations. They always read or write a single
+ * whole block of size ‘blksize’.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <nbdkit-filter.h>
+
+#include "bitmap.h"
+
+#include "cache.h"
+#include "blk.h"
+
+/* The cache. */
+static int fd = -1;
+
+/* Bitmap. There are two bits per block which are updated as we read,
+ * write back or write through blocks.
+ *
+ * 00 = not in cache
+ * 01 = block cached and clean
+ * 10 = <unused>
+ * 11 = block cached and dirty
+ */
+static struct bitmap bm;
+
+enum bm_entry {
+ BLOCK_NOT_CACHED = 0,
+ BLOCK_CLEAN = 1,
+ BLOCK_DIRTY = 3,
+};
+
+int
+blk_init (void)
+{
+ const char *tmpdir;
+ size_t len;
+ char *template;
+
+ bitmap_init (&bm, BLKSIZE, 2 /* bits per block */);
+
+ tmpdir = getenv ("TMPDIR");
+ if (!tmpdir)
+ tmpdir = LARGE_TMPDIR;
+
+ nbdkit_debug ("cache: temporary directory for cache: %s", tmpdir);
+
+ len = strlen (tmpdir) + 8;
+ template = alloca (len);
+ snprintf (template, len, "%s/XXXXXX", tmpdir);
+
+#ifdef HAVE_MKOSTEMP
+ fd = mkostemp (template, O_CLOEXEC);
+#else
+ fd = mkstemp (template);
+ fcntl (fd, F_SETFD, FD_CLOEXEC);
+#endif
+ if (fd == -1) {
+ nbdkit_error ("mkostemp: %s: %m", tmpdir);
+ return -1;
+ }
+
+ unlink (template);
+
+ return 0;
+}
+
+void
+blk_free (void)
+{
+ if (fd >= 0)
+ close (fd);
+
+ bitmap_free (&bm);
+}
+
+int
+blk_set_size (uint64_t new_size)
+{
+ if (bitmap_resize (&bm, new_size) == -1)
+ return -1;
+
+ if (ftruncate (fd, new_size) == -1) {
+ nbdkit_error ("ftruncate: %m");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
+ uint64_t blknum, uint8_t *block, int *err)
+{
+ off_t offset = blknum * BLKSIZE;
+ enum bm_entry state = bitmap_get_blk (&bm, blknum, BLOCK_NOT_CACHED);
+
+ nbdkit_debug ("cache: blk_read block %" PRIu64 " (offset %" PRIu64
") is %s",
+ blknum, (uint64_t) offset,
+ state == BLOCK_NOT_CACHED ? "not cached" :
+ state == BLOCK_CLEAN ? "clean" :
+ state == BLOCK_DIRTY ? "dirty" :
+ "unknown");
+
+ if (state == BLOCK_NOT_CACHED) { /* Read underlying plugin. */
+ if (next_ops->pread (nxdata, block, BLKSIZE, offset, 0, err) == -1)
+ return -1;
+
+ /* If cache-on-read, copy the block to the cache. */
+ if (cache_on_read) {
+ off_t offset = blknum * BLKSIZE;
+
+ nbdkit_debug ("cache: cache-on-read block %" PRIu64
+ " (offset %" PRIu64 ")",
+ blknum, (uint64_t) offset);
+
+ if (pwrite (fd, block, BLKSIZE, offset) == -1) {
+ *err = errno;
+ nbdkit_error ("pwrite: %m");
+ return -1;
+ }
+ bitmap_set_blk (&bm, blknum, BLOCK_CLEAN);
+ }
+ return 0;
+ }
+ else { /* Read cache. */
+ if (pread (fd, block, BLKSIZE, offset) == -1) {
+ *err = errno;
+ nbdkit_error ("pread: %m");
+ return -1;
+ }
+ return 0;
+ }
+}
+
+int
+blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
+ uint64_t blknum, const uint8_t *block, uint32_t flags,
+ int *err)
+{
+ off_t offset = blknum * BLKSIZE;
+
+ nbdkit_debug ("cache: writethrough block %" PRIu64 " (offset %"
PRIu64 ")",
+ blknum, (uint64_t) offset);
+
+ if (pwrite (fd, block, BLKSIZE, offset) == -1) {
+ *err = errno;
+ nbdkit_error ("pwrite: %m");
+ return -1;
+ }
+
+ if (next_ops->pwrite (nxdata, block, BLKSIZE, offset, flags, err) == -1)
+ return -1;
+
+ bitmap_set_blk (&bm, blknum, BLOCK_CLEAN);
+
+ return 0;
+}
+
+int
+blk_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+ uint64_t blknum, const uint8_t *block, uint32_t flags,
+ int *err)
+{
+ off_t offset;
+
+ if (cache_mode == CACHE_MODE_WRITETHROUGH ||
+ (cache_mode == CACHE_MODE_WRITEBACK && (flags & NBDKIT_FLAG_FUA)))
+ return blk_writethrough (next_ops, nxdata, blknum, block, flags, err);
+
+ offset = blknum * BLKSIZE;
+
+ nbdkit_debug ("cache: writeback block %" PRIu64 " (offset %" PRIu64
")",
+ blknum, (uint64_t) offset);
+
+ if (pwrite (fd, block, BLKSIZE, offset) == -1) {
+ *err = errno;
+ nbdkit_error ("pwrite: %m");
+ return -1;
+ }
+ bitmap_set_blk (&bm, blknum, BLOCK_DIRTY);
+
+ return 0;
+}
+
+int
+for_each_dirty_block (block_callback f, void *vp)
+{
+ uint64_t blknum;
+ enum bm_entry state;
+
+ bitmap_for (&bm, blknum) {
+ state = bitmap_get_blk (&bm, blknum, BLOCK_NOT_CACHED);
+ if (state == BLOCK_DIRTY) {
+ if (f (blknum, vp) == -1)
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/filters/cache/cache.c b/filters/cache/cache.c
index b1c3d53..0d006bc 100644
--- a/filters/cache/cache.c
+++ b/filters/cache/cache.c
@@ -52,89 +52,28 @@
#include <nbdkit-filter.h>
-#include "bitmap.h"
+#include "cache.h"
+#include "blk.h"
/* XXX See design comment in filters/cow/cow.c. */
#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
-/* Size of a block in the cache. A 4K block size means that we need
- * 64 MB of memory to store the bitmaps for a 1 TB underlying image.
- * It is also smaller than the usual hole size for sparse files, which
- * means we have no reason to call next_ops->zero.
- */
-#define BLKSIZE 4096
-
-/* The cache. */
-static int fd = -1;
-
-/* Bitmap. There are two bits per block which are updated as we read,
- * write back or write through blocks.
- *
- * 00 = not in cache
- * 01 = block cached and clean
- * 10 = <unused>
- * 11 = block cached and dirty
- */
-static struct bitmap bm;
-
-enum bm_entry {
- BLOCK_NOT_CACHED = 0,
- BLOCK_CLEAN = 1,
- BLOCK_DIRTY = 3,
-};
-
-/* Caching mode. */
-static enum cache_mode {
- CACHE_MODE_WRITEBACK,
- CACHE_MODE_WRITETHROUGH,
- CACHE_MODE_UNSAFE,
-} cache_mode = CACHE_MODE_WRITEBACK;
-
-/* Cache read requests. */
-static bool cache_on_read = false;
+enum cache_mode cache_mode = CACHE_MODE_WRITEBACK;
+bool cache_on_read = false;
static int cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
uint32_t flags, int *err);
static void
cache_load (void)
{
- const char *tmpdir;
- size_t len;
- char *template;
-
- bitmap_init (&bm, BLKSIZE, 2 /* bits per block */);
-
- tmpdir = getenv ("TMPDIR");
- if (!tmpdir)
- tmpdir = LARGE_TMPDIR;
-
- nbdkit_debug ("cache: temporary directory for cache: %s", tmpdir);
-
- len = strlen (tmpdir) + 8;
- template = alloca (len);
- snprintf (template, len, "%s/XXXXXX", tmpdir);
-
-#ifdef HAVE_MKOSTEMP
- fd = mkostemp (template, O_CLOEXEC);
-#else
- fd = mkstemp (template);
- fcntl (fd, F_SETFD, FD_CLOEXEC);
-#endif
- if (fd == -1) {
- nbdkit_error ("mkostemp: %s: %m", tmpdir);
+ if (blk_init () == -1)
exit (EXIT_FAILURE);
- }
-
- unlink (template);
}
static void
cache_unload (void)
{
- if (fd >= 0)
- close (fd);
-
- bitmap_free (&bm);
+ blk_free ();
}
static int
@@ -187,21 +126,6 @@ cache_open (nbdkit_next_open *next, void *nxdata, int readonly)
return &handle;
}
-/* Allocate or resize the cache file and bitmap. */
-static int
-blk_set_size (uint64_t new_size)
-{
- if (bitmap_resize (&bm, new_size) == -1)
- return -1;
-
- if (ftruncate (fd, new_size) == -1) {
- nbdkit_error ("ftruncate: %m");
- return -1;
- }
-
- return 0;
-}
-
/* Get the file size and ensure the cache is the correct size. */
static int64_t
cache_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
@@ -237,113 +161,6 @@ cache_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
return 0;
}
-/* These are the block operations. They always read or write a single
- * whole block of size ‘blksize’.
- */
-static int
-blk_read (struct nbdkit_next_ops *next_ops, void *nxdata,
- uint64_t blknum, uint8_t *block, int *err)
-{
- off_t offset = blknum * BLKSIZE;
- enum bm_entry state = bitmap_get_blk (&bm, blknum, BLOCK_NOT_CACHED);
-
- nbdkit_debug ("cache: blk_read block %" PRIu64 " (offset %" PRIu64
") is %s",
- blknum, (uint64_t) offset,
- state == BLOCK_NOT_CACHED ? "not cached" :
- state == BLOCK_CLEAN ? "clean" :
- state == BLOCK_DIRTY ? "dirty" :
- "unknown");
-
- if (state == BLOCK_NOT_CACHED) { /* Read underlying plugin. */
- if (next_ops->pread (nxdata, block, BLKSIZE, offset, 0, err) == -1)
- return -1;
-
- /* If cache-on-read, copy the block to the cache. */
- if (cache_on_read) {
- off_t offset = blknum * BLKSIZE;
-
- nbdkit_debug ("cache: cache-on-read block %" PRIu64
- " (offset %" PRIu64 ")",
- blknum, (uint64_t) offset);
-
- if (pwrite (fd, block, BLKSIZE, offset) == -1) {
- *err = errno;
- nbdkit_error ("pwrite: %m");
- return -1;
- }
- bitmap_set_blk (&bm, blknum, BLOCK_CLEAN);
- }
- return 0;
- }
- else { /* Read cache. */
- if (pread (fd, block, BLKSIZE, offset) == -1) {
- *err = errno;
- nbdkit_error ("pread: %m");
- return -1;
- }
- return 0;
- }
-}
-
-/* Write to the cache and the plugin. */
-static int
-blk_writethrough (struct nbdkit_next_ops *next_ops, void *nxdata,
- uint64_t blknum, const uint8_t *block, uint32_t flags,
- int *err)
-{
- off_t offset = blknum * BLKSIZE;
-
- nbdkit_debug ("cache: writethrough block %" PRIu64 " (offset %"
PRIu64 ")",
- blknum, (uint64_t) offset);
-
- if (pwrite (fd, block, BLKSIZE, offset) == -1) {
- *err = errno;
- nbdkit_error ("pwrite: %m");
- return -1;
- }
-
- if (next_ops->pwrite (nxdata, block, BLKSIZE, offset, flags, err) == -1)
- return -1;
-
- bitmap_set_blk (&bm, blknum, BLOCK_CLEAN);
-
- return 0;
-}
-
-/* Write a whole block.
- *
- * If the cache is in writethrough mode, or the FUA flag is set, then
- * this calls blk_writethrough above which will write both to the
- * cache and through to the underlying device.
- *
- * Otherwise it will only write to the cache.
- */
-static int
-blk_write (struct nbdkit_next_ops *next_ops, void *nxdata,
- uint64_t blknum, const uint8_t *block, uint32_t flags,
- int *err)
-{
- off_t offset;
-
- if (cache_mode == CACHE_MODE_WRITETHROUGH ||
- (cache_mode == CACHE_MODE_WRITEBACK && (flags & NBDKIT_FLAG_FUA)))
- return blk_writethrough (next_ops, nxdata, blknum, block, flags, err);
-
- offset = blknum * BLKSIZE;
-
- nbdkit_debug ("cache: writeback block %" PRIu64 " (offset %" PRIu64
")",
- blknum, (uint64_t) offset);
-
- if (pwrite (fd, block, BLKSIZE, offset) == -1) {
- *err = errno;
- nbdkit_error ("pwrite: %m");
- return -1;
- }
- bitmap_set_blk (&bm, blknum, BLOCK_DIRTY);
-
- return 0;
-}
-
/* Read data. */
static int
cache_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
@@ -489,57 +306,79 @@ cache_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
}
/* Flush: Go through all the dirty blocks, flushing them to disk. */
+struct flush_data {
+ uint8_t *block; /* bounce buffer */
+ unsigned errors; /* count of errors seen */
+ int first_errno; /* first errno seen */
+ struct nbdkit_next_ops *next_ops;
+ void *nxdata;
+};
+
+static int flush_dirty_block (uint64_t blknum, void *);
+
static int
cache_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
uint32_t flags, int *err)
{
- uint8_t *block = NULL;
- uint64_t blknum;
- enum bm_entry state;
- unsigned errors = 0;
+ struct flush_data data =
+ { .errors = 0, .first_errno = 0, .next_ops = next_ops, .nxdata = nxdata };
int tmp;
if (cache_mode == CACHE_MODE_UNSAFE)
return 0;
+ assert (!flags);
+
+ /* Allocate the bounce buffer. */
+ data.block = malloc (BLKSIZE);
+ if (data.block == NULL) {
+ *err = errno;
+ nbdkit_error ("malloc: %m");
+ return -1;
+ }
+
/* In theory if cache_mode == CACHE_MODE_WRITETHROUGH then there
* should be no dirty blocks. However we go through the cache here
* to be sure. Also we still need to issue the flush to the
* underlying storage.
*/
- assert (!flags);
- bitmap_for (&bm, blknum) {
- state = bitmap_get_blk (&bm, blknum, BLOCK_NOT_CACHED);
- if (state == BLOCK_DIRTY) {
- /* Lazily allocate the bounce buffer. */
- if (!block) {
- block = malloc (BLKSIZE);
- if (block == NULL) {
- *err = errno;
- nbdkit_error ("malloc: %m");
- return -1;
- }
- }
- /* Perform a read + writethrough which will read from the
- * cache and write it through to the underlying storage.
- */
- if (blk_read (next_ops, nxdata, blknum, block,
- errors ? &tmp : err) == -1 ||
- blk_writethrough (next_ops, nxdata, blknum, block, 0,
- errors ? &tmp : err) == -1) {
- nbdkit_error ("cache: flush of block %" PRIu64 " failed",
blknum);
- errors++;
- }
- }
- }
-
- free (block);
+ for_each_dirty_block (flush_dirty_block, &data);
+ free (data.block);
/* Now issue a flush request to the underlying storage. */
- if (next_ops->flush (nxdata, 0, errors ? &tmp : err) == -1)
- errors++;
+ if (next_ops->flush (nxdata, 0,
+ data.errors ? &tmp : &data.first_errno) == -1)
+ data.errors++;
- return errors == 0 ? 0 : -1;
+ if (data.errors > 0) {
+ *err = data.first_errno;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+flush_dirty_block (uint64_t blknum, void *datav)
+{
+ struct flush_data *data = datav;
+ int tmp;
+
+ /* Perform a read + writethrough which will read from the
+ * cache and write it through to the underlying storage.
+ */
+ if (blk_read (data->next_ops, data->nxdata, blknum, data->block,
+ data->errors ? &tmp : &data->first_errno) == -1)
+ goto err;
+ if (blk_writethrough (data->next_ops, data->nxdata, blknum, data->block, 0,
+ data->errors ? &tmp : &data->first_errno) == -1)
+ goto err;
+
+ return 0;
+
+ err:
+ nbdkit_error ("cache: flush of block %" PRIu64 " failed", blknum);
+ data->errors++;
+ return 0; /* continue scanning and flushing. */
}
static struct nbdkit_filter filter = {
diff --git a/filters/cache/Makefile.am b/filters/cache/Makefile.am
index 89f4396..0b79be5 100644
--- a/filters/cache/Makefile.am
+++ b/filters/cache/Makefile.am
@@ -37,7 +37,10 @@ EXTRA_DIST = nbdkit-cache-filter.pod
filter_LTLIBRARIES = nbdkit-cache-filter.la
nbdkit_cache_filter_la_SOURCES = \
+ blk.c \
+ blk.h \
cache.c \
+ cache.h \
$(top_srcdir)/include/nbdkit-filter.h
nbdkit_cache_filter_la_CPPFLAGS = \
--
2.19.2