diff options
author | Hannes Reinecke <hare@suse.de> | 2008-07-17 19:53:03 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-26 15:14:51 -0400 |
commit | b6ff1b14cdf4b4cb5403f3af2c3272f7e609a241 (patch) | |
tree | d3a8231c0c4f6c647a8546dcea4659c19ea33949 | |
parent | 4c05ae52fcb0e27a2ee4a16d1f31f8c547fd4886 (diff) |
[SCSI] scsi_dh: Update EMC handler
This patch converts the EMC device handler to use a proper
state machine. We now also parse the extended INQUIRY
information to determine if long trespass commands are
supported. And we're now using the long trespass command
correctly. And finally there's now an check at init time
to refuse to attach to devices not supporting EMC-specific
VPD pages.
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_emc.c | 547 | ||||
-rw-r--r-- | include/scsi/scsi_dh.h | 1 |
2 files changed, 373 insertions, 175 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index bf0a389c52d8..aa46b131b20e 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c | |||
@@ -25,28 +25,31 @@ | |||
25 | #include <scsi/scsi_dh.h> | 25 | #include <scsi/scsi_dh.h> |
26 | #include <scsi/scsi_device.h> | 26 | #include <scsi/scsi_device.h> |
27 | 27 | ||
28 | #define CLARIION_NAME "emc_clariion" | 28 | #define CLARIION_NAME "emc" |
29 | 29 | ||
30 | #define CLARIION_TRESPASS_PAGE 0x22 | 30 | #define CLARIION_TRESPASS_PAGE 0x22 |
31 | #define CLARIION_BUFFER_SIZE 0x80 | 31 | #define CLARIION_BUFFER_SIZE 0xFC |
32 | #define CLARIION_TIMEOUT (60 * HZ) | 32 | #define CLARIION_TIMEOUT (60 * HZ) |
33 | #define CLARIION_RETRIES 3 | 33 | #define CLARIION_RETRIES 3 |
34 | #define CLARIION_UNBOUND_LU -1 | 34 | #define CLARIION_UNBOUND_LU -1 |
35 | #define CLARIION_SP_A 0 | ||
36 | #define CLARIION_SP_B 1 | ||
35 | 37 | ||
36 | static unsigned char long_trespass[] = { | 38 | /* Flags */ |
37 | 0, 0, 0, 0, | 39 | #define CLARIION_SHORT_TRESPASS 1 |
38 | CLARIION_TRESPASS_PAGE, /* Page code */ | 40 | #define CLARIION_HONOR_RESERVATIONS 2 |
39 | 0x09, /* Page length - 2 */ | ||
40 | 0x81, /* Trespass code + Honor reservation bit */ | ||
41 | 0xff, 0xff, /* Trespass target */ | ||
42 | 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ | ||
43 | }; | ||
44 | 41 | ||
45 | static unsigned char long_trespass_hr[] = { | 42 | /* LUN states */ |
46 | 0, 0, 0, 0, | 43 | #define CLARIION_LUN_UNINITIALIZED -1 |
44 | #define CLARIION_LUN_UNBOUND 0 | ||
45 | #define CLARIION_LUN_BOUND 1 | ||
46 | #define CLARIION_LUN_OWNED 2 | ||
47 | |||
48 | static unsigned char long_trespass[] = { | ||
49 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
47 | CLARIION_TRESPASS_PAGE, /* Page code */ | 50 | CLARIION_TRESPASS_PAGE, /* Page code */ |
48 | 0x09, /* Page length - 2 */ | 51 | 0x09, /* Page length - 2 */ |
49 | 0x01, /* Trespass code + Honor reservation bit */ | 52 | 0x01, /* Trespass code */ |
50 | 0xff, 0xff, /* Trespass target */ | 53 | 0xff, 0xff, /* Trespass target */ |
51 | 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ | 54 | 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */ |
52 | }; | 55 | }; |
@@ -55,39 +58,56 @@ static unsigned char short_trespass[] = { | |||
55 | 0, 0, 0, 0, | 58 | 0, 0, 0, 0, |
56 | CLARIION_TRESPASS_PAGE, /* Page code */ | 59 | CLARIION_TRESPASS_PAGE, /* Page code */ |
57 | 0x02, /* Page length - 2 */ | 60 | 0x02, /* Page length - 2 */ |
58 | 0x81, /* Trespass code + Honor reservation bit */ | 61 | 0x01, /* Trespass code */ |
59 | 0xff, /* Trespass target */ | 62 | 0xff, /* Trespass target */ |
60 | }; | 63 | }; |
61 | 64 | ||
62 | static unsigned char short_trespass_hr[] = { | 65 | static const char * lun_state[] = |
63 | 0, 0, 0, 0, | 66 | { |
64 | CLARIION_TRESPASS_PAGE, /* Page code */ | 67 | "not bound", |
65 | 0x02, /* Page length - 2 */ | 68 | "bound", |
66 | 0x01, /* Trespass code + Honor reservation bit */ | 69 | "owned", |
67 | 0xff, /* Trespass target */ | ||
68 | }; | 70 | }; |
69 | 71 | ||
70 | struct clariion_dh_data { | 72 | struct clariion_dh_data { |
71 | /* | 73 | /* |
74 | * Flags: | ||
75 | * CLARIION_SHORT_TRESPASS | ||
72 | * Use short trespass command (FC-series) or the long version | 76 | * Use short trespass command (FC-series) or the long version |
73 | * (default for AX/CX CLARiiON arrays). | 77 | * (default for AX/CX CLARiiON arrays). |
74 | */ | 78 | * |
75 | unsigned short_trespass; | 79 | * CLARIION_HONOR_RESERVATIONS |
76 | /* | ||
77 | * Whether or not (default) to honor SCSI reservations when | 80 | * Whether or not (default) to honor SCSI reservations when |
78 | * initiating a switch-over. | 81 | * initiating a switch-over. |
79 | */ | 82 | */ |
80 | unsigned hr; | 83 | unsigned flags; |
81 | /* I/O buffer for both MODE_SELECT and INQUIRY commands. */ | 84 | /* |
85 | * I/O buffer for both MODE_SELECT and INQUIRY commands. | ||
86 | */ | ||
82 | char buffer[CLARIION_BUFFER_SIZE]; | 87 | char buffer[CLARIION_BUFFER_SIZE]; |
83 | /* | 88 | /* |
84 | * SCSI sense buffer for commands -- assumes serial issuance | 89 | * SCSI sense buffer for commands -- assumes serial issuance |
85 | * and completion sequence of all commands for same multipath. | 90 | * and completion sequence of all commands for same multipath. |
86 | */ | 91 | */ |
87 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; | 92 | unsigned char sense[SCSI_SENSE_BUFFERSIZE]; |
88 | /* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */ | 93 | unsigned int senselen; |
94 | /* | ||
95 | * LUN state | ||
96 | */ | ||
97 | int lun_state; | ||
98 | /* | ||
99 | * SP Port number | ||
100 | */ | ||
101 | int port; | ||
102 | /* | ||
103 | * which SP (A=0,B=1,UNBOUND=-1) is the default SP for this | ||
104 | * path's mapped LUN | ||
105 | */ | ||
89 | int default_sp; | 106 | int default_sp; |
90 | /* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */ | 107 | /* |
108 | * which SP (A=0,B=1,UNBOUND=-1) is the active SP for this | ||
109 | * path's mapped LUN | ||
110 | */ | ||
91 | int current_sp; | 111 | int current_sp; |
92 | }; | 112 | }; |
93 | 113 | ||
@@ -102,19 +122,16 @@ static inline struct clariion_dh_data | |||
102 | /* | 122 | /* |
103 | * Parse MODE_SELECT cmd reply. | 123 | * Parse MODE_SELECT cmd reply. |
104 | */ | 124 | */ |
105 | static int trespass_endio(struct scsi_device *sdev, int result) | 125 | static int trespass_endio(struct scsi_device *sdev, char *sense) |
106 | { | 126 | { |
107 | int err = SCSI_DH_OK; | 127 | int err = SCSI_DH_IO; |
108 | struct scsi_sense_hdr sshdr; | 128 | struct scsi_sense_hdr sshdr; |
109 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
110 | char *sense = csdev->sense; | ||
111 | 129 | ||
112 | if (status_byte(result) == CHECK_CONDITION && | 130 | if (!scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { |
113 | scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) { | 131 | sdev_printk(KERN_ERR, sdev, "%s: Found valid sense data 0x%2x, " |
114 | sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, " | ||
115 | "0x%2x, 0x%2x while sending CLARiiON trespass " | 132 | "0x%2x, 0x%2x while sending CLARiiON trespass " |
116 | "command.\n", sshdr.sense_key, sshdr.asc, | 133 | "command.\n", CLARIION_NAME, sshdr.sense_key, |
117 | sshdr.ascq); | 134 | sshdr.asc, sshdr.ascq); |
118 | 135 | ||
119 | if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) && | 136 | if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) && |
120 | (sshdr.ascq == 0x00)) { | 137 | (sshdr.ascq == 0x00)) { |
@@ -122,9 +139,9 @@ static int trespass_endio(struct scsi_device *sdev, int result) | |||
122 | * Array based copy in progress -- do not send | 139 | * Array based copy in progress -- do not send |
123 | * mode_select or copy will be aborted mid-stream. | 140 | * mode_select or copy will be aborted mid-stream. |
124 | */ | 141 | */ |
125 | sdev_printk(KERN_INFO, sdev, "Array Based Copy in " | 142 | sdev_printk(KERN_INFO, sdev, "%s: Array Based Copy in " |
126 | "progress while sending CLARiiON trespass " | 143 | "progress while sending CLARiiON trespass " |
127 | "command.\n"); | 144 | "command.\n", CLARIION_NAME); |
128 | err = SCSI_DH_DEV_TEMP_BUSY; | 145 | err = SCSI_DH_DEV_TEMP_BUSY; |
129 | } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) && | 146 | } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) && |
130 | (sshdr.ascq == 0x03)) { | 147 | (sshdr.ascq == 0x03)) { |
@@ -132,109 +149,113 @@ static int trespass_endio(struct scsi_device *sdev, int result) | |||
132 | * LUN Not Ready - Manual Intervention Required | 149 | * LUN Not Ready - Manual Intervention Required |
133 | * indicates in-progress ucode upgrade (NDU). | 150 | * indicates in-progress ucode upgrade (NDU). |
134 | */ | 151 | */ |
135 | sdev_printk(KERN_INFO, sdev, "Detected in-progress " | 152 | sdev_printk(KERN_INFO, sdev, "%s: Detected in-progress " |
136 | "ucode upgrade NDU operation while sending " | 153 | "ucode upgrade NDU operation while sending " |
137 | "CLARiiON trespass command.\n"); | 154 | "CLARiiON trespass command.\n", CLARIION_NAME); |
138 | err = SCSI_DH_DEV_TEMP_BUSY; | 155 | err = SCSI_DH_DEV_TEMP_BUSY; |
139 | } else | 156 | } else |
140 | err = SCSI_DH_DEV_FAILED; | 157 | err = SCSI_DH_DEV_FAILED; |
141 | } else if (result) { | 158 | } else { |
142 | sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending " | 159 | sdev_printk(KERN_INFO, sdev, |
143 | "CLARiiON trespass command.\n", result); | 160 | "%s: failed to send MODE SELECT, no sense available\n", |
144 | err = SCSI_DH_IO; | 161 | CLARIION_NAME); |
145 | } | 162 | } |
146 | |||
147 | return err; | 163 | return err; |
148 | } | 164 | } |
149 | 165 | ||
150 | static int parse_sp_info_reply(struct scsi_device *sdev, int result, | 166 | static int parse_sp_info_reply(struct scsi_device *sdev, |
151 | int *default_sp, int *current_sp, int *new_current_sp) | 167 | struct clariion_dh_data *csdev) |
152 | { | 168 | { |
153 | int err = SCSI_DH_OK; | 169 | int err = SCSI_DH_OK; |
154 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
155 | |||
156 | if (result == 0) { | ||
157 | /* check for in-progress ucode upgrade (NDU) */ | ||
158 | if (csdev->buffer[48] != 0) { | ||
159 | sdev_printk(KERN_NOTICE, sdev, "Detected in-progress " | ||
160 | "ucode upgrade NDU operation while finding " | ||
161 | "current active SP."); | ||
162 | err = SCSI_DH_DEV_TEMP_BUSY; | ||
163 | } else { | ||
164 | *default_sp = csdev->buffer[5]; | ||
165 | |||
166 | if (csdev->buffer[4] == 2) | ||
167 | /* SP for path is current */ | ||
168 | *current_sp = csdev->buffer[8]; | ||
169 | else { | ||
170 | if (csdev->buffer[4] == 1) | ||
171 | /* SP for this path is NOT current */ | ||
172 | if (csdev->buffer[8] == 0) | ||
173 | *current_sp = 1; | ||
174 | else | ||
175 | *current_sp = 0; | ||
176 | else | ||
177 | /* unbound LU or LUNZ */ | ||
178 | *current_sp = CLARIION_UNBOUND_LU; | ||
179 | } | ||
180 | *new_current_sp = csdev->buffer[8]; | ||
181 | } | ||
182 | } else { | ||
183 | struct scsi_sense_hdr sshdr; | ||
184 | 170 | ||
185 | err = SCSI_DH_IO; | 171 | /* check for in-progress ucode upgrade (NDU) */ |
186 | 172 | if (csdev->buffer[48] != 0) { | |
187 | if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, | 173 | sdev_printk(KERN_NOTICE, sdev, "%s: Detected in-progress " |
188 | &sshdr)) | 174 | "ucode upgrade NDU operation while finding " |
189 | sdev_printk(KERN_ERR, sdev, "Found valid sense data " | 175 | "current active SP.", CLARIION_NAME); |
190 | "0x%2x, 0x%2x, 0x%2x while finding current " | 176 | err = SCSI_DH_DEV_TEMP_BUSY; |
191 | "active SP.", sshdr.sense_key, sshdr.asc, | 177 | goto out; |
192 | sshdr.ascq); | 178 | } |
193 | else | 179 | if (csdev->buffer[4] < 0 || csdev->buffer[4] > 2) { |
194 | sdev_printk(KERN_ERR, sdev, "Error 0x%x finding " | 180 | /* Invalid buffer format */ |
195 | "current active SP.", result); | 181 | sdev_printk(KERN_NOTICE, sdev, |
182 | "%s: invalid VPD page 0xC0 format\n", | ||
183 | CLARIION_NAME); | ||
184 | err = SCSI_DH_NOSYS; | ||
185 | goto out; | ||
186 | } | ||
187 | switch (csdev->buffer[28] & 0x0f) { | ||
188 | case 6: | ||
189 | sdev_printk(KERN_NOTICE, sdev, | ||
190 | "%s: ALUA failover mode detected\n", | ||
191 | CLARIION_NAME); | ||
192 | break; | ||
193 | case 4: | ||
194 | /* Linux failover */ | ||
195 | break; | ||
196 | default: | ||
197 | sdev_printk(KERN_WARNING, sdev, | ||
198 | "%s: Invalid failover mode %d\n", | ||
199 | CLARIION_NAME, csdev->buffer[28] & 0x0f); | ||
200 | err = SCSI_DH_NOSYS; | ||
201 | goto out; | ||
196 | } | 202 | } |
197 | 203 | ||
204 | csdev->default_sp = csdev->buffer[5]; | ||
205 | csdev->lun_state = csdev->buffer[4]; | ||
206 | csdev->current_sp = csdev->buffer[8]; | ||
207 | csdev->port = csdev->buffer[7]; | ||
208 | |||
209 | out: | ||
198 | return err; | 210 | return err; |
199 | } | 211 | } |
200 | 212 | ||
201 | static int sp_info_endio(struct scsi_device *sdev, int result, | 213 | #define emc_default_str "FC (Legacy)" |
202 | int mode_select_sent, int *done) | 214 | |
215 | static char * parse_sp_model(struct scsi_device *sdev, unsigned char *buffer) | ||
203 | { | 216 | { |
204 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | 217 | unsigned char len = buffer[4] + 5; |
205 | int err_flags, default_sp, current_sp, new_current_sp; | 218 | char *sp_model = NULL; |
219 | unsigned char sp_len, serial_len; | ||
220 | |||
221 | if (len < 160) { | ||
222 | sdev_printk(KERN_WARNING, sdev, | ||
223 | "%s: Invalid information section length %d\n", | ||
224 | CLARIION_NAME, len); | ||
225 | /* Check for old FC arrays */ | ||
226 | if (!strncmp(buffer + 8, "DGC", 3)) { | ||
227 | /* Old FC array, not supporting extended information */ | ||
228 | sp_model = emc_default_str; | ||
229 | } | ||
230 | goto out; | ||
231 | } | ||
206 | 232 | ||
207 | err_flags = parse_sp_info_reply(sdev, result, &default_sp, | 233 | /* |
208 | ¤t_sp, &new_current_sp); | 234 | * Parse extended information for SP model number |
235 | */ | ||
236 | serial_len = buffer[160]; | ||
237 | if (serial_len == 0 || serial_len + 161 > len) { | ||
238 | sdev_printk(KERN_WARNING, sdev, | ||
239 | "%s: Invalid array serial number length %d\n", | ||
240 | CLARIION_NAME, serial_len); | ||
241 | goto out; | ||
242 | } | ||
243 | sp_len = buffer[99]; | ||
244 | if (sp_len == 0 || serial_len + sp_len + 161 > len) { | ||
245 | sdev_printk(KERN_WARNING, sdev, | ||
246 | "%s: Invalid model number length %d\n", | ||
247 | CLARIION_NAME, sp_len); | ||
248 | goto out; | ||
249 | } | ||
250 | sp_model = &buffer[serial_len + 161]; | ||
251 | /* Strip whitespace at the end */ | ||
252 | while (sp_len > 1 && sp_model[sp_len - 1] == ' ') | ||
253 | sp_len--; | ||
209 | 254 | ||
210 | if (err_flags != SCSI_DH_OK) | 255 | sp_model[sp_len] = '\0'; |
211 | goto done; | ||
212 | 256 | ||
213 | if (mode_select_sent) { | 257 | out: |
214 | csdev->default_sp = default_sp; | 258 | return sp_model; |
215 | csdev->current_sp = current_sp; | ||
216 | } else { | ||
217 | /* | ||
218 | * Issue the actual module_selec request IFF either | ||
219 | * (1) we do not know the identity of the current SP OR | ||
220 | * (2) what we think we know is actually correct. | ||
221 | */ | ||
222 | if ((current_sp != CLARIION_UNBOUND_LU) && | ||
223 | (new_current_sp != current_sp)) { | ||
224 | |||
225 | csdev->default_sp = default_sp; | ||
226 | csdev->current_sp = current_sp; | ||
227 | |||
228 | sdev_printk(KERN_INFO, sdev, "Ignoring path group " | ||
229 | "switch-over command for CLARiiON SP%s since " | ||
230 | " mapped device is already initialized.", | ||
231 | current_sp ? "B" : "A"); | ||
232 | if (done) | ||
233 | *done = 1; /* as good as doing it */ | ||
234 | } | ||
235 | } | ||
236 | done: | ||
237 | return err_flags; | ||
238 | } | 259 | } |
239 | 260 | ||
240 | /* | 261 | /* |
@@ -244,48 +265,37 @@ done: | |||
244 | * Uses data and sense buffers in hardware handler context structure and | 265 | * Uses data and sense buffers in hardware handler context structure and |
245 | * assumes serial servicing of commands, both issuance and completion. | 266 | * assumes serial servicing of commands, both issuance and completion. |
246 | */ | 267 | */ |
247 | static struct request *get_req(struct scsi_device *sdev, int cmd) | 268 | static struct request *get_req(struct scsi_device *sdev, int cmd, |
269 | unsigned char *buffer) | ||
248 | { | 270 | { |
249 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
250 | struct request *rq; | 271 | struct request *rq; |
251 | unsigned char *page22; | ||
252 | int len = 0; | 272 | int len = 0; |
253 | 273 | ||
254 | rq = blk_get_request(sdev->request_queue, | 274 | rq = blk_get_request(sdev->request_queue, |
255 | (cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC); | 275 | (cmd == MODE_SELECT) ? WRITE : READ, GFP_NOIO); |
256 | if (!rq) { | 276 | if (!rq) { |
257 | sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed"); | 277 | sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed"); |
258 | return NULL; | 278 | return NULL; |
259 | } | 279 | } |
260 | 280 | ||
261 | memset(&rq->cmd, 0, BLK_MAX_CDB); | 281 | memset(rq->cmd, 0, BLK_MAX_CDB); |
282 | rq->cmd_len = COMMAND_SIZE(cmd); | ||
262 | rq->cmd[0] = cmd; | 283 | rq->cmd[0] = cmd; |
263 | rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); | ||
264 | 284 | ||
265 | switch (cmd) { | 285 | switch (cmd) { |
266 | case MODE_SELECT: | 286 | case MODE_SELECT: |
267 | if (csdev->short_trespass) { | 287 | len = sizeof(short_trespass); |
268 | page22 = csdev->hr ? short_trespass_hr : short_trespass; | 288 | rq->cmd_flags |= REQ_RW; |
269 | len = sizeof(short_trespass); | 289 | rq->cmd[1] = 0x10; |
270 | } else { | 290 | break; |
271 | page22 = csdev->hr ? long_trespass_hr : long_trespass; | 291 | case MODE_SELECT_10: |
272 | len = sizeof(long_trespass); | 292 | len = sizeof(long_trespass); |
273 | } | ||
274 | /* | ||
275 | * Can't DMA from kernel BSS -- must copy selected trespass | ||
276 | * command mode page contents to context buffer which is | ||
277 | * allocated by kmalloc. | ||
278 | */ | ||
279 | BUG_ON((len > CLARIION_BUFFER_SIZE)); | ||
280 | memcpy(csdev->buffer, page22, len); | ||
281 | rq->cmd_flags |= REQ_RW; | 293 | rq->cmd_flags |= REQ_RW; |
282 | rq->cmd[1] = 0x10; | 294 | rq->cmd[1] = 0x10; |
283 | break; | 295 | break; |
284 | case INQUIRY: | 296 | case INQUIRY: |
285 | rq->cmd[1] = 0x1; | ||
286 | rq->cmd[2] = 0xC0; | ||
287 | len = CLARIION_BUFFER_SIZE; | 297 | len = CLARIION_BUFFER_SIZE; |
288 | memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE); | 298 | memset(buffer, 0, len); |
289 | break; | 299 | break; |
290 | default: | 300 | default: |
291 | BUG_ON(1); | 301 | BUG_ON(1); |
@@ -298,47 +308,94 @@ static struct request *get_req(struct scsi_device *sdev, int cmd) | |||
298 | rq->timeout = CLARIION_TIMEOUT; | 308 | rq->timeout = CLARIION_TIMEOUT; |
299 | rq->retries = CLARIION_RETRIES; | 309 | rq->retries = CLARIION_RETRIES; |
300 | 310 | ||
301 | rq->sense = csdev->sense; | 311 | if (blk_rq_map_kern(rq->q, rq, buffer, len, GFP_NOIO)) { |
302 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | 312 | blk_put_request(rq); |
303 | rq->sense_len = 0; | ||
304 | |||
305 | if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer, | ||
306 | len, GFP_ATOMIC)) { | ||
307 | __blk_put_request(rq->q, rq); | ||
308 | return NULL; | 313 | return NULL; |
309 | } | 314 | } |
310 | 315 | ||
311 | return rq; | 316 | return rq; |
312 | } | 317 | } |
313 | 318 | ||
314 | static int send_cmd(struct scsi_device *sdev, int cmd) | 319 | static int send_inquiry_cmd(struct scsi_device *sdev, int page, |
320 | struct clariion_dh_data *csdev) | ||
315 | { | 321 | { |
316 | struct request *rq = get_req(sdev, cmd); | 322 | struct request *rq = get_req(sdev, INQUIRY, csdev->buffer); |
323 | int err; | ||
317 | 324 | ||
318 | if (!rq) | 325 | if (!rq) |
319 | return SCSI_DH_RES_TEMP_UNAVAIL; | 326 | return SCSI_DH_RES_TEMP_UNAVAIL; |
320 | 327 | ||
321 | return blk_execute_rq(sdev->request_queue, NULL, rq, 1); | 328 | rq->sense = csdev->sense; |
329 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); | ||
330 | rq->sense_len = csdev->senselen = 0; | ||
331 | |||
332 | rq->cmd[0] = INQUIRY; | ||
333 | if (page != 0) { | ||
334 | rq->cmd[1] = 1; | ||
335 | rq->cmd[2] = page; | ||
336 | } | ||
337 | err = blk_execute_rq(sdev->request_queue, NULL, rq, 1); | ||
338 | if (err == -EIO) { | ||
339 | sdev_printk(KERN_INFO, sdev, | ||
340 | "%s: failed to send %s INQUIRY: %x\n", | ||
341 | CLARIION_NAME, page?"EVPD":"standard", | ||
342 | rq->errors); | ||
343 | csdev->senselen = rq->sense_len; | ||
344 | err = SCSI_DH_IO; | ||
345 | } | ||
346 | |||
347 | blk_put_request(rq); | ||
348 | |||
349 | return err; | ||
322 | } | 350 | } |
323 | 351 | ||
324 | static int clariion_activate(struct scsi_device *sdev) | 352 | static int send_trespass_cmd(struct scsi_device *sdev, |
353 | struct clariion_dh_data *csdev) | ||
325 | { | 354 | { |
326 | int result, done = 0; | 355 | struct request *rq; |
356 | unsigned char *page22; | ||
357 | int err, len, cmd; | ||
358 | |||
359 | if (csdev->flags & CLARIION_SHORT_TRESPASS) { | ||
360 | page22 = short_trespass; | ||
361 | if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) | ||
362 | /* Set Honor Reservations bit */ | ||
363 | page22[6] |= 0x80; | ||
364 | len = sizeof(short_trespass); | ||
365 | cmd = MODE_SELECT; | ||
366 | } else { | ||
367 | page22 = long_trespass; | ||
368 | if (!(csdev->flags & CLARIION_HONOR_RESERVATIONS)) | ||
369 | /* Set Honor Reservations bit */ | ||
370 | page22[10] |= 0x80; | ||
371 | len = sizeof(long_trespass); | ||
372 | cmd = MODE_SELECT_10; | ||
373 | } | ||
374 | BUG_ON((len > CLARIION_BUFFER_SIZE)); | ||
375 | memcpy(csdev->buffer, page22, len); | ||
327 | 376 | ||
328 | result = send_cmd(sdev, INQUIRY); | 377 | rq = get_req(sdev, cmd, csdev->buffer); |
329 | result = sp_info_endio(sdev, result, 0, &done); | 378 | if (!rq) |
330 | if (result || done) | 379 | return SCSI_DH_RES_TEMP_UNAVAIL; |
331 | goto done; | ||
332 | 380 | ||
333 | result = send_cmd(sdev, MODE_SELECT); | 381 | rq->sense = csdev->sense; |
334 | result = trespass_endio(sdev, result); | 382 | memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); |
335 | if (result) | 383 | rq->sense_len = csdev->senselen = 0; |
336 | goto done; | ||
337 | 384 | ||
338 | result = send_cmd(sdev, INQUIRY); | 385 | err = blk_execute_rq(sdev->request_queue, NULL, rq, 1); |
339 | result = sp_info_endio(sdev, result, 1, NULL); | 386 | if (err == -EIO) { |
340 | done: | 387 | if (rq->sense_len) { |
341 | return result; | 388 | err = trespass_endio(sdev, csdev->sense); |
389 | } else { | ||
390 | sdev_printk(KERN_INFO, sdev, | ||
391 | "%s: failed to send MODE SELECT: %x\n", | ||
392 | CLARIION_NAME, rq->errors); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | blk_put_request(rq); | ||
397 | |||
398 | return err; | ||
342 | } | 399 | } |
343 | 400 | ||
344 | static int clariion_check_sense(struct scsi_device *sdev, | 401 | static int clariion_check_sense(struct scsi_device *sdev, |
@@ -386,13 +443,129 @@ static int clariion_check_sense(struct scsi_device *sdev, | |||
386 | break; | 443 | break; |
387 | } | 444 | } |
388 | 445 | ||
389 | /* success just means we do not care what scsi-ml does */ | 446 | return SCSI_RETURN_NOT_HANDLED; |
390 | return SUCCESS; | 447 | } |
448 | |||
449 | static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) | ||
450 | { | ||
451 | struct clariion_dh_data *h = get_clariion_data(sdev); | ||
452 | int ret = BLKPREP_OK; | ||
453 | |||
454 | if (h->lun_state != CLARIION_LUN_OWNED) { | ||
455 | ret = BLKPREP_KILL; | ||
456 | req->cmd_flags |= REQ_QUIET; | ||
457 | } | ||
458 | return ret; | ||
459 | |||
460 | } | ||
461 | |||
462 | static int clariion_std_inquiry(struct scsi_device *sdev, | ||
463 | struct clariion_dh_data *csdev) | ||
464 | { | ||
465 | int err; | ||
466 | char *sp_model; | ||
467 | |||
468 | err = send_inquiry_cmd(sdev, 0, csdev); | ||
469 | if (err != SCSI_DH_OK && csdev->senselen) { | ||
470 | struct scsi_sense_hdr sshdr; | ||
471 | |||
472 | if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, | ||
473 | &sshdr)) { | ||
474 | sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code " | ||
475 | "%02x/%02x/%02x\n", CLARIION_NAME, | ||
476 | sshdr.sense_key, sshdr.asc, sshdr.ascq); | ||
477 | } | ||
478 | err = SCSI_DH_IO; | ||
479 | goto out; | ||
480 | } | ||
481 | |||
482 | sp_model = parse_sp_model(sdev, csdev->buffer); | ||
483 | if (!sp_model) { | ||
484 | err = SCSI_DH_DEV_UNSUPP; | ||
485 | goto out; | ||
486 | } | ||
487 | |||
488 | /* | ||
489 | * FC Series arrays do not support long trespass | ||
490 | */ | ||
491 | if (!strlen(sp_model) || !strncmp(sp_model, "FC",2)) | ||
492 | csdev->flags |= CLARIION_SHORT_TRESPASS; | ||
493 | |||
494 | sdev_printk(KERN_INFO, sdev, | ||
495 | "%s: detected Clariion %s, flags %x\n", | ||
496 | CLARIION_NAME, sp_model, csdev->flags); | ||
497 | out: | ||
498 | return err; | ||
499 | } | ||
500 | |||
501 | static int clariion_send_inquiry(struct scsi_device *sdev, | ||
502 | struct clariion_dh_data *csdev) | ||
503 | { | ||
504 | int err, retry = CLARIION_RETRIES; | ||
505 | |||
506 | retry: | ||
507 | err = send_inquiry_cmd(sdev, 0xC0, csdev); | ||
508 | if (err != SCSI_DH_OK && csdev->senselen) { | ||
509 | struct scsi_sense_hdr sshdr; | ||
510 | |||
511 | err = scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE, | ||
512 | &sshdr); | ||
513 | if (!err) | ||
514 | return SCSI_DH_IO; | ||
515 | |||
516 | err = clariion_check_sense(sdev, &sshdr); | ||
517 | if (retry > 0 && err == NEEDS_RETRY) { | ||
518 | retry--; | ||
519 | goto retry; | ||
520 | } | ||
521 | sdev_printk(KERN_ERR, sdev, "%s: INQUIRY sense code " | ||
522 | "%02x/%02x/%02x\n", CLARIION_NAME, | ||
523 | sshdr.sense_key, sshdr.asc, sshdr.ascq); | ||
524 | err = SCSI_DH_IO; | ||
525 | } else { | ||
526 | err = parse_sp_info_reply(sdev, csdev); | ||
527 | } | ||
528 | return err; | ||
529 | } | ||
530 | |||
531 | static int clariion_activate(struct scsi_device *sdev) | ||
532 | { | ||
533 | struct clariion_dh_data *csdev = get_clariion_data(sdev); | ||
534 | int result; | ||
535 | |||
536 | result = clariion_send_inquiry(sdev, csdev); | ||
537 | if (result != SCSI_DH_OK) | ||
538 | goto done; | ||
539 | |||
540 | if (csdev->lun_state == CLARIION_LUN_OWNED) | ||
541 | goto done; | ||
542 | |||
543 | result = send_trespass_cmd(sdev, csdev); | ||
544 | if (result != SCSI_DH_OK) | ||
545 | goto done; | ||
546 | sdev_printk(KERN_INFO, sdev,"%s: %s trespass command sent\n", | ||
547 | CLARIION_NAME, | ||
548 | csdev->flags&CLARIION_SHORT_TRESPASS?"short":"long" ); | ||
549 | |||
550 | /* Update status */ | ||
551 | result = clariion_send_inquiry(sdev, csdev); | ||
552 | if (result != SCSI_DH_OK) | ||
553 | goto done; | ||
554 | |||
555 | done: | ||
556 | sdev_printk(KERN_INFO, sdev, | ||
557 | "%s: at SP %c Port %d (%s, default SP %c)\n", | ||
558 | CLARIION_NAME, csdev->current_sp + 'A', | ||
559 | csdev->port, lun_state[csdev->lun_state], | ||
560 | csdev->default_sp + 'A'); | ||
561 | |||
562 | return result; | ||
391 | } | 563 | } |
392 | 564 | ||
393 | const struct scsi_dh_devlist clariion_dev_list[] = { | 565 | const struct scsi_dh_devlist clariion_dev_list[] = { |
394 | {"DGC", "RAID"}, | 566 | {"DGC", "RAID"}, |
395 | {"DGC", "DISK"}, | 567 | {"DGC", "DISK"}, |
568 | {"DGC", "VRAID"}, | ||
396 | {NULL, NULL}, | 569 | {NULL, NULL}, |
397 | }; | 570 | }; |
398 | 571 | ||
@@ -407,6 +580,7 @@ static struct scsi_device_handler clariion_dh = { | |||
407 | .detach = clariion_bus_detach, | 580 | .detach = clariion_bus_detach, |
408 | .check_sense = clariion_check_sense, | 581 | .check_sense = clariion_check_sense, |
409 | .activate = clariion_activate, | 582 | .activate = clariion_activate, |
583 | .prep_fn = clariion_prep_fn, | ||
410 | }; | 584 | }; |
411 | 585 | ||
412 | /* | 586 | /* |
@@ -417,28 +591,50 @@ static int clariion_bus_attach(struct scsi_device *sdev) | |||
417 | struct scsi_dh_data *scsi_dh_data; | 591 | struct scsi_dh_data *scsi_dh_data; |
418 | struct clariion_dh_data *h; | 592 | struct clariion_dh_data *h; |
419 | unsigned long flags; | 593 | unsigned long flags; |
594 | int err; | ||
420 | 595 | ||
421 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) | 596 | scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) |
422 | + sizeof(*h) , GFP_KERNEL); | 597 | + sizeof(*h) , GFP_KERNEL); |
423 | if (!scsi_dh_data) { | 598 | if (!scsi_dh_data) { |
424 | sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n", | 599 | sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", |
425 | CLARIION_NAME); | 600 | CLARIION_NAME); |
426 | return -ENOMEM; | 601 | return -ENOMEM; |
427 | } | 602 | } |
428 | 603 | ||
429 | scsi_dh_data->scsi_dh = &clariion_dh; | 604 | scsi_dh_data->scsi_dh = &clariion_dh; |
430 | h = (struct clariion_dh_data *) scsi_dh_data->buf; | 605 | h = (struct clariion_dh_data *) scsi_dh_data->buf; |
606 | h->lun_state = CLARIION_LUN_UNINITIALIZED; | ||
431 | h->default_sp = CLARIION_UNBOUND_LU; | 607 | h->default_sp = CLARIION_UNBOUND_LU; |
432 | h->current_sp = CLARIION_UNBOUND_LU; | 608 | h->current_sp = CLARIION_UNBOUND_LU; |
433 | 609 | ||
610 | err = clariion_std_inquiry(sdev, h); | ||
611 | if (err != SCSI_DH_OK) | ||
612 | goto failed; | ||
613 | |||
614 | err = clariion_send_inquiry(sdev, h); | ||
615 | if (err != SCSI_DH_OK) | ||
616 | goto failed; | ||
617 | |||
618 | if (!try_module_get(THIS_MODULE)) | ||
619 | goto failed; | ||
620 | |||
434 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); | 621 | spin_lock_irqsave(sdev->request_queue->queue_lock, flags); |
435 | sdev->scsi_dh_data = scsi_dh_data; | 622 | sdev->scsi_dh_data = scsi_dh_data; |
436 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | 623 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); |
437 | 624 | ||
438 | sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME); | 625 | sdev_printk(KERN_INFO, sdev, |
439 | try_module_get(THIS_MODULE); | 626 | "%s: connected to SP %c Port %d (%s, default SP %c)\n", |
627 | CLARIION_NAME, h->current_sp + 'A', | ||
628 | h->port, lun_state[h->lun_state], | ||
629 | h->default_sp + 'A'); | ||
440 | 630 | ||
441 | return 0; | 631 | return 0; |
632 | |||
633 | failed: | ||
634 | kfree(scsi_dh_data); | ||
635 | sdev_printk(KERN_ERR, sdev, "%s: not attached\n", | ||
636 | CLARIION_NAME); | ||
637 | return -EINVAL; | ||
442 | } | 638 | } |
443 | 639 | ||
444 | static void clariion_bus_detach(struct scsi_device *sdev) | 640 | static void clariion_bus_detach(struct scsi_device *sdev) |
@@ -451,7 +647,7 @@ static void clariion_bus_detach(struct scsi_device *sdev) | |||
451 | sdev->scsi_dh_data = NULL; | 647 | sdev->scsi_dh_data = NULL; |
452 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); | 648 | spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); |
453 | 649 | ||
454 | sdev_printk(KERN_NOTICE, sdev, "Detached %s.\n", | 650 | sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", |
455 | CLARIION_NAME); | 651 | CLARIION_NAME); |
456 | 652 | ||
457 | kfree(scsi_dh_data); | 653 | kfree(scsi_dh_data); |
@@ -464,7 +660,8 @@ static int __init clariion_init(void) | |||
464 | 660 | ||
465 | r = scsi_register_device_handler(&clariion_dh); | 661 | r = scsi_register_device_handler(&clariion_dh); |
466 | if (r != 0) | 662 | if (r != 0) |
467 | printk(KERN_ERR "Failed to register scsi device handler."); | 663 | printk(KERN_ERR "%s: Failed to register scsi device handler.", |
664 | CLARIION_NAME); | ||
468 | return r; | 665 | return r; |
469 | } | 666 | } |
470 | 667 | ||
diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h index 3ad2303d1a16..e15e2aade69c 100644 --- a/include/scsi/scsi_dh.h +++ b/include/scsi/scsi_dh.h | |||
@@ -32,6 +32,7 @@ enum { | |||
32 | */ | 32 | */ |
33 | SCSI_DH_DEV_FAILED, /* generic device error */ | 33 | SCSI_DH_DEV_FAILED, /* generic device error */ |
34 | SCSI_DH_DEV_TEMP_BUSY, | 34 | SCSI_DH_DEV_TEMP_BUSY, |
35 | SCSI_DH_DEV_UNSUPP, /* device handler not supported */ | ||
35 | SCSI_DH_DEVICE_MAX, /* max device blkerr definition */ | 36 | SCSI_DH_DEVICE_MAX, /* max device blkerr definition */ |
36 | 37 | ||
37 | /* | 38 | /* |