Commit b6ff1b14 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by James Bottomley

[SCSI] scsi_dh: Update EMC handler

This patch converts the EMC device handler to use a proper
state machine. We now also parse the extended INQUIRY
information to determine if long trespass commands are
supported. And we're now using the long trespass command
correctly. And finally there's now an check at init time
to refuse to attach to devices not supporting EMC-specific
VPD pages.
Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarChandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 4c05ae52
...@@ -25,28 +25,31 @@ ...@@ -25,28 +25,31 @@
#include <scsi/scsi_dh.h> #include <scsi/scsi_dh.h>
#include <scsi/scsi_device.h> #include <scsi/scsi_device.h>
#define CLARIION_NAME "emc_clariion" #define CLARIION_NAME "emc"
#define CLARIION_TRESPASS_PAGE 0x22 #define CLARIION_TRESPASS_PAGE 0x22
#define CLARIION_BUFFER_SIZE 0x80 #define CLARIION_BUFFER_SIZE 0xFC
#define CLARIION_TIMEOUT (60 * HZ) #define CLARIION_TIMEOUT (60 * HZ)
#define CLARIION_RETRIES 3 #define CLARIION_RETRIES 3
#define CLARIION_UNBOUND_LU -1 #define CLARIION_UNBOUND_LU -1
#define CLARIION_SP_A 0
#define CLARIION_SP_B 1
static unsigned char long_trespass[] = { /* Flags */
0, 0, 0, 0, #define CLARIION_SHORT_TRESPASS 1
CLARIION_TRESPASS_PAGE, /* Page code */ #define CLARIION_HONOR_RESERVATIONS 2
0x09, /* Page length - 2 */
0x81, /* Trespass code + Honor reservation bit */
0xff, 0xff, /* Trespass target */
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
};
static unsigned char long_trespass_hr[] = { /* LUN states */
0, 0, 0, 0, #define CLARIION_LUN_UNINITIALIZED -1
#define CLARIION_LUN_UNBOUND 0
#define CLARIION_LUN_BOUND 1
#define CLARIION_LUN_OWNED 2
static unsigned char long_trespass[] = {
0, 0, 0, 0, 0, 0, 0, 0,
CLARIION_TRESPASS_PAGE, /* Page code */ CLARIION_TRESPASS_PAGE, /* Page code */
0x09, /* Page length - 2 */ 0x09, /* Page length - 2 */
0x01, /* Trespass code + Honor reservation bit */ 0x01, /* Trespass code */
0xff, 0xff, /* Trespass target */ 0xff, 0xff, /* Trespass target */
0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
}; };
...@@ -55,39 +58,56 @@ static unsigned char short_trespass[] = { ...@@ -55,39 +58,56 @@ static unsigned char short_trespass[] = {
0, 0, 0, 0, 0, 0, 0, 0,
CLARIION_TRESPASS_PAGE, /* Page code */ CLARIION_TRESPASS_PAGE, /* Page code */
0x02, /* Page length - 2 */ 0x02, /* Page length - 2 */
0x81, /* Trespass code + Honor reservation bit */ 0x01, /* Trespass code */
0xff, /* Trespass target */ 0xff, /* Trespass target */
}; };
static unsigned char short_trespass_hr[] = { static const char * lun_state[] =
0, 0, 0, 0, {
CLARIION_TRESPASS_PAGE, /* Page code */ "not bound",
0x02, /* Page length - 2 */ "bound",
0x01, /* Trespass code + Honor reservation bit */ "owned",
0xff, /* Trespass target */
}; };
struct clariion_dh_data { struct clariion_dh_data {
/* /*
* Flags:
* CLARIION_SHORT_TRESPASS
* Use short trespass command (FC-series) or the long version * Use short trespass command (FC-series) or the long version
* (default for AX/CX CLARiiON arrays). * (default for AX/CX CLARiiON arrays).
*/ *
unsigned short_trespass; * CLARIION_HONOR_RESERVATIONS
/*
* Whether or not (default) to honor SCSI reservations when * Whether or not (default) to honor SCSI reservations when
* initiating a switch-over. * initiating a switch-over.
*/ */
unsigned hr; unsigned flags;
/* I/O buffer for both MODE_SELECT and INQUIRY commands. */ /*
* I/O buffer for both MODE_SELECT and INQUIRY commands.
*/
char buffer[CLARIION_BUFFER_SIZE]; char buffer[CLARIION_BUFFER_SIZE];
/* /*
* SCSI sense buffer for commands -- assumes serial issuance * SCSI sense buffer for commands -- assumes serial issuance
* and completion sequence of all commands for same multipath. * and completion sequence of all commands for same multipath.
*/ */
unsigned char sense[SCSI_SENSE_BUFFERSIZE]; unsigned char sense[SCSI_SENSE_BUFFERSIZE];
/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */ unsigned int senselen;
/*
* LUN state
*/
int lun_state;
/*
* SP Port number
*/
int port;
/*
* which SP (A=0,B=1,UNBOUND=-1) is the default SP for this
* path's mapped LUN
*/
int default_sp; int default_sp;
/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */ /*
* which SP (A=0,B=1,UNBOUND=-1) is the active SP for this
* path's mapped LUN
*/
int current_sp; int current_sp;
}; };
...@@ -102,19 +122,16 @@ static inline struct clariion_dh_data ...@@ -102,19 +122,16 @@ static inline struct clariion_dh_data
/* /*
* Parse MODE_SELECT cmd reply. * Parse MODE_SELECT cmd reply.
*/ */
static int trespass_endio(struct scsi_device *sdev, int result) static int trespass_endio(struct scsi_device *sdev, char *sense)
{ {
int err = SCSI_DH_OK; int err = SCSI_DH_IO;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
struct clariion_dh_data *csdev = get_clariion_data(sdev);
char *sense = csdev->sense;
if (status_byte(result) == CHECK_CONDITION && if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, "
sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
"0x%2x, 0x%2x while sending CLARiiON trespass " "0x%2x, 0x%2x while sending CLARiiON trespass "
"command.\n", sshdr.sense_key, sshdr.asc, "command.\n", CLARIION_NAME, sshdr.sense_key,
sshdr.ascq); sshdr.asc, sshdr.ascq);
if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) && if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
(sshdr.ascq == 0x00)) { (sshdr.ascq == 0x00)) {
...@@ -122,9 +139,9 @@ static int trespass_endio(struct scsi_device *sdev, int result) ...@@ -122,9 +139,9 @@ static int trespass_endio(struct scsi_device *sdev, int result)
* Array based copy in progress -- do not send * Array based copy in progress -- do not send
* mode_select or copy will be aborted mid-stream. * mode_select or copy will be aborted mid-stream.
*/ */
sdev_printk(KERN_INFO, sdev, "Array Based Copy in " sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in "
"progress while sending CLARiiON trespass " "progress while sending CLARiiON trespass "
"command.\n"); "command.\n", CLARIION_NAME);
err = SCSI_DH_DEV_TEMP_BUSY; err = SCSI_DH_DEV_TEMP_BUSY;
} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) && } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
(sshdr.ascq == 0x03)) { (sshdr.ascq == 0x03)) {
...@@ -132,109 +149,113 @@ static int trespass_endio(struct scsi_device *sdev, int result) ...@@ -132,109 +149,113 @@ static int trespass_endio(struct scsi_device *sdev, int result)
* LUN Not Ready - Manual Intervention Required * LUN Not Ready - Manual Intervention Required
* indicates in-progress ucode upgrade (NDU). * indicates in-progress ucode upgrade (NDU).
*/ */
sdev_printk(KERN_INFO, sdev, "Detected in-progress " sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress "
"ucode upgrade NDU operation while sending " "ucode upgrade NDU operation while sending "
"CLARiiON trespass command.\n"); "CLARiiON trespass command.\n", CLARIION_NAME);
err = SCSI_DH_DEV_TEMP_BUSY; err = SCSI_DH_DEV_TEMP_BUSY;
} else } else
err = SCSI_DH_DEV_FAILED; err = SCSI_DH_DEV_FAILED;
} else if (result) { } else {
sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending " sdev_printk(KERN_INFO, sdev,
"CLARiiON trespass command.\n", result); "%s: failed to send MODE SELECT, no sense available\n",
err = SCSI_DH_IO; CLARIION_NAME);
} }
return err; return err;
} }
static int parse_sp_info_reply(struct scsi_device *sdev, int result, static int parse_sp_info_reply(struct scsi_device *sdev,
int *default_sp, int *current_sp, int *new_current_sp) struct clariion_dh_data *csdev)
{ {
int err = SCSI_DH_OK; int err = SCSI_DH_OK;
struct clariion_dh_data *csdev = get_clariion_data(sdev);
if (result == 0) {
/* check for in-progress ucode upgrade (NDU) */ /* check for in-progress ucode upgrade (NDU) */
if (csdev->buffer[48] != 0) { if (csdev->buffer[48] != 0) {
sdev_printk(KERN_NOTICE, sdev, "Detected in-progress " sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress "
"ucode upgrade NDU operation while finding " "ucode upgrade NDU operation while finding "
"current active SP."); "current active SP.", CLARIION_NAME);
err = SCSI_DH_DEV_TEMP_BUSY; err = SCSI_DH_DEV_TEMP_BUSY;
} else { goto out;
*default_sp = csdev->buffer[5];
if (csdev->buffer[4] == 2)
/* SP for path is current */
*current_sp = csdev->buffer[8];
else {
if (csdev->buffer[4] == 1)
/* SP for this path is NOT current */
if (csdev->buffer[8] == 0)
*current_sp = 1;
else
*current_sp = 0;
else
/* unbound LU or LUNZ */
*current_sp = CLARIION_UNBOUND_LU;
} }
*new_current_sp = csdev->buffer[8]; if (csdev->buffer[4] < 0 || csdev->buffer[4] > 2) {
/* Invalid buffer format */
sdev_printk(KERN_NOTICE, sdev,
"%s: invalid VPD page 0xC0 format\n",
CLARIION_NAME);
err = SCSI_DH_NOSYS;
goto out;
} }
} else { switch (csdev->buffer[28] & 0x0f) {
struct scsi_sense_hdr sshdr; case 6:
sdev_printk(KERN_NOTICE, sdev,
err = SCSI_DH_IO; "%s: ALUA failover mode detected\n",
CLARIION_NAME);
if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, break;
&sshdr)) case 4:
sdev_printk(KERN_ERR, sdev, "Found valid sense data " /* Linux failover */
"0x%2x, 0x%2x, 0x%2x while finding current " break;
"active SP.", sshdr.sense_key, sshdr.asc, default:
sshdr.ascq); sdev_printk(KERN_WARNING, sdev,
else "%s: Invalid failover mode %d\n",
sdev_printk(KERN_ERR, sdev, "Error 0x%x finding " CLARIION_NAME, csdev->buffer[28] & 0x0f);
"current active SP.", result); err = SCSI_DH_NOSYS;
goto out;
} }
csdev->default_sp = csdev->buffer[5];
csdev->lun_state = csdev->buffer[4];
csdev->current_sp = csdev->buffer[8];
csdev->port = csdev->buffer[7];
out:
return err; return err;
} }
static int sp_info_endio(struct scsi_device *sdev, int result, #define emc_default_str "FC (Legacy)"
int mode_select_sent, int *done)
{
struct clariion_dh_data *csdev = get_clariion_data(sdev);
int err_flags, default_sp, current_sp, new_current_sp;
err_flags = parse_sp_info_reply(sdev, result, &default_sp,
&current_sp, &new_current_sp);
if (err_flags != SCSI_DH_OK) static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer)
goto done; {
unsigned char len = buffer[4] + 5;
char *sp_model = NULL;
unsigned char sp_len, serial_len;
if (len < 160) {
sdev_printk(KERN_WARNING, sdev,
"%s: Invalid information section length %d\n",
CLARIION_NAME, len);
/* Check for old FC arrays */
if (!strncmp(buffer + 8, "DGC", 3)) {
/* Old FC array, not supporting extended information */
sp_model = emc_default_str;
}
goto out;
}
if (mode_select_sent) {
csdev->default_sp = default_sp;
csdev->current_sp = current_sp;
} else {
/* /*
* Issue the actual module_selec request IFF either * Parse extended information for SP model number
* (1) we do not know the identity of the current SP OR
* (2) what we think we know is actually correct.
*/ */
if ((current_sp != CLARIION_UNBOUND_LU) && serial_len = buffer[160];
(new_current_sp != current_sp)) { if (serial_len == 0 || serial_len + 161 > len) {
sdev_printk(KERN_WARNING, sdev,
csdev->default_sp = default_sp; "%s: Invalid array serial number length %d\n",
csdev->current_sp = current_sp; CLARIION_NAME, serial_len);
goto out;
sdev_printk(KERN_INFO, sdev, "Ignoring path group "
"switch-over command for CLARiiON SP%s since "
" mapped device is already initialized.",
current_sp ? "B" : "A");
if (done)
*done = 1; /* as good as doing it */
} }
sp_len = buffer[99];
if (sp_len == 0 || serial_len + sp_len + 161 > len) {
sdev_printk(KERN_WARNING, sdev,
"%s: Invalid model number length %d\n",
CLARIION_NAME, sp_len);
goto out;
} }
done: sp_model = &buffer[serial_len + 161];
return err_flags; /* Strip whitespace at the end */
while (sp_len > 1 && sp_model[sp_len - 1] == ' ')
sp_len--;
sp_model[sp_len] = '\0';
out:
return sp_model;
} }
/* /*
...@@ -244,48 +265,37 @@ static int sp_info_endio(struct scsi_device *sdev, int result, ...@@ -244,48 +265,37 @@ static int sp_info_endio(struct scsi_device *sdev, int result,
* Uses data and sense buffers in hardware handler context structure and * Uses data and sense buffers in hardware handler context structure and
* assumes serial servicing of commands, both issuance and completion. * assumes serial servicing of commands, both issuance and completion.
*/ */
static struct request *get_req(struct scsi_device *sdev, int cmd) static struct request *get_req(struct scsi_device *sdev, int cmd,
unsigned char *buffer)
{ {
struct clariion_dh_data *csdev = get_clariion_data(sdev);
struct request *rq; struct request *rq;
unsigned char *page22;
int len = 0; int len = 0;
rq = blk_get_request(sdev->request_queue, rq = blk_get_request(sdev->request_queue,
(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC); (cmd == MODE_SELECT) ? WRITE : READ, GFP_NOIO);
if (!rq) { if (!rq) {
sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed"); sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
return NULL; return NULL;
} }
memset(&rq->cmd, 0, BLK_MAX_CDB); memset(rq->cmd, 0, BLK_MAX_CDB);
rq->cmd_len = COMMAND_SIZE(cmd);
rq->cmd[0] = cmd; rq->cmd[0] = cmd;
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
switch (cmd) { switch (cmd) {
case MODE_SELECT: case MODE_SELECT:
if (csdev->short_trespass) {
page22 = csdev->hr ? short_trespass_hr : short_trespass;
len = sizeof(short_trespass); len = sizeof(short_trespass);
} else { rq->cmd_flags |= REQ_RW;
page22 = csdev->hr ? long_trespass_hr : long_trespass; rq->cmd[1] = 0x10;
break;
case MODE_SELECT_10:
len = sizeof(long_trespass); len = sizeof(long_trespass);
}
/*
* Can't DMA from kernel BSS -- must copy selected trespass
* command mode page contents to context buffer which is
* allocated by kmalloc.
*/
BUG_ON((len > CLARIION_BUFFER_SIZE));
memcpy(csdev->buffer, page22, len);
rq->cmd_flags |= REQ_RW; rq->cmd_flags |= REQ_RW;
rq->cmd[1] = 0x10; rq->cmd[1] = 0x10;
break; break;
case INQUIRY: case INQUIRY:
rq->cmd[1] = 0x1;
rq->cmd[2] = 0xC0;
len = CLARIION_BUFFER_SIZE; len = CLARIION_BUFFER_SIZE;
memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE); memset(buffer, 0, len);
break; break;
default: default:
BUG_ON(1); BUG_ON(1);
...@@ -298,47 +308,94 @@ static struct request *get_req(struct scsi_device *sdev, int cmd) ...@@ -298,47 +308,94 @@ static struct request *get_req(struct scsi_device *sdev, int cmd)
rq->timeout = CLARIION_TIMEOUT; rq->timeout = CLARIION_TIMEOUT;
rq->retries = CLARIION_RETRIES; rq->retries = CLARIION_RETRIES;
rq->sense = csdev->sense; if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) {
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); blk_put_request(rq);
rq->sense_len = 0;
if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
len, GFP_ATOMIC)) {
__blk_put_request(rq->q, rq);
return NULL; return NULL;
} }
return rq; return rq;
} }
static int send_cmd(struct scsi_device *sdev, int cmd) static int send_inquiry_cmd(struct scsi_device *sdev, int page,
struct clariion_dh_data *csdev)
{ {
struct request *rq = get_req(sdev, cmd); struct request *rq = get_req(sdev, INQUIRY, csdev->buffer);
int err;
if (!rq) if (!rq)
return SCSI_DH_RES_TEMP_UNAVAIL; return SCSI_DH_RES_TEMP_UNAVAIL;
return blk_execute_rq(sdev->request_queue, NULL, rq, 1); rq->sense = csdev->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
rq->sense_len = csdev->senselen = 0;
rq->cmd[0] = INQUIRY;
if (page != 0) {
rq->cmd[1] = 1;
rq->cmd[2] = page;
}
err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
if (err == -EIO) {
sdev_printk(KERN_INFO, sdev,
"%s: failed to send %s INQUIRY: %x\n",
CLARIION_NAME, page?"EVPD":"standard",
rq->errors);
csdev->senselen = rq->sense_len;
err = SCSI_DH_IO;
}
blk_put_request(rq);
return err;
} }
static int clariion_activate(struct scsi_device *sdev) static int send_trespass_cmd(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{ {
int result, done = 0; struct request *rq;
unsigned char *page22;
int err, len, cmd;
result = send_cmd(sdev, INQUIRY); if (csdev->flags & CLARIION_SHORT_TRESPASS) {
result = sp_info_endio(sdev, result, 0, &done); page22 = short_trespass;
if (result || done) if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
goto done; /* Set Honor Reservations bit */
page22[6] |= 0x80;
len = sizeof(short_trespass);
cmd = MODE_SELECT;
} else {
page22 = long_trespass;
if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
/* Set Honor Reservations bit */
page22[10] |= 0x80;
len = sizeof(long_trespass);
cmd = MODE_SELECT_10;
}
BUG_ON((len > CLARIION_BUFFER_SIZE));
memcpy(csdev->buffer, page22, len);
result = send_cmd(sdev, MODE_SELECT); rq = get_req(sdev, cmd, csdev->buffer);
result = trespass_endio(sdev, result); if (!rq)
if (result) return SCSI_DH_RES_TEMP_UNAVAIL;
goto done;
result = send_cmd(sdev, INQUIRY); rq->sense = csdev->sense;
result = sp_info_endio(sdev, result, 1, NULL); memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
done: rq->sense_len = csdev->senselen = 0;
return result;
err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
if (err == -EIO) {
if (rq->sense_len) {
err = trespass_endio(sdev, csdev->sense);
} else {
sdev_printk(KERN_INFO, sdev,
"%s: failed to send MODE SELECT: %x\n",
CLARIION_NAME, rq->errors);
}
}
blk_put_request(rq);
return err;
} }
static int clariion_check_sense(struct scsi_device *sdev, static int clariion_check_sense(struct scsi_device *sdev,
...@@ -386,13 +443,129 @@ static int clariion_check_sense(struct scsi_device *sdev, ...@@ -386,13 +443,129 @@ static int clariion_check_sense(struct scsi_device *sdev,
break; break;
} }
/* success just means we do not care what scsi-ml does */ return SCSI_RETURN_NOT_HANDLED;
return SUCCESS; }
static int clariion_prep_fn(struct scsi_device *sdev, struct request *req)
{
struct clariion_dh_data *h = get_clariion_data(sdev);
int ret = BLKPREP_OK;
if (h->lun_state != CLARIION_LUN_OWNED) {
ret = BLKPREP_KILL;
req->cmd_flags |= REQ_QUIET;
}
return ret;
}
static int clariion_std_inquiry(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
int err;
char *sp_model;
err = send_inquiry_cmd(sdev, 0, csdev);
if (err != SCSI_DH_OK && csdev->senselen) {
struct scsi_sense_hdr sshdr;
if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
&sshdr)) {
sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
"%02x/%02x/%02x\n", CLARIION_NAME,
sshdr.sense_key, sshdr.asc, sshdr.ascq);
}
err = SCSI_DH_IO;
goto out;
}
sp_model = parse_sp_model(sdev, csdev->buffer);
if (!sp_model) {
err = SCSI_DH_DEV_UNSUPP;
goto out;
}
/*
* FC Series arrays do not support long trespass
*/
if (!strlen(sp_model) || !strncmp(sp_model, "FC",2))
csdev->flags |= CLARIION_SHORT_TRESPASS;
sdev_printk(KERN_INFO, sdev,
"%s: detected Clariion %s, flags %x\n",
CLARIION_NAME, sp_model, csdev->flags);
out:
return err;
}
static int clariion_send_inquiry(struct scsi_device *sdev,
struct clariion_dh_data *csdev)
{
int err, retry = CLARIION_RETRIES;
retry:
err = send_inquiry_cmd(sdev, 0xC0, csdev);
if (err != SCSI_DH_OK && csdev->senselen) {
struct scsi_sense_hdr sshdr;
err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
&sshdr);
if (!err)
return SCSI_DH_IO;
err = clariion_check_sense(sdev, &sshdr);
if (retry > 0 && err == NEEDS_RETRY) {
retry--;
goto retry;
}
sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
"%02x/%02x/%02x\n", CLARIION_NAME,
sshdr.sense_key, sshdr.asc, sshdr.ascq);
err = SCSI_DH_IO;
} else {
err = parse_sp_info_reply(sdev, csdev);
}
return err;
}
static int clariion_activate(struct scsi_device *sdev)
{
struct clariion_dh_data *csdev = get_clariion_data(sdev);
int result;
result = clariion_send_inquiry(sdev, csdev);
if (result != SCSI_DH_OK)
goto done;
if (csdev->lun_state == CLARIION_LUN_OWNED)
goto done;
result = send_trespass_cmd(sdev, csdev);
if (result != SCSI_DH_OK)
goto done;
sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n",
CLARIION_NAME,
csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" );
/* Update status */
result = clariion_send_inquiry(sdev, csdev);
if (result != SCSI_DH_OK)
goto done;
done:
sdev_printk(KERN_INFO, sdev,
"%s: at SP %c Port %d (%s, default SP %c)\n",
CLARIION_NAME, csdev->current_sp + 'A',
csdev->port, lun_state[csdev->lun_state],
csdev->default_sp + 'A');
return result;
} }
const struct scsi_dh_devlist clariion_dev_list[] = { const struct scsi_dh_devlist clariion_dev_list[] = {
{"DGC", "RAID"}, {"DGC", "RAID"},
{"DGC", "DISK"}, {"DGC", "DISK"},
{"DGC", "VRAID"},
{NULL, NULL}, {NULL, NULL},
}; };
...@@ -407,6 +580,7 @@ static struct scsi_device_handler clariion_dh = { ...@@ -407,6 +580,7 @@ static struct scsi_device_handler clariion_dh = {
.detach = clariion_bus_detach, .detach = clariion_bus_detach,
.check_sense = clariion_check_sense, .check_sense = clariion_check_sense,
.activate = clariion_activate, .activate = clariion_activate,
.prep_fn = clariion_prep_fn,
}; };
/* /*
...@@ -417,28 +591,50 @@ static int clariion_bus_attach(struct scsi_device *sdev) ...@@ -417,28 +591,50 @@ static int clariion_bus_attach(struct scsi_device *sdev)
struct scsi_dh_data *scsi_dh_data; struct scsi_dh_data *scsi_dh_data;
struct clariion_dh_data *h; struct clariion_dh_data *h;
unsigned long flags; unsigned long flags;
int err;
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ sizeof(*h) , GFP_KERNEL); + sizeof(*h) , GFP_KERNEL);
if (!scsi_dh_data) { if (!scsi_dh_data) {
sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n", sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
CLARIION_NAME); CLARIION_NAME);
return -ENOMEM; return -ENOMEM;
} }
scsi_dh_data->scsi_dh = &clariion_dh; scsi_dh_data->scsi_dh = &clariion_dh;
h = (struct clariion_dh_data *) scsi_dh_data->buf; h = (struct clariion_dh_data *) scsi_dh_data->buf;
h->lun_state = CLARIION_LUN_UNINITIALIZED;
h->default_sp = CLARIION_UNBOUND_LU; h->default_sp = CLARIION_UNBOUND_LU;
h->current_sp = CLARIION_UNBOUND_LU; h->current_sp = CLARIION_UNBOUND_LU;
err = clariion_std_inquiry(sdev, h);
if (err != SCSI_DH_OK)
goto failed;
err = clariion_send_inquiry(sdev, h);
if (err != SCSI_DH_OK)
goto failed;
if (!try_module_get(THIS_MODULE))
goto failed;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags); spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
sdev->scsi_dh_data = scsi_dh_data; sdev->scsi_dh_data = scsi_dh_data;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME); sdev_printk(KERN_INFO, sdev,
try_module_get(THIS_MODULE); "%s: connected to SP %c Port %d (%s, default SP %c)\n",
CLARIION_NAME, h->current_sp + 'A',
h->port, lun_state[h->lun_state],
h->default_sp + 'A');
return 0; return 0;
failed:
kfree(scsi_dh_data);
sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
CLARIION_NAME);
return -EINVAL;
} }
static void clariion_bus_detach(struct scsi_device *sdev) static void clariion_bus_detach(struct scsi_device *sdev)
...@@ -451,7 +647,7 @@ static void clariion_bus_detach(struct scsi_device *sdev) ...@@ -451,7 +647,7 @@ static void clariion_bus_detach(struct scsi_device *sdev)
sdev->scsi_dh_data = NULL; sdev->scsi_dh_data = NULL;
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
sdev_printk(KERN_NOTICE, sdev, "Detached %s.\n", sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n",
CLARIION_NAME); CLARIION_NAME);
kfree(scsi_dh_data); kfree(scsi_dh_data);
...@@ -464,7 +660,8 @@ static int __init clariion_init(void) ...@@ -464,7 +660,8 @@ static int __init clariion_init(void)
r = scsi_register_device_handler(&clariion_dh); r = scsi_register_device_handler(&clariion_dh);
if (r != 0) if (r != 0)
printk(KERN_ERR "Failed to register scsi device handler."); printk(KERN_ERR "%s: Failed to register scsi device handler.",
CLARIION_NAME);
return r; return r;
} }
......
...@@ -32,6 +32,7 @@ enum { ...@@ -32,6 +32,7 @@ enum {
*/ */
SCSI_DH_DEV_FAILED, /* generic device error */ SCSI_DH_DEV_FAILED, /* generic device error */
SCSI_DH_DEV_TEMP_BUSY, SCSI_DH_DEV_TEMP_BUSY,
SCSI_DH_DEV_UNSUPP, /* device handler not supported */
SCSI_DH_DEVICE_MAX, /* max device blkerr definition */ SCSI_DH_DEVICE_MAX, /* max device blkerr definition */
/* /*
......
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