fs_enet: restore promiscuous and multicast settings in restart()
[pandora-kernel.git] / drivers / scsi / scsi_error.c
index 547e85a..eaf5a8a 100644 (file)
@@ -524,6 +524,41 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
        return rtn;
 }
 
+static void __scsi_report_device_reset(struct scsi_device *sdev, void *data)
+{
+       sdev->was_reset = 1;
+       sdev->expecting_cc_ua = 1;
+}
+
+/**
+ * scsi_try_target_reset - Ask host to perform a target reset
+ * @scmd:      SCSI cmd used to send a target reset
+ *
+ * Notes:
+ *    There is no timeout for this operation.  if this operation is
+ *    unreliable for a given host, then the host itself needs to put a
+ *    timer on it, and set the host back to a consistent state prior to
+ *    returning.
+ */
+static int scsi_try_target_reset(struct scsi_cmnd *scmd)
+{
+       unsigned long flags;
+       int rtn;
+
+       if (!scmd->device->host->hostt->eh_target_reset_handler)
+               return FAILED;
+
+       rtn = scmd->device->host->hostt->eh_target_reset_handler(scmd);
+       if (rtn == SUCCESS) {
+               spin_lock_irqsave(scmd->device->host->host_lock, flags);
+               __starget_for_each_device(scsi_target(scmd->device), NULL,
+                                         __scsi_report_device_reset);
+               spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+       }
+
+       return rtn;
+}
+
 /**
  * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
  * @scmd:      SCSI cmd used to send BDR
@@ -542,11 +577,8 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
                return FAILED;
 
        rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
-       if (rtn == SUCCESS) {
-               scmd->device->was_reset = 1;
-               scmd->device->expecting_cc_ua = 1;
-       }
-
+       if (rtn == SUCCESS)
+               __scsi_report_device_reset(scmd->device, NULL);
        return rtn;
 }
 
@@ -584,8 +616,9 @@ static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
 {
        if (__scsi_try_to_abort_cmd(scmd) != SUCCESS)
                if (scsi_try_bus_device_reset(scmd) != SUCCESS)
-                       if (scsi_try_bus_reset(scmd) != SUCCESS)
-                               scsi_try_host_reset(scmd);
+                       if (scsi_try_target_reset(scmd) != SUCCESS)
+                               if (scsi_try_bus_reset(scmd) != SUCCESS)
+                                       scsi_try_host_reset(scmd);
 }
 
 /**
@@ -593,7 +626,7 @@ static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
  * @scmd:       SCSI command structure to hijack
  * @ses:        structure to save restore information
  * @cmnd:       CDB to send. Can be NULL if no new cmnd is needed
- * @cmnd_size:  size in bytes of @cmnd
+ * @cmnd_size:  size in bytes of @cmnd (must be <= BLK_MAX_CDB)
  * @sense_bytes: size of sense data to copy. or 0 (if != 0 @cmnd is ignored)
  *
  * This function is used to save a scsi command information before re-execution
@@ -615,33 +648,32 @@ void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses,
         * command.
         */
        ses->cmd_len = scmd->cmd_len;
-       memcpy(ses->cmnd, scmd->cmnd, sizeof(scmd->cmnd));
+       ses->cmnd = scmd->cmnd;
        ses->data_direction = scmd->sc_data_direction;
-       ses->bufflen = scmd->request_bufflen;
-       ses->buffer = scmd->request_buffer;
-       ses->use_sg = scmd->use_sg;
-       ses->resid = scmd->resid;
+       ses->sdb = scmd->sdb;
+       ses->next_rq = scmd->request->next_rq;
        ses->result = scmd->result;
 
+       scmd->cmnd = ses->eh_cmnd;
+       memset(scmd->cmnd, 0, BLK_MAX_CDB);
+       memset(&scmd->sdb, 0, sizeof(scmd->sdb));
+       scmd->request->next_rq = NULL;
+
        if (sense_bytes) {
-               scmd->request_bufflen = min_t(unsigned,
-                                      SCSI_SENSE_BUFFERSIZE, sense_bytes);
+               scmd->sdb.length = min_t(unsigned, SCSI_SENSE_BUFFERSIZE,
+                                        sense_bytes);
                sg_init_one(&ses->sense_sgl, scmd->sense_buffer,
-                                                      scmd->request_bufflen);
-               scmd->request_buffer = &ses->sense_sgl;
+                           scmd->sdb.length);
+               scmd->sdb.table.sgl = &ses->sense_sgl;
                scmd->sc_data_direction = DMA_FROM_DEVICE;
-               scmd->use_sg = 1;
-               memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
+               scmd->sdb.table.nents = 1;
                scmd->cmnd[0] = REQUEST_SENSE;
-               scmd->cmnd[4] = scmd->request_bufflen;
+               scmd->cmnd[4] = scmd->sdb.length;
                scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
        } else {
-               scmd->request_buffer = NULL;
-               scmd->request_bufflen = 0;
                scmd->sc_data_direction = DMA_NONE;
-               scmd->use_sg = 0;
                if (cmnd) {
-                       memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
+                       BUG_ON(cmnd_size > BLK_MAX_CDB);
                        memcpy(scmd->cmnd, cmnd, cmnd_size);
                        scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
                }
@@ -674,12 +706,10 @@ void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses)
         * Restore original data
         */
        scmd->cmd_len = ses->cmd_len;
-       memcpy(scmd->cmnd, ses->cmnd, sizeof(scmd->cmnd));
+       scmd->cmnd = ses->cmnd;
        scmd->sc_data_direction = ses->data_direction;
