[Libguestfs] [PATCH libnbd 5/5] info: --map: Coalesce adjacent extents of the same type.

Richard W.M. Jones
Tue, 27 Oct 2020 18:38:41 -0000

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