cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

scsi_dh_emc.c (13657B)


      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
     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_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 */
    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 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
    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	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;
    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 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
    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	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
    283static enum scsi_disposition 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
    331static 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
    344static 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);
    365out:
    366	return err;
    367}
    368
    369static 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
    381static 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
    406done:
    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 */
    423static 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
    469done:
    470	return result;
    471}
    472
    473static 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
    502failed:
    503	kfree(h);
    504	return err;
    505}
    506
    507static void clariion_bus_detach(struct scsi_device *sdev)
    508{
    509	kfree(sdev->handler_data);
    510	sdev->handler_data = NULL;
    511}
    512
    513static 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
    524static 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
    535static void __exit clariion_exit(void)
    536{
    537	scsi_unregister_device_handler(&clariion_dh);
    538}
    539
    540module_init(clariion_init);
    541module_exit(clariion_exit);
    542
    543MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
    544MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
    545MODULE_LICENSE("GPL");