mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging
Block layer patches - virtio-scsi: add iothread-vq-mapping parameter - Improve writethrough performance - Fix missing zero init in bdrv_snapshot_goto() - Added scripts/qcow2-to-stdout.py - Code cleanup and iotests fixes # -----BEGIN PGP SIGNATURE----- # # iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmfTDysRHGt3b2xmQHJl # ZGhhdC5jb20ACgkQfwmycsiPL9Yz6A//asOl37zjbtf9pYjY/gliH859TQOppPGD # LB9IIr+nTDME0wfUkCOlag+CeEYZwkeo2PF+XeopsyzlJeBOk4tL7AkY57XYe3lZ # M5hlnNrn6l3gb6iioMg60pEKSMrpKprB16vT3nAtyN6aEXsm9TvtPkWPFTCFGVeK # W74VCr7wuXbfdEJcOGd8WhB9ZHIgwoWYnoL41tvCoefW2yNaMA6X0TLn98toXzOi # il50ZnnchTQngns5R+n+1R1Ma995t393D+CArQcYVRzxKGOs5p0y4otz4gCkMhdp # GVL09R7Ge4TteSJ2myxlN/EjYOxmdoMrVDajr4xPdHBw12MKzgk8i82h4/Es/Q5o # 3Npgx74+jDyqlICb/czTVM5KJINpyO80vO3N3WpYUOQGyTCcYgv7pIpy8pB2o6Te # RPlv0W9bHVSSgThFFLQ0Ud8WRGJe1K/ar8bdmiWN08Wez1avENWaYmsv5zGnFL24 # vD6cNXMR4mF7mzyeWda/5hGKv75djVgX+ZfzvWNT3qgizD56JBOA3RdCRwBZJOJb # TvJkfi5RGyaji9BfKVCYBL3/iDELJEVDW8jxvIIUrS0aPcTHpAQ5gTO7VAokreqZ # 5Smll11eeoEgPPvNLw8ikmOGTWOMkJGrmExP2K1ApANq3kSbBSU4jroEr0BG9PZT # 6Y0hUdtFSdU= # =w2Ri # -----END PGP SIGNATURE----- # gpg: Signature made Fri 14 Mar 2025 01:00:27 HKT # 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 * tag 'for-upstream' of https://repo.or.cz/qemu/kevin: (23 commits) scripts/qcow2-to-stdout.py: Add script to write qcow2 images to stdout virtio-scsi: only expose cmd vqs via iothread-vq-mapping virtio-scsi: handle ctrl virtqueue in main loop virtio-scsi: add iothread-vq-mapping parameter virtio: extract iothread-vq-mapping.h API virtio-blk: tidy up iothread_vq_mapping functions virtio-blk: extract cleanup_iothread_vq_mapping() function virtio-scsi: perform TMFs in appropriate AioContexts virtio-scsi: protect events_dropped field virtio-scsi: introduce event and ctrl virtqueue locks scsi: introduce requests_lock scsi: track per-SCSIRequest AioContext dma: use current AioContext for dma_blk_io() scsi-disk: drop unused SCSIDiskState->bh field iotests: Limit qsd-migrate to working formats aio-posix: Adjust polling time also for new handlers aio-posix: Separate AioPolledEvent per AioHandler aio-posix: Factor out adjust_polling_time() aio: Create AioPolledEvent block/io: Ignore FUA with cache.no-flush=on ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
@@ -2357,18 +2357,6 @@ void *blk_blockalign(BlockBackend *blk, size_t size)
|
||||
return qemu_blockalign(blk ? blk_bs(blk) : NULL, size);
|
||||
}
|
||||
|
||||
bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
GLOBAL_STATE_CODE();
|
||||
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
||||
|
||||
if (!bs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bdrv_op_is_blocked(bs, op, errp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return BB's current AioContext. Note that this context may change
|
||||
|
||||
+21
-8
@@ -194,6 +194,7 @@ static int fd_open(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
static int64_t raw_getlength(BlockDriverState *bs);
|
||||
static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs);
|
||||
|
||||
typedef struct RawPosixAIOData {
|
||||
BlockDriverState *bs;
|
||||
@@ -804,6 +805,13 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
#endif
|
||||
s->needs_alignment = raw_needs_alignment(bs);
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||
if (s->use_linux_aio && !laio_has_fua()) {
|
||||
bs->supported_write_flags &= ~BDRV_REQ_FUA;
|
||||
} else if (s->use_linux_io_uring && !luring_has_fua()) {
|
||||
bs->supported_write_flags &= ~BDRV_REQ_FUA;
|
||||
}
|
||||
|
||||
bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
/* When extending regular files, we get zeros from the OS */
|
||||
@@ -2477,7 +2485,8 @@ static inline bool raw_check_linux_aio(BDRVRawState *s)
|
||||
#endif
|
||||
|
||||
static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr,
|
||||
uint64_t bytes, QEMUIOVector *qiov, int type)
|
||||
uint64_t bytes, QEMUIOVector *qiov, int type,
|
||||
int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawPosixAIOData acb;
|
||||
@@ -2508,13 +2517,13 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr,
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
} else if (raw_check_linux_io_uring(s)) {
|
||||
assert(qiov->size == bytes);
|
||||
ret = luring_co_submit(bs, s->fd, offset, qiov, type);
|
||||
ret = luring_co_submit(bs, s->fd, offset, qiov, type, flags);
|
||||
goto out;
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
} else if (raw_check_linux_aio(s)) {
|
||||
assert(qiov->size == bytes);
|
||||
ret = laio_co_submit(s->fd, offset, qiov, type,
|
||||
ret = laio_co_submit(s->fd, offset, qiov, type, flags,
|
||||
s->aio_max_batch);
|
||||
goto out;
|
||||
#endif
|
||||
@@ -2534,6 +2543,10 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr,
|
||||
|
||||
assert(qiov->size == bytes);
|
||||
ret = raw_thread_pool_submit(handle_aiocb_rw, &acb);
|
||||
if (ret == 0 && (flags & BDRV_REQ_FUA)) {
|
||||
/* TODO Use pwritev2() instead if it's available */
|
||||
ret = raw_co_flush_to_disk(bs);
|
||||
}
|
||||
goto out; /* Avoid the compiler err of unused label */
|
||||
|
||||
out:
|
||||
@@ -2571,14 +2584,14 @@ static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, QEMUIOVector *qiov,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ);
|
||||
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, QEMUIOVector *qiov,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE);
|
||||
return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
|
||||
@@ -2600,12 +2613,12 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
|
||||
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
if (raw_check_linux_io_uring(s)) {
|
||||
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH);
|
||||
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_LINUX_AIO
|
||||
if (s->has_laio_fdsync && raw_check_linux_aio(s)) {
|
||||
return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
|
||||
return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0, 0);
|
||||
}
|
||||
#endif
|
||||
return raw_thread_pool_submit(handle_aiocb_flush, &acb);
|
||||
@@ -3540,7 +3553,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS);
|
||||
return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND);
|
||||
return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1058,6 +1058,10 @@ bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
if (bs->open_flags & BDRV_O_NO_FLUSH) {
|
||||
flags &= ~BDRV_REQ_FUA;
|
||||
}
|
||||
|
||||
if ((flags & BDRV_REQ_FUA) &&
|
||||
(~bs->supported_write_flags & BDRV_REQ_FUA)) {
|
||||
flags &= ~BDRV_REQ_FUA;
|
||||
|
||||
+22
-3
@@ -335,15 +335,24 @@ static void luring_deferred_fn(void *opaque)
|
||||
*
|
||||
*/
|
||||
static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s,
|
||||
uint64_t offset, int type)
|
||||
uint64_t offset, int type, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
struct io_uring_sqe *sqes = &luringcb->sqeq;
|
||||
|
||||
switch (type) {
|
||||
case QEMU_AIO_WRITE:
|
||||
#ifdef HAVE_IO_URING_PREP_WRITEV2
|
||||
{
|
||||
int luring_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
|
||||
io_uring_prep_writev2(sqes, fd, luringcb->qiov->iov,
|
||||
luringcb->qiov->niov, offset, luring_flags);
|
||||
}
|
||||
#else
|
||||
assert(flags == 0);
|
||||
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
|
||||
luringcb->qiov->niov, offset);
|
||||
#endif
|
||||
break;
|
||||
case QEMU_AIO_ZONE_APPEND:
|
||||
io_uring_prep_writev(sqes, fd, luringcb->qiov->iov,
|
||||
@@ -380,7 +389,8 @@ static int luring_do_submit(int fd, LuringAIOCB *luringcb, LuringState *s,
|
||||
}
|
||||
|
||||
int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset,
|
||||
QEMUIOVector *qiov, int type)
|
||||
QEMUIOVector *qiov, int type,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
int ret;
|
||||
AioContext *ctx = qemu_get_current_aio_context();
|
||||
@@ -393,7 +403,7 @@ int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset,
|
||||
};
|
||||
trace_luring_co_submit(bs, s, &luringcb, fd, offset, qiov ? qiov->size : 0,
|
||||
type);
|
||||
ret = luring_do_submit(fd, &luringcb, s, offset, type);
|
||||
ret = luring_do_submit(fd, &luringcb, s, offset, type, flags);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -448,3 +458,12 @@ void luring_cleanup(LuringState *s)
|
||||
trace_luring_cleanup_state(s);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
bool luring_has_fua(void)
|
||||
{
|
||||
#ifdef HAVE_IO_URING_PREP_WRITEV2
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
+22
-3
@@ -368,7 +368,8 @@ static void laio_deferred_fn(void *opaque)
|
||||
}
|
||||
|
||||
static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
int type, uint64_t dev_max_batch)
|
||||
int type, BdrvRequestFlags flags,
|
||||
uint64_t dev_max_batch)
|
||||
{
|
||||
LinuxAioState *s = laiocb->ctx;
|
||||
struct iocb *iocbs = &laiocb->iocb;
|
||||
@@ -376,7 +377,15 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
|
||||
switch (type) {
|
||||
case QEMU_AIO_WRITE:
|
||||
#ifdef HAVE_IO_PREP_PWRITEV2
|
||||
{
|
||||
int laio_flags = (flags & BDRV_REQ_FUA) ? RWF_DSYNC : 0;
|
||||
io_prep_pwritev2(iocbs, fd, qiov->iov, qiov->niov, offset, laio_flags);
|
||||
}
|
||||
#else
|
||||
assert(flags == 0);
|
||||
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
|
||||
#endif
|
||||
break;
|
||||
case QEMU_AIO_ZONE_APPEND:
|
||||
io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
|
||||
@@ -409,7 +418,8 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
}
|
||||
|
||||
int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
|
||||
int type, uint64_t dev_max_batch)
|
||||
int type, BdrvRequestFlags flags,
|
||||
uint64_t dev_max_batch)
|
||||
{
|
||||
int ret;
|
||||
AioContext *ctx = qemu_get_current_aio_context();
|
||||
@@ -422,7 +432,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
|
||||
.qiov = qiov,
|
||||
};
|
||||
|
||||
ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch);
|
||||
ret = laio_do_submit(fd, &laiocb, offset, type, flags, dev_max_batch);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -505,3 +515,12 @@ bool laio_has_fdsync(int fd)
|
||||
io_destroy(ctx);
|
||||
return (ret == -EINVAL) ? false : true;
|
||||
}
|
||||
|
||||
bool laio_has_fua(void)
|
||||
{
|
||||
#ifdef HAVE_IO_PREP_PWRITEV2
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -296,6 +296,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
bdrv_graph_wrunlock();
|
||||
|
||||
ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp);
|
||||
memset(bs->opaque, 0, drv->instance_size);
|
||||
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
|
||||
qobject_unref(options);
|
||||
if (open_ret < 0) {
|
||||
|
||||
+3
-129
@@ -33,6 +33,7 @@
|
||||
#endif
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "migration/qemu-file-types.h"
|
||||
#include "hw/virtio/iothread-vq-mapping.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "hw/virtio/virtio-blk-common.h"
|
||||
#include "qemu/coroutine.h"
|
||||
@@ -1423,128 +1424,6 @@ static const BlockDevOps virtio_block_ops = {
|
||||
.drained_end = virtio_blk_drained_end,
|
||||
};
|
||||
|
||||
static bool
|
||||
validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list,
|
||||
uint16_t num_queues, Error **errp)
|
||||
{
|
||||
g_autofree unsigned long *vqs = bitmap_new(num_queues);
|
||||
g_autoptr(GHashTable) iothreads =
|
||||
g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
for (IOThreadVirtQueueMappingList *node = list; node; node = node->next) {
|
||||
const char *name = node->value->iothread;
|
||||
uint16List *vq;
|
||||
|
||||
if (!iothread_by_id(name)) {
|
||||
error_setg(errp, "IOThread \"%s\" object does not exist", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_hash_table_add(iothreads, (gpointer)name)) {
|
||||
error_setg(errp,
|
||||
"duplicate IOThread name \"%s\" in iothread-vq-mapping",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node != list) {
|
||||
if (!!node->value->vqs != !!list->value->vqs) {
|
||||
error_setg(errp, "either all items in iothread-vq-mapping "
|
||||
"must have vqs or none of them must have it");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (vq = node->value->vqs; vq; vq = vq->next) {
|
||||
if (vq->value >= num_queues) {
|
||||
error_setg(errp, "vq index %u for IOThread \"%s\" must be "
|
||||
"less than num_queues %u in iothread-vq-mapping",
|
||||
vq->value, name, num_queues);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(vq->value, vqs)) {
|
||||
error_setg(errp, "cannot assign vq %u to IOThread \"%s\" "
|
||||
"because it is already assigned", vq->value, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list->value->vqs) {
|
||||
for (uint16_t i = 0; i < num_queues; i++) {
|
||||
if (!test_bit(i, vqs)) {
|
||||
error_setg(errp,
|
||||
"missing vq %u IOThread assignment in iothread-vq-mapping",
|
||||
i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply_iothread_vq_mapping:
|
||||
* @iothread_vq_mapping_list: The mapping of virtqueues to IOThreads.
|
||||
* @vq_aio_context: The array of AioContext pointers to fill in.
|
||||
* @num_queues: The length of @vq_aio_context.
|
||||
* @errp: If an error occurs, a pointer to the area to store the error.
|
||||
*
|
||||
* Fill in the AioContext for each virtqueue in the @vq_aio_context array given
|
||||
* the iothread-vq-mapping parameter in @iothread_vq_mapping_list.
|
||||
*
|
||||
* Returns: %true on success, %false on failure.
|
||||
**/
|
||||
static bool apply_iothread_vq_mapping(
|
||||
IOThreadVirtQueueMappingList *iothread_vq_mapping_list,
|
||||
AioContext **vq_aio_context,
|
||||
uint16_t num_queues,
|
||||
Error **errp)
|
||||
{
|
||||
IOThreadVirtQueueMappingList *node;
|
||||
size_t num_iothreads = 0;
|
||||
size_t cur_iothread = 0;
|
||||
|
||||
if (!validate_iothread_vq_mapping_list(iothread_vq_mapping_list,
|
||||
num_queues, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (node = iothread_vq_mapping_list; node; node = node->next) {
|
||||
num_iothreads++;
|
||||
}
|
||||
|
||||
for (node = iothread_vq_mapping_list; node; node = node->next) {
|
||||
IOThread *iothread = iothread_by_id(node->value->iothread);
|
||||
AioContext *ctx = iothread_get_aio_context(iothread);
|
||||
|
||||
/* Released in virtio_blk_vq_aio_context_cleanup() */
|
||||
object_ref(OBJECT(iothread));
|
||||
|
||||
if (node->value->vqs) {
|
||||
uint16List *vq;
|
||||
|
||||
/* Explicit vq:IOThread assignment */
|
||||
for (vq = node->value->vqs; vq; vq = vq->next) {
|
||||
assert(vq->value < num_queues);
|
||||
vq_aio_context[vq->value] = ctx;
|
||||
}
|
||||
} else {
|
||||
/* Round-robin vq:IOThread assignment */
|
||||
for (unsigned i = cur_iothread; i < num_queues;
|
||||
i += num_iothreads) {
|
||||
vq_aio_context[i] = ctx;
|
||||
}
|
||||
}
|
||||
|
||||
cur_iothread++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Context: BQL held */
|
||||
static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
|
||||
{
|
||||
@@ -1577,7 +1456,7 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
|
||||
s->vq_aio_context = g_new(AioContext *, conf->num_queues);
|
||||
|
||||
if (conf->iothread_vq_mapping_list) {
|
||||
if (!apply_iothread_vq_mapping(conf->iothread_vq_mapping_list,
|
||||
if (!iothread_vq_mapping_apply(conf->iothread_vq_mapping_list,
|
||||
s->vq_aio_context,
|
||||
conf->num_queues,
|
||||
errp)) {
|
||||
@@ -1611,12 +1490,7 @@ static void virtio_blk_vq_aio_context_cleanup(VirtIOBlock *s)
|
||||
assert(!s->ioeventfd_started);
|
||||
|
||||
if (conf->iothread_vq_mapping_list) {
|
||||
IOThreadVirtQueueMappingList *node;
|
||||
|
||||
for (node = conf->iothread_vq_mapping_list; node; node = node->next) {
|
||||
IOThread *iothread = iothread_by_id(node->value->iothread);
|
||||
object_unref(OBJECT(iothread));
|
||||
}
|
||||
iothread_vq_mapping_cleanup(conf->iothread_vq_mapping_list);
|
||||
}
|
||||
|
||||
if (conf->iothread) {
|
||||
|
||||
+1
-2
@@ -968,8 +968,7 @@ static void ide_dma_cb(void *opaque, int ret)
|
||||
BDRV_SECTOR_SIZE, ide_dma_cb, s);
|
||||
break;
|
||||
case IDE_DMA_TRIM:
|
||||
s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk),
|
||||
&s->sg, offset, BDRV_SECTOR_SIZE,
|
||||
s->bus->dma->aiocb = dma_blk_io(&s->sg, offset, BDRV_SECTOR_SIZE,
|
||||
ide_issue_trim, s, ide_dma_cb, s,
|
||||
DMA_DIRECTION_TO_DEVICE);
|
||||
break;
|
||||
|
||||
+1
-2
@@ -187,8 +187,7 @@ static void pmac_ide_transfer_cb(void *opaque, int ret)
|
||||
pmac_ide_transfer_cb, io);
|
||||
break;
|
||||
case IDE_DMA_TRIM:
|
||||
s->bus->dma->aiocb = dma_blk_io(blk_get_aio_context(s->blk), &s->sg,
|
||||
offset, 0x1, ide_issue_trim, s,
|
||||
s->bus->dma->aiocb = dma_blk_io(&s->sg, offset, 0x1, ide_issue_trim, s,
|
||||
pmac_ide_transfer_cb, io,
|
||||
DMA_DIRECTION_TO_DEVICE);
|
||||
break;
|
||||
|
||||
+86
-35
@@ -100,8 +100,15 @@ static void scsi_device_for_each_req_sync(SCSIDevice *s,
|
||||
assert(!runstate_is_running());
|
||||
assert(qemu_in_main_thread());
|
||||
|
||||
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) {
|
||||
fn(req, opaque);
|
||||
/*
|
||||
* Locking is not necessary because the guest is stopped and no other
|
||||
* threads can be accessing the requests list, but take the lock for
|
||||
* consistency.
|
||||
*/
|
||||
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
|
||||
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) {
|
||||
fn(req, opaque);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,21 +122,29 @@ static void scsi_device_for_each_req_async_bh(void *opaque)
|
||||
{
|
||||
g_autofree SCSIDeviceForEachReqAsyncData *data = opaque;
|
||||
SCSIDevice *s = data->s;
|
||||
AioContext *ctx;
|
||||
SCSIRequest *req;
|
||||
SCSIRequest *next;
|
||||
g_autoptr(GList) reqs = NULL;
|
||||
|
||||
/*
|
||||
* The BB cannot have changed contexts between this BH being scheduled and
|
||||
* now: BBs' AioContexts, when they have a node attached, can only be
|
||||
* changed via bdrv_try_change_aio_context(), in a drained section. While
|
||||
* we have the in-flight counter incremented, that drain must block.
|
||||
* Build a list of requests in this AioContext so fn() can be invoked later
|
||||
* outside requests_lock.
|
||||
*/
|
||||
ctx = blk_get_aio_context(s->conf.blk);
|
||||
assert(ctx == qemu_get_current_aio_context());
|
||||
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
|
||||
AioContext *ctx = qemu_get_current_aio_context();
|
||||
SCSIRequest *req;
|
||||
SCSIRequest *next;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||
data->fn(req, data->fn_opaque);
|
||||
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||
if (req->ctx == ctx) {
|
||||
scsi_req_ref(req); /* dropped after calling fn() */
|
||||
reqs = g_list_prepend(reqs, req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Call fn() on each request */
|
||||
for (GList *elem = g_list_first(reqs); elem; elem = g_list_next(elem)) {
|
||||
data->fn(elem->data, data->fn_opaque);
|
||||
scsi_req_unref(elem->data);
|
||||
}
|
||||
|
||||
/* Drop the reference taken by scsi_device_for_each_req_async() */
|
||||
@@ -139,9 +154,35 @@ static void scsi_device_for_each_req_async_bh(void *opaque)
|
||||
blk_dec_in_flight(s->conf.blk);
|
||||
}
|
||||
|
||||
static void scsi_device_for_each_req_async_do_ctx(gpointer key, gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
AioContext *ctx = key;
|
||||
SCSIDeviceForEachReqAsyncData *params = user_data;
|
||||
SCSIDeviceForEachReqAsyncData *data;
|
||||
|
||||
data = g_new(SCSIDeviceForEachReqAsyncData, 1);
|
||||
data->s = params->s;
|
||||
data->fn = params->fn;
|
||||
data->fn_opaque = params->fn_opaque;
|
||||
|
||||
/*
|
||||
* Hold a reference to the SCSIDevice until
|
||||
* scsi_device_for_each_req_async_bh() finishes.
|
||||
*/
|
||||
object_ref(OBJECT(data->s));
|
||||
|
||||
/* Paired with scsi_device_for_each_req_async_bh() */
|
||||
blk_inc_in_flight(data->s->conf.blk);
|
||||
|
||||
aio_bh_schedule_oneshot(ctx, scsi_device_for_each_req_async_bh, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule @fn() to be invoked for each enqueued request in device @s. @fn()
|
||||
* runs in the AioContext that is executing the request.
|
||||
* must be thread-safe because it runs concurrently in each AioContext that is
|
||||
* executing a request.
|
||||
*
|
||||
* Keeps the BlockBackend's in-flight counter incremented until everything is
|
||||
* done, so draining it will settle all scheduled @fn() calls.
|
||||
*/
|
||||
@@ -151,24 +192,26 @@ static void scsi_device_for_each_req_async(SCSIDevice *s,
|
||||
{
|
||||
assert(qemu_in_main_thread());
|
||||
|
||||
SCSIDeviceForEachReqAsyncData *data =
|
||||
g_new(SCSIDeviceForEachReqAsyncData, 1);
|
||||
/* The set of AioContexts where the requests are being processed */
|
||||
g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL);
|
||||
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
|
||||
SCSIRequest *req;
|
||||
QTAILQ_FOREACH(req, &s->requests, next) {
|
||||
g_hash_table_add(aio_contexts, req->ctx);
|
||||
}
|
||||
}
|
||||
|
||||
data->s = s;
|
||||
data->fn = fn;
|
||||
data->fn_opaque = opaque;
|
||||
|
||||
/*
|
||||
* Hold a reference to the SCSIDevice until
|
||||
* scsi_device_for_each_req_async_bh() finishes.
|
||||
*/
|
||||
object_ref(OBJECT(s));
|
||||
|
||||
/* Paired with blk_dec_in_flight() in scsi_device_for_each_req_async_bh() */
|
||||
blk_inc_in_flight(s->conf.blk);
|
||||
aio_bh_schedule_oneshot(blk_get_aio_context(s->conf.blk),
|
||||
scsi_device_for_each_req_async_bh,
|
||||
data);
|
||||
/* Schedule a BH for each AioContext */
|
||||
SCSIDeviceForEachReqAsyncData params = {
|
||||
.s = s,
|
||||
.fn = fn,
|
||||
.fn_opaque = opaque,
|
||||
};
|
||||
g_hash_table_foreach(
|
||||
aio_contexts,
|
||||
scsi_device_for_each_req_async_do_ctx,
|
||||
¶ms
|
||||
);
|
||||
}
|
||||
|
||||
static void scsi_device_realize(SCSIDevice *s, Error **errp)
|
||||
@@ -349,6 +392,7 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
|
||||
dev->lun = lun;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&dev->requests_lock);
|
||||
QTAILQ_INIT(&dev->requests);
|
||||
scsi_device_realize(dev, &local_err);
|
||||
if (local_err) {
|
||||
@@ -369,6 +413,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev)
|
||||
|
||||
scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE));
|
||||
|
||||
qemu_mutex_destroy(&dev->requests_lock);
|
||||
|
||||
scsi_device_unrealize(dev);
|
||||
|
||||
blockdev_mark_auto_del(dev->conf.blk);
|
||||
@@ -868,6 +914,7 @@ invalid_opcode:
|
||||
}
|
||||
}
|
||||
|
||||
req->ctx = qemu_get_current_aio_context();
|
||||
req->cmd = cmd;
|
||||
req->residual = req->cmd.xfer;
|
||||
|
||||
@@ -964,7 +1011,10 @@ static void scsi_req_enqueue_internal(SCSIRequest *req)
|
||||
req->sg = NULL;
|
||||
}
|
||||
req->enqueued = true;
|
||||
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) {
|
||||
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t scsi_req_enqueue(SCSIRequest *req)
|
||||
@@ -984,7 +1034,9 @@ static void scsi_req_dequeue(SCSIRequest *req)
|
||||
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
|
||||
req->retry = false;
|
||||
if (req->enqueued) {
|
||||
QTAILQ_REMOVE(&req->dev->requests, req, next);
|
||||
WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) {
|
||||
QTAILQ_REMOVE(&req->dev->requests, req, next);
|
||||
}
|
||||
req->enqueued = false;
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
@@ -1961,8 +2013,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
static void scsi_dev_instance_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
SCSIDevice *s = SCSI_DEVICE(dev);
|
||||
SCSIDevice *s = SCSI_DEVICE(obj);
|
||||
|
||||
device_add_bootindex_property(obj, &s->conf.bootindex,
|
||||
"bootindex", NULL,
|
||||
|
||||
+8
-16
@@ -106,7 +106,6 @@ struct SCSIDiskState {
|
||||
uint64_t max_unmap_size;
|
||||
uint64_t max_io_size;
|
||||
uint32_t quirks;
|
||||
QEMUBH *bh;
|
||||
char *version;
|
||||
char *serial;
|
||||
char *vendor;
|
||||
@@ -329,9 +328,8 @@ static void scsi_aio_complete(void *opaque, int ret)
|
||||
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
/* The request must only run in the BlockBackend's AioContext */
|
||||
assert(blk_get_aio_context(s->qdev.conf.blk) ==
|
||||
qemu_get_current_aio_context());
|
||||
/* The request must run in its AioContext */
|
||||
assert(r->req.ctx == qemu_get_current_aio_context());
|
||||
|
||||
assert(r->req.aiocb != NULL);
|
||||
r->req.aiocb = NULL;
|
||||
@@ -431,12 +429,10 @@ static void scsi_dma_complete(void *opaque, int ret)
|
||||
|
||||
static void scsi_read_complete_noio(SCSIDiskReq *r, int ret)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint32_t n;
|
||||
|
||||
/* The request must only run in the BlockBackend's AioContext */
|
||||
assert(blk_get_aio_context(s->qdev.conf.blk) ==
|
||||
qemu_get_current_aio_context());
|
||||
/* The request must run in its AioContext */
|
||||
assert(r->req.ctx == qemu_get_current_aio_context());
|
||||
|
||||
assert(r->req.aiocb == NULL);
|
||||
if (scsi_disk_req_check_error(r, ret, ret > 0)) {
|
||||
@@ -488,8 +484,7 @@ static void scsi_do_read(SCSIDiskReq *r, int ret)
|
||||
if (r->req.sg) {
|
||||
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
|
||||
r->req.residual -= r->req.sg->size;
|
||||
r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
|
||||
r->req.sg, r->sector << BDRV_SECTOR_BITS,
|
||||
r->req.aiocb = dma_blk_io(r->req.sg, r->sector << BDRV_SECTOR_BITS,
|
||||
BDRV_SECTOR_SIZE,
|
||||
sdc->dma_readv, r, scsi_dma_complete, r,
|
||||
DMA_DIRECTION_FROM_DEVICE);
|
||||
@@ -564,12 +559,10 @@ static void scsi_read_data(SCSIRequest *req)
|
||||
|
||||
static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint32_t n;
|
||||
|
||||
/* The request must only run in the BlockBackend's AioContext */
|
||||
assert(blk_get_aio_context(s->qdev.conf.blk) ==
|
||||
qemu_get_current_aio_context());
|
||||
/* The request must run in its AioContext */
|
||||
assert(r->req.ctx == qemu_get_current_aio_context());
|
||||
|
||||
assert (r->req.aiocb == NULL);
|
||||
if (scsi_disk_req_check_error(r, ret, ret > 0)) {
|
||||
@@ -651,8 +644,7 @@ static void scsi_write_data(SCSIRequest *req)
|
||||
if (r->req.sg) {
|
||||
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
|
||||
r->req.residual -= r->req.sg->size;
|
||||
r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
|
||||
r->req.sg, r->sector << BDRV_SECTOR_BITS,
|
||||
r->req.aiocb = dma_blk_io(r->req.sg, r->sector << BDRV_SECTOR_BITS,
|
||||
BDRV_SECTOR_SIZE,
|
||||
sdc->dma_writev, r, scsi_dma_complete, r,
|
||||
DMA_DIRECTION_TO_DEVICE);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "system/block-backend.h"
|
||||
#include "hw/scsi/scsi.h"
|
||||
#include "scsi/constants.h"
|
||||
#include "hw/virtio/iothread-vq-mapping.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
|
||||
/* Context: BQL held */
|
||||
@@ -28,7 +29,14 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
|
||||
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
|
||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||
|
||||
if (vs->conf.iothread) {
|
||||
if (vs->conf.iothread && vs->conf.iothread_vq_mapping_list) {
|
||||
error_setg(errp,
|
||||
"iothread and iothread-vq-mapping properties cannot be set "
|
||||
"at the same time");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vs->conf.iothread || vs->conf.iothread_vq_mapping_list) {
|
||||
if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
|
||||
error_setg(errp,
|
||||
"device is incompatible with iothread "
|
||||
@@ -39,15 +47,64 @@ void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
|
||||
error_setg(errp, "ioeventfd is required for iothread");
|
||||
return;
|
||||
}
|
||||
s->ctx = iothread_get_aio_context(vs->conf.iothread);
|
||||
} else {
|
||||
if (!virtio_device_ioeventfd_enabled(vdev)) {
|
||||
}
|
||||
|
||||
s->vq_aio_context = g_new(AioContext *, vs->conf.num_queues +
|
||||
VIRTIO_SCSI_VQ_NUM_FIXED);
|
||||
|
||||
/*
|
||||
* Handle the ctrl virtqueue in the main loop thread where device resets
|
||||
* can be performed.
|
||||
*/
|
||||
s->vq_aio_context[0] = qemu_get_aio_context();
|
||||
|
||||
/*
|
||||
* Handle the event virtqueue in the main loop thread where its no_poll
|
||||
* behavior won't stop IOThread polling.
|
||||
*/
|
||||
s->vq_aio_context[1] = qemu_get_aio_context();
|
||||
|
||||
if (vs->conf.iothread_vq_mapping_list) {
|
||||
if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list,
|
||||
&s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED],
|
||||
vs->conf.num_queues, errp)) {
|
||||
g_free(s->vq_aio_context);
|
||||
s->vq_aio_context = NULL;
|
||||
return;
|
||||
}
|
||||
s->ctx = qemu_get_aio_context();
|
||||
} else if (vs->conf.iothread) {
|
||||
AioContext *ctx = iothread_get_aio_context(vs->conf.iothread);
|
||||
for (uint16_t i = 0; i < vs->conf.num_queues; i++) {
|
||||
s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx;
|
||||
}
|
||||
|
||||
/* Released in virtio_scsi_dataplane_cleanup() */
|
||||
object_ref(OBJECT(vs->conf.iothread));
|
||||
} else {
|
||||
AioContext *ctx = qemu_get_aio_context();
|
||||
for (unsigned i = 0; i < vs->conf.num_queues; i++) {
|
||||
s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Context: BQL held */
|
||||
void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s)
|
||||
{
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
|
||||
if (vs->conf.iothread_vq_mapping_list) {
|
||||
iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list);
|
||||
}
|
||||
|
||||
if (vs->conf.iothread) {
|
||||
object_unref(OBJECT(vs->conf.iothread));
|
||||
}
|
||||
|
||||
g_free(s->vq_aio_context);
|
||||
s->vq_aio_context = NULL;
|
||||
}
|
||||
|
||||
static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
|
||||
{
|
||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
|
||||
@@ -66,31 +123,20 @@ static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
|
||||
}
|
||||
|
||||
/* Context: BH in IOThread */
|
||||
static void virtio_scsi_dataplane_stop_bh(void *opaque)
|
||||
static void virtio_scsi_dataplane_stop_vq_bh(void *opaque)
|
||||
{
|
||||
VirtIOSCSI *s = opaque;
|
||||
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
|
||||
AioContext *ctx = qemu_get_current_aio_context();
|
||||
VirtQueue *vq = opaque;
|
||||
EventNotifier *host_notifier;
|
||||
int i;
|
||||
|
||||
virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx);
|
||||
host_notifier = virtio_queue_get_host_notifier(vs->ctrl_vq);
|
||||
virtio_queue_aio_detach_host_notifier(vq, ctx);
|
||||
host_notifier = virtio_queue_get_host_notifier(vq);
|
||||
|
||||
/*
|
||||
* Test and clear notifier after disabling event, in case poll callback
|
||||
* didn't have time to run.
|
||||
*/
|
||||
virtio_queue_host_notifier_read(host_notifier);
|
||||
|
||||
virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx);
|
||||
host_notifier = virtio_queue_get_host_notifier(vs->event_vq);
|
||||
virtio_queue_host_notifier_read(host_notifier);
|
||||
|
||||
for (i = 0; i < vs->conf.num_queues; i++) {
|
||||
virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx);
|
||||
host_notifier = virtio_queue_get_host_notifier(vs->cmd_vqs[i]);
|
||||
virtio_queue_host_notifier_read(host_notifier);
|
||||
}
|
||||
}
|
||||
|
||||
/* Context: BQL held */
|
||||
@@ -154,11 +200,14 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
|
||||
smp_wmb(); /* paired with aio_notify_accept() */
|
||||
|
||||
if (s->bus.drain_count == 0) {
|
||||
virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
|
||||
virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
|
||||
virtio_queue_aio_attach_host_notifier(vs->ctrl_vq,
|
||||
s->vq_aio_context[0]);
|
||||
virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq,
|
||||
s->vq_aio_context[1]);
|
||||
|
||||
for (i = 0; i < vs->conf.num_queues; i++) {
|
||||
virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
|
||||
AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i];
|
||||
virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], ctx);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -207,7 +256,11 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
|
||||
s->dataplane_stopping = true;
|
||||
|
||||
if (s->bus.drain_count == 0) {
|
||||
aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
|
||||
for (i = 0; i < vs->conf.num_queues + VIRTIO_SCSI_VQ_NUM_FIXED; i++) {
|
||||
VirtQueue *vq = virtio_get_queue(&vs->parent_obj, i);
|
||||
AioContext *ctx = s->vq_aio_context[i];
|
||||
aio_wait_bh_oneshot(ctx, virtio_scsi_dataplane_stop_vq_bh, vq);
|
||||
}
|
||||
}
|
||||
|
||||
blk_drain_all(); /* ensure there are no in-flight requests */
|
||||
|
||||
+313
-229
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* IOThread Virtqueue Mapping
|
||||
*
|
||||
* Copyright Red Hat, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/iothread.h"
|
||||
#include "hw/virtio/iothread-vq-mapping.h"
|
||||
|
||||
static bool
|
||||
iothread_vq_mapping_validate(IOThreadVirtQueueMappingList *list, uint16_t
|
||||
num_queues, Error **errp)
|
||||
{
|
||||
g_autofree unsigned long *vqs = bitmap_new(num_queues);
|
||||
g_autoptr(GHashTable) iothreads =
|
||||
g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
for (IOThreadVirtQueueMappingList *node = list; node; node = node->next) {
|
||||
const char *name = node->value->iothread;
|
||||
uint16List *vq;
|
||||
|
||||
if (!iothread_by_id(name)) {
|
||||
error_setg(errp, "IOThread \"%s\" object does not exist", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_hash_table_add(iothreads, (gpointer)name)) {
|
||||
error_setg(errp,
|
||||
"duplicate IOThread name \"%s\" in iothread-vq-mapping",
|
||||
name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node != list) {
|
||||
if (!!node->value->vqs != !!list->value->vqs) {
|
||||
error_setg(errp, "either all items in iothread-vq-mapping "
|
||||
"must have vqs or none of them must have it");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (vq = node->value->vqs; vq; vq = vq->next) {
|
||||
if (vq->value >= num_queues) {
|
||||
error_setg(errp, "vq index %u for IOThread \"%s\" must be "
|
||||
"less than num_queues %u in iothread-vq-mapping",
|
||||
vq->value, name, num_queues);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(vq->value, vqs)) {
|
||||
error_setg(errp, "cannot assign vq %u to IOThread \"%s\" "
|
||||
"because it is already assigned", vq->value, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (list->value->vqs) {
|
||||
for (uint16_t i = 0; i < num_queues; i++) {
|
||||
if (!test_bit(i, vqs)) {
|
||||
error_setg(errp,
|
||||
"missing vq %u IOThread assignment in iothread-vq-mapping",
|
||||
i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool iothread_vq_mapping_apply(
|
||||
IOThreadVirtQueueMappingList *list,
|
||||
AioContext **vq_aio_context,
|
||||
uint16_t num_queues,
|
||||
Error **errp)
|
||||
{
|
||||
IOThreadVirtQueueMappingList *node;
|
||||
size_t num_iothreads = 0;
|
||||
size_t cur_iothread = 0;
|
||||
|
||||
if (!iothread_vq_mapping_validate(list, num_queues, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (node = list; node; node = node->next) {
|
||||
num_iothreads++;
|
||||
}
|
||||
|
||||
for (node = list; node; node = node->next) {
|
||||
IOThread *iothread = iothread_by_id(node->value->iothread);
|
||||
AioContext *ctx = iothread_get_aio_context(iothread);
|
||||
|
||||
/* Released in virtio_blk_vq_aio_context_cleanup() */
|
||||
object_ref(OBJECT(iothread));
|
||||
|
||||
if (node->value->vqs) {
|
||||
uint16List *vq;
|
||||
|
||||
/* Explicit vq:IOThread assignment */
|
||||
for (vq = node->value->vqs; vq; vq = vq->next) {
|
||||
assert(vq->value < num_queues);
|
||||
vq_aio_context[vq->value] = ctx;
|
||||
}
|
||||
} else {
|
||||
/* Round-robin vq:IOThread assignment */
|
||||
for (unsigned i = cur_iothread; i < num_queues;
|
||||
i += num_iothreads) {
|
||||
vq_aio_context[i] = ctx;
|
||||
}
|
||||
}
|
||||
|
||||
cur_iothread++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list)
|
||||
{
|
||||
IOThreadVirtQueueMappingList *node;
|
||||
|
||||
for (node = list; node; node = node->next) {
|
||||
IOThread *iothread = iothread_by_id(node->value->iothread);
|
||||
object_unref(OBJECT(iothread));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
system_virtio_ss = ss.source_set()
|
||||
system_virtio_ss.add(files('virtio-bus.c'))
|
||||
system_virtio_ss.add(files('iothread-vq-mapping.c'))
|
||||
system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('virtio-pci.c'))
|
||||
system_virtio_ss.add(when: 'CONFIG_VIRTIO_MMIO', if_true: files('virtio-mmio.c'))
|
||||
system_virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c'))
|
||||
|
||||
+4
-1
@@ -123,6 +123,10 @@ struct BHListSlice {
|
||||
|
||||
typedef QSLIST_HEAD(, AioHandler) AioHandlerSList;
|
||||
|
||||
typedef struct AioPolledEvent {
|
||||
int64_t ns; /* current polling time in nanoseconds */
|
||||
} AioPolledEvent;
|
||||
|
||||
struct AioContext {
|
||||
GSource source;
|
||||
|
||||
@@ -229,7 +233,6 @@ struct AioContext {
|
||||
int poll_disable_cnt;
|
||||
|
||||
/* Polling mode parameters */
|
||||
int64_t poll_ns; /* current polling time in nanoseconds */
|
||||
int64_t poll_max_ns; /* maximum polling time in nanoseconds */
|
||||
int64_t poll_grow; /* polling time growth factor */
|
||||
int64_t poll_shrink; /* polling time shrink factor */
|
||||
|
||||
+17
-2
@@ -17,6 +17,7 @@
|
||||
#define QEMU_RAW_AIO_H
|
||||
|
||||
#include "block/aio.h"
|
||||
#include "block/block-common.h"
|
||||
#include "qemu/iov.h"
|
||||
|
||||
/* AIO request types */
|
||||
@@ -58,11 +59,18 @@ void laio_cleanup(LinuxAioState *s);
|
||||
|
||||
/* laio_co_submit: submit I/O requests in the thread's current AioContext. */
|
||||
int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
|
||||
int type, uint64_t dev_max_batch);
|
||||
int type, BdrvRequestFlags flags,
|
||||
uint64_t dev_max_batch);
|
||||
|
||||
bool laio_has_fdsync(int);
|
||||
bool laio_has_fua(void);
|
||||
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context);
|
||||
void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context);
|
||||
#else
|
||||
static inline bool laio_has_fua(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
/* io_uring.c - Linux io_uring implementation */
|
||||
#ifdef CONFIG_LINUX_IO_URING
|
||||
@@ -71,9 +79,16 @@ void luring_cleanup(LuringState *s);
|
||||
|
||||
/* luring_co_submit: submit I/O requests in the thread's current AioContext. */
|
||||
int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset,
|
||||
QEMUIOVector *qiov, int type);
|
||||
QEMUIOVector *qiov, int type,
|
||||
BdrvRequestFlags flags);
|
||||
void luring_detach_aio_context(LuringState *s, AioContext *old_context);
|
||||
void luring_attach_aio_context(LuringState *s, AioContext *new_context);
|
||||
bool luring_has_fua(void);
|
||||
#else
|
||||
static inline bool luring_has_fua(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -24,6 +24,7 @@ struct SCSIRequest {
|
||||
SCSIBus *bus;
|
||||
SCSIDevice *dev;
|
||||
const SCSIReqOps *ops;
|
||||
AioContext *ctx;
|
||||
uint32_t refcount;
|
||||
uint32_t tag;
|
||||
uint32_t lun;
|
||||
@@ -48,6 +49,8 @@ struct SCSIRequest {
|
||||
bool dma_started;
|
||||
BlockAIOCB *aiocb;
|
||||
QEMUSGList *sg;
|
||||
|
||||
/* Protected by SCSIDevice->requests_lock */
|
||||
QTAILQ_ENTRY(SCSIRequest) next;
|
||||
};
|
||||
|
||||
@@ -76,10 +79,7 @@ struct SCSIDevice
|
||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||
uint32_t sense_len;
|
||||
|
||||
/*
|
||||
* The requests list is only accessed from the AioContext that executes
|
||||
* requests or from the main loop when IOThread processing is stopped.
|
||||
*/
|
||||
QemuMutex requests_lock; /* protects the requests list */
|
||||
QTAILQ_HEAD(, SCSIRequest) requests;
|
||||
|
||||
uint32_t channel;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* IOThread Virtqueue Mapping
|
||||
*
|
||||
* Copyright Red Hat, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef HW_VIRTIO_IOTHREAD_VQ_MAPPING_H
|
||||
#define HW_VIRTIO_IOTHREAD_VQ_MAPPING_H
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-types-virtio.h"
|
||||
|
||||
/**
|
||||
* iothread_vq_mapping_apply:
|
||||
* @list: The mapping of virtqueues to IOThreads.
|
||||
* @vq_aio_context: The array of AioContext pointers to fill in.
|
||||
* @num_queues: The length of @vq_aio_context.
|
||||
* @errp: If an error occurs, a pointer to the area to store the error.
|
||||
*
|
||||
* Fill in the AioContext for each virtqueue in the @vq_aio_context array given
|
||||
* the iothread-vq-mapping parameter in @list.
|
||||
*
|
||||
* iothread_vq_mapping_cleanup() must be called to free IOThread object
|
||||
* references after this function returns success.
|
||||
*
|
||||
* Returns: %true on success, %false on failure.
|
||||
**/
|
||||
bool iothread_vq_mapping_apply(
|
||||
IOThreadVirtQueueMappingList *list,
|
||||
AioContext **vq_aio_context,
|
||||
uint16_t num_queues,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
* iothread_vq_mapping_cleanup:
|
||||
* @list: The mapping of virtqueues to IOThreads.
|
||||
*
|
||||
* Release IOThread object references that were acquired by
|
||||
* iothread_vq_mapping_apply().
|
||||
*/
|
||||
void iothread_vq_mapping_cleanup(IOThreadVirtQueueMappingList *list);
|
||||
|
||||
#endif /* HW_VIRTIO_IOTHREAD_VQ_MAPPING_H */
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/scsi/scsi.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "qapi/qapi-types-virtio.h"
|
||||
#include "system/iothread.h"
|
||||
|
||||
#define TYPE_VIRTIO_SCSI_COMMON "virtio-scsi-common"
|
||||
@@ -60,6 +61,7 @@ struct VirtIOSCSIConf {
|
||||
CharBackend chardev;
|
||||
uint32_t boot_tpgt;
|
||||
IOThread *iothread;
|
||||
IOThreadVirtQueueMappingList *iothread_vq_mapping_list;
|
||||
};
|
||||
|
||||
struct VirtIOSCSI;
|
||||
@@ -82,18 +84,14 @@ struct VirtIOSCSI {
|
||||
|
||||
SCSIBus bus;
|
||||
int resetting; /* written from main loop thread, read from any thread */
|
||||
|
||||
QemuMutex event_lock; /* protects event_vq and events_dropped */
|
||||
bool events_dropped;
|
||||
|
||||
/*
|
||||
* TMFs deferred to main loop BH. These fields are protected by
|
||||
* tmf_bh_lock.
|
||||
*/
|
||||
QemuMutex tmf_bh_lock;
|
||||
QEMUBH *tmf_bh;
|
||||
QTAILQ_HEAD(, VirtIOSCSIReq) tmf_bh_list;
|
||||
QemuMutex ctrl_lock; /* protects ctrl_vq */
|
||||
|
||||
/* Fields for dataplane below */
|
||||
AioContext *ctx; /* one iothread per virtio-scsi-pci for now */
|
||||
AioContext **vq_aio_context; /* per-virtqueue AioContext pointer */
|
||||
|
||||
bool dataplane_started;
|
||||
bool dataplane_starting;
|
||||
@@ -111,6 +109,7 @@ void virtio_scsi_common_realize(DeviceState *dev,
|
||||
void virtio_scsi_common_unrealize(DeviceState *dev);
|
||||
|
||||
void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp);
|
||||
void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s);
|
||||
int virtio_scsi_dataplane_start(VirtIODevice *s);
|
||||
void virtio_scsi_dataplane_stop(VirtIODevice *s);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user