Block layer patches

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJagxpPAAoJEH8JsnLIjy/WvR4P/2yD/Jt2WrT+z4hpVl9F9hHi
 D3bfIjh7Zue6HN1pMkRZ6kgi0CYS1wbFQDCLDXcN55DVZo1J6jqDZgWHvAeyigJw
 oAb/mvGCkgsavHuv1O4pRiF/e5Tkxh1Abu+r+3d1sNEaw+qfXcGNg1Rphtbn4fDE
 kVSUlWtyVG47oLV/ltI//dW+0ueu2CoCiWsVE/0B0DAX5TWEZ6IfeTR/cFeYnAf5
 3LgOjMLnAN7XJWZ9znHGNLP0nuHLL3IWVJ8dEz+B7qIFVaftrIRdxqHynhsTWxsB
 u4+RgSaf2ebviI7m4obgaCtyQDSdmvJmrM7UGMRQWt0TXX4df/0ccBLFqgxwygAc
 QQ43wYInbj9HlZ8B/NzzuCGM9XzD2nGu9T5PxNbj9XjPQ7As2Itwrwwcw4rjIfrX
 zzRfTa8Z06Ro+QTNHg4FM7FPFqnKD9vDQkYkDy2L+fqBJKWYIL+wWT1znJsPlQLe
 maP4FeptPpgsP21HZoaJ1TtHvAuLckUa0nwZiYIFsZ+oNayzXvKHGcElC5HxFVNR
 KK2ERnFebXtXNSJxXN4M5WjGCykII71fCFYI8jJLy1MVcvZnOhB/xkfGpi9i6VNM
 lGDucPXCr0HwID2Sge9+2gwIa1K8okgn47S1KMhLK6zutmovFX/gd+zxk9WbDv/O
 s+hRru7oxLayS41h54PP
 =gwPJ
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches

# gpg: Signature made Tue 13 Feb 2018 17:03:11 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# 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: (55 commits)
  iotests: Add l2-cache-entry-size to iotest 137
  iotests: Test downgrading an image using a small L2 slice size
  iotests: Test valid values of l2-cache-entry-size
  qcow2: Allow configuring the L2 slice size
  qcow2: Rename l2_table in count_cow_clusters()
  qcow2: Rename l2_table in count_contiguous_clusters_unallocated()
  qcow2: Rename l2_table in count_contiguous_clusters()
  qcow2: Rename l2_table in qcow2_alloc_compressed_cluster_offset()
  qcow2: Update qcow2_truncate() to support L2 slices
  qcow2: Update expand_zero_clusters_in_l1() to support L2 slices
  qcow2: Prepare expand_zero_clusters_in_l1() for adding L2 slice support
  qcow2: Read refcount before L2 table in expand_zero_clusters_in_l1()
  qcow2: Update qcow2_update_snapshot_refcount() to support L2 slices
  qcow2: Prepare qcow2_update_snapshot_refcount() for adding L2 slice support
  qcow2: Update zero_single_l2() to support L2 slices
  qcow2: Update discard_single_l2() to support L2 slices
  qcow2: Update handle_alloc() to support L2 slices
  qcow2: Update handle_copied() to support L2 slices
  qcow2: Update qcow2_alloc_cluster_link_l2() to support L2 slices
  qcow2: Update qcow2_get_cluster_offset() to support L2 slices
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-02-13 19:57:46 +00:00
commit 685a4eaf6d
27 changed files with 824 additions and 565 deletions

View File

@ -52,8 +52,6 @@ struct BdrvDirtyBitmap {
Such operations must fail and both the image Such operations must fail and both the image
and this bitmap must remain unchanged while and this bitmap must remain unchanged while
this flag is set. */ this flag is set. */
bool autoload; /* For persistent bitmaps: bitmap must be
autoloaded on image opening */
bool persistent; /* bitmap must be saved to owner disk image */ bool persistent; /* bitmap must be saved to owner disk image */
QLIST_ENTRY(BdrvDirtyBitmap) list; QLIST_ENTRY(BdrvDirtyBitmap) list;
}; };
@ -104,7 +102,6 @@ void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
g_free(bitmap->name); g_free(bitmap->name);
bitmap->name = NULL; bitmap->name = NULL;
bitmap->persistent = false; bitmap->persistent = false;
bitmap->autoload = false;
} }
/* Called with BQL taken. */ /* Called with BQL taken. */
@ -261,8 +258,6 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
bitmap->successor = NULL; bitmap->successor = NULL;
successor->persistent = bitmap->persistent; successor->persistent = bitmap->persistent;
bitmap->persistent = false; bitmap->persistent = false;
successor->autoload = bitmap->autoload;
bitmap->autoload = false;
bdrv_release_dirty_bitmap(bs, bitmap); bdrv_release_dirty_bitmap(bs, bitmap);
return successor; return successor;
@ -666,19 +661,6 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
return false; return false;
} }
/* Called with BQL taken. */
void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload)
{
qemu_mutex_lock(bitmap->mutex);
bitmap->autoload = autoload;
qemu_mutex_unlock(bitmap->mutex);
}
bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap)
{
return bitmap->autoload;
}
/* Called with BQL taken. */ /* Called with BQL taken. */
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
{ {

View File

@ -965,12 +965,68 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
} }
#endif #endif
static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
PreallocMode prealloc, Error **errp)
{
int64_t current_length;
current_length = glfs_lseek(fd, 0, SEEK_END);
if (current_length < 0) {
error_setg_errno(errp, errno, "Failed to determine current size");
return -errno;
}
if (current_length > offset && prealloc != PREALLOC_MODE_OFF) {
error_setg(errp, "Cannot use preallocation for shrinking files");
return -ENOTSUP;
}
if (current_length == offset) {
return 0;
}
switch (prealloc) {
#ifdef CONFIG_GLUSTERFS_FALLOCATE
case PREALLOC_MODE_FALLOC:
if (glfs_fallocate(fd, 0, current_length, offset - current_length)) {
error_setg_errno(errp, errno, "Could not preallocate data");
return -errno;
}
break;
#endif /* CONFIG_GLUSTERFS_FALLOCATE */
#ifdef CONFIG_GLUSTERFS_ZEROFILL
case PREALLOC_MODE_FULL:
if (glfs_ftruncate(fd, offset)) {
error_setg_errno(errp, errno, "Could not resize file");
return -errno;
}
if (glfs_zerofill(fd, current_length, offset - current_length)) {
error_setg_errno(errp, errno, "Could not zerofill the new area");
return -errno;
}
break;
#endif /* CONFIG_GLUSTERFS_ZEROFILL */
case PREALLOC_MODE_OFF:
if (glfs_ftruncate(fd, offset)) {
error_setg_errno(errp, errno, "Could not resize file");
return -errno;
}
break;
default:
error_setg(errp, "Unsupported preallocation mode: %s",
PreallocMode_str(prealloc));
return -EINVAL;
}
return 0;
}
static int qemu_gluster_create(const char *filename, static int qemu_gluster_create(const char *filename,
QemuOpts *opts, Error **errp) QemuOpts *opts, Error **errp)
{ {
BlockdevOptionsGluster *gconf; BlockdevOptionsGluster *gconf;
struct glfs *glfs; struct glfs *glfs;
struct glfs_fd *fd; struct glfs_fd *fd = NULL;
int ret = 0; int ret = 0;
PreallocMode prealloc; PreallocMode prealloc;
int64_t total_size = 0; int64_t total_size = 0;
@ -1019,45 +1075,14 @@ static int qemu_gluster_create(const char *filename,
goto out; goto out;
} }
switch (prealloc) { ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);
#ifdef CONFIG_GLUSTERFS_FALLOCATE
case PREALLOC_MODE_FALLOC:
if (glfs_fallocate(fd, 0, 0, total_size)) {
error_setg(errp, "Could not preallocate data for the new file");
ret = -errno;
}
break;
#endif /* CONFIG_GLUSTERFS_FALLOCATE */
#ifdef CONFIG_GLUSTERFS_ZEROFILL
case PREALLOC_MODE_FULL:
if (!glfs_ftruncate(fd, total_size)) {
if (glfs_zerofill(fd, 0, total_size)) {
error_setg(errp, "Could not zerofill the new file");
ret = -errno;
}
} else {
error_setg(errp, "Could not resize file");
ret = -errno;
}
break;
#endif /* CONFIG_GLUSTERFS_ZEROFILL */
case PREALLOC_MODE_OFF:
if (glfs_ftruncate(fd, total_size) != 0) {
ret = -errno;
error_setg(errp, "Could not resize file");
}
break;
default:
ret = -EINVAL;
error_setg(errp, "Unsupported preallocation mode: %s",
PreallocMode_str(prealloc));
break;
}
if (glfs_close(fd) != 0) {
ret = -errno;
}
out: out:
if (fd) {
if (glfs_close(fd) != 0 && ret == 0) {
ret = -errno;
}
}
qapi_free_BlockdevOptionsGluster(gconf); qapi_free_BlockdevOptionsGluster(gconf);
glfs_clear_preopened(glfs); glfs_clear_preopened(glfs);
return ret; return ret;
@ -1097,23 +1122,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp) PreallocMode prealloc, Error **errp)
{ {
int ret;
BDRVGlusterState *s = bs->opaque; BDRVGlusterState *s = bs->opaque;
return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
if (prealloc != PREALLOC_MODE_OFF) {
error_setg(errp, "Unsupported preallocation mode '%s'",
PreallocMode_str(prealloc));
return -ENOTSUP;
}
ret = glfs_ftruncate(s->fd, offset);
if (ret < 0) {
ret = -errno;
error_setg_errno(errp, -ret, "Failed to truncate file");
return ret;
}
return 0;
} }
static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,

