[Libguestfs] [PATCH nbdkit 1/9] server: Implement extents/can_extents calls for plugins and filters.
Eric Blake
eblake at redhat.com
Wed Mar 20 13:49:21 UTC 2019
On 3/19/19 11:34 AM, Richard W.M. Jones wrote:
> This pair of calls allows plugins to describe which extents in the
> virtual disk are allocated, holes or zeroes.
> ---
Resuming where I left off, if it helps:
> +#ifdef IN_TEST_EXTENTS
> +/* These functions are only compiled for and called from the unit tests. */
> +
> +void
> +dump_extents_map (const struct nbdkit_extents_map *map)
> +{
> + size_t i;
> +
> + fprintf (stderr, "map %p:\n", map);
> + fprintf (stderr, "\tnr_extents=%zu allocated=%zu\n",
> + map->nr_extents, map->allocated);
> + for (i = 0; i < map->nr_extents; ++i) {
> + fprintf (stderr, "\textent %zu:", i);
> + fprintf (stderr,
> + "\toffset=%" PRIu64 " length=%" PRIu64 " "
> + "last=%" PRIu64 " type=%" PRIu32 "\n",
Worth using fixed-width formatting so that consecutive lines are
tabular, and/or using hex offsets (in addition to decimal) for easier
spotting of alignments?
> + map->extents[i].offset, map->extents[i].length,
> + map->extents[i].offset + map->extents[i].length - 1,
> + map->extents[i].type);
> + }
> +}
> +
> +void
> +validate_extents_map (const struct nbdkit_extents_map *map)
> +{
> + size_t i;
> + bool fail = false;
> +
> + if (map->nr_extents > map->allocated) {
> + fprintf (stderr, "validate_extents_map: nr_extents > allocated\n");
> + fail = true;
> + }
> +
> + for (i = 0; i < map->nr_extents; ++i) {
Of course, if we failed the previous check, this now reads beyond the
end of the allocation. But it's debug code.
> + /* Extent length must be > 0. */
> + if (map->extents[i].length == 0) {
> + fprintf (stderr, "validate_extents_map: %zu: extent length must be > 0\n",
> + i);
> + fail = true;
> + }
> +
> + /* Extent length must not wrap around, indicating that we have
> + * computed a negative length (as unsigned) somewhere.
> + */
> + if (map->extents[i].offset + map->extents[i].length <
> + map->extents[i].offset) {
> + fprintf (stderr, "validate_extents_map: %zu: negative length\n",
> + i);
> + fail = true;
> + }
> +
No validation that extents don't exceed INT64_MAX?
> + if (i > 0) {
> + /* Extents are stored in strictly ascending order. */
> + if (map->extents[i-1].offset >= map->extents[i].offset) {
> + fprintf (stderr,
> + "validate_extents_map: %zu: "
> + "not strictly ascending order\n", i);
> + fail = true;
> + }
> +
> + /* Adjacent extents must not overlap. */
> + if (map->extents[i-1].offset + map->extents[i-1].length >
> + map->extents[i].offset) {
> + fprintf (stderr,
> + "validate_extents_map: %zu: "
> + "overlapping extents\n", i);
> + fail = true;
> + }
> +
> + /* Adjacent extents either have a different type or are
> + * separated by a gap.
This changes if you don't allow gaps (it does sound like the
implementation will be easier if it is gap-free...)
> + */
> + if (map->extents[i-1].type == map->extents[i].type &&
> + map->extents[i-1].offset + map->extents[i-1].length ==
> + map->extents[i].offset) {
> + fprintf (stderr,
> + "validate_extents_map: %zu: "
> + "adjacent extents with same type not coalesced\n", i);
> + fail = true;
> + }
> + }
> + }
> +
> + if (fail) {
> + dump_extents_map (map);
> + abort ();
> + }
> +}
> @@ -511,6 +530,21 @@ filter_can_zero (struct backend *b, struct connection *conn)
> return f->backend.next->can_zero (f->backend.next, conn);
> }
>
> +static int
> +filter_can_extents (struct backend *b, struct connection *conn)
> +{
> + struct backend_filter *f = container_of (b, struct backend_filter, backend);
> + void *handle = connection_get_handle (conn, f->backend.i);
> + struct b_conn nxdata = { .b = f->backend.next, .conn = conn };
> +
> + debug ("%s: can_extents", f->name);
> +
> + if (f->filter.can_extents)
> + return f->filter.can_extents (&next_ops, &nxdata, handle);
> + else
> + return f->backend.next->can_extents (f->backend.next, conn);
For now, should we default filter.can_extents to false until we
completed the work for all filters?
> +++ b/server/test-extents.c
> +
> +static int
> +compare (uint64_t offset, uint64_t length, uint32_t type, void *mapv)
> +{
> + const struct nbdkit_extents_map *map = mapv;
> + size_t j;
> +
> + /* nbdkit_extents_foreach_range should ensure this is true even if
> + * there are extents which go below or above the range.
> + */
> + assert (offset >= 0);
Dead assertion (always true since offset is unsigned).
> +
> + /* Read out the extents and compare them to the disk. */
> + assert (nbdkit_extents_foreach (map, compare, map,
> + NBDKIT_EXTENTS_FOREACH_FLAG_RANGE,
> + 0, DISK_SIZE-1) == 0);
> +
The test is rendered less powerful when NDEBUG is defined. But it
doesn't affect production code (and maybe we could ensure that this file
#undef NDEBUG regardless of what the user decides via -D for assertions
in the overall project)
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3226
Virtualization: qemu.org | libvirt.org
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20190320/41118ec8/attachment.sig>
More information about the Libguestfs
mailing list