Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging

Pull request

This pull request contain's Sam Li's zoned storage support in the QEMU block
layer and virtio-blk emulation.

v2:
- Sam fixed the CI failures. CI passes for me now. [Richard]

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEhpWov9P5fNqsNXdanKSrs4Grc8gFAmRiWCgACgkQnKSrs4Gr
# c8h/7gf+MMm2cGEaf376t8HMwTc6wbXVfbmAlZrge2EXPZfFvEaxj7HClcEraOgV
# yJsGWeU6mOw4r68ICJ/4KhrY1cdv+VZym/LsMLMcFUTXFHnyX4pyU3am31FPOI4K
# +wrDYJOJhc4DkAESWGgEWiMKpuO/uUEgBmHdW+qPFCl77Yl/eP6H5uNP6nGFn55p
# QpS/l8iha7PDkc81EsrjA+e/YI0ubfNSP7+zZElhQ98354CQ0MCfmZ6h9bT+o2bu
# R7SBUj80e+2X0a1b9s/2Jz/x8l4TEsl8kr48/Q1usq3GVVkbjEgqsk6wTN13Q/4g
# CeIR7E61ZeYzmpb4tLFRIqK2Jw+NEQ==
# =Q8xW
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 15 May 2023 09:04:56 AM PDT
# gpg:                using RSA key 8695A8BFD3F97CDAAC35775A9CA4ABB381AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" [full]
# gpg:                 aka "Stefan Hajnoczi <stefanha@gmail.com>" [full]

