This plugin now implements efficient zeroing, pre-fetch and extents,
which should give quite a performance boost.
XXX
On the flip side, it no longer ignores flush and FUA (which we can
ignore because these are temporary disks), which very likely has a
large negative impact on performance. Fixing this would involve
generalising fileops a little.
---
plugins/tmpdisk/Makefile.am | 2 +
plugins/tmpdisk/tmpdisk.c | 247 +++++-------------------------------
2 files changed, 32 insertions(+), 217 deletions(-)
diff --git a/plugins/tmpdisk/Makefile.am b/plugins/tmpdisk/Makefile.am
index 20aeb05f..471a0f6a 100644
--- a/plugins/tmpdisk/Makefile.am
+++ b/plugins/tmpdisk/Makefile.am
@@ -57,6 +57,7 @@ nbdkit_tmpdisk_plugin_la_SOURCES = \
nbdkit_tmpdisk_plugin_la_CPPFLAGS = \
-I$(top_srcdir)/include \
+ -I$(top_srcdir)/common/fileops \
-I$(top_srcdir)/common/utils \
$(NULL)
nbdkit_tmpdisk_plugin_la_CFLAGS = $(WARNINGS_CFLAGS)
@@ -65,6 +66,7 @@ nbdkit_tmpdisk_plugin_la_LDFLAGS = \
-Wl,--version-script=$(top_srcdir)/plugins/plugins.syms \
$(NULL)
nbdkit_tmpdisk_plugin_la_LIBADD = \
+ $(top_builddir)/common/fileops/libfileops.la \
$(top_builddir)/common/utils/libutils.la \
$(NULL)
diff --git a/plugins/tmpdisk/tmpdisk.c b/plugins/tmpdisk/tmpdisk.c
index 6d8efc08..23f335ac 100644
--- a/plugins/tmpdisk/tmpdisk.c
+++ b/plugins/tmpdisk/tmpdisk.c
@@ -43,13 +43,13 @@
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <sys/wait.h>
#define NBDKIT_API_VERSION 2
#include <nbdkit-plugin.h>
#include "cleanup.h"
+#include "fileops.h"
#include "utils.h"
static const char *tmpdir = "/var/tmp";
@@ -153,12 +153,6 @@ tmpdisk_config_complete (void)
"type=ext4|... The filesystem type.\n" \
"command=<COMMAND> Alternate command instead of mkfs."
-struct handle {
- int fd;
- int64_t size;
- bool can_punch_hole;
-};
-
/* Multi-conn is absolutely unsafe! In this callback it is simply
* returning the default value (no multi-conn), that's to make it
* clear for future authors.
@@ -169,33 +163,6 @@ tmpdisk_can_multi_conn (void *handle)
return 0;
}
-static int
-tmpdisk_can_trim (void *handle)
-{
-#ifdef FALLOC_FL_PUNCH_HOLE
- return 1;
-#else
- return 0;
-#endif
-}
-
-/* Pretend we have native FUA support, but actually because all disks
- * are temporary we will deliberately ignore flush/FUA operations.
- */
-static int
-tmpdisk_can_fua (void *handle)
-{
- return NBDKIT_FUA_NATIVE;
-}
-
-static int64_t
-tmpdisk_get_size (void *handle)
-{
- struct handle *h = handle;
-
- return h->size;
-}
-
/* This creates and runs the full "mkfs" (or whatever) command. */
static int
run_command (const char *disk)
@@ -263,37 +230,18 @@ run_command (const char *disk)
return 0;
}
-/* For block devices, stat->st_size is not the true size. */
-static int64_t
-block_device_size (int fd)
-{
- off_t size;
-
- size = lseek (fd, 0, SEEK_END);
- if (size == -1) {
- nbdkit_error ("lseek: %m");
- return -1;
- }
-
- return size;
-}
-
static void *
tmpdisk_open (int readonly)
{
- struct handle *h;
+ struct fileops *fops;
CLEANUP_FREE char *dir = NULL, *disk = NULL;
- int flags;
- struct stat statbuf;
+ int fd, flags;
- h = malloc (sizeof *h);
- if (h == NULL) {
+ fops = malloc (sizeof *fops);
+ if (fops == NULL) {
nbdkit_error ("malloc: %m");
- goto error;
+ return NULL;
}
- h->fd = -1;
- h->size = -1;
- h->can_punch_hole = true;
/* For security reasons we have to create a temporary directory
* under tmpdir that only the current user can access. If we
@@ -303,20 +251,20 @@ tmpdisk_open (int readonly)
*/
if (asprintf (&dir, "%s/tmpdiskXXXXXX", tmpdir) == -1) {
nbdkit_error ("asprintf: %m");
- goto error;
+ goto error0;
}
if (mkdtemp (dir) == NULL) {
nbdkit_error ("%s: %m", dir);
- goto error;
+ goto error0;
}
if (asprintf (&disk, "%s/disk", dir) == -1) {
nbdkit_error ("asprintf: %m");
- goto error;
+ goto error1;
}
/* Now run the mkfs command. */
if (run_command (disk) == -1)
- goto error;
+ goto error2;
/* The external command must have created the disk, and then we must
* find the true size.
@@ -325,29 +273,14 @@ tmpdisk_open (int readonly)
flags = O_RDONLY | O_CLOEXEC;
else
flags = O_RDWR | O_CLOEXEC;
- h->fd = open (disk, flags);
- if (h->fd == -1) {
+ fd = open (disk, flags);
+ if (fd == -1) {
nbdkit_error ("open: %s: %m", disk);
- goto error;
+ goto error2;
}
- if (fstat (h->fd, &statbuf) == -1) {
- nbdkit_error ("fstat: %s: %m", disk);
- goto error;
- }
-
- /* The command could set $disk to a regular file or a block device
- * (or a symlink to either), so we must check that here.
- */
- if (S_ISBLK (statbuf.st_mode)) {
- h->size = block_device_size (h->fd);
- if (h->size == -1)
- goto error;
- }
- else /* Regular file. */
- h->size = statbuf.st_size;
- nbdkit_debug ("tmpdisk: requested_size = %" PRIi64 ", size = %"
PRIi64,
- requested_size, h->size);
+ if (init_fileops (fd, fops) == -1)
+ goto error3;
/* We don't need the disk to appear in the filesystem since we hold
* a file descriptor and access it through that, so unlink the disk.
@@ -357,139 +290,26 @@ tmpdisk_open (int readonly)
rmdir (dir);
/* Return the handle. */
- return h;
+ return fops;
- error:
- if (h) {
- if (h->fd >= 0) {
- close (h->fd);
- unlink (disk);
- }
- free (h);
- }
+ error3:
+ close (fd);
+ error2:
+ unlink (disk);
+ error1:
+ rmdir (dir);
+ error0:
+ free (fops);
return NULL;
}
static void
tmpdisk_close (void *handle)
{
- struct handle *h = handle;
+ struct fileops *fops = handle;
- close (h->fd);
- free (h);
-}
-
-/* Read data from the file. */
-static int
-tmpdisk_pread (void *handle, void *buf,
- uint32_t count, uint64_t offset,
- uint32_t flags)
-{
- struct handle *h = handle;
-
- while (count > 0) {
- ssize_t r = pread (h->fd, buf, count, offset);
- if (r == -1) {
- nbdkit_error ("pread: %m");
- return -1;
- }
- if (r == 0) {
- nbdkit_error ("pread: unexpected end of file");
- return -1;
- }
- buf += r;
- count -= r;
- offset += r;
- }
-
- return 0;
-}
-
-/* Write data to the file. */
-static int
-tmpdisk_pwrite (void *handle, const void *buf,
- uint32_t count, uint64_t offset,
- uint32_t flags)
-{
- struct handle *h = handle;
-
- while (count > 0) {
- ssize_t r = pwrite (h->fd, buf, count, offset);
- if (r == -1) {
- nbdkit_error ("pwrite: %m");
- return -1;
- }
- buf += r;
- count -= r;
- offset += r;
- }
-
- /* Deliberately ignore FUA if present in flags. */
-
- return 0;
-}
-
-/* This plugin deliberately provides a null flush operation, because
- * all of the disks created are temporary.
- */
-static int
-tmpdisk_flush (void *handle, uint32_t flags)
-{
- return 0;
-}
-
-#if defined (FALLOC_FL_PUNCH_HOLE)
-static int
-do_fallocate (int fd, int mode, off_t offset, off_t len)
-{
- int r = fallocate (fd, mode, offset, len);
- if (r == -1 && errno == ENODEV) {
- /* kernel 3.10 fails with ENODEV for block device. Kernel >= 4.9 fails
- * with EOPNOTSUPP in this case. Normalize errno to simplify callers.
- */
- errno = EOPNOTSUPP;
- }
- return r;
-}
-
-static bool
-is_enotsup (int err)
-{
- return err == ENOTSUP || err == EOPNOTSUPP;
-}
-#endif
-
-/* Punch a hole in the file. */
-static int
-tmpdisk_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
-{
-#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 (is_enotsup (EOPNOTSUPP))
- h->can_punch_hole = false;
-
- nbdkit_debug ("ignoring failed fallocate during trim: %m");
- }
- }
-#endif
-
- /* Deliberately ignore FUA if present in flags. */
-
- return 0;
+ close_fileops (fops);
+ free (fops);
}
#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
@@ -505,19 +325,12 @@ static struct nbdkit_plugin plugin = {
.config_help = tmpdisk_config_help,
.magic_config_key = "size",
- .can_multi_conn = tmpdisk_can_multi_conn,
- .can_trim = tmpdisk_can_trim,
- .can_fua = tmpdisk_can_fua,
- .get_size = tmpdisk_get_size,
-
.open = tmpdisk_open,
.close = tmpdisk_close,
- .pread = tmpdisk_pread,
- .pwrite = tmpdisk_pwrite,
- .flush = tmpdisk_flush,
- .trim = tmpdisk_trim,
+ .can_multi_conn = tmpdisk_can_multi_conn,
- .errno_is_preserved = 1,
+ /* Data serving is implemented in common/fileops/fileops.c */
+ FILEOPS_READ_WRITE_CALLBACKS
};
NBDKIT_REGISTER_PLUGIN(plugin)
--
2.25.0