View File

@ -933,14 +933,14 @@ static void set_readonly_helper(gpointer bitmap, gpointer value)
bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value);
} }
/* qcow2_load_autoloading_dirty_bitmaps() /* qcow2_load_dirty_bitmaps()
* Return value is a hint for caller: true means that the Qcow2 header was * Return value is a hint for caller: true means that the Qcow2 header was
* updated. (false doesn't mean that the header should be updated by the * updated. (false doesn't mean that the header should be updated by the
* caller, it just means that updating was not needed or the image cannot be * caller, it just means that updating was not needed or the image cannot be
* written to). * written to).
* On failure the function returns false. * On failure the function returns false.
*/ */
bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp) bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
Qcow2BitmapList *bm_list; Qcow2BitmapList *bm_list;
@ -960,14 +960,16 @@ bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp)
} }
QSIMPLEQ_FOREACH(bm, bm_list, entry) { QSIMPLEQ_FOREACH(bm, bm_list, entry) {
if ((bm->flags & BME_FLAG_AUTO) && !(bm->flags & BME_FLAG_IN_USE)) { if (!(bm->flags & BME_FLAG_IN_USE)) {
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
if (bitmap == NULL) { if (bitmap == NULL) {
goto fail; goto fail;
} }
if (!(bm->flags & BME_FLAG_AUTO)) {
bdrv_disable_dirty_bitmap(bitmap);
}
bdrv_dirty_bitmap_set_persistance(bitmap, true); bdrv_dirty_bitmap_set_persistance(bitmap, true);
bdrv_dirty_bitmap_set_autoload(bitmap, true);
bm->flags |= BME_FLAG_IN_USE; bm->flags |= BME_FLAG_IN_USE;
created_dirty_bitmaps = created_dirty_bitmaps =
g_slist_append(created_dirty_bitmaps, bitmap); g_slist_append(created_dirty_bitmaps, bitmap);
@ -1369,7 +1371,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
bm->table.size = 0; bm->table.size = 0;
QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry);
} }
bm->flags = bdrv_dirty_bitmap_get_autoload(bitmap) ? BME_FLAG_AUTO : 0; bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0;
bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap)); bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap));
bm->dirty_bitmap = bitmap; bm->dirty_bitmap = bitmap;
} }

View File

@ -39,26 +39,23 @@ struct Qcow2Cache {
Qcow2CachedTable *entries; Qcow2CachedTable *entries;
struct Qcow2Cache *depends; struct Qcow2Cache *depends;
int size; int size;
int table_size;
bool depends_on_flush; bool depends_on_flush;
void *table_array; void *table_array;
uint64_t lru_counter; uint64_t lru_counter;
uint64_t cache_clean_lru_counter; uint64_t cache_clean_lru_counter;
}; };
static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table)
Qcow2Cache *c, int table)
{ {
BDRVQcow2State *s = bs->opaque; return (uint8_t *) c->table_array + (size_t) table * c->table_size;
return (uint8_t *) c->table_array + (size_t) table * s->cluster_size;
} }
static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table)
Qcow2Cache *c, void *table)
{ {
BDRVQcow2State *s = bs->opaque;
ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array;
int idx = table_offset / s->cluster_size; int idx = table_offset / c->table_size;
assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0);
return idx; return idx;
} }
@ -74,15 +71,13 @@ static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c)
} }
} }
static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables)
int i, int num_tables)
{ {
/* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ /* Using MADV_DONTNEED to discard memory is a Linux-specific feature */
#ifdef CONFIG_LINUX #ifdef CONFIG_LINUX
BDRVQcow2State *s = bs->opaque; void *t = qcow2_cache_get_table_addr(c, i);
void *t = qcow2_cache_get_table_addr(bs, c, i);
int align = getpagesize(); int align = getpagesize();
size_t mem_size = (size_t) s->cluster_size * num_tables; size_t mem_size = (size_t) c->table_size * num_tables;
size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t;
size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align);
if (mem_size > offset && length > 0) { if (mem_size > offset && length > 0) {
@ -98,7 +93,7 @@ static inline bool can_clean_entry(Qcow2Cache *c, int i)
t->lru_counter <= c->cache_clean_lru_counter; t->lru_counter <= c->cache_clean_lru_counter;
} }
void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) void qcow2_cache_clean_unused(Qcow2Cache *c)
{ {
int i = 0; int i = 0;
while (i < c->size) { while (i < c->size) {
@ -118,23 +113,30 @@ void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c)
} }
if (to_clean > 0) { if (to_clean > 0) {
qcow2_cache_table_release(bs, c, i - to_clean, to_clean); qcow2_cache_table_release(c, i - to_clean, to_clean);
} }
} }
c->cache_clean_lru_counter = c->lru_counter; c->cache_clean_lru_counter = c->lru_counter;
} }
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
unsigned table_size)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
Qcow2Cache *c; Qcow2Cache *c;
assert(num_tables > 0);
assert(is_power_of_2(table_size));
assert(table_size >= (1 << MIN_CLUSTER_BITS));
assert(table_size <= s->cluster_size);
c = g_new0(Qcow2Cache, 1); c = g_new0(Qcow2Cache, 1);
c->size = num_tables; c->size = num_tables;
c->table_size = table_size;
c->entries = g_try_new0(Qcow2CachedTable, num_tables); c->entries = g_try_new0(Qcow2CachedTable, num_tables);
c->table_array = qemu_try_blockalign(bs->file->bs, c->table_array = qemu_try_blockalign(bs->file->bs,
(size_t) num_tables * s->cluster_size); (size_t) num_tables * c->table_size);
if (!c->entries || !c->table_array) { if (!c->entries || !c->table_array) {
qemu_vfree(c->table_array); qemu_vfree(c->table_array);
@ -146,7 +148,7 @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
return c; return c;
} }
int qcow2_cache_destroy(BlockDriverState *bs, Qcow2Cache *c) int qcow2_cache_destroy(Qcow2Cache *c)
{ {
int i; int i;
@ -203,13 +205,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
if (c == s->refcount_block_cache) { if (c == s->refcount_block_cache) {
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
c->entries[i].offset, s->cluster_size); c->entries[i].offset, c->table_size);
} else if (c == s->l2_table_cache) { } else if (c == s->l2_table_cache) {
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
c->entries[i].offset, s->cluster_size); c->entries[i].offset, c->table_size);
} else { } else {
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
c->entries[i].offset, s->cluster_size); c->entries[i].offset, c->table_size);
} }
if (ret < 0) { if (ret < 0) {
@ -223,7 +225,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
} }
ret = bdrv_pwrite(bs->file, c->entries[i].offset, ret = bdrv_pwrite(bs->file, c->entries[i].offset,
qcow2_cache_get_table_addr(bs, c, i), s->cluster_size); qcow2_cache_get_table_addr(c, i), c->table_size);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -309,7 +311,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
c->entries[i].lru_counter = 0; c->entries[i].lru_counter = 0;
} }
qcow2_cache_table_release(bs, c, 0, c->size); qcow2_cache_table_release(c, 0, c->size);
c->lru_counter = 0; c->lru_counter = 0;
@ -331,7 +333,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache,
offset, read_from_disk); offset, read_from_disk);
if (offset_into_cluster(s, offset)) { if (!QEMU_IS_ALIGNED(offset, c->table_size)) {
qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s "
"cache: Offset %#" PRIx64 " is unaligned", "cache: Offset %#" PRIx64 " is unaligned",
qcow2_cache_get_name(s, c), offset); qcow2_cache_get_name(s, c), offset);
@ -339,7 +341,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
} }
/* Check if the table is already cached */ /* Check if the table is already cached */
i = lookup_index = (offset / s->cluster_size * 4) % c->size; i = lookup_index = (offset / c->table_size * 4) % c->size;
do { do {
const Qcow2CachedTable *t = &c->entries[i]; const Qcow2CachedTable *t = &c->entries[i];
if (t->offset == offset) { if (t->offset == offset) {
@ -379,8 +381,8 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
} }
ret = bdrv_pread(bs->file, offset, ret = bdrv_pread(bs->file, offset,
qcow2_cache_get_table_addr(bs, c, i), qcow2_cache_get_table_addr(c, i),
s->cluster_size); c->table_size);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -391,7 +393,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
/* And return the right table */ /* And return the right table */
found: found:
c->entries[i].ref++; c->entries[i].ref++;
*table = qcow2_cache_get_table_addr(bs, c, i); *table = qcow2_cache_get_table_addr(c, i);
trace_qcow2_cache_get_done(qemu_coroutine_self(), trace_qcow2_cache_get_done(qemu_coroutine_self(),
c == s->l2_table_cache, i); c == s->l2_table_cache, i);
@ -411,9 +413,9 @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
return qcow2_cache_do_get(bs, c, offset, table, false); return qcow2_cache_do_get(bs, c, offset, table, false);
} }
void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) void qcow2_cache_put(Qcow2Cache *c, void **table)
{ {
int i = qcow2_cache_get_table_idx(bs, c, *table); int i = qcow2_cache_get_table_idx(c, *table);
c->entries[i].ref--; c->entries[i].ref--;
*table = NULL; *table = NULL;
@ -425,30 +427,28 @@ void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
assert(c->entries[i].ref >= 0); assert(c->entries[i].ref >= 0);
} }
void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
void *table)
{ {
int i = qcow2_cache_get_table_idx(bs, c, table); int i = qcow2_cache_get_table_idx(c, table);
assert(c->entries[i].offset != 0); assert(c->entries[i].offset != 0);
c->entries[i].dirty = true; c->entries[i].dirty = true;
} }
void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset)
uint64_t offset)
{ {
int i; int i;
for (i = 0; i < c->size; i++) { for (i = 0; i < c->size; i++) {
if (c->entries[i].offset == offset) { if (c->entries[i].offset == offset) {
return qcow2_cache_get_table_addr(bs, c, i); return qcow2_cache_get_table_addr(c, i);
} }
} }
return NULL; return NULL;
} }
void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table) void qcow2_cache_discard(Qcow2Cache *c, void *table)
{ {
int i = qcow2_cache_get_table_idx(bs, c, table); int i = qcow2_cache_get_table_idx(c, table);
assert(c->entries[i].ref == 0); assert(c->entries[i].ref == 0);
@ -456,5 +456,5 @@ void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table)
c->entries[i].lru_counter = 0; c->entries[i].lru_counter = 0;
c->entries[i].dirty = false; c->entries[i].dirty = false;
qcow2_cache_table_release(bs, c, i, 1); qcow2_cache_table_release(c, i, 1);
} }

