mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-18 23:52:14 -06:00
scsi: introduce requests_lock
SCSIDevice keeps track of in-flight requests for device reset and Task Management Functions (TMFs). The request list requires protection so that multi-threaded SCSI emulation can be implemented in commits that follow. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Message-ID: <20250311132616.1049687-5-stefanha@redhat.com> Tested-by: Peter Krempa <pkrempa@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
7eecba3778
commit
1cf18cc9bf
2 changed files with 88 additions and 39 deletions
|
@ -100,9 +100,16 @@ static void scsi_device_for_each_req_sync(SCSIDevice *s,
|
||||||
assert(!runstate_is_running());
|
assert(!runstate_is_running());
|
||||||
assert(qemu_in_main_thread());
|
assert(qemu_in_main_thread());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) {
|
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) {
|
||||||
fn(req, opaque);
|
fn(req, opaque);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -115,21 +122,29 @@ static void scsi_device_for_each_req_async_bh(void *opaque)
|
||||||
{
|
{
|
||||||
g_autofree SCSIDeviceForEachReqAsyncData *data = opaque;
|
g_autofree SCSIDeviceForEachReqAsyncData *data = opaque;
|
||||||
SCSIDevice *s = data->s;
|
SCSIDevice *s = data->s;
|
||||||
AioContext *ctx;
|
g_autoptr(GList) reqs = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a list of requests in this AioContext so fn() can be invoked later
|
||||||
|
* outside requests_lock.
|
||||||
|
*/
|
||||||
|
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
|
||||||
|
AioContext *ctx = qemu_get_current_aio_context();
|
||||||
SCSIRequest *req;
|
SCSIRequest *req;
|
||||||
SCSIRequest *next;
|
SCSIRequest *next;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
ctx = blk_get_aio_context(s->conf.blk);
|
|
||||||
assert(ctx == qemu_get_current_aio_context());
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
|
||||||
data->fn(req, data->fn_opaque);
|
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() */
|
/* 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);
|
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()
|
* 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
|
* Keeps the BlockBackend's in-flight counter incremented until everything is
|
||||||
* done, so draining it will settle all scheduled @fn() calls.
|
* 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());
|
assert(qemu_in_main_thread());
|
||||||
|
|
||||||
SCSIDeviceForEachReqAsyncData *data =
|
/* The set of AioContexts where the requests are being processed */
|
||||||
g_new(SCSIDeviceForEachReqAsyncData, 1);
|
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;
|
/* Schedule a BH for each AioContext */
|
||||||
data->fn = fn;
|
SCSIDeviceForEachReqAsyncData params = {
|
||||||
data->fn_opaque = opaque;
|
.s = s,
|
||||||
|
.fn = fn,
|
||||||
/*
|
.fn_opaque = opaque,
|
||||||
* Hold a reference to the SCSIDevice until
|
};
|
||||||
* scsi_device_for_each_req_async_bh() finishes.
|
g_hash_table_foreach(
|
||||||
*/
|
aio_contexts,
|
||||||
object_ref(OBJECT(s));
|
scsi_device_for_each_req_async_do_ctx,
|
||||||
|
¶ms
|
||||||
/* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scsi_device_realize(SCSIDevice *s, Error **errp)
|
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;
|
dev->lun = lun;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qemu_mutex_init(&dev->requests_lock);
|
||||||
QTAILQ_INIT(&dev->requests);
|
QTAILQ_INIT(&dev->requests);
|
||||||
scsi_device_realize(dev, &local_err);
|
scsi_device_realize(dev, &local_err);
|
||||||
if (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));
|
scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE));
|
||||||
|
|
||||||
|
qemu_mutex_destroy(&dev->requests_lock);
|
||||||
|
|
||||||
scsi_device_unrealize(dev);
|
scsi_device_unrealize(dev);
|
||||||
|
|
||||||
blockdev_mark_auto_del(dev->conf.blk);
|
blockdev_mark_auto_del(dev->conf.blk);
|
||||||
|
@ -965,7 +1011,10 @@ static void scsi_req_enqueue_internal(SCSIRequest *req)
|
||||||
req->sg = NULL;
|
req->sg = NULL;
|
||||||
}
|
}
|
||||||
req->enqueued = true;
|
req->enqueued = true;
|
||||||
|
|
||||||
|
WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) {
|
||||||
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
|
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t scsi_req_enqueue(SCSIRequest *req)
|
int32_t scsi_req_enqueue(SCSIRequest *req)
|
||||||
|
@ -985,7 +1034,9 @@ static void scsi_req_dequeue(SCSIRequest *req)
|
||||||
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
|
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
|
||||||
req->retry = false;
|
req->retry = false;
|
||||||
if (req->enqueued) {
|
if (req->enqueued) {
|
||||||
|
WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) {
|
||||||
QTAILQ_REMOVE(&req->dev->requests, req, next);
|
QTAILQ_REMOVE(&req->dev->requests, req, next);
|
||||||
|
}
|
||||||
req->enqueued = false;
|
req->enqueued = false;
|
||||||
scsi_req_unref(req);
|
scsi_req_unref(req);
|
||||||
}
|
}
|
||||||
|
@ -1962,8 +2013,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
|
||||||
|
|
||||||
static void scsi_dev_instance_init(Object *obj)
|
static void scsi_dev_instance_init(Object *obj)
|
||||||
{
|
{
|
||||||
DeviceState *dev = DEVICE(obj);
|
SCSIDevice *s = SCSI_DEVICE(obj);
|
||||||
SCSIDevice *s = SCSI_DEVICE(dev);
|
|
||||||
|
|
||||||
device_add_bootindex_property(obj, &s->conf.bootindex,
|
device_add_bootindex_property(obj, &s->conf.bootindex,
|
||||||
"bootindex", NULL,
|
"bootindex", NULL,
|
||||||
|
|
|
@ -49,6 +49,8 @@ struct SCSIRequest {
|
||||||
bool dma_started;
|
bool dma_started;
|
||||||
BlockAIOCB *aiocb;
|
BlockAIOCB *aiocb;
|
||||||
QEMUSGList *sg;
|
QEMUSGList *sg;
|
||||||
|
|
||||||
|
/* Protected by SCSIDevice->requests_lock */
|
||||||
QTAILQ_ENTRY(SCSIRequest) next;
|
QTAILQ_ENTRY(SCSIRequest) next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,10 +79,7 @@ struct SCSIDevice
|
||||||
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
uint8_t sense[SCSI_SENSE_BUF_SIZE];
|
||||||
uint32_t sense_len;
|
uint32_t sense_len;
|
||||||
|
|
||||||
/*
|
QemuMutex requests_lock; /* protects the requests list */
|
||||||
* The requests list is only accessed from the AioContext that executes
|
|
||||||
* requests or from the main loop when IOThread processing is stopped.
|
|
||||||
*/
|
|
||||||
QTAILQ_HEAD(, SCSIRequest) requests;
|
QTAILQ_HEAD(, SCSIRequest) requests;
|
||||||
|
|
||||||
uint32_t channel;
|
uint32_t channel;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue