[SCSI] aacraid: prohibit access to array container space
[safe/jmp/linux-2.6] / drivers / scsi / scsi_error.c
index ad6a137..f31d868 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <linux/gfp.h>
 #include <linux/timer.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
@@ -38,6 +39,8 @@
 #include "scsi_logging.h"
 #include "scsi_transport_api.h"
 
+#include <trace/events/scsi.h>
+
 #define SENSE_TIMEOUT          (10*HZ)
 
 /*
@@ -51,6 +54,7 @@
 void scsi_eh_wakeup(struct Scsi_Host *shost)
 {
        if (shost->host_busy == shost->host_failed) {
+               trace_scsi_eh_wakeup(shost);
                wake_up_process(shost->ehandler);
                SCSI_LOG_ERROR_RECOVERY(5,
                                printk("Waking error handler thread\n"));
@@ -126,6 +130,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
        struct scsi_cmnd *scmd = req->special;
        enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED;
 
+       trace_scsi_dispatch_cmd_timeout(scmd);
        scsi_log_completion(scmd, TIMEOUT_ERROR);
 
        if (scmd->device->host->transportt->eh_timed_out)
@@ -331,6 +336,64 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
        }
 }
 
+static void scsi_handle_queue_ramp_up(struct scsi_device *sdev)
+{
+       struct scsi_host_template *sht = sdev->host->hostt;
+       struct scsi_device *tmp_sdev;
+
+       if (!sht->change_queue_depth ||
+           sdev->queue_depth >= sdev->max_queue_depth)
+               return;
+
+       if (time_before(jiffies,
+           sdev->last_queue_ramp_up + sdev->queue_ramp_up_period))
+               return;
+
+       if (time_before(jiffies,
+           sdev->last_queue_full_time + sdev->queue_ramp_up_period))
+               return;
+
+       /*
+        * Walk all devices of a target and do
+        * ramp up on them.
+        */
+       shost_for_each_device(tmp_sdev, sdev->host) {
+               if (tmp_sdev->channel != sdev->channel ||
+                   tmp_sdev->id != sdev->id ||
+                   tmp_sdev->queue_depth == sdev->max_queue_depth)
+                       continue;
+               /*
+                * call back into LLD to increase queue_depth by one
+                * with ramp up reason code.
+                */
+               sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1,
+                                       SCSI_QDEPTH_RAMP_UP);
+               sdev->last_queue_ramp_up = jiffies;
+       }
+}
+
+static void scsi_handle_queue_full(struct scsi_device *sdev)
+{
+       struct scsi_host_template *sht = sdev->host->hostt;
+       struct scsi_device *tmp_sdev;
+
+       if (!sht->change_queue_depth)
+               return;
+
+       shost_for_each_device(tmp_sdev, sdev->host) {
+               if (tmp_sdev->channel != sdev->channel ||
+                   tmp_sdev->id != sdev->id)
+                       continue;
+               /*
+                * We do not know the number of commands that were at
+                * the device when we got the queue full so we start
+                * from the highest possible value and work our way down.
+                */
+               sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1,
+                                       SCSI_QDEPTH_QFULL);
+       }
+}
+
 /**
  * scsi_eh_completed_normally - Disposition a eh cmd on return from LLD.
  * @scmd:      SCSI cmd to examine.
@@ -371,6 +434,7 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
         */
        switch (status_byte(scmd->result)) {
        case GOOD:
+               scsi_handle_queue_ramp_up(scmd->device);
        case COMMAND_TERMINATED:
                return SUCCESS;
        case CHECK_CONDITION:
@@ -382,9 +446,15 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
                 * who knows?  FIXME(eric)
                 */
                return SUCCESS;
-       case BUSY:
-       case QUEUE_FULL:
        case RESERVATION_CONFLICT:
+               /*
+                * let issuer deal with this, it could be just fine
+                */
+               return SUCCESS;
+       case QUEUE_FULL:
+               scsi_handle_queue_full(scmd->device);
+               /* fall through */
+       case BUSY:
        default:
                return FAILED;
        }
@@ -641,9 +711,9 @@ EXPORT_SYMBOL(scsi_eh_prep_cmnd);
 /**
  * scsi_eh_restore_cmnd  - Restore a scsi command info as part of error recory
  * @scmd:       SCSI command structure to restore
- * @ses:        saved information from a coresponding call to scsi_prep_eh_cmnd
+ * @ses:        saved information from a coresponding call to scsi_eh_prep_cmnd
  *
- * Undo any damage done by above scsi_prep_eh_cmnd().
+ * Undo any damage done by above scsi_eh_prep_cmnd().
  */
 void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
 {
@@ -721,6 +791,9 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
                case NEEDS_RETRY:
                case FAILED:
                        break;
+               case ADD_TO_MLQUEUE:
+                       rtn = NEEDS_RETRY;
+                       break;
                default:
                        rtn = FAILED;
                        break;
@@ -888,9 +961,10 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
                                                  "0x%p\n", current->comm,
                                                  scmd));
                rtn = scsi_try_to_abort_cmd(scmd);
-               if (rtn == SUCCESS) {
+               if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD;
                        if (!scsi_device_online(scmd->device) ||
+                           rtn == FAST_IO_FAIL ||
                            !scsi_eh_tur(scmd)) {
                                scsi_eh_finish_cmd(scmd, done_q);
                        }
@@ -1017,8 +1091,9 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
                                                  " 0x%p\n", current->comm,
                                                  sdev));
                rtn = scsi_try_bus_device_reset(bdr_scmd);
-               if (rtn == SUCCESS) {
+               if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        if (!scsi_device_online(sdev) ||
+                           rtn == FAST_IO_FAIL ||
                            !scsi_eh_tur(bdr_scmd)) {
                                list_for_each_entry_safe(scmd, next,
                                                         work_q, eh_entry) {
@@ -1081,10 +1156,11 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
                                                  "to target %d\n",
                                                  current->comm, id));
                rtn = scsi_try_target_reset(tgtr_scmd);
-               if (rtn == SUCCESS) {
+               if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
                                if (id == scmd_id(scmd))
                                        if (!scsi_device_online(scmd->device) ||
+                                           rtn == FAST_IO_FAIL ||
                                            !scsi_eh_tur(tgtr_scmd))
                                                scsi_eh_finish_cmd(scmd,
                                                                   done_q);
@@ -1140,10 +1216,11 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost,
                                                  " %d\n", current->comm,
                                                  channel));
                rtn = scsi_try_bus_reset(chan_scmd);
-               if (rtn == SUCCESS) {
+               if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
                                if (channel == scmd_channel(scmd))
                                        if (!scsi_device_online(scmd->device) ||
+                                           rtn == FAST_IO_FAIL ||
                                            !scsi_eh_tur(scmd))
                                                scsi_eh_finish_cmd(scmd,
                                                                   done_q);
@@ -1177,9 +1254,10 @@ static int scsi_eh_host_reset(struct list_head *work_q,
                                                  , current->comm));
 
                rtn = scsi_try_host_reset(scmd);
-               if (rtn == SUCCESS) {
+               if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
                        list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
                                if (!scsi_device_online(scmd->device) ||
+                                   rtn == FAST_IO_FAIL ||
                                    (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) ||
                                    !scsi_eh_tur(scmd))
                                        scsi_eh_finish_cmd(scmd, done_q);
@@ -1380,6 +1458,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
         */
        switch (status_byte(scmd->result)) {
        case QUEUE_FULL:
+               scsi_handle_queue_full(scmd->device);
                /*
                 * the case of trying to send too many commands to a
                 * tagged queueing device.
@@ -1393,6 +1472,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
                 */
                return ADD_TO_MLQUEUE;
        case GOOD:
+               scsi_handle_queue_ramp_up(scmd->device);
        case COMMAND_TERMINATED:
                return SUCCESS;
        case TASK_ABORTED:
@@ -1441,41 +1521,48 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
        }
 }
 
+static void eh_lock_door_done(struct request *req, int uptodate)
+{
+       __blk_put_request(req->q, req);
+}
+
 /**
  * scsi_eh_lock_door - Prevent medium removal for the specified device
  * @sdev:      SCSI device to prevent medium removal
  *
  * Locking:
- *     We must be called from process context; scsi_allocate_request()
- *     may sleep.
+ *     We must be called from process context.
  *
  * Notes:
  *     We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request on the
  *     head of the devices request queue, and continue.
- *
- * Bugs:
- *     scsi_allocate_request() may sleep waiting for existing requests to
- *     be processed.  However, since we haven't kicked off any request
- *     processing for this host, this may deadlock.
- *
- *     If scsi_allocate_request() fails for what ever reason, we
- *     completely forget to lock the door.
  */
 static void scsi_eh_lock_door(struct scsi_device *sdev)
 {
-       unsigned char cmnd[MAX_COMMAND_SIZE];
+       struct request *req;
+
+       /*
+        * blk_get_request with GFP_KERNEL (__GFP_WAIT) sleeps until a
+        * request becomes available
+        */
+       req = blk_get_request(sdev->request_queue, READ, GFP_KERNEL);
 
-       cmnd[0] = ALLOW_MEDIUM_REMOVAL;
-       cmnd[1] = 0;
-       cmnd[2] = 0;
-       cmnd[3] = 0;
-       cmnd[4] = SCSI_REMOVAL_PREVENT;
-       cmnd[5] = 0;
+       req->cmd[0] = ALLOW_MEDIUM_REMOVAL;
+       req->cmd[1] = 0;
+       req->cmd[2] = 0;
+       req->cmd[3] = 0;
+       req->cmd[4] = SCSI_REMOVAL_PREVENT;
+       req->cmd[5] = 0;
 
-       scsi_execute_async(sdev, cmnd, 6, DMA_NONE, NULL, 0, 0, 10 * HZ,
-                          5, NULL, NULL, GFP_KERNEL);
-}
+       req->cmd_len = COMMAND_SIZE(req->cmd[0]);
 
+       req->cmd_type = REQ_TYPE_BLOCK_PC;
+       req->cmd_flags |= REQ_QUIET;
+       req->timeout = 10 * HZ;
+       req->retries = 5;
+
+       blk_execute_rq_nowait(req->q, NULL, req, 1, eh_lock_door_done);
+}
 
 /**
  * scsi_restart_operations - restart io operations to the specified host.