File diff suppressed because it is too large Load Diff

View File

@ -277,7 +277,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
block_index = cluster_index & (s->refcount_block_size - 1); block_index = cluster_index & (s->refcount_block_size - 1);
*refcount = s->get_refcount(refcount_block, block_index); *refcount = s->get_refcount(refcount_block, block_index);
qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); qcow2_cache_put(s->refcount_block_cache, &refcount_block);
return 0; return 0;
} }
@ -421,7 +421,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
/* Now the new refcount block needs to be written to disk */ /* Now the new refcount block needs to be written to disk */
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE);
qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, *refcount_block); qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block);
ret = qcow2_cache_flush(bs, s->refcount_block_cache); ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
@ -449,7 +449,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
return -EAGAIN; return -EAGAIN;
} }
qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); qcow2_cache_put(s->refcount_block_cache, refcount_block);
/* /*
* If we come here, we need to grow the refcount table. Again, a new * If we come here, we need to grow the refcount table. Again, a new
@ -501,7 +501,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
fail: fail:
if (*refcount_block != NULL) { if (*refcount_block != NULL) {
qcow2_cache_put(bs, s->refcount_block_cache, refcount_block); qcow2_cache_put(s->refcount_block_cache, refcount_block);
} }
return ret; return ret;
} }
@ -623,7 +623,7 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
goto fail; goto fail;
} }
memset(refblock_data, 0, s->cluster_size); memset(refblock_data, 0, s->cluster_size);
qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, qcow2_cache_entry_mark_dirty(s->refcount_block_cache,
refblock_data); refblock_data);
new_table[i] = block_offset; new_table[i] = block_offset;
@ -656,11 +656,11 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
s->set_refcount(refblock_data, j, 1); s->set_refcount(refblock_data, j, 1);
} }
qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, qcow2_cache_entry_mark_dirty(s->refcount_block_cache,
refblock_data); refblock_data);
} }
qcow2_cache_put(bs, s->refcount_block_cache, &refblock_data); qcow2_cache_put(s->refcount_block_cache, &refblock_data);
} }
assert(block_offset == table_offset); assert(block_offset == table_offset);
@ -836,7 +836,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
/* Load the refcount block and allocate it if needed */ /* Load the refcount block and allocate it if needed */
if (table_index != old_table_index) { if (table_index != old_table_index) {
if (refcount_block) { if (refcount_block) {
qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); qcow2_cache_put(s->refcount_block_cache, &refcount_block);
} }
ret = alloc_refcount_block(bs, cluster_index, &refcount_block); ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
if (ret < 0) { if (ret < 0) {
@ -845,8 +845,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
} }
old_table_index = table_index; old_table_index = table_index;
qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block);
refcount_block);
/* we can update the count and save it */ /* we can update the count and save it */
block_index = cluster_index & (s->refcount_block_size - 1); block_index = cluster_index & (s->refcount_block_size - 1);
@ -872,16 +871,16 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
if (refcount == 0) { if (refcount == 0) {
void *table; void *table;
table = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, table = qcow2_cache_is_table_offset(s->refcount_block_cache,
offset); offset);
if (table != NULL) { if (table != NULL) {
qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); qcow2_cache_put(s->refcount_block_cache, &refcount_block);
qcow2_cache_discard(bs, s->refcount_block_cache, table); qcow2_cache_discard(s->refcount_block_cache, table);
} }
table = qcow2_cache_is_table_offset(bs, s->l2_table_cache, offset); table = qcow2_cache_is_table_offset(s->l2_table_cache, offset);
if (table != NULL) { if (table != NULL) {
qcow2_cache_discard(bs, s->l2_table_cache, table); qcow2_cache_discard(s->l2_table_cache, table);
} }
if (s->discard_passthrough[type]) { if (s->discard_passthrough[type]) {
@ -898,7 +897,7 @@ fail:
/* Write last changed block to disk */ /* Write last changed block to disk */
if (refcount_block) { if (refcount_block) {
qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block); qcow2_cache_put(s->refcount_block_cache, &refcount_block);
} }
/* /*
@ -1184,17 +1183,20 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int64_t l1_table_offset, int l1_size, int addend) int64_t l1_table_offset, int l1_size, int addend)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
uint64_t *l1_table, *l2_table, l2_offset, entry, l1_size2, refcount; uint64_t *l1_table, *l2_slice, l2_offset, entry, l1_size2, refcount;
bool l1_allocated = false; bool l1_allocated = false;
int64_t old_entry, old_l2_offset; int64_t old_entry, old_l2_offset;
unsigned slice, slice_size2, n_slices;
int i, j, l1_modified = 0, nb_csectors; int i, j, l1_modified = 0, nb_csectors;
int ret; int ret;
assert(addend >= -1 && addend <= 1); assert(addend >= -1 && addend <= 1);
l2_table = NULL; l2_slice = NULL;
l1_table = NULL; l1_table = NULL;
l1_size2 = l1_size * sizeof(uint64_t); l1_size2 = l1_size * sizeof(uint64_t);
slice_size2 = s->l2_slice_size * sizeof(uint64_t);
n_slices = s->cluster_size / slice_size2;
s->cache_discards = true; s->cache_discards = true;
@ -1237,92 +1239,98 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
goto fail; goto fail;
} }
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, for (slice = 0; slice < n_slices; slice++) {
(void**) &l2_table); ret = qcow2_cache_get(bs, s->l2_table_cache,
if (ret < 0) { l2_offset + slice * slice_size2,
goto fail; (void **) &l2_slice);
} if (ret < 0) {
goto fail;
}
for (j = 0; j < s->l2_size; j++) { for (j = 0; j < s->l2_slice_size; j++) {
uint64_t cluster_index; uint64_t cluster_index;
uint64_t offset; uint64_t offset;
entry = be64_to_cpu(l2_table[j]); entry = be64_to_cpu(l2_slice[j]);
old_entry = entry; old_entry = entry;
entry &= ~QCOW_OFLAG_COPIED; entry &= ~QCOW_OFLAG_COPIED;
offset = entry & L2E_OFFSET_MASK; offset = entry & L2E_OFFSET_MASK;
switch (qcow2_get_cluster_type(entry)) { switch (qcow2_get_cluster_type(entry)) {
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
nb_csectors = ((entry >> s->csize_shift) & nb_csectors = ((entry >> s->csize_shift) &
s->csize_mask) + 1; s->csize_mask) + 1;
if (addend != 0) { if (addend != 0) {
ret = update_refcount(bs, ret = update_refcount(
(entry & s->cluster_offset_mask) & ~511, bs, (entry & s->cluster_offset_mask) & ~511,
nb_csectors * 512, abs(addend), addend < 0, nb_csectors * 512, abs(addend), addend < 0,
QCOW2_DISCARD_SNAPSHOT); QCOW2_DISCARD_SNAPSHOT);
if (ret < 0) {
goto fail;
}
}
/* compressed clusters are never modified */
refcount = 2;
break;
case QCOW2_CLUSTER_NORMAL:
case QCOW2_CLUSTER_ZERO_ALLOC:
if (offset_into_cluster(s, offset)) {
/* Here l2_index means table (not slice) index */
int l2_index = slice * s->l2_slice_size + j;
qcow2_signal_corruption(
bs, true, -1, -1, "Cluster "
"allocation offset %#" PRIx64
" unaligned (L2 offset: %#"
PRIx64 ", L2 index: %#x)",
offset, l2_offset, l2_index);
ret = -EIO;
goto fail;
}
cluster_index = offset >> s->cluster_bits;
assert(cluster_index);
if (addend != 0) {
ret = qcow2_update_cluster_refcount(
bs, cluster_index, abs(addend), addend < 0,
QCOW2_DISCARD_SNAPSHOT);
if (ret < 0) {
goto fail;
}
}
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
} break;
/* compressed clusters are never modified */
refcount = 2;
break;
case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_ZERO_ALLOC: case QCOW2_CLUSTER_UNALLOCATED:
if (offset_into_cluster(s, offset)) { refcount = 0;
qcow2_signal_corruption(bs, true, -1, -1, "Cluster " break;
"allocation offset %#" PRIx64
" unaligned (L2 offset: %#" default:
PRIx64 ", L2 index: %#x)", abort();
offset, l2_offset, j);
ret = -EIO;
goto fail;
} }
cluster_index = offset >> s->cluster_bits; if (refcount == 1) {
assert(cluster_index); entry |= QCOW_OFLAG_COPIED;
if (addend != 0) { }
ret = qcow2_update_cluster_refcount(bs, if (entry != old_entry) {
cluster_index, abs(addend), addend < 0, if (addend > 0) {
QCOW2_DISCARD_SNAPSHOT); qcow2_cache_set_dependency(bs, s->l2_table_cache,
if (ret < 0) { s->refcount_block_cache);
goto fail;
} }
l2_slice[j] = cpu_to_be64(entry);
qcow2_cache_entry_mark_dirty(s->l2_table_cache,
l2_slice);
} }
ret = qcow2_get_refcount(bs, cluster_index, &refcount);
if (ret < 0) {
goto fail;
}
break;
case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_UNALLOCATED:
refcount = 0;
break;
default:
abort();
} }
if (refcount == 1) { qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
entry |= QCOW_OFLAG_COPIED;
}
if (entry != old_entry) {
if (addend > 0) {
qcow2_cache_set_dependency(bs, s->l2_table_cache,
s->refcount_block_cache);
}
l2_table[j] = cpu_to_be64(entry);
qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,
l2_table);
}
} }
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
if (addend != 0) { if (addend != 0) {
ret = qcow2_update_cluster_refcount(bs, l2_offset >> ret = qcow2_update_cluster_refcount(bs, l2_offset >>
s->cluster_bits, s->cluster_bits,
@ -1348,8 +1356,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
ret = bdrv_flush(bs); ret = bdrv_flush(bs);
fail: fail:
if (l2_table) { if (l2_slice) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice);
} }
s->cache_discards = false; s->cache_discards = false;
@ -2849,7 +2857,7 @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable,
new_reftable_size, new_refblock, new_reftable_size, new_refblock,
new_refblock_empty, allocated, errp); new_refblock_empty, allocated, errp);
if (ret < 0) { if (ret < 0) {
qcow2_cache_put(bs, s->refcount_block_cache, &refblock); qcow2_cache_put(s->refcount_block_cache, &refblock);
return ret; return ret;
} }
@ -2862,7 +2870,7 @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable,
if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { if (new_refcount_bits < 64 && refcount >> new_refcount_bits) {
uint64_t offset; uint64_t offset;
qcow2_cache_put(bs, s->refcount_block_cache, &refblock); qcow2_cache_put(s->refcount_block_cache, &refblock);
offset = ((reftable_index << s->refcount_block_bits) offset = ((reftable_index << s->refcount_block_bits)
+ refblock_index) << s->cluster_bits; + refblock_index) << s->cluster_bits;
@ -2883,7 +2891,7 @@ static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable,
new_refblock_empty = new_refblock_empty && refcount == 0; new_refblock_empty = new_refblock_empty && refcount == 0;
} }
qcow2_cache_put(bs, s->refcount_block_cache, &refblock); qcow2_cache_put(s->refcount_block_cache, &refblock);
} else { } else {
/* No refblock means every refcount is 0 */ /* No refblock means every refcount is 0 */
for (refblock_index = 0; refblock_index < s->refcount_block_size; for (refblock_index = 0; refblock_index < s->refcount_block_size;
@ -3175,24 +3183,24 @@ static int qcow2_discard_refcount_block(BlockDriverState *bs,
offset_to_reftable_index(s, discard_block_offs), offset_to_reftable_index(s, discard_block_offs),
discard_block_offs, discard_block_offs,
s->get_refcount(refblock, block_index)); s->get_refcount(refblock, block_index));
qcow2_cache_put(bs, s->refcount_block_cache, &refblock); qcow2_cache_put(s->refcount_block_cache, &refblock);
return -EINVAL; return -EINVAL;
} }
s->set_refcount(refblock, block_index, 0); s->set_refcount(refblock, block_index, 0);
qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, refblock); qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock);
qcow2_cache_put(bs, s->refcount_block_cache, &refblock); qcow2_cache_put(s->refcount_block_cache, &refblock);
if (cluster_index < s->free_cluster_index) { if (cluster_index < s->free_cluster_index) {
s->free_cluster_index = cluster_index; s->free_cluster_index = cluster_index;
} }
refblock = qcow2_cache_is_table_offset(bs, s->refcount_block_cache, refblock = qcow2_cache_is_table_offset(s->refcount_block_cache,
discard_block_offs); discard_block_offs);
if (refblock) { if (refblock) {
/* discard refblock from the cache if refblock is cached */ /* discard refblock from the cache if refblock is cached */
qcow2_cache_discard(bs, s->refcount_block_cache, refblock); qcow2_cache_discard(s->refcount_block_cache, refblock);
} }
update_refcount_discard(bs, discard_block_offs, s->cluster_size); update_refcount_discard(bs, discard_block_offs, s->cluster_size);
@ -3235,7 +3243,7 @@ int qcow2_shrink_reftable(BlockDriverState *bs)
} else { } else {
unused_block = buffer_is_zero(refblock, s->cluster_size); unused_block = buffer_is_zero(refblock, s->cluster_size);
} }
qcow2_cache_put(bs, s->refcount_block_cache, &refblock); qcow2_cache_put(s->refcount_block_cache, &refblock);
reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]);
} }

