Skip to content

Commit 1cf18cc

Browse files
stefanhaRHkevmw
authored andcommitted
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 <[email protected]> Reviewed-by: Kevin Wolf <[email protected]> Message-ID: <[email protected]> Tested-by: Peter Krempa <[email protected]> Signed-off-by: Kevin Wolf <[email protected]>
1 parent 7eecba3 commit 1cf18cc

File tree

2 files changed

+88
-39
lines changed

2 files changed

+88
-39
lines changed

hw/scsi/scsi-bus.c

Lines changed: 85 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,15 @@ static void scsi_device_for_each_req_sync(SCSIDevice *s,
100100
assert(!runstate_is_running());
101101
assert(qemu_in_main_thread());
102102

103-
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) {
104-
fn(req, opaque);
103+
/*
104+
* Locking is not necessary because the guest is stopped and no other
105+
* threads can be accessing the requests list, but take the lock for
106+
* consistency.
107+
*/
108+
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
109+
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next_req) {
110+
fn(req, opaque);
111+
}
105112
}
106113
}
107114

@@ -115,21 +122,29 @@ static void scsi_device_for_each_req_async_bh(void *opaque)
115122
{
116123
g_autofree SCSIDeviceForEachReqAsyncData *data = opaque;
117124
SCSIDevice *s = data->s;
118-
AioContext *ctx;
119-
SCSIRequest *req;
120-
SCSIRequest *next;
125+
g_autoptr(GList) reqs = NULL;
121126

122127
/*
123-
* The BB cannot have changed contexts between this BH being scheduled and
124-
* now: BBs' AioContexts, when they have a node attached, can only be
125-
* changed via bdrv_try_change_aio_context(), in a drained section. While
126-
* we have the in-flight counter incremented, that drain must block.
128+
* Build a list of requests in this AioContext so fn() can be invoked later
129+
* outside requests_lock.
127130
*/
128-
ctx = blk_get_aio_context(s->conf.blk);
129-
assert(ctx == qemu_get_current_aio_context());
131+
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
132+
AioContext *ctx = qemu_get_current_aio_context();
133+
SCSIRequest *req;
134+
SCSIRequest *next;
135+
136+
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
137+
if (req->ctx == ctx) {
138+
scsi_req_ref(req); /* dropped after calling fn() */
139+
reqs = g_list_prepend(reqs, req);
140+
}
141+
}
142+
}
130143

131-
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
132-
data->fn(req, data->fn_opaque);
144+
/* Call fn() on each request */
145+
for (GList *elem = g_list_first(reqs); elem; elem = g_list_next(elem)) {
146+
data->fn(elem->data, data->fn_opaque);
147+
scsi_req_unref(elem->data);
133148
}
134149

135150
/* 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)
139154
blk_dec_in_flight(s->conf.blk);
140155
}
141156

157+
static void scsi_device_for_each_req_async_do_ctx(gpointer key, gpointer value,
158+
gpointer user_data)
159+
{
160+
AioContext *ctx = key;
161+
SCSIDeviceForEachReqAsyncData *params = user_data;
162+
SCSIDeviceForEachReqAsyncData *data;
163+
164+
data = g_new(SCSIDeviceForEachReqAsyncData, 1);
165+
data->s = params->s;
166+
data->fn = params->fn;
167+
data->fn_opaque = params->fn_opaque;
168+
169+
/*
170+
* Hold a reference to the SCSIDevice until
171+
* scsi_device_for_each_req_async_bh() finishes.
172+
*/
173+
object_ref(OBJECT(data->s));
174+
175+
/* Paired with scsi_device_for_each_req_async_bh() */
176+
blk_inc_in_flight(data->s->conf.blk);
177+
178+
aio_bh_schedule_oneshot(ctx, scsi_device_for_each_req_async_bh, data);
179+
}
180+
142181
/*
143182
* Schedule @fn() to be invoked for each enqueued request in device @s. @fn()
144-
* runs in the AioContext that is executing the request.
183+
* must be thread-safe because it runs concurrently in each AioContext that is
184+
* executing a request.
185+
*
145186
* Keeps the BlockBackend's in-flight counter incremented until everything is
146187
* done, so draining it will settle all scheduled @fn() calls.
147188
*/
@@ -151,24 +192,26 @@ static void scsi_device_for_each_req_async(SCSIDevice *s,
151192
{
152193
assert(qemu_in_main_thread());
153194

154-
SCSIDeviceForEachReqAsyncData *data =
155-
g_new(SCSIDeviceForEachReqAsyncData, 1);
156-
157-
data->s = s;
158-
data->fn = fn;
159-
data->fn_opaque = opaque;
160-
161-
/*
162-
* Hold a reference to the SCSIDevice until
163-
* scsi_device_for_each_req_async_bh() finishes.
164-
*/
165-
object_ref(OBJECT(s));
195+
/* The set of AioContexts where the requests are being processed */
196+
g_autoptr(GHashTable) aio_contexts = g_hash_table_new(NULL, NULL);
197+
WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
198+
SCSIRequest *req;
199+
QTAILQ_FOREACH(req, &s->requests, next) {
200+
g_hash_table_add(aio_contexts, req->ctx);
201+
}
202+
}
166203

167-
/* Paired with blk_dec_in_flight() in scsi_device_for_each_req_async_bh() */
168-
blk_inc_in_flight(s->conf.blk);
169-
aio_bh_schedule_oneshot(blk_get_aio_context(s->conf.blk),
170-
scsi_device_for_each_req_async_bh,
171-
data);
204+
/* Schedule a BH for each AioContext */
205+
SCSIDeviceForEachReqAsyncData params = {
206+
.s = s,
207+
.fn = fn,
208+
.fn_opaque = opaque,
209+
};
210+
g_hash_table_foreach(
211+
aio_contexts,
212+
scsi_device_for_each_req_async_do_ctx,
213+
&params
214+
);
172215
}
173216

174217
static void scsi_device_realize(SCSIDevice *s, Error **errp)
@@ -349,6 +392,7 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
349392
dev->lun = lun;
350393
}
351394

395+
qemu_mutex_init(&dev->requests_lock);
352396
QTAILQ_INIT(&dev->requests);
353397
scsi_device_realize(dev, &local_err);
354398
if (local_err) {
@@ -369,6 +413,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev)
369413

370414
scsi_device_purge_requests(dev, SENSE_CODE(NO_SENSE));
371415

416+
qemu_mutex_destroy(&dev->requests_lock);
417+
372418
scsi_device_unrealize(dev);
373419

374420
blockdev_mark_auto_del(dev->conf.blk);
@@ -965,7 +1011,10 @@ static void scsi_req_enqueue_internal(SCSIRequest *req)
9651011
req->sg = NULL;
9661012
}
9671013
req->enqueued = true;
968-
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
1014+
1015+
WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) {
1016+
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
1017+
}
9691018
}
9701019

9711020
int32_t scsi_req_enqueue(SCSIRequest *req)
@@ -985,7 +1034,9 @@ static void scsi_req_dequeue(SCSIRequest *req)
9851034
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
9861035
req->retry = false;
9871036
if (req->enqueued) {
988-
QTAILQ_REMOVE(&req->dev->requests, req, next);
1037+
WITH_QEMU_LOCK_GUARD(&req->dev->requests_lock) {
1038+
QTAILQ_REMOVE(&req->dev->requests, req, next);
1039+
}
9891040
req->enqueued = false;
9901041
scsi_req_unref(req);
9911042
}
@@ -1962,8 +2013,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data)
19622013

19632014
static void scsi_dev_instance_init(Object *obj)
19642015
{
1965-
DeviceState *dev = DEVICE(obj);
1966-
SCSIDevice *s = SCSI_DEVICE(dev);
2016+
SCSIDevice *s = SCSI_DEVICE(obj);
19672017

19682018
device_add_bootindex_property(obj, &s->conf.bootindex,
19692019
"bootindex", NULL,

include/hw/scsi/scsi.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct SCSIRequest {
4949
bool dma_started;
5050
BlockAIOCB *aiocb;
5151
QEMUSGList *sg;
52+
53+
/* Protected by SCSIDevice->requests_lock */
5254
QTAILQ_ENTRY(SCSIRequest) next;
5355
};
5456

@@ -77,10 +79,7 @@ struct SCSIDevice
7779
uint8_t sense[SCSI_SENSE_BUF_SIZE];
7880
uint32_t sense_len;
7981

80-
/*
81-
* The requests list is only accessed from the AioContext that executes
82-
* requests or from the main loop when IOThread processing is stopped.
83-
*/
82+
QemuMutex requests_lock; /* protects the requests list */
8483
QTAILQ_HEAD(, SCSIRequest) requests;
8584

8685
uint32_t channel;

0 commit comments

Comments
 (0)