Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / drivers / scsi / scsi_lib.c
index 9c2b99e..1344553 100644 (file)
@@ -950,7 +950,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
 
        /*
         * If there had been no error, but we have leftover bytes in the
-        * requeues just queue the command up again.
+        * request just queue the command up again.
         */
        if (likely(result == 0))
                scsi_io_completion_reprep(cmd, q);
@@ -1530,7 +1530,7 @@ static int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
 
        return rtn;
  done:
-       cmd->scsi_done(cmd);
+       scsi_done(cmd);
        return 0;
 }
 
@@ -1585,8 +1585,17 @@ static blk_status_t scsi_prepare_cmd(struct request *req)
        return scsi_cmd_to_driver(cmd)->init_command(cmd);
 }
 
-static void scsi_mq_done(struct scsi_cmnd *cmd)
+void scsi_done(struct scsi_cmnd *cmd)
 {
+       switch (cmd->submitter) {
+       case SUBMITTED_BY_BLOCK_LAYER:
+               break;
+       case SUBMITTED_BY_SCSI_ERROR_HANDLER:
+               return scsi_eh_done(cmd);
+       case SUBMITTED_BY_SCSI_RESET_IOCTL:
+               return;
+       }
+
        if (unlikely(blk_should_fake_timeout(scsi_cmd_to_rq(cmd)->q)))
                return;
        if (unlikely(test_and_set_bit(SCMD_STATE_COMPLETE, &cmd->state)))
@@ -1594,6 +1603,7 @@ static void scsi_mq_done(struct scsi_cmnd *cmd)
        trace_scsi_dispatch_cmd_done(cmd);
        blk_mq_complete_request(scsi_cmd_to_rq(cmd));
 }
+EXPORT_SYMBOL(scsi_done);
 
 static void scsi_mq_put_budget(struct request_queue *q, int budget_token)
 {
@@ -1693,7 +1703,7 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx,
 
        scsi_set_resid(cmd, 0);
        memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
-       cmd->scsi_done = scsi_mq_done;
+       cmd->submitter = SUBMITTED_BY_BLOCK_LAYER;
 
        blk_mq_start_request(req);
        reason = scsi_dispatch_cmd(cmd);
@@ -2042,8 +2052,15 @@ scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
        memset(cmd, 0, sizeof(cmd));
        cmd[1] = (pf ? 0x10 : 0) | (sp ? 0x01 : 0);
 
-       if (sdev->use_10_for_ms) {
-               if (len > 65535)
+       /*
+        * Use MODE SELECT(10) if the device asked for it or if the mode page
+        * and the mode select header cannot fit within the maximumm 255 bytes
+        * of the MODE SELECT(6) command.
+        */
+       if (sdev->use_10_for_ms ||
+           len + 4 > 255 ||
+           data->block_descriptor_length > 255) {
+               if (len > 65535 - 8)
                        return -EINVAL;
                real_buffer = kmalloc(8 + len, GFP_KERNEL);
                if (!real_buffer)
@@ -2056,15 +2073,13 @@ scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage,
                real_buffer[3] = data->device_specific;
                real_buffer[4] = data->longlba ? 0x01 : 0;
                real_buffer[5] = 0;
-               real_buffer[6] = data->block_descriptor_length >> 8;
-               real_buffer[7] = data->block_descriptor_length;
+               put_unaligned_be16(data->block_descriptor_length,
+                                  &real_buffer[6]);
 
                cmd[0] = MODE_SELECT_10;
-               cmd[7] = len >> 8;
-               cmd[8] = len;
+               put_unaligned_be16(len, &cmd[7]);
        } else {
-               if (len > 255 || data->block_descriptor_length > 255 ||
-                   data->longlba)
+               if (data->longlba)
                        return -EINVAL;
 
                real_buffer = kmalloc(4 + len, GFP_KERNEL);
@@ -2091,7 +2106,7 @@ EXPORT_SYMBOL_GPL(scsi_mode_select);
 /**
  *     scsi_mode_sense - issue a mode sense, falling back from 10 to six bytes if necessary.
  *     @sdev:  SCSI device to be queried
- *     @dbd:   set if mode sense will allow block descriptors to be returned
+ *     @dbd:   set to prevent mode sense from returning block descriptors
  *     @modepage: mode page being requested
  *     @buffer: request buffer (may not be smaller than eight bytes)
  *     @len:   length of request buffer.
@@ -2126,18 +2141,18 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                sshdr = &my_sshdr;
 
  retry:
-       use_10_for_ms = sdev->use_10_for_ms;
+       use_10_for_ms = sdev->use_10_for_ms || len > 255;
 
        if (use_10_for_ms) {
-               if (len < 8)
-                       len = 8;
+               if (len < 8 || len > 65535)
+                       return -EINVAL;
 
                cmd[0] = MODE_SENSE_10;
-               cmd[8] = len;
+               put_unaligned_be16(len, &cmd[7]);
                header_length = 8;
        } else {
                if (len < 4)
-                       len = 4;
+                       return -EINVAL;
 
                cmd[0] = MODE_SENSE;
                cmd[4] = len;
@@ -2161,9 +2176,15 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                        if ((sshdr->sense_key == ILLEGAL_REQUEST) &&
                            (sshdr->asc == 0x20) && (sshdr->ascq == 0)) {
                                /*
-                                * Invalid command operation code
+                                * Invalid command operation code: retry using
+                                * MODE SENSE(6) if this was a MODE SENSE(10)
+                                * request, except if the request mode page is
+                                * too large for MODE SENSE single byte
+                                * allocation length field.
                                 */
                                if (use_10_for_ms) {
+                                       if (len > 255)
+                                               return -EIO;
                                        sdev->use_10_for_ms = 0;
                                        goto retry;
                                }
@@ -2187,12 +2208,11 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
                data->longlba = 0;
                data->block_descriptor_length = 0;
        } else if (use_10_for_ms) {
-               data->length = buffer[0]*256 + buffer[1] + 2;
+               data->length = get_unaligned_be16(&buffer[0]) + 2;
                data->medium_type = buffer[2];
                data->device_specific = buffer[3];
                data->longlba = buffer[4] & 0x01;
-               data->block_descriptor_length = buffer[6]*256
-                       + buffer[7];
+               data->block_descriptor_length = get_unaligned_be16(&buffer[6]);
        } else {
                data->length = buffer[0] + 1;
                data->medium_type = buffer[1];