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

Block layer patches:

- qemu-storage-daemon: add --pidfile option
- qemu-storage-daemon: CLI error messages include the option name now
- vhost-user-blk export: Misc fixes
- docs: Improvements for qemu-storage-daemon documentation
- parallels: load bitmap extension
- backup-top: Don't crash on post-finalize accesses
- Improve error messages related to node-name options
- iotests improvements

# gpg: Signature made Mon 08 Mar 2021 17:01:41 GMT
# gpg:                using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6
# gpg:                issuer "kwolf@redhat.com"
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (30 commits)
  blockdev: Clarify error messages pertaining to 'node-name'
  block: Clarify error messages pertaining to 'node-name'
  docs: qsd: Explain --export nbd,name=... default
  MAINTAINERS: update parallels block driver
  iotests: add parallels-read-bitmap test
  iotests.py: add unarchive_sample_image() helper
  parallels: support bitmap extension for read-only mode
  block/parallels: BDRVParallelsState: add cluster_size field
  parallels.txt: fix bitmap L1 table description
  qcow2-bitmap: make bytes_covered_by_bitmap_cluster() public
  block/export: port virtio-blk read/write range check
  block/export: port virtio-blk discard/write zeroes input validation
  block/export: fix vhost-user-blk export sector number calculation
  block/export: use VIRTIO_BLK_SECTOR_BITS
  block/export: fix blk_size double byteswap
  libqtest: add qtest_remove_abrt_handler()
  libqtest: add qtest_kill_qemu()
  libqtest: add qtest_socket_server()
  vhost-user-blk: fix blkcfg->num_queues endianness
  docs: replace insecure /tmp examples in qsd docs
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2021-03-09 21:31:18 +00:00
51 changed files with 969 additions and 175 deletions
+3
View File
@@ -3132,10 +3132,13 @@ F: block/dmg.c
parallels
M: Stefan Hajnoczi <stefanha@redhat.com>
M: Denis V. Lunev <den@openvz.org>
M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
L: qemu-block@nongnu.org
S: Supported
F: block/parallels.c
F: block/parallels-ext.c
F: docs/interop/parallels.txt
T: git https://src.openvz.org/scm/~vsementsov/qemu.git parallels
qed
M: Stefan Hajnoczi <stefanha@redhat.com>
+4 -4
View File
@@ -1440,7 +1440,7 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
* Check for empty string or invalid characters, but not if it is
* generated (generated names use characters not available to the user)
*/
error_setg(errp, "Invalid node name");
error_setg(errp, "Invalid node-name: '%s'", node_name);
return;
}
@@ -1453,7 +1453,7 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
/* takes care of avoiding duplicates node names */
if (bdrv_find_node(node_name)) {
error_setg(errp, "Duplicate node name");
error_setg(errp, "Duplicate nodes with node-name='%s'", node_name);
goto out;
}
@@ -5432,7 +5432,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device,
}
}
error_setg(errp, "Cannot find device=%s nor node_name=%s",
error_setg(errp, "Cannot find device=\'%s\' nor node-name=\'%s\'",
device ? device : "",
node_name ? node_name : "");
return NULL;
@@ -6752,7 +6752,7 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
AioContext *aio_context;
if (!to_replace_bs) {
error_setg(errp, "Node name '%s' not found", node_name);
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
return NULL;
}
+10
View File
@@ -45,6 +45,12 @@ static coroutine_fn int backup_top_co_preadv(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
BDRVBackupTopState *s = bs->opaque;
if (!s->active) {
return -EIO;
}
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
}
@@ -54,6 +60,10 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
BDRVBackupTopState *s = bs->opaque;
uint64_t off, end;
if (!s->active) {
return -EIO;
}
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
return 0;
}
+1
View File
@@ -103,6 +103,7 @@ static void backup_abort(Job *job)
static void backup_clean(Job *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
block_job_remove_all_bdrv(&s->common);
bdrv_backup_top_drop(s->backup_top);
}
+13
View File
@@ -726,6 +726,19 @@ uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap)
return hbitmap_serialization_align(bitmap->bitmap);
}
/* Return the disk size covered by a chunk of serialized bitmap data. */
uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
const BdrvDirtyBitmap *bitmap)
{
uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
uint64_t limit = granularity * (serialized_chunk_size << 3);
assert(QEMU_IS_ALIGNED(limit,
bdrv_dirty_bitmap_serialization_align(bitmap)));
return limit;
}
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
uint8_t *buf, uint64_t offset,
uint64_t bytes)
+121 -29
View File
@@ -20,8 +20,17 @@
#include "sysemu/block-backend.h"
#include "util/block-helpers.h"
/*
* Sector units are 512 bytes regardless of the
* virtio_blk_config->blk_size value.
*/
#define VIRTIO_BLK_SECTOR_BITS 9
#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS)
enum {
VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1,
VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768,
VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768,
};
struct virtio_blk_inhdr {
unsigned char status;
@@ -58,30 +67,102 @@ static void vu_blk_req_complete(VuBlkReq *req)
free(req);
}
static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector,
size_t size)
{
uint64_t nb_sectors = size >> BDRV_SECTOR_BITS;
uint64_t total_sectors;
if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
return false;
}
if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) {
return false;
}
blk_get_geometry(vexp->export.blk, &total_sectors);
if (sector > total_sectors || nb_sectors > total_sectors - sector) {
return false;
}
return true;
}
static int coroutine_fn
vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov,
vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov,
uint32_t iovcnt, uint32_t type)
{
BlockBackend *blk = vexp->export.blk;
struct virtio_blk_discard_write_zeroes desc;
ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
ssize_t size;
uint64_t sector;
uint32_t num_sectors;
uint32_t max_sectors;
uint32_t flags;
int bytes;
/* Only one desc is currently supported */
if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) {
return VIRTIO_BLK_S_UNSUPP;
}
size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc));
if (unlikely(size != sizeof(desc))) {
error_report("Invalid size %zd, expect %zu", size, sizeof(desc));
return -EINVAL;
error_report("Invalid size %zd, expected %zu", size, sizeof(desc));
return VIRTIO_BLK_S_IOERR;
}
uint64_t range[2] = { le64_to_cpu(desc.sector) << 9,
le32_to_cpu(desc.num_sectors) << 9 };
if (type == VIRTIO_BLK_T_DISCARD) {
if (blk_co_pdiscard(blk, range[0], range[1]) == 0) {
return 0;
sector = le64_to_cpu(desc.sector);
num_sectors = le32_to_cpu(desc.num_sectors);
flags = le32_to_cpu(desc.flags);
max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ?
VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS :
VHOST_USER_BLK_MAX_DISCARD_SECTORS;
/* This check ensures that 'bytes' fits in an int */
if (unlikely(num_sectors > max_sectors)) {
return VIRTIO_BLK_S_IOERR;
}
bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS;
if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) {
return VIRTIO_BLK_S_IOERR;
}
/*
* The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard
* and write zeroes commands if any unknown flag is set.
*/
if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
return VIRTIO_BLK_S_UNSUPP;
}
if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
int blk_flags = 0;
if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) {
blk_flags |= BDRV_REQ_MAY_UNMAP;
}
} else if (type == VIRTIO_BLK_T_WRITE_ZEROES) {
if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) {
return 0;
if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS,
bytes, blk_flags) == 0) {
return VIRTIO_BLK_S_OK;
}
} else if (type == VIRTIO_BLK_T_DISCARD) {
/*
* The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for
* discard commands if the unmap flag is set.
*/
if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) {
return VIRTIO_BLK_S_UNSUPP;
}
if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS,
bytes) == 0) {
return VIRTIO_BLK_S_OK;
}
}
return -EINVAL;
return VIRTIO_BLK_S_IOERR;
}
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
@@ -128,6 +209,8 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
switch (type & ~VIRTIO_BLK_T_BARRIER) {
case VIRTIO_BLK_T_IN:
case VIRTIO_BLK_T_OUT: {
QEMUIOVector qiov;
int64_t offset;
ssize_t ret = 0;
bool is_write = type & VIRTIO_BLK_T_OUT;
req->sector_num = le64_to_cpu(req->out.sector);
@@ -137,13 +220,24 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
break;
}
int64_t offset = req->sector_num * vexp->blk_size;
QEMUIOVector qiov;
if (is_write) {
qemu_iovec_init_external(&qiov, out_iov, out_num);
ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
} else {
qemu_iovec_init_external(&qiov, in_iov, in_num);
}
if (unlikely(!vu_blk_sect_range_ok(vexp,
req->sector_num,
qiov.size))) {
req->in->status = VIRTIO_BLK_S_IOERR;
break;
}
offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS;
if (is_write) {
ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0);
} else {
ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0);
}
if (ret >= 0) {
@@ -170,19 +264,13 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
}
case VIRTIO_BLK_T_DISCARD:
case VIRTIO_BLK_T_WRITE_ZEROES: {
int rc;
if (!vexp->writable) {
req->in->status = VIRTIO_BLK_S_IOERR;
break;
}
rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type);
if (rc == 0) {
req->in->status = VIRTIO_BLK_S_OK;
} else {
req->in->status = VIRTIO_BLK_S_IOERR;
}
req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num,
type);
break;
}
default:
@@ -347,17 +435,21 @@ vu_blk_initialize_config(BlockDriverState *bs,
uint32_t blk_size,
uint16_t num_queues)
{
config->capacity = cpu_to_le64(bdrv_getlength(bs) >> BDRV_SECTOR_BITS);
config->capacity =
cpu_to_le64(bdrv_getlength(bs) >> VIRTIO_BLK_SECTOR_BITS);
config->blk_size = cpu_to_le32(blk_size);
config->size_max = cpu_to_le32(0);
config->seg_max = cpu_to_le32(128 - 2);
config->min_io_size = cpu_to_le16(1);
config->opt_io_size = cpu_to_le32(1);
config->num_queues = cpu_to_le16(num_queues);
config->max_discard_sectors = cpu_to_le32(32768);
config->max_discard_sectors =
cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS);
config->max_discard_seg = cpu_to_le32(1);
config->discard_sector_alignment = cpu_to_le32(config->blk_size >> 9);
config->max_write_zeroes_sectors = cpu_to_le32(32768);
config->discard_sector_alignment =
cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS);
config->max_write_zeroes_sectors
= cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS);
config->max_write_zeroes_seg = cpu_to_le32(1);
}
@@ -383,7 +475,7 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts,
if (vu_opts->has_logical_block_size) {
logical_block_size = vu_opts->logical_block_size;
} else {
logical_block_size = BDRV_SECTOR_SIZE;
logical_block_size = VIRTIO_BLK_SECTOR_SIZE;
}
check_block_size(exp->id, "logical-block-size", logical_block_size,
&local_err);
+2 -1
View File
@@ -57,7 +57,8 @@ block_ss.add(when: 'CONFIG_QED', if_true: files(
'qed-table.c',
'qed.c',
))
block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], if_true: files('parallels.c'))
block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'],
if_true: files('parallels.c', 'parallels-ext.c'))
block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c'))
block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit])
block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c'))
+300
View File
@@ -0,0 +1,300 @@
/*
* Support of Parallels Format Extension. It's a part of Parallels format
* driver.
*
* Copyright (c) 2021 Virtuozzo International GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "block/block_int.h"
#include "parallels.h"
#include "crypto/hash.h"
#include "qemu/uuid.h"
#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL
#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL
#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL
typedef struct ParallelsFormatExtensionHeader {
uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */
uint8_t check_sum[16];
} QEMU_PACKED ParallelsFormatExtensionHeader;
typedef struct ParallelsFeatureHeader {
uint64_t magic;
uint64_t flags;
uint32_t data_size;
uint32_t _unused;
} QEMU_PACKED ParallelsFeatureHeader;
typedef struct ParallelsDirtyBitmapFeature {
uint64_t size;
uint8_t id[16];
uint32_t granularity;
uint32_t l1_size;
/* L1 table follows */
} QEMU_PACKED ParallelsDirtyBitmapFeature;
/* Given L1 table read bitmap data from the image and populate @bitmap */
static int parallels_load_bitmap_data(BlockDriverState *bs,
const uint64_t *l1_table,
uint32_t l1_size,
BdrvDirtyBitmap *bitmap,
Error **errp)
{
BDRVParallelsState *s = bs->opaque;
int ret = 0;
uint64_t offset, limit;
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
uint8_t *buf = NULL;
uint64_t i, tab_size =
DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
s->cluster_size);
if (tab_size != l1_size) {
error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
"to bitmap size and cluster size. Expected %" PRIu64,
l1_size, tab_size);
return -EINVAL;
}
buf = qemu_blockalign(bs, s->cluster_size);
limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
uint64_t count = MIN(bm_size - offset, limit);
uint64_t entry = l1_table[i];
if (entry == 0) {
/* No need to deserialize zeros because @bitmap is cleared. */
continue;
}
if (entry == 1) {
bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false);
} else {
ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf,
s->cluster_size);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to read bitmap data cluster");
goto finish;
}
bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
false);
}
}
ret = 0;
bdrv_dirty_bitmap_deserialize_finish(bitmap);
finish:
qemu_vfree(buf);
return ret;
}
/*
* @data buffer (of @data_size size) is the Dirty bitmaps feature which
* consists of ParallelsDirtyBitmapFeature followed by L1 table.
*/
static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs,
uint8_t *data,
size_t data_size,
Error **errp)
{
int ret;
ParallelsDirtyBitmapFeature bf;
g_autofree uint64_t *l1_table = NULL;
BdrvDirtyBitmap *bitmap;
QemuUUID uuid;
char uuidstr[UUID_FMT_LEN + 1];
int i;
if (data_size < sizeof(bf)) {
error_setg(errp, "Too small Bitmap Feature area in Parallels Format "
"Extension: %zu bytes, expected at least %zu bytes",
data_size, sizeof(bf));
return NULL;
}
memcpy(&bf, data, sizeof(bf));
bf.size = le64_to_cpu(bf.size);
bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS;
bf.l1_size = le32_to_cpu(bf.l1_size);
data += sizeof(bf);
data_size -= sizeof(bf);
if (bf.size != bs->total_sectors) {
error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from "
"disk size in sectors %" PRId64, bf.size, bs->total_sectors);
return NULL;
}
if (bf.l1_size * sizeof(uint64_t) > data_size) {
error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds "
"extension data_size");
return NULL;
}
memcpy(&uuid, bf.id, sizeof(uuid));
qemu_uuid_unparse(&uuid, uuidstr);
bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp);
if (!bitmap) {
return NULL;
}
l1_table = g_new(uint64_t, bf.l1_size);
for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
l1_table[i] = ldq_le_p(data);
}
ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
if (ret < 0) {
bdrv_release_dirty_bitmap(bitmap);
return NULL;
}
/* We support format extension only for RO parallels images. */
assert(!(bs->open_flags & BDRV_O_RDWR));
bdrv_dirty_bitmap_set_readonly(bitmap, true);
return bitmap;
}
static int parallels_parse_format_extension(BlockDriverState *bs,
uint8_t *ext_cluster, Error **errp)
{
BDRVParallelsState *s = bs->opaque;
int ret;
int remaining = s->cluster_size;
uint8_t *pos = ext_cluster;
ParallelsFormatExtensionHeader eh;
g_autofree uint8_t *hash = NULL;
size_t hash_len = 0;
GSList *bitmaps = NULL, *el;
memcpy(&eh, pos, sizeof(eh));
eh.magic = le64_to_cpu(eh.magic);
pos += sizeof(eh);
remaining -= sizeof(eh);
if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) {
error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64
", expected: 0x%llx", eh.magic,
PARALLELS_FORMAT_EXTENSION_MAGIC);
goto fail;
}
ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining,
&hash, &hash_len, errp);
if (ret < 0) {
goto fail;
}
if (hash_len != sizeof(eh.check_sum) ||
memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) {
error_setg(errp, "Wrong checksum in Format Extension header. Format "
"extension is corrupted.");
goto fail;
}
while (true) {
ParallelsFeatureHeader fh;
BdrvDirtyBitmap *bitmap;
if (remaining < sizeof(fh)) {
error_setg(errp, "Can not read feature header, as remaining bytes "
"(%d) in Format Extension is less than Feature header "
"size (%zu)", remaining, sizeof(fh));
goto fail;
}
memcpy(&fh, pos, sizeof(fh));
pos += sizeof(fh);
remaining -= sizeof(fh);
fh.magic = le64_to_cpu(fh.magic);
fh.flags = le64_to_cpu(fh.flags);
fh.data_size = le32_to_cpu(fh.data_size);
if (fh.flags) {
error_setg(errp, "Flags for extension feature are unsupported");
goto fail;
}
if (fh.data_size > remaining) {
error_setg(errp, "Feature data_size exceedes Format Extension "
"cluster");
goto fail;
}
switch (fh.magic) {
case PARALLELS_END_OF_FEATURES_MAGIC:
return 0;
case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC:
bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp);
if (!bitmap) {
goto fail;
}
bitmaps = g_slist_append(bitmaps, bitmap);
break;
default:
error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic);
goto fail;
}
pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8);
}
fail:
for (el = bitmaps; el; el = el->next) {
bdrv_release_dirty_bitmap(el->data);
}
g_slist_free(bitmaps);
return -EINVAL;
}
int parallels_read_format_extension(BlockDriverState *bs,
int64_t ext_off, Error **errp)
{
BDRVParallelsState *s = bs->opaque;
int ret;
uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size);
assert(ext_off > 0);
ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to read Format Extension cluster");
goto out;
}
ret = parallels_parse_format_extension(bs, ext_cluster, errp);
out:
qemu_vfree(ext_cluster);
return ret;
}
+22 -4
View File
@@ -29,6 +29,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "block/block_int.h"
#include "block/qdict.h"
@@ -421,7 +422,6 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
int ret;
uint32_t i;
bool flush_bat = false;
int cluster_size = s->tracks << BDRV_SECTOR_BITS;
size = bdrv_getlength(bs->file->bs);
if (size < 0) {
@@ -472,7 +472,7 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
high_off = off;
}
if (prev_off != 0 && (prev_off + cluster_size) != off) {
if (prev_off != 0 && (prev_off + s->cluster_size) != off) {
res->bfi.fragmented_clusters++;
}
prev_off = off;
@@ -487,10 +487,10 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
}
}
res->image_end_offset = high_off + cluster_size;
res->image_end_offset = high_off + s->cluster_size;
if (size > res->image_end_offset) {
int64_t count;
count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size);
count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size);
fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n",
fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR",
size - res->image_end_offset);
@@ -771,6 +771,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EFBIG;
goto fail;
}
s->cluster_size = s->tracks << BDRV_SECTOR_BITS;
s->bat_size = le32_to_cpu(ph.bat_entries);
if (s->bat_size > INT_MAX / sizeof(uint32_t)) {
@@ -843,6 +844,23 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
goto fail_options;
}
if (ph.ext_off) {
if (flags & BDRV_O_RDWR) {
/*
* It's unsafe to open image RW if there is an extension (as we
* don't support it). But parallels driver in QEMU historically
* ignores the extension, so print warning and don't care.
*/
warn_report("Format Extension ignored in RW mode");
} else {
ret = parallels_read_format_extension(
bs, le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS, errp);
if (ret < 0) {
goto fail;
}
}
}
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) {
s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC);
ret = parallels_update_header(bs);
+6 -1
View File
@@ -48,7 +48,8 @@ typedef struct ParallelsHeader {
uint64_t nb_sectors;
uint32_t inuse;
uint32_t data_off;
char padding[12];
uint32_t flags;
uint64_t ext_off;
} QEMU_PACKED ParallelsHeader;
typedef enum ParallelsPreallocMode {
@@ -79,9 +80,13 @@ typedef struct BDRVParallelsState {
ParallelsPreallocMode prealloc_mode;
unsigned int tracks;
unsigned int cluster_size;
unsigned int off_multiplier;
Error *migration_blocker;
} BDRVParallelsState;
int parallels_read_format_extension(BlockDriverState *bs,
int64_t ext_off, Error **errp);
#endif
+2 -14
View File
@@ -278,18 +278,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
return 0;
}
/* Return the disk size covered by a single qcow2 cluster of bitmap data. */
static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s,
const BdrvDirtyBitmap *bitmap)
{
uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
uint64_t limit = granularity * (s->cluster_size << 3);
assert(QEMU_IS_ALIGNED(limit,
bdrv_dirty_bitmap_serialization_align(bitmap)));
return limit;
}
/* load_bitmap_data
* @bitmap_table entries must satisfy specification constraints.
* @bitmap must be cleared */
@@ -312,7 +300,7 @@ static int load_bitmap_data(BlockDriverState *bs,
}
buf = g_malloc(s->cluster_size);
limit = bytes_covered_by_bitmap_cluster(s, bitmap);
limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
uint64_t count = MIN(bm_size - offset, limit);
uint64_t entry = bitmap_table[i];
@@ -1303,7 +1291,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs,
}
buf = g_malloc(s->cluster_size);
limit = bytes_covered_by_bitmap_cluster(s, bitmap);
limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
offset = 0;
+7 -6
View File
@@ -1515,13 +1515,13 @@ static void external_snapshot_prepare(BlkActionState *common,
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
if (node_name && !snapshot_node_name) {
error_setg(errp, "New overlay node name missing");
error_setg(errp, "New overlay node-name missing");
goto out;
}
if (snapshot_node_name &&
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
error_setg(errp, "New overlay node name already in use");
error_setg(errp, "New overlay node-name already in use");
goto out;
}
@@ -3598,13 +3598,14 @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
/* Check for the selected node name */
if (!options->has_node_name) {
error_setg(errp, "Node name not specified");
error_setg(errp, "node-name not specified");
goto fail;
}
bs = bdrv_find_node(options->node_name);
if (!bs) {
error_setg(errp, "Cannot find node named '%s'", options->node_name);
error_setg(errp, "Failed to find node with node-name='%s'",
options->node_name);
goto fail;
}
@@ -3635,7 +3636,7 @@ void qmp_blockdev_del(const char *node_name, Error **errp)
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Cannot find node %s", node_name);
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
return;
}
if (bdrv_has_blk(bs)) {
@@ -3758,7 +3759,7 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Cannot find node %s", node_name);
error_setg(errp, "Failed to find node with node-name='%s'", node_name);
return;
}
+7 -3
View File
@@ -318,8 +318,12 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
info->status = job->job.status;
info->auto_finalize = job->job.auto_finalize;
info->auto_dismiss = job->job.auto_dismiss;
info->has_error = job->job.ret != 0;
info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
if (job->job.ret) {
info->has_error = true;
info->error = job->job.err ?
g_strdup(error_get_pretty(job->job.err)) :
g_strdup(strerror(-job->job.ret));
}
return info;
}
@@ -356,7 +360,7 @@ static void block_job_event_completed(Notifier *n, void *opaque)
}
if (job->job.ret < 0) {
msg = strerror(-job->job.ret);
msg = error_get_pretty(job->job.err);
}
qapi_event_send_block_job_completed(job_type(&job->job),
+16 -12
View File
@@ -208,21 +208,25 @@ of its data area are:
28 - 31: l1_size
The number of entries in the L1 table of the bitmap.
variable: l1_table (8 * l1_size bytes)
L1 offset table (in bytes)
variable: L1 offset table (l1_table), size: 8 * l1_size bytes
A dirty bitmap is stored using a one-level structure for the mapping to host
clusters - an L1 table.
The dirty bitmap described by this feature extension is stored in a set of
clusters inside the Parallels image file. The offsets of these clusters are
saved in the L1 offset table specified by the feature extension. Each L1 table
entry is a 64 bit integer as described below:
Given an offset in bytes into the bitmap data, the offset in bytes into the
image file can be obtained as follows:
Given an offset in bytes into the bitmap data, corresponding L1 entry is
offset = l1_table[offset / cluster_size] + (offset % cluster_size)
l1_table[offset / cluster_size]
If an L1 table entry is 0, the corresponding cluster of the bitmap is assumed
to be zero.
If an L1 table entry is 0, all bits in the corresponding cluster of the bitmap
are assumed to be 0.
If an L1 table entry is 1, the corresponding cluster of the bitmap is assumed
to have all bits set.
If an L1 table entry is 1, all bits in the corresponding cluster of the bitmap
are assumed to be 1.
If an L1 table entry is not 0 or 1, it allocates a cluster from the data area.
If an L1 table entry is not 0 or 1, it contains the corresponding cluster
offset (in 512b sectors). Given an offset in bytes into the bitmap data the
offset in bytes into the image file can be obtained as follows:
offset = l1_table[offset / cluster_size] * 512 + (offset % cluster_size)
+61 -7
View File
@@ -69,7 +69,7 @@ Standard options:
a description of character device properties. A common character device
definition configures a UNIX domain socket::
--chardev socket,id=char1,path=/tmp/qmp.sock,server=on,wait=off
--chardev socket,id=char1,path=/var/run/qsd-qmp.sock,server=on,wait=off
.. option:: --export [type=]nbd,id=<id>,node-name=<node-name>[,name=<export-name>][,writable=on|off][,bitmap=<name>]
--export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,addr.type=unix,addr.path=<socket-path>[,writable=on|off][,logical-block-size=<block-size>][,num-queues=<num-queues>]
@@ -80,8 +80,9 @@ Standard options:
requests for modifying data (the default is off).
The ``nbd`` export type requires ``--nbd-server`` (see below). ``name`` is
the NBD export name. ``bitmap`` is the name of a dirty bitmap reachable from
the block node, so the NBD client can use NBD_OPT_SET_META_CONTEXT with the
the NBD export name (if not specified, it defaults to the given
``node-name``). ``bitmap`` is the name of a dirty bitmap reachable from the
block node, so the NBD client can use NBD_OPT_SET_META_CONTEXT with the
metadata context name "qemu:dirty-bitmap:BITMAP" to inspect the bitmap.
The ``vhost-user-blk`` export type takes a vhost-user socket address on which
@@ -101,14 +102,17 @@ Standard options:
.. option:: --nbd-server addr.type=inet,addr.host=<host>,addr.port=<port>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
--nbd-server addr.type=unix,addr.path=<path>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
--nbd-server addr.type=fd,addr.str=<fd>[,tls-creds=<id>][,tls-authz=<id>][,max-connections=<n>]
is a server for NBD exports. Both TCP and UNIX domain sockets are supported.
TLS encryption can be configured using ``--object`` tls-creds-* and authz-*
secrets (see below).
A listen socket can be provided via file descriptor passing (see Examples
below). TLS encryption can be configured using ``--object`` tls-creds-* and
authz-* secrets (see below).
To configure an NBD server on UNIX domain socket path ``/tmp/nbd.sock``::
To configure an NBD server on UNIX domain socket path
``/var/run/qsd-nbd.sock``::
--nbd-server addr.type=unix,addr.path=/tmp/nbd.sock
--nbd-server addr.type=unix,addr.path=/var/run/qsd-nbd.sock
.. option:: --object help
--object <type>,help
@@ -118,6 +122,20 @@ Standard options:
List object properties with ``<type>,help``. See the :manpage:`qemu(1)`
manual page for a description of the object properties.
.. option:: --pidfile PATH
is the path to a file where the daemon writes its pid. This allows scripts to
stop the daemon by sending a signal::
$ kill -SIGTERM $(<path/to/qsd.pid)
A file lock is applied to the file so only one instance of the daemon can run
with a given pid file path. The daemon unlinks its pid file when terminating.
The pid file is written after chardevs, exports, and NBD servers have been
created but before accepting connections. The daemon has started successfully
when the pid file is written and clients may begin connecting.
Examples
--------
Launch the daemon with QMP monitor socket ``qmp.sock`` so clients can execute
@@ -127,6 +145,42 @@ QMP commands::
--chardev socket,path=qmp.sock,server=on,wait=off,id=char1 \
--monitor chardev=char1
Launch the daemon from Python with a QMP monitor socket using file descriptor
passing so there is no need to busy wait for the QMP monitor to become
available::
#!/usr/bin/env python3
import subprocess
import socket
sock_path = '/var/run/qmp.sock'
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as listen_sock:
listen_sock.bind(sock_path)
listen_sock.listen()
fd = listen_sock.fileno()
subprocess.Popen(
['qemu-storage-daemon',
'--chardev', f'socket,fd={fd},server=on,id=char1',
'--monitor', 'chardev=char1'],
pass_fds=[fd],
)
# listen_sock was automatically closed when leaving the 'with' statement
# body. If the daemon process terminated early then the following connect()
# will fail with "Connection refused" because no process has the listen
# socket open anymore. Launch errors can be detected this way.
qmp_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
qmp_sock.connect(sock_path)
...QMP interaction...
The same socket spawning approach also works with the ``--nbd-server
addr.type=fd,addr.str=<fd>`` and ``--export
type=vhost-user-blk,addr.type=fd,addr.str=<fd>`` options.
Export raw image file ``disk.img`` over NBD UNIX domain socket ``nbd.sock``::
$ qemu-storage-daemon \
+3 -4
View File
@@ -54,6 +54,9 @@ static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
{
VHostUserBlk *s = VHOST_USER_BLK(vdev);
/* Our num_queues overrides the device backend */
virtio_stw_p(vdev, &s->blkcfg.num_queues, s->num_queues);
memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
}
@@ -491,10 +494,6 @@ reconnect:
goto reconnect;
}
if (s->blkcfg.num_queues != s->num_queues) {
s->blkcfg.num_queues = s->num_queues;
}
return;
virtio_err:
+2
View File
@@ -57,6 +57,8 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter);
uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap,
uint64_t offset, uint64_t bytes);
uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap);
uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size,
const BdrvDirtyBitmap *bitmap);
void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap,
uint8_t *buf, uint64_t offset,
uint64_t bytes);
+53 -3
View File
@@ -59,6 +59,7 @@
#include "sysemu/runstate.h"
#include "trace/control.h"
static const char *pid_file;
static volatile bool exit_requested = false;
void qemu_system_killed(int signal, pid_t pid)
@@ -115,6 +116,8 @@ static void help(void)
" See the qemu(1) man page for documentation of the\n"
" objects that can be added.\n"
"\n"
" --pidfile <path> write process ID to a file after startup\n"
"\n"
QEMU_HELP_BOTTOM "\n",
error_get_progname());
}
@@ -126,6 +129,7 @@ enum {
OPTION_MONITOR,
OPTION_NBD_SERVER,
OPTION_OBJECT,
OPTION_PIDFILE,
};
extern QemuOptsList qemu_chardev_opts;
@@ -152,6 +156,20 @@ static void init_qmp_commands(void)
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}
static int getopt_set_loc(int argc, char **argv, const char *optstring,
const struct option *longopts)
{
int c, save_index;
optarg = NULL;
save_index = optind;
c = getopt_long(argc, argv, optstring, longopts, NULL);
if (optarg) {
loc_set_cmdline(argv, save_index, MAX(1, optind - save_index));
}
return c;
}
static void process_options(int argc, char *argv[])
{
int c;
@@ -164,6 +182,7 @@ static void process_options(int argc, char *argv[])
{"monitor", required_argument, NULL, OPTION_MONITOR},
{"nbd-server", required_argument, NULL, OPTION_NBD_SERVER},
{"object", required_argument, NULL, OPTION_OBJECT},
{"pidfile", required_argument, NULL, OPTION_PIDFILE},
{"trace", required_argument, NULL, 'T'},
{"version", no_argument, NULL, 'V'},
{0, 0, 0, 0}
@@ -174,7 +193,7 @@ static void process_options(int argc, char *argv[])
* they are given on the command lines. This means that things must be
* defined first before they can be referenced in another option.
*/
while ((c = getopt_long(argc, argv, "hT:V", long_options, NULL)) != -1) {
while ((c = getopt_set_loc(argc, argv, "-hT:V", long_options)) != -1) {
switch (c) {
case '?':
exit(EXIT_FAILURE);
@@ -275,14 +294,38 @@ static void process_options(int argc, char *argv[])
qobject_unref(args);
break;
}
case OPTION_PIDFILE:
pid_file = optarg;
break;
case 1:
error_report("Unexpected argument");
exit(EXIT_FAILURE);
default:
g_assert_not_reached();
}
}
if (optind != argc) {
error_report("Unexpected argument: %s", argv[optind]);
loc_set_none();
}
static void pid_file_cleanup(void)
{
unlink(pid_file);
}
static void pid_file_init(void)
{
Error *err = NULL;
if (!pid_file) {
return;
}
if (!qemu_write_pidfile(pid_file, &err)) {
error_reportf_err(err, "cannot create PID file: ");
exit(EXIT_FAILURE);
}
atexit(pid_file_cleanup);
}
int main(int argc, char *argv[])
@@ -312,6 +355,13 @@ int main(int argc, char *argv[])
qemu_init_main_loop(&error_fatal);
process_options(argc, argv);
/*
* Write the pid file after creating chardevs, exports, and NBD servers but
* before accepting connections. This ordering is documented. Do not change
* it.
*/
pid_file_init();
while (!exit_requested) {
main_loop_wait(false);
}
+2 -2
View File
@@ -153,7 +153,7 @@ class TestSingleDrive(iotests.QMPTestCase):
def test_device_not_found(self):
result = self.vm.qmp('block-stream', device='nonexistent')
self.assert_qmp(result, 'error/desc',
'Cannot find device=nonexistent nor node_name=nonexistent')
'Cannot find device=\'nonexistent\' nor node-name=\'nonexistent\'')
def test_job_id_missing(self):
result = self.vm.qmp('block-stream', device='mid')
@@ -507,7 +507,7 @@ class TestParallelOps(iotests.QMPTestCase):
# Error: the base node does not exist
result = self.vm.qmp('block-stream', device='node4', base_node='none', job_id='stream')
self.assert_qmp(result, 'error/desc',
'Cannot find device= nor node_name=none')
'Cannot find device=\'\' nor node-name=\'none\'')
# Error: the base node is not a backing file of the top node
result = self.vm.qmp('block-stream', device='node4', base_node='node6', job_id='stream')
+2 -2
View File
@@ -175,13 +175,13 @@ class TestSingleDrive(ImageCommitTestCase):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile")
self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'")
def test_base_node_invalid(self):
self.assert_no_active_block_jobs()
result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile")
self.assert_qmp(result, 'error/desc', "Cannot find device='' nor node-name='badfile'")
def test_top_path_and_node(self):
self.assert_no_active_block_jobs()

Some files were not shown because too many files have changed in this diff Show More