[Libguestfs] [PATCH nbdkit 8/9] vddk: Implement extents.

Richard W.M. Jones rjones at redhat.com
Tue Mar 19 16:35:05 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         | 127 ++++++++++++++++++++++++++++++++++++
 2 files changed, 141 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..ce6633c 100644
--- a/plugins/vddk/vddk.c
+++ b/plugins/vddk/vddk.c
@@ -45,6 +45,8 @@
 #include <nbdkit-plugin.h>
 
 #include "isaligned.h"
+#include "minmax.h"
+#include "rounding.h"
 
 #include "vddk-structs.h"
 
@@ -67,6 +69,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 +178,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 +579,122 @@ 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
+vddk_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+              struct nbdkit_extents_map *extents_map)
+{
+  struct vddk_handle *h = handle;
+  uint64_t end, start_sector;
+
+  /* nbdkit extents_map defaults to all allocated.  However VDDK gives
+   * us the allocated blocks.  Therefore set the whole region to a
+   * hole first.
+   */
+  if (nbdkit_extent_add (extents_map, offset, count,
+                         NBDKIT_EXTENT_HOLE|NBDKIT_EXTENT_ZERO) == -1)
+    return -1;
+
+  /* We can only query whole chunks.  Therefore start with the first
+   * chunk before offset.
+   */
+  end = offset + count;
+  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 offset, length;
+
+      offset = block_list->blocks[i].offset * VIXDISKLIB_SECTOR_SIZE;
+      length = block_list->blocks[i].length * VIXDISKLIB_SECTOR_SIZE;
+
+      if (nbdkit_extent_add (extents_map,
+                             offset, length, 0 /* allocated data */) == -1) {
+        DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+        VixDiskLib_FreeBlockList (block_list);
+        return -1;
+      }
+    }
+    DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+    VixDiskLib_FreeBlockList (block_list);
+
+    start_sector += nr_sectors;
+  }
+
+  return 0;
+}
+
 static struct nbdkit_plugin plugin = {
   .name              = "vddk",
   .longname          = "VMware VDDK plugin",
@@ -585,6 +710,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