View File

@ -675,6 +675,11 @@ static QemuOptsList qcow2_runtime_opts = {
.type = QEMU_OPT_SIZE, .type = QEMU_OPT_SIZE,
.help = "Maximum L2 table cache size", .help = "Maximum L2 table cache size",
}, },
{
.name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE,
.type = QEMU_OPT_SIZE,
.help = "Size of each entry in the L2 cache",
},
{ {
.name = QCOW2_OPT_REFCOUNT_CACHE_SIZE, .name = QCOW2_OPT_REFCOUNT_CACHE_SIZE,
.type = QEMU_OPT_SIZE, .type = QEMU_OPT_SIZE,
@ -706,8 +711,8 @@ static void cache_clean_timer_cb(void *opaque)
{ {
BlockDriverState *bs = opaque; BlockDriverState *bs = opaque;
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
qcow2_cache_clean_unused(bs, s->l2_table_cache); qcow2_cache_clean_unused(s->l2_table_cache);
qcow2_cache_clean_unused(bs, s->refcount_block_cache); qcow2_cache_clean_unused(s->refcount_block_cache);
timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
(int64_t) s->cache_clean_interval * 1000); (int64_t) s->cache_clean_interval * 1000);
} }
@ -747,6 +752,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs,
static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
uint64_t *l2_cache_size, uint64_t *l2_cache_size,
uint64_t *l2_cache_entry_size,
uint64_t *refcount_cache_size, Error **errp) uint64_t *refcount_cache_size, Error **errp)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
@ -762,6 +768,9 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
*refcount_cache_size = qemu_opt_get_size(opts, *refcount_cache_size = qemu_opt_get_size(opts,
QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0);
*l2_cache_entry_size = qemu_opt_get_size(
opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size);
if (combined_cache_size_set) { if (combined_cache_size_set) {
if (l2_cache_size_set && refcount_cache_size_set) { if (l2_cache_size_set && refcount_cache_size_set) {
error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE
@ -802,11 +811,21 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
/ DEFAULT_L2_REFCOUNT_SIZE_RATIO; / DEFAULT_L2_REFCOUNT_SIZE_RATIO;
} }
} }
if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) ||
*l2_cache_entry_size > s->cluster_size ||
!is_power_of_2(*l2_cache_entry_size)) {
error_setg(errp, "L2 cache entry size must be a power of two "
"between %d and the cluster size (%d)",
1 << MIN_CLUSTER_BITS, s->cluster_size);
return;
}
} }
typedef struct Qcow2ReopenState { typedef struct Qcow2ReopenState {
Qcow2Cache *l2_table_cache; Qcow2Cache *l2_table_cache;
Qcow2Cache *refcount_block_cache; Qcow2Cache *refcount_block_cache;
int l2_slice_size; /* Number of entries in a slice of the L2 table */
bool use_lazy_refcounts; bool use_lazy_refcounts;
int overlap_check; int overlap_check;
bool discard_passthrough[QCOW2_DISCARD_MAX]; bool discard_passthrough[QCOW2_DISCARD_MAX];
@ -823,7 +842,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
QemuOpts *opts = NULL; QemuOpts *opts = NULL;
const char *opt_overlap_check, *opt_overlap_check_template; const char *opt_overlap_check, *opt_overlap_check_template;
int overlap_check_template = 0; int overlap_check_template = 0;
uint64_t l2_cache_size, refcount_cache_size; uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size;
int i; int i;
const char *encryptfmt; const char *encryptfmt;
QDict *encryptopts = NULL; QDict *encryptopts = NULL;
@ -842,15 +861,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
} }
/* get L2 table/refcount block cache size from command line options */ /* get L2 table/refcount block cache size from command line options */
read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size,
&local_err); &refcount_cache_size, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
l2_cache_size /= s->cluster_size; l2_cache_size /= l2_cache_entry_size;
if (l2_cache_size < MIN_L2_CACHE_SIZE) { if (l2_cache_size < MIN_L2_CACHE_SIZE) {
l2_cache_size = MIN_L2_CACHE_SIZE; l2_cache_size = MIN_L2_CACHE_SIZE;
} }
@ -888,8 +907,11 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
} }
} }
r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t);
r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size,
l2_cache_entry_size);
r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size,
s->cluster_size);
if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) {
error_setg(errp, "Could not allocate metadata caches"); error_setg(errp, "Could not allocate metadata caches");
ret = -ENOMEM; ret = -ENOMEM;
@ -1044,13 +1066,14 @@ static void qcow2_update_options_commit(BlockDriverState *bs,
int i; int i;
if (s->l2_table_cache) { if (s->l2_table_cache) {
qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(s->l2_table_cache);
} }
if (s->refcount_block_cache) { if (s->refcount_block_cache) {
qcow2_cache_destroy(bs, s->refcount_block_cache); qcow2_cache_destroy(s->refcount_block_cache);
} }
s->l2_table_cache = r->l2_table_cache; s->l2_table_cache = r->l2_table_cache;
s->refcount_block_cache = r->refcount_block_cache; s->refcount_block_cache = r->refcount_block_cache;
s->l2_slice_size = r->l2_slice_size;
s->overlap_check = r->overlap_check; s->overlap_check = r->overlap_check;
s->use_lazy_refcounts = r->use_lazy_refcounts; s->use_lazy_refcounts = r->use_lazy_refcounts;
@ -1073,10 +1096,10 @@ static void qcow2_update_options_abort(BlockDriverState *bs,
Qcow2ReopenState *r) Qcow2ReopenState *r)
{ {
if (r->l2_table_cache) { if (r->l2_table_cache) {
qcow2_cache_destroy(bs, r->l2_table_cache); qcow2_cache_destroy(r->l2_table_cache);
} }
if (r->refcount_block_cache) { if (r->refcount_block_cache) {
qcow2_cache_destroy(bs, r->refcount_block_cache); qcow2_cache_destroy(r->refcount_block_cache);
} }
qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); qapi_free_QCryptoBlockOpenOptions(r->crypto_opts);
} }
@ -1460,7 +1483,7 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
} }
if (qcow2_load_autoloading_dirty_bitmaps(bs, &local_err)) { if (qcow2_load_dirty_bitmaps(bs, &local_err)) {
update_header = false; update_header = false;
} }
if (local_err != NULL) { if (local_err != NULL) {
@ -1514,10 +1537,10 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
s->l1_table = NULL; s->l1_table = NULL;
cache_clean_timer_del(bs); cache_clean_timer_del(bs);
if (s->l2_table_cache) { if (s->l2_table_cache) {
qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(s->l2_table_cache);
} }
if (s->refcount_block_cache) { if (s->refcount_block_cache) {
qcow2_cache_destroy(bs, s->refcount_block_cache); qcow2_cache_destroy(s->refcount_block_cache);
} }
qcrypto_block_free(s->crypto); qcrypto_block_free(s->crypto);
qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); qapi_free_QCryptoBlockOpenOptions(s->crypto_opts);
@ -2065,8 +2088,8 @@ static void qcow2_close(BlockDriverState *bs)
} }
cache_clean_timer_del(bs); cache_clean_timer_del(bs);
qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(s->l2_table_cache);
qcow2_cache_destroy(bs, s->refcount_block_cache); qcow2_cache_destroy(s->refcount_block_cache);
qcrypto_block_free(s->crypto); qcrypto_block_free(s->crypto);
s->crypto = NULL; s->crypto = NULL;
@ -3259,9 +3282,9 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
host_offset = allocation_start; host_offset = allocation_start;
guest_offset = old_length; guest_offset = old_length;
while (nb_new_data_clusters) { while (nb_new_data_clusters) {
int64_t guest_cluster = guest_offset >> s->cluster_bits; int64_t nb_clusters = MIN(
int64_t nb_clusters = MIN(nb_new_data_clusters, nb_new_data_clusters,
s->l2_size - guest_cluster % s->l2_size); s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset));
QCowL2Meta allocation = { QCowL2Meta allocation = {
.offset = guest_offset, .offset = guest_offset,
.alloc_offset = host_offset, .alloc_offset = host_offset,

View File

@ -68,7 +68,7 @@
#define MAX_CLUSTER_BITS 21 #define MAX_CLUSTER_BITS 21
/* Must be at least 2 to cover COW */ /* Must be at least 2 to cover COW */
#define MIN_L2_CACHE_SIZE 2 /* clusters */ #define MIN_L2_CACHE_SIZE 2 /* cache entries */
/* Must be at least 4 to cover all cases of refcount table growth */ /* Must be at least 4 to cover all cases of refcount table growth */
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
@ -100,6 +100,7 @@
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
#define QCOW2_OPT_CACHE_SIZE "cache-size" #define QCOW2_OPT_CACHE_SIZE "cache-size"
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" #define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size"
#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" #define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval"
@ -251,6 +252,7 @@ typedef struct BDRVQcow2State {
int cluster_bits; int cluster_bits;
int cluster_size; int cluster_size;
int cluster_sectors; int cluster_sectors;
int l2_slice_size;
int l2_bits; int l2_bits;
int l2_size; int l2_size;
int l1_size; int l1_size;
@ -463,11 +465,21 @@ static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size)
return (size + (1ULL << shift) - 1) >> shift; return (size + (1ULL << shift) - 1) >> shift;
} }
static inline int offset_to_l1_index(BDRVQcow2State *s, uint64_t offset)
{
return offset >> (s->l2_bits + s->cluster_bits);
}
static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset)
{ {
return (offset >> s->cluster_bits) & (s->l2_size - 1); return (offset >> s->cluster_bits) & (s->l2_size - 1);
} }
static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset)
{
return (offset >> s->cluster_bits) & (s->l2_slice_size - 1);
}
static inline int64_t align_offset(int64_t offset, int n) static inline int64_t align_offset(int64_t offset, int n)
{ {
offset = (offset + n - 1) & ~(n - 1); offset = (offset + n - 1) & ~(n - 1);
@ -636,34 +648,33 @@ void qcow2_free_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs);
/* qcow2-cache.c functions */ /* qcow2-cache.c functions */
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); unsigned table_size);
int qcow2_cache_destroy(Qcow2Cache *c);
void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c, void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
void *table);
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c);
int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
Qcow2Cache *dependency); Qcow2Cache *dependency);
void qcow2_cache_depends_on_flush(Qcow2Cache *c); void qcow2_cache_depends_on_flush(Qcow2Cache *c);
void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c); void qcow2_cache_clean_unused(Qcow2Cache *c);
int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c);
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table); void **table);
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table); void **table);
void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); void qcow2_cache_put(Qcow2Cache *c, void **table);
void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c, void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset);
uint64_t offset); void qcow2_cache_discard(Qcow2Cache *c, void *table);
void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table);
/* qcow2-bitmap.c functions */ /* qcow2-bitmap.c functions */
int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
void **refcount_table, void **refcount_table,
int64_t *refcount_table_size); int64_t *refcount_table_size);
bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp); bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp);
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);

