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 <[email protected]>
---
 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

_______________________________________________
Libguestfs mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/libguestfs

Reply via email to