549 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 Christie9 */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 0x2220#define CLARIION_BUFFER_SIZE 0xFC21#define CLARIION_TIMEOUT (60 * HZ)22#define CLARIION_RETRIES 323#define CLARIION_UNBOUND_LU -124#define CLARIION_SP_A 025#define CLARIION_SP_B 126 27/* Flags */28#define CLARIION_SHORT_TRESPASS 129#define CLARIION_HONOR_RESERVATIONS 230 31/* LUN states */32#define CLARIION_LUN_UNINITIALIZED -133#define CLARIION_LUN_UNBOUND 034#define CLARIION_LUN_BOUND 135#define CLARIION_LUN_OWNED 236 37static 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 46static 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 54static const char * lun_state[] =55{56 "not bound",57 "bound",58 "owned",59};60 61struct clariion_dh_data {62 /*63 * Flags:64 * CLARIION_SHORT_TRESPASS65 * Use short trespass command (FC-series) or the long version66 * (default for AX/CX CLARiiON arrays).67 *68 * CLARIION_HONOR_RESERVATIONS69 * Whether or not (default) to honor SCSI reservations when70 * 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 state79 */80 int lun_state;81 /*82 * SP Port number83 */84 int port;85 /*86 * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this87 * path's mapped LUN88 */89 int default_sp;90 /*91 * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this92 * path's mapped LUN93 */94 int current_sp;95};96 97/*98 * Parse MODE_SELECT cmd reply.99 */100static 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 send114 * 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 Required124 * 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 } else131 err = SCSI_DH_DEV_FAILED;132 return err;133}134 135static 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 else180 sdev->access_state = SCSI_ACCESS_STATE_STANDBY;181 if (csdev->default_sp == csdev->current_sp)182 sdev->access_state |= SCSI_ACCESS_STATE_PREFERRED;183out:184 return err;185}186 187#define emc_default_str "FC (Legacy)"188 189static 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 number209 */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 231out:232 return sp_model;233}234 235static 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 blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |243 REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;244 const struct scsi_exec_args exec_args = {245 .sshdr = &sshdr,246 };247 248 if (csdev->flags & CLARIION_SHORT_TRESPASS) {249 page22 = short_trespass;250 if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))251 /* Set Honor Reservations bit */252 page22[6] |= 0x80;253 len = sizeof(short_trespass);254 cdb[0] = MODE_SELECT;255 cdb[1] = 0x10;256 cdb[4] = len;257 } else {258 page22 = long_trespass;259 if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS))260 /* Set Honor Reservations bit */261 page22[10] |= 0x80;262 len = sizeof(long_trespass);263 cdb[0] = MODE_SELECT_10;264 cdb[8] = len;265 }266 BUG_ON((len > CLARIION_BUFFER_SIZE));267 memcpy(csdev->buffer, page22, len);268 269 err = scsi_execute_cmd(sdev, cdb, opf, csdev->buffer, len,270 CLARIION_TIMEOUT * HZ, CLARIION_RETRIES,271 &exec_args);272 if (err) {273 if (scsi_sense_valid(&sshdr))274 res = trespass_endio(sdev, &sshdr);275 else {276 sdev_printk(KERN_INFO, sdev,277 "%s: failed to send MODE SELECT: %x\n",278 CLARIION_NAME, err);279 res = SCSI_DH_IO;280 }281 }282 283 return res;284}285 286static enum scsi_disposition clariion_check_sense(struct scsi_device *sdev,287 struct scsi_sense_hdr *sense_hdr)288{289 switch (sense_hdr->sense_key) {290 case NOT_READY:291 if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)292 /*293 * LUN Not Ready - Manual Intervention Required294 * indicates this is a passive path.295 *296 * FIXME: However, if this is seen and EVPD C0297 * indicates that this is due to a NDU in298 * progress, we should set FAIL_PATH too.299 * This indicates we might have to do a SCSI300 * inquiry in the end_io path. Ugh.301 *302 * Can return FAILED only when we want the error303 * recovery process to kick in.304 */305 return SUCCESS;306 break;307 case ILLEGAL_REQUEST:308 if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)309 /*310 * An array based copy is in progress. Do not311 * fail the path, do not bypass to another PG,312 * do not retry. Fail the IO immediately.313 * (Actually this is the same conclusion as in314 * the default handler, but lets make sure.)315 *316 * Can return FAILED only when we want the error317 * recovery process to kick in.318 */319 return SUCCESS;320 break;321 case UNIT_ATTENTION:322 if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)323 /*324 * Unit Attention Code. This is the first IO325 * to the new path, so just retry.326 */327 return ADD_TO_MLQUEUE;328 break;329 }330 331 return SCSI_RETURN_NOT_HANDLED;332}333 334static blk_status_t clariion_prep_fn(struct scsi_device *sdev,335 struct request *req)336{337 struct clariion_dh_data *h = sdev->handler_data;338 339 if (h->lun_state != CLARIION_LUN_OWNED) {340 req->rq_flags |= RQF_QUIET;341 return BLK_STS_IOERR;342 }343 344 return BLK_STS_OK;345}346 347static int clariion_std_inquiry(struct scsi_device *sdev,348 struct clariion_dh_data *csdev)349{350 int err = SCSI_DH_OK;351 char *sp_model;352 353 sp_model = parse_sp_model(sdev, sdev->inquiry);354 if (!sp_model) {355 err = SCSI_DH_DEV_UNSUPP;356 goto out;357 }358 359 /*360 * FC Series arrays do not support long trespass361 */362 if (!strlen(sp_model) || !strncmp(sp_model, "FC",2))363 csdev->flags |= CLARIION_SHORT_TRESPASS;364 365 sdev_printk(KERN_INFO, sdev,366 "%s: detected Clariion %s, flags %x\n",367 CLARIION_NAME, sp_model, csdev->flags);368out:369 return err;370}371 372static int clariion_send_inquiry(struct scsi_device *sdev,373 struct clariion_dh_data *csdev)374{375 int err = SCSI_DH_IO;376 377 if (!scsi_get_vpd_page(sdev, 0xC0, csdev->buffer,378 CLARIION_BUFFER_SIZE))379 err = parse_sp_info_reply(sdev, csdev);380 381 return err;382}383 384static int clariion_activate(struct scsi_device *sdev,385 activate_complete fn, void *data)386{387 struct clariion_dh_data *csdev = sdev->handler_data;388 int result;389 390 result = clariion_send_inquiry(sdev, csdev);391 if (result != SCSI_DH_OK)392 goto done;393 394 if (csdev->lun_state == CLARIION_LUN_OWNED)395 goto done;396 397 result = send_trespass_cmd(sdev, csdev);398 if (result != SCSI_DH_OK)399 goto done;400 sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n",401 CLARIION_NAME,402 csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" );403 404 /* Update status */405 result = clariion_send_inquiry(sdev, csdev);406 if (result != SCSI_DH_OK)407 goto done;408 409done:410 sdev_printk(KERN_INFO, sdev,411 "%s: at SP %c Port %d (%s, default SP %c)\n",412 CLARIION_NAME, csdev->current_sp + 'A',413 csdev->port, lun_state[csdev->lun_state],414 csdev->default_sp + 'A');415 416 if (fn)417 fn(data, result);418 return 0;419}420/*421 * params - parameters in the following format422 * "no_of_params\0param1\0param2\0param3\0...\0"423 * for example, string for 2 parameters with value 10 and 21424 * is specified as "2\010\021\0".425 */426static int clariion_set_params(struct scsi_device *sdev, const char *params)427{428 struct clariion_dh_data *csdev = sdev->handler_data;429 unsigned int hr = 0, st = 0, argc;430 const char *p = params;431 int result = SCSI_DH_OK;432 433 if ((sscanf(params, "%u", &argc) != 1) || (argc != 2))434 return -EINVAL;435 436 while (*p++)437 ;438 if ((sscanf(p, "%u", &st) != 1) || (st > 1))439 return -EINVAL;440 441 while (*p++)442 ;443 if ((sscanf(p, "%u", &hr) != 1) || (hr > 1))444 return -EINVAL;445 446 if (st)447 csdev->flags |= CLARIION_SHORT_TRESPASS;448 else449 csdev->flags &= ~CLARIION_SHORT_TRESPASS;450 451 if (hr)452 csdev->flags |= CLARIION_HONOR_RESERVATIONS;453 else454 csdev->flags &= ~CLARIION_HONOR_RESERVATIONS;455 456 /*457 * If this path is owned, we have to send a trespass command458 * with the new parameters. If not, simply return. Next trespass459 * command would use the parameters.460 */461 if (csdev->lun_state != CLARIION_LUN_OWNED)462 goto done;463 464 csdev->lun_state = CLARIION_LUN_UNINITIALIZED;465 result = send_trespass_cmd(sdev, csdev);466 if (result != SCSI_DH_OK)467 goto done;468 469 /* Update status */470 result = clariion_send_inquiry(sdev, csdev);471 472done:473 return result;474}475 476static int clariion_bus_attach(struct scsi_device *sdev)477{478 struct clariion_dh_data *h;479 int err;480 481 h = kzalloc(sizeof(*h) , GFP_KERNEL);482 if (!h)483 return SCSI_DH_NOMEM;484 h->lun_state = CLARIION_LUN_UNINITIALIZED;485 h->default_sp = CLARIION_UNBOUND_LU;486 h->current_sp = CLARIION_UNBOUND_LU;487 488 err = clariion_std_inquiry(sdev, h);489 if (err != SCSI_DH_OK)490 goto failed;491 492 err = clariion_send_inquiry(sdev, h);493 if (err != SCSI_DH_OK)494 goto failed;495 496 sdev_printk(KERN_INFO, sdev,497 "%s: connected to SP %c Port %d (%s, default SP %c)\n",498 CLARIION_NAME, h->current_sp + 'A',499 h->port, lun_state[h->lun_state],500 h->default_sp + 'A');501 502 sdev->handler_data = h;503 return SCSI_DH_OK;504 505failed:506 kfree(h);507 return err;508}509 510static void clariion_bus_detach(struct scsi_device *sdev)511{512 kfree(sdev->handler_data);513 sdev->handler_data = NULL;514}515 516static struct scsi_device_handler clariion_dh = {517 .name = CLARIION_NAME,518 .module = THIS_MODULE,519 .attach = clariion_bus_attach,520 .detach = clariion_bus_detach,521 .check_sense = clariion_check_sense,522 .activate = clariion_activate,523 .prep_fn = clariion_prep_fn,524 .set_params = clariion_set_params,525};526 527static int __init clariion_init(void)528{529 int r;530 531 r = scsi_register_device_handler(&clariion_dh);532 if (r != 0)533 printk(KERN_ERR "%s: Failed to register scsi device handler.",534 CLARIION_NAME);535 return r;536}537 538static void __exit clariion_exit(void)539{540 scsi_unregister_device_handler(&clariion_dh);541}542 543module_init(clariion_init);544module_exit(clariion_exit);545 546MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");547MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");548MODULE_LICENSE("GPL");549