scsi: storvsc: Fix handling of srb_status and capacity change events

Current handling of the srb_status is incorrect. Commit 52e1b3b3da
("scsi: storvsc: Correctly handle multiple flags in srb_status")
is based on srb_status being a set of flags, when in fact only the
2 high order bits are flags and the remaining 6 bits are an integer
status. Because the integer values of interest mostly look like flags,
the code actually works when treated that way.

But in the interest of correctness going forward, fix this by treating
the low 6 bits of srb_status as an integer status code. Add handling
for SRB_STATUS_INVALID_REQUEST, which was the original intent of commit
52e1b3b3da. Furthermore, treat the ERROR, ABORTED, and INVALID_REQUEST
srb status codes as essentially equivalent for the cases we care about.
There's no harm in doing so, and it isn't always clear which status code
current or older versions of Hyper-V report for particular conditions.

Treating the srb status codes as equivalent has the additional benefit
of ensuring that capacity change events result in an immediate rescan
so that the new size is known to Linux. Existing code checks SCSI
sense data for capacity change events when the srb status is ABORTED.
But capacity change events are also being observed when Hyper-V reports
the srb status as ERROR. Without the immediate rescan, the new size
isn't known until something else causes a rescan (such as running
fdisk to expand a partition), and in the meantime, tools such as "lsblk"
continue to report the old size.

Fixes: 52e1b3b3da ("scsi: storvsc: Correctly handle multiple flags in srb_status")
Reported-by: Juan Tian <juantian@microsoft.com>
Signed-off-by: Michael Kelley <mikelley@microsoft.com>
Link: https://lore.kernel.org/r/1668019722-1983-1-git-send-email-mikelley@microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>
This commit is contained in:
Michael Kelley 2022-11-09 10:48:42 -08:00 committed by Wei Liu
parent ee68154163
commit b8a5376c32

View File

@ -303,16 +303,21 @@ enum storvsc_request_type {
};
/*
* SRB status codes and masks; a subset of the codes used here.
* SRB status codes and masks. In the 8-bit field, the two high order bits
* are flags, while the remaining 6 bits are an integer status code. The
* definitions here include only the subset of the integer status codes that
* are tested for in this driver.
*/
#define SRB_STATUS_AUTOSENSE_VALID 0x80
#define SRB_STATUS_QUEUE_FROZEN 0x40
#define SRB_STATUS_INVALID_LUN 0x20
#define SRB_STATUS_SUCCESS 0x01
#define SRB_STATUS_ABORTED 0x02
#define SRB_STATUS_ERROR 0x04
#define SRB_STATUS_DATA_OVERRUN 0x12
/* SRB status integer codes */
#define SRB_STATUS_SUCCESS 0x01
#define SRB_STATUS_ABORTED 0x02
#define SRB_STATUS_ERROR 0x04
#define SRB_STATUS_INVALID_REQUEST 0x06
#define SRB_STATUS_DATA_OVERRUN 0x12
#define SRB_STATUS_INVALID_LUN 0x20
#define SRB_STATUS(status) \
(status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN))
@ -969,38 +974,25 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
void (*process_err_fn)(struct work_struct *work);
struct hv_host_device *host_dev = shost_priv(host);
/*
* In some situations, Hyper-V sets multiple bits in the
* srb_status, such as ABORTED and ERROR. So process them
* individually, with the most specific bits first.
*/
switch (SRB_STATUS(vm_srb->srb_status)) {
case SRB_STATUS_ERROR:
case SRB_STATUS_ABORTED:
case SRB_STATUS_INVALID_REQUEST:
if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID) {
/* Check for capacity change */
if ((asc == 0x2a) && (ascq == 0x9)) {
process_err_fn = storvsc_device_scan;
/* Retry the I/O that triggered this. */
set_host_byte(scmnd, DID_REQUEUE);
goto do_work;
}
if (vm_srb->srb_status & SRB_STATUS_INVALID_LUN) {
set_host_byte(scmnd, DID_NO_CONNECT);
process_err_fn = storvsc_remove_lun;
goto do_work;
}
if (vm_srb->srb_status & SRB_STATUS_ABORTED) {
if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID &&
/* Capacity data has changed */
(asc == 0x2a) && (ascq == 0x9)) {
process_err_fn = storvsc_device_scan;
/*
* Retry the I/O that triggered this.
* Otherwise, let upper layer deal with the
* error when sense message is present
*/
set_host_byte(scmnd, DID_REQUEUE);
goto do_work;
}
}
if (vm_srb->srb_status & SRB_STATUS_ERROR) {
/*
* Let upper layer deal with error when
* sense message is present.
*/
if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID)
return;
}
/*
* If there is an error; offline the device since all
@ -1023,6 +1015,13 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
default:
set_host_byte(scmnd, DID_ERROR);
}
return;
case SRB_STATUS_INVALID_LUN:
set_host_byte(scmnd, DID_NO_CONNECT);
process_err_fn = storvsc_remove_lun;
goto do_work;
}
return;