View File

@ -1826,40 +1826,34 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
return 0; return 0;
} }
static int sd_prealloc(const char *filename, Error **errp) static int sd_prealloc(BlockDriverState *bs, int64_t old_size, int64_t new_size,
Error **errp)
{ {
BlockBackend *blk = NULL; BlockBackend *blk = NULL;
BDRVSheepdogState *base = NULL; BDRVSheepdogState *base = bs->opaque;
unsigned long buf_size; unsigned long buf_size;
uint32_t idx, max_idx; uint32_t idx, max_idx;
uint32_t object_size; uint32_t object_size;
int64_t vdi_size;
void *buf = NULL; void *buf = NULL;
int ret; int ret;
blk = blk_new_open(filename, NULL, NULL, blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); BLK_PERM_ALL);
if (blk == NULL) {
ret = -EIO; ret = blk_insert_bs(blk, bs, errp);
if (ret < 0) {
goto out_with_err_set; goto out_with_err_set;
} }
blk_set_allow_write_beyond_eof(blk, true); blk_set_allow_write_beyond_eof(blk, true);
vdi_size = blk_getlength(blk);
if (vdi_size < 0) {
ret = vdi_size;
goto out;
}
base = blk_bs(blk)->opaque;
object_size = (UINT32_C(1) << base->inode.block_size_shift); object_size = (UINT32_C(1) << base->inode.block_size_shift);
buf_size = MIN(object_size, SD_DATA_OBJ_SIZE); buf_size = MIN(object_size, SD_DATA_OBJ_SIZE);
buf = g_malloc0(buf_size); buf = g_malloc0(buf_size);
max_idx = DIV_ROUND_UP(vdi_size, buf_size); max_idx = DIV_ROUND_UP(new_size, buf_size);
for (idx = 0; idx < max_idx; idx++) { for (idx = old_size / buf_size; idx < max_idx; idx++) {
/* /*
* The created image can be a cloned image, so we need to read * The created image can be a cloned image, so we need to read
* a data from the source image. * a data from the source image.
@ -2108,7 +2102,20 @@ static int sd_create(const char *filename, QemuOpts *opts,
} }
if (prealloc) { if (prealloc) {
ret = sd_prealloc(filename, errp); BlockDriverState *bs;
QDict *opts;
opts = qdict_new();
qdict_put_str(opts, "driver", "sheepdog");
bs = bdrv_open(filename, NULL, opts, BDRV_O_PROTOCOL | BDRV_O_RDWR,
errp);
if (!bs) {
goto out;
}
ret = sd_prealloc(bs, 0, s->inode.vdi_size, errp);
bdrv_unref(bs);
} }
out: out:
g_free(backing_file); g_free(backing_file);
@ -2173,15 +2180,16 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset,
int ret, fd; int ret, fd;
unsigned int datalen; unsigned int datalen;
uint64_t max_vdi_size; uint64_t max_vdi_size;
int64_t old_size = s->inode.vdi_size;
if (prealloc != PREALLOC_MODE_OFF) { if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_FULL) {
error_setg(errp, "Unsupported preallocation mode '%s'", error_setg(errp, "Unsupported preallocation mode '%s'",
PreallocMode_str(prealloc)); PreallocMode_str(prealloc));
return -ENOTSUP; return -ENOTSUP;
} }
max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS; max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS;
if (offset < s->inode.vdi_size) { if (offset < old_size) {
error_setg(errp, "shrinking is not supported"); error_setg(errp, "shrinking is not supported");
return -EINVAL; return -EINVAL;
} else if (offset > max_vdi_size) { } else if (offset > max_vdi_size) {
@ -2204,9 +2212,17 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset,
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "failed to update an inode"); error_setg_errno(errp, -ret, "failed to update an inode");
return ret;
} }
return ret; if (prealloc == PREALLOC_MODE_FULL) {
ret = sd_prealloc(bs, old_size, offset, errp);
if (ret < 0) {
return ret;
}
}
return 0;
} }
/* /*

View File

@ -2825,14 +2825,9 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
if (!has_persistent) { if (!has_persistent) {
persistent = false; persistent = false;
} }
if (!has_autoload) {
autoload = false;
}
if (has_autoload && !persistent) { if (has_autoload) {
error_setg(errp, "Autoload flag must be used only for persistent " warn_report("Autoload option is deprecated and its value is ignored");
"bitmaps");
return;
} }
if (persistent && if (persistent &&
@ -2847,7 +2842,6 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
} }
bdrv_dirty_bitmap_set_persistance(bitmap, persistent); bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
bdrv_dirty_bitmap_set_autoload(bitmap, autoload);
} }
void qmp_block_dirty_bitmap_remove(const char *node, const char *name, void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
@ -3569,6 +3563,11 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
return; return;
} }
/* Early check to avoid creating target */
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
return;
}
aio_context = bdrv_get_aio_context(bs); aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);

View File

@ -845,6 +845,16 @@ QEMU transparently handles lock handover during shared storage migration. For
shared virtual disk images between multiple VMs, the "share-rw" device option shared virtual disk images between multiple VMs, the "share-rw" device option
should be used. should be used.
By default, the guest has exclusive write access to its disk image. If the
guest can safely share the disk image with other writers the @code{-device
...,share-rw=on} parameter can be used. This is only safe if the guest is
running software, such as a cluster file system, that coordinates disk accesses
to avoid corruption.
Note that share-rw=on only declares the guest's ability to share the disk.
Some QEMU features, such as image file formats, require exclusive write access
to the disk image and this is unaffected by the share-rw=on option.
Alternatively, locking can be fully disabled by "locking=off" block device Alternatively, locking can be fully disabled by "locking=off" block device
option. In the command line, the option is usually in the form of option. In the command line, the option is usually in the form of
"file.locking=off" as the protocol driver is normally placed as a "file" child "file.locking=off" as the protocol driver is normally placed as a "file" child

View File

@ -66,7 +66,6 @@ void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap,
void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap);
void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value);
void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload);
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap,
bool persistent); bool persistent);