* tag 'block-pull-request' of https://gitlab.com/stefanha/qemu:
  docs/zoned-storage:add zoned emulation use case
  virtio-blk: add some trace events for zoned emulation
  block: add accounting for zone append operation
  virtio-blk: add zoned storage emulation for zoned devices
  block: add some trace events for zone append
  qemu-iotests: test zone append operation
  block: introduce zone append write for zoned devices
  file-posix: add tracking of the zone write pointers
  docs/zoned-storage: add zoned device documentation
  block: add some trace events for new block layer APIs
  iotests: test new zone operations
  block: add zoned BlockDriver check to block layer
  block/raw-format: add zone operations to pass through requests
  block/block-backend: add block layer APIs resembling Linux ZonedBlockDevice ioctls
  block/file-posix: introduce helper functions for sysfs attributes
  block/block-common: add zoned device structs

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2023-05-15 13:54:33 -07:00
30 changed files with 2106 additions and 58 deletions
+19
View File
@@ -7982,6 +7982,25 @@ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs,
return;
}
/*
* Non-zoned block drivers do not follow zoned storage constraints
* (i.e. sequential writes to zones). Refuse mixing zoned and non-zoned
* drivers in a graph.
*/
if (!parent_bs->drv->supports_zoned_children &&
child_bs->bl.zoned == BLK_Z_HM) {
/*
* The host-aware model allows zoned storage constraints and random
* write. Allow mixing host-aware and non-zoned drivers. Using
* host-aware device as a regular device.
*/
error_setg(errp, "Cannot add a %s child to a %s parent",
child_bs->bl.zoned == BLK_Z_HM ? "zoned" : "non-zoned",
parent_bs->drv->supports_zoned_children ?
"support zoned children" : "not support zoned children");
return;
}
if (!QLIST_EMPTY(&child_bs->parents)) {
error_setg(errp, "The node %s already has a parent",
child_bs->node_name);
+198
View File
@@ -1845,6 +1845,204 @@ int coroutine_fn blk_co_flush(BlockBackend *blk)
return ret;
}
static void coroutine_fn blk_aio_zone_report_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_zone_report(rwco->blk, rwco->offset,
(unsigned int*)(uintptr_t)acb->bytes,
rwco->iobuf);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset,
unsigned int *nr_zones,
BlockZoneDescriptor *zones,
BlockCompletionFunc *cb, void *opaque)
{
BlkAioEmAIOCB *acb;
Coroutine *co;
IO_CODE();
blk_inc_in_flight(blk);
acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque);
acb->rwco = (BlkRwCo) {
.blk = blk,
.offset = offset,
.iobuf = zones,
.ret = NOT_DONE,
};
acb->bytes = (int64_t)(uintptr_t)nr_zones,
acb->has_returned = false;
co = qemu_coroutine_create(blk_aio_zone_report_entry, acb);
aio_co_enter(blk_get_aio_context(blk), co);
acb->has_returned = true;
if (acb->rwco.ret != NOT_DONE) {
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
blk_aio_complete_bh, acb);
}
return &acb->common;
}
static void coroutine_fn blk_aio_zone_mgmt_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_zone_mgmt(rwco->blk,
(BlockZoneOp)(uintptr_t)rwco->iobuf,
rwco->offset, acb->bytes);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op,
int64_t offset, int64_t len,
BlockCompletionFunc *cb, void *opaque) {
BlkAioEmAIOCB *acb;
Coroutine *co;
IO_CODE();
blk_inc_in_flight(blk);
acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque);
acb->rwco = (BlkRwCo) {
.blk = blk,
.offset = offset,
.iobuf = (void *)(uintptr_t)op,
.ret = NOT_DONE,
};
acb->bytes = len;
acb->has_returned = false;
co = qemu_coroutine_create(blk_aio_zone_mgmt_entry, acb);
aio_co_enter(blk_get_aio_context(blk), co);
acb->has_returned = true;
if (acb->rwco.ret != NOT_DONE) {
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
blk_aio_complete_bh, acb);
}
return &acb->common;
}
static void coroutine_fn blk_aio_zone_append_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_zone_append(rwco->blk, (int64_t *)(uintptr_t)acb->bytes,
rwco->iobuf, rwco->flags);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset,
QEMUIOVector *qiov, BdrvRequestFlags flags,
BlockCompletionFunc *cb, void *opaque) {
BlkAioEmAIOCB *acb;
Coroutine *co;
IO_CODE();
blk_inc_in_flight(blk);
acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque);
acb->rwco = (BlkRwCo) {
.blk = blk,
.ret = NOT_DONE,
.flags = flags,
.iobuf = qiov,
};
acb->bytes = (int64_t)(uintptr_t)offset;
acb->has_returned = false;
co = qemu_coroutine_create(blk_aio_zone_append_entry, acb);
aio_co_enter(blk_get_aio_context(blk), co);
acb->has_returned = true;
if (acb->rwco.ret != NOT_DONE) {
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
blk_aio_complete_bh, acb);
}
return &acb->common;
}
/*
* Send a zone_report command.
* offset is a byte offset from the start of the device. No alignment
* required for offset.
* nr_zones represents IN maximum and OUT actual.
*/
int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset,
unsigned int *nr_zones,
BlockZoneDescriptor *zones)
{
int ret;
IO_CODE();
blk_inc_in_flight(blk); /* increase before waiting */
blk_wait_while_drained(blk);
GRAPH_RDLOCK_GUARD();
if (!blk_is_available(blk)) {
blk_dec_in_flight(blk);
return -ENOMEDIUM;
}
ret = bdrv_co_zone_report(blk_bs(blk), offset, nr_zones, zones);
blk_dec_in_flight(blk);
return ret;
}
/*
* Send a zone_management command.
* op is the zone operation;
* offset is the byte offset from the start of the zoned device;
* len is the maximum number of bytes the command should operate on. It
* should be aligned with the device zone size.
*/
int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op,
int64_t offset, int64_t len)
{
int ret;
IO_CODE();
blk_inc_in_flight(blk);
blk_wait_while_drained(blk);
GRAPH_RDLOCK_GUARD();
ret = blk_check_byte_request(blk, offset, len);
if (ret < 0) {
blk_dec_in_flight(blk);
return ret;
}
ret = bdrv_co_zone_mgmt(blk_bs(blk), op, offset, len);
blk_dec_in_flight(blk);
return ret;
}
/*
* Send a zone_append command.
*/
int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset,
QEMUIOVector *qiov, BdrvRequestFlags flags)
{
int ret;
IO_CODE();
blk_inc_in_flight(blk);
blk_wait_while_drained(blk);
GRAPH_RDLOCK_GUARD();
if (!blk_is_available(blk)) {
blk_dec_in_flight(blk);
return -ENOMEDIUM;
}
ret = bdrv_co_zone_append(blk_bs(blk), offset, qiov, flags);
blk_dec_in_flight(blk);
return ret;
}
void blk_drain(BlockBackend *blk)
{
BlockDriverState *bs = blk_bs(blk);
+646 -46
View File
File diff suppressed because it is too large Load Diff
+68
View File
@@ -3113,6 +3113,74 @@ out:
return co.ret;
}
int coroutine_fn bdrv_co_zone_report(BlockDriverState *bs, int64_t offset,
unsigned int *nr_zones,
BlockZoneDescriptor *zones)
{
BlockDriver *drv = bs->drv;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
IO_CODE();
bdrv_inc_in_flight(bs);
if (!drv || !drv->bdrv_co_zone_report || bs->bl.zoned == BLK_Z_NONE) {
co.ret = -ENOTSUP;
goto out;
}
co.ret = drv->bdrv_co_zone_report(bs, offset, nr_zones, zones);
out:
bdrv_dec_in_flight(bs);
return co.ret;
}
int coroutine_fn bdrv_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
int64_t offset, int64_t len)
{
BlockDriver *drv = bs->drv;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
IO_CODE();
bdrv_inc_in_flight(bs);
if (!drv || !drv->bdrv_co_zone_mgmt || bs->bl.zoned == BLK_Z_NONE) {
co.ret = -ENOTSUP;
goto out;
}
co.ret = drv->bdrv_co_zone_mgmt(bs, op, offset, len);
out:
bdrv_dec_in_flight(bs);
return co.ret;
}
int coroutine_fn bdrv_co_zone_append(BlockDriverState *bs, int64_t *offset,
QEMUIOVector *qiov,
BdrvRequestFlags flags)
{
int ret;
BlockDriver *drv = bs->drv;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
IO_CODE();
ret = bdrv_check_qiov_request(*offset, qiov->size, qiov, 0, NULL);
if (ret < 0) {
return ret;
}
bdrv_inc_in_flight(bs);
if (!drv || !drv->bdrv_co_zone_append || bs->bl.zoned == BLK_Z_NONE) {
co.ret = -ENOTSUP;
goto out;
}
co.ret = drv->bdrv_co_zone_append(bs, offset, qiov, flags);
out:
bdrv_dec_in_flight(bs);
return co.ret;
}
void *qemu_blockalign(BlockDriverState *bs, size_t size)
{
IO_CODE();
+4
View File
@@ -350,6 +350,10 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s,
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
luringcb->qiov->niov, offset);
break;
case QEMU_AIO_ZONE_APPEND:
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
luringcb->qiov->niov, offset);
break;
case QEMU_AIO_READ:
io_uring_prep_readv(sqes, fd, luringcb->qiov->iov,
luringcb->qiov->niov, offset);
+3
View File
@@ -394,6 +394,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
case QEMU_AIO_WRITE:
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
break;
case QEMU_AIO_ZONE_APPEND:
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
break;
case QEMU_AIO_READ:
io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
break;
+11
View File
@@ -517,6 +517,7 @@ void qmp_block_latency_histogram_set(
bool has_boundaries, uint64List *boundaries,
bool has_boundaries_read, uint64List *boundaries_read,
bool has_boundaries_write, uint64List *boundaries_write,
bool has_boundaries_append, uint64List *boundaries_append,
bool has_boundaries_flush, uint64List *boundaries_flush,
Error **errp)
{
@@ -557,6 +558,16 @@ void qmp_block_latency_histogram_set(
}
}
if (has_boundaries || has_boundaries_append) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_ZONE_APPEND,
has_boundaries_append ? boundaries_append : boundaries);
if (ret) {
error_setg(errp, "Device '%s' set append write boundaries fail", id);
return;
}
}
if (has_boundaries || has_boundaries_flush) {
ret = block_latency_histogram_set(
stats, BLOCK_ACCT_FLUSH,
+18
View File
@@ -533,27 +533,36 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ];
ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE];
ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND];
ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP];
ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ];
ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE];
ds->zone_append_operations = stats->nr_ops[BLOCK_ACCT_ZONE_APPEND];
ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP];
ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ];
ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE];
ds->failed_zone_append_operations =
stats->failed_ops[BLOCK_ACCT_ZONE_APPEND];
ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH];
ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP];
ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ];
ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE];
ds->invalid_zone_append_operations =
stats->invalid_ops[BLOCK_ACCT_ZONE_APPEND];
ds->invalid_flush_operations =
stats->invalid_ops[BLOCK_ACCT_FLUSH];
ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP];
ds->rd_merged = stats->merged[BLOCK_ACCT_READ];
ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE];
ds->zone_append_merged = stats->merged[BLOCK_ACCT_ZONE_APPEND];
ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP];
ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH];
ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE];
ds->zone_append_total_time_ns =
stats->total_time_ns[BLOCK_ACCT_ZONE_APPEND];
ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ];
ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH];
ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP];
@@ -571,6 +580,7 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ];
TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE];
TimedAverage *zap = &ts->latency[BLOCK_ACCT_ZONE_APPEND];
TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH];
dev_stats->interval_length = ts->interval_length;
@@ -583,6 +593,10 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
dev_stats->max_wr_latency_ns = timed_average_max(wr);
dev_stats->avg_wr_latency_ns = timed_average_avg(wr);
dev_stats->min_zone_append_latency_ns = timed_average_min(zap);
dev_stats->max_zone_append_latency_ns = timed_average_max(zap);
dev_stats->avg_zone_append_latency_ns = timed_average_avg(zap);
dev_stats->min_flush_latency_ns = timed_average_min(fl);
dev_stats->max_flush_latency_ns = timed_average_max(fl);
dev_stats->avg_flush_latency_ns = timed_average_avg(fl);
@@ -591,6 +605,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
block_acct_queue_depth(ts, BLOCK_ACCT_READ);
dev_stats->avg_wr_queue_depth =
block_acct_queue_depth(ts, BLOCK_ACCT_WRITE);
dev_stats->avg_zone_append_queue_depth =
block_acct_queue_depth(ts, BLOCK_ACCT_ZONE_APPEND);
QAPI_LIST_PREPEND(ds->timed_stats, dev_stats);
}
@@ -600,6 +616,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk)
= bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_READ]);
ds->wr_latency_histogram
= bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_WRITE]);
ds->zone_append_latency_histogram
= bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]);
ds->flush_latency_histogram
= bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]);
}
+26
View File
@@ -317,6 +317,28 @@ raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes)
return bdrv_co_pdiscard(bs->file, offset, bytes);
}
static int coroutine_fn GRAPH_RDLOCK
raw_co_zone_report(BlockDriverState *bs, int64_t offset,
unsigned int *nr_zones,
BlockZoneDescriptor *zones)
{
return bdrv_co_zone_report(bs->file->bs, offset, nr_zones, zones);
}
static int coroutine_fn GRAPH_RDLOCK
raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op,
int64_t offset, int64_t len)
{
return bdrv_co_zone_mgmt(bs->file->bs, op, offset, len);
}
static int coroutine_fn GRAPH_RDLOCK
raw_co_zone_append(BlockDriverState *bs,int64_t *offset, QEMUIOVector *qiov,
BdrvRequestFlags flags)
{
return bdrv_co_zone_append(bs->file->bs, offset, qiov, flags);
}
static int64_t coroutine_fn GRAPH_RDLOCK
raw_co_getlength(BlockDriverState *bs)
{
@@ -608,6 +630,7 @@ static void raw_child_perm(BlockDriverState *bs, BdrvChild *c,
BlockDriver bdrv_raw = {
.format_name = "raw",
.instance_size = sizeof(BDRVRawState),
.supports_zoned_children = true,
.bdrv_probe = &raw_probe,
.bdrv_reopen_prepare = &raw_reopen_prepare,
.bdrv_reopen_commit = &raw_reopen_commit,
@@ -619,6 +642,9 @@ BlockDriver bdrv_raw = {
.bdrv_co_pwritev = &raw_co_pwritev,
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
.bdrv_co_pdiscard = &raw_co_pdiscard,
.bdrv_co_zone_report = &raw_co_zone_report,
.bdrv_co_zone_mgmt = &raw_co_zone_mgmt,
.bdrv_co_zone_append = &raw_co_zone_append,
.bdrv_co_block_status = &raw_co_block_status,
.bdrv_co_copy_range_from = &raw_co_copy_range_from,
.bdrv_co_copy_range_to = &raw_co_copy_range_to,
+4
View File
@@ -209,6 +209,10 @@ file_FindEjectableOpticalMedia(const char *media) "Matching using %s"
file_setup_cdrom(const char *partition) "Using %s as optical disc"
file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d"
file_flush_fdatasync_failed(int err) "errno %d"
zbd_zone_report(void *bs, unsigned int nr_zones, int64_t sector) "bs %p report %d zones starting at sector offset 0x%" PRIx64 ""
zbd_zone_mgmt(void *bs, const char *op_name, int64_t sector, int64_t len) "bs %p %s starts at sector offset 0x%" PRIx64 " over a range of 0x%" PRIx64 " sectors"
zbd_zone_append(void *bs, int64_t sector) "bs %p append at sector offset 0x%" PRIx64 ""
zbd_zone_append_complete(void *bs, int64_t sector) "bs %p returns append sector 0x%" PRIx64 ""
# ssh.c
sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)"
+1
View File
@@ -12,3 +12,4 @@ generated from in-code annotations to function prototypes.
memory
modules
ui
zoned-storage
+62
View File
@@ -0,0 +1,62 @@
=============
zoned-storage
=============
Zoned Block Devices (ZBDs) divide the LBA space into block regions called zones
that are larger than the LBA size. They can only allow sequential writes, which
can reduce write amplification in SSDs, and potentially lead to higher
throughput and increased capacity. More details about ZBDs can be found at:
https://zonedstorage.io/docs/introduction/zoned-storage
1. Block layer APIs for zoned storage
-------------------------------------
QEMU block layer supports three zoned storage models:
- BLK_Z_HM: The host-managed zoned model only allows sequential writes access
to zones. It supports ZBD-specific I/O commands that can be used by a host to
manage the zones of a device.
- BLK_Z_HA: The host-aware zoned model allows random write operations in
zones, making it backward compatible with regular block devices.
- BLK_Z_NONE: The non-zoned model has no zones support. It includes both
regular and drive-managed ZBD devices. ZBD-specific I/O commands are not
supported.
The block device information resides inside BlockDriverState. QEMU uses
BlockLimits struct(BlockDriverState::bl) that is continuously accessed by the
block layer while processing I/O requests. A BlockBackend has a root pointer to
a BlockDriverState graph(for example, raw format on top of file-posix). The
zoned storage information can be propagated from the leaf BlockDriverState all
the way up to the BlockBackend. If the zoned storage model in file-posix is
set to BLK_Z_HM, then block drivers will declare support for zoned host device.
The block layer APIs support commands needed for zoned storage devices,
including report zones, four zone operations, and zone append.
2. Emulating zoned storage controllers
--------------------------------------
When the BlockBackend's BlockLimits model reports a zoned storage device, users
like the virtio-blk emulation or the qemu-io-cmds.c utility can use block layer
APIs for zoned storage emulation or testing.
For example, to test zone_report on a null_blk device using qemu-io is::
$ path/to/qemu-io --image-opts -n driver=host_device,filename=/dev/nullb0 -c "zrp offset nr_zones"
To expose the host's zoned block device through virtio-blk, the command line
can be (includes the -device parameter)::
-blockdev node-name=drive0,driver=host_device,filename=/dev/nullb0,cache.direct=on \
-device virtio-blk-pci,drive=drive0
Or only use the -drive parameter::
-driver driver=host_device,file=/dev/nullb0,if=virtio,cache.direct=on
Additionally, QEMU has several ways of supporting zoned storage, including:
(1) Using virtio-scsi: --device scsi-block allows for the passing through of
SCSI ZBC devices, enabling the attachment of ZBC or ZAC HDDs to QEMU.
(2) PCI device pass-through: While NVMe ZNS emulation is available for testing
purposes, it cannot yet pass through a zoned device from the host. To pass on
the NVMe ZNS device to the guest, use VFIO PCI pass the entire NVMe PCI adapter
through to the guest. Likewise, an HDD HBA can be passed on to QEMU all HDDs
attached to the HBA.
+6
View File
@@ -430,6 +430,12 @@ Hard disks
you may corrupt your host data (use the ``-snapshot`` command
line option or modify the device permissions accordingly).
Zoned block devices
Zoned block devices can be passed through to the guest if the emulated storage
controller supports zoned storage. Use ``--blockdev host_device,
node-name=drive0,filename=/dev/nullb0,cache.direct=on`` to pass through
``/dev/nullb0`` as ``drive0``.
Windows
^^^^^^^
+7
View File
@@ -44,9 +44,16 @@ pflash_write_unknown(const char *name, uint8_t cmd) "%s: unknown command 0x%02x"
# virtio-blk.c
virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d"
virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d"
virtio_blk_zone_report_complete(void *vdev, void *req, unsigned int nr_zones, int ret) "vdev %p req %p nr_zones %u ret %d"
virtio_blk_zone_mgmt_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d"
virtio_blk_zone_append_complete(void *vdev, void *req, int64_t sector, int ret) "vdev %p req %p, append sector 0x%" PRIx64 " ret %d"
virtio_blk_handle_write(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu"
virtio_blk_handle_read(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu"
virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "vdev %p mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d"
virtio_blk_handle_zone_report(void *vdev, void *req, int64_t sector, unsigned int nr_zones) "vdev %p req %p sector 0x%" PRIx64 " nr_zones %u"
virtio_blk_handle_zone_mgmt(void *vdev, void *req, uint8_t op, int64_t sector, int64_t len) "vdev %p req %p op 0x%x sector 0x%" PRIx64 " len 0x%" PRIx64 ""
virtio_blk_handle_zone_reset_all(void *vdev, void *req, int64_t sector, int64_t len) "vdev %p req %p sector 0x%" PRIx64 " cap 0x%" PRIx64 ""
virtio_blk_handle_zone_append(void *vdev, void *req, int64_t sector) "vdev %p req %p, append sector 0x%" PRIx64 ""
# hd-geometry.c
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
+2
View File
@@ -29,6 +29,8 @@ static const VirtIOFeature feature_sizes[] = {
.end = endof(struct virtio_blk_config, discard_sector_alignment)},
{.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES,
.end = endof(struct virtio_blk_config, write_zeroes_may_unmap)},
{.flags = 1ULL << VIRTIO_BLK_F_ZONED,
.end = endof(struct virtio_blk_config, zoned)},
{}
};
+405
View File
@@ -17,6 +17,7 @@
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "block/block_int.h"
#include "trace.h"
#include "hw/block/block.h"
#include "hw/qdev-properties.h"
@@ -601,6 +602,351 @@ err:
return err_status;
}
typedef struct ZoneCmdData {
VirtIOBlockReq *req;
struct iovec *in_iov;
unsigned in_num;
union {
struct {
unsigned int nr_zones;
BlockZoneDescriptor *zones;
} zone_report_data;
struct {
int64_t offset;
} zone_append_data;
};
} ZoneCmdData;
/*
* check zoned_request: error checking before issuing requests. If all checks
* passed, return true.
* append: true if only zone append requests issued.
*/
static bool check_zoned_request(VirtIOBlock *s, int64_t offset, int64_t len,
bool append, uint8_t *status) {
BlockDriverState *bs = blk_bs(s->blk);
int index;
if (!virtio_has_feature(s->host_features, VIRTIO_BLK_F_ZONED)) {
*status = VIRTIO_BLK_S_UNSUPP;
return false;
}
if (offset < 0 || len < 0 || len > (bs->total_sectors << BDRV_SECTOR_BITS)
|| offset > (bs->total_sectors << BDRV_SECTOR_BITS) - len) {
*status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
return false;
}
if (append) {
if (bs->bl.write_granularity) {
if ((offset % bs->bl.write_granularity) != 0) {
*status = VIRTIO_BLK_S_ZONE_UNALIGNED_WP;
return false;
}
}
index = offset / bs->bl.zone_size;
if (BDRV_ZT_IS_CONV(bs->wps->wp[index])) {
*status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
return false;
}
if (len / 512 > bs->bl.max_append_sectors) {
if (bs->bl.max_append_sectors == 0) {
*status = VIRTIO_BLK_S_UNSUPP;
} else {
*status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
}
return false;
}
}
return true;
}
static void virtio_blk_zone_report_complete(void *opaque, int ret)
{
ZoneCmdData *data = opaque;
VirtIOBlockReq *req = data->req;
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(req->dev);
struct iovec *in_iov = data->in_iov;
unsigned in_num = data->in_num;
int64_t zrp_size, n, j = 0;
int64_t nz = data->zone_report_data.nr_zones;
int8_t err_status = VIRTIO_BLK_S_OK;
trace_virtio_blk_zone_report_complete(vdev, req, nz, ret);
if (ret) {
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
goto out;
}
struct virtio_blk_zone_report zrp_hdr = (struct virtio_blk_zone_report) {
.nr_zones = cpu_to_le64(nz),
};
zrp_size = sizeof(struct virtio_blk_zone_report)
+ sizeof(struct virtio_blk_zone_descriptor) * nz;
n = iov_from_buf(in_iov, in_num, 0, &zrp_hdr, sizeof(zrp_hdr));
if (n != sizeof(zrp_hdr)) {
virtio_error(vdev, "Driver provided input buffer that is too small!");
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
goto out;
}
for (size_t i = sizeof(zrp_hdr); i < zrp_size;
i += sizeof(struct virtio_blk_zone_descriptor), ++j) {
struct virtio_blk_zone_descriptor desc =
(struct virtio_blk_zone_descriptor) {
.z_start = cpu_to_le64(data->zone_report_data.zones[j].start
>> BDRV_SECTOR_BITS),
.z_cap = cpu_to_le64(data->zone_report_data.zones[j].cap
>> BDRV_SECTOR_BITS),
.z_wp = cpu_to_le64(data->zone_report_data.zones[j].wp
>> BDRV_SECTOR_BITS),
};
switch (data->zone_report_data.zones[j].type) {
case BLK_ZT_CONV:
desc.z_type = VIRTIO_BLK_ZT_CONV;
break;
case BLK_ZT_SWR:
desc.z_type = VIRTIO_BLK_ZT_SWR;
break;
case BLK_ZT_SWP:
desc.z_type = VIRTIO_BLK_ZT_SWP;
break;
default:
g_assert_not_reached();
}
switch (data->zone_report_data.zones[j].state) {
case BLK_ZS_RDONLY:
desc.z_state = VIRTIO_BLK_ZS_RDONLY;
break;
case BLK_ZS_OFFLINE:
desc.z_state = VIRTIO_BLK_ZS_OFFLINE;
break;
case BLK_ZS_EMPTY:
desc.z_state = VIRTIO_BLK_ZS_EMPTY;
break;
case BLK_ZS_CLOSED:
desc.z_state = VIRTIO_BLK_ZS_CLOSED;
break;
case BLK_ZS_FULL:
desc.z_state = VIRTIO_BLK_ZS_FULL;
break;
case BLK_ZS_EOPEN:
desc.z_state = VIRTIO_BLK_ZS_EOPEN;
break;
case BLK_ZS_IOPEN:
desc.z_state = VIRTIO_BLK_ZS_IOPEN;
break;
case BLK_ZS_NOT_WP:
desc.z_state = VIRTIO_BLK_ZS_NOT_WP;
break;
default:
g_assert_not_reached();
}
/* TODO: it takes O(n^2) time complexity. Optimizations required. */
n = iov_from_buf(in_iov, in_num, i, &desc, sizeof(desc));
if (n != sizeof(desc)) {
virtio_error(vdev, "Driver provided input buffer "
"for descriptors that is too small!");
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
}
}
out:
aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
aio_context_release(blk_get_aio_context(s->conf.conf.blk));
g_free(data->zone_report_data.zones);
g_free(data);
}
static void virtio_blk_handle_zone_report(VirtIOBlockReq *req,
struct iovec *in_iov,
unsigned in_num)
{
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
unsigned int nr_zones;
ZoneCmdData *data;
int64_t zone_size, offset;
uint8_t err_status;
if (req->in_len < sizeof(struct virtio_blk_inhdr) +
sizeof(struct virtio_blk_zone_report) +
sizeof(struct virtio_blk_zone_descriptor)) {
virtio_error(vdev, "in buffer too small for zone report");
return;
}
/* start byte offset of the zone report */
offset = virtio_ldq_p(vdev, &req->out.sector) << BDRV_SECTOR_BITS;
if (!check_zoned_request(s, offset, 0, false, &err_status)) {
goto out;
}
nr_zones = (req->in_len - sizeof(struct virtio_blk_inhdr) -
sizeof(struct virtio_blk_zone_report)) /
sizeof(struct virtio_blk_zone_descriptor);
trace_virtio_blk_handle_zone_report(vdev, req,
offset >> BDRV_SECTOR_BITS, nr_zones);
zone_size = sizeof(BlockZoneDescriptor) * nr_zones;
data = g_malloc(sizeof(ZoneCmdData));
data->req = req;
data->in_iov = in_iov;
data->in_num = in_num;
data->zone_report_data.nr_zones = nr_zones;
data->zone_report_data.zones = g_malloc(zone_size),
blk_aio_zone_report(s->blk, offset, &data->zone_report_data.nr_zones,
data->zone_report_data.zones,
virtio_blk_zone_report_complete, data);
return;
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
}
static void virtio_blk_zone_mgmt_complete(void *opaque, int ret)
{
VirtIOBlockReq *req = opaque;
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
int8_t err_status = VIRTIO_BLK_S_OK;
trace_virtio_blk_zone_mgmt_complete(vdev, req,ret);
if (ret) {
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
}
aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
aio_context_release(blk_get_aio_context(s->conf.conf.blk));
}
static int virtio_blk_handle_zone_mgmt(VirtIOBlockReq *req, BlockZoneOp op)
{
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
BlockDriverState *bs = blk_bs(s->blk);
int64_t offset = virtio_ldq_p(vdev, &req->out.sector) << BDRV_SECTOR_BITS;
uint64_t len;
uint64_t capacity = bs->total_sectors << BDRV_SECTOR_BITS;
uint8_t err_status = VIRTIO_BLK_S_OK;
uint32_t type = virtio_ldl_p(vdev, &req->out.type);
if (type == VIRTIO_BLK_T_ZONE_RESET_ALL) {
/* Entire drive capacity */
offset = 0;
len = capacity;
trace_virtio_blk_handle_zone_reset_all(vdev, req, 0,
bs->total_sectors);
} else {
if (bs->bl.zone_size > capacity - offset) {
/* The zoned device allows the last smaller zone. */
len = capacity - bs->bl.zone_size * (bs->bl.nr_zones - 1);
} else {
len = bs->bl.zone_size;
}
trace_virtio_blk_handle_zone_mgmt(vdev, req, op,
offset >> BDRV_SECTOR_BITS,
len >> BDRV_SECTOR_BITS);
}
if (!check_zoned_request(s, offset, len, false, &err_status)) {
goto out;
}
blk_aio_zone_mgmt(s->blk, op, offset, len,
virtio_blk_zone_mgmt_complete, req);
return 0;
out:
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
return err_status;
}
static void virtio_blk_zone_append_complete(void *opaque, int ret)
{
ZoneCmdData *data = opaque;
VirtIOBlockReq *req = data->req;
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(req->dev);
int64_t append_sector, n;
uint8_t err_status = VIRTIO_BLK_S_OK;
if (ret) {
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
goto out;
}
virtio_stq_p(vdev, &append_sector,
data->zone_append_data.offset >> BDRV_SECTOR_BITS);
n = iov_from_buf(data->in_iov, data->in_num, 0, &append_sector,
sizeof(append_sector));
if (n != sizeof(append_sector)) {
virtio_error(vdev, "Driver provided input buffer less than size of "
"append_sector");
err_status = VIRTIO_BLK_S_ZONE_INVALID_CMD;
goto out;
}
trace_virtio_blk_zone_append_complete(vdev, req, append_sector, ret);
out:
aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
aio_context_release(blk_get_aio_context(s->conf.conf.blk));
g_free(data);
}
static int virtio_blk_handle_zone_append(VirtIOBlockReq *req,
struct iovec *out_iov,
struct iovec *in_iov,
uint64_t out_num,
unsigned in_num) {
VirtIOBlock *s = req->dev;
VirtIODevice *vdev = VIRTIO_DEVICE(s);
uint8_t err_status = VIRTIO_BLK_S_OK;
int64_t offset = virtio_ldq_p(vdev, &req->out.sector) << BDRV_SECTOR_BITS;
int64_t len = iov_size(out_iov, out_num);
trace_virtio_blk_handle_zone_append(vdev, req, offset >> BDRV_SECTOR_BITS);
if (!check_zoned_request(s, offset, len, true, &err_status)) {
goto out;
}
ZoneCmdData *data = g_malloc(sizeof(ZoneCmdData));
data->req = req;
data->in_iov = in_iov;
data->in_num = in_num;
data->zone_append_data.offset = offset;
qemu_iovec_init_external(&req->qiov, out_iov, out_num);
block_acct_start(blk_get_stats(s->blk), &req->acct, len,
BLOCK_ACCT_ZONE_APPEND);
blk_aio_zone_append(s->blk, &data->zone_append_data.offset, &req->qiov, 0,
virtio_blk_zone_append_complete, data);
return 0;
out:
aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
virtio_blk_req_complete(req, err_status);
virtio_blk_free_request(req);
aio_context_release(blk_get_aio_context(s->conf.conf.blk));
return err_status;
}
static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
uint32_t type;
@@ -687,6 +1033,24 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
case VIRTIO_BLK_T_FLUSH:
virtio_blk_handle_flush(req, mrb);
break;
case VIRTIO_BLK_T_ZONE_REPORT:
virtio_blk_handle_zone_report(req, in_iov, in_num);
break;
case VIRTIO_BLK_T_ZONE_OPEN:
virtio_blk_handle_zone_mgmt(req, BLK_ZO_OPEN);
break;
case VIRTIO_BLK_T_ZONE_CLOSE:
virtio_blk_handle_zone_mgmt(req, BLK_ZO_CLOSE);
break;
case VIRTIO_BLK_T_ZONE_FINISH:
virtio_blk_handle_zone_mgmt(req, BLK_ZO_FINISH);
break;
case VIRTIO_BLK_T_ZONE_RESET:
virtio_blk_handle_zone_mgmt(req, BLK_ZO_RESET);
break;
case VIRTIO_BLK_T_ZONE_RESET_ALL:
virtio_blk_handle_zone_mgmt(req, BLK_ZO_RESET);
break;
case VIRTIO_BLK_T_SCSI_CMD:
virtio_blk_handle_scsi(req);
break;
@@ -705,6 +1069,14 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
virtio_blk_free_request(req);
break;
}
case VIRTIO_BLK_T_ZONE_APPEND & ~VIRTIO_BLK_T_OUT:
/*
* Passing out_iov/out_num and in_iov/in_num is not safe
* to access req->elem.out_sg directly because it may be
* modified by virtio_blk_handle_request().
*/
virtio_blk_handle_zone_append(req, out_iov, in_iov, out_num, in_num);
break;
/*
* VIRTIO_BLK_T_DISCARD and VIRTIO_BLK_T_WRITE_ZEROES are defined with
* VIRTIO_BLK_T_OUT flag set. We masked this flag in the switch statement,
@@ -890,6 +1262,7 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
{
VirtIOBlock *s = VIRTIO_BLK(vdev);
BlockConf *conf = &s->conf.conf;
BlockDriverState *bs = blk_bs(s->blk);
struct virtio_blk_config blkcfg;
uint64_t capacity;
int64_t length;
@@ -954,6 +1327,30 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
blkcfg.write_zeroes_may_unmap = 1;
virtio_stl_p(vdev, &blkcfg.max_write_zeroes_seg, 1);
}
if (bs->bl.zoned != BLK_Z_NONE) {
switch (bs->bl.zoned) {
case BLK_Z_HM:
blkcfg.zoned.model = VIRTIO_BLK_Z_HM;
break;
case BLK_Z_HA:
blkcfg.zoned.model = VIRTIO_BLK_Z_HA;
break;
default:
g_assert_not_reached();
}
virtio_stl_p(vdev, &blkcfg.zoned.zone_sectors,
bs->bl.zone_size / 512);
virtio_stl_p(vdev, &blkcfg.zoned.max_active_zones,
bs->bl.max_active_zones);
virtio_stl_p(vdev, &blkcfg.zoned.max_open_zones,
bs->bl.max_open_zones);
virtio_stl_p(vdev, &blkcfg.zoned.write_granularity, blk_size);
virtio_stl_p(vdev, &blkcfg.zoned.max_append_sectors,
bs->bl.max_append_sectors);
} else {
blkcfg.zoned.model = VIRTIO_BLK_Z_NONE;
}
memcpy(config, &blkcfg, s->config_size);
}
@@ -1163,6 +1560,14 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
return;
}
BlockDriverState *bs = blk_bs(conf->conf.blk);
if (bs->bl.zoned != BLK_Z_NONE) {
virtio_add_feature(&s->host_features, VIRTIO_BLK_F_ZONED);
if (bs->bl.zoned == BLK_Z_HM) {
virtio_clear_feature(&s->host_features, VIRTIO_BLK_F_DISCARD);
}
}
if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD) &&
(!conf->max_discard_sectors ||
conf->max_discard_sectors > BDRV_REQUEST_MAX_SECTORS)) {
+2
View File
@@ -176,6 +176,8 @@ static const qmp_virtio_feature_map_t virtio_blk_feature_map[] = {
"VIRTIO_BLK_F_DISCARD: Discard command supported"),
FEATURE_ENTRY(VIRTIO_BLK_F_WRITE_ZEROES, \
"VIRTIO_BLK_F_WRITE_ZEROES: Write zeroes command supported"),
FEATURE_ENTRY(VIRTIO_BLK_F_ZONED, \
"VIRTIO_BLK_F_ZONED: Zoned block devices"),
#ifndef VIRTIO_BLK_NO_LEGACY
FEATURE_ENTRY(VIRTIO_BLK_F_BARRIER, \
"VIRTIO_BLK_F_BARRIER: Request barriers supported"),
+1
View File
@@ -37,6 +37,7 @@ enum BlockAcctType {
BLOCK_ACCT_READ,
BLOCK_ACCT_WRITE,
BLOCK_ACCT_FLUSH,
BLOCK_ACCT_ZONE_APPEND,
BLOCK_ACCT_UNMAP,
BLOCK_MAX_IOTYPE,
};
+57
View File
@@ -75,6 +75,57 @@ typedef struct BlockDriver BlockDriver;
typedef struct BdrvChild BdrvChild;
typedef struct BdrvChildClass BdrvChildClass;
typedef enum BlockZoneOp {
BLK_ZO_OPEN,
BLK_ZO_CLOSE,
BLK_ZO_FINISH,
BLK_ZO_RESET,
} BlockZoneOp;
typedef enum BlockZoneModel {
BLK_Z_NONE = 0x0, /* Regular block device */
BLK_Z_HM = 0x1, /* Host-managed zoned block device */
BLK_Z_HA = 0x2, /* Host-aware zoned block device */
} BlockZoneModel;
typedef enum BlockZoneState {
BLK_ZS_NOT_WP = 0x0,
BLK_ZS_EMPTY = 0x1,
BLK_ZS_IOPEN = 0x2,
BLK_ZS_EOPEN = 0x3,
BLK_ZS_CLOSED = 0x4,
BLK_ZS_RDONLY = 0xD,
BLK_ZS_FULL = 0xE,
BLK_ZS_OFFLINE = 0xF,
} BlockZoneState;
typedef enum BlockZoneType {
BLK_ZT_CONV = 0x1, /* Conventional random writes supported */
BLK_ZT_SWR = 0x2, /* Sequential writes required */
BLK_ZT_SWP = 0x3, /* Sequential writes preferred */
} BlockZoneType;
/*
* Zone descriptor data structure.
* Provides information on a zone with all position and size values in bytes.
*/
typedef struct BlockZoneDescriptor {
uint64_t start;
uint64_t length;
uint64_t cap;
uint64_t wp;
BlockZoneType type;
BlockZoneState state;
} BlockZoneDescriptor;
/*
* Track write pointers of a zone in bytes.
*/
typedef struct BlockZoneWps {
CoMutex colock;
uint64_t wp[];
} BlockZoneWps;
typedef struct BlockDriverInfo {
/* in bytes, 0 if irrelevant */
int cluster_size;
@@ -197,6 +248,12 @@ typedef enum {
#define BDRV_SECTOR_BITS 9
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
/*
* Get the first most significant bit of wp. If it is zero, then
* the zone type is SWR.
*/
#define BDRV_ZT_IS_CONV(wp) (wp & (1ULL << 63))
#define BDRV_REQUEST_MAX_SECTORS MIN_CONST(SIZE_MAX >> BDRV_SECTOR_BITS, \
INT_MAX >> BDRV_SECTOR_BITS)
#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS)
+13
View File
@@ -114,6 +114,19 @@ int coroutine_fn GRAPH_RDLOCK bdrv_co_flush(BlockDriverState *bs);
int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
int64_t bytes);
/* Report zone information of zone block device. */
int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_report(BlockDriverState *bs,
int64_t offset,
unsigned int *nr_zones,
BlockZoneDescriptor *zones);
int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_mgmt(BlockDriverState *bs,
BlockZoneOp op,
int64_t offset, int64_t len);
int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_append(BlockDriverState *bs,
int64_t *offset,
QEMUIOVector *qiov,
BdrvRequestFlags flags);
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
int bdrv_block_status(BlockDriverState *bs, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map,

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