From: Yingjie Wang The block-device SG_IO path copies sense and READ bounce data to userspace while the completed request still owns its queue tag. Either copy can fault. If the fault reads from the same one-tag device, the nested I/O cannot obtain a tag and the ioctl remains stuck in uninterruptible sleep. Snapshot all request-backed status, residual, and sense data, then free the request before either userspace copy. Preserve the existing error precedence between sense copy and data copyback. A same-device INQUIRY on a one-tag /dev/sr0 queue hung beyond 10 seconds before this change, with sg_io -> blk_rq_unmap_user -> filemap_fault in the stack. The fixed case completed without a stuck task or hung-task warning. Fixes: 31151ba2cef1 ("fix mismerge in ll_rw_blk.c") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Yingjie Wang --- drivers/scsi/scsi_ioctl.c | 45 +++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index c14f81403a09..0d1f8eb22dfb 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -388,41 +388,38 @@ static int scsi_fill_sghdr_rq(struct scsi_device *sdev, struct request *rq, return 0; } -static int scsi_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr, - struct bio *bio) +static int scsi_complete_sghdr_rq(struct sg_io_hdr *hdr, int result, + unsigned int resid_len, + const u8 *sense_buffer, + unsigned int sense_len) { - struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); - int r, ret = 0; + int ret = 0; /* * fill in all the output members */ - hdr->status = scmd->result & 0xff; - hdr->masked_status = sg_status_byte(scmd->result); + hdr->status = result & 0xff; + hdr->masked_status = sg_status_byte(result); hdr->msg_status = COMMAND_COMPLETE; - hdr->host_status = host_byte(scmd->result); + hdr->host_status = host_byte(result); hdr->driver_status = 0; if (scsi_status_is_check_condition(hdr->status)) hdr->driver_status = DRIVER_SENSE; hdr->info = 0; if (hdr->masked_status || hdr->host_status || hdr->driver_status) hdr->info |= SG_INFO_CHECK; - hdr->resid = scmd->resid_len; + hdr->resid = resid_len; hdr->sb_len_wr = 0; - if (scmd->sense_len && hdr->sbp) { - int len = min((unsigned int) hdr->mx_sb_len, scmd->sense_len); + if (sense_len && hdr->sbp) { + int len = min_t(unsigned int, hdr->mx_sb_len, sense_len); - if (!copy_to_user(hdr->sbp, scmd->sense_buffer, len)) + if (!copy_to_user(hdr->sbp, sense_buffer, len)) hdr->sb_len_wr = len; else ret = -EFAULT; } - r = blk_rq_unmap_user(bio); - if (!ret) - ret = r; - return ret; } @@ -436,6 +433,9 @@ static int sg_io(struct scsi_device *sdev, struct sg_io_hdr *hdr, struct request *rq; struct scsi_cmnd *scmd; struct bio *bio; + u8 sense_buffer[SCSI_SENSE_BUFFERSIZE]; + unsigned int sense_len, resid_len; + int result, unmap_ret; if (hdr->interface_id != 'S') return -EINVAL; @@ -487,7 +487,20 @@ static int sg_io(struct scsi_device *sdev, struct sg_io_hdr *hdr, hdr->duration = jiffies_to_msecs(jiffies - start_time); - ret = scsi_complete_sghdr_rq(rq, hdr, bio); + result = scmd->result; + resid_len = scmd->resid_len; + sense_len = min_t(unsigned int, scmd->sense_len, + sizeof(sense_buffer)); + if (sense_len) + memcpy(sense_buffer, scmd->sense_buffer, sense_len); + + blk_mq_free_request(rq); + ret = scsi_complete_sghdr_rq(hdr, result, resid_len, sense_buffer, + sense_len); + unmap_ret = blk_rq_unmap_user(bio); + if (!ret) + ret = unmap_ret; + return ret; out_put_request: blk_mq_free_request(rq); -- 2.43.0