View File

@ -1593,9 +1593,9 @@
# Qcow2 disks support persistent bitmaps. Default is false for # Qcow2 disks support persistent bitmaps. Default is false for
# block-dirty-bitmap-add. (Since: 2.10) # block-dirty-bitmap-add. (Since: 2.10)
# #
# @autoload: the bitmap will be automatically loaded when the image it is stored # @autoload: ignored and deprecated since 2.12.
# in is opened. This flag may only be specified for persistent # Currently, all dirty tracking bitmaps are loaded from Qcow2 on
# bitmaps. Default is false for block-dirty-bitmap-add. (Since: 2.10) # open.
# #
# Since: 2.4 # Since: 2.4
## ##
@ -2521,6 +2521,11 @@
# @l2-cache-size: the maximum size of the L2 table cache in # @l2-cache-size: the maximum size of the L2 table cache in
# bytes (since 2.2) # bytes (since 2.2)
# #
# @l2-cache-entry-size: the size of each entry in the L2 cache in
# bytes. It must be a power of two between 512
# and the cluster size. The default value is
# the cluster size (since 2.12)
#
# @refcount-cache-size: the maximum size of the refcount block cache # @refcount-cache-size: the maximum size of the refcount block cache
# in bytes (since 2.2) # in bytes (since 2.2)
# #
@ -2542,6 +2547,7 @@
'*overlap-check': 'Qcow2OverlapChecks', '*overlap-check': 'Qcow2OverlapChecks',
'*cache-size': 'int', '*cache-size': 'int',
'*l2-cache-size': 'int', '*l2-cache-size': 'int',
'*l2-cache-entry-size': 'int',
'*refcount-cache-size': 'int', '*refcount-cache-size': 'int',
'*cache-clean-interval': 'int', '*cache-clean-interval': 'int',
'*encrypt': 'BlockdevQcow2Encryption' } } '*encrypt': 'BlockdevQcow2Encryption' } }

