diff options
Diffstat (limited to 'block/io.c')
-rw-r--r-- | block/io.c | 132 |
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 |