Either if we are querying an extent larger than ~ 4G, or more simply if the server returns adjacent extents of the same type, nbdinfo --map will now coalesce them which makes the output more readable. This also adjust an existing test and adds new tests. I didn't feel there was any need to add a "no-coalesce" option (although one certainly could be added) for a couple of reasons: (1) nbdinfo is splitting some large requests itself. (2) If you really want this information you can use the libnbd API. --- info/Makefile.am | 2 + info/info-map-base-allocation-large.sh | 7 ++- info/info-map-base-allocation-weird.sh | 49 +++++++++++++++ info/info-map-base-allocation-zero.sh | 37 +++++++++++ info/nbdinfo.c | 85 ++++++++++++++++++-------- 5 files changed, 150 insertions(+), 30 deletions(-) diff --git a/info/Makefile.am b/info/Makefile.am index 8303658..c3ab780 100644 --- a/info/Makefile.am +++ b/info/Makefile.am @@ -32,6 +32,8 @@ info_sh_files = \ info-map-base-allocation.sh \ info-map-base-allocation-json.sh \ info-map-base-allocation-large.sh \ + info-map-base-allocation-weird.sh \ + info-map-base-allocation-zero.sh \ info-map-qemu-dirty-bitmap.sh \ info-atomic-output.sh \ $(NULL) diff --git a/info/info-map-base-allocation-large.sh b/info/info-map-base-allocation-large.sh index 373a974..7d87368 100755 --- a/info/info-map-base-allocation-large.sh +++ b/info/info-map-base-allocation-large.sh @@ -30,7 +30,7 @@ rm -f $out # The sparse allocator used by nbdkit-data-plugin uses a 32K page # size, and extents are always aligned with this. -nbdkit -U - data data='1 @131072 2' size=6G \ +nbdkit -U - data data='1 @131072 2 @6442450944 3' size=8G \ --run '$VG nbdinfo --map "$uri"' > $out cat $out @@ -38,8 +38,9 @@ cat $out if [ "$(tr -s ' ' < $out)" != " 0 32768 0 allocated 32768 98304 3 hole,zero 131072 32768 0 allocated - 163840 4294803456 3 hole,zero -4294967296 2147483648 3 hole,zero" ]; then + 163840 6442287104 3 hole,zero +6442450944 32768 0 allocated +6442483712 2147450880 3 hole,zero" ]; then echo "$0: unexpected output from nbdinfo --map" exit 1 fi diff --git a/info/info-map-base-allocation-weird.sh b/info/info-map-base-allocation-weird.sh new file mode 100755 index 0000000..c766ebf --- /dev/null +++ b/info/info-map-base-allocation-weird.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# nbd client library in userspace +# Copyright (C) 2020 Red Hat Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +. ../tests/functions.sh + +set -e +set -x + +requires nbdkit --version +requires nbdkit sh --version +requires tr --version + +out=info-base-allocation-weird.out +cleanup_fn rm -f $out +rm -f $out + +# This is a "weird" server that returns extents that are all 1 byte. +nbdkit -U - sh - \ + --run '$VG nbdinfo --map "$uri"' > $out <<'EOF' +case "$1" in + get_size) echo 32 ;; + pread) dd if=/dev/zero count=$3 iflag=count_bytes ;; + can_extents) exit 0 ;; + extents) echo $4 1 ;; + *) exit 2 ;; +esac +EOF + +cat $out + +if [ "$(tr -s ' ' < $out)" != " 0 32 0 allocated" ]; then + echo "$0: unexpected output from nbdinfo --map" + exit 1 +fi diff --git a/info/info-map-base-allocation-zero.sh b/info/info-map-base-allocation-zero.sh new file mode 100755 index 0000000..3d3d44e --- /dev/null +++ b/info/info-map-base-allocation-zero.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# nbd client library in userspace +# Copyright (C) 2020 Red Hat Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +. ../tests/functions.sh + +set -e +set -x + +requires nbdkit --version + +out=info-base-allocation-zero.out +cleanup_fn rm -f $out +rm -f $out + +nbdkit -U - null --run '$VG nbdinfo --map "$uri"' > $out + +cat $out + +if [ "$(cat $out)" != "" ]; then + echo "$0: unexpected output from nbdinfo --map" + exit 1 +fi diff --git a/info/nbdinfo.c b/info/nbdinfo.c index 088d673..2a08a13 100644 --- a/info/nbdinfo.c +++ b/info/nbdinfo.c @@ -801,45 +801,76 @@ extent_description (const char *metacontext, uint32_t type) return NULL; /* Don't know - description field will be omitted. */ } +static void print_one_extent (uint64_t offset, uint64_t len, uint32_t type); + static void print_extents (uint32_vector *entries) { - uint64_t offset = 0; - size_t i; - bool comma = false; + size_t i, j; + uint64_t offset = 0; /* end of last extent printed + 1 */ + size_t last = 0; /* last entry printed + 2 */ if (json_output) fprintf (fp, "[\n"); for (i = 0; i < entries->size; i += 2) { - const char *descr = extent_description (map, entries->ptr[i+1]); + uint32_t type = entries->ptr[last+1]; - if (!json_output) { - fprintf (fp, "%10" PRIu64 " " - "%10" PRIu32 " " - "%3" PRIu32, - offset, entries->ptr[i], entries->ptr[i+1]); - if (descr) - fprintf (fp, " %s", descr); - fprintf (fp, "\n"); - } - else { - if (comma) - fprintf (fp, ",\n"); + /* If we're coalescing and the current type is different from the + * previous one then we should print everything up to this entry. + */ + if (last != i && entries->ptr[i+1] != type) { + uint64_t len; - fprintf (fp, "{ \"offset\": %" PRIu64 ", " - "\"length\": %" PRIu32 ", " - "\"type\": %" PRIu32, - offset, entries->ptr[i], entries->ptr[i+1]); - if (descr) { - fprintf (fp, ", \"description\": "); - print_json_string (descr); - } - fprintf (fp, "}"); - comma = true; + /* Calculate the length of the coalesced extent. */ + for (j = last, len = 0; j < i; j += 2) + len += entries->ptr[j]; + print_one_extent (offset, len, type); + offset += len; + last = i; } + } + + /* Print the last extent if there is one. */ + if (last != i) { + uint32_t type = entries->ptr[last+1]; + uint64_t len; - offset += entries->ptr[i]; + for (j = last, len = 0; j < i; j += 2) + len += entries->ptr[j]; + print_one_extent (offset, len, type); } if (json_output) fprintf (fp, "\n]\n"); } + +static void +print_one_extent (uint64_t offset, uint64_t len, uint32_t type) +{ + static bool comma = false; + const char *descr = extent_description (map, type); + + if (!json_output) { + fprintf (fp, "%10" PRIu64 " " + "%10" PRIu64 " " + "%3" PRIu32, + offset, len, type); + if (descr) + fprintf (fp, " %s", descr); + fprintf (fp, "\n"); + } + else { + if (comma) + fprintf (fp, ",\n"); + + fprintf (fp, "{ \"offset\": %" PRIu64 ", " + "\"length\": %" PRIu64 ", " + "\"type\": %" PRIu32, + offset, len, type); + if (descr) { + fprintf (fp, ", \"description\": "); + print_json_string (descr); + } + fprintf (fp, "}"); + comma = true; + } +} -- 2.29.0.rc2