Commit 15680951 authored by Jens Axboe's avatar Jens Axboe

[PATCH] fix SCSI non-sector bio backed IO

This fixes the SCSI layer to handle non-sector-aligned requests from
SG_IO (and potentially anything else producing these requests) that
could stall the machine and cause all sorts of funnies depending on the
low level driver used.
parent 8dfb4dc9
......@@ -493,7 +493,7 @@ void scsi_run_host_queues(struct Scsi_Host *shost)
* at some point during this call.
*/
static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate,
int sectors, int requeue)
int bytes, int requeue)
{
request_queue_t *q = cmd->device->request_queue;
struct request *req = cmd->request;
......@@ -503,12 +503,15 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate,
* If there are blocks left over at the end, set up the command
* to queue the remainder of them.
*/
if (end_that_request_first(req, uptodate, sectors)) {
int leftover = req->hard_nr_sectors - sectors;
if (end_that_request_chunk(req, uptodate, bytes)) {
int leftover = (req->hard_nr_sectors << 9) - bytes;
if (blk_pc_request(req))
leftover = req->data_len - bytes;
/* kill remainder if no retrys */
if (!uptodate && blk_noretry_request(req))
end_that_request_first(req, 0, leftover);
end_that_request_chunk(req, 0, leftover);
else {
if (requeue)
/*
......@@ -649,11 +652,11 @@ static void scsi_release_buffers(struct scsi_cmnd *cmd)
* b) We can just use scsi_requeue_command() here. This would
* be used if we just wanted to retry, for example.
*/
void scsi_io_completion(struct scsi_cmnd *cmd, int good_sectors,
int block_sectors)
void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
unsigned int block_bytes)
{
int result = cmd->result;
int this_count = cmd->bufflen >> 9;
int this_count = cmd->bufflen;
request_queue_t *q = cmd->device->request_queue;
struct request *req = cmd->request;
int clear_errors = 1;
......@@ -705,9 +708,9 @@ void scsi_io_completion(struct scsi_cmnd *cmd, int good_sectors,
* Next deal with any sectors which we were able to correctly
* handle.
*/
if (good_sectors >= 0) {
SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d sectors done.\n",
req->nr_sectors, good_sectors));
if (good_bytes >= 0) {
SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d bytes done.\n",
req->nr_sectors, good_bytes));
SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg));
if (clear_errors)
......@@ -717,13 +720,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, int good_sectors,
* they will have been finished off by the first command.
* If not, then we have a multi-buffer command.
*
* If block_sectors != 0, it means we had a medium error
* If block_bytes != 0, it means we had a medium error
* of some sort, and that we want to mark some number of
* sectors as not uptodate. Thus we want to inhibit
* requeueing right here - we will requeue down below
* when we handle the bad sectors.
*/
cmd = scsi_end_request(cmd, 1, good_sectors, result == 0);
cmd = scsi_end_request(cmd, 1, good_bytes, result == 0);
/*
* If the command completed without error, then either finish off the
......@@ -808,7 +811,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, int good_sectors,
(int) cmd->device->id, (int) cmd->device->lun);
print_command(cmd->data_cmnd);
print_sense("", cmd);
cmd = scsi_end_request(cmd, 0, block_sectors, 1);
cmd = scsi_end_request(cmd, 0, block_bytes, 1);
return;
default:
break;
......@@ -837,8 +840,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, int good_sectors,
* We sometimes get this cruft in the event that a medium error
* isn't properly reported.
*/
cmd = scsi_end_request(cmd, 0, req->current_nr_sectors, 1);
return;
block_bytes = req->hard_cur_sectors << 9;
if (!block_bytes)
block_bytes = req->data_len;
cmd = scsi_end_request(cmd, 0, block_bytes, 1);
}
}
......
......@@ -661,8 +661,8 @@ static struct block_device_operations sd_fops = {
static void sd_rw_intr(struct scsi_cmnd * SCpnt)
{
int result = SCpnt->result;
int this_count = SCpnt->bufflen >> 9;
int good_sectors = (result == 0 ? this_count : 0);
int this_count = SCpnt->bufflen;
int good_bytes = (result == 0 ? this_count : 0);
sector_t block_sectors = 1;
sector_t error_sector;
#ifdef CONFIG_SCSI_LOGGING
......@@ -688,6 +688,8 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt)
case MEDIUM_ERROR:
if (!(SCpnt->sense_buffer[0] & 0x80))
break;
if (!blk_fs_request(SCpnt->request))
break;
error_sector = (SCpnt->sense_buffer[3] << 24) |
(SCpnt->sense_buffer[4] << 16) |
(SCpnt->sense_buffer[5] << 8) |
......@@ -718,9 +720,9 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt)
}
error_sector &= ~(block_sectors - 1);
good_sectors = error_sector - SCpnt->request->sector;
if (good_sectors < 0 || good_sectors >= this_count)
good_sectors = 0;
good_bytes = (error_sector - SCpnt->request->sector) << 9;
if (good_bytes < 0 || good_bytes >= this_count)
good_bytes = 0;
break;
case RECOVERED_ERROR:
......@@ -732,7 +734,7 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt)
print_sense("sd", SCpnt);
SCpnt->result = 0;
SCpnt->sense_buffer[0] = 0x0;
good_sectors = this_count;
good_bytes = this_count;
break;
case ILLEGAL_REQUEST:
......@@ -755,7 +757,7 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt)
* how many actual sectors finished, and how many sectors we need
* to say have failed.
*/
scsi_io_completion(SCpnt, good_sectors, block_sectors);
scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
}
static int media_not_present(struct scsi_disk *sdkp, struct scsi_request *srp)
......
......@@ -179,14 +179,14 @@ static inline struct scsi_cd *scsi_cd(struct gendisk *disk)
static void rw_intr(struct scsi_cmnd * SCpnt)
{
int result = SCpnt->result;
int this_count = SCpnt->bufflen >> 9;
int good_sectors = (result == 0 ? this_count : 0);
int this_count = SCpnt->bufflen;
int good_bytes = (result == 0 ? this_count : 0);
int block_sectors = 0;
long error_sector;
struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
#ifdef DEBUG
printk("sr.c done: %x %p\n", result, SCpnt->request->bh->b_data);
printk("sr.c done: %x\n", result);
#endif
/*
......@@ -203,6 +203,8 @@ static void rw_intr(struct scsi_cmnd * SCpnt)
case ILLEGAL_REQUEST:
if (!(SCpnt->sense_buffer[0] & 0x90))
break;
if (!blk_fs_request(SCpnt->request))
break;
error_sector = (SCpnt->sense_buffer[3] << 24) |
(SCpnt->sense_buffer[4] << 16) |
(SCpnt->sense_buffer[5] << 8) |
......@@ -215,9 +217,9 @@ static void rw_intr(struct scsi_cmnd * SCpnt)
if (cd->device->sector_size == 2048)
error_sector <<= 2;
error_sector &= ~(block_sectors - 1);
good_sectors = error_sector - SCpnt->request->sector;
if (good_sectors < 0 || good_sectors >= this_count)
good_sectors = 0;
good_bytes = (error_sector - SCpnt->request->sector) << 9;
if (good_bytes < 0 || good_bytes >= this_count)
good_bytes = 0;
/*
* The SCSI specification allows for the value
* returned by READ CAPACITY to be up to 75 2K
......@@ -241,7 +243,7 @@ static void rw_intr(struct scsi_cmnd * SCpnt)
print_sense("sr", SCpnt);
SCpnt->result = 0;
SCpnt->sense_buffer[0] = 0x0;
good_sectors = this_count;
good_bytes = this_count;
break;
default:
......@@ -254,7 +256,7 @@ static void rw_intr(struct scsi_cmnd * SCpnt)
* how many actual sectors finished, and how many sectors we need
* to say have failed.
*/
scsi_io_completion(SCpnt, good_sectors, block_sectors);
scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
}
static int sr_init_command(struct scsi_cmnd * SCpnt)
......
......@@ -4005,7 +4005,7 @@ static int st_remove(struct device *dev)
static void st_intr(struct scsi_cmnd *SCpnt)
{
scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen >> 9), 1);
scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen), 1);
}
/*
......
......@@ -158,6 +158,6 @@ struct scsi_cmnd {
extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, int);
extern void scsi_put_command(struct scsi_cmnd *);
extern void scsi_io_completion(struct scsi_cmnd *, int, int);
extern void scsi_io_completion(struct scsi_cmnd *, unsigned int, unsigned int);
#endif /* _SCSI_SCSI_CMND_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment