Skip to content

Commit

Permalink
scsi: fetch unit attention when creating the request
Browse files Browse the repository at this point in the history
Commit 1880ad4 ("virtio-scsi: Batched prepare for cmd reqs") split
calls to scsi_req_new() and scsi_req_enqueue() in the virtio-scsi device.
No ill effects were observed until commit 8cc5583 ("virtio-scsi: Send
"REPORTED LUNS CHANGED" sense data upon disk hotplug events") added a
unit attention that was easy to trigger with device hotplug and
hot-unplug.

Because the two calls were separated, all requests in the batch were
prepared calling scsi_req_new() to report a sense.  The first one
submitted would report the right sense and reset it to NO_SENSE, while
the others reported CHECK_CONDITION with no sense data.  This caused
SCSI errors in Linux.

To solve this issue, let's fetch the unit attention as early as possible
when we prepare the request, so that only the first request in the batch
will use the unit attention SCSIReqOps and the others will not report
CHECK CONDITION.

Fixes: 1880ad4 ("virtio-scsi: Batched prepare for cmd reqs")
Fixes: 8cc5583 ("virtio-scsi: Send "REPORTED LUNS CHANGED" sense data upon disk hotplug events")
Reported-by: Thomas Huth <thuth@redhat.com>
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2176702
Co-developed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Message-ID: <20230712134352.118655-2-sgarzare@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
stefano-garzarella authored and bonzini committed Jul 14, 2023
1 parent cc9ff56 commit 9472083
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
36 changes: 33 additions & 3 deletions hw/scsi/scsi-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,19 +412,35 @@ static const struct SCSIReqOps reqops_invalid_opcode = {

/* SCSIReqOps implementation for unit attention conditions. */

static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
static void scsi_fetch_unit_attention_sense(SCSIRequest *req)
{
SCSISense *ua = NULL;

if (req->dev->unit_attention.key == UNIT_ATTENTION) {
scsi_req_build_sense(req, req->dev->unit_attention);
ua = &req->dev->unit_attention;
} else if (req->bus->unit_attention.key == UNIT_ATTENTION) {
scsi_req_build_sense(req, req->bus->unit_attention);
ua = &req->bus->unit_attention;
}

/*
* Fetch the unit attention sense immediately so that another
* scsi_req_new does not use reqops_unit_attention.
*/
if (ua) {
scsi_req_build_sense(req, *ua);
*ua = SENSE_CODE(NO_SENSE);
}
}

static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
{
scsi_req_complete(req, CHECK_CONDITION);
return 0;
}

static const struct SCSIReqOps reqops_unit_attention = {
.size = sizeof(SCSIRequest),
.init_req = scsi_fetch_unit_attention_sense,
.send_command = scsi_unit_attention
};

Expand Down Expand Up @@ -699,6 +715,11 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
object_ref(OBJECT(d));
object_ref(OBJECT(qbus->parent));
notifier_list_init(&req->cancel_notifiers);

if (reqops->init_req) {
reqops->init_req(req);
}

trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
return req;
}
Expand Down Expand Up @@ -798,6 +819,15 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req)
static void scsi_clear_unit_attention(SCSIRequest *req)
{
SCSISense *ua;

/*
* scsi_fetch_unit_attention_sense() already cleaned the unit attention
* in this case.
*/
if (req->ops == &reqops_unit_attention) {
return;
}

if (req->dev->unit_attention.key != UNIT_ATTENTION &&
req->bus->unit_attention.key != UNIT_ATTENTION) {
return;
Expand Down
1 change: 1 addition & 0 deletions include/hw/scsi/scsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
/* scsi-bus.c */
struct SCSIReqOps {
size_t size;
void (*init_req)(SCSIRequest *req);
void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req);
Expand Down

0 comments on commit 9472083

Please sign in to comment.