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
