// SPDX-License-Identifier: GPL-2.0-or-later /* * Target driver for EMC CLARiiON AX/CX-series hardware. * Based on code from Lars Marowsky-Bree <lmb@suse.de> * and Ed Goggin <egoggin@emc.com>. * * Copyright (C) 2006 Red Hat, Inc. All rights reserved. * Copyright (C) 2006 Mike Christie
*/ #include <linux/slab.h> #include <linux/module.h> #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> #include <scsi/scsi_device.h>
struct clariion_dh_data { /* * Flags: * CLARIION_SHORT_TRESPASS * Use short trespass command (FC-series) or the long version * (default for AX/CX CLARiiON arrays). * * CLARIION_HONOR_RESERVATIONS * Whether or not (default) to honor SCSI reservations when * initiating a switch-over.
*/ unsigned flags; /* * I/O buffer for both MODE_SELECT and INQUIRY commands.
*/ unsignedchar buffer[CLARIION_BUFFER_SIZE]; /* * LUN state
*/ int lun_state; /* * SP Port number
*/ int port; /* * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this * path's mapped LUN
*/ int default_sp; /* * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this * path's mapped LUN
*/ int current_sp;
};
if (len < 160) {
sdev_printk(KERN_WARNING, sdev, "%s: Invalid information section length %d\n",
CLARIION_NAME, len); /* Check for old FC arrays */ if (!strncmp(buffer + 8, "DGC", 3)) { /* Old FC array, not supporting extended information */
sp_model = emc_default_str;
} goto out;
}
/* * Parse extended information for SP model number
*/
serial_len = buffer[160]; if (serial_len == 0 || serial_len + 161 > len) {
sdev_printk(KERN_WARNING, sdev, "%s: Invalid array serial number length %d\n",
CLARIION_NAME, serial_len); goto out;
}
sp_len = buffer[99]; if (sp_len == 0 || serial_len + sp_len + 161 > len) {
sdev_printk(KERN_WARNING, sdev, "%s: Invalid model number length %d\n",
CLARIION_NAME, sp_len); goto out;
}
sp_model = &buffer[serial_len + 161]; /* Strip whitespace at the end */ while (sp_len > 1 && sp_model[sp_len - 1] == ' ')
sp_len--;
if (csdev->flags & CLARIION_SHORT_TRESPASS) {
page22 = short_trespass; if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) /* Set Honor Reservations bit */
page22[6] |= 0x80;
len = sizeof(short_trespass);
cdb[0] = MODE_SELECT;
cdb[1] = 0x10;
cdb[4] = len;
} else {
page22 = long_trespass; if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) /* Set Honor Reservations bit */
page22[10] |= 0x80;
len = sizeof(long_trespass);
cdb[0] = MODE_SELECT_10;
cdb[8] = len;
}
BUG_ON((len > CLARIION_BUFFER_SIZE));
memcpy(csdev->buffer, page22, len);
err = scsi_execute_cmd(sdev, cdb, opf, csdev->buffer, len,
CLARIION_TIMEOUT * HZ, CLARIION_RETRIES,
&exec_args); if (err) { if (scsi_sense_valid(&sshdr))
res = trespass_endio(sdev, &sshdr); else {
sdev_printk(KERN_INFO, sdev, "%s: failed to send MODE SELECT: %x\n",
CLARIION_NAME, err);
res = SCSI_DH_IO;
}
}
return res;
}
staticenum scsi_disposition clariion_check_sense(struct scsi_device *sdev, struct scsi_sense_hdr *sense_hdr)
{ switch (sense_hdr->sense_key) { case NOT_READY: if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03) /* * LUN Not Ready - Manual Intervention Required * indicates this is a passive path. * * FIXME: However, if this is seen and EVPD C0 * indicates that this is due to a NDU in * progress, we should set FAIL_PATH too. * This indicates we might have to do a SCSI * inquiry in the end_io path. Ugh. * * Can return FAILED only when we want the error * recovery process to kick in.
*/ return SUCCESS; break; case ILLEGAL_REQUEST: if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01) /* * An array based copy is in progress. Do not * fail the path, do not bypass to another PG, * do not retry. Fail the IO immediately. * (Actually this is the same conclusion as in * the default handler, but lets make sure.) * * Can return FAILED only when we want the error * recovery process to kick in.
*/ return SUCCESS; break; case UNIT_ATTENTION: if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) /* * Unit Attention Code. This is the first IO * to the new path, so just retry.
*/ return ADD_TO_MLQUEUE; break;
}
if (fn)
fn(data, result); return 0;
} /* * params - parameters in the following format * "no_of_params\0param1\0param2\0param3\0...\0" * for example, string for 2 parameters with value 10 and 21 * is specified as "2\010\021\0".
*/ staticint clariion_set_params(struct scsi_device *sdev, constchar *params)
{ struct clariion_dh_data *csdev = sdev->handler_data; unsignedint hr = 0, st = 0, argc; constchar *p = params; int result = SCSI_DH_OK;
while (*p++)
; if ((sscanf(p, "%u", &st) != 1) || (st > 1)) return -EINVAL;
while (*p++)
; if ((sscanf(p, "%u", &hr) != 1) || (hr > 1)) return -EINVAL;
if (st)
csdev->flags |= CLARIION_SHORT_TRESPASS; else
csdev->flags &= ~CLARIION_SHORT_TRESPASS;
if (hr)
csdev->flags |= CLARIION_HONOR_RESERVATIONS; else
csdev->flags &= ~CLARIION_HONOR_RESERVATIONS;
/* * If this path is owned, we have to send a trespass command * with the new parameters. If not, simply return. Next trespass * command would use the parameters.
*/ if (csdev->lun_state != CLARIION_LUN_OWNED) goto done;
csdev->lun_state = CLARIION_LUN_UNINITIALIZED;
result = send_trespass_cmd(sdev, csdev); if (result != SCSI_DH_OK) goto done;
/* Update status */
result = clariion_send_inquiry(sdev, csdev);
done: return result;
}
staticint clariion_bus_attach(struct scsi_device *sdev)
{ struct clariion_dh_data *h; int err;
h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) return SCSI_DH_NOMEM;
h->lun_state = CLARIION_LUN_UNINITIALIZED;
h->default_sp = CLARIION_UNBOUND_LU;
h->current_sp = CLARIION_UNBOUND_LU;
err = clariion_std_inquiry(sdev, h); if (err != SCSI_DH_OK) goto failed;
err = clariion_send_inquiry(sdev, h); if (err != SCSI_DH_OK) goto failed;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.