max_sectors in struct Scsi_Host specifies maximum number of sectors allowed in a single SCSI command. The data type of max_sectors is unsigned short, so the maximum transfer length per SCSI command is limited to less than 256MB in 4096-bytes sector size. (0xffff * 4096)
This commit increases the SCSI mid level's limitation for max_sectors upto the block layer's limitation for max_hw_sectors by extending the data type of max_sectors in struct Scsi_Host and scsi_host_template, so that SCSI lower level drivers can specify more than 0xffff. This change requires the scsi disk (sd) driver to handle the requests whose transfer length is more than 0xffff with READ_16 or WRITE_16. Also, this needs to prevent SG_GET_RESERVED_SIZE and SG_SET_RESERVED_SIZE ioctls for the scsi generic (sg) driver from integer overflow when converting max_sectors to bytes. Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com> Cc: Jens Axboe <ax...@kernel.dk> Cc: "James E.J. Bottomley" <jbottom...@parallels.com> Cc: Douglas Gilbert <dgilb...@interlog.com> Cc: linux-scsi@vger.kernel.org --- drivers/scsi/sd.c | 5 +---- drivers/scsi/sg.c | 17 +++++++++++++---- include/scsi/scsi_host.h | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index efcbcd1..4508115 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1056,7 +1056,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) SCpnt->cmnd[29] = (unsigned char) (this_count >> 16) & 0xff; SCpnt->cmnd[30] = (unsigned char) (this_count >> 8) & 0xff; SCpnt->cmnd[31] = (unsigned char) this_count & 0xff; - } else if (sdp->use_16_for_rw) { + } else if ((this_count > 0xffff) || sdp->use_16_for_rw) { SCpnt->cmnd[0] += READ_16 - READ_6; SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; @@ -1075,9 +1075,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) } else if ((this_count > 0xff) || (block > 0x1fffff) || scsi_device_protection(SCpnt->device) || SCpnt->device->use_10_for_rw) { - if (this_count > 0xffff) - this_count = 0xffff; - SCpnt->cmnd[0] += READ_10 - READ_6; SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0); SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index df5e961..e3404d2 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -806,6 +806,15 @@ static int srp_done(Sg_fd *sfp, Sg_request *srp) return ret; } +static int max_sectors_bytes(struct request_queue *q) +{ + unsigned int max_sectors = queue_max_sectors(q); + + max_sectors = min_t(unsigned int, max_sectors, INT_MAX >> 9); + + return max_sectors << 9; +} + static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { @@ -945,7 +954,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) if (val < 0) return -EINVAL; val = min_t(int, val, - queue_max_sectors(sdp->device->request_queue) * 512); + max_sectors_bytes(sdp->device->request_queue)); if (val != sfp->reserve.bufflen) { if (sg_res_in_use(sfp) || sfp->mmap_called) return -EBUSY; @@ -955,7 +964,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return 0; case SG_GET_RESERVED_SIZE: val = min_t(int, sfp->reserve.bufflen, - queue_max_sectors(sdp->device->request_queue) * 512); + max_sectors_bytes(sdp->device->request_queue)); return put_user(val, ip); case SG_SET_COMMAND_Q: result = get_user(val, ip); @@ -1095,7 +1104,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) return -ENODEV; return scsi_ioctl(sdp->device, cmd_in, p); case BLKSECTGET: - return put_user(queue_max_sectors(sdp->device->request_queue) * 512, + return put_user(max_sectors_bytes(sdp->device->request_queue), ip); case BLKTRACESETUP: return blk_trace_setup(sdp->device->request_queue, @@ -2086,7 +2095,7 @@ sg_add_sfp(Sg_device * sdp, int dev) sg_big_buff = def_reserved_size; bufflen = min_t(int, sg_big_buff, - queue_max_sectors(sdp->device->request_queue) * 512); + max_sectors_bytes(sdp->device->request_queue)); sg_build_reserve(sfp, bufflen); SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n", sfp->reserve.bufflen, sfp->reserve.k_use_sg)); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 94844fc..db7d8bd 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -408,7 +408,7 @@ struct scsi_host_template { /* * Set this if the host adapter has limitations beside segment count. */ - unsigned short max_sectors; + unsigned int max_sectors; /* * DMA scatter gather segment boundary limit. A segment crossing this @@ -652,7 +652,7 @@ struct Scsi_Host { short cmd_per_lun; short unsigned int sg_tablesize; short unsigned int sg_prot_tablesize; - short unsigned int max_sectors; + unsigned int max_sectors; unsigned long dma_boundary; /* * Used to assign serial numbers to the cmds. -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html