Patchwork [RFC,17/17] raw: implement is_allocated

login
register
mail settings
Submitter Paolo Bonzini
Date March 8, 2012, 5:15 p.m.
Message ID <1331226917-6658-18-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/145607/
State New
Headers show

Comments

Paolo Bonzini - March 8, 2012, 5:15 p.m.
SEEK_DATA and SEEK_HOLE can be used to implement the is_allocated
callback for raw files.  These currently work on btrfs, with an XFS
implementation also coming soon.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/raw-posix.c |   62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 block/raw.c       |    8 ++++++
 2 files changed, 70 insertions(+), 0 deletions(-)
Christoph Hellwig - March 24, 2012, 3:42 p.m.
On Thu, Mar 08, 2012 at 06:15:17PM +0100, Paolo Bonzini wrote:
> SEEK_DATA and SEEK_HOLE can be used to implement the is_allocated
> callback for raw files.  These currently work on btrfs, with an XFS
> implementation also coming soon.

Btw - if you're interested in a bit more kernel hacking it would be cool
to implement it for block devices using GET LBA STATUS.

Patch

diff --git a/block/raw-posix.c b/block/raw-posix.c
index 56aabc7..c0b32e1 100644
--- a/block/raw-posix.c
+++ b/block/raw-posix.c
@@ -105,6 +105,13 @@ 
 #define O_DIRECT O_DSYNC
 #endif
 
+#ifndef SEEK_DATA
+#define SEEK_DATA 3
+#endif
+#ifndef SEEK_HOLE
+#define SEEK_HOLE 4
+#endif
+
 #define FTYPE_FILE   0
 #define FTYPE_CD     1
 #define FTYPE_FD     2
@@ -583,6 +590,60 @@  static int raw_create(const char *filename, QEMUOptionParameter *options)
     return result;
 }
 
+/*
+ * Returns true iff the specified sector is present in the disk image. Drivers
+ * not implementing the functionality are assumed to not support backing files,
+ * hence all their sectors are reported as allocated.
+ *
+ * If 'sector_num' is beyond the end of the disk image the return value is 0
+ * and 'pnum' is set to 0.
+ *
+ * 'pnum' is set to the number of sectors (including and immediately following
+ * the specified sector) that are known to be in the same
+ * allocated/unallocated state.
+ *
+ * 'nb_sectors' is the max value 'pnum' should be set to.  If nb_sectors goes
+ * beyond the end of the disk image it will be clamped.
+ */
+static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
+                                            int64_t sector_num,
+                                            int nb_sectors, int *pnum)
+{
+    off_t start, data, hole;
+    int ret, num;
+    BDRVRawState *s = bs->opaque;
+
+    ret = fd_open(bs);
+    if (ret < 0) {
+        return ret;
+    }
+
+    start = sector_num * BDRV_SECTOR_SIZE;
+    hole = lseek(s->fd, start, SEEK_HOLE);
+    if (hole == -1) {
+        /* -ENXIO indicates that sector_num was past the end of the file.
+         * There is a virtual hole there.  */
+        assert(errno != -ENXIO);
+
+        /* Most likely EINVAL.  Assume everything is allocated.  */
+        return 1;
+    }
+
+    if (hole == start) {
+        /* On a hole.  We need another syscall to find its end.  */
+        data = lseek(s->fd, start, SEEK_DATA);
+        assert (data != -1);
+        num = (data - start) / BDRV_SECTOR_SIZE;
+    } else {
+        data = start;
+        num = (hole - start) / BDRV_SECTOR_SIZE;
+    }
+
+    /* Clamping to the file size is done by the kernel.  */
+    *pnum = MIN(nb_sectors, num);
+    return data == start;
+}
+
 #ifdef CONFIG_XFS
 static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors)
 {
@@ -661,6 +722,7 @@  static BlockDriver bdrv_file = {
     .bdrv_close = raw_close,
     .bdrv_create = raw_create,
     .bdrv_co_discard = raw_co_discard,
+    .bdrv_co_is_allocated = raw_co_is_allocated,
 
     .bdrv_aio_readv = raw_aio_readv,
     .bdrv_aio_writev = raw_aio_writev,
diff --git a/block/raw.c b/block/raw.c
index 3618c2d..e433bf2 100644
--- a/block/raw.c
+++ b/block/raw.c
@@ -30,6 +30,13 @@  static int coroutine_fn raw_co_flush(BlockDriverState *bs)
     return bdrv_co_flush(bs->file);
 }
 
+static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
+                                            int64_t sector_num,
+                                            int nb_sectors, int *pnum)
+{
+    return bdrv_co_is_allocated(bs->file, sector_num, nb_sectors, pnum);
+}
+
 static int64_t raw_getlength(BlockDriverState *bs)
 {
     return bdrv_getlength(bs->file);
@@ -119,6 +126,7 @@  static BlockDriver bdrv_raw = {
     .bdrv_co_readv          = raw_co_readv,
     .bdrv_co_writev         = raw_co_writev,
     .bdrv_co_flush_to_disk  = raw_co_flush,
+    .bdrv_co_is_allocated   = raw_co_is_allocated,
     .bdrv_co_discard        = raw_co_discard,
 
     .bdrv_probe         = raw_probe,