Eric Blake
2022-Feb-24 15:36 UTC
[Libguestfs] [nbdkit PATCH v2 0/3] Saner --filter=blocksize defaults
In v2: - add debug statements at end of .config_complete, .prepare, .block_size - formatting cleanups - split old patch 1 into two patches: first, a mechanical rename into per-handle state with globals changed to config_*, second the deferral of setting defaults until .prepare - in patch 2, rename test-blocksize-wrap.sh to test-blocksize-default.sh, and actually test two different exports from one server Eric Blake (3): blocksize: Refactor to per-handle constraints blocksize: Defer per-handle initialization to .prepare blocksize: Default internal block size based on plugin filters/blocksize/nbdkit-blocksize-filter.pod | 14 +- tests/Makefile.am | 14 +- filters/blocksize/blocksize.c | 201 ++++++++++++------ tests/test-blocksize-default.sh | 88 ++++++++ tests/test-blocksize.sh | 3 - 5 files changed, 246 insertions(+), 74 deletions(-) create mode 100755 tests/test-blocksize-default.sh -- 2.35.1
Eric Blake
2022-Feb-24 15:36 UTC
[Libguestfs] [nbdkit PATCH v2 1/3] blocksize: Refactor to per-handle constraints
An upcoming patch wants to make the blocksize filter more responsive to the actual .block_size limits from the underlying plugin. But those limits can differ per export (for example, qemu can export multiple images, where one backed by a file has minblock 1, but another backed by encryption has minblock 512). To do that, we need to track block size constraints per handle, rather than globally. At this point, no functional change is intended other than three new debug lines; the change is merely the refactoring to get per-handle values set in .open (mostly mechanical renaming to easily distinguish .config defaults from the per-handle values, even though for now every handle has the same values). The next patches can then defer the defaulting of unspecified values to .prepare, where it can also take into account .block_size constraints. --- filters/blocksize/blocksize.c | 167 ++++++++++++++++++++++------------ 1 file changed, 107 insertions(+), 60 deletions(-) diff --git a/filters/blocksize/blocksize.c b/filters/blocksize/blocksize.c index 38b57c1e..e0420409 100644 --- a/filters/blocksize/blocksize.c +++ b/filters/blocksize/blocksize.c @@ -61,9 +61,17 @@ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; */ static char bounce[BLOCKSIZE_MIN_LIMIT]; -static unsigned int minblock; -static unsigned int maxdata; -static unsigned int maxlen; +/* Globals set by .config */ +static unsigned int config_minblock; +static unsigned int config_maxdata; +static unsigned int config_maxlen; + +/* Per-handle values set during .open */ +struct blocksize_handle { + uint32_t minblock; + uint32_t maxdata; + uint32_t maxlen; +}; static int blocksize_parse (const char *name, const char *s, unsigned int *v) @@ -91,11 +99,11 @@ blocksize_config (nbdkit_next_config *next, nbdkit_backend *nxdata, { if (strcmp (key, "minblock") == 0) - return blocksize_parse (key, value, &minblock); + return blocksize_parse (key, value, &config_minblock); if (strcmp (key, "maxdata") == 0) - return blocksize_parse (key, value, &maxdata); + return blocksize_parse (key, value, &config_maxdata); if (strcmp (key, "maxlen") == 0) - return blocksize_parse (key, value, &maxlen); + return blocksize_parse (key, value, &config_maxlen); return next (nxdata, key, value); } @@ -104,39 +112,41 @@ static int blocksize_config_complete (nbdkit_next_config_complete *next, nbdkit_backend *nxdata) { - if (minblock) { - if (!is_power_of_2 (minblock)) { + if (config_minblock) { + if (!is_power_of_2 (config_minblock)) { nbdkit_error ("minblock must be a power of 2"); return -1; } - if (minblock > BLOCKSIZE_MIN_LIMIT) { + if (config_minblock > BLOCKSIZE_MIN_LIMIT) { nbdkit_error ("minblock must not exceed %u", BLOCKSIZE_MIN_LIMIT); return -1; } } else - minblock = 1; + config_minblock = 1; - if (maxdata) { - if (maxdata & (minblock - 1)) { - nbdkit_error ("maxdata must be a multiple of %u", minblock); + if (config_maxdata) { + if (config_maxdata & (config_minblock - 1)) { + nbdkit_error ("maxdata must be a multiple of %u", config_minblock); return -1; } } - else if (maxlen) - maxdata = MIN (maxlen, 64 * 1024 * 1024); + else if (config_maxlen) + config_maxdata = MIN (config_maxlen, 64 * 1024 * 1024); else - maxdata = 64 * 1024 * 1024; + config_maxdata = 64 * 1024 * 1024; - if (maxlen) { - if (maxlen & (minblock - 1)) { - nbdkit_error ("maxlen must be a multiple of %u", minblock); + if (config_maxlen) { + if (config_maxlen & (config_minblock - 1)) { + nbdkit_error ("maxlen must be a multiple of %u", config_minblock); return -1; } } else - maxlen = -minblock; + config_maxlen = -config_minblock; + nbdkit_debug ("configured values minblock=%u maxdata=%u maxlen=%u", + config_minblock, config_maxdata, config_maxlen); return next (nxdata); } @@ -145,16 +155,40 @@ blocksize_config_complete (nbdkit_next_config_complete *next, "maxdata=<SIZE> Maximum size for read/write (default 64M).\n" \ "maxlen=<SIZE> Maximum size for trim/zero (default 4G-minblock)." +static void * +blocksize_open (nbdkit_next_open *next, nbdkit_context *nxdata, + int readonly, const char *exportname, int is_tls) +{ + struct blocksize_handle *h; + + if (next (nxdata, readonly, exportname) == -1) + return NULL; + + h = malloc (sizeof *h); + if (h == NULL) { + nbdkit_error ("malloc: %m"); + return NULL; + } + + h->minblock = config_minblock; + h->maxdata = config_maxdata; + h->maxlen = config_maxlen; + nbdkit_debug ("handle values minblock=%u maxdata=%u maxlen=%u", + h->minblock, h->maxdata, h->maxlen); + return h; +} + /* Round size down to avoid issues at end of file. */ static int64_t blocksize_get_size (nbdkit_next *next, void *handle) { + struct blocksize_handle *h = handle; int64_t size = next->get_size (next); if (size == -1) return -1; - return ROUND_DOWN (size, minblock); + return ROUND_DOWN (size, h->minblock); } /* Block size constraints. @@ -168,15 +202,19 @@ static int blocksize_block_size (nbdkit_next *next, void *handle, uint32_t *minimum, uint32_t *preferred, uint32_t *maximum) { + struct blocksize_handle *h = handle; + if (next->block_size (next, minimum, preferred, maximum) == -1) return -1; if (*preferred == 0) - *preferred = MAX (4096, minblock); + *preferred = MAX (4096, h->minblock); *minimum = 1; *maximum = 0xffffffff; + nbdkit_debug ("advertising min=%" PRIu32 " pref=%" PRIu32 " max=%" PRIu32, + *minimum, *preferred, *maximum); return 0; } @@ -185,16 +223,17 @@ blocksize_pread (nbdkit_next *next, void *handle, void *b, uint32_t count, uint64_t offs, uint32_t flags, int *err) { + struct blocksize_handle *h = handle; char *buf = b; uint32_t keep; uint32_t drop; /* Unaligned head */ - if (offs & (minblock - 1)) { + if (offs & (h->minblock - 1)) { ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock); - drop = offs & (minblock - 1); - keep = MIN (minblock - drop, count); - if (next->pread (next, bounce, minblock, offs - drop, flags, err) == -1) + drop = offs & (h->minblock - 1); + keep = MIN (h->minblock - drop, count); + if (next->pread (next, bounce, h->minblock, offs - drop, flags, err) == -1) return -1; memcpy (buf, bounce + drop, keep); buf += keep; @@ -203,8 +242,8 @@ blocksize_pread (nbdkit_next *next, } /* Aligned body */ - while (count >= minblock) { - keep = MIN (maxdata, ROUND_DOWN (count, minblock)); + while (count >= h->minblock) { + keep = MIN (h->maxdata, ROUND_DOWN (count, h->minblock)); if (next->pread (next, buf, keep, offs, flags, err) == -1) return -1; buf += keep; @@ -215,7 +254,7 @@ blocksize_pread (nbdkit_next *next, /* Unaligned tail */ if (count) { ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock); - if (next->pread (next, bounce, minblock, offs, flags, err) == -1) + if (next->pread (next, bounce, h->minblock, offs, flags, err) == -1) return -1; memcpy (buf, bounce, count); } @@ -228,6 +267,7 @@ blocksize_pwrite (nbdkit_next *next, void *handle, const void *b, uint32_t count, uint64_t offs, uint32_t flags, int *err) { + struct blocksize_handle *h = handle; const char *buf = b; uint32_t keep; uint32_t drop; @@ -240,14 +280,14 @@ blocksize_pwrite (nbdkit_next *next, } /* Unaligned head */ - if (offs & (minblock - 1)) { + if (offs & (h->minblock - 1)) { ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock); - drop = offs & (minblock - 1); - keep = MIN (minblock - drop, count); - if (next->pread (next, bounce, minblock, offs - drop, 0, err) == -1) + drop = offs & (h->minblock - 1); + keep = MIN (h->minblock - drop, count); + if (next->pread (next, bounce, h->minblock, offs - drop, 0, err) == -1) return -1; memcpy (bounce + drop, buf, keep); - if (next->pwrite (next, bounce, minblock, offs - drop, flags, err) == -1) + if (next->pwrite (next, bounce, h->minblock, offs - drop, flags, err) == -1) return -1; buf += keep; offs += keep; @@ -255,8 +295,8 @@ blocksize_pwrite (nbdkit_next *next, } /* Aligned body */ - while (count >= minblock) { - keep = MIN (maxdata, ROUND_DOWN (count, minblock)); + while (count >= h->minblock) { + keep = MIN (h->maxdata, ROUND_DOWN (count, h->minblock)); if (next->pwrite (next, buf, keep, offs, flags, err) == -1) return -1; buf += keep; @@ -267,10 +307,10 @@ blocksize_pwrite (nbdkit_next *next, /* Unaligned tail */ if (count) { ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock); - if (next->pread (next, bounce, minblock, offs, 0, err) == -1) + if (next->pread (next, bounce, h->minblock, offs, 0, err) == -1) return -1; memcpy (bounce, buf, count); - if (next->pwrite (next, bounce, minblock, offs, flags, err) == -1) + if (next->pwrite (next, bounce, h->minblock, offs, flags, err) == -1) return -1; } @@ -284,6 +324,7 @@ blocksize_trim (nbdkit_next *next, void *handle, uint32_t count, uint64_t offs, uint32_t flags, int *err) { + struct blocksize_handle *h = handle; uint32_t keep; bool need_flush = false; @@ -294,18 +335,18 @@ blocksize_trim (nbdkit_next *next, } /* Ignore unaligned head */ - if (offs & (minblock - 1)) { - keep = MIN (minblock - (offs & (minblock - 1)), count); + if (offs & (h->minblock - 1)) { + keep = MIN (h->minblock - (offs & (h->minblock - 1)), count); offs += keep; count -= keep; } /* Ignore unaligned tail */ - count = ROUND_DOWN (count, minblock); + count = ROUND_DOWN (count, h->minblock); /* Aligned body */ while (count) { - keep = MIN (maxlen, count); + keep = MIN (h->maxlen, count); if (next->trim (next, keep, offs, flags, err) == -1) return -1; offs += keep; @@ -322,6 +363,7 @@ blocksize_zero (nbdkit_next *next, void *handle, uint32_t count, uint64_t offs, uint32_t flags, int *err) { + struct blocksize_handle *h = handle; uint32_t keep; uint32_t drop; bool need_flush = false; @@ -332,7 +374,7 @@ blocksize_zero (nbdkit_next *next, * calls; it's easier to just declare that anything that can't be * done in one call to the plugin is not fast. */ - if ((offs | count) & (minblock - 1) || count > maxlen) { + if ((offs | count) & (h->minblock - 1) || count > h->maxlen) { *err = ENOTSUP; return -1; } @@ -345,14 +387,14 @@ blocksize_zero (nbdkit_next *next, } /* Unaligned head */ - if (offs & (minblock - 1)) { + if (offs & (h->minblock - 1)) { ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock); - drop = offs & (minblock - 1); - keep = MIN (minblock - drop, count); - if (next->pread (next, bounce, minblock, offs - drop, 0, err) == -1) + drop = offs & (h->minblock - 1); + keep = MIN (h->minblock - drop, count); + if (next->pread (next, bounce, h->minblock, offs - drop, 0, err) == -1) return -1; memset (bounce + drop, 0, keep); - if (next->pwrite (next, bounce, minblock, offs - drop, + if (next->pwrite (next, bounce, h->minblock, offs - drop, flags & ~NBDKIT_FLAG_MAY_TRIM, err) == -1) return -1; offs += keep; @@ -360,8 +402,8 @@ blocksize_zero (nbdkit_next *next, } /* Aligned body */ - while (count >= minblock) { - keep = MIN (maxlen, ROUND_DOWN (count, minblock)); + while (count >= h->minblock) { + keep = MIN (h->maxlen, ROUND_DOWN (count, h->minblock)); if (next->zero (next, keep, offs, flags, err) == -1) return -1; offs += keep; @@ -371,10 +413,10 @@ blocksize_zero (nbdkit_next *next, /* Unaligned tail */ if (count) { ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock); - if (next->pread (next, bounce, minblock, offs, 0, err) == -1) + if (next->pread (next, bounce, h->minblock, offs, 0, err) == -1) return -1; memset (bounce, 0, count); - if (next->pwrite (next, bounce, minblock, offs, + if (next->pwrite (next, bounce, h->minblock, offs, flags & ~NBDKIT_FLAG_MAY_TRIM, err) == -1) return -1; } @@ -395,20 +437,22 @@ blocksize_extents (nbdkit_next *next, * fine to return less than the full count as long as we're making * progress. */ + struct blocksize_handle *h = handle; CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL; size_t i; struct nbdkit_extent e; - extents2 = nbdkit_extents_new (ROUND_DOWN (offset, minblock), - ROUND_UP (offset + count, minblock)); + extents2 = nbdkit_extents_new (ROUND_DOWN (offset, h->minblock), + ROUND_UP (offset + count, h->minblock)); if (extents2 == NULL) { *err = errno; return -1; } - if (nbdkit_extents_aligned (next, MIN (ROUND_UP (count, minblock), maxlen), - ROUND_DOWN (offset, minblock), flags, minblock, - extents2, err) == -1) + if (nbdkit_extents_aligned (next, MIN (ROUND_UP (count, h->minblock), + h->maxlen), + ROUND_DOWN (offset, h->minblock), flags, + h->minblock, extents2, err) == -1) return -1; for (i = 0; i < nbdkit_extents_count (extents2); ++i) { @@ -426,20 +470,21 @@ blocksize_cache (nbdkit_next *next, void *handle, uint32_t count, uint64_t offs, uint32_t flags, int *err) { + struct blocksize_handle *h = handle; uint32_t limit; uint64_t remaining = count; /* Rounding out could exceed 32 bits */ /* Unaligned head */ - limit = offs & (minblock - 1); + limit = offs & (h->minblock - 1); remaining += limit; offs -= limit; /* Unaligned tail */ - remaining = ROUND_UP (remaining, minblock); + remaining = ROUND_UP (remaining, h->minblock); /* Aligned body */ while (remaining) { - limit = MIN (maxdata, remaining); + limit = MIN (h->maxdata, remaining); if (next->cache (next, limit, offs, flags, err) == -1) return -1; offs += limit; @@ -455,6 +500,8 @@ static struct nbdkit_filter filter = { .config = blocksize_config, .config_complete = blocksize_config_complete, .config_help = blocksize_config_help, + .open = blocksize_open, + .close = free, .get_size = blocksize_get_size, .block_size = blocksize_block_size, .pread = blocksize_pread, -- 2.35.1
Eric Blake
2022-Feb-24 15:36 UTC
[Libguestfs] [nbdkit PATCH v2 2/3] blocksize: Defer per-handle initialization to .prepare
Now that we can track block size constraints per handle, it makes more sense to initialize parameters during .prepare (where the next patch will also take into consideration next->block_size values), than it is to supply values for unspecified globals during .config. This has no logical change, but is intentionally separate from the renaming churn of the previous patch. --- filters/blocksize/blocksize.c | 39 ++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/filters/blocksize/blocksize.c b/filters/blocksize/blocksize.c index e0420409..bca9fb73 100644 --- a/filters/blocksize/blocksize.c +++ b/filters/blocksize/blocksize.c @@ -66,7 +66,7 @@ static unsigned int config_minblock; static unsigned int config_maxdata; static unsigned int config_maxlen; -/* Per-handle values set during .open */ +/* Per-handle values set during .prepare */ struct blocksize_handle { uint32_t minblock; uint32_t maxdata; @@ -122,28 +122,20 @@ blocksize_config_complete (nbdkit_next_config_complete *next, return -1; } } - else - config_minblock = 1; - if (config_maxdata) { + if (config_maxdata && config_minblock) { if (config_maxdata & (config_minblock - 1)) { nbdkit_error ("maxdata must be a multiple of %u", config_minblock); return -1; } } - else if (config_maxlen) - config_maxdata = MIN (config_maxlen, 64 * 1024 * 1024); - else - config_maxdata = 64 * 1024 * 1024; - if (config_maxlen) { + if (config_maxlen && config_minblock) { if (config_maxlen & (config_minblock - 1)) { nbdkit_error ("maxlen must be a multiple of %u", config_minblock); return -1; } } - else - config_maxlen = -config_minblock; nbdkit_debug ("configured values minblock=%u maxdata=%u maxlen=%u", config_minblock, config_maxdata, config_maxlen); @@ -173,9 +165,31 @@ blocksize_open (nbdkit_next_open *next, nbdkit_context *nxdata, h->minblock = config_minblock; h->maxdata = config_maxdata; h->maxlen = config_maxlen; + return h; +} + +static int +blocksize_prepare (nbdkit_next *next, void *handle, + int readonly) +{ + struct blocksize_handle *h = handle; + + if (h->minblock == 0) + h->minblock = 1; + + if (h->maxdata == 0) { + if (h->maxlen) + h->maxdata = MIN (h->maxlen, 64 * 1024 * 1024); + else + h->maxdata = 64 * 1024 * 1024; + } + + if (h->maxlen == 0) + h->maxlen = -h->minblock; + nbdkit_debug ("handle values minblock=%u maxdata=%u maxlen=%u", h->minblock, h->maxdata, h->maxlen); - return h; + return 0; } /* Round size down to avoid issues at end of file. */ @@ -501,6 +515,7 @@ static struct nbdkit_filter filter = { .config_complete = blocksize_config_complete, .config_help = blocksize_config_help, .open = blocksize_open, + .prepare = blocksize_prepare, .close = free, .get_size = blocksize_get_size, .block_size = blocksize_block_size, -- 2.35.1
Eric Blake
2022-Feb-24 15:36 UTC
[Libguestfs] [nbdkit PATCH v2 3/3] blocksize: Default internal block size based on plugin
Now that we have .block_size, if the plugin reports specific block constraints, honor those by default rather than requiring the user to duplicate work by passing in extra filter parameters on the command line. The TODO in test-blocksize.sh is no longer relevant (we now advertise block size, so modern qemu no longer does read-modify-write), but that test is still valuable as written, so the real coverage of the new default behavior is in a new test. --- filters/blocksize/nbdkit-blocksize-filter.pod | 14 ++- tests/Makefile.am | 14 ++- filters/blocksize/blocksize.c | 19 +++- tests/test-blocksize-default.sh | 88 +++++++++++++++++++ tests/test-blocksize.sh | 3 - 5 files changed, 124 insertions(+), 14 deletions(-) create mode 100755 tests/test-blocksize-default.sh diff --git a/filters/blocksize/nbdkit-blocksize-filter.pod b/filters/blocksize/nbdkit-blocksize-filter.pod index 5df9e719..4a1ebded 100644 --- a/filters/blocksize/nbdkit-blocksize-filter.pod +++ b/filters/blocksize/nbdkit-blocksize-filter.pod @@ -23,6 +23,9 @@ refuses a read or write larger than 64 megabytes, while many other NBD servers limit things to 32 megabytes). The blocksize filter can be used to modify the client requests to meet the plugin restrictions. +This filter can be combined with L<nbdkit-blocksize-policy-filter(1)> +to advertise different block sizes to the client. + =head1 PARAMETERS The nbdkit-blocksize-filter accepts the following parameters. @@ -33,7 +36,8 @@ The nbdkit-blocksize-filter accepts the following parameters. The minimum block size and alignment to pass to the plugin. This must be a power of two, and no larger than 64k. If omitted, this defaults -to 1 (that is, no minimum size restrictions). The filter rounds up +to the minimum block size of the underlying plugin, or 1 if the plugin +did not report a minimum block size. The filter rounds up read requests to alignment boundaries, performs read-modify-write cycles for any unaligned head or tail of a write or zero request, and silently ignores any unaligned head or tail of a trim request. The @@ -47,8 +51,9 @@ This parameter understands the suffix 'k' for 1024. =item B<maxdata=>SIZE The maximum block size for any single transaction with data (read and -write). If omitted, this defaults to 64 megabytes (that is, the -nbdkit maximum). This need not be a power of two, but must be an +write). If omitted, this defaults to the minimum of 64 megabytes (that +is, the nbdkit maximum) or any maximum reported by the underlying plugin. +This need not be a power of two, but must be an integer multiple of C<minblock>. The filter fragments any larger client request into multiple plugin requests. @@ -113,6 +118,7 @@ L<nbdkit(1)>, L<nbdkit-nbd-plugin(1)>, L<nbdkit-vddk-plugin(1)>, L<nbdkit-filter(3)>, +L<nbdkit-blocksize-policy-filter(1)>, L<nbdkit-truncate-filter(1)>. =head1 AUTHORS @@ -121,4 +127,4 @@ Eric Blake =head1 COPYRIGHT -Copyright (C) 2018 Red Hat Inc. +Copyright (C) 2018-2022 Red Hat Inc. diff --git a/tests/Makefile.am b/tests/Makefile.am index dd3b4ded..77d13dc9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ # nbdkit -# Copyright (C) 2013-2021 Red Hat Inc. +# Copyright (C) 2013-2022 Red Hat Inc. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are @@ -1408,8 +1408,16 @@ test_layers_filter3_la_LDFLAGS = \ test_layers_filter3_la_LIBADD = $(IMPORT_LIBRARY_ON_WINDOWS) # blocksize filter test. -TESTS += test-blocksize.sh test-blocksize-extents.sh -EXTRA_DIST += test-blocksize.sh test-blocksize-extents.sh +TESTS += \ + test-blocksize.sh \ + test-blocksize-extents.sh \ + test-blocksize-default.sh \ + $(NULL) +EXTRA_DIST += \ + test-blocksize.sh \ + test-blocksize-extents.sh \ + test-blocksize-default.sh \ + $(NULL) # blocksize-policy filter test. TESTS += test-blocksize-policy.sh test-blocksize-error-policy.sh diff --git a/filters/blocksize/blocksize.c b/filters/blocksize/blocksize.c index bca9fb73..03da4971 100644 --- a/filters/blocksize/blocksize.c +++ b/filters/blocksize/blocksize.c @@ -173,9 +173,15 @@ blocksize_prepare (nbdkit_next *next, void *handle, int readonly) { struct blocksize_handle *h = handle; + uint32_t minimum, preferred, maximum; - if (h->minblock == 0) - h->minblock = 1; + /* Here, minimum and maximum will clamp per-handle defaults not set + * by globals in .config; preferred has no impact until .block_size. + */ + if (next->block_size (next, &minimum, &preferred, &maximum) == -1) + return -1; + + h->minblock = MAX (MAX (h->minblock, 1), minimum); if (h->maxdata == 0) { if (h->maxlen) @@ -183,9 +189,14 @@ blocksize_prepare (nbdkit_next *next, void *handle, else h->maxdata = 64 * 1024 * 1024; } + if (maximum) + h->maxdata = MIN (h->maxdata, maximum); + h->maxdata = ROUND_DOWN (h->maxdata, h->minblock); if (h->maxlen == 0) h->maxlen = -h->minblock; + else + h->maxlen = ROUND_DOWN (h->maxlen, h->minblock); nbdkit_debug ("handle values minblock=%u maxdata=%u maxlen=%u", h->minblock, h->maxdata, h->maxlen); @@ -218,11 +229,11 @@ blocksize_block_size (nbdkit_next *next, void *handle, { struct blocksize_handle *h = handle; + /* Here we only need preferred; see also blocksize_prepare. */ if (next->block_size (next, minimum, preferred, maximum) == -1) return -1; - if (*preferred == 0) - *preferred = MAX (4096, h->minblock); + *preferred = MAX (MAX (*preferred, 4096), h->minblock); *minimum = 1; *maximum = 0xffffffff; diff --git a/tests/test-blocksize-default.sh b/tests/test-blocksize-default.sh new file mode 100755 index 00000000..738b9c00 --- /dev/null +++ b/tests/test-blocksize-default.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# nbdkit +# Copyright (C) 2019-2022 Red Hat Inc. +# +# 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. + +source ./functions.sh +set -e +set -x + +requires_plugin eval +requires nbdsh -c 'print(h.get_block_size)' + +# Create an nbdkit eval plugin which presents per-export block size +# constraints based on the export name. +# Check that the blocksize filter advertises full access, that size is +# truncated to minblock constraints, and underlying plugin access uses +# read-modify-write at appropriate boundaries. +export script=' +import os + +sock = os.environ["unixsocket"] + +ha = h; +ha.set_export_name("a") +ha.connect_unix(sock) +hb = nbd.NBD() +hb.set_export_name("b") +hb.connect_unix(sock) + +assert ha.get_size() == 2 * 1024 * 1024 - 1 * 1024 +assert ha.get_block_size(nbd.SIZE_MINIMUM) == 1 +assert ha.get_block_size(nbd.SIZE_PREFERRED) == 128 * 1024 +assert ha.get_block_size(nbd.SIZE_MAXIMUM) == 4 * 1024 * 1024 * 1024 - 1 +assert ha.pread(1024 * 1024 + 1, 1) == b"\0" * (1024 * 1024 + 1) + +assert hb.get_size() == 2 * 1024 * 1024 - 2 * 1024 +assert hb.get_block_size(nbd.SIZE_MINIMUM) == 1 +assert hb.get_block_size(nbd.SIZE_PREFERRED) == 64 * 1024 +assert hb.get_block_size(nbd.SIZE_MAXIMUM) == 4 * 1024 * 1024 * 1024 - 1 +assert hb.pread(1024 * 1024 + 1, 1) == b"\0" * (1024 * 1024 + 1) + +ha.shutdown() +hb.shutdown() +' +nbdkit -v -U - --filter=blocksize eval \ + open='echo $3' \ + get_size="echo $((2 * 1024 * 1024 - 1))" \ + block_size='case $2 in + a) echo 1K 128K 512K ;; + b) echo 2K 64K 1M ;; + *) echo unknown export name >&2; exit 1 ;; + esac' \ + pread='case $2.$3.$4 in + a.$((1*1024)).0 | a.$((512*1024)).$((1*1024)) | \ + a.$((511*1024)).$((513*1024)) | a.$((1*1024)).$((1024*1024)) | \ + b.$((2*1024)).0 | b.$((1022*1024)).$((2*1024)) | \ + b.$((2*1024)).$((1024*1024)) ) + dd if=/dev/zero count=$3 iflag=count_bytes ;; + *) echo EIO >&2; exit 1 ;; + esac' \ + --run 'export unixsocket; nbdsh -c "$script"' diff --git a/tests/test-blocksize.sh b/tests/test-blocksize.sh index 64196bb8..ee325eff 100755 --- a/tests/test-blocksize.sh +++ b/tests/test-blocksize.sh @@ -42,9 +42,6 @@ files="blocksize1.img blocksize1.log $sock1 blocksize1.pid rm -f $files # Prep images, and check that qemu-io understands the actions we plan on doing. -# TODO: Until we implement NBD_INFO_BLOCK_SIZE, qemu-io does its own -# read-modify-write at 512-byte alignment, while we'd like to ultimately test -# 1-byte accesses truncate -s 10M blocksize1.img if ! qemu-io -f raw -c 'r 0 1' -c 'w -z 1000 2000' \ -c 'w -P 0 1M 2M' -c 'discard 3M 4M' blocksize1.img; then -- 2.35.1