[Libguestfs] [nbdkit PATCH 2/3] extents: Add nbdkit_extents_aligned()

Eric Blake eblake at redhat.com
Tue Jul 7 22:22:46 UTC 2020


We have several filters that would benefit from reporting extents to
the client that are always aligned to boundaries chosen by the filter,
regardless of whether the plugin reports extents at a finer
granularity.  Add a new helper function to make the work easier,
without having to duplicate code in each filter.  Any alignment block
that straddles more than one plugin extent is reported as a single
extent to the client, with a type determined by the intersection of
the underlying extents.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 docs/nbdkit-filter.pod  | 18 ++++++++++
 include/nbdkit-filter.h |  5 +++
 server/extents.c        | 80 ++++++++++++++++++++++++++++++++++++++++-
 server/nbdkit.syms      |  1 +
 4 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/docs/nbdkit-filter.pod b/docs/nbdkit-filter.pod
index 510781e1..066ca1c7 100644
--- a/docs/nbdkit-filter.pod
+++ b/docs/nbdkit-filter.pod
@@ -719,6 +719,24 @@ Returns the number of extents in the list.

 Returns a copy of the C<i>'th extent.

+=head3 Enforcing alignment of an nbdkit_extents list
+
+A convenience function is provided to filters only which makes it
+easier to ensure that the client only encounters aligned extents.
+
+ int nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
+                             nbdkit_backend *nxdata,
+                             uint32_t count, uint64_t offset,
+                             uint32_t flags, uint32_t align,
+                             struct nbdkit_extents *extents, int *err);
+
+Calls next_ops->extents as needed until at least C<align> bytes are
+obtained.  Anywhere the underlying plugin returns differing extents
+within C<align> bytes, this function treats that portion of the disk
+as a single extent with zero and sparse status bits determined by the
+intersection of all underlying extents.  It is an error to call this
+function with C<count> or C<offset> that is not already aligned.
+
 =head2 C<.cache>

  int (*cache) (struct nbdkit_next_ops *next_ops, void *nxdata,
diff --git a/include/nbdkit-filter.h b/include/nbdkit-filter.h
index d81186f5..229a54b4 100644
--- a/include/nbdkit-filter.h
+++ b/include/nbdkit-filter.h
@@ -116,6 +116,11 @@ extern void nbdkit_extents_free (struct nbdkit_extents *);
 extern size_t nbdkit_extents_count (const struct nbdkit_extents *);
 extern struct nbdkit_extent nbdkit_get_extent (const struct nbdkit_extents *,
                                                size_t);
+extern int nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
+                                   nbdkit_backend *nxdata,
+                                   uint32_t count, uint64_t offset,
+                                   uint32_t flags, uint32_t align,
+                                   struct nbdkit_extents *extents, int *err);

 /* Filter struct. */
 struct nbdkit_filter {
diff --git a/server/extents.c b/server/extents.c
index 4ab5946c..035497b5 100644
--- a/server/extents.c
+++ b/server/extents.c
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2019 Red Hat Inc.
+ * Copyright (C) 2019-2020 Red Hat Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -41,7 +41,10 @@
 #include <errno.h>
 #include <assert.h>

+#include "cleanup.h"
+#include "isaligned.h"
 #include "minmax.h"
+#include "rounding.h"
 #include "vector.h"

 #include "internal.h"
@@ -206,3 +209,78 @@ nbdkit_add_extent (struct nbdkit_extents *exts,
     return append_extent (exts, &e);
   }
 }
+
+/* Compute aligned extents on behalf of a filter. */
+int
+nbdkit_extents_aligned (struct nbdkit_next_ops *next_ops,
+                        nbdkit_backend *nxdata,
+                        uint32_t count, uint64_t offset,
+                        uint32_t flags, uint32_t align,
+                        struct nbdkit_extents *exts, int *err)
+{
+  size_t i;
+  struct nbdkit_extent e, e2;
+
+  if (!IS_ALIGNED(count | offset, align)) {
+    nbdkit_error ("nbdkit_extents_aligned: unaligned request");
+    *err = EINVAL;
+    return -1;
+  }
+
+  /* Perform an initial query, then scan for the first unaligned extent. */
+  if (next_ops->extents (nxdata, count, offset, flags, exts, err) == -1)
+    return -1;
+  for (i = 0; i < exts->extents.size; ++i) {
+    e = exts->extents.ptr[i];
+    if (!IS_ALIGNED(e.length, align)) {
+      /* If the unalignment is past align, just truncate and return early */
+      if (e.offset + e.length > offset + align) {
+        e.length = ROUND_DOWN (e.length, align);
+        exts->extents.size = i + !!e.length;
+        exts->next = e.offset + e.length;
+        break;
+      }
+
+      /* Otherwise, coalesce until we have at least align bytes, which
+       * may require further queries.
+       */
+      assert (i == 0);
+      while (e.length < align) {
+        if (exts->extents.size > 1) {
+          e.length += exts->extents.ptr[1].length;
+          e.type &= exts->extents.ptr[1].type;
+          extents_remove (&exts->extents, 1);
+        }
+        else {
+          /* The plugin needs a fresh extents object each time, but
+           * with care, we can merge it into the callers' extents.
+           */
+          extents tmp;
+          CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL;
+
+          extents2 = nbdkit_extents_new (e.offset + e.length, offset + align);
+          if (next_ops->extents (nxdata, offset + align - e.length,
+                                 e.offset + e.length,
+                                 flags & ~NBDKIT_FLAG_REQ_ONE,
+                                 extents2, err) == -1)
+            return -1;
+          e2 = extents2->extents.ptr[0];
+          assert (e2.offset == e.offset + e.length);
+          e2.offset = e.offset;
+          e2.length += e.length;
+          e2.type &= e.type;
+          e = e2;
+          tmp = exts->extents;
+          exts->extents = extents2->extents;
+          extents2->extents = tmp;
+        }
+      }
+      e.length = align;
+      exts->extents.size = 1;
+      exts->next = e.offset + e.length;
+      break;
+    }
+  }
+  /* Once we get here, all extents are aligned. */
+  return 0;
+}
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 20c390a9..d62ad484 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -43,6 +43,7 @@
     nbdkit_debug;
     nbdkit_error;
     nbdkit_export_name;
+    nbdkit_extents_aligned;
     nbdkit_extents_count;
     nbdkit_extents_free;
     nbdkit_extents_new;
-- 
2.27.0




More information about the Libguestfs mailing list