[Libguestfs] [PATCH nbdkit v5 FINAL 16/19] vddk: Implement extents.

Richard W.M. Jones rjones at redhat.com
Thu Mar 28 16:18:43 UTC 2019


This uses a new API VixDiskLib_QueryAllocatedBlocks provided in
VDDK >= 6.7.

Thanks: Martin Kletzander.
---
 plugins/vddk/vddk-structs.h |  15 +++-
 plugins/vddk/vddk.c         | 169 ++++++++++++++++++++++++++++++++++++
 2 files changed, 183 insertions(+), 1 deletion(-)

diff --git a/plugins/vddk/vddk-structs.h b/plugins/vddk/vddk-structs.h
index dbed94a..df88322 100644
--- a/plugins/vddk/vddk-structs.h
+++ b/plugins/vddk/vddk-structs.h
@@ -1,5 +1,5 @@
 /* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -47,6 +47,9 @@ typedef uint64_t VixError;
 #define VIXDISKLIB_FLAG_OPEN_READ_ONLY 4
 #define VIXDISKLIB_SECTOR_SIZE 512
 
+#define VIXDISKLIB_MIN_CHUNK_SIZE 128
+#define VIXDISKLIB_MAX_CHUNK_NUMBER (512*1024)
+
 typedef void *VixDiskLibConnection;
 typedef void *VixDiskLibHandle;
 
@@ -124,4 +127,14 @@ typedef struct VixDiskLibInfo {
   char *uuid;
 } VixDiskLibInfo;
 
+typedef struct {
+  uint64_t offset;
+  uint64_t length;
+} VixDiskLibBlock;
+
+typedef struct {
+  uint32_t numBlocks;
+  VixDiskLibBlock blocks[1];
+} VixDiskLibBlockList;
+
 #endif /* NBDKIT_VDDK_STRUCTS_H */
diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c
index 56871cc..56c5335 100644
--- a/plugins/vddk/vddk.c
+++ b/plugins/vddk/vddk.c
@@ -45,12 +45,17 @@
 #include <nbdkit-plugin.h>
 
 #include "isaligned.h"
+#include "minmax.h"
+#include "rounding.h"
 
 #include "vddk-structs.h"
 
 /* Enable extra disk info debugging with: -D vddk.diskinfo=1 */
 int vddk_debug_diskinfo;
 
+/* Enable debugging of extents code with: -D vddk.extents=1 */
+int vddk_debug_extents;
+
 /* The VDDK APIs that we call.  These globals are initialized when the
  * plugin is loaded (by vddk_load).
  */
@@ -67,6 +72,8 @@ static VixError (*VixDiskLib_GetInfo) (VixDiskLibHandle handle, VixDiskLibInfo *
 static void (*VixDiskLib_FreeInfo) (VixDiskLibInfo *info);
 static VixError (*VixDiskLib_Read) (VixDiskLibHandle handle, uint64_t start_sector, uint64_t nr_sectors, unsigned char *buf);
 static VixError (*VixDiskLib_Write) (VixDiskLibHandle handle, uint64_t start_sector, uint64_t nr_sectors, const unsigned char *buf);
+static VixError (*VixDiskLib_QueryAllocatedBlocks) (VixDiskLibHandle diskHandle, uint64_t start_sector, uint64_t nr_sectors, uint64_t chunk_size, VixDiskLibBlockList **block_list);
+static VixError (*VixDiskLib_FreeBlockList) (VixDiskLibBlockList *block_list);
 
 /* Parameters passed to InitEx. */
 #define VDDK_MAJOR 5
@@ -174,6 +181,11 @@ vddk_load (void)
   VixDiskLib_FreeInfo = dlsym (dl, "VixDiskLib_FreeInfo");
   VixDiskLib_Read = dlsym (dl, "VixDiskLib_Read");
   VixDiskLib_Write = dlsym (dl, "VixDiskLib_Write");
+
+  /* Added in VDDK 6.7, these will be NULL for earlier versions: */
+  VixDiskLib_QueryAllocatedBlocks =
+    dlsym (dl, "VixDiskLib_QueryAllocatedBlocks");
+  VixDiskLib_FreeBlockList = dlsym (dl, "VixDiskLib_FreeBlockList");
 }
 
 static void
@@ -570,6 +582,161 @@ vddk_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset)
   return 0;
 }
 
