brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · 3cf88db Raw
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