-       scmd->request_bufflen = ses->bufflen;
-       scmd->request_buffer = ses->buffer;
-       scmd->use_sg = ses->use_sg;
-       scmd->resid = ses->resid;
+       scmd->sdb = ses->sdb;
+       scmd->request->next_rq = ses->next_rq;
        scmd->result = ses->result;
 }
 EXPORT_SYMBOL(scsi_eh_restore_cmnd);
@@ -1063,6 +1093,56 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
        return list_empty(work_q);
 }
 
+/**
+ * scsi_eh_target_reset - send target reset if needed
+ * @shost:     scsi host being recovered.
+ * @work_q:     &list_head for pending commands.
+ * @done_q:    &list_head for processed commands.
+ *
+ * Notes:
+ *    Try a target reset.
+ */
+static int scsi_eh_target_reset(struct Scsi_Host *shost,
+                               struct list_head *work_q,
+                               struct list_head *done_q)
+{
+       struct scsi_cmnd *scmd, *tgtr_scmd, *next;
+       unsigned int id;
+       int rtn;
+
+       for (id = 0; id <= shost->max_id; id++) {
+               tgtr_scmd = NULL;
+               list_for_each_entry(scmd, work_q, eh_entry) {
+                       if (id == scmd_id(scmd)) {
+                               tgtr_scmd = scmd;
+                               break;
+                       }
+               }
+               if (!tgtr_scmd)
+                       continue;
+
+               SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset "
+                                                 "to target %d\n",
+                                                 current->comm, id));
+               rtn = scsi_try_target_reset(tgtr_scmd);
+               if (rtn == SUCCESS) {
+                       list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
+                               if (id == scmd_id(scmd))
+                                       if (!scsi_device_online(scmd->device) ||
+                                           !scsi_eh_tur(tgtr_scmd))
+                                               scsi_eh_finish_cmd(scmd,
+                                                                  done_q);
+                       }
+               } else
+                       SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset"
+                                                         " failed target: "
+                                                         "%d\n",
+                                                         current->comm, id));
+       }
+
+       return list_empty(work_q);
+}
+
 /**
  * scsi_eh_bus_reset - send a bus reset 
  * @shost:     &scsi host being recovered.
@@ -1451,9 +1531,11 @@ void scsi_eh_ready_devs(struct Scsi_Host *shost,
 {
        if (!scsi_eh_stu(shost, work_q, done_q))
                if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
-                       if (!scsi_eh_bus_reset(shost, work_q, done_q))
-                               if (!scsi_eh_host_reset(work_q, done_q))
-                                       scsi_eh_offline_sdevs(work_q, done_q);
+                       if (!scsi_eh_target_reset(shost, work_q, done_q))
+                               if (!scsi_eh_bus_reset(shost, work_q, done_q))
+                                       if (!scsi_eh_host_reset(work_q, done_q))
+                                               scsi_eh_offline_sdevs(work_q,
+                                                                     done_q);
 }
 EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);
 
@@ -1623,10 +1705,8 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)
        struct scsi_device *sdev;
 
        __shost_for_each_device(sdev, shost) {
-               if (channel == sdev_channel(sdev)) {
-                       sdev->was_reset = 1;
-                       sdev->expecting_cc_ua = 1;
-               }
+               if (channel == sdev_channel(sdev))
+                       __scsi_report_device_reset(sdev, NULL);
        }
 }
 EXPORT_SYMBOL(scsi_report_bus_reset);
@@ -1659,10 +1739,8 @@ void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target)
 
        __shost_for_each_device(sdev, shost) {
                if (channel == sdev_channel(sdev) &&
-                   target == sdev_id(sdev)) {
-                       sdev->was_reset = 1;
-                       sdev->expecting_cc_ua = 1;
-               }
+                   target == sdev_id(sdev))
+                       __scsi_report_device_reset(sdev, NULL);
        }
 }
 EXPORT_SYMBOL(scsi_report_device_reset);
@@ -1694,14 +1772,14 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
        unsigned long flags;
        int rtn;
 
+       blk_rq_init(NULL, &req);
        scmd->request = &req;
        memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout));
 
-       memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd));
-    
+       scmd->cmnd = req.cmd;
+
        scmd->scsi_done         = scsi_reset_provider_done_command;
-       scmd->request_buffer            = NULL;
-       scmd->request_bufflen           = 0;
+       memset(&scmd->sdb, 0, sizeof(scmd->sdb));
 
        scmd->cmd_len                   = 0;
 
@@ -1719,6 +1797,11 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
                if (rtn == SUCCESS)
                        break;
                /* FALLTHROUGH */
+       case SCSI_TRY_RESET_TARGET:
+               rtn = scsi_try_target_reset(scmd);
+               if (rtn == SUCCESS)
+                       break;
+               /* FALLTHROUGH */
        case SCSI_TRY_RESET_BUS:
                rtn = scsi_try_bus_reset(scmd);
                if (rtn == SUCCESS)
@@ -1912,3 +1995,31 @@ int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
        }
 }
 EXPORT_SYMBOL(scsi_get_sense_info_fld);
+
+/**
+ * scsi_build_sense_buffer - build sense data in a buffer
+ * @desc:      Sense format (non zero == descriptor format,
+ *             0 == fixed format)
+ * @buf:       Where to build sense data
+ * @key:       Sense key
+ * @asc:       Additional sense code
+ * @ascq:      Additional sense code qualifier
+ *
+ **/
+void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)
+{
+       if (desc) {
+               buf[0] = 0x72;  /* descriptor, current */
+               buf[1] = key;
+               buf[2] = asc;
+               buf[3] = ascq;
+               buf[7] = 0;
+       } else {
+               buf[0] = 0x70;  /* fixed, current */
+               buf[2] = key;
+               buf[7] = 0xa;
+               buf[12] = asc;
+               buf[13] = ascq;
+       }
+}
+EXPORT_SYMBOL(scsi_build_sense_buffer);