root/drivers/scsi/device_handler/scsi_dh_emc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. trespass_endio
  2. parse_sp_info_reply
  3. parse_sp_model
  4. send_trespass_cmd
  5. clariion_check_sense
  6. clariion_prep_fn
  7. clariion_std_inquiry
  8. clariion_send_inquiry
  9. clariion_activate
  10. clariion_set_params
  11. clariion_bus_attach
  12. clariion_bus_detach
  13. clariion_init
  14. clariion_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Target driver for EMC CLARiiON AX/CX-series hardware.
   4  * Based on code from Lars Marowsky-Bree <lmb@suse.de>
   5  * and Ed Goggin <egoggin@emc.com>.
   6  *
   7  * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
   8  * Copyright (C) 2006 Mike Christie
   9  */
  10 #include <linux/slab.h>
  11 #include <linux/module.h>
  12 #include <scsi/scsi.h>
  13 #include <scsi/scsi_eh.h>
  14 #include <scsi/scsi_dh.h>
  15 #include <scsi/scsi_device.h>
  16 
  17 #define CLARIION_NAME                   "emc"
  18 
  19 #define CLARIION_TRESPASS_PAGE          0x22
  20 #define CLARIION_BUFFER_SIZE            0xFC
  21 #define CLARIION_TIMEOUT                (60 * HZ)
  22 #define CLARIION_RETRIES                3
  23 #define CLARIION_UNBOUND_LU             -1
  24 #define CLARIION_SP_A                   0
  25 #define CLARIION_SP_B                   1
  26 
  27 /* Flags */
  28 #define CLARIION_SHORT_TRESPASS         1
  29 #define CLARIION_HONOR_RESERVATIONS     2
  30 
  31 /* LUN states */
  32 #define CLARIION_LUN_UNINITIALIZED      -1
  33 #define CLARIION_LUN_UNBOUND            0
  34 #define CLARIION_LUN_BOUND              1
  35 #define CLARIION_LUN_OWNED              2
  36 
  37 static unsigned char long_trespass[] = {
  38         0, 0, 0, 0, 0, 0, 0, 0,
  39         CLARIION_TRESPASS_PAGE, /* Page code */
  40         0x09,                   /* Page length - 2 */
  41         0x01,                   /* Trespass code */
  42         0xff, 0xff,             /* Trespass target */
  43         0, 0, 0, 0, 0, 0        /* Reserved bytes / unknown */
  44 };
  45 
  46 static unsigned char short_trespass[] = {
  47         0, 0, 0, 0,
  48         CLARIION_TRESPASS_PAGE, /* Page code */
  49         0x02,                   /* Page length - 2 */
  50         0x01,                   /* Trespass code */
  51         0xff,                   /* Trespass target */
  52 };
  53 
  54 static const char * lun_state[] =
  55 {
  56     "not bound",
  57     "bound",
  58     "owned",
  59 };
  60 
  61 struct clariion_dh_data {
  62         /*
  63          * Flags:
  64          *  CLARIION_SHORT_TRESPASS
  65          * Use short trespass command (FC-series) or the long version
  66          * (default for AX/CX CLARiiON arrays).
  67          *
  68          *  CLARIION_HONOR_RESERVATIONS
  69          * Whether or not (default) to honor SCSI reservations when
  70          * initiating a switch-over.
  71          */
  72         unsigned flags;
  73         /*
  74          * I/O buffer for both MODE_SELECT and INQUIRY commands.
  75          */
  76         unsigned char buffer[CLARIION_BUFFER_SIZE];
  77         /*
  78          * LUN state
  79          */
  80         int lun_state;
  81         /*
  82          * SP Port number
  83          */
  84         int port;
  85         /*
  86          * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this
  87          * path's mapped LUN
  88          */
  89         int default_sp;
  90         /*
  91          * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this
  92          * path's mapped LUN
  93          */
  94         int current_sp;
  95 };
  96 
  97 /*
  98  * Parse MODE_SELECT cmd reply.
  99  */
 100 static int trespass_endio(struct scsi_device *sdev,
 101                           struct scsi_sense_hdr *sshdr)
 102 {
 103         int err = SCSI_DH_IO;
 104 
 105         sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, "
 106                     "0x%2x, 0x%2x while sending CLARiiON trespass "
 107                     "command.\n", CLARIION_NAME, sshdr->sense_key,
 108                     sshdr->asc, sshdr->ascq);
 109 
 110         if (sshdr->sense_key == 0x05 && sshdr->asc == 0x04 &&
 111             sshdr->ascq == 0x00) {
 112                 /*
 113                  * Array based copy in progress -- do not send
 114                  * mode_select or copy will be aborted mid-stream.
 115                  */
 116                 sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in "
 117                             "progress while sending CLARiiON trespass "
 118                             "command.\n", CLARIION_NAME);
 119                 err = SCSI_DH_DEV_TEMP_BUSY;
 120         } else if (sshdr->sense_key == 0x02 && sshdr->asc == 0x04 &&
 121                    sshdr->ascq == 0x03) {
 122                 /*
 123                  * LUN Not Ready - Manual Intervention Required
 124                  * indicates in-progress ucode upgrade (NDU).
 125                  */
 126                 sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress "
 127                             "ucode upgrade NDU operation while sending "
 128                             "CLARiiON trespass command.\n", CLARIION_NAME);
 129                 err = SCSI_DH_DEV_TEMP_BUSY;
 130         } else
 131                 err = SCSI_DH_DEV_FAILED;
 132         return err;
 133 }
 134 
 135 static int parse_sp_info_reply(struct scsi_device *sdev,
 136                                struct clariion_dh_data *csdev)
 137 {
 138         int err = SCSI_DH_OK;
 139 
 140         /* check for in-progress ucode upgrade (NDU) */
 141         if (csdev->buffer[48] != 0) {
 142                 sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress "
 143                             "ucode upgrade NDU operation while finding "
 144                             "current active SP.", CLARIION_NAME);
 145                 err = SCSI_DH_DEV_TEMP_BUSY;
 146                 goto out;
 147         }
 148         if (csdev->buffer[4] > 2) {
 149                 /* Invalid buffer format */
 150                 sdev_printk(KERN_NOTICE, sdev,
 151                             "%s: invalid VPD page 0xC0 format\n",
 152                             CLARIION_NAME);
 153                 err = SCSI_DH_NOSYS;
 154                 goto out;
 155         }
 156         switch (csdev->buffer[28] & 0x0f) {
 157         case 6:
 158                 sdev_printk(KERN_NOTICE, sdev,
 159                             "%s: ALUA failover mode detected\n",
 160                             CLARIION_NAME);
 161                 break;
 162         case 4:
 163                 /* Linux failover */
 164                 break;
 165         default:
 166                 sdev_printk(KERN_WARNING, sdev,
 167                             "%s: Invalid failover mode %d\n",
 168                             CLARIION_NAME, csdev->buffer[28] & 0x0f);
 169                 err = SCSI_DH_NOSYS;
 170                 goto out;
 171         }
 172 
 173         csdev->default_sp = csdev->buffer[5];
 174         csdev->lun_state = csdev->buffer[4];
 175         csdev->current_sp = csdev->buffer[8];
 176         csdev->port = csdev->buffer[7];
 177         if (csdev->lun_state == CLARIION_LUN_OWNED)
 178                 sdev->access_state = SCSI_ACCESS_STATE_OPTIMAL;
 179         else
 180                 sdev->access_state = SCSI_ACCESS_STATE_STANDBY;
 181         if (csdev->default_sp == csdev->current_sp)
 182                 sdev->access_state |= SCSI_ACCESS_STATE_PREFERRED;
 183 out:
 184         return err;
 185 }
 186 
 187 #define emc_default_str "FC (Legacy)"
 188 
 189 static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer)
 190 {
 191         unsigned char len = buffer[4] + 5;
 192         char *sp_model = NULL;
 193         unsigned char sp_len, serial_len;
 194 
 195         if (len < 160) {
 196                 sdev_printk(KERN_WARNING, sdev,
 197                             "%s: Invalid information section length %d\n",
 198                             CLARIION_NAME, len);
 199                 /* Check for old FC arrays */
 200                 if (!strncmp(buffer + 8, "DGC", 3)) {
 201                         /* Old FC array, not supporting extended information */
 202                         sp_model = emc_default_str;
 203                 }
 204                 goto out;
 205         }
 206 
 207         /*
 208          * Parse extended information for SP model number
 209          */
 210         serial_len = buffer[160];
 211         if (serial_len == 0 || serial_len + 161 > len) {
 212                 sdev_printk(KERN_WARNING, sdev,
 213                             "%s: Invalid array serial number length %d\n",
 214                             CLARIION_NAME, serial_len);
 215                 goto out;
 216         }
 217         sp_len = buffer[99];
 218         if (sp_len == 0 || serial_len + sp_len + 161 > len) {
 219                 sdev_printk(KERN_WARNING, sdev,
 220                             "%s: Invalid model number length %d\n",
 221                             CLARIION_NAME, sp_len);
 222                 goto out;
 223         }
 224         sp_model = &buffer[serial_len + 161];
 225         /* Strip whitespace at the end */
 226         while (sp_len > 1 && sp_model[sp_len - 1] == ' ')
 227                 sp_len--;
 228 
 229         sp_model[sp_len] = '\0';
 230 
 231 out:
 232         return sp_model;
 233 }
 234 
 235 static int send_trespass_cmd(struct scsi_device *sdev,
 236                             struct clariion_dh_data *csdev)
 237 {
 238         unsigned char *page22;
 239         unsigned char cdb[MAX_COMMAND_SIZE];
 240         int err, res = SCSI_DH_OK, len;
 241         struct scsi_sense_hdr sshdr;
 242         u64 req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
 243                 REQ_FAILFAST_DRIVER;
 244 
 245         if (csdev->flags & CLARIION_SHORT_TRESPASS) {
 246                 page22 = short_trespass;
 247                 if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
 248                         /* Set Honor Reservations bit */
 249                         page22[6] |= 0x80;
 250                 len = sizeof(short_trespass);
 251                 cdb[0] = MODE_SELECT;
 252                 cdb[1] = 0x10;
 253                 cdb[4] = len;
 254         } else {
 255                 page22 = long_trespass;
 256                 if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))
 257                         /* Set Honor Reservations bit */
 258                         page22[10] |= 0x80;
 259                 len = sizeof(long_trespass);
 260                 cdb[0] = MODE_SELECT_10;
 261                 cdb[8] = len;
 262         }
 263         BUG_ON((len > CLARIION_BUFFER_SIZE));
 264         memcpy(csdev->buffer, page22, len);
 265 
 266         err = scsi_execute(sdev, cdb, DMA_TO_DEVICE, csdev->buffer, len, NULL,
 267                         &sshdr, CLARIION_TIMEOUT * HZ, CLARIION_RETRIES,
 268                         req_flags, 0, NULL);
 269         if (err) {
 270                 if (scsi_sense_valid(&sshdr))
 271                         res = trespass_endio(sdev, &sshdr);
 272                 else {
 273                         sdev_printk(KERN_INFO, sdev,
 274                                     "%s: failed to send MODE SELECT: %x\n",
 275                                     CLARIION_NAME, err);
 276                         res = SCSI_DH_IO;
 277                 }
 278         }
 279 
 280         return res;
 281 }
 282 
 283 static int clariion_check_sense(struct scsi_device *sdev,
 284                                 struct scsi_sense_hdr *sense_hdr)
 285 {
 286         switch (sense_hdr->sense_key) {
 287         case NOT_READY:
 288                 if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
 289                         /*
 290                          * LUN Not Ready - Manual Intervention Required
 291                          * indicates this is a passive path.
 292                          *
 293                          * FIXME: However, if this is seen and EVPD C0
 294                          * indicates that this is due to a NDU in
 295                          * progress, we should set FAIL_PATH too.
 296                          * This indicates we might have to do a SCSI
 297                          * inquiry in the end_io path. Ugh.
 298                          *
 299                          * Can return FAILED only when we want the error
 300                          * recovery process to kick in.
 301                          */
 302                         return SUCCESS;
 303                 break;
 304         case ILLEGAL_REQUEST:
 305                 if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
 306                         /*
 307                          * An array based copy is in progress. Do not
 308                          * fail the path, do not bypass to another PG,
 309                          * do not retry. Fail the IO immediately.
 310                          * (Actually this is the same conclusion as in
 311                          * the default handler, but lets make sure.)
 312                          *
 313                          * Can return FAILED only when we want the error
 314                          * recovery process to kick in.
 315                          */
 316                         return SUCCESS;
 317                 break;
 318         case UNIT_ATTENTION:
 319                 if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
 320                         /*
 321                          * Unit Attention Code. This is the first IO
 322                          * to the new path, so just retry.
 323                          */
 324                         return ADD_TO_MLQUEUE;
 325                 break;
 326         }
 327 
 328         return SCSI_RETURN_NOT_HANDLED;
 329 }
 330 
 331 static blk_status_t clariion_prep_fn(struct scsi_device *sdev,
 332                 struct request *req)
 333 {
 334         struct clariion_dh_data *h = sdev->handler_data;
 335 
 336         if (h->lun_state != CLARIION_LUN_OWNED) {
 337                 req->rq_flags |= RQF_QUIET;
 338                 return BLK_STS_IOERR;
 339         }
 340 
 341         return BLK_STS_OK;
 342 }
 343 
 344 static int clariion_std_inquiry(struct scsi_device *sdev,
 345                                 struct clariion_dh_data *csdev)
 346 {
 347         int err = SCSI_DH_OK;
 348         char *sp_model;
 349 
 350         sp_model = parse_sp_model(sdev, sdev->inquiry);
 351         if (!sp_model) {
 352                 err = SCSI_DH_DEV_UNSUPP;
 353                 goto out;
 354         }
 355 
 356         /*
 357          * FC Series arrays do not support long trespass
 358          */
 359         if (!strlen(sp_model) || !strncmp(sp_model, "FC",2))
 360                 csdev->flags |= CLARIION_SHORT_TRESPASS;
 361 
 362         sdev_printk(KERN_INFO, sdev,
 363                     "%s: detected Clariion %s, flags %x\n",
 364                     CLARIION_NAME, sp_model, csdev->flags);
 365 out:
 366         return err;
 367 }
 368 
 369 static int clariion_send_inquiry(struct scsi_device *sdev,
 370                                  struct clariion_dh_data *csdev)
 371 {
 372         int err = SCSI_DH_IO;
 373 
 374         if (!scsi_get_vpd_page(sdev, 0xC0, csdev->buffer,
 375                                CLARIION_BUFFER_SIZE))
 376                 err = parse_sp_info_reply(sdev, csdev);
 377 
 378         return err;
 379 }
 380 
 381 static int clariion_activate(struct scsi_device *sdev,
 382                                 activate_complete fn, void *data)
 383 {
 384         struct clariion_dh_data *csdev = sdev->handler_data;
 385         int result;
 386 
 387         result = clariion_send_inquiry(sdev, csdev);
 388         if (result != SCSI_DH_OK)
 389                 goto done;
 390 
 391         if (csdev->lun_state == CLARIION_LUN_OWNED)
 392                 goto done;
 393 
 394         result = send_trespass_cmd(sdev, csdev);
 395         if (result != SCSI_DH_OK)
 396                 goto done;
 397         sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n",
 398                     CLARIION_NAME,
 399                     csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" );
 400 
 401         /* Update status */
 402         result = clariion_send_inquiry(sdev, csdev);
 403         if (result != SCSI_DH_OK)
 404                 goto done;
 405 
 406 done:
 407         sdev_printk(KERN_INFO, sdev,
 408                     "%s: at SP %c Port %d (%s, default SP %c)\n",
 409                     CLARIION_NAME, csdev->current_sp + 'A',
 410                     csdev->port, lun_state[csdev->lun_state],
 411                     csdev->default_sp + 'A');
 412 
 413         if (fn)
 414                 fn(data, result);
 415         return 0;
 416 }
 417 /*
 418  * params - parameters in the following format
 419  *      "no_of_params\0param1\0param2\0param3\0...\0"
 420  *      for example, string for 2 parameters with value 10 and 21
 421  *      is specified as "2\010\021\0".
 422  */
 423 static int clariion_set_params(struct scsi_device *sdev, const char *params)
 424 {
 425         struct clariion_dh_data *csdev = sdev->handler_data;
 426         unsigned int hr = 0, st = 0, argc;
 427         const char *p = params;
 428         int result = SCSI_DH_OK;
 429 
 430         if ((sscanf(params, "%u", &argc) != 1) || (argc != 2))
 431                 return -EINVAL;
 432 
 433         while (*p++)
 434                 ;
 435         if ((sscanf(p, "%u", &st) != 1) || (st > 1))
 436                 return -EINVAL;
 437 
 438         while (*p++)
 439                 ;
 440         if ((sscanf(p, "%u", &hr) != 1) || (hr > 1))
 441                 return -EINVAL;
 442 
 443         if (st)
 444                 csdev->flags |= CLARIION_SHORT_TRESPASS;
 445         else
 446                 csdev->flags &= ~CLARIION_SHORT_TRESPASS;
 447 
 448         if (hr)
 449                 csdev->flags |= CLARIION_HONOR_RESERVATIONS;
 450         else
 451                 csdev->flags &= ~CLARIION_HONOR_RESERVATIONS;
 452 
 453         /*
 454          * If this path is owned, we have to send a trespass command
 455          * with the new parameters. If not, simply return. Next trespass
 456          * command would use the parameters.
 457          */
 458         if (csdev->lun_state != CLARIION_LUN_OWNED)
 459                 goto done;
 460 
 461         csdev->lun_state = CLARIION_LUN_UNINITIALIZED;
 462         result = send_trespass_cmd(sdev, csdev);
 463         if (result != SCSI_DH_OK)
 464                 goto done;
 465 
 466         /* Update status */
 467         result = clariion_send_inquiry(sdev, csdev);
 468 
 469 done:
 470         return result;
 471 }
 472 
 473 static int clariion_bus_attach(struct scsi_device *sdev)
 474 {
 475         struct clariion_dh_data *h;
 476         int err;
 477 
 478         h = kzalloc(sizeof(*h) , GFP_KERNEL);
 479         if (!h)
 480                 return SCSI_DH_NOMEM;
 481         h->lun_state = CLARIION_LUN_UNINITIALIZED;
 482         h->default_sp = CLARIION_UNBOUND_LU;
 483         h->current_sp = CLARIION_UNBOUND_LU;
 484 
 485         err = clariion_std_inquiry(sdev, h);
 486         if (err != SCSI_DH_OK)
 487                 goto failed;
 488 
 489         err = clariion_send_inquiry(sdev, h);
 490         if (err != SCSI_DH_OK)
 491                 goto failed;
 492 
 493         sdev_printk(KERN_INFO, sdev,
 494                     "%s: connected to SP %c Port %d (%s, default SP %c)\n",
 495                     CLARIION_NAME, h->current_sp + 'A',
 496                     h->port, lun_state[h->lun_state],
 497                     h->default_sp + 'A');
 498 
 499         sdev->handler_data = h;
 500         return SCSI_DH_OK;
 501 
 502 failed:
 503         kfree(h);
 504         return err;
 505 }
 506 
 507 static void clariion_bus_detach(struct scsi_device *sdev)
 508 {
 509         kfree(sdev->handler_data);
 510         sdev->handler_data = NULL;
 511 }
 512 
 513 static struct scsi_device_handler clariion_dh = {
 514         .name           = CLARIION_NAME,
 515         .module         = THIS_MODULE,
 516         .attach         = clariion_bus_attach,
 517         .detach         = clariion_bus_detach,
 518         .check_sense    = clariion_check_sense,
 519         .activate       = clariion_activate,
 520         .prep_fn        = clariion_prep_fn,
 521         .set_params     = clariion_set_params,
 522 };
 523 
 524 static int __init clariion_init(void)
 525 {
 526         int r;
 527 
 528         r = scsi_register_device_handler(&clariion_dh);
 529         if (r != 0)
 530                 printk(KERN_ERR "%s: Failed to register scsi device handler.",
 531                         CLARIION_NAME);
 532         return r;
 533 }
 534 
 535 static void __exit clariion_exit(void)
 536 {
 537         scsi_unregister_device_handler(&clariion_dh);
 538 }
 539 
 540 module_init(clariion_init);
 541 module_exit(clariion_exit);
 542 
 543 MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
 544 MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
 545 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */