summaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2017-09-27 16:48:39 +0100
committerPeter Maydell <peter.maydell@linaro.org>2017-09-27 16:48:39 +0100
commitcfe4cade054c0e0d00d0185cdc433a9e3ce3e2e4 (patch)
treefb6658177a90773b24ec9ca6fd00f4142fb9887e /block
parentd666cacaeab904fb6e8361ee791b6e8ab8448577 (diff)
parentb156d51b62e6970753e1f9f36f7c4d5fdbf4c619 (diff)
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block layer patches # gpg: Signature made Tue 26 Sep 2017 14:52:32 BST # gpg: using RSA key 0x7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (24 commits) block/qcow2-bitmap: fix use of uninitialized pointer qemu-iotests: add shrinking image test qcow2: add shrink image support qcow2: add qcow2_cache_discard qemu-img: add --shrink flag for resize iotests: fix 181: enable postcopy-ram capability on target qemu-iotests: Test change-backing-file command block: Fix permissions after bdrv_reopen() block: reopen: Queue children after their parents block: Base permissions on rw state after reopen block: Add reopen queue to bdrv_check_perm() block: Add reopen_queue to bdrv_child_perm() qemu-io: Drop write permissions before read-only reopen block: Clean up some bad code in the vvfat driver block/throttle-groups.c: allocate RestartData on the heap throttle: Assert that bkt->max is valid in throttle_compute_wait() iotests: Print full path of bad output if mismatch iotests: use virtio aliases for 067 iotests: use -ccw on s390x for 051 iotests: use -ccw on s390x for 040, 139, and 182 ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'block')
-rw-r--r--block/commit.c1
-rw-r--r--block/file-posix.c10
-rw-r--r--block/mirror.c1
-rw-r--r--block/qcow2-bitmap.c2
-rw-r--r--block/qcow2-cache.c26
-rw-r--r--block/qcow2-cluster.c50
-rw-r--r--block/qcow2-refcount.c140
-rw-r--r--block/qcow2.c43
-rw-r--r--block/qcow2.h17
-rw-r--r--block/replication.c1
-rw-r--r--block/throttle-groups.c12
-rw-r--r--block/vvfat.c27
12 files changed, 289 insertions, 41 deletions
diff --git a/block/commit.c b/block/commit.c
index 898d91f653..8f0e83578a 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -257,6 +257,7 @@ static void bdrv_commit_top_close(BlockDriverState *bs)
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
diff --git a/block/file-posix.c b/block/file-posix.c
index ab12a2b591..36ee89e940 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -2730,6 +2730,16 @@ static int hdev_create(const char *filename, QemuOpts *opts,
ret = -ENOSPC;
}
+ if (!ret && total_size) {
+ uint8_t buf[BDRV_SECTOR_SIZE] = { 0 };
+ int64_t zero_size = MIN(BDRV_SECTOR_SIZE, total_size);
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ ret = -errno;
+ } else {
+ ret = qemu_write_full(fd, buf, zero_size);
+ ret = ret == zero_size ? 0 : -errno;
+ }
+ }
qemu_close(fd);
return ret;
}
diff --git a/block/mirror.c b/block/mirror.c
index 6531652d73..6f5cb9f26c 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1084,6 +1084,7 @@ static void bdrv_mirror_top_close(BlockDriverState *bs)
static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index e8d3bdbd6e..14f41d0427 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -602,7 +602,7 @@ static Qcow2BitmapList *bitmap_list_load(BlockDriverState *bs, uint64_t offset,
goto fail;
}
- bm = g_new(Qcow2Bitmap, 1);
+ bm = g_new0(Qcow2Bitmap, 1);
bm->table.offset = e->bitmap_table_offset;
bm->table.size = e->bitmap_table_size;
bm->flags = e->flags;
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
index 1d25147392..75746a7f43 100644
--- a/block/qcow2-cache.c
+++ b/block/qcow2-cache.c
@@ -411,3 +411,29 @@ void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c,
assert(c->entries[i].offset != 0);
c->entries[i].dirty = true;
}
+
+void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c,
+ uint64_t offset)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].offset == offset) {
+ return qcow2_cache_get_table_addr(bs, c, i);
+ }
+ }
+ return NULL;
+}
+
+void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table)
+{
+ int i = qcow2_cache_get_table_idx(bs, c, table);
+
+ assert(c->entries[i].ref == 0);
+
+ c->entries[i].offset = 0;
+ c->entries[i].lru_counter = 0;
+ c->entries[i].dirty = false;
+
+ qcow2_cache_table_release(bs, c, i, 1);
+}
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 0d4824993c..d2518d1893 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -32,6 +32,56 @@
#include "qemu/bswap.h"
#include "trace.h"
+int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int new_l1_size, i, ret;
+
+ if (exact_size >= s->l1_size) {
+ return 0;
+ }
+
+ new_l1_size = exact_size;
+
+#ifdef DEBUG_ALLOC2
+ fprintf(stderr, "shrink l1_table from %d to %d\n", s->l1_size, new_l1_size);
+#endif
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE);
+ ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset +
+ new_l1_size * sizeof(uint64_t),
+ (s->l1_size - new_l1_size) * sizeof(uint64_t), 0);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = bdrv_flush(bs->file->bs);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS);
+ for (i = s->l1_size - 1; i > new_l1_size - 1; i--) {
+ if ((s->l1_table[i] & L1E_OFFSET_MASK) == 0) {
+ continue;
+ }
+ qcow2_free_clusters(bs, s->l1_table[i] & L1E_OFFSET_MASK,
+ s->cluster_size, QCOW2_DISCARD_ALWAYS);
+ s->l1_table[i] = 0;
+ }
+ return 0;
+
+fail:
+ /*
+ * If the write in the l1_table failed the image may contain a partially
+ * overwritten l1_table. In this case it would be better to clear the
+ * l1_table in memory to avoid possible image corruption.
+ */
+ memset(s->l1_table + new_l1_size, 0,
+ (s->l1_size - new_l1_size) * sizeof(uint64_t));
+ return ret;
+}
+
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
bool exact_size)
{
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 168fc32e7b..88d5a3f1ad 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -29,6 +29,7 @@
#include "block/qcow2.h"
#include "qemu/range.h"
#include "qemu/bswap.h"
+#include "qemu/cutils.h"
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
@@ -861,8 +862,24 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
}
s->set_refcount(refcount_block, block_index, refcount);
- if (refcount == 0 && s->discard_passthrough[type]) {
- update_refcount_discard(bs, cluster_offset, s->cluster_size);
+ if (refcount == 0) {
+ void *table;
+
+ table = qcow2_cache_is_table_offset(bs, s->refcount_block_cache,
+ offset);
+ if (table != NULL) {
+ qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
+ qcow2_cache_discard(bs, s->refcount_block_cache, table);
+ }
+
+ table = qcow2_cache_is_table_offset(bs, s->l2_table_cache, offset);
+ if (table != NULL) {
+ qcow2_cache_discard(bs, s->l2_table_cache, table);
+ }
+
+ if (s->discard_passthrough[type]) {
+ update_refcount_discard(bs, cluster_offset, s->cluster_size);
+ }
}
}
@@ -3045,3 +3062,122 @@ done:
qemu_vfree(new_refblock);
return ret;
}
+
+static int qcow2_discard_refcount_block(BlockDriverState *bs,
+ uint64_t discard_block_offs)
+{
+ BDRVQcow2State *s = bs->opaque;
+ uint64_t refblock_offs = get_refblock_offset(s, discard_block_offs);
+ uint64_t cluster_index = discard_block_offs >> s->cluster_bits;
+ uint32_t block_index = cluster_index & (s->refcount_block_size - 1);
+ void *refblock;
+ int ret;
+
+ assert(discard_block_offs != 0);
+
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs,
+ &refblock);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (s->get_refcount(refblock, block_index) != 1) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Invalid refcount:"
+ " refblock offset %#" PRIx64
+ ", reftable index %u"
+ ", block offset %#" PRIx64
+ ", refcount %#" PRIx64,
+ refblock_offs,
+ offset_to_reftable_index(s, discard_block_offs),
+ discard_block_offs,
+ s->get_refcount(refblock, block_index));
+ qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
+ return -EINVAL;
+ }
+ s->set_refcount(refblock, block_index, 0);
+
+ qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, refblock);
+
+ qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
+
+ if (cluster_index < s->free_cluster_index) {
+ s->free_cluster_index = cluster_index;
+ }
+
+ refblock = qcow2_cache_is_table_offset(bs, s->refcount_block_cache,
+ discard_block_offs);
+ if (refblock) {
+ /* discard refblock from the cache if refblock is cached */
+ qcow2_cache_discard(bs, s->refcount_block_cache, refblock);
+ }
+ update_refcount_discard(bs, discard_block_offs, s->cluster_size);
+
+ return 0;
+}
+
+int qcow2_shrink_reftable(BlockDriverState *bs)
+{
+ BDRVQcow2State *s = bs->opaque;
+ uint64_t *reftable_tmp =
+ g_malloc(s->refcount_table_size * sizeof(uint64_t));
+ int i, ret;
+
+ for (i = 0; i < s->refcount_table_size; i++) {
+ int64_t refblock_offs = s->refcount_table[i] & REFT_OFFSET_MASK;
+ void *refblock;
+ bool unused_block;
+
+ if (refblock_offs == 0) {
+ reftable_tmp[i] = 0;
+ continue;
+ }
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs,
+ &refblock);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* the refblock has own reference */
+ if (i == offset_to_reftable_index(s, refblock_offs)) {
+ uint64_t block_index = (refblock_offs >> s->cluster_bits) &
+ (s->refcount_block_size - 1);
+ uint64_t refcount = s->get_refcount(refblock, block_index);
+
+ s->set_refcount(refblock, block_index, 0);
+
+ unused_block = buffer_is_zero(refblock, s->cluster_size);
+
+ s->set_refcount(refblock, block_index, refcount);
+ } else {
+ unused_block = buffer_is_zero(refblock, s->cluster_size);
+ }
+ qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
+
+ reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]);
+ }
+
+ ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset, reftable_tmp,
+ s->refcount_table_size * sizeof(uint64_t));
+ /*
+ * If the write in the reftable failed the image may contain a partially
+ * overwritten reftable. In this case it would be better to clear the
+ * reftable in memory to avoid possible image corruption.
+ */
+ for (i = 0; i < s->refcount_table_size; i++) {
+ if (s->refcount_table[i] && !reftable_tmp[i]) {
+ if (ret == 0) {
+ ret = qcow2_discard_refcount_block(bs, s->refcount_table[i] &
+ REFT_OFFSET_MASK);
+ }
+ s->refcount_table[i] = 0;
+ }
+ }
+
+ if (!s->cache_discards) {
+ qcow2_process_discards(bs, ret);
+ }
+
+out:
+ g_free(reftable_tmp);
+ return ret;
+}
diff --git a/block/qcow2.c b/block/qcow2.c
index d33fb3ecdd..970006fc1d 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3104,18 +3104,43 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
}
old_length = bs->total_sectors * 512;
+ new_l1_size = size_to_l1(s, offset);
- /* shrinking is currently not supported */
if (offset < old_length) {
- error_setg(errp, "qcow2 doesn't support shrinking images yet");
- return -ENOTSUP;
- }
+ if (prealloc != PREALLOC_MODE_OFF) {
+ error_setg(errp,
+ "Preallocation can't be used for shrinking an image");
+ return -EINVAL;
+ }
- new_l1_size = size_to_l1(s, offset);
- ret = qcow2_grow_l1_table(bs, new_l1_size, true);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Failed to grow the L1 table");
- return ret;
+ ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size),
+ old_length - ROUND_UP(offset,
+ s->cluster_size),
+ QCOW2_DISCARD_ALWAYS, true);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to discard cropped clusters");
+ return ret;
+ }
+
+ ret = qcow2_shrink_l1_table(bs, new_l1_size);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Failed to reduce the number of L2 tables");
+ return ret;
+ }
+
+ ret = qcow2_shrink_reftable(bs);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Failed to discard unused refblocks");
+ return ret;
+ }
+ } else {
+ ret = qcow2_grow_l1_table(bs, new_l1_size, true);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to grow the L1 table");
+ return ret;
+ }
}
switch (prealloc) {
diff --git a/block/qcow2.h b/block/qcow2.h
index 96a8d43c17..5a289a81e2 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -521,6 +521,18 @@ static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2)
return r1 > r2 ? r1 - r2 : r2 - r1;
}
+static inline
+uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
+{
+ return offset >> (s->refcount_block_bits + s->cluster_bits);
+}
+
+static inline uint64_t get_refblock_offset(BDRVQcow2State *s, uint64_t offset)
+{
+ uint32_t index = offset_to_reftable_index(s, offset);
+ return s->refcount_table[index] & REFT_OFFSET_MASK;
+}
+
/* qcow2.c functions */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t sector_num, int nb_sectors);
@@ -584,10 +596,12 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res,
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque, Error **errp);
+int qcow2_shrink_reftable(BlockDriverState *bs);
/* qcow2-cluster.c functions */
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
bool exact_size);
+int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size);
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
@@ -649,6 +663,9 @@ int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table);
void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table);
+void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c,
+ uint64_t offset);
+void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table);
/* qcow2-bitmap.c functions */
int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
diff --git a/block/replication.c b/block/replication.c
index bf4462c8e7..3a4e6822e4 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -157,6 +157,7 @@ static void replication_close(BlockDriverState *bs)
static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 6ba992c8d7..b291a88481 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -403,17 +403,19 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
schedule_next_request(tgm, is_write);
qemu_mutex_unlock(&tg->lock);
}
+
+ g_free(data);
}
static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
{
Coroutine *co;
- RestartData rd = {
- .tgm = tgm,
- .is_write = is_write
- };
+ RestartData *rd = g_new0(RestartData, 1);
+
+ rd->tgm = tgm;
+ rd->is_write = is_write;
- co = qemu_coroutine_create(throttle_group_restart_queue_entry, &rd);
+ co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd);
aio_co_enter(tgm->aio_context, co);
}
diff --git a/block/vvfat.c b/block/vvfat.c
index cbabb36f62..a0f2335894 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -57,15 +57,6 @@
static void checkpoint(void);
-#ifdef __MINGW32__
-void nonono(const char* file, int line, const char* msg) {
- fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg);
- exit(-5);
-}
-#undef assert
-#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
-#endif
-
#else
#define DLOG(a)
@@ -3211,6 +3202,7 @@ err:
static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
@@ -3270,24 +3262,11 @@ static void bdrv_vvfat_init(void)
block_init(bdrv_vvfat_init);
#ifdef DEBUG
-static void checkpoint(void) {
+static void checkpoint(void)
+{
assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
check1(vvv);
check2(vvv);
assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
-#if 0
- if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
- fprintf(stderr, "Nonono!\n");
- mapping_t* mapping;
- direntry_t* direntry;
- assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
- assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
- if (vvv->mapping.next<47)
- return;
- assert((mapping = array_get(&(vvv->mapping), 47)));
- assert(mapping->dir_index < vvv->directory.next);
- direntry = array_get(&(vvv->directory), mapping->dir_index);
- assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
-#endif
}
#endif