aboutsummaryrefslogtreecommitdiff
path: root/block/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/io.c')
-rw-r--r--block/io.c132
1 files changed, 75 insertions, 57 deletions
diff --git a/block/io.c b/block/io.c
index 54f0968aee..02528b3823 100644
--- a/block/io.c
+++ b/block/io.c
@@ -2343,6 +2343,7 @@ early_out:
int coroutine_fn
bdrv_co_common_block_status_above(BlockDriverState *bs,
BlockDriverState *base,
+ bool include_base,
bool want_zero,
int64_t offset,
int64_t bytes,
@@ -2350,34 +2351,84 @@ bdrv_co_common_block_status_above(BlockDriverState *bs,
int64_t *map,
BlockDriverState **file)
{
+ int ret;
BlockDriverState *p;
- int ret = 0;
- bool first = true;
+ int64_t eof = 0;
+
+ assert(!include_base || base); /* Can't include NULL base */
+
+ if (!include_base && bs == base) {
+ *pnum = bytes;
+ return 0;
+ }
+
+ ret = bdrv_co_block_status(bs, want_zero, offset, bytes, pnum, map, file);
+ if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) {
+ return ret;
+ }
+
+ if (ret & BDRV_BLOCK_EOF) {
+ eof = offset + *pnum;
+ }
+
+ assert(*pnum <= bytes);
+ bytes = *pnum;
- assert(bs != base);
- for (p = bs; p != base; p = bdrv_filter_or_cow_bs(p)) {
+ for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base;
+ p = bdrv_filter_or_cow_bs(p))
+ {
ret = bdrv_co_block_status(p, want_zero, offset, bytes, pnum, map,
file);
if (ret < 0) {
- break;
+ return ret;
}
- if (ret & BDRV_BLOCK_ZERO && ret & BDRV_BLOCK_EOF && !first) {
+ if (*pnum == 0) {
/*
- * Reading beyond the end of the file continues to read
- * zeroes, but we can only widen the result to the
- * unallocated length we learned from an earlier
- * iteration.
+ * The top layer deferred to this layer, and because this layer is
+ * short, any zeroes that we synthesize beyond EOF behave as if they
+ * were allocated at this layer.
+ *
+ * We don't include BDRV_BLOCK_EOF into ret, as upper layer may be
+ * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see
+ * below.
*/
+ assert(ret & BDRV_BLOCK_EOF);
*pnum = bytes;
+ if (file) {
+ *file = p;
+ }
+ ret = BDRV_BLOCK_ZERO | BDRV_BLOCK_ALLOCATED;
+ break;
+ }
+ if (ret & BDRV_BLOCK_ALLOCATED) {
+ /*
+ * We've found the node and the status, we must break.
+ *
+ * Drop BDRV_BLOCK_EOF, as it's not for upper layer, which may be
+ * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see
+ * below.
+ */
+ ret &= ~BDRV_BLOCK_EOF;
+ break;
}
- if (ret & (BDRV_BLOCK_ZERO | BDRV_BLOCK_DATA)) {
+
+ if (p == base) {
+ assert(include_base);
break;
}
- /* [offset, pnum] unallocated on this layer, which could be only
- * the first part of [offset, bytes]. */
- bytes = MIN(bytes, *pnum);
- first = false;
+
+ /*
+ * OK, [offset, offset + *pnum) region is unallocated on this layer,
+ * let's continue the diving.
+ */
+ assert(*pnum <= bytes);
+ bytes = *pnum;
+ }
+
+ if (offset + *pnum == eof) {
+ ret |= BDRV_BLOCK_EOF;
}
+
return ret;
}
@@ -2385,7 +2436,7 @@ int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base,
int64_t offset, int64_t bytes, int64_t *pnum,
int64_t *map, BlockDriverState **file)
{
- return bdrv_common_block_status_above(bs, base, true, offset, bytes,
+ return bdrv_common_block_status_above(bs, base, false, true, offset, bytes,
pnum, map, file);
}
@@ -2402,9 +2453,9 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
int ret;
int64_t dummy;
- ret = bdrv_common_block_status_above(bs, bdrv_filter_or_cow_bs(bs), false,
- offset, bytes, pnum ? pnum : &dummy,
- NULL, NULL);
+ ret = bdrv_common_block_status_above(bs, bs, true, false, offset,
+ bytes, pnum ? pnum : &dummy, NULL,
+ NULL);
if (ret < 0) {
return ret;
}
@@ -2426,52 +2477,19 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
* at 'offset + *pnum' may return the same allocation status (in other
* words, the result is not necessarily the maximum possible range);
* but 'pnum' will only be 0 when end of file is reached.
- *
*/
int bdrv_is_allocated_above(BlockDriverState *top,
BlockDriverState *base,
bool include_base, int64_t offset,
int64_t bytes, int64_t *pnum)
{
- BlockDriverState *intermediate;
- int ret;
- int64_t n = bytes;
-
- assert(base || !include_base);
-
- intermediate = top;
- while (include_base || intermediate != base) {
- int64_t pnum_inter;
- int64_t size_inter;
-
- assert(intermediate);
- ret = bdrv_is_allocated(intermediate, offset, bytes, &pnum_inter);
- if (ret < 0) {
- return ret;
- }
- if (ret) {
- *pnum = pnum_inter;
- return 1;
- }
-
- size_inter = bdrv_getlength(intermediate);
- if (size_inter < 0) {
- return size_inter;
- }
- if (n > pnum_inter &&
- (intermediate == top || offset + pnum_inter < size_inter)) {
- n = pnum_inter;
- }
-
- if (intermediate == base) {
- break;
- }
-
- intermediate = bdrv_filter_or_cow_bs(intermediate);
+ int ret = bdrv_common_block_status_above(top, base, include_base, false,
+ offset, bytes, pnum, NULL, NULL);
+ if (ret < 0) {
+ return ret;
}
- *pnum = n;
- return 0;
+ return !!(ret & BDRV_BLOCK_ALLOCATED);
}
int coroutine_fn