#include <linux/time.h>
#include <linux/workqueue.h>
#include <scsi/scsi_dh.h>
+#include <scsi/scsi_eh.h>
#include <asm/atomic.h>
#define DM_MSG_PREFIX "multipath"
struct dm_mpath_io {
struct pgpath *pgpath;
size_t nr_bytes;
+ char sense[SCSI_SENSE_BUFFERSIZE];
};
typedef int (*action_fn) (struct pgpath *pgpath);
map_context->ptr = mpio;
clone->cmd_flags |= REQ_FAILFAST_TRANSPORT;
+ /* Always attach a sense buffer */
+ if (!clone->sense)
+ clone->sense = mpio->sense;
r = map_io(m, clone, mpio, 0);
if (r < 0 || r == DM_MAPIO_REQUEUE)
mempool_free(mpio, m->mpio_pool);
}
/*
+ * Evaluate scsi return code
+ */
+static int eval_scsi_error(int result, char *sense, int sense_len)
+{
+ struct scsi_sense_hdr sshdr;
+ int r = DM_ENDIO_REQUEUE;
+
+ if (host_byte(result) != DID_OK)
+ return r;
+
+ if (msg_byte(result) != COMMAND_COMPLETE)
+ return r;
+
+ if (status_byte(result) == RESERVATION_CONFLICT)
+ /* Do not retry here, possible data corruption */
+ return -EIO;
+
+ if (status_byte(result) == CHECK_CONDITION &&
+ !scsi_normalize_sense(sense, sense_len, &sshdr)) {
+
+ switch (sshdr.sense_key) {
+ case MEDIUM_ERROR:
+ case DATA_PROTECT:
+ case BLANK_CHECK:
+ case COPY_ABORTED:
+ case VOLUME_OVERFLOW:
+ case MISCOMPARE:
+ r = -EIO;
+ break;
+ }
+ }
+
+ return r;
+}
+
+/*
* end_io handling
*/
static int do_end_io(struct multipath *m, struct request *clone,
if (error == -EOPNOTSUPP)
return error;
+ r = eval_scsi_error(clone->errors, clone->sense, clone->sense_len);
+ if (r != DM_ENDIO_REQUEUE)
+ return r;
+
if (mpio->pgpath)
fail_path(mpio->pgpath);
if (ps->type->end_io)
ps->type->end_io(ps, &pgpath->path, mpio->nr_bytes);
}
+ if (clone->sense == mpio->sense) {
+ clone->sense = NULL;
+ clone->sense_len = 0;
+ }
mempool_free(mpio, m->mpio_pool);
return r;
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
- if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
- req->errors = result;
- if (result) {
- if (sense_valid && req->sense) {
- /*
- * SG_IO wants current and deferred errors
- */
- int len = 8 + cmd->sense_buffer[7];
+ req->errors = result;
+ if (sense_valid && req->sense) {
+ int len = 8 + cmd->sense_buffer[7];
+
+ if (len > SCSI_SENSE_BUFFERSIZE)
+ len = SCSI_SENSE_BUFFERSIZE;
+ memcpy(req->sense, cmd->sense_buffer, len);
+ req->sense_len = len;
+ }
- if (len > SCSI_SENSE_BUFFERSIZE)
- len = SCSI_SENSE_BUFFERSIZE;
- memcpy(req->sense, cmd->sense_buffer, len);
- req->sense_len = len;
- }
- if (!sense_deferred)
- error = -EIO;
- }
+ if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
+ if ((result) && (!sense_deferred))
+ error = -EIO;
req->resid_len = scsi_get_resid(cmd);