block: return errors from bdrv_register_buf()

Registering an I/O buffer is only a performance optimization hint but it
is still necessary to return errors when it fails.

Later patches will need to detect errors when registering buffers but an
immediate advantage is that error_report() calls are no longer needed in
block driver .bdrv_register_buf() functions.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 20221013185908.1297568-8-stefanha@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2022-10-13 14:59:02 -04:00
parent e8b6535533
commit f4ec04bae9
7 changed files with 52 additions and 18 deletions

View file

@ -2545,10 +2545,10 @@ static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
}
}
void blk_register_buf(BlockBackend *blk, void *host, size_t size)
bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp)
{
GLOBAL_STATE_CODE();
bdrv_register_buf(blk_bs(blk), host, size);
return bdrv_register_buf(blk_bs(blk), host, size, errp);
}
void blk_unregister_buf(BlockBackend *blk, void *host, size_t size)

View file

@ -3277,17 +3277,45 @@ void bdrv_io_unplug(BlockDriverState *bs)
}
}
void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size)
/* Helper that undoes bdrv_register_buf() when it fails partway through */
static void bdrv_register_buf_rollback(BlockDriverState *bs,
void *host,
size_t size,
BdrvChild *final_child)
{
BdrvChild *child;
QLIST_FOREACH(child, &bs->children, next) {
if (child == final_child) {
break;
}
bdrv_unregister_buf(child->bs, host, size);
}
if (bs->drv && bs->drv->bdrv_unregister_buf) {
bs->drv->bdrv_unregister_buf(bs, host, size);
}
}
bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size,
Error **errp)
{
BdrvChild *child;
GLOBAL_STATE_CODE();
if (bs->drv && bs->drv->bdrv_register_buf) {
bs->drv->bdrv_register_buf(bs, host, size);
if (!bs->drv->bdrv_register_buf(bs, host, size, errp)) {
return false;
}
}
QLIST_FOREACH(child, &bs->children, next) {
bdrv_register_buf(child->bs, host, size);
if (!bdrv_register_buf(child->bs, host, size, errp)) {
bdrv_register_buf_rollback(bs, host, size, child);
return false;
}
}
return true;
}
void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size)

View file

@ -1587,19 +1587,19 @@ static void nvme_aio_unplug(BlockDriverState *bs)
}
}
static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size)
static bool nvme_register_buf(BlockDriverState *bs, void *host, size_t size,
Error **errp)
{
int ret;
Error *local_err = NULL;
BDRVNVMeState *s = bs->opaque;
ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL, &local_err);
if (ret) {
/* FIXME: we may run out of IOVA addresses after repeated
* bdrv_register_buf/bdrv_unregister_buf, because nvme_vfio_dma_unmap
* doesn't reclaim addresses for fixed mappings. */
error_reportf_err(local_err, "nvme_register_buf failed: ");
}
/*
* FIXME: we may run out of IOVA addresses after repeated
* bdrv_register_buf/bdrv_unregister_buf, because nvme_vfio_dma_unmap
* doesn't reclaim addresses for fixed mappings.
*/
ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL, errp);
return ret == 0;
}
static void nvme_unregister_buf(BlockDriverState *bs, void *host, size_t size)

View file

@ -246,8 +246,11 @@ void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
*
* Buffers must not overlap and they must be unregistered with the same <host,
* size> values that they were registered with.
*
* Returns: true on success, false on failure
*/
void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size);
bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size,
Error **errp);
void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size);
void bdrv_cancel_in_flight(BlockDriverState *bs);

View file

@ -433,8 +433,11 @@ struct BlockDriver {
* that it can do IOMMU mapping with VFIO etc., in order to get better
* performance. In the case of VFIO drivers, this callback is used to do
* DMA mapping for hot buffers.
*
* Returns: true on success, false on failure
*/
void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size);
bool (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size,
Error **errp);
void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host, size_t size);
/*

View file

@ -106,7 +106,7 @@ void blk_io_limits_enable(BlockBackend *blk, const char *group);
void blk_io_limits_update_group(BlockBackend *blk, const char *group);
void blk_set_force_allow_inactivate(BlockBackend *blk);
void blk_register_buf(BlockBackend *blk, void *host, size_t size);
bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp);
void blk_unregister_buf(BlockBackend *blk, void *host, size_t size);
const BdrvChild *blk_root(BlockBackend *blk);

View file

@ -4570,7 +4570,7 @@ static int img_bench(int argc, char **argv)
data.buf = blk_blockalign(blk, buf_size);
memset(data.buf, pattern, data.nrreq * data.bufsize);
blk_register_buf(blk, data.buf, buf_size);
blk_register_buf(blk, data.buf, buf_size, &error_fatal);
data.qiov = g_new(QEMUIOVector, data.nrreq);
for (i = 0; i < data.nrreq; i++) {