View File

@ -2757,6 +2757,13 @@ used and it will be removed with no replacement.
The ``convert -s snapshot_id_or_name'' argument is obsoleted The ``convert -s snapshot_id_or_name'' argument is obsoleted
by the ``convert -l snapshot_param'' argument instead. by the ``convert -l snapshot_param'' argument instead.
@section QEMU Machine Protocol (QMP) commands
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
"autoload" parameter is now ignored. All bitmaps are automatically loaded
from qcow2 images.
@section System emulator human monitor commands @section System emulator human monitor commands
@subsection host_net_add (since 2.10.0) @subsection host_net_add (since 2.10.0)

View File

@ -33,38 +33,14 @@ The following commands are supported:
Command parameters: Command parameters:
@table @var @table @var
@item filename @item filename
is a disk image filename is a disk image filename
@item --object @var{objectdef}
is a QEMU user creatable object definition. See the @code{qemu(1)} manual
page for a description of the object properties. The most common object
type is a @code{secret}, which is used to supply passwords and/or encryption
keys.
@item --image-opts
Indicates that the source @var{filename} parameter is to be interpreted as a
full option string, not a plain filename. This parameter is mutually
exclusive with the @var{-f} parameter.
@item --target-image-opts
Indicates that the @var{output_filename} parameter(s) are to be interpreted as
a full option string, not a plain filename. This parameter is mutually
exclusive with the @var{-O} parameters. It is currently required to also use
the @var{-n} parameter to skip image creation. This restriction may be relaxed
in a future release.
@item fmt @item fmt
is the disk image format. It is guessed automatically in most cases. See below is the disk image format. It is guessed automatically in most cases. See below
for a description of the supported disk formats. for a description of the supported disk formats.
@item --backing-chain
will enumerate information about backing files in a disk image chain. Refer
below for further description.
@item size @item size
is the disk image size in bytes. Optional suffixes @code{k} or @code{K} is the disk image size in bytes. Optional suffixes @code{k} or @code{K}
(kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M) (kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M)
@ -74,42 +50,86 @@ and T (terabyte, 1024G) are supported. @code{b} is ignored.
is the destination disk image filename is the destination disk image filename
@item output_fmt @item output_fmt
is the destination format is the destination format
@item options @item options
is a comma separated list of format specific options in a is a comma separated list of format specific options in a
name=value format. Use @code{-o ?} for an overview of the options supported name=value format. Use @code{-o ?} for an overview of the options supported
by the used format or see the format descriptions below for details. by the used format or see the format descriptions below for details.
@item snapshot_param @item snapshot_param
is param used for internal snapshot, format is is param used for internal snapshot, format is
'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]'
@item snapshot_id_or_name @item snapshot_id_or_name
is deprecated, use snapshot_param instead is deprecated, use snapshot_param instead
@end table
@table @option
@item --object @var{objectdef}
is a QEMU user creatable object definition. See the @code{qemu(1)} manual
page for a description of the object properties. The most common object
type is a @code{secret}, which is used to supply passwords and/or encryption
keys.
@item --image-opts
Indicates that the source @var{filename} parameter is to be interpreted as a
full option string, not a plain filename. This parameter is mutually
exclusive with the @var{-f} parameter.
@item --target-image-opts
Indicates that the @var{output_filename} parameter(s) are to be interpreted as
a full option string, not a plain filename. This parameter is mutually
exclusive with the @var{-O} parameters. It is currently required to also use
the @var{-n} parameter to skip image creation. This restriction may be relaxed
in a future release.
@item --force-share (-U)
If specified, @code{qemu-img} will open the image in shared mode, allowing
other QEMU processes to open it in write mode. For example, this can be used to
get the image information (with 'info' subcommand) when the image is used by a
running guest. Note that this could produce inconsistent results because of
concurrent metadata changes, etc. This option is only allowed when opening
images in read-only mode.
@item --backing-chain
will enumerate information about backing files in a disk image chain. Refer
below for further description.
@item -c @item -c
indicates that target image must be compressed (qcow format only) indicates that target image must be compressed (qcow format only)
@item -h @item -h
with or without a command shows help and lists the supported formats with or without a command shows help and lists the supported formats
@item -p @item -p
display progress bar (compare, convert and rebase commands only). display progress bar (compare, convert and rebase commands only).
If the @var{-p} option is not used for a command that supports it, the If the @var{-p} option is not used for a command that supports it, the
progress is reported when the process receives a @code{SIGUSR1} or progress is reported when the process receives a @code{SIGUSR1} or
@code{SIGINFO} signal. @code{SIGINFO} signal.
@item -q @item -q
Quiet mode - do not print any output (except errors). There's no progress bar Quiet mode - do not print any output (except errors). There's no progress bar
in case both @var{-q} and @var{-p} options are used. in case both @var{-q} and @var{-p} options are used.
@item -S @var{size} @item -S @var{size}
indicates the consecutive number of bytes that must contain only zeros indicates the consecutive number of bytes that must contain only zeros
for qemu-img to create a sparse image during conversion. This value is rounded for qemu-img to create a sparse image during conversion. This value is rounded
down to the nearest 512 bytes. You may use the common size suffixes like down to the nearest 512 bytes. You may use the common size suffixes like
@code{k} for kilobytes. @code{k} for kilobytes.
@item -t @var{cache} @item -t @var{cache}
specifies the cache mode that should be used with the (destination) file. See specifies the cache mode that should be used with the (destination) file. See
the documentation of the emulator's @code{-drive cache=...} option for allowed the documentation of the emulator's @code{-drive cache=...} option for allowed
values. values.
@item -T @var{src_cache} @item -T @var{src_cache}
specifies the cache mode that should be used with the source file(s). See specifies the cache mode that should be used with the source file(s). See
the documentation of the emulator's @code{-drive cache=...} option for allowed the documentation of the emulator's @code{-drive cache=...} option for allowed
values. values.
@end table @end table
Parameters to snapshot subcommand: Parameters to snapshot subcommand:

View File

