diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/block/dasd_3990_erp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/block/dasd_3990_erp.c')
-rw-r--r-- | drivers/s390/block/dasd_3990_erp.c | 2742 |
1 files changed, 2742 insertions, 0 deletions
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c new file mode 100644 index 000000000000..c143ecb53d9d --- /dev/null +++ b/drivers/s390/block/dasd_3990_erp.c | |||
@@ -0,0 +1,2742 @@ | |||
1 | /* | ||
2 | * File...........: linux/drivers/s390/block/dasd_3990_erp.c | ||
3 | * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com> | ||
4 | * Holger Smolinski <Holger.Smolinski@de.ibm.com> | ||
5 | * Bugreports.to..: <Linux390@de.ibm.com> | ||
6 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001 | ||
7 | * | ||
8 | * $Revision: 1.36 $ | ||
9 | */ | ||
10 | |||
11 | #include <linux/timer.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <asm/idals.h> | ||
14 | #include <asm/todclk.h> | ||
15 | |||
16 | #define PRINTK_HEADER "dasd_erp(3990): " | ||
17 | |||
18 | #include "dasd_int.h" | ||
19 | #include "dasd_eckd.h" | ||
20 | |||
21 | |||
22 | struct DCTL_data { | ||
23 | unsigned char subcommand; /* e.g Inhibit Write, Enable Write,... */ | ||
24 | unsigned char modifier; /* Subcommand modifier */ | ||
25 | unsigned short res; /* reserved */ | ||
26 | } __attribute__ ((packed)); | ||
27 | |||
28 | /* | ||
29 | ***************************************************************************** | ||
30 | * SECTION ERP EXAMINATION | ||
31 | ***************************************************************************** | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * DASD_3990_ERP_EXAMINE_24 | ||
36 | * | ||
37 | * DESCRIPTION | ||
38 | * Checks only for fatal (unrecoverable) error. | ||
39 | * A detailed examination of the sense data is done later outside | ||
40 | * the interrupt handler. | ||
41 | * | ||
42 | * Each bit configuration leading to an action code 2 (Exit with | ||
43 | * programming error or unusual condition indication) | ||
44 | * are handled as fatal errorīs. | ||
45 | * | ||
46 | * All other configurations are handled as recoverable errors. | ||
47 | * | ||
48 | * RETURN VALUES | ||
49 | * dasd_era_fatal for all fatal (unrecoverable errors) | ||
50 | * dasd_era_recover for all others. | ||
51 | */ | ||
52 | static dasd_era_t | ||
53 | dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense) | ||
54 | { | ||
55 | |||
56 | struct dasd_device *device = cqr->device; | ||
57 | |||
58 | /* check for 'Command Reject' */ | ||
59 | if ((sense[0] & SNS0_CMD_REJECT) && | ||
60 | (!(sense[2] & SNS2_ENV_DATA_PRESENT))) { | ||
61 | |||
62 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
63 | "EXAMINE 24: Command Reject detected - " | ||
64 | "fatal error"); | ||
65 | |||
66 | return dasd_era_fatal; | ||
67 | } | ||
68 | |||
69 | /* check for 'Invalid Track Format' */ | ||
70 | if ((sense[1] & SNS1_INV_TRACK_FORMAT) && | ||
71 | (!(sense[2] & SNS2_ENV_DATA_PRESENT))) { | ||
72 | |||
73 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
74 | "EXAMINE 24: Invalid Track Format detected " | ||
75 | "- fatal error"); | ||
76 | |||
77 | return dasd_era_fatal; | ||
78 | } | ||
79 | |||
80 | /* check for 'No Record Found' */ | ||
81 | if (sense[1] & SNS1_NO_REC_FOUND) { | ||
82 | |||
83 | /* FIXME: fatal error ?!? */ | ||
84 | DEV_MESSAGE(KERN_ERR, device, | ||
85 | "EXAMINE 24: No Record Found detected %s", | ||
86 | device->state <= DASD_STATE_BASIC ? | ||
87 | " " : "- fatal error"); | ||
88 | |||
89 | return dasd_era_fatal; | ||
90 | } | ||
91 | |||
92 | /* return recoverable for all others */ | ||
93 | return dasd_era_recover; | ||
94 | } /* END dasd_3990_erp_examine_24 */ | ||
95 | |||
96 | /* | ||
97 | * DASD_3990_ERP_EXAMINE_32 | ||
98 | * | ||
99 | * DESCRIPTION | ||
100 | * Checks only for fatal/no/recoverable error. | ||
101 | * A detailed examination of the sense data is done later outside | ||
102 | * the interrupt handler. | ||
103 | * | ||
104 | * RETURN VALUES | ||
105 | * dasd_era_none no error | ||
106 | * dasd_era_fatal for all fatal (unrecoverable errors) | ||
107 | * dasd_era_recover for recoverable others. | ||
108 | */ | ||
109 | static dasd_era_t | ||
110 | dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense) | ||
111 | { | ||
112 | |||
113 | struct dasd_device *device = cqr->device; | ||
114 | |||
115 | switch (sense[25]) { | ||
116 | case 0x00: | ||
117 | return dasd_era_none; | ||
118 | |||
119 | case 0x01: | ||
120 | DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error"); | ||
121 | |||
122 | return dasd_era_fatal; | ||
123 | |||
124 | default: | ||
125 | |||
126 | return dasd_era_recover; | ||
127 | } | ||
128 | |||
129 | } /* end dasd_3990_erp_examine_32 */ | ||
130 | |||
131 | /* | ||
132 | * DASD_3990_ERP_EXAMINE | ||
133 | * | ||
134 | * DESCRIPTION | ||
135 | * Checks only for fatal/no/recover error. | ||
136 | * A detailed examination of the sense data is done later outside | ||
137 | * the interrupt handler. | ||
138 | * | ||
139 | * The logic is based on the 'IBM 3990 Storage Control Reference' manual | ||
140 | * 'Chapter 7. Error Recovery Procedures'. | ||
141 | * | ||
142 | * RETURN VALUES | ||
143 | * dasd_era_none no error | ||
144 | * dasd_era_fatal for all fatal (unrecoverable errors) | ||
145 | * dasd_era_recover for all others. | ||
146 | */ | ||
147 | dasd_era_t | ||
148 | dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb) | ||
149 | { | ||
150 | |||
151 | char *sense = irb->ecw; | ||
152 | dasd_era_t era = dasd_era_recover; | ||
153 | struct dasd_device *device = cqr->device; | ||
154 | |||
155 | /* check for successful execution first */ | ||
156 | if (irb->scsw.cstat == 0x00 && | ||
157 | irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | ||
158 | return dasd_era_none; | ||
159 | |||
160 | /* distinguish between 24 and 32 byte sense data */ | ||
161 | if (sense[27] & DASD_SENSE_BIT_0) { | ||
162 | |||
163 | era = dasd_3990_erp_examine_24(cqr, sense); | ||
164 | |||
165 | } else { | ||
166 | |||
167 | era = dasd_3990_erp_examine_32(cqr, sense); | ||
168 | |||
169 | } | ||
170 | |||
171 | /* log the erp chain if fatal error occurred */ | ||
172 | if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) { | ||
173 | dasd_log_sense(cqr, irb); | ||
174 | dasd_log_ccw(cqr, 0, irb->scsw.cpa); | ||
175 | } | ||
176 | |||
177 | return era; | ||
178 | |||
179 | } /* END dasd_3990_erp_examine */ | ||
180 | |||
181 | /* | ||
182 | ***************************************************************************** | ||
183 | * SECTION ERP HANDLING | ||
184 | ***************************************************************************** | ||
185 | */ | ||
186 | /* | ||
187 | ***************************************************************************** | ||
188 | * 24 and 32 byte sense ERP functions | ||
189 | ***************************************************************************** | ||
190 | */ | ||
191 | |||
192 | /* | ||
193 | * DASD_3990_ERP_CLEANUP | ||
194 | * | ||
195 | * DESCRIPTION | ||
196 | * Removes the already build but not necessary ERP request and sets | ||
197 | * the status of the original cqr / erp to the given (final) status | ||
198 | * | ||
199 | * PARAMETER | ||
200 | * erp request to be blocked | ||
201 | * final_status either DASD_CQR_DONE or DASD_CQR_FAILED | ||
202 | * | ||
203 | * RETURN VALUES | ||
204 | * cqr original cqr | ||
205 | */ | ||
206 | static struct dasd_ccw_req * | ||
207 | dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) | ||
208 | { | ||
209 | struct dasd_ccw_req *cqr = erp->refers; | ||
210 | |||
211 | dasd_free_erp_request(erp, erp->device); | ||
212 | cqr->status = final_status; | ||
213 | return cqr; | ||
214 | |||
215 | } /* end dasd_3990_erp_cleanup */ | ||
216 | |||
217 | /* | ||
218 | * DASD_3990_ERP_BLOCK_QUEUE | ||
219 | * | ||
220 | * DESCRIPTION | ||
221 | * Block the given device request queue to prevent from further | ||
222 | * processing until the started timer has expired or an related | ||
223 | * interrupt was received. | ||
224 | */ | ||
225 | static void | ||
226 | dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) | ||
227 | { | ||
228 | |||
229 | struct dasd_device *device = erp->device; | ||
230 | |||
231 | DEV_MESSAGE(KERN_INFO, device, | ||
232 | "blocking request queue for %is", expires/HZ); | ||
233 | |||
234 | device->stopped |= DASD_STOPPED_PENDING; | ||
235 | erp->status = DASD_CQR_QUEUED; | ||
236 | |||
237 | dasd_set_timer(device, expires); | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * DASD_3990_ERP_INT_REQ | ||
242 | * | ||
243 | * DESCRIPTION | ||
244 | * Handles 'Intervention Required' error. | ||
245 | * This means either device offline or not installed. | ||
246 | * | ||
247 | * PARAMETER | ||
248 | * erp current erp | ||
249 | * RETURN VALUES | ||
250 | * erp modified erp | ||
251 | */ | ||
252 | static struct dasd_ccw_req * | ||
253 | dasd_3990_erp_int_req(struct dasd_ccw_req * erp) | ||
254 | { | ||
255 | |||
256 | struct dasd_device *device = erp->device; | ||
257 | |||
258 | /* first time set initial retry counter and erp_function */ | ||
259 | /* and retry once without blocking queue */ | ||
260 | /* (this enables easier enqueing of the cqr) */ | ||
261 | if (erp->function != dasd_3990_erp_int_req) { | ||
262 | |||
263 | erp->retries = 256; | ||
264 | erp->function = dasd_3990_erp_int_req; | ||
265 | |||
266 | } else { | ||
267 | |||
268 | /* issue a message and wait for 'device ready' interrupt */ | ||
269 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
270 | "is offline or not installed - " | ||
271 | "INTERVENTION REQUIRED!!"); | ||
272 | |||
273 | dasd_3990_erp_block_queue(erp, 60*HZ); | ||
274 | } | ||
275 | |||
276 | return erp; | ||
277 | |||
278 | } /* end dasd_3990_erp_int_req */ | ||
279 | |||
280 | /* | ||
281 | * DASD_3990_ERP_ALTERNATE_PATH | ||
282 | * | ||
283 | * DESCRIPTION | ||
284 | * Repeat the operation on a different channel path. | ||
285 | * If all alternate paths have been tried, the request is posted with a | ||
286 | * permanent error. | ||
287 | * | ||
288 | * PARAMETER | ||
289 | * erp pointer to the current ERP | ||
290 | * | ||
291 | * RETURN VALUES | ||
292 | * erp modified pointer to the ERP | ||
293 | */ | ||
294 | static void | ||
295 | dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) | ||
296 | { | ||
297 | struct dasd_device *device = erp->device; | ||
298 | __u8 opm; | ||
299 | |||
300 | /* try alternate valid path */ | ||
301 | opm = ccw_device_get_path_mask(device->cdev); | ||
302 | //FIXME: start with get_opm ? | ||
303 | if (erp->lpm == 0) | ||
304 | erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum); | ||
305 | else | ||
306 | erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum); | ||
307 | |||
308 | if ((erp->lpm & opm) != 0x00) { | ||
309 | |||
310 | DEV_MESSAGE(KERN_DEBUG, device, | ||
311 | "try alternate lpm=%x (lpum=%x / opm=%x)", | ||
312 | erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm); | ||
313 | |||
314 | /* reset status to queued to handle the request again... */ | ||
315 | if (erp->status > DASD_CQR_QUEUED) | ||
316 | erp->status = DASD_CQR_QUEUED; | ||
317 | erp->retries = 1; | ||
318 | } else { | ||
319 | DEV_MESSAGE(KERN_ERR, device, | ||
320 | "No alternate channel path left (lpum=%x / " | ||
321 | "opm=%x) -> permanent error", | ||
322 | erp->irb.esw.esw0.sublog.lpum, opm); | ||
323 | |||
324 | /* post request with permanent error */ | ||
325 | if (erp->status > DASD_CQR_QUEUED) | ||
326 | erp->status = DASD_CQR_FAILED; | ||
327 | } | ||
328 | } /* end dasd_3990_erp_alternate_path */ | ||
329 | |||
330 | /* | ||
331 | * DASD_3990_ERP_DCTL | ||
332 | * | ||
333 | * DESCRIPTION | ||
334 | * Setup cqr to do the Diagnostic Control (DCTL) command with an | ||
335 | * Inhibit Write subcommand (0x20) and the given modifier. | ||
336 | * | ||
337 | * PARAMETER | ||
338 | * erp pointer to the current (failed) ERP | ||
339 | * modifier subcommand modifier | ||
340 | * | ||
341 | * RETURN VALUES | ||
342 | * dctl_cqr pointer to NEW dctl_cqr | ||
343 | * | ||
344 | */ | ||
345 | static struct dasd_ccw_req * | ||
346 | dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) | ||
347 | { | ||
348 | |||
349 | struct dasd_device *device = erp->device; | ||
350 | struct DCTL_data *DCTL_data; | ||
351 | struct ccw1 *ccw; | ||
352 | struct dasd_ccw_req *dctl_cqr; | ||
353 | |||
354 | dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1, | ||
355 | sizeof (struct DCTL_data), | ||
356 | erp->device); | ||
357 | if (IS_ERR(dctl_cqr)) { | ||
358 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
359 | "Unable to allocate DCTL-CQR"); | ||
360 | erp->status = DASD_CQR_FAILED; | ||
361 | return erp; | ||
362 | } | ||
363 | |||
364 | DCTL_data = dctl_cqr->data; | ||
365 | |||
366 | DCTL_data->subcommand = 0x02; /* Inhibit Write */ | ||
367 | DCTL_data->modifier = modifier; | ||
368 | |||
369 | ccw = dctl_cqr->cpaddr; | ||
370 | memset(ccw, 0, sizeof (struct ccw1)); | ||
371 | ccw->cmd_code = CCW_CMD_DCTL; | ||
372 | ccw->count = 4; | ||
373 | ccw->cda = (__u32)(addr_t) DCTL_data; | ||
374 | dctl_cqr->function = dasd_3990_erp_DCTL; | ||
375 | dctl_cqr->refers = erp; | ||
376 | dctl_cqr->device = erp->device; | ||
377 | dctl_cqr->magic = erp->magic; | ||
378 | dctl_cqr->expires = 5 * 60 * HZ; | ||
379 | dctl_cqr->retries = 2; | ||
380 | |||
381 | dctl_cqr->buildclk = get_clock(); | ||
382 | |||
383 | dctl_cqr->status = DASD_CQR_FILLED; | ||
384 | |||
385 | return dctl_cqr; | ||
386 | |||
387 | } /* end dasd_3990_erp_DCTL */ | ||
388 | |||
389 | /* | ||
390 | * DASD_3990_ERP_ACTION_1 | ||
391 | * | ||
392 | * DESCRIPTION | ||
393 | * Setup ERP to do the ERP action 1 (see Reference manual). | ||
394 | * Repeat the operation on a different channel path. | ||
395 | * If all alternate paths have been tried, the request is posted with a | ||
396 | * permanent error. | ||
397 | * Note: duplex handling is not implemented (yet). | ||
398 | * | ||
399 | * PARAMETER | ||
400 | * erp pointer to the current ERP | ||
401 | * | ||
402 | * RETURN VALUES | ||
403 | * erp pointer to the ERP | ||
404 | * | ||
405 | */ | ||
406 | static struct dasd_ccw_req * | ||
407 | dasd_3990_erp_action_1(struct dasd_ccw_req * erp) | ||
408 | { | ||
409 | |||
410 | erp->function = dasd_3990_erp_action_1; | ||
411 | |||
412 | dasd_3990_erp_alternate_path(erp); | ||
413 | |||
414 | return erp; | ||
415 | |||
416 | } /* end dasd_3990_erp_action_1 */ | ||
417 | |||
418 | /* | ||
419 | * DASD_3990_ERP_ACTION_4 | ||
420 | * | ||
421 | * DESCRIPTION | ||
422 | * Setup ERP to do the ERP action 4 (see Reference manual). | ||
423 | * Set the current request to PENDING to block the CQR queue for that device | ||
424 | * until the state change interrupt appears. | ||
425 | * Use a timer (20 seconds) to retry the cqr if the interrupt is still | ||
426 | * missing. | ||
427 | * | ||
428 | * PARAMETER | ||
429 | * sense sense data of the actual error | ||
430 | * erp pointer to the current ERP | ||
431 | * | ||
432 | * RETURN VALUES | ||
433 | * erp pointer to the ERP | ||
434 | * | ||
435 | */ | ||
436 | static struct dasd_ccw_req * | ||
437 | dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) | ||
438 | { | ||
439 | |||
440 | struct dasd_device *device = erp->device; | ||
441 | |||
442 | /* first time set initial retry counter and erp_function */ | ||
443 | /* and retry once without waiting for state change pending */ | ||
444 | /* interrupt (this enables easier enqueing of the cqr) */ | ||
445 | if (erp->function != dasd_3990_erp_action_4) { | ||
446 | |||
447 | DEV_MESSAGE(KERN_INFO, device, "%s", | ||
448 | "dasd_3990_erp_action_4: first time retry"); | ||
449 | |||
450 | erp->retries = 256; | ||
451 | erp->function = dasd_3990_erp_action_4; | ||
452 | |||
453 | } else { | ||
454 | |||
455 | if (sense[25] == 0x1D) { /* state change pending */ | ||
456 | |||
457 | DEV_MESSAGE(KERN_INFO, device, | ||
458 | "waiting for state change pending " | ||
459 | "interrupt, %d retries left", | ||
460 | erp->retries); | ||
461 | |||
462 | dasd_3990_erp_block_queue(erp, 30*HZ); | ||
463 | |||
464 | } else if (sense[25] == 0x1E) { /* busy */ | ||
465 | DEV_MESSAGE(KERN_INFO, device, | ||
466 | "busy - redriving request later, " | ||
467 | "%d retries left", | ||
468 | erp->retries); | ||
469 | dasd_3990_erp_block_queue(erp, HZ); | ||
470 | } else { | ||
471 | |||
472 | /* no state change pending - retry */ | ||
473 | DEV_MESSAGE (KERN_INFO, device, | ||
474 | "redriving request immediately, " | ||
475 | "%d retries left", | ||
476 | erp->retries); | ||
477 | erp->status = DASD_CQR_QUEUED; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | return erp; | ||
482 | |||
483 | } /* end dasd_3990_erp_action_4 */ | ||
484 | |||
485 | /* | ||
486 | ***************************************************************************** | ||
487 | * 24 byte sense ERP functions (only) | ||
488 | ***************************************************************************** | ||
489 | */ | ||
490 | |||
491 | /* | ||
492 | * DASD_3990_ERP_ACTION_5 | ||
493 | * | ||
494 | * DESCRIPTION | ||
495 | * Setup ERP to do the ERP action 5 (see Reference manual). | ||
496 | * NOTE: Further handling is done in xxx_further_erp after the retries. | ||
497 | * | ||
498 | * PARAMETER | ||
499 | * erp pointer to the current ERP | ||
500 | * | ||
501 | * RETURN VALUES | ||
502 | * erp pointer to the ERP | ||
503 | * | ||
504 | */ | ||
505 | static struct dasd_ccw_req * | ||
506 | dasd_3990_erp_action_5(struct dasd_ccw_req * erp) | ||
507 | { | ||
508 | |||
509 | /* first of all retry */ | ||
510 | erp->retries = 10; | ||
511 | erp->function = dasd_3990_erp_action_5; | ||
512 | |||
513 | return erp; | ||
514 | |||
515 | } /* end dasd_3990_erp_action_5 */ | ||
516 | |||
517 | /* | ||
518 | * DASD_3990_HANDLE_ENV_DATA | ||
519 | * | ||
520 | * DESCRIPTION | ||
521 | * Handles 24 byte 'Environmental data present'. | ||
522 | * Does a analysis of the sense data (message Format) | ||
523 | * and prints the error messages. | ||
524 | * | ||
525 | * PARAMETER | ||
526 | * sense current sense data | ||
527 | * | ||
528 | * RETURN VALUES | ||
529 | * void | ||
530 | */ | ||
531 | static void | ||
532 | dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense) | ||
533 | { | ||
534 | |||
535 | struct dasd_device *device = erp->device; | ||
536 | char msg_format = (sense[7] & 0xF0); | ||
537 | char msg_no = (sense[7] & 0x0F); | ||
538 | |||
539 | switch (msg_format) { | ||
540 | case 0x00: /* Format 0 - Program or System Checks */ | ||
541 | |||
542 | if (sense[1] & 0x10) { /* check message to operator bit */ | ||
543 | |||
544 | switch (msg_no) { | ||
545 | case 0x00: /* No Message */ | ||
546 | break; | ||
547 | case 0x01: | ||
548 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
549 | "FORMAT 0 - Invalid Command"); | ||
550 | break; | ||
551 | case 0x02: | ||
552 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
553 | "FORMAT 0 - Invalid Command " | ||
554 | "Sequence"); | ||
555 | break; | ||
556 | case 0x03: | ||
557 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
558 | "FORMAT 0 - CCW Count less than " | ||
559 | "required"); | ||
560 | break; | ||
561 | case 0x04: | ||
562 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
563 | "FORMAT 0 - Invalid Parameter"); | ||
564 | break; | ||
565 | case 0x05: | ||
566 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
567 | "FORMAT 0 - Diagnostic of Sepecial" | ||
568 | " Command Violates File Mask"); | ||
569 | break; | ||
570 | case 0x07: | ||
571 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
572 | "FORMAT 0 - Channel Returned with " | ||
573 | "Incorrect retry CCW"); | ||
574 | break; | ||
575 | case 0x08: | ||
576 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
577 | "FORMAT 0 - Reset Notification"); | ||
578 | break; | ||
579 | case 0x09: | ||
580 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
581 | "FORMAT 0 - Storage Path Restart"); | ||
582 | break; | ||
583 | case 0x0A: | ||
584 | DEV_MESSAGE(KERN_WARNING, device, | ||
585 | "FORMAT 0 - Channel requested " | ||
586 | "... %02x", sense[8]); | ||
587 | break; | ||
588 | case 0x0B: | ||
589 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
590 | "FORMAT 0 - Invalid Defective/" | ||
591 | "Alternate Track Pointer"); | ||
592 | break; | ||
593 | case 0x0C: | ||
594 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
595 | "FORMAT 0 - DPS Installation " | ||
596 | "Check"); | ||
597 | break; | ||
598 | case 0x0E: | ||
599 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
600 | "FORMAT 0 - Command Invalid on " | ||
601 | "Secondary Address"); | ||
602 | break; | ||
603 | case 0x0F: | ||
604 | DEV_MESSAGE(KERN_WARNING, device, | ||
605 | "FORMAT 0 - Status Not As " | ||
606 | "Required: reason %02x", sense[8]); | ||
607 | break; | ||
608 | default: | ||
609 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
610 | "FORMAT 0 - Reseved"); | ||
611 | } | ||
612 | } else { | ||
613 | switch (msg_no) { | ||
614 | case 0x00: /* No Message */ | ||
615 | break; | ||
616 | case 0x01: | ||
617 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
618 | "FORMAT 0 - Device Error Source"); | ||
619 | break; | ||
620 | case 0x02: | ||
621 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
622 | "FORMAT 0 - Reserved"); | ||
623 | break; | ||
624 | case 0x03: | ||
625 | DEV_MESSAGE(KERN_WARNING, device, | ||
626 | "FORMAT 0 - Device Fenced - " | ||
627 | "device = %02x", sense[4]); | ||
628 | break; | ||
629 | case 0x04: | ||
630 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
631 | "FORMAT 0 - Data Pinned for " | ||
632 | "Device"); | ||
633 | break; | ||
634 | default: | ||
635 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
636 | "FORMAT 0 - Reserved"); | ||
637 | } | ||
638 | } | ||
639 | break; | ||
640 | |||
641 | case 0x10: /* Format 1 - Device Equipment Checks */ | ||
642 | switch (msg_no) { | ||
643 | case 0x00: /* No Message */ | ||
644 | break; | ||
645 | case 0x01: | ||
646 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
647 | "FORMAT 1 - Device Status 1 not as " | ||
648 | "expected"); | ||
649 | break; | ||
650 | case 0x03: | ||
651 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
652 | "FORMAT 1 - Index missing"); | ||
653 | break; | ||
654 | case 0x04: | ||
655 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
656 | "FORMAT 1 - Interruption cannot be reset"); | ||
657 | break; | ||
658 | case 0x05: | ||
659 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
660 | "FORMAT 1 - Device did not respond to " | ||
661 | "selection"); | ||
662 | break; | ||
663 | case 0x06: | ||
664 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
665 | "FORMAT 1 - Device check-2 error or Set " | ||
666 | "Sector is not complete"); | ||
667 | break; | ||
668 | case 0x07: | ||
669 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
670 | "FORMAT 1 - Head address does not " | ||
671 | "compare"); | ||
672 | break; | ||
673 | case 0x08: | ||
674 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
675 | "FORMAT 1 - Device status 1 not valid"); | ||
676 | break; | ||
677 | case 0x09: | ||
678 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
679 | "FORMAT 1 - Device not ready"); | ||
680 | break; | ||
681 | case 0x0A: | ||
682 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
683 | "FORMAT 1 - Track physical address did " | ||
684 | "not compare"); | ||
685 | break; | ||
686 | case 0x0B: | ||
687 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
688 | "FORMAT 1 - Missing device address bit"); | ||
689 | break; | ||
690 | case 0x0C: | ||
691 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
692 | "FORMAT 1 - Drive motor switch is off"); | ||
693 | break; | ||
694 | case 0x0D: | ||
695 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
696 | "FORMAT 1 - Seek incomplete"); | ||
697 | break; | ||
698 | case 0x0E: | ||
699 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
700 | "FORMAT 1 - Cylinder address did not " | ||
701 | "compare"); | ||
702 | break; | ||
703 | case 0x0F: | ||
704 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
705 | "FORMAT 1 - Offset active cannot be " | ||
706 | "reset"); | ||
707 | break; | ||
708 | default: | ||
709 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
710 | "FORMAT 1 - Reserved"); | ||
711 | } | ||
712 | break; | ||
713 | |||
714 | case 0x20: /* Format 2 - 3990 Equipment Checks */ | ||
715 | switch (msg_no) { | ||
716 | case 0x08: | ||
717 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
718 | "FORMAT 2 - 3990 check-2 error"); | ||
719 | break; | ||
720 | case 0x0E: | ||
721 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
722 | "FORMAT 2 - Support facility errors"); | ||
723 | break; | ||
724 | case 0x0F: | ||
725 | DEV_MESSAGE(KERN_WARNING, device, | ||
726 | "FORMAT 2 - Microcode detected error %02x", | ||
727 | sense[8]); | ||
728 | break; | ||
729 | default: | ||
730 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
731 | "FORMAT 2 - Reserved"); | ||
732 | } | ||
733 | break; | ||
734 | |||
735 | case 0x30: /* Format 3 - 3990 Control Checks */ | ||
736 | switch (msg_no) { | ||
737 | case 0x0F: | ||
738 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
739 | "FORMAT 3 - Allegiance terminated"); | ||
740 | break; | ||
741 | default: | ||
742 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
743 | "FORMAT 3 - Reserved"); | ||
744 | } | ||
745 | break; | ||
746 | |||
747 | case 0x40: /* Format 4 - Data Checks */ | ||
748 | switch (msg_no) { | ||
749 | case 0x00: | ||
750 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
751 | "FORMAT 4 - Home address area error"); | ||
752 | break; | ||
753 | case 0x01: | ||
754 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
755 | "FORMAT 4 - Count area error"); | ||
756 | break; | ||
757 | case 0x02: | ||
758 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
759 | "FORMAT 4 - Key area error"); | ||
760 | break; | ||
761 | case 0x03: | ||
762 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
763 | "FORMAT 4 - Data area error"); | ||
764 | break; | ||
765 | case 0x04: | ||
766 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
767 | "FORMAT 4 - No sync byte in home address " | ||
768 | "area"); | ||
769 | break; | ||
770 | case 0x05: | ||
771 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
772 | "FORMAT 4 - No sync byte in count address " | ||
773 | "area"); | ||
774 | break; | ||
775 | case 0x06: | ||
776 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
777 | "FORMAT 4 - No sync byte in key area"); | ||
778 | break; | ||
779 | case 0x07: | ||
780 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
781 | "FORMAT 4 - No sync byte in data area"); | ||
782 | break; | ||
783 | case 0x08: | ||
784 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
785 | "FORMAT 4 - Home address area error; " | ||
786 | "offset active"); | ||
787 | break; | ||
788 | case 0x09: | ||
789 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
790 | "FORMAT 4 - Count area error; offset " | ||
791 | "active"); | ||
792 | break; | ||
793 | case 0x0A: | ||
794 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
795 | "FORMAT 4 - Key area error; offset " | ||
796 | "active"); | ||
797 | break; | ||
798 | case 0x0B: | ||
799 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
800 | "FORMAT 4 - Data area error; " | ||
801 | "offset active"); | ||
802 | break; | ||
803 | case 0x0C: | ||
804 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
805 | "FORMAT 4 - No sync byte in home " | ||
806 | "address area; offset active"); | ||
807 | break; | ||
808 | case 0x0D: | ||
809 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
810 | "FORMAT 4 - No syn byte in count " | ||
811 | "address area; offset active"); | ||
812 | break; | ||
813 | case 0x0E: | ||
814 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
815 | "FORMAT 4 - No sync byte in key area; " | ||
816 | "offset active"); | ||
817 | break; | ||
818 | case 0x0F: | ||
819 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
820 | "FORMAT 4 - No syn byte in data area; " | ||
821 | "offset active"); | ||
822 | break; | ||
823 | default: | ||
824 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
825 | "FORMAT 4 - Reserved"); | ||
826 | } | ||
827 | break; | ||
828 | |||
829 | case 0x50: /* Format 5 - Data Check with displacement information */ | ||
830 | switch (msg_no) { | ||
831 | case 0x00: | ||
832 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
833 | "FORMAT 5 - Data Check in the " | ||
834 | "home address area"); | ||
835 | break; | ||
836 | case 0x01: | ||
837 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
838 | "FORMAT 5 - Data Check in the count area"); | ||
839 | break; | ||
840 | case 0x02: | ||
841 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
842 | "FORMAT 5 - Data Check in the key area"); | ||
843 | break; | ||
844 | case 0x03: | ||
845 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
846 | "FORMAT 5 - Data Check in the data area"); | ||
847 | break; | ||
848 | case 0x08: | ||
849 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
850 | "FORMAT 5 - Data Check in the " | ||
851 | "home address area; offset active"); | ||
852 | break; | ||
853 | case 0x09: | ||
854 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
855 | "FORMAT 5 - Data Check in the count area; " | ||
856 | "offset active"); | ||
857 | break; | ||
858 | case 0x0A: | ||
859 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
860 | "FORMAT 5 - Data Check in the key area; " | ||
861 | "offset active"); | ||
862 | break; | ||
863 | case 0x0B: | ||
864 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
865 | "FORMAT 5 - Data Check in the data area; " | ||
866 | "offset active"); | ||
867 | break; | ||
868 | default: | ||
869 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
870 | "FORMAT 5 - Reserved"); | ||
871 | } | ||
872 | break; | ||
873 | |||
874 | case 0x60: /* Format 6 - Usage Statistics/Overrun Errors */ | ||
875 | switch (msg_no) { | ||
876 | case 0x00: | ||
877 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
878 | "FORMAT 6 - Overrun on channel A"); | ||
879 | break; | ||
880 | case 0x01: | ||
881 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
882 | "FORMAT 6 - Overrun on channel B"); | ||
883 | break; | ||
884 | case 0x02: | ||
885 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
886 | "FORMAT 6 - Overrun on channel C"); | ||
887 | break; | ||
888 | case 0x03: | ||
889 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
890 | "FORMAT 6 - Overrun on channel D"); | ||
891 | break; | ||
892 | case 0x04: | ||
893 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
894 | "FORMAT 6 - Overrun on channel E"); | ||
895 | break; | ||
896 | case 0x05: | ||
897 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
898 | "FORMAT 6 - Overrun on channel F"); | ||
899 | break; | ||
900 | case 0x06: | ||
901 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
902 | "FORMAT 6 - Overrun on channel G"); | ||
903 | break; | ||
904 | case 0x07: | ||
905 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
906 | "FORMAT 6 - Overrun on channel H"); | ||
907 | break; | ||
908 | default: | ||
909 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
910 | "FORMAT 6 - Reserved"); | ||
911 | } | ||
912 | break; | ||
913 | |||
914 | case 0x70: /* Format 7 - Device Connection Control Checks */ | ||
915 | switch (msg_no) { | ||
916 | case 0x00: | ||
917 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
918 | "FORMAT 7 - RCC initiated by a connection " | ||
919 | "check alert"); | ||
920 | break; | ||
921 | case 0x01: | ||
922 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
923 | "FORMAT 7 - RCC 1 sequence not " | ||
924 | "successful"); | ||
925 | break; | ||
926 | case 0x02: | ||
927 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
928 | "FORMAT 7 - RCC 1 and RCC 2 sequences not " | ||
929 | "successful"); | ||
930 | break; | ||
931 | case 0x03: | ||
932 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
933 | "FORMAT 7 - Invalid tag-in during " | ||
934 | "selection sequence"); | ||
935 | break; | ||
936 | case 0x04: | ||
937 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
938 | "FORMAT 7 - extra RCC required"); | ||
939 | break; | ||
940 | case 0x05: | ||
941 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
942 | "FORMAT 7 - Invalid DCC selection " | ||
943 | "response or timeout"); | ||
944 | break; | ||
945 | case 0x06: | ||
946 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
947 | "FORMAT 7 - Missing end operation; device " | ||
948 | "transfer complete"); | ||
949 | break; | ||
950 | case 0x07: | ||
951 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
952 | "FORMAT 7 - Missing end operation; device " | ||
953 | "transfer incomplete"); | ||
954 | break; | ||
955 | case 0x08: | ||
956 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
957 | "FORMAT 7 - Invalid tag-in for an " | ||
958 | "immediate command sequence"); | ||
959 | break; | ||
960 | case 0x09: | ||
961 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
962 | "FORMAT 7 - Invalid tag-in for an " | ||
963 | "extended command sequence"); | ||
964 | break; | ||
965 | case 0x0A: | ||
966 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
967 | "FORMAT 7 - 3990 microcode time out when " | ||
968 | "stopping selection"); | ||
969 | break; | ||
970 | case 0x0B: | ||
971 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
972 | "FORMAT 7 - No response to selection " | ||
973 | "after a poll interruption"); | ||
974 | break; | ||
975 | case 0x0C: | ||
976 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
977 | "FORMAT 7 - Permanent path error (DASD " | ||
978 | "controller not available)"); | ||
979 | break; | ||
980 | case 0x0D: | ||
981 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
982 | "FORMAT 7 - DASD controller not available" | ||
983 | " on disconnected command chain"); | ||
984 | break; | ||
985 | default: | ||
986 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
987 | "FORMAT 7 - Reserved"); | ||
988 | } | ||
989 | break; | ||
990 | |||
991 | case 0x80: /* Format 8 - Additional Device Equipment Checks */ | ||
992 | switch (msg_no) { | ||
993 | case 0x00: /* No Message */ | ||
994 | case 0x01: | ||
995 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
996 | "FORMAT 8 - Error correction code " | ||
997 | "hardware fault"); | ||
998 | break; | ||
999 | case 0x03: | ||
1000 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1001 | "FORMAT 8 - Unexpected end operation " | ||
1002 | "response code"); | ||
1003 | break; | ||
1004 | case 0x04: | ||
1005 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1006 | "FORMAT 8 - End operation with transfer " | ||
1007 | "count not zero"); | ||
1008 | break; | ||
1009 | case 0x05: | ||
1010 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1011 | "FORMAT 8 - End operation with transfer " | ||
1012 | "count zero"); | ||
1013 | break; | ||
1014 | case 0x06: | ||
1015 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1016 | "FORMAT 8 - DPS checks after a system " | ||
1017 | "reset or selective reset"); | ||
1018 | break; | ||
1019 | case 0x07: | ||
1020 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1021 | "FORMAT 8 - DPS cannot be filled"); | ||
1022 | break; | ||
1023 | case 0x08: | ||
1024 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1025 | "FORMAT 8 - Short busy time-out during " | ||
1026 | "device selection"); | ||
1027 | break; | ||
1028 | case 0x09: | ||
1029 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1030 | "FORMAT 8 - DASD controller failed to " | ||
1031 | "set or reset the long busy latch"); | ||
1032 | break; | ||
1033 | case 0x0A: | ||
1034 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1035 | "FORMAT 8 - No interruption from device " | ||
1036 | "during a command chain"); | ||
1037 | break; | ||
1038 | default: | ||
1039 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1040 | "FORMAT 8 - Reserved"); | ||
1041 | } | ||
1042 | break; | ||
1043 | |||
1044 | case 0x90: /* Format 9 - Device Read, Write, and Seek Checks */ | ||
1045 | switch (msg_no) { | ||
1046 | case 0x00: | ||
1047 | break; /* No Message */ | ||
1048 | case 0x06: | ||
1049 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1050 | "FORMAT 9 - Device check-2 error"); | ||
1051 | break; | ||
1052 | case 0x07: | ||
1053 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1054 | "FORMAT 9 - Head address did not compare"); | ||
1055 | break; | ||
1056 | case 0x0A: | ||
1057 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1058 | "FORMAT 9 - Track physical address did " | ||
1059 | "not compare while oriented"); | ||
1060 | break; | ||
1061 | case 0x0E: | ||
1062 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1063 | "FORMAT 9 - Cylinder address did not " | ||
1064 | "compare"); | ||
1065 | break; | ||
1066 | default: | ||
1067 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1068 | "FORMAT 9 - Reserved"); | ||
1069 | } | ||
1070 | break; | ||
1071 | |||
1072 | case 0xF0: /* Format F - Cache Storage Checks */ | ||
1073 | switch (msg_no) { | ||
1074 | case 0x00: | ||
1075 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1076 | "FORMAT F - Operation Terminated"); | ||
1077 | break; | ||
1078 | case 0x01: | ||
1079 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1080 | "FORMAT F - Subsystem Processing Error"); | ||
1081 | break; | ||
1082 | case 0x02: | ||
1083 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1084 | "FORMAT F - Cache or nonvolatile storage " | ||
1085 | "equipment failure"); | ||
1086 | break; | ||
1087 | case 0x04: | ||
1088 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1089 | "FORMAT F - Caching terminated"); | ||
1090 | break; | ||
1091 | case 0x06: | ||
1092 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1093 | "FORMAT F - Cache fast write access not " | ||
1094 | "authorized"); | ||
1095 | break; | ||
1096 | case 0x07: | ||
1097 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1098 | "FORMAT F - Track format incorrect"); | ||
1099 | break; | ||
1100 | case 0x09: | ||
1101 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1102 | "FORMAT F - Caching reinitiated"); | ||
1103 | break; | ||
1104 | case 0x0A: | ||
1105 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1106 | "FORMAT F - Nonvolatile storage " | ||
1107 | "terminated"); | ||
1108 | break; | ||
1109 | case 0x0B: | ||
1110 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1111 | "FORMAT F - Volume is suspended duplex"); | ||
1112 | break; | ||
1113 | case 0x0C: | ||
1114 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1115 | "FORMAT F - Subsystem status connot be " | ||
1116 | "determined"); | ||
1117 | break; | ||
1118 | case 0x0D: | ||
1119 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1120 | "FORMAT F - Caching status reset to " | ||
1121 | "default"); | ||
1122 | break; | ||
1123 | case 0x0E: | ||
1124 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1125 | "FORMAT F - DASD Fast Write inhibited"); | ||
1126 | break; | ||
1127 | default: | ||
1128 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
1129 | "FORMAT D - Reserved"); | ||
1130 | } | ||
1131 | break; | ||
1132 | |||
1133 | default: /* unknown message format - should not happen */ | ||
1134 | DEV_MESSAGE (KERN_WARNING, device, | ||
1135 | "unknown message format %02x", | ||
1136 | msg_format); | ||
1137 | break; | ||
1138 | } /* end switch message format */ | ||
1139 | |||
1140 | } /* end dasd_3990_handle_env_data */ | ||
1141 | |||
1142 | /* | ||
1143 | * DASD_3990_ERP_COM_REJ | ||
1144 | * | ||
1145 | * DESCRIPTION | ||
1146 | * Handles 24 byte 'Command Reject' error. | ||
1147 | * | ||
1148 | * PARAMETER | ||
1149 | * erp current erp_head | ||
1150 | * sense current sense data | ||
1151 | * | ||
1152 | * RETURN VALUES | ||
1153 | * erp 'new' erp_head - pointer to new ERP | ||
1154 | */ | ||
1155 | static struct dasd_ccw_req * | ||
1156 | dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) | ||
1157 | { | ||
1158 | |||
1159 | struct dasd_device *device = erp->device; | ||
1160 | |||
1161 | erp->function = dasd_3990_erp_com_rej; | ||
1162 | |||
1163 | /* env data present (ACTION 10 - retry should work) */ | ||
1164 | if (sense[2] & SNS2_ENV_DATA_PRESENT) { | ||
1165 | |||
1166 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1167 | "Command Reject - environmental data present"); | ||
1168 | |||
1169 | dasd_3990_handle_env_data(erp, sense); | ||
1170 | |||
1171 | erp->retries = 5; | ||
1172 | |||
1173 | } else { | ||
1174 | /* fatal error - set status to FAILED */ | ||
1175 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1176 | "Command Reject - Fatal error"); | ||
1177 | |||
1178 | erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); | ||
1179 | } | ||
1180 | |||
1181 | return erp; | ||
1182 | |||
1183 | } /* end dasd_3990_erp_com_rej */ | ||
1184 | |||
1185 | /* | ||
1186 | * DASD_3990_ERP_BUS_OUT | ||
1187 | * | ||
1188 | * DESCRIPTION | ||
1189 | * Handles 24 byte 'Bus Out Parity Check' error. | ||
1190 | * | ||
1191 | * PARAMETER | ||
1192 | * erp current erp_head | ||
1193 | * RETURN VALUES | ||
1194 | * erp new erp_head - pointer to new ERP | ||
1195 | */ | ||
1196 | static struct dasd_ccw_req * | ||
1197 | dasd_3990_erp_bus_out(struct dasd_ccw_req * erp) | ||
1198 | { | ||
1199 | |||
1200 | struct dasd_device *device = erp->device; | ||
1201 | |||
1202 | /* first time set initial retry counter and erp_function */ | ||
1203 | /* and retry once without blocking queue */ | ||
1204 | /* (this enables easier enqueing of the cqr) */ | ||
1205 | if (erp->function != dasd_3990_erp_bus_out) { | ||
1206 | erp->retries = 256; | ||
1207 | erp->function = dasd_3990_erp_bus_out; | ||
1208 | |||
1209 | } else { | ||
1210 | |||
1211 | /* issue a message and wait for 'device ready' interrupt */ | ||
1212 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1213 | "bus out parity error or BOPC requested by " | ||
1214 | "channel"); | ||
1215 | |||
1216 | dasd_3990_erp_block_queue(erp, 60*HZ); | ||
1217 | |||
1218 | } | ||
1219 | |||
1220 | return erp; | ||
1221 | |||
1222 | } /* end dasd_3990_erp_bus_out */ | ||
1223 | |||
1224 | /* | ||
1225 | * DASD_3990_ERP_EQUIP_CHECK | ||
1226 | * | ||
1227 | * DESCRIPTION | ||
1228 | * Handles 24 byte 'Equipment Check' error. | ||
1229 | * | ||
1230 | * PARAMETER | ||
1231 | * erp current erp_head | ||
1232 | * RETURN VALUES | ||
1233 | * erp new erp_head - pointer to new ERP | ||
1234 | */ | ||
1235 | static struct dasd_ccw_req * | ||
1236 | dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) | ||
1237 | { | ||
1238 | |||
1239 | struct dasd_device *device = erp->device; | ||
1240 | |||
1241 | erp->function = dasd_3990_erp_equip_check; | ||
1242 | |||
1243 | if (sense[1] & SNS1_WRITE_INHIBITED) { | ||
1244 | |||
1245 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1246 | "Write inhibited path encountered"); | ||
1247 | |||
1248 | /* vary path offline */ | ||
1249 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1250 | "Path should be varied off-line. " | ||
1251 | "This is not implemented yet \n - please report " | ||
1252 | "to linux390@de.ibm.com"); | ||
1253 | |||
1254 | erp = dasd_3990_erp_action_1(erp); | ||
1255 | |||
1256 | } else if (sense[2] & SNS2_ENV_DATA_PRESENT) { | ||
1257 | |||
1258 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1259 | "Equipment Check - " "environmental data present"); | ||
1260 | |||
1261 | dasd_3990_handle_env_data(erp, sense); | ||
1262 | |||
1263 | erp = dasd_3990_erp_action_4(erp, sense); | ||
1264 | |||
1265 | } else if (sense[1] & SNS1_PERM_ERR) { | ||
1266 | |||
1267 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1268 | "Equipment Check - retry exhausted or " | ||
1269 | "undesirable"); | ||
1270 | |||
1271 | erp = dasd_3990_erp_action_1(erp); | ||
1272 | |||
1273 | } else { | ||
1274 | /* all other equipment checks - Action 5 */ | ||
1275 | /* rest is done when retries == 0 */ | ||
1276 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1277 | "Equipment check or processing error"); | ||
1278 | |||
1279 | erp = dasd_3990_erp_action_5(erp); | ||
1280 | } | ||
1281 | |||
1282 | return erp; | ||
1283 | |||
1284 | } /* end dasd_3990_erp_equip_check */ | ||
1285 | |||
1286 | /* | ||
1287 | * DASD_3990_ERP_DATA_CHECK | ||
1288 | * | ||
1289 | * DESCRIPTION | ||
1290 | * Handles 24 byte 'Data Check' error. | ||
1291 | * | ||
1292 | * PARAMETER | ||
1293 | * erp current erp_head | ||
1294 | * RETURN VALUES | ||
1295 | * erp new erp_head - pointer to new ERP | ||
1296 | */ | ||
1297 | static struct dasd_ccw_req * | ||
1298 | dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense) | ||
1299 | { | ||
1300 | |||
1301 | struct dasd_device *device = erp->device; | ||
1302 | |||
1303 | erp->function = dasd_3990_erp_data_check; | ||
1304 | |||
1305 | if (sense[2] & SNS2_CORRECTABLE) { /* correctable data check */ | ||
1306 | |||
1307 | /* issue message that the data has been corrected */ | ||
1308 | DEV_MESSAGE(KERN_EMERG, device, "%s", | ||
1309 | "Data recovered during retry with PCI " | ||
1310 | "fetch mode active"); | ||
1311 | |||
1312 | /* not possible to handle this situation in Linux */ | ||
1313 | panic("No way to inform application about the possibly " | ||
1314 | "incorrect data"); | ||
1315 | |||
1316 | } else if (sense[2] & SNS2_ENV_DATA_PRESENT) { | ||
1317 | |||
1318 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1319 | "Uncorrectable data check recovered secondary " | ||
1320 | "addr of duplex pair"); | ||
1321 | |||
1322 | erp = dasd_3990_erp_action_4(erp, sense); | ||
1323 | |||
1324 | } else if (sense[1] & SNS1_PERM_ERR) { | ||
1325 | |||
1326 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1327 | "Uncorrectable data check with internal " | ||
1328 | "retry exhausted"); | ||
1329 | |||
1330 | erp = dasd_3990_erp_action_1(erp); | ||
1331 | |||
1332 | } else { | ||
1333 | /* all other data checks */ | ||
1334 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1335 | "Uncorrectable data check with retry count " | ||
1336 | "exhausted..."); | ||
1337 | |||
1338 | erp = dasd_3990_erp_action_5(erp); | ||
1339 | } | ||
1340 | |||
1341 | return erp; | ||
1342 | |||
1343 | } /* end dasd_3990_erp_data_check */ | ||
1344 | |||
1345 | /* | ||
1346 | * DASD_3990_ERP_OVERRUN | ||
1347 | * | ||
1348 | * DESCRIPTION | ||
1349 | * Handles 24 byte 'Overrun' error. | ||
1350 | * | ||
1351 | * PARAMETER | ||
1352 | * erp current erp_head | ||
1353 | * RETURN VALUES | ||
1354 | * erp new erp_head - pointer to new ERP | ||
1355 | */ | ||
1356 | static struct dasd_ccw_req * | ||
1357 | dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense) | ||
1358 | { | ||
1359 | |||
1360 | struct dasd_device *device = erp->device; | ||
1361 | |||
1362 | erp->function = dasd_3990_erp_overrun; | ||
1363 | |||
1364 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1365 | "Overrun - service overrun or overrun" | ||
1366 | " error requested by channel"); | ||
1367 | |||
1368 | erp = dasd_3990_erp_action_5(erp); | ||
1369 | |||
1370 | return erp; | ||
1371 | |||
1372 | } /* end dasd_3990_erp_overrun */ | ||
1373 | |||
1374 | /* | ||
1375 | * DASD_3990_ERP_INV_FORMAT | ||
1376 | * | ||
1377 | * DESCRIPTION | ||
1378 | * Handles 24 byte 'Invalid Track Format' error. | ||
1379 | * | ||
1380 | * PARAMETER | ||
1381 | * erp current erp_head | ||
1382 | * RETURN VALUES | ||
1383 | * erp new erp_head - pointer to new ERP | ||
1384 | */ | ||
1385 | static struct dasd_ccw_req * | ||
1386 | dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) | ||
1387 | { | ||
1388 | |||
1389 | struct dasd_device *device = erp->device; | ||
1390 | |||
1391 | erp->function = dasd_3990_erp_inv_format; | ||
1392 | |||
1393 | if (sense[2] & SNS2_ENV_DATA_PRESENT) { | ||
1394 | |||
1395 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1396 | "Track format error when destaging or " | ||
1397 | "staging data"); | ||
1398 | |||
1399 | dasd_3990_handle_env_data(erp, sense); | ||
1400 | |||
1401 | erp = dasd_3990_erp_action_4(erp, sense); | ||
1402 | |||
1403 | } else { | ||
1404 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1405 | "Invalid Track Format - Fatal error should have " | ||
1406 | "been handled within the interrupt handler"); | ||
1407 | |||
1408 | erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); | ||
1409 | } | ||
1410 | |||
1411 | return erp; | ||
1412 | |||
1413 | } /* end dasd_3990_erp_inv_format */ | ||
1414 | |||
1415 | /* | ||
1416 | * DASD_3990_ERP_EOC | ||
1417 | * | ||
1418 | * DESCRIPTION | ||
1419 | * Handles 24 byte 'End-of-Cylinder' error. | ||
1420 | * | ||
1421 | * PARAMETER | ||
1422 | * erp already added default erp | ||
1423 | * RETURN VALUES | ||
1424 | * erp pointer to original (failed) cqr. | ||
1425 | */ | ||
1426 | static struct dasd_ccw_req * | ||
1427 | dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense) | ||
1428 | { | ||
1429 | |||
1430 | struct dasd_device *device = default_erp->device; | ||
1431 | |||
1432 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1433 | "End-of-Cylinder - must never happen"); | ||
1434 | |||
1435 | /* implement action 7 - BUG */ | ||
1436 | return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); | ||
1437 | |||
1438 | } /* end dasd_3990_erp_EOC */ | ||
1439 | |||
1440 | /* | ||
1441 | * DASD_3990_ERP_ENV_DATA | ||
1442 | * | ||
1443 | * DESCRIPTION | ||
1444 | * Handles 24 byte 'Environmental-Data Present' error. | ||
1445 | * | ||
1446 | * PARAMETER | ||
1447 | * erp current erp_head | ||
1448 | * RETURN VALUES | ||
1449 | * erp new erp_head - pointer to new ERP | ||
1450 | */ | ||
1451 | static struct dasd_ccw_req * | ||
1452 | dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) | ||
1453 | { | ||
1454 | |||
1455 | struct dasd_device *device = erp->device; | ||
1456 | |||
1457 | erp->function = dasd_3990_erp_env_data; | ||
1458 | |||
1459 | DEV_MESSAGE(KERN_DEBUG, device, "%s", "Environmental data present"); | ||
1460 | |||
1461 | dasd_3990_handle_env_data(erp, sense); | ||
1462 | |||
1463 | /* don't retry on disabled interface */ | ||
1464 | if (sense[7] != 0x0F) { | ||
1465 | |||
1466 | erp = dasd_3990_erp_action_4(erp, sense); | ||
1467 | } else { | ||
1468 | |||
1469 | erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO); | ||
1470 | } | ||
1471 | |||
1472 | return erp; | ||
1473 | |||
1474 | } /* end dasd_3990_erp_env_data */ | ||
1475 | |||
1476 | /* | ||
1477 | * DASD_3990_ERP_NO_REC | ||
1478 | * | ||
1479 | * DESCRIPTION | ||
1480 | * Handles 24 byte 'No Record Found' error. | ||
1481 | * | ||
1482 | * PARAMETER | ||
1483 | * erp already added default ERP | ||
1484 | * | ||
1485 | * RETURN VALUES | ||
1486 | * erp new erp_head - pointer to new ERP | ||
1487 | */ | ||
1488 | static struct dasd_ccw_req * | ||
1489 | dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense) | ||
1490 | { | ||
1491 | |||
1492 | struct dasd_device *device = default_erp->device; | ||
1493 | |||
1494 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1495 | "No Record Found - Fatal error should " | ||
1496 | "have been handled within the interrupt handler"); | ||
1497 | |||
1498 | return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); | ||
1499 | |||
1500 | } /* end dasd_3990_erp_no_rec */ | ||
1501 | |||
1502 | /* | ||
1503 | * DASD_3990_ERP_FILE_PROT | ||
1504 | * | ||
1505 | * DESCRIPTION | ||
1506 | * Handles 24 byte 'File Protected' error. | ||
1507 | * Note: Seek related recovery is not implemented because | ||
1508 | * wee don't use the seek command yet. | ||
1509 | * | ||
1510 | * PARAMETER | ||
1511 | * erp current erp_head | ||
1512 | * RETURN VALUES | ||
1513 | * erp new erp_head - pointer to new ERP | ||
1514 | */ | ||
1515 | static struct dasd_ccw_req * | ||
1516 | dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) | ||
1517 | { | ||
1518 | |||
1519 | struct dasd_device *device = erp->device; | ||
1520 | |||
1521 | DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected"); | ||
1522 | |||
1523 | return dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); | ||
1524 | |||
1525 | } /* end dasd_3990_erp_file_prot */ | ||
1526 | |||
1527 | /* | ||
1528 | * DASD_3990_ERP_INSPECT_24 | ||
1529 | * | ||
1530 | * DESCRIPTION | ||
1531 | * Does a detailed inspection of the 24 byte sense data | ||
1532 | * and sets up a related error recovery action. | ||
1533 | * | ||
1534 | * PARAMETER | ||
1535 | * sense sense data of the actual error | ||
1536 | * erp pointer to the currently created default ERP | ||
1537 | * | ||
1538 | * RETURN VALUES | ||
1539 | * erp pointer to the (addtitional) ERP | ||
1540 | */ | ||
1541 | static struct dasd_ccw_req * | ||
1542 | dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense) | ||
1543 | { | ||
1544 | |||
1545 | struct dasd_ccw_req *erp_filled = NULL; | ||
1546 | |||
1547 | /* Check sense for .... */ | ||
1548 | /* 'Command Reject' */ | ||
1549 | if ((erp_filled == NULL) && (sense[0] & SNS0_CMD_REJECT)) { | ||
1550 | erp_filled = dasd_3990_erp_com_rej(erp, sense); | ||
1551 | } | ||
1552 | /* 'Intervention Required' */ | ||
1553 | if ((erp_filled == NULL) && (sense[0] & SNS0_INTERVENTION_REQ)) { | ||
1554 | erp_filled = dasd_3990_erp_int_req(erp); | ||
1555 | } | ||
1556 | /* 'Bus Out Parity Check' */ | ||
1557 | if ((erp_filled == NULL) && (sense[0] & SNS0_BUS_OUT_CHECK)) { | ||
1558 | erp_filled = dasd_3990_erp_bus_out(erp); | ||
1559 | } | ||
1560 | /* 'Equipment Check' */ | ||
1561 | if ((erp_filled == NULL) && (sense[0] & SNS0_EQUIPMENT_CHECK)) { | ||
1562 | erp_filled = dasd_3990_erp_equip_check(erp, sense); | ||
1563 | } | ||
1564 | /* 'Data Check' */ | ||
1565 | if ((erp_filled == NULL) && (sense[0] & SNS0_DATA_CHECK)) { | ||
1566 | erp_filled = dasd_3990_erp_data_check(erp, sense); | ||
1567 | } | ||
1568 | /* 'Overrun' */ | ||
1569 | if ((erp_filled == NULL) && (sense[0] & SNS0_OVERRUN)) { | ||
1570 | erp_filled = dasd_3990_erp_overrun(erp, sense); | ||
1571 | } | ||
1572 | /* 'Invalid Track Format' */ | ||
1573 | if ((erp_filled == NULL) && (sense[1] & SNS1_INV_TRACK_FORMAT)) { | ||
1574 | erp_filled = dasd_3990_erp_inv_format(erp, sense); | ||
1575 | } | ||
1576 | /* 'End-of-Cylinder' */ | ||
1577 | if ((erp_filled == NULL) && (sense[1] & SNS1_EOC)) { | ||
1578 | erp_filled = dasd_3990_erp_EOC(erp, sense); | ||
1579 | } | ||
1580 | /* 'Environmental Data' */ | ||
1581 | if ((erp_filled == NULL) && (sense[2] & SNS2_ENV_DATA_PRESENT)) { | ||
1582 | erp_filled = dasd_3990_erp_env_data(erp, sense); | ||
1583 | } | ||
1584 | /* 'No Record Found' */ | ||
1585 | if ((erp_filled == NULL) && (sense[1] & SNS1_NO_REC_FOUND)) { | ||
1586 | erp_filled = dasd_3990_erp_no_rec(erp, sense); | ||
1587 | } | ||
1588 | /* 'File Protected' */ | ||
1589 | if ((erp_filled == NULL) && (sense[1] & SNS1_FILE_PROTECTED)) { | ||
1590 | erp_filled = dasd_3990_erp_file_prot(erp); | ||
1591 | } | ||
1592 | /* other (unknown) error - do default ERP */ | ||
1593 | if (erp_filled == NULL) { | ||
1594 | |||
1595 | erp_filled = erp; | ||
1596 | } | ||
1597 | |||
1598 | return erp_filled; | ||
1599 | |||
1600 | } /* END dasd_3990_erp_inspect_24 */ | ||
1601 | |||
1602 | /* | ||
1603 | ***************************************************************************** | ||
1604 | * 32 byte sense ERP functions (only) | ||
1605 | ***************************************************************************** | ||
1606 | */ | ||
1607 | |||
1608 | /* | ||
1609 | * DASD_3990_ERPACTION_10_32 | ||
1610 | * | ||
1611 | * DESCRIPTION | ||
1612 | * Handles 32 byte 'Action 10' of Single Program Action Codes. | ||
1613 | * Just retry and if retry doesn't work, return with error. | ||
1614 | * | ||
1615 | * PARAMETER | ||
1616 | * erp current erp_head | ||
1617 | * sense current sense data | ||
1618 | * RETURN VALUES | ||
1619 | * erp modified erp_head | ||
1620 | */ | ||
1621 | static struct dasd_ccw_req * | ||
1622 | dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense) | ||
1623 | { | ||
1624 | |||
1625 | struct dasd_device *device = erp->device; | ||
1626 | |||
1627 | erp->retries = 256; | ||
1628 | erp->function = dasd_3990_erp_action_10_32; | ||
1629 | |||
1630 | DEV_MESSAGE(KERN_DEBUG, device, "%s", "Perform logging requested"); | ||
1631 | |||
1632 | return erp; | ||
1633 | |||
1634 | } /* end dasd_3990_erp_action_10_32 */ | ||
1635 | |||
1636 | /* | ||
1637 | * DASD_3990_ERP_ACTION_1B_32 | ||
1638 | * | ||
1639 | * DESCRIPTION | ||
1640 | * Handles 32 byte 'Action 1B' of Single Program Action Codes. | ||
1641 | * A write operation could not be finished because of an unexpected | ||
1642 | * condition. | ||
1643 | * The already created 'default erp' is used to get the link to | ||
1644 | * the erp chain, but it can not be used for this recovery | ||
1645 | * action because it contains no DE/LO data space. | ||
1646 | * | ||
1647 | * PARAMETER | ||
1648 | * default_erp already added default erp. | ||
1649 | * sense current sense data | ||
1650 | * | ||
1651 | * RETURN VALUES | ||
1652 | * erp new erp or | ||
1653 | * default_erp in case of imprecise ending or error | ||
1654 | */ | ||
1655 | static struct dasd_ccw_req * | ||
1656 | dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) | ||
1657 | { | ||
1658 | |||
1659 | struct dasd_device *device = default_erp->device; | ||
1660 | __u32 cpa = 0; | ||
1661 | struct dasd_ccw_req *cqr; | ||
1662 | struct dasd_ccw_req *erp; | ||
1663 | struct DE_eckd_data *DE_data; | ||
1664 | char *LO_data; /* LO_eckd_data_t */ | ||
1665 | struct ccw1 *ccw; | ||
1666 | |||
1667 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1668 | "Write not finished because of unexpected condition"); | ||
1669 | |||
1670 | default_erp->function = dasd_3990_erp_action_1B_32; | ||
1671 | |||
1672 | /* determine the original cqr */ | ||
1673 | cqr = default_erp; | ||
1674 | |||
1675 | while (cqr->refers != NULL) { | ||
1676 | cqr = cqr->refers; | ||
1677 | } | ||
1678 | |||
1679 | /* for imprecise ending just do default erp */ | ||
1680 | if (sense[1] & 0x01) { | ||
1681 | |||
1682 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1683 | "Imprecise ending is set - just retry"); | ||
1684 | |||
1685 | return default_erp; | ||
1686 | } | ||
1687 | |||
1688 | /* determine the address of the CCW to be restarted */ | ||
1689 | /* Imprecise ending is not set -> addr from IRB-SCSW */ | ||
1690 | cpa = default_erp->refers->irb.scsw.cpa; | ||
1691 | |||
1692 | if (cpa == 0) { | ||
1693 | |||
1694 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1695 | "Unable to determine address of the CCW " | ||
1696 | "to be restarted"); | ||
1697 | |||
1698 | return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); | ||
1699 | } | ||
1700 | |||
1701 | /* Build new ERP request including DE/LO */ | ||
1702 | erp = dasd_alloc_erp_request((char *) &cqr->magic, | ||
1703 | 2 + 1,/* DE/LO + TIC */ | ||
1704 | sizeof (struct DE_eckd_data) + | ||
1705 | sizeof (struct LO_eckd_data), device); | ||
1706 | |||
1707 | if (IS_ERR(erp)) { | ||
1708 | DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate ERP"); | ||
1709 | return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); | ||
1710 | } | ||
1711 | |||
1712 | /* use original DE */ | ||
1713 | DE_data = erp->data; | ||
1714 | memcpy(DE_data, cqr->data, sizeof (struct DE_eckd_data)); | ||
1715 | |||
1716 | /* create LO */ | ||
1717 | LO_data = erp->data + sizeof (struct DE_eckd_data); | ||
1718 | |||
1719 | if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) { | ||
1720 | |||
1721 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1722 | "BUG - this should not happen"); | ||
1723 | |||
1724 | return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); | ||
1725 | } | ||
1726 | |||
1727 | if ((sense[7] & 0x3F) == 0x01) { | ||
1728 | /* operation code is WRITE DATA -> data area orientation */ | ||
1729 | LO_data[0] = 0x81; | ||
1730 | |||
1731 | } else if ((sense[7] & 0x3F) == 0x03) { | ||
1732 | /* operation code is FORMAT WRITE -> index orientation */ | ||
1733 | LO_data[0] = 0xC3; | ||
1734 | |||
1735 | } else { | ||
1736 | LO_data[0] = sense[7]; /* operation */ | ||
1737 | } | ||
1738 | |||
1739 | LO_data[1] = sense[8]; /* auxiliary */ | ||
1740 | LO_data[2] = sense[9]; | ||
1741 | LO_data[3] = sense[3]; /* count */ | ||
1742 | LO_data[4] = sense[29]; /* seek_addr.cyl */ | ||
1743 | LO_data[5] = sense[30]; /* seek_addr.cyl 2nd byte */ | ||
1744 | LO_data[7] = sense[31]; /* seek_addr.head 2nd byte */ | ||
1745 | |||
1746 | memcpy(&(LO_data[8]), &(sense[11]), 8); | ||
1747 | |||
1748 | /* create DE ccw */ | ||
1749 | ccw = erp->cpaddr; | ||
1750 | memset(ccw, 0, sizeof (struct ccw1)); | ||
1751 | ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT; | ||
1752 | ccw->flags = CCW_FLAG_CC; | ||
1753 | ccw->count = 16; | ||
1754 | ccw->cda = (__u32)(addr_t) DE_data; | ||
1755 | |||
1756 | /* create LO ccw */ | ||
1757 | ccw++; | ||
1758 | memset(ccw, 0, sizeof (struct ccw1)); | ||
1759 | ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD; | ||
1760 | ccw->flags = CCW_FLAG_CC; | ||
1761 | ccw->count = 16; | ||
1762 | ccw->cda = (__u32)(addr_t) LO_data; | ||
1763 | |||
1764 | /* TIC to the failed ccw */ | ||
1765 | ccw++; | ||
1766 | ccw->cmd_code = CCW_CMD_TIC; | ||
1767 | ccw->cda = cpa; | ||
1768 | |||
1769 | /* fill erp related fields */ | ||
1770 | erp->function = dasd_3990_erp_action_1B_32; | ||
1771 | erp->refers = default_erp->refers; | ||
1772 | erp->device = device; | ||
1773 | erp->magic = default_erp->magic; | ||
1774 | erp->expires = 0; | ||
1775 | erp->retries = 256; | ||
1776 | erp->buildclk = get_clock(); | ||
1777 | erp->status = DASD_CQR_FILLED; | ||
1778 | |||
1779 | /* remove the default erp */ | ||
1780 | dasd_free_erp_request(default_erp, device); | ||
1781 | |||
1782 | return erp; | ||
1783 | |||
1784 | } /* end dasd_3990_erp_action_1B_32 */ | ||
1785 | |||
1786 | /* | ||
1787 | * DASD_3990_UPDATE_1B | ||
1788 | * | ||
1789 | * DESCRIPTION | ||
1790 | * Handles the update to the 32 byte 'Action 1B' of Single Program | ||
1791 | * Action Codes in case the first action was not successful. | ||
1792 | * The already created 'previous_erp' is the currently not successful | ||
1793 | * ERP. | ||
1794 | * | ||
1795 | * PARAMETER | ||
1796 | * previous_erp already created previous erp. | ||
1797 | * sense current sense data | ||
1798 | * RETURN VALUES | ||
1799 | * erp modified erp | ||
1800 | */ | ||
1801 | static struct dasd_ccw_req * | ||
1802 | dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) | ||
1803 | { | ||
1804 | |||
1805 | struct dasd_device *device = previous_erp->device; | ||
1806 | __u32 cpa = 0; | ||
1807 | struct dasd_ccw_req *cqr; | ||
1808 | struct dasd_ccw_req *erp; | ||
1809 | char *LO_data; /* struct LO_eckd_data */ | ||
1810 | struct ccw1 *ccw; | ||
1811 | |||
1812 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1813 | "Write not finished because of unexpected condition" | ||
1814 | " - follow on"); | ||
1815 | |||
1816 | /* determine the original cqr */ | ||
1817 | cqr = previous_erp; | ||
1818 | |||
1819 | while (cqr->refers != NULL) { | ||
1820 | cqr = cqr->refers; | ||
1821 | } | ||
1822 | |||
1823 | /* for imprecise ending just do default erp */ | ||
1824 | if (sense[1] & 0x01) { | ||
1825 | |||
1826 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1827 | "Imprecise ending is set - just retry"); | ||
1828 | |||
1829 | previous_erp->status = DASD_CQR_QUEUED; | ||
1830 | |||
1831 | return previous_erp; | ||
1832 | } | ||
1833 | |||
1834 | /* determine the address of the CCW to be restarted */ | ||
1835 | /* Imprecise ending is not set -> addr from IRB-SCSW */ | ||
1836 | cpa = previous_erp->irb.scsw.cpa; | ||
1837 | |||
1838 | if (cpa == 0) { | ||
1839 | |||
1840 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
1841 | "Unable to determine address of the CCW " | ||
1842 | "to be restarted"); | ||
1843 | |||
1844 | previous_erp->status = DASD_CQR_FAILED; | ||
1845 | |||
1846 | return previous_erp; | ||
1847 | } | ||
1848 | |||
1849 | erp = previous_erp; | ||
1850 | |||
1851 | /* update the LO with the new returned sense data */ | ||
1852 | LO_data = erp->data + sizeof (struct DE_eckd_data); | ||
1853 | |||
1854 | if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) { | ||
1855 | |||
1856 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
1857 | "BUG - this should not happen"); | ||
1858 | |||
1859 | previous_erp->status = DASD_CQR_FAILED; | ||
1860 | |||
1861 | return previous_erp; | ||
1862 | } | ||
1863 | |||
1864 | if ((sense[7] & 0x3F) == 0x01) { | ||
1865 | /* operation code is WRITE DATA -> data area orientation */ | ||
1866 | LO_data[0] = 0x81; | ||
1867 | |||
1868 | } else if ((sense[7] & 0x3F) == 0x03) { | ||
1869 | /* operation code is FORMAT WRITE -> index orientation */ | ||
1870 | LO_data[0] = 0xC3; | ||
1871 | |||
1872 | } else { | ||
1873 | LO_data[0] = sense[7]; /* operation */ | ||
1874 | } | ||
1875 | |||
1876 | LO_data[1] = sense[8]; /* auxiliary */ | ||
1877 | LO_data[2] = sense[9]; | ||
1878 | LO_data[3] = sense[3]; /* count */ | ||
1879 | LO_data[4] = sense[29]; /* seek_addr.cyl */ | ||
1880 | LO_data[5] = sense[30]; /* seek_addr.cyl 2nd byte */ | ||
1881 | LO_data[7] = sense[31]; /* seek_addr.head 2nd byte */ | ||
1882 | |||
1883 | memcpy(&(LO_data[8]), &(sense[11]), 8); | ||
1884 | |||
1885 | /* TIC to the failed ccw */ | ||
1886 | ccw = erp->cpaddr; /* addr of DE ccw */ | ||
1887 | ccw++; /* addr of LE ccw */ | ||
1888 | ccw++; /* addr of TIC ccw */ | ||
1889 | ccw->cda = cpa; | ||
1890 | |||
1891 | erp->status = DASD_CQR_QUEUED; | ||
1892 | |||
1893 | return erp; | ||
1894 | |||
1895 | } /* end dasd_3990_update_1B */ | ||
1896 | |||
1897 | /* | ||
1898 | * DASD_3990_ERP_COMPOUND_RETRY | ||
1899 | * | ||
1900 | * DESCRIPTION | ||
1901 | * Handles the compound ERP action retry code. | ||
1902 | * NOTE: At least one retry is done even if zero is specified | ||
1903 | * by the sense data. This makes enqueueing of the request | ||
1904 | * easier. | ||
1905 | * | ||
1906 | * PARAMETER | ||
1907 | * sense sense data of the actual error | ||
1908 | * erp pointer to the currently created ERP | ||
1909 | * | ||
1910 | * RETURN VALUES | ||
1911 | * erp modified ERP pointer | ||
1912 | * | ||
1913 | */ | ||
1914 | static void | ||
1915 | dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense) | ||
1916 | { | ||
1917 | |||
1918 | switch (sense[25] & 0x03) { | ||
1919 | case 0x00: /* no not retry */ | ||
1920 | erp->retries = 1; | ||
1921 | break; | ||
1922 | |||
1923 | case 0x01: /* retry 2 times */ | ||
1924 | erp->retries = 2; | ||
1925 | break; | ||
1926 | |||
1927 | case 0x02: /* retry 10 times */ | ||
1928 | erp->retries = 10; | ||
1929 | break; | ||
1930 | |||
1931 | case 0x03: /* retry 256 times */ | ||
1932 | erp->retries = 256; | ||
1933 | break; | ||
1934 | |||
1935 | default: | ||
1936 | BUG(); | ||
1937 | } | ||
1938 | |||
1939 | erp->function = dasd_3990_erp_compound_retry; | ||
1940 | |||
1941 | } /* end dasd_3990_erp_compound_retry */ | ||
1942 | |||
1943 | /* | ||
1944 | * DASD_3990_ERP_COMPOUND_PATH | ||
1945 | * | ||
1946 | * DESCRIPTION | ||
1947 | * Handles the compound ERP action for retry on alternate | ||
1948 | * channel path. | ||
1949 | * | ||
1950 | * PARAMETER | ||
1951 | * sense sense data of the actual error | ||
1952 | * erp pointer to the currently created ERP | ||
1953 | * | ||
1954 | * RETURN VALUES | ||
1955 | * erp modified ERP pointer | ||
1956 | * | ||
1957 | */ | ||
1958 | static void | ||
1959 | dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) | ||
1960 | { | ||
1961 | |||
1962 | if (sense[25] & DASD_SENSE_BIT_3) { | ||
1963 | dasd_3990_erp_alternate_path(erp); | ||
1964 | |||
1965 | if (erp->status == DASD_CQR_FAILED) { | ||
1966 | /* reset the lpm and the status to be able to | ||
1967 | * try further actions. */ | ||
1968 | |||
1969 | erp->lpm = 0; | ||
1970 | |||
1971 | erp->status = DASD_CQR_ERROR; | ||
1972 | |||
1973 | } | ||
1974 | } | ||
1975 | |||
1976 | erp->function = dasd_3990_erp_compound_path; | ||
1977 | |||
1978 | } /* end dasd_3990_erp_compound_path */ | ||
1979 | |||
1980 | /* | ||
1981 | * DASD_3990_ERP_COMPOUND_CODE | ||
1982 | * | ||
1983 | * DESCRIPTION | ||
1984 | * Handles the compound ERP action for retry code. | ||
1985 | * | ||
1986 | * PARAMETER | ||
1987 | * sense sense data of the actual error | ||
1988 | * erp pointer to the currently created ERP | ||
1989 | * | ||
1990 | * RETURN VALUES | ||
1991 | * erp NEW ERP pointer | ||
1992 | * | ||
1993 | */ | ||
1994 | static struct dasd_ccw_req * | ||
1995 | dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense) | ||
1996 | { | ||
1997 | |||
1998 | if (sense[25] & DASD_SENSE_BIT_2) { | ||
1999 | |||
2000 | switch (sense[28]) { | ||
2001 | case 0x17: | ||
2002 | /* issue a Diagnostic Control command with an | ||
2003 | * Inhibit Write subcommand and controler modifier */ | ||
2004 | erp = dasd_3990_erp_DCTL(erp, 0x20); | ||
2005 | break; | ||
2006 | |||
2007 | case 0x25: | ||
2008 | /* wait for 5 seconds and retry again */ | ||
2009 | erp->retries = 1; | ||
2010 | |||
2011 | dasd_3990_erp_block_queue (erp, 5*HZ); | ||
2012 | break; | ||
2013 | |||
2014 | default: | ||
2015 | /* should not happen - continue */ | ||
2016 | break; | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2020 | erp->function = dasd_3990_erp_compound_code; | ||
2021 | |||
2022 | return erp; | ||
2023 | |||
2024 | } /* end dasd_3990_erp_compound_code */ | ||
2025 | |||
2026 | /* | ||
2027 | * DASD_3990_ERP_COMPOUND_CONFIG | ||
2028 | * | ||
2029 | * DESCRIPTION | ||
2030 | * Handles the compound ERP action for configruation | ||
2031 | * dependent error. | ||
2032 | * Note: duplex handling is not implemented (yet). | ||
2033 | * | ||
2034 | * PARAMETER | ||
2035 | * sense sense data of the actual error | ||
2036 | * erp pointer to the currently created ERP | ||
2037 | * | ||
2038 | * RETURN VALUES | ||
2039 | * erp modified ERP pointer | ||
2040 | * | ||
2041 | */ | ||
2042 | static void | ||
2043 | dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense) | ||
2044 | { | ||
2045 | |||
2046 | if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) { | ||
2047 | |||
2048 | /* set to suspended duplex state then restart */ | ||
2049 | struct dasd_device *device = erp->device; | ||
2050 | |||
2051 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
2052 | "Set device to suspended duplex state should be " | ||
2053 | "done!\n" | ||
2054 | "This is not implemented yet (for compound ERP)" | ||
2055 | " - please report to linux390@de.ibm.com"); | ||
2056 | |||
2057 | } | ||
2058 | |||
2059 | erp->function = dasd_3990_erp_compound_config; | ||
2060 | |||
2061 | } /* end dasd_3990_erp_compound_config */ | ||
2062 | |||
2063 | /* | ||
2064 | * DASD_3990_ERP_COMPOUND | ||
2065 | * | ||
2066 | * DESCRIPTION | ||
2067 | * Does the further compound program action if | ||
2068 | * compound retry was not successful. | ||
2069 | * | ||
2070 | * PARAMETER | ||
2071 | * sense sense data of the actual error | ||
2072 | * erp pointer to the current (failed) ERP | ||
2073 | * | ||
2074 | * RETURN VALUES | ||
2075 | * erp (additional) ERP pointer | ||
2076 | * | ||
2077 | */ | ||
2078 | static struct dasd_ccw_req * | ||
2079 | dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) | ||
2080 | { | ||
2081 | |||
2082 | if ((erp->function == dasd_3990_erp_compound_retry) && | ||
2083 | (erp->status == DASD_CQR_ERROR)) { | ||
2084 | |||
2085 | dasd_3990_erp_compound_path(erp, sense); | ||
2086 | } | ||
2087 | |||
2088 | if ((erp->function == dasd_3990_erp_compound_path) && | ||
2089 | (erp->status == DASD_CQR_ERROR)) { | ||
2090 | |||
2091 | erp = dasd_3990_erp_compound_code(erp, sense); | ||
2092 | } | ||
2093 | |||
2094 | if ((erp->function == dasd_3990_erp_compound_code) && | ||
2095 | (erp->status == DASD_CQR_ERROR)) { | ||
2096 | |||
2097 | dasd_3990_erp_compound_config(erp, sense); | ||
2098 | } | ||
2099 | |||
2100 | /* if no compound action ERP specified, the request failed */ | ||
2101 | if (erp->status == DASD_CQR_ERROR) { | ||
2102 | |||
2103 | erp->status = DASD_CQR_FAILED; | ||
2104 | } | ||
2105 | |||
2106 | return erp; | ||
2107 | |||
2108 | } /* end dasd_3990_erp_compound */ | ||
2109 | |||
2110 | /* | ||
2111 | * DASD_3990_ERP_INSPECT_32 | ||
2112 | * | ||
2113 | * DESCRIPTION | ||
2114 | * Does a detailed inspection of the 32 byte sense data | ||
2115 | * and sets up a related error recovery action. | ||
2116 | * | ||
2117 | * PARAMETER | ||
2118 | * sense sense data of the actual error | ||
2119 | * erp pointer to the currently created default ERP | ||
2120 | * | ||
2121 | * RETURN VALUES | ||
2122 | * erp_filled pointer to the ERP | ||
2123 | * | ||
2124 | */ | ||
2125 | static struct dasd_ccw_req * | ||
2126 | dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) | ||
2127 | { | ||
2128 | |||
2129 | struct dasd_device *device = erp->device; | ||
2130 | |||
2131 | erp->function = dasd_3990_erp_inspect_32; | ||
2132 | |||
2133 | if (sense[25] & DASD_SENSE_BIT_0) { | ||
2134 | |||
2135 | /* compound program action codes (byte25 bit 0 == '1') */ | ||
2136 | dasd_3990_erp_compound_retry(erp, sense); | ||
2137 | |||
2138 | } else { | ||
2139 | |||
2140 | /* single program action codes (byte25 bit 0 == '0') */ | ||
2141 | switch (sense[25]) { | ||
2142 | |||
2143 | case 0x00: /* success - use default ERP for retries */ | ||
2144 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
2145 | "ERP called for successful request" | ||
2146 | " - just retry"); | ||
2147 | break; | ||
2148 | |||
2149 | case 0x01: /* fatal error */ | ||
2150 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
2151 | "Fatal error should have been " | ||
2152 | "handled within the interrupt handler"); | ||
2153 | |||
2154 | erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); | ||
2155 | break; | ||
2156 | |||
2157 | case 0x02: /* intervention required */ | ||
2158 | case 0x03: /* intervention required during dual copy */ | ||
2159 | erp = dasd_3990_erp_int_req(erp); | ||
2160 | break; | ||
2161 | |||
2162 | case 0x0F: /* length mismatch during update write command */ | ||
2163 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
2164 | "update write command error - should not " | ||
2165 | "happen;\n" | ||
2166 | "Please send this message together with " | ||
2167 | "the above sense data to linux390@de." | ||
2168 | "ibm.com"); | ||
2169 | |||
2170 | erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); | ||
2171 | break; | ||
2172 | |||
2173 | case 0x10: /* logging required for other channel program */ | ||
2174 | erp = dasd_3990_erp_action_10_32(erp, sense); | ||
2175 | break; | ||
2176 | |||
2177 | case 0x15: /* next track outside defined extend */ | ||
2178 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
2179 | "next track outside defined extend - " | ||
2180 | "should not happen;\n" | ||
2181 | "Please send this message together with " | ||
2182 | "the above sense data to linux390@de." | ||
2183 | "ibm.com"); | ||
2184 | |||
2185 | erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); | ||
2186 | break; | ||
2187 | |||
2188 | case 0x1B: /* unexpected condition during write */ | ||
2189 | |||
2190 | erp = dasd_3990_erp_action_1B_32(erp, sense); | ||
2191 | break; | ||
2192 | |||
2193 | case 0x1C: /* invalid data */ | ||
2194 | DEV_MESSAGE(KERN_EMERG, device, "%s", | ||
2195 | "Data recovered during retry with PCI " | ||
2196 | "fetch mode active"); | ||
2197 | |||
2198 | /* not possible to handle this situation in Linux */ | ||
2199 | panic | ||
2200 | ("Invalid data - No way to inform application " | ||
2201 | "about the possibly incorrect data"); | ||
2202 | break; | ||
2203 | |||
2204 | case 0x1D: /* state-change pending */ | ||
2205 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
2206 | "A State change pending condition exists " | ||
2207 | "for the subsystem or device"); | ||
2208 | |||
2209 | erp = dasd_3990_erp_action_4(erp, sense); | ||
2210 | break; | ||
2211 | |||
2212 | case 0x1E: /* busy */ | ||
2213 | DEV_MESSAGE(KERN_DEBUG, device, "%s", | ||
2214 | "Busy condition exists " | ||
2215 | "for the subsystem or device"); | ||
2216 | erp = dasd_3990_erp_action_4(erp, sense); | ||
2217 | break; | ||
2218 | |||
2219 | default: /* all others errors - default erp */ | ||
2220 | break; | ||
2221 | } | ||
2222 | } | ||
2223 | |||
2224 | return erp; | ||
2225 | |||
2226 | } /* end dasd_3990_erp_inspect_32 */ | ||
2227 | |||
2228 | /* | ||
2229 | ***************************************************************************** | ||
2230 | * main ERP control fuctions (24 and 32 byte sense) | ||
2231 | ***************************************************************************** | ||
2232 | */ | ||
2233 | |||
2234 | /* | ||
2235 | * DASD_3990_ERP_INSPECT | ||
2236 | * | ||
2237 | * DESCRIPTION | ||
2238 | * Does a detailed inspection for sense data by calling either | ||
2239 | * the 24-byte or the 32-byte inspection routine. | ||
2240 | * | ||
2241 | * PARAMETER | ||
2242 | * erp pointer to the currently created default ERP | ||
2243 | * RETURN VALUES | ||
2244 | * erp_new contens was possibly modified | ||
2245 | */ | ||
2246 | static struct dasd_ccw_req * | ||
2247 | dasd_3990_erp_inspect(struct dasd_ccw_req * erp) | ||
2248 | { | ||
2249 | |||
2250 | struct dasd_ccw_req *erp_new = NULL; | ||
2251 | /* sense data are located in the refers record of the */ | ||
2252 | /* already set up new ERP ! */ | ||
2253 | char *sense = erp->refers->irb.ecw; | ||
2254 | |||
2255 | /* distinguish between 24 and 32 byte sense data */ | ||
2256 | if (sense[27] & DASD_SENSE_BIT_0) { | ||
2257 | |||
2258 | /* inspect the 24 byte sense data */ | ||
2259 | erp_new = dasd_3990_erp_inspect_24(erp, sense); | ||
2260 | |||
2261 | } else { | ||
2262 | |||
2263 | /* inspect the 32 byte sense data */ | ||
2264 | erp_new = dasd_3990_erp_inspect_32(erp, sense); | ||
2265 | |||
2266 | } /* end distinguish between 24 and 32 byte sense data */ | ||
2267 | |||
2268 | return erp_new; | ||
2269 | } | ||
2270 | |||
2271 | /* | ||
2272 | * DASD_3990_ERP_ADD_ERP | ||
2273 | * | ||
2274 | * DESCRIPTION | ||
2275 | * This funtion adds an additional request block (ERP) to the head of | ||
2276 | * the given cqr (or erp). | ||
2277 | * This erp is initialized as an default erp (retry TIC) | ||
2278 | * | ||
2279 | * PARAMETER | ||
2280 | * cqr head of the current ERP-chain (or single cqr if | ||
2281 | * first error) | ||
2282 | * RETURN VALUES | ||
2283 | * erp pointer to new ERP-chain head | ||
2284 | */ | ||
2285 | static struct dasd_ccw_req * | ||
2286 | dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) | ||
2287 | { | ||
2288 | |||
2289 | struct dasd_device *device = cqr->device; | ||
2290 | struct ccw1 *ccw; | ||
2291 | |||
2292 | /* allocate additional request block */ | ||
2293 | struct dasd_ccw_req *erp; | ||
2294 | |||
2295 | erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device); | ||
2296 | if (IS_ERR(erp)) { | ||
2297 | if (cqr->retries <= 0) { | ||
2298 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
2299 | "Unable to allocate ERP request"); | ||
2300 | cqr->status = DASD_CQR_FAILED; | ||
2301 | cqr->stopclk = get_clock (); | ||
2302 | } else { | ||
2303 | DEV_MESSAGE (KERN_ERR, device, | ||
2304 | "Unable to allocate ERP request " | ||
2305 | "(%i retries left)", | ||
2306 | cqr->retries); | ||
2307 | dasd_set_timer(device, (HZ << 3)); | ||
2308 | } | ||
2309 | return cqr; | ||
2310 | } | ||
2311 | |||
2312 | /* initialize request with default TIC to current ERP/CQR */ | ||
2313 | ccw = erp->cpaddr; | ||
2314 | ccw->cmd_code = CCW_CMD_NOOP; | ||
2315 | ccw->flags = CCW_FLAG_CC; | ||
2316 | ccw++; | ||
2317 | ccw->cmd_code = CCW_CMD_TIC; | ||
2318 | ccw->cda = (long)(cqr->cpaddr); | ||
2319 | erp->function = dasd_3990_erp_add_erp; | ||
2320 | erp->refers = cqr; | ||
2321 | erp->device = cqr->device; | ||
2322 | erp->magic = cqr->magic; | ||
2323 | erp->expires = 0; | ||
2324 | erp->retries = 256; | ||
2325 | erp->buildclk = get_clock(); | ||
2326 | |||
2327 | erp->status = DASD_CQR_FILLED; | ||
2328 | |||
2329 | return erp; | ||
2330 | } | ||
2331 | |||
2332 | /* | ||
2333 | * DASD_3990_ERP_ADDITIONAL_ERP | ||
2334 | * | ||
2335 | * DESCRIPTION | ||
2336 | * An additional ERP is needed to handle the current error. | ||
2337 | * Add ERP to the head of the ERP-chain containing the ERP processing | ||
2338 | * determined based on the sense data. | ||
2339 | * | ||
2340 | * PARAMETER | ||
2341 | * cqr head of the current ERP-chain (or single cqr if | ||
2342 | * first error) | ||
2343 | * | ||
2344 | * RETURN VALUES | ||
2345 | * erp pointer to new ERP-chain head | ||
2346 | */ | ||
2347 | static struct dasd_ccw_req * | ||
2348 | dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr) | ||
2349 | { | ||
2350 | |||
2351 | struct dasd_ccw_req *erp = NULL; | ||
2352 | |||
2353 | /* add erp and initialize with default TIC */ | ||
2354 | erp = dasd_3990_erp_add_erp(cqr); | ||
2355 | |||
2356 | /* inspect sense, determine specific ERP if possible */ | ||
2357 | if (erp != cqr) { | ||
2358 | |||
2359 | erp = dasd_3990_erp_inspect(erp); | ||
2360 | } | ||
2361 | |||
2362 | return erp; | ||
2363 | |||
2364 | } /* end dasd_3990_erp_additional_erp */ | ||
2365 | |||
2366 | /* | ||
2367 | * DASD_3990_ERP_ERROR_MATCH | ||
2368 | * | ||
2369 | * DESCRIPTION | ||
2370 | * Check if the device status of the given cqr is the same. | ||
2371 | * This means that the failed CCW and the relevant sense data | ||
2372 | * must match. | ||
2373 | * I don't distinguish between 24 and 32 byte sense because in case of | ||
2374 | * 24 byte sense byte 25 and 27 is set as well. | ||
2375 | * | ||
2376 | * PARAMETER | ||
2377 | * cqr1 first cqr, which will be compared with the | ||
2378 | * cqr2 second cqr. | ||
2379 | * | ||
2380 | * RETURN VALUES | ||
2381 | * match 'boolean' for match found | ||
2382 | * returns 1 if match found, otherwise 0. | ||
2383 | */ | ||
2384 | static int | ||
2385 | dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2) | ||
2386 | { | ||
2387 | |||
2388 | /* check failed CCW */ | ||
2389 | if (cqr1->irb.scsw.cpa != cqr2->irb.scsw.cpa) { | ||
2390 | // return 0; /* CCW doesn't match */ | ||
2391 | } | ||
2392 | |||
2393 | /* check sense data; byte 0-2,25,27 */ | ||
2394 | if (!((memcmp (cqr1->irb.ecw, cqr2->irb.ecw, 3) == 0) && | ||
2395 | (cqr1->irb.ecw[27] == cqr2->irb.ecw[27]) && | ||
2396 | (cqr1->irb.ecw[25] == cqr2->irb.ecw[25]))) { | ||
2397 | |||
2398 | return 0; /* sense doesn't match */ | ||
2399 | } | ||
2400 | |||
2401 | return 1; /* match */ | ||
2402 | |||
2403 | } /* end dasd_3990_erp_error_match */ | ||
2404 | |||
2405 | /* | ||
2406 | * DASD_3990_ERP_IN_ERP | ||
2407 | * | ||
2408 | * DESCRIPTION | ||
2409 | * check if the current error already happened before. | ||
2410 | * quick exit if current cqr is not an ERP (cqr->refers=NULL) | ||
2411 | * | ||
2412 | * PARAMETER | ||
2413 | * cqr failed cqr (either original cqr or already an erp) | ||
2414 | * | ||
2415 | * RETURN VALUES | ||
2416 | * erp erp-pointer to the already defined error | ||
2417 | * recovery procedure OR | ||
2418 | * NULL if a 'new' error occurred. | ||
2419 | */ | ||
2420 | static struct dasd_ccw_req * | ||
2421 | dasd_3990_erp_in_erp(struct dasd_ccw_req *cqr) | ||
2422 | { | ||
2423 | |||
2424 | struct dasd_ccw_req *erp_head = cqr, /* save erp chain head */ | ||
2425 | *erp_match = NULL; /* save erp chain head */ | ||
2426 | int match = 0; /* 'boolean' for matching error found */ | ||
2427 | |||
2428 | if (cqr->refers == NULL) { /* return if not in erp */ | ||
2429 | return NULL; | ||
2430 | } | ||
2431 | |||
2432 | /* check the erp/cqr chain for current error */ | ||
2433 | do { | ||
2434 | match = dasd_3990_erp_error_match(erp_head, cqr->refers); | ||
2435 | erp_match = cqr; /* save possible matching erp */ | ||
2436 | cqr = cqr->refers; /* check next erp/cqr in queue */ | ||
2437 | |||
2438 | } while ((cqr->refers != NULL) && (!match)); | ||
2439 | |||
2440 | if (!match) { | ||
2441 | return NULL; /* no match was found */ | ||
2442 | } | ||
2443 | |||
2444 | return erp_match; /* return address of matching erp */ | ||
2445 | |||
2446 | } /* END dasd_3990_erp_in_erp */ | ||
2447 | |||
2448 | /* | ||
2449 | * DASD_3990_ERP_FURTHER_ERP (24 & 32 byte sense) | ||
2450 | * | ||
2451 | * DESCRIPTION | ||
2452 | * No retry is left for the current ERP. Check what has to be done | ||
2453 | * with the ERP. | ||
2454 | * - do further defined ERP action or | ||
2455 | * - wait for interrupt or | ||
2456 | * - exit with permanent error | ||
2457 | * | ||
2458 | * PARAMETER | ||
2459 | * erp ERP which is in progress with no retry left | ||
2460 | * | ||
2461 | * RETURN VALUES | ||
2462 | * erp modified/additional ERP | ||
2463 | */ | ||
2464 | static struct dasd_ccw_req * | ||
2465 | dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) | ||
2466 | { | ||
2467 | |||
2468 | struct dasd_device *device = erp->device; | ||
2469 | char *sense = erp->irb.ecw; | ||
2470 | |||
2471 | /* check for 24 byte sense ERP */ | ||
2472 | if ((erp->function == dasd_3990_erp_bus_out) || | ||
2473 | (erp->function == dasd_3990_erp_action_1) || | ||
2474 | (erp->function == dasd_3990_erp_action_4)) { | ||
2475 | |||
2476 | erp = dasd_3990_erp_action_1(erp); | ||
2477 | |||
2478 | } else if (erp->function == dasd_3990_erp_action_5) { | ||
2479 | |||
2480 | /* retries have not been successful */ | ||
2481 | /* prepare erp for retry on different channel path */ | ||
2482 | erp = dasd_3990_erp_action_1(erp); | ||
2483 | |||
2484 | if (!(sense[2] & DASD_SENSE_BIT_0)) { | ||
2485 | |||
2486 | /* issue a Diagnostic Control command with an | ||
2487 | * Inhibit Write subcommand */ | ||
2488 | |||
2489 | switch (sense[25]) { | ||
2490 | case 0x17: | ||
2491 | case 0x57:{ /* controller */ | ||
2492 | erp = dasd_3990_erp_DCTL(erp, 0x20); | ||
2493 | break; | ||
2494 | } | ||
2495 | case 0x18: | ||
2496 | case 0x58:{ /* channel path */ | ||
2497 | erp = dasd_3990_erp_DCTL(erp, 0x40); | ||
2498 | break; | ||
2499 | } | ||
2500 | case 0x19: | ||
2501 | case 0x59:{ /* storage director */ | ||
2502 | erp = dasd_3990_erp_DCTL(erp, 0x80); | ||
2503 | break; | ||
2504 | } | ||
2505 | default: | ||
2506 | DEV_MESSAGE(KERN_DEBUG, device, | ||
2507 | "invalid subcommand modifier 0x%x " | ||
2508 | "for Diagnostic Control Command", | ||
2509 | sense[25]); | ||
2510 | } | ||
2511 | } | ||
2512 | |||
2513 | /* check for 32 byte sense ERP */ | ||
2514 | } else if ((erp->function == dasd_3990_erp_compound_retry) || | ||
2515 | (erp->function == dasd_3990_erp_compound_path) || | ||
2516 | (erp->function == dasd_3990_erp_compound_code) || | ||
2517 | (erp->function == dasd_3990_erp_compound_config)) { | ||
2518 | |||
2519 | erp = dasd_3990_erp_compound(erp, sense); | ||
2520 | |||
2521 | } else { | ||
2522 | /* No retry left and no additional special handling */ | ||
2523 | /*necessary */ | ||
2524 | DEV_MESSAGE(KERN_ERR, device, | ||
2525 | "no retries left for erp %p - " | ||
2526 | "set status to FAILED", erp); | ||
2527 | |||
2528 | erp->status = DASD_CQR_FAILED; | ||
2529 | } | ||
2530 | |||
2531 | return erp; | ||
2532 | |||
2533 | } /* end dasd_3990_erp_further_erp */ | ||
2534 | |||
2535 | /* | ||
2536 | * DASD_3990_ERP_HANDLE_MATCH_ERP | ||
2537 | * | ||
2538 | * DESCRIPTION | ||
2539 | * An error occurred again and an ERP has been detected which is already | ||
2540 | * used to handle this error (e.g. retries). | ||
2541 | * All prior ERP's are asumed to be successful and therefore removed | ||
2542 | * from queue. | ||
2543 | * If retry counter of matching erp is already 0, it is checked if further | ||
2544 | * action is needed (besides retry) or if the ERP has failed. | ||
2545 | * | ||
2546 | * PARAMETER | ||
2547 | * erp_head first ERP in ERP-chain | ||
2548 | * erp ERP that handles the actual error. | ||
2549 | * (matching erp) | ||
2550 | * | ||
2551 | * RETURN VALUES | ||
2552 | * erp modified/additional ERP | ||
2553 | */ | ||
2554 | static struct dasd_ccw_req * | ||
2555 | dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, | ||
2556 | struct dasd_ccw_req *erp) | ||
2557 | { | ||
2558 | |||
2559 | struct dasd_device *device = erp_head->device; | ||
2560 | struct dasd_ccw_req *erp_done = erp_head; /* finished req */ | ||
2561 | struct dasd_ccw_req *erp_free = NULL; /* req to be freed */ | ||
2562 | |||
2563 | /* loop over successful ERPs and remove them from chanq */ | ||
2564 | while (erp_done != erp) { | ||
2565 | |||
2566 | if (erp_done == NULL) /* end of chain reached */ | ||
2567 | panic(PRINTK_HEADER "Programming error in ERP! The " | ||
2568 | "original request was lost\n"); | ||
2569 | |||
2570 | /* remove the request from the device queue */ | ||
2571 | list_del(&erp_done->list); | ||
2572 | |||
2573 | erp_free = erp_done; | ||
2574 | erp_done = erp_done->refers; | ||
2575 | |||
2576 | /* free the finished erp request */ | ||
2577 | dasd_free_erp_request(erp_free, erp_free->device); | ||
2578 | |||
2579 | } /* end while */ | ||
2580 | |||
2581 | if (erp->retries > 0) { | ||
2582 | |||
2583 | char *sense = erp->refers->irb.ecw; | ||
2584 | |||
2585 | /* check for special retries */ | ||
2586 | if (erp->function == dasd_3990_erp_action_4) { | ||
2587 | |||
2588 | erp = dasd_3990_erp_action_4(erp, sense); | ||
2589 | |||
2590 | } else if (erp->function == dasd_3990_erp_action_1B_32) { | ||
2591 | |||
2592 | erp = dasd_3990_update_1B(erp, sense); | ||
2593 | |||
2594 | } else if (erp->function == dasd_3990_erp_int_req) { | ||
2595 | |||
2596 | erp = dasd_3990_erp_int_req(erp); | ||
2597 | |||
2598 | } else { | ||
2599 | /* simple retry */ | ||
2600 | DEV_MESSAGE(KERN_DEBUG, device, | ||
2601 | "%i retries left for erp %p", | ||
2602 | erp->retries, erp); | ||
2603 | |||
2604 | /* handle the request again... */ | ||
2605 | erp->status = DASD_CQR_QUEUED; | ||
2606 | } | ||
2607 | |||
2608 | } else { | ||
2609 | /* no retry left - check for further necessary action */ | ||
2610 | /* if no further actions, handle rest as permanent error */ | ||
2611 | erp = dasd_3990_erp_further_erp(erp); | ||
2612 | } | ||
2613 | |||
2614 | return erp; | ||
2615 | |||
2616 | } /* end dasd_3990_erp_handle_match_erp */ | ||
2617 | |||
2618 | /* | ||
2619 | * DASD_3990_ERP_ACTION | ||
2620 | * | ||
2621 | * DESCRIPTION | ||
2622 | * controll routine for 3990 erp actions. | ||
2623 | * Has to be called with the queue lock (namely the s390_irq_lock) acquired. | ||
2624 | * | ||
2625 | * PARAMETER | ||
2626 | * cqr failed cqr (either original cqr or already an erp) | ||
2627 | * | ||
2628 | * RETURN VALUES | ||
2629 | * erp erp-pointer to the head of the ERP action chain. | ||
2630 | * This means: | ||
2631 | * - either a ptr to an additional ERP cqr or | ||
2632 | * - the original given cqr (which's status might | ||
2633 | * be modified) | ||
2634 | */ | ||
2635 | struct dasd_ccw_req * | ||
2636 | dasd_3990_erp_action(struct dasd_ccw_req * cqr) | ||
2637 | { | ||
2638 | |||
2639 | struct dasd_ccw_req *erp = NULL; | ||
2640 | struct dasd_device *device = cqr->device; | ||
2641 | __u32 cpa = cqr->irb.scsw.cpa; | ||
2642 | |||
2643 | #ifdef ERP_DEBUG | ||
2644 | /* print current erp_chain */ | ||
2645 | DEV_MESSAGE(KERN_ERR, device, "%s", | ||
2646 | "ERP chain at BEGINNING of ERP-ACTION"); | ||
2647 | { | ||
2648 | struct dasd_ccw_req *temp_erp = NULL; | ||
2649 | |||
2650 | for (temp_erp = cqr; | ||
2651 | temp_erp != NULL; temp_erp = temp_erp->refers) { | ||
2652 | |||
2653 | DEV_MESSAGE(KERN_ERR, device, | ||
2654 | " erp %p (%02x) refers to %p", | ||
2655 | temp_erp, temp_erp->status, | ||
2656 | temp_erp->refers); | ||
2657 | } | ||
2658 | } | ||
2659 | #endif /* ERP_DEBUG */ | ||
2660 | |||
2661 | /* double-check if current erp/cqr was successfull */ | ||
2662 | if ((cqr->irb.scsw.cstat == 0x00) && | ||
2663 | (cqr->irb.scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) { | ||
2664 | |||
2665 | DEV_MESSAGE(KERN_DEBUG, device, | ||
2666 | "ERP called for successful request %p" | ||
2667 | " - NO ERP necessary", cqr); | ||
2668 | |||
2669 | cqr->status = DASD_CQR_DONE; | ||
2670 | |||
2671 | return cqr; | ||
2672 | } | ||
2673 | /* check if sense data are available */ | ||
2674 | if (!cqr->irb.ecw) { | ||
2675 | DEV_MESSAGE(KERN_DEBUG, device, | ||
2676 | "ERP called witout sense data avail ..." | ||
2677 | "request %p - NO ERP possible", cqr); | ||
2678 | |||
2679 | cqr->status = DASD_CQR_FAILED; | ||
2680 | |||
2681 | return cqr; | ||
2682 | |||
2683 | } | ||
2684 | |||
2685 | /* check if error happened before */ | ||
2686 | erp = dasd_3990_erp_in_erp(cqr); | ||
2687 | |||
2688 | if (erp == NULL) { | ||
2689 | /* no matching erp found - set up erp */ | ||
2690 | erp = dasd_3990_erp_additional_erp(cqr); | ||
2691 | } else { | ||
2692 | /* matching erp found - set all leading erp's to DONE */ | ||
2693 | erp = dasd_3990_erp_handle_match_erp(cqr, erp); | ||
2694 | } | ||
2695 | |||
2696 | #ifdef ERP_DEBUG | ||
2697 | /* print current erp_chain */ | ||
2698 | DEV_MESSAGE(KERN_ERR, device, "%s", "ERP chain at END of ERP-ACTION"); | ||
2699 | { | ||
2700 | struct dasd_ccw_req *temp_erp = NULL; | ||
2701 | for (temp_erp = erp; | ||
2702 | temp_erp != NULL; temp_erp = temp_erp->refers) { | ||
2703 | |||
2704 | DEV_MESSAGE(KERN_ERR, device, | ||
2705 | " erp %p (%02x) refers to %p", | ||
2706 | temp_erp, temp_erp->status, | ||
2707 | temp_erp->refers); | ||
2708 | } | ||
2709 | } | ||
2710 | #endif /* ERP_DEBUG */ | ||
2711 | |||
2712 | if (erp->status == DASD_CQR_FAILED) | ||
2713 | dasd_log_ccw(erp, 1, cpa); | ||
2714 | |||
2715 | /* enqueue added ERP request */ | ||
2716 | if (erp->status == DASD_CQR_FILLED) { | ||
2717 | erp->status = DASD_CQR_QUEUED; | ||
2718 | list_add(&erp->list, &device->ccw_queue); | ||
2719 | } | ||
2720 | |||
2721 | return erp; | ||
2722 | |||
2723 | } /* end dasd_3990_erp_action */ | ||
2724 | |||
2725 | /* | ||
2726 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
2727 | * Emacs will notice this stuff at the end of the file and automatically | ||
2728 | * adjust the settings for this buffer only. This must remain at the end | ||
2729 | * of the file. | ||
2730 | * --------------------------------------------------------------------------- | ||
2731 | * Local variables: | ||
2732 | * c-indent-level: 4 | ||
2733 | * c-brace-imaginary-offset: 0 | ||
2734 | * c-brace-offset: -4 | ||
2735 | * c-argdecl-indent: 4 | ||
2736 | * c-label-offset: -4 | ||
2737 | * c-continued-statement-offset: 4 | ||
2738 | * c-continued-brace-offset: 0 | ||
2739 | * indent-tabs-mode: 1 | ||
2740 | * tab-width: 8 | ||
2741 | * End: | ||
2742 | */ | ||