2  * Target driver for EMC CLARiiON AX/CX-series hardware.
 
   3  * Based on code from Lars Marowsky-Bree <lmb@suse.de>
 
   4  * and Ed Goggin <egoggin@emc.com>.
 
   6  * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
 
   7  * Copyright (C) 2006 Mike Christie
 
   9  * This program is free software; you can redistribute it and/or modify
 
  10  * it under the terms of the GNU General Public License as published by
 
  11  * the Free Software Foundation; either version 2, or (at your option)
 
  14  * This program is distributed in the hope that it will be useful,
 
  15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  17  * GNU General Public License for more details.
 
  19  * You should have received a copy of the GNU General Public License
 
  20  * along with this program; see the file COPYING.  If not, write to
 
  21  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
  23 #include <scsi/scsi.h>
 
  24 #include <scsi/scsi_eh.h>
 
  25 #include <scsi/scsi_dh.h>
 
  26 #include <scsi/scsi_device.h>
 
  28 #define CLARIION_NAME                   "emc"
 
  30 #define CLARIION_TRESPASS_PAGE          0x22
 
  31 #define CLARIION_BUFFER_SIZE            0xFC
 
  32 #define CLARIION_TIMEOUT                (60 * HZ)
 
  33 #define CLARIION_RETRIES                3
 
  34 #define CLARIION_UNBOUND_LU             -1
 
  35 #define CLARIION_SP_A                   0
 
  36 #define CLARIION_SP_B                   1
 
  39 #define CLARIION_SHORT_TRESPASS         1
 
  40 #define CLARIION_HONOR_RESERVATIONS     2
 
  43 #define CLARIION_LUN_UNINITIALIZED      -1
 
  44 #define CLARIION_LUN_UNBOUND            0
 
  45 #define CLARIION_LUN_BOUND              1
 
  46 #define CLARIION_LUN_OWNED              2
 
  48 static unsigned char long_trespass[] = {
 
  49         0, 0, 0, 0, 0, 0, 0, 0,
 
  50         CLARIION_TRESPASS_PAGE, /* Page code */
 
  51         0x09,                   /* Page length - 2 */
 
  52         0x01,                   /* Trespass code */
 
  53         0xff, 0xff,             /* Trespass target */
 
  54         0, 0, 0, 0, 0, 0        /* Reserved bytes / unknown */
 
  57 static unsigned char short_trespass[] = {
 
  59         CLARIION_TRESPASS_PAGE, /* Page code */
 
  60         0x02,                   /* Page length - 2 */
 
  61         0x01,                   /* Trespass code */
 
  62         0xff,                   /* Trespass target */
 
  65 static const char * lun_state[] =
 
  72 struct clariion_dh_data {
 
  75          *  CLARIION_SHORT_TRESPASS
 
  76          * Use short trespass command (FC-series) or the long version
 
  77          * (default for AX/CX CLARiiON arrays).
 
  79          *  CLARIION_HONOR_RESERVATIONS
 
  80          * Whether or not (default) to honor SCSI reservations when
 
  81          * initiating a switch-over.
 
  85          * I/O buffer for both MODE_SELECT and INQUIRY commands.
 
  87         unsigned char buffer[CLARIION_BUFFER_SIZE];
 
  89          * SCSI sense buffer for commands -- assumes serial issuance
 
  90          * and completion sequence of all commands for same multipath.
 
  92         unsigned char sense[SCSI_SENSE_BUFFERSIZE];
 
  93         unsigned int senselen;
 
 103          * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this
 
 108          * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this
 
 114 static inline struct clariion_dh_data
 
 115                         *get_clariion_data(struct scsi_device *sdev)
 
 117         struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
 
 118         BUG_ON(scsi_dh_data == NULL);
 
 119         return ((struct clariion_dh_data *) scsi_dh_data->buf);
 
 123  * Parse MODE_SELECT cmd reply.
 
 125 static int trespass_endio(struct scsi_device *sdev, char *sense)
 
 127         int err = SCSI_DH_IO;
 
 128         struct scsi_sense_hdr sshdr;
 
 130         if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
 
 131                 sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, "
 
 132                             "0x%2x, 0x%2x while sending CLARiiON trespass "
 
 133                             "command.\n", CLARIION_NAME, sshdr.sense_key,
 
 134                             sshdr.asc, sshdr.ascq);
 
 136                 if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
 
 137                      (sshdr.ascq == 0x00)) {
 
 139                          * Array based copy in progress -- do not send
 
 140                          * mode_select or copy will be aborted mid-stream.
 
 142                         sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in "
 
 143                                     "progress while sending CLARiiON trespass "
 
 144                                     "command.\n", CLARIION_NAME);
 
 145                         err = SCSI_DH_DEV_TEMP_BUSY;
 
 146                 } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
 
 147                             (sshdr.ascq == 0x03)) {
 
 149                          * LUN Not Ready - Manual Intervention Required
 
 150                          * indicates in-progress ucode upgrade (NDU).
 
 152                         sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress "
 
 153                                     "ucode upgrade NDU operation while sending "
 
 154                                     "CLARiiON trespass command.\n", CLARIION_NAME);
 
 155                         err = SCSI_DH_DEV_TEMP_BUSY;
 
 157                         err = SCSI_DH_DEV_FAILED;
 
 159                 sdev_printk(KERN_INFO, sdev,
 
 160                             "%s: failed to send MODE SELECT, no sense available\n",
 
 166 static int parse_sp_info_reply(struct scsi_device *sdev,
 
 167                                struct clariion_dh_data *csdev)
 
 169         int err = SCSI_DH_OK;
 
 171         /* check for in-progress ucode upgrade (NDU) */
 
 172         if (csdev->buffer[48] != 0) {
 
 173                 sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress "
 
 174                             "ucode upgrade NDU operation while finding "
 
 175                             "current active SP.", CLARIION_NAME);
 
 176                 err = SCSI_DH_DEV_TEMP_BUSY;
 
 179         if (csdev->buffer[4] > 2) {
 
 180                 /* Invalid buffer format */
 
 181                 sdev_printk(KERN_NOTICE, sdev,
 
 182                             "%s: invalid VPD page 0xC0 format\n",
 
 187         switch (csdev->buffer[28] & 0x0f) {
 
 189                 sdev_printk(KERN_NOTICE, sdev,
 
 190                             "%s: ALUA failover mode detected\n",
 
 197                 sdev_printk(KERN_WARNING, sdev,
 
 198                             "%s: Invalid failover mode %d\n",
 
 199                             CLARIION_NAME, csdev->buffer[28] & 0x0f);
 
 204         csdev->default_sp = csdev->buffer[5];
 
 205         csdev->lun_state = csdev->buffer[4];
 
 206         csdev->current_sp = csdev->buffer[8];
 
 207         csdev->port = csdev->buffer[7];
 
 213 #define emc_default_str "FC (Legacy)"
 
 215 static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer)
 
 217         unsigned char len = buffer[4] + 5;
 
 218         char *sp_model = NULL;
 
 219         unsigned char sp_len, serial_len;
 
 222                 sdev_printk(KERN_WARNING, sdev,
 
 223                             "%s: Invalid information section length %d\n",
 
 225                 /* Check for old FC arrays */
 
 226                 if (!strncmp(buffer + 8, "DGC", 3)) {
 
 227                         /* Old FC array, not supporting extended information */
 
 228                         sp_model = emc_default_str;
 
 234          * Parse extended information for SP model number
 
 236         serial_len = buffer[160];
 
 237         if (serial_len == 0 || serial_len + 161 > len) {
 
 238                 sdev_printk(KERN_WARNING, sdev,
 
 239                             "%s: Invalid array serial number length %d\n",
 
 240                             CLARIION_NAME, serial_len);
 
 244         if (sp_len == 0 || serial_len + sp_len + 161 > len) {
 
 245                 sdev_printk(KERN_WARNING, sdev,
 
 246                             "%s: Invalid model number length %d\n",
 
 247                             CLARIION_NAME, sp_len);
 
 250         sp_model = &buffer[serial_len + 161];
 
 251         /* Strip whitespace at the end */
 
 252         while (sp_len > 1 && sp_model[sp_len - 1] == ' ')
 
 255         sp_model[sp_len] = '\0';
 
 262  * Get block request for REQ_BLOCK_PC command issued to path.  Currently
 
 263  * limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
 
 265  * Uses data and sense buffers in hardware handler context structure and
 
 266  * assumes serial servicing of commands, both issuance and completion.
 
 268 static struct request *get_req(struct scsi_device *sdev, int cmd,
 
 269                                 unsigned char *buffer)
 
 274         rq = blk_get_request(sdev->request_queue,
 
 275                         (cmd == MODE_SELECT) ? WRITE : READ, GFP_NOIO);
 
 277                 sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
 
 281         rq->cmd_len = COMMAND_SIZE(cmd);
 
 286                 len = sizeof(short_trespass);
 
 287                 rq->cmd_flags |= REQ_RW;
 
 291                 len = sizeof(long_trespass);
 
 292                 rq->cmd_flags |= REQ_RW;
 
 296                 len = CLARIION_BUFFER_SIZE;
 
 297                 memset(buffer, 0, len);
 
 305         rq->cmd_type = REQ_TYPE_BLOCK_PC;
 
 306         rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
 
 308         rq->timeout = CLARIION_TIMEOUT;
 
 309         rq->retries = CLARIION_RETRIES;
 
 311         if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) {
 
 319 static int send_inquiry_cmd(struct scsi_device *sdev, int page,
 
 320                             struct clariion_dh_data *csdev)
 
 322         struct request *rq = get_req(sdev, INQUIRY, csdev->buffer);
 
 326                 return SCSI_DH_RES_TEMP_UNAVAIL;
 
 328         rq->sense = csdev->sense;
 
 329         memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
 
 330         rq->sense_len = csdev->senselen = 0;
 
 332         rq->cmd[0] = INQUIRY;
 
 337         err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
 
 339                 sdev_printk(KERN_INFO, sdev,
 
 340                             "%s: failed to send %s INQUIRY: %x\n",
 
 341                             CLARIION_NAME, page?"EVPD":"standard",
 
 343                 csdev->senselen = rq->sense_len;
 
 352 static int send_trespass_cmd(struct scsi_device *sdev,
 
 353                             struct clariion_dh_data *csdev)
 
 356         unsigned char *page22;
 
 359         if (csdev->flags & CLARIION_SHORT_TRESPASS) {
 
 360                 page22 = short_trespass;
 
 361                 if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
 
 362                         /* Set Honor Reservations bit */
 
 364                 len = sizeof(short_trespass);
 
 367                 page22 = long_trespass;
 
 368                 if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
 
 369                         /* Set Honor Reservations bit */
 
 371                 len = sizeof(long_trespass);
 
 372                 cmd = MODE_SELECT_10;
 
 374         BUG_ON((len > CLARIION_BUFFER_SIZE));
 
 375         memcpy(csdev->buffer, page22, len);
 
 377         rq = get_req(sdev, cmd, csdev->buffer);
 
 379                 return SCSI_DH_RES_TEMP_UNAVAIL;
 
 381         rq->sense = csdev->sense;
 
 382         memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
 
 383         rq->sense_len = csdev->senselen = 0;
 
 385         err = blk_execute_rq(sdev->request_queue, NULL, rq, 1);
 
 388                         err = trespass_endio(sdev, csdev->sense);
 
 390                         sdev_printk(KERN_INFO, sdev,
 
 391                                     "%s: failed to send MODE SELECT: %x\n",
 
 392                                     CLARIION_NAME, rq->errors);
 
 401 static int clariion_check_sense(struct scsi_device *sdev,
 
 402                                 struct scsi_sense_hdr *sense_hdr)
 
 404         switch (sense_hdr->sense_key) {
 
 406                 if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
 
 408                          * LUN Not Ready - Manual Intervention Required
 
 409                          * indicates this is a passive path.
 
 411                          * FIXME: However, if this is seen and EVPD C0
 
 412                          * indicates that this is due to a NDU in
 
 413                          * progress, we should set FAIL_PATH too.
 
 414                          * This indicates we might have to do a SCSI
 
 415                          * inquiry in the end_io path. Ugh.
 
 417                          * Can return FAILED only when we want the error
 
 418                          * recovery process to kick in.
 
 422         case ILLEGAL_REQUEST:
 
 423                 if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
 
 425                          * An array based copy is in progress. Do not
 
 426                          * fail the path, do not bypass to another PG,
 
 427                          * do not retry. Fail the IO immediately.
 
 428                          * (Actually this is the same conclusion as in
 
 429                          * the default handler, but lets make sure.)
 
 431                          * Can return FAILED only when we want the error
 
 432                          * recovery process to kick in.
 
 437                 if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
 
 439                          * Unit Attention Code. This is the first IO
 
 440                          * to the new path, so just retry.
 
 442                         return ADD_TO_MLQUEUE;
 
 446         return SCSI_RETURN_NOT_HANDLED;
 
 449 static int clariion_prep_fn(struct scsi_device *sdev, struct request *req)
 
 451         struct clariion_dh_data *h = get_clariion_data(sdev);
 
 452         int ret = BLKPREP_OK;
 
 454         if (h->lun_state != CLARIION_LUN_OWNED) {
 
 456                 req->cmd_flags |= REQ_QUIET;
 
 462 static int clariion_std_inquiry(struct scsi_device *sdev,
 
 463                                 struct clariion_dh_data *csdev)
 
 468         err = send_inquiry_cmd(sdev, 0, csdev);
 
 469         if (err != SCSI_DH_OK && csdev->senselen) {
 
 470                 struct scsi_sense_hdr sshdr;
 
 472                 if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
 
 474                         sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
 
 475                                     "%02x/%02x/%02x\n", CLARIION_NAME,
 
 476                                     sshdr.sense_key, sshdr.asc, sshdr.ascq);
 
 482         sp_model = parse_sp_model(sdev, csdev->buffer);
 
 484                 err = SCSI_DH_DEV_UNSUPP;
 
 489          * FC Series arrays do not support long trespass
 
 491         if (!strlen(sp_model) || !strncmp(sp_model, "FC",2))
 
 492                 csdev->flags |= CLARIION_SHORT_TRESPASS;
 
 494         sdev_printk(KERN_INFO, sdev,
 
 495                     "%s: detected Clariion %s, flags %x\n",
 
 496                     CLARIION_NAME, sp_model, csdev->flags);
 
 501 static int clariion_send_inquiry(struct scsi_device *sdev,
 
 502                                  struct clariion_dh_data *csdev)
 
 504         int err, retry = CLARIION_RETRIES;
 
 507         err = send_inquiry_cmd(sdev, 0xC0, csdev);
 
 508         if (err != SCSI_DH_OK && csdev->senselen) {
 
 509                 struct scsi_sense_hdr sshdr;
 
 511                 err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
 
 516                 err = clariion_check_sense(sdev, &sshdr);
 
 517                 if (retry > 0 && err == ADD_TO_MLQUEUE) {
 
 521                 sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code "
 
 522                             "%02x/%02x/%02x\n", CLARIION_NAME,
 
 523                               sshdr.sense_key, sshdr.asc, sshdr.ascq);
 
 526                 err = parse_sp_info_reply(sdev, csdev);
 
 531 static int clariion_activate(struct scsi_device *sdev)
 
 533         struct clariion_dh_data *csdev = get_clariion_data(sdev);
 
 536         result = clariion_send_inquiry(sdev, csdev);
 
 537         if (result != SCSI_DH_OK)
 
 540         if (csdev->lun_state == CLARIION_LUN_OWNED)
 
 543         result = send_trespass_cmd(sdev, csdev);
 
 544         if (result != SCSI_DH_OK)
 
 546         sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n",
 
 548                     csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" );
 
 551         result = clariion_send_inquiry(sdev, csdev);
 
 552         if (result != SCSI_DH_OK)
 
 556         sdev_printk(KERN_INFO, sdev,
 
 557                     "%s: at SP %c Port %d (%s, default SP %c)\n",
 
 558                     CLARIION_NAME, csdev->current_sp + 'A',
 
 559                     csdev->port, lun_state[csdev->lun_state],
 
 560                     csdev->default_sp + 'A');
 
 565 static const struct scsi_dh_devlist clariion_dev_list[] = {
 
 572 static int clariion_bus_attach(struct scsi_device *sdev);
 
 573 static void clariion_bus_detach(struct scsi_device *sdev);
 
 575 static struct scsi_device_handler clariion_dh = {
 
 576         .name           = CLARIION_NAME,
 
 577         .module         = THIS_MODULE,
 
 578         .devlist        = clariion_dev_list,
 
 579         .attach         = clariion_bus_attach,
 
 580         .detach         = clariion_bus_detach,
 
 581         .check_sense    = clariion_check_sense,
 
 582         .activate       = clariion_activate,
 
 583         .prep_fn        = clariion_prep_fn,
 
 587  * TODO: need some interface so we can set trespass values
 
 589 static int clariion_bus_attach(struct scsi_device *sdev)
 
 591         struct scsi_dh_data *scsi_dh_data;
 
 592         struct clariion_dh_data *h;
 
 596         scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
 
 597                                + sizeof(*h) , GFP_KERNEL);
 
 599                 sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
 
 604         scsi_dh_data->scsi_dh = &clariion_dh;
 
 605         h = (struct clariion_dh_data *) scsi_dh_data->buf;
 
 606         h->lun_state = CLARIION_LUN_UNINITIALIZED;
 
 607         h->default_sp = CLARIION_UNBOUND_LU;
 
 608         h->current_sp = CLARIION_UNBOUND_LU;
 
 610         err = clariion_std_inquiry(sdev, h);
 
 611         if (err != SCSI_DH_OK)
 
 614         err = clariion_send_inquiry(sdev, h);
 
 615         if (err != SCSI_DH_OK)
 
 618         if (!try_module_get(THIS_MODULE))
 
 621         spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
 
 622         sdev->scsi_dh_data = scsi_dh_data;
 
 623         spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
 
 625         sdev_printk(KERN_INFO, sdev,
 
 626                     "%s: connected to SP %c Port %d (%s, default SP %c)\n",
 
 627                     CLARIION_NAME, h->current_sp + 'A',
 
 628                     h->port, lun_state[h->lun_state],
 
 629                     h->default_sp + 'A');
 
 635         sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
 
 640 static void clariion_bus_detach(struct scsi_device *sdev)
 
 642         struct scsi_dh_data *scsi_dh_data;
 
 645         spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
 
 646         scsi_dh_data = sdev->scsi_dh_data;
 
 647         sdev->scsi_dh_data = NULL;
 
 648         spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
 
 650         sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n",
 
 654         module_put(THIS_MODULE);
 
 657 static int __init clariion_init(void)
 
 661         r = scsi_register_device_handler(&clariion_dh);
 
 663                 printk(KERN_ERR "%s: Failed to register scsi device handler.",
 
 668 static void __exit clariion_exit(void)
 
 670         scsi_unregister_device_handler(&clariion_dh);
 
 673 module_init(clariion_init);
 
 674 module_exit(clariion_exit);
 
 676 MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
 
 677 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
 
 678 MODULE_LICENSE("GPL");