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