@ -11,6 +11,9 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <getopt.h> #include <getopt.h>
#include <libgen.h> #include <libgen.h>
#ifndef _WIN32
#include <termios.h>
#endif
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu-io.h" #include "qemu-io.h"
@ -42,6 +45,26 @@ static bool imageOpts;
static ReadLineState *readline_state; static ReadLineState *readline_state;
static int ttyEOF;
static int get_eof_char(void)
{
#ifdef _WIN32
return 0x4; /* Ctrl-D */
#else
struct termios tty;
if (tcgetattr(STDIN_FILENO, &tty) != 0) {
if (errno == ENOTTY) {
return 0x0; /* just expect read() == 0 */
} else {
return 0x4; /* Ctrl-D */
}
}
return tty.c_cc[VEOF];
#endif
}
static int close_f(BlockBackend *blk, int argc, char **argv) static int close_f(BlockBackend *blk, int argc, char **argv)
{ {
blk_unref(qemuio_blk); blk_unref(qemuio_blk);
@ -323,7 +346,8 @@ static char *fetchline_readline(void)
readline_start(readline_state, get_prompt(), 0, readline_func, &line); readline_start(readline_state, get_prompt(), 0, readline_func, &line);
while (!line) { while (!line) {
int ch = getchar(); int ch = getchar();
if (ch == EOF) { if (ttyEOF != 0x0 && ch == ttyEOF) {
printf("\n");
break; break;
} }
readline_handle_byte(readline_state, ch); readline_handle_byte(readline_state, ch);
@ -593,6 +617,7 @@ int main(int argc, char **argv)
qemuio_add_command(&close_cmd); qemuio_add_command(&close_cmd);
if (isatty(STDIN_FILENO)) { if (isatty(STDIN_FILENO)) {
ttyEOF = get_eof_char();
readline_state = readline_init(readline_printf_func, readline_state = readline_init(readline_printf_func,
readline_flush_func, readline_flush_func,
NULL, NULL,

View File

@ -2358,5 +2358,5 @@ Offset Length Mapped to File
0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk 0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk
=== Testing afl image with a very large capacity === === Testing afl image with a very large capacity ===
qemu-img: Could not open 'TEST_DIR/afl9.IMGFMT': Could not open 'TEST_DIR/afl9.IMGFMT': Invalid argument qemu-img: Can't get image size 'TEST_DIR/afl9.IMGFMT': File too large
*** done *** done

View File

@ -53,6 +53,22 @@ $PYTHON qcow2.py "$TEST_IMG" dump-header
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img _check_test_img
echo
echo "=== Testing version downgrade with zero expansion and 4K cache entries ==="
echo
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "write -z 32M 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io
$PYTHON qcow2.py "$TEST_IMG" dump-header
$QEMU_IMG amend -o "compat=0.10" --image-opts \
driver=qcow2,file.filename=$TEST_IMG,l2-cache-entry-size=4096
$PYTHON qcow2.py "$TEST_IMG" dump-header
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -P 0 32M 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo echo
echo "=== Testing dirty version downgrade ===" echo "=== Testing dirty version downgrade ==="
echo echo

View File

@ -52,6 +52,67 @@ read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image. No errors were found on the image.
=== Testing version downgrade with zero expansion and 4K cache entries ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 33554432
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
128 KiB (0x20000) bytes allocated at offset 0 bytes (0x0)
31.875 MiB (0x1fe0000) bytes not allocated at offset 128 KiB (0x20000)
128 KiB (0x20000) bytes allocated at offset 32 MiB (0x2000000)
31.875 MiB (0x1fe0000) bytes not allocated at offset 32.125 MiB (0x2020000)
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x1
autoclear_features 0x0
refcount_order 4
header_length 104
Header extension:
magic 0x6803f857
length 144
data <binary>
magic 0x514649fb
version 2
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 72
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 33554432
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
64 MiB (0x4000000) bytes not allocated at offset 0 bytes (0x0)
No errors were found on the image.
=== Testing dirty version downgrade === === Testing dirty version downgrade ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864

View File

@ -66,6 +66,14 @@ $QEMU_IO -c "open -o cache-size=1M,refcount-cache-size=2M $TEST_IMG" 2>&1 \
$QEMU_IO -c "open -o cache-size=0,l2-cache-size=0,refcount-cache-size=0 $TEST_IMG" \ $QEMU_IO -c "open -o cache-size=0,l2-cache-size=0,refcount-cache-size=0 $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt 2>&1 | _filter_testdir | _filter_imgfmt
# Invalid cache entry sizes
$QEMU_IO -c "open -o l2-cache-entry-size=256 $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt
$QEMU_IO -c "open -o l2-cache-entry-size=4242 $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt
$QEMU_IO -c "open -o l2-cache-entry-size=128k $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt
echo echo
echo '=== Testing valid option combinations ===' echo '=== Testing valid option combinations ==='
echo echo
@ -94,6 +102,15 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \
-c 'read -P 42 0 64k' \ -c 'read -P 42 0 64k' \
| _filter_qemu_io | _filter_qemu_io
# Valid cache entry sizes
$QEMU_IO -c "open -o l2-cache-entry-size=512 $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt
$QEMU_IO -c "open -o l2-cache-entry-size=16k $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt
$QEMU_IO -c "open -o l2-cache-entry-size=64k $TEST_IMG" \
2>&1 | _filter_testdir | _filter_imgfmt
echo echo
echo '=== Testing minimal L2 cache and COW ===' echo '=== Testing minimal L2 cache and COW ==='
echo echo

View File

@ -9,6 +9,9 @@ can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cach
can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size
can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size
can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time
can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536)
can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536)
can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536)
=== Testing valid option combinations === === Testing valid option combinations ===

View File

@ -83,6 +83,9 @@ $QEMU_IO \
-c "reopen -o overlap-check.inactive-l2=off" \ -c "reopen -o overlap-check.inactive-l2=off" \
-c "reopen -o cache-size=1M" \ -c "reopen -o cache-size=1M" \
-c "reopen -o l2-cache-size=512k" \ -c "reopen -o l2-cache-size=512k" \
-c "reopen -o l2-cache-entry-size=512" \
-c "reopen -o l2-cache-entry-size=4k" \
-c "reopen -o l2-cache-entry-size=64k" \
-c "reopen -o refcount-cache-size=128k" \ -c "reopen -o refcount-cache-size=128k" \
-c "reopen -o cache-clean-interval=5" \ -c "reopen -o cache-clean-interval=5" \
-c "reopen -o cache-clean-interval=0" \ -c "reopen -o cache-clean-interval=0" \
@ -107,6 +110,8 @@ $QEMU_IO \
-c "reopen -o cache-size=1M,l2-cache-size=2M" \ -c "reopen -o cache-size=1M,l2-cache-size=2M" \
-c "reopen -o cache-size=1M,refcount-cache-size=2M" \ -c "reopen -o cache-size=1M,refcount-cache-size=2M" \
-c "reopen -o l2-cache-size=256T" \ -c "reopen -o l2-cache-size=256T" \
-c "reopen -o l2-cache-entry-size=33k" \
-c "reopen -o l2-cache-entry-size=128k" \
-c "reopen -o refcount-cache-size=256T" \ -c "reopen -o refcount-cache-size=256T" \
-c "reopen -o overlap-check=constant,overlap-check.template=all" \ -c "reopen -o overlap-check=constant,overlap-check.template=all" \
-c "reopen -o overlap-check=blubb" \ -c "reopen -o overlap-check=blubb" \

View File

@ -20,6 +20,8 @@ cache-size, l2-cache-size and refcount-cache-size may not be set the same time
l2-cache-size may not exceed cache-size l2-cache-size may not exceed cache-size
refcount-cache-size may not exceed cache-size refcount-cache-size may not exceed cache-size
L2 cache size too big L2 cache size too big
L2 cache entry size must be a power of two between 512 and the cluster size (65536)
L2 cache entry size must be a power of two between 512 and the cluster size (65536)
L2 cache size too big L2 cache size too big
Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all')
Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all

View File

@ -64,7 +64,7 @@ class BaseClass(iotests.QMPTestCase):
'file': {'driver': 'file', 'file': {'driver': 'file',
'filename': source_img}} 'filename': source_img}}
self.vm.add_blockdev(self.qmp_to_opts(blockdev)) self.vm.add_blockdev(self.qmp_to_opts(blockdev))
self.vm.add_device('floppy,id=qdev0,drive=source') self.vm.add_device('virtio-blk,id=qdev0,drive=source')
self.vm.launch() self.vm.launch()
self.assertIntactSourceBackingChain() self.assertIntactSourceBackingChain()
@ -173,21 +173,24 @@ class MirrorBaseClass(BaseClass):
def testFull(self): def testFull(self):
self.runMirror('full') self.runMirror('full')
node = self.findBlockNode('target', 'qdev0') node = self.findBlockNode('target',
'/machine/peripheral/qdev0/virtio-backend')
self.assertCorrectBackingImage(node, None) self.assertCorrectBackingImage(node, None)
self.assertIntactSourceBackingChain() self.assertIntactSourceBackingChain()
def testTop(self): def testTop(self):
self.runMirror('top') self.runMirror('top')
node = self.findBlockNode('target', 'qdev0') node = self.findBlockNode('target',
'/machine/peripheral/qdev0/virtio-backend')
self.assertCorrectBackingImage(node, back2_img) self.assertCorrectBackingImage(node, back2_img)
self.assertIntactSourceBackingChain() self.assertIntactSourceBackingChain()
def testNone(self): def testNone(self):
self.runMirror('none') self.runMirror('none')
node = self.findBlockNode('target', 'qdev0') node = self.findBlockNode('target',
'/machine/peripheral/qdev0/virtio-backend')
self.assertCorrectBackingImage(node, source_img) self.assertCorrectBackingImage(node, source_img)
self.assertIntactSourceBackingChain() self.assertIntactSourceBackingChain()
@ -239,7 +242,8 @@ class TestCommit(BaseClass):
self.vm.event_wait('BLOCK_JOB_COMPLETED') self.vm.event_wait('BLOCK_JOB_COMPLETED')
node = self.findBlockNode(None, 'qdev0') node = self.findBlockNode(None,
'/machine/peripheral/qdev0/virtio-backend')
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
back1_img) back1_img)
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',

View File

@ -64,7 +64,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase):
def qmpAddBitmap(self): def qmpAddBitmap(self):
self.vm.qmp('block-dirty-bitmap-add', node='drive0', self.vm.qmp('block-dirty-bitmap-add', node='drive0',
name='bitmap0', persistent=True, autoload=True) name='bitmap0', persistent=True)
def test_persistent(self): def test_persistent(self):
self.vm = self.mkVm() self.vm = self.mkVm()

View File

@ -95,7 +95,7 @@ case $reason in
"file": { "driver": "file", "filename": "$TEST_IMG" } } } "file": { "driver": "file", "filename": "$TEST_IMG" } } }
{ "execute": "block-dirty-bitmap-add", { "execute": "block-dirty-bitmap-add",
"arguments": { "node": "drive0", "name": "bitmap0", "arguments": { "node": "drive0", "name": "bitmap0",
"persistent": true, "autoload": true } } "persistent": true } }
{ "execute": "quit" } { "execute": "quit" }
EOF EOF
;; ;;