+static int
+vddk_can_extents (void *handle)
+{
+  struct vddk_handle *h = handle;
+  VixError err;
+  VixDiskLibBlockList *block_list;
+
+  /* This call was added in VDDK 6.7.  In earlier versions the
+   * function pointer will be NULL and we cannot query extents.
+   */
+  if (VixDiskLib_QueryAllocatedBlocks == NULL) {
+    nbdkit_debug ("can_extents: VixDiskLib_QueryAllocatedBlocks == NULL, "
+                  "probably this is VDDK < 6.7");
+    return 0;
+  }
+
+  /* However even when the call is available it rarely works well so
+   * the best thing we can do here is to try the call and if it's
+   * non-functional return false.
+   */
+  DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",
+              "handle, 0, %d sectors, %d sectors",
+              VIXDISKLIB_MIN_CHUNK_SIZE, VIXDISKLIB_MIN_CHUNK_SIZE);
+  err = VixDiskLib_QueryAllocatedBlocks (h->handle,
+                                         0, VIXDISKLIB_MIN_CHUNK_SIZE,
+                                         VIXDISKLIB_MIN_CHUNK_SIZE,
+                                         &block_list);
+  if (err == VIX_OK) {
+    DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+    VixDiskLib_FreeBlockList (block_list);
+  }
+  if (err != VIX_OK) {
+    char *errmsg = VixDiskLib_GetErrorText (err, NULL);
+    nbdkit_debug ("can_extents: VixDiskLib_QueryAllocatedBlocks test failed, "
+                  "extents support will be disabled: "
+                  "original error: %s",
+                  errmsg);
+    VixDiskLib_FreeErrorText (errmsg);
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+add_extent (struct nbdkit_extents *extents,
+            uint64_t *position, uint64_t next_position, bool is_hole)
+{
+  const uint32_t type = is_hole ? NBDKIT_EXTENT_HOLE|NBDKIT_EXTENT_ZERO : 0;
+  const uint64_t length = next_position - *position;
+
+  assert (*position <= next_position);
+  if (*position == next_position)
+    return 0;
+
+  if (vddk_debug_extents)
+    nbdkit_debug ("adding extent type %s at [%" PRIu64 "...%" PRIu64 "]",
+                  is_hole ? "hole" : "allocated data",
+                  *position, next_position-1);
+  if (nbdkit_add_extent (extents, *position, length, type) == -1)
+    return -1;
+
+  *position = next_position;
+  return 0;
+}
+
+static int
+vddk_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+              struct nbdkit_extents *extents)
+{
+  struct vddk_handle *h = handle;
+  bool req_one = flags & NBDKIT_FLAG_REQ_ONE;
+  uint64_t position, end, start_sector;
+
+  position = offset;
+  end = offset + count;
+
+  /* We can only query whole chunks.  Therefore start with the first
+   * chunk before offset.
+   */
+  start_sector =
+    ROUND_DOWN (offset, VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE)
+    / VIXDISKLIB_SECTOR_SIZE;
+  while (start_sector * VIXDISKLIB_SECTOR_SIZE < end) {
+    VixError err;
+    uint32_t i;
+    uint64_t nr_chunks, nr_sectors;
+    VixDiskLibBlockList *block_list;
+
+    assert (IS_ALIGNED (start_sector, VIXDISKLIB_MIN_CHUNK_SIZE));
+
+    nr_chunks =
+      ROUND_UP (end - start_sector * VIXDISKLIB_SECTOR_SIZE,
+                VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE)
+      / (VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE);
+    nr_chunks = MIN (nr_chunks, VIXDISKLIB_MAX_CHUNK_NUMBER);
+    nr_sectors = nr_chunks * VIXDISKLIB_MIN_CHUNK_SIZE;
+
+    DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",
+                "handle, %" PRIu64 " sectors, %" PRIu64 " sectors, "
+                "%d sectors",
+                start_sector, nr_sectors, VIXDISKLIB_MIN_CHUNK_SIZE);
+    err = VixDiskLib_QueryAllocatedBlocks (h->handle,
+                                           start_sector, nr_sectors,
+                                           VIXDISKLIB_MIN_CHUNK_SIZE,
+                                           &block_list);
+    if (err != VIX_OK) {
+      VDDK_ERROR (err, "VixDiskLib_QueryAllocatedBlocks");
+      return -1;
+    }
+
+    for (i = 0; i < block_list->numBlocks; ++i) {
+      uint64_t blk_offset, blk_length;
+
+      blk_offset = block_list->blocks[i].offset * VIXDISKLIB_SECTOR_SIZE;
+      blk_length = block_list->blocks[i].length * VIXDISKLIB_SECTOR_SIZE;
+
+      /* The query returns allocated blocks.  We must insert holes
+       * between the blocks as necessary.
+       */
+      if (position < blk_offset &&
+          add_extent (extents, &position, blk_offset, true) == -1)
+        goto error_in_add;
+      if (add_extent (extents,
+                      &position, blk_offset + blk_length, false) == -1) {
+      error_in_add:
+        DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+        VixDiskLib_FreeBlockList (block_list);
+        return -1;
+      }
+    }
+    DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+    VixDiskLib_FreeBlockList (block_list);
+
+    /* There's an implicit hole after the returned list of blocks, up
+     * to the end of the QueryAllocatedBlocks request.
+     */
+    if (add_extent (extents,
+                    &position,
+                    (start_sector + nr_sectors) * VIXDISKLIB_SECTOR_SIZE,
+                    true) == -1)
+      return -1;
+
+    start_sector += nr_sectors;
+
+    /* If one extent was requested, as long as we've added an extent
+     * overlapping the original offset we're done.
+     */
+    if (req_one && position > offset)
+      break;
+  }
+
+  return 0;
+}
+
 static struct nbdkit_plugin plugin = {
   .name              = "vddk",
   .longname          = "VMware VDDK plugin",
@@ -585,6 +752,8 @@ static struct nbdkit_plugin plugin = {
   .get_size          = vddk_get_size,
   .pread             = vddk_pread,
   .pwrite            = vddk_pwrite,
+  .can_extents       = vddk_can_extents,
+  .extents           = vddk_extents,
 };
 
 NBDKIT_REGISTER_PLUGIN(plugin)
-- 
2.20.1




More information about the Libguestfs mailing list