Skip to content

Commit

Permalink
scsi: sd: Check for unaligned partial completion
Browse files Browse the repository at this point in the history
Commit <f2e767bb5d6e> ("mpt3sas: Force request partial completion
alignment") was not considering the case of commands not operating on
logical block size units (e.g. REQ_OP_ZONE_REPORT and its 64B aligned
partial replies). In this case, forcing alignment of resid to the device
logical block size can break the command result, e.g. in the case of
REQ_OP_ZONE_REPORT, the exact number of zone reported by the device.

Move the partial completion alignement check of mpt3sas to a generic
implementation in sd_done(). The check is added within the default
section of the initial req_op() switch case so that the report and reset
zone commands are ignored. In addition, as sd_done() is not called for
passthrough requests, resid corrections are not done as intended by the
initial mpt3sas patch.

Fixes: f2e767b ("mpt3sas: Force request partial completion alignment")
Cc: <stable@vger.kernel.org> # v4.10
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Acked-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Bart Van Assche <bart.vanassche@sandisk.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Damien Le Moal authored and Martin K. Petersen committed Mar 2, 2017
1 parent a4b0e8a commit c46f091
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 15 deletions.
15 changes: 0 additions & 15 deletions drivers/scsi/mpt3sas/mpt3sas_scsih.c
Original file line number Diff line number Diff line change
Expand Up @@ -4677,7 +4677,6 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
struct MPT3SAS_DEVICE *sas_device_priv_data;
u32 response_code = 0;
unsigned long flags;
unsigned int sector_sz;

mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply);

Expand Down Expand Up @@ -4742,20 +4741,6 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
}

xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);

/* In case of bogus fw or device, we could end up having
* unaligned partial completion. We can force alignment here,
* then scsi-ml does not need to handle this misbehavior.
*/
sector_sz = scmd->device->sector_size;
if (unlikely(!blk_rq_is_passthrough(scmd->request) && sector_sz &&
xfer_cnt % sector_sz)) {
sdev_printk(KERN_INFO, scmd->device,
"unaligned partial completion avoided (xfer_cnt=%u, sector_sz=%u)\n",
xfer_cnt, sector_sz);
xfer_cnt = round_down(xfer_cnt, sector_sz);
}

scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt);
if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE)
log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
Expand Down
17 changes: 17 additions & 0 deletions drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1783,6 +1783,8 @@ static int sd_done(struct scsi_cmnd *SCpnt)
{
int result = SCpnt->result;
unsigned int good_bytes = result ? 0 : scsi_bufflen(SCpnt);
unsigned int sector_size = SCpnt->device->sector_size;
unsigned int resid;
struct scsi_sense_hdr sshdr;
struct scsi_disk *sdkp = scsi_disk(SCpnt->request->rq_disk);
struct request *req = SCpnt->request;
Expand Down Expand Up @@ -1813,6 +1815,21 @@ static int sd_done(struct scsi_cmnd *SCpnt)
scsi_set_resid(SCpnt, blk_rq_bytes(req));
}
break;
default:
/*
* In case of bogus fw or device, we could end up having
* an unaligned partial completion. Check this here and force
* alignment.
*/
resid = scsi_get_resid(SCpnt);
if (resid & (sector_size - 1)) {
sd_printk(KERN_INFO, sdkp,
"Unaligned partial completion (resid=%u, sector_sz=%u)\n",
resid, sector_size);
resid = min(scsi_bufflen(SCpnt),
round_up(resid, sector_size));
scsi_set_resid(SCpnt, resid);
}
}

if (result) {
Expand Down

0 comments on commit c46f091

Please sign in to comment.