diff options
author | Stefan Weinhuber <wein@de.ibm.com> | 2009-12-07 06:51:51 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:34 -0500 |
commit | eb6e199bef288611157b8198c25d12b32bf058d0 (patch) | |
tree | 80737a2703a9f4d09cee2410342aeccb281413ae /drivers/s390/block/dasd_3990_erp.c | |
parent | 626350b63ef2cd447023d3dc2a34eaa7ca01bfff (diff) |
[S390] dasd: improve error recovery for internal I/O
Most of the error conditions reported by a FICON storage server
indicate situations which can be recovered. Sometimes the host just
needs to retry an I/O request, but sometimes the recovery
is more complex and requires the device driver to wait, choose
a different path, etc.
The DASD device driver has a fully featured error recovery
for normal block layer I/O, but not for internal I/O request which
are for example used during the device bring up.
This can lead to situations where the IPL of a system fails because
DASD devices are not properly recognized.
This patch will extend the internal I/O handling to use the existing
error recovery procedures.
Signed-off-by: Stefan Weinhuber <wein@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block/dasd_3990_erp.c')
-rw-r--r-- | drivers/s390/block/dasd_3990_erp.c | 46 |
1 files changed, 33 insertions, 13 deletions
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 316eb1256a99..44796ba4eb9b 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c | |||
@@ -69,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) | |||
69 | * processing until the started timer has expired or an related | 69 | * processing until the started timer has expired or an related |
70 | * interrupt was received. | 70 | * interrupt was received. |
71 | */ | 71 | */ |
72 | static void | 72 | static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires) |
73 | dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) | ||
74 | { | 73 | { |
75 | 74 | ||
76 | struct dasd_device *device = erp->startdev; | 75 | struct dasd_device *device = erp->startdev; |
@@ -80,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) | |||
80 | "blocking request queue for %is", expires/HZ); | 79 | "blocking request queue for %is", expires/HZ); |
81 | 80 | ||
82 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | 81 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); |
83 | device->stopped |= DASD_STOPPED_PENDING; | 82 | dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING); |
84 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | 83 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); |
85 | erp->status = DASD_CQR_FILLED; | 84 | erp->status = DASD_CQR_FILLED; |
86 | dasd_block_set_timer(device->block, expires); | 85 | if (erp->block) |
86 | dasd_block_set_timer(erp->block, expires); | ||
87 | else | ||
88 | dasd_device_set_timer(device, expires); | ||
87 | } | 89 | } |
88 | 90 | ||
89 | /* | 91 | /* |
@@ -242,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) | |||
242 | * DESCRIPTION | 244 | * DESCRIPTION |
243 | * Setup ERP to do the ERP action 1 (see Reference manual). | 245 | * Setup ERP to do the ERP action 1 (see Reference manual). |
244 | * Repeat the operation on a different channel path. | 246 | * Repeat the operation on a different channel path. |
245 | * If all alternate paths have been tried, the request is posted with a | 247 | * As deviation from the recommended recovery action, we reset the path mask |
246 | * permanent error. | 248 | * after we have tried each path and go through all paths a second time. |
247 | * Note: duplex handling is not implemented (yet). | 249 | * This will cover situations where only one path at a time is actually down, |
250 | * but all paths fail and recover just with the same sequence and timing as | ||
251 | * we try to use them (flapping links). | ||
252 | * If all alternate paths have been tried twice, the request is posted with | ||
253 | * a permanent error. | ||
248 | * | 254 | * |
249 | * PARAMETER | 255 | * PARAMETER |
250 | * erp pointer to the current ERP | 256 | * erp pointer to the current ERP |
@@ -253,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) | |||
253 | * erp pointer to the ERP | 259 | * erp pointer to the ERP |
254 | * | 260 | * |
255 | */ | 261 | */ |
256 | static struct dasd_ccw_req * | 262 | static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp) |
257 | dasd_3990_erp_action_1(struct dasd_ccw_req * erp) | ||
258 | { | 263 | { |
264 | erp->function = dasd_3990_erp_action_1_sec; | ||
265 | dasd_3990_erp_alternate_path(erp); | ||
266 | return erp; | ||
267 | } | ||
259 | 268 | ||
269 | static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp) | ||
270 | { | ||
260 | erp->function = dasd_3990_erp_action_1; | 271 | erp->function = dasd_3990_erp_action_1; |
261 | |||
262 | dasd_3990_erp_alternate_path(erp); | 272 | dasd_3990_erp_alternate_path(erp); |
263 | 273 | if (erp->status == DASD_CQR_FAILED) { | |
274 | erp->status = DASD_CQR_FILLED; | ||
275 | erp->retries = 10; | ||
276 | erp->lpm = LPM_ANYPATH; | ||
277 | erp->function = dasd_3990_erp_action_1_sec; | ||
278 | } | ||
264 | return erp; | 279 | return erp; |
265 | 280 | } /* end dasd_3990_erp_action_1(b) */ | |
266 | } /* end dasd_3990_erp_action_1 */ | ||
267 | 281 | ||
268 | /* | 282 | /* |
269 | * DASD_3990_ERP_ACTION_4 | 283 | * DASD_3990_ERP_ACTION_4 |
@@ -2294,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) | |||
2294 | return cqr; | 2308 | return cqr; |
2295 | } | 2309 | } |
2296 | 2310 | ||
2311 | ccw = cqr->cpaddr; | ||
2297 | if (cqr->cpmode == 1) { | 2312 | if (cqr->cpmode == 1) { |
2298 | /* make a shallow copy of the original tcw but set new tsb */ | 2313 | /* make a shallow copy of the original tcw but set new tsb */ |
2299 | erp->cpmode = 1; | 2314 | erp->cpmode = 1; |
@@ -2302,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) | |||
2302 | tsb = (struct tsb *) &tcw[1]; | 2317 | tsb = (struct tsb *) &tcw[1]; |
2303 | *tcw = *((struct tcw *)cqr->cpaddr); | 2318 | *tcw = *((struct tcw *)cqr->cpaddr); |
2304 | tcw->tsb = (long)tsb; | 2319 | tcw->tsb = (long)tsb; |
2320 | } else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) { | ||
2321 | /* PSF cannot be chained from NOOP/TIC */ | ||
2322 | erp->cpaddr = cqr->cpaddr; | ||
2305 | } else { | 2323 | } else { |
2306 | /* initialize request with default TIC to current ERP/CQR */ | 2324 | /* initialize request with default TIC to current ERP/CQR */ |
2307 | ccw = erp->cpaddr; | 2325 | ccw = erp->cpaddr; |
@@ -2486,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) | |||
2486 | 2504 | ||
2487 | erp = dasd_3990_erp_action_1(erp); | 2505 | erp = dasd_3990_erp_action_1(erp); |
2488 | 2506 | ||
2507 | } else if (erp->function == dasd_3990_erp_action_1_sec) { | ||
2508 | erp = dasd_3990_erp_action_1_sec(erp); | ||
2489 | } else if (erp->function == dasd_3990_erp_action_5) { | 2509 | } else if (erp->function == dasd_3990_erp_action_5) { |
2490 | 2510 | ||
2491 | /* retries have not been successful */ | 2511 | /* retries have not been successful */ |