Commit eb6e199b authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky

[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: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 626350b6
This diff is collapsed.
...@@ -69,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) ...@@ -69,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
* processing until the started timer has expired or an related * processing until the started timer has expired or an related
* interrupt was received. * interrupt was received.
*/ */
static void static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires)
dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
{ {
struct dasd_device *device = erp->startdev; struct dasd_device *device = erp->startdev;
...@@ -80,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) ...@@ -80,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
"blocking request queue for %is", expires/HZ); "blocking request queue for %is", expires/HZ);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped |= DASD_STOPPED_PENDING; dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
erp->status = DASD_CQR_FILLED; erp->status = DASD_CQR_FILLED;
dasd_block_set_timer(device->block, expires); if (erp->block)
dasd_block_set_timer(erp->block, expires);
else
dasd_device_set_timer(device, expires);
} }
/* /*
...@@ -242,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ...@@ -242,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
* DESCRIPTION * DESCRIPTION
* Setup ERP to do the ERP action 1 (see Reference manual). * Setup ERP to do the ERP action 1 (see Reference manual).
* Repeat the operation on a different channel path. * Repeat the operation on a different channel path.
* If all alternate paths have been tried, the request is posted with a * As deviation from the recommended recovery action, we reset the path mask
* permanent error. * after we have tried each path and go through all paths a second time.
* Note: duplex handling is not implemented (yet). * This will cover situations where only one path at a time is actually down,
* but all paths fail and recover just with the same sequence and timing as
* we try to use them (flapping links).
* If all alternate paths have been tried twice, the request is posted with
* a permanent error.
* *
* PARAMETER * PARAMETER
* erp pointer to the current ERP * erp pointer to the current ERP
...@@ -253,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ...@@ -253,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
* erp pointer to the ERP * erp pointer to the ERP
* *
*/ */
static struct dasd_ccw_req * static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp)
dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
{ {
erp->function = dasd_3990_erp_action_1_sec;
dasd_3990_erp_alternate_path(erp);
return erp;
}
static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
{
erp->function = dasd_3990_erp_action_1; erp->function = dasd_3990_erp_action_1;
dasd_3990_erp_alternate_path(erp); dasd_3990_erp_alternate_path(erp);
if (erp->status == DASD_CQR_FAILED) {
erp->status = DASD_CQR_FILLED;
erp->retries = 10;
erp->lpm = LPM_ANYPATH;
erp->function = dasd_3990_erp_action_1_sec;
}
return erp; return erp;
} /* end dasd_3990_erp_action_1(b) */
} /* end dasd_3990_erp_action_1 */
/* /*
* DASD_3990_ERP_ACTION_4 * 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,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
return cqr; return cqr;
} }
ccw = cqr->cpaddr;
if (cqr->cpmode == 1) { if (cqr->cpmode == 1) {
/* make a shallow copy of the original tcw but set new tsb */ /* make a shallow copy of the original tcw but set new tsb */
erp->cpmode = 1; erp->cpmode = 1;
...@@ -2302,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) ...@@ -2302,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
tsb = (struct tsb *) &tcw[1]; tsb = (struct tsb *) &tcw[1];
*tcw = *((struct tcw *)cqr->cpaddr); *tcw = *((struct tcw *)cqr->cpaddr);
tcw->tsb = (long)tsb; tcw->tsb = (long)tsb;
} else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) {
/* PSF cannot be chained from NOOP/TIC */
erp->cpaddr = cqr->cpaddr;
} else { } else {
/* initialize request with default TIC to current ERP/CQR */ /* initialize request with default TIC to current ERP/CQR */
ccw = erp->cpaddr; ccw = erp->cpaddr;
...@@ -2486,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) ...@@ -2486,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
erp = dasd_3990_erp_action_1(erp); erp = dasd_3990_erp_action_1(erp);
} else if (erp->function == dasd_3990_erp_action_1_sec) {
erp = dasd_3990_erp_action_1_sec(erp);
} else if (erp->function == dasd_3990_erp_action_5) { } else if (erp->function == dasd_3990_erp_action_5) {
/* retries have not been successful */ /* retries have not been successful */
......
...@@ -755,11 +755,11 @@ static void __stop_device_on_lcu(struct dasd_device *device, ...@@ -755,11 +755,11 @@ static void __stop_device_on_lcu(struct dasd_device *device,
{ {
/* If pos == device then device is already locked! */ /* If pos == device then device is already locked! */
if (pos == device) { if (pos == device) {
pos->stopped |= DASD_STOPPED_SU; dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
return; return;
} }
spin_lock(get_ccwdev_lock(pos->cdev)); spin_lock(get_ccwdev_lock(pos->cdev));
pos->stopped |= DASD_STOPPED_SU; dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
spin_unlock(get_ccwdev_lock(pos->cdev)); spin_unlock(get_ccwdev_lock(pos->cdev));
} }
...@@ -793,26 +793,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) ...@@ -793,26 +793,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
list_for_each_entry(device, &lcu->active_devices, alias_list) { list_for_each_entry(device, &lcu->active_devices, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
list_for_each_entry(device, &lcu->inactive_devices, alias_list) { list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
list_for_each_entry(pavgroup, &lcu->grouplist, group) { list_for_each_entry(pavgroup, &lcu->grouplist, group) {
list_for_each_entry(device, &pavgroup->baselist, alias_list) { list_for_each_entry(device, &pavgroup->baselist, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags); flags);
} }
list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags); flags);
} }
...@@ -836,7 +836,8 @@ static void summary_unit_check_handling_work(struct work_struct *work) ...@@ -836,7 +836,8 @@ static void summary_unit_check_handling_work(struct work_struct *work)
/* 2. reset summary unit check */ /* 2. reset summary unit check */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING); dasd_device_remove_stop_bits(device,
(DASD_STOPPED_SU | DASD_STOPPED_PENDING));
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
reset_summary_unit_check(lcu, device, suc_data->reason); reset_summary_unit_check(lcu, device, suc_data->reason);
......
...@@ -77,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids); ...@@ -77,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
static struct ccw_driver dasd_eckd_driver; /* see below */ static struct ccw_driver dasd_eckd_driver; /* see below */
#define INIT_CQR_OK 0
#define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2
/* initial attempt at a probe function. this can be simplified once /* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */ * the other detection code is gone */
static int static int
...@@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, ...@@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
cqr->block = NULL; cqr->block = NULL;
cqr->expires = 10*HZ; cqr->expires = 10*HZ;
cqr->lpm = lpm; cqr->lpm = lpm;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->retries = 256;
cqr->retries = 2;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
return cqr; return cqr;
...@@ -949,8 +953,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) ...@@ -949,8 +953,7 @@ static int dasd_eckd_read_features(struct dasd_device *device)
cqr->startdev = device; cqr->startdev = device;
cqr->memdev = device; cqr->memdev = device;
cqr->block = NULL; cqr->block = NULL;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->retries = 256;
cqr->retries = 5;
cqr->expires = 10 * HZ; cqr->expires = 10 * HZ;
/* Prepare for Read Subsystem Data */ /* Prepare for Read Subsystem Data */
...@@ -1025,6 +1028,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, ...@@ -1025,6 +1028,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
cqr->startdev = device; cqr->startdev = device;
cqr->memdev = device; cqr->memdev = device;
cqr->block = NULL; cqr->block = NULL;
cqr->retries = 256;
cqr->expires = 10*HZ; cqr->expires = 10*HZ;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
...@@ -1068,6 +1072,7 @@ static int dasd_eckd_validate_server(struct dasd_device *device) ...@@ -1068,6 +1072,7 @@ static int dasd_eckd_validate_server(struct dasd_device *device)
else else
enable_pav = 1; enable_pav = 1;
rc = dasd_eckd_psf_ssc(device, enable_pav); rc = dasd_eckd_psf_ssc(device, enable_pav);
/* may be requested feature is not available on server, /* may be requested feature is not available on server,
* therefore just report error and go ahead */ * therefore just report error and go ahead */
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
...@@ -1265,12 +1270,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) ...@@ -1265,12 +1270,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
cqr->block = NULL; cqr->block = NULL;
cqr->startdev = device; cqr->startdev = device;
cqr->memdev = device; cqr->memdev = device;
cqr->retries = 0; cqr->retries = 255;
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
return cqr; return cqr;
} }
/* differentiate between 'no record found' and any other error */
static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
{
char *sense;
if (init_cqr->status == DASD_CQR_DONE)
return INIT_CQR_OK;
else if (init_cqr->status == DASD_CQR_NEED_ERP ||
init_cqr->status == DASD_CQR_FAILED) {
sense = dasd_get_sense(&init_cqr->irb);
if (sense && (sense[1] & SNS1_NO_REC_FOUND))
return INIT_CQR_UNFORMATTED;
else
return INIT_CQR_ERROR;
} else
return INIT_CQR_ERROR;
}
/* /*
* This is the callback function for the init_analysis cqr. It saves * This is the callback function for the init_analysis cqr. It saves
* the status of the initial analysis ccw before it frees it and kicks * the status of the initial analysis ccw before it frees it and kicks
...@@ -1278,21 +1300,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) ...@@ -1278,21 +1300,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
* dasd_eckd_do_analysis again (if the devices has not been marked * dasd_eckd_do_analysis again (if the devices has not been marked
* for deletion in the meantime). * for deletion in the meantime).
*/ */
static void static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) void *data)
{ {
struct dasd_eckd_private *private; struct dasd_eckd_private *private;
struct dasd_device *device; struct dasd_device *device;
device = init_cqr->startdev; device = init_cqr->startdev;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
private->init_cqr_status = init_cqr->status; private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
dasd_sfree_request(init_cqr, device); dasd_sfree_request(init_cqr, device);
dasd_kick_device(device); dasd_kick_device(device);
} }
static int static int dasd_eckd_start_analysis(struct dasd_block *block)
dasd_eckd_start_analysis(struct dasd_block *block)
{ {
struct dasd_eckd_private *private; struct dasd_eckd_private *private;
struct dasd_ccw_req *init_cqr; struct dasd_ccw_req *init_cqr;
...@@ -1304,27 +1325,44 @@ dasd_eckd_start_analysis(struct dasd_block *block) ...@@ -1304,27 +1325,44 @@ dasd_eckd_start_analysis(struct dasd_block *block)
init_cqr->callback = dasd_eckd_analysis_callback; init_cqr->callback = dasd_eckd_analysis_callback;
init_cqr->callback_data = NULL; init_cqr->callback_data = NULL;
init_cqr->expires = 5*HZ; init_cqr->expires = 5*HZ;
/* first try without ERP, so we can later handle unformatted
* devices as special case
*/
clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
init_cqr->retries = 0;
dasd_add_request_head(init_cqr); dasd_add_request_head(init_cqr);
return -EAGAIN; return -EAGAIN;
} }
static int static int dasd_eckd_end_analysis(struct dasd_block *block)
dasd_eckd_end_analysis(struct dasd_block *block)
{ {
struct dasd_device *device; struct dasd_device *device;
struct dasd_eckd_private *private; struct dasd_eckd_private *private;
struct eckd_count *count_area; struct eckd_count *count_area;
unsigned int sb, blk_per_trk; unsigned int sb, blk_per_trk;
int status, i; int status, i;
struct dasd_ccw_req *init_cqr;
device = block->base; device = block->base;
private = (struct dasd_eckd_private *) device->private; private = (struct dasd_eckd_private *) device->private;
status = private->init_cqr_status; status = private->init_cqr_status;
private->init_cqr_status = -1; private->init_cqr_status = -1;
if (status != DASD_CQR_DONE) { if (status == INIT_CQR_ERROR) {
dev_warn(&device->cdev->dev, /* try again, this time with full ERP */
"The DASD is not formatted\n"); init_cqr = dasd_eckd_analysis_ccw(device);
dasd_sleep_on(init_cqr);
status = dasd_eckd_analysis_evaluation(init_cqr);
dasd_sfree_request(init_cqr, device);
}
if (status == INIT_CQR_UNFORMATTED) {
dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
return -EMEDIUMTYPE; return -EMEDIUMTYPE;
} else if (status == INIT_CQR_ERROR) {
dev_err(&device->cdev->dev,
"Detecting the DASD disk layout failed because "
"of an I/O error\n");
return -EIO;
} }
private->uses_cdl = 1; private->uses_cdl = 1;
...@@ -1616,8 +1654,7 @@ dasd_eckd_format_device(struct dasd_device * device, ...@@ -1616,8 +1654,7 @@ dasd_eckd_format_device(struct dasd_device * device,
} }
fcp->startdev = device; fcp->startdev = device;
fcp->memdev = device; fcp->memdev = device;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags); fcp->retries = 256;
fcp->retries = 5; /* set retry counter to enable default ERP */
fcp->buildclk = get_clock(); fcp->buildclk = get_clock();
fcp->status = DASD_CQR_FILLED; fcp->status = DASD_CQR_FILLED;
return fcp; return fcp;
...@@ -2699,6 +2736,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) ...@@ -2699,6 +2736,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
cqr->startdev = device; cqr->startdev = device;
cqr->memdev = device; cqr->memdev = device;
cqr->retries = 0; cqr->retries = 0;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
cqr->expires = 10 * HZ; cqr->expires = 10 * HZ;
/* Prepare for Read Subsystem Data */ /* Prepare for Read Subsystem Data */
......
...@@ -595,6 +595,9 @@ int dasd_generic_restore_device(struct ccw_device *); ...@@ -595,6 +595,9 @@ int dasd_generic_restore_device(struct ccw_device *);
int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
char *dasd_get_sense(struct irb *); char *dasd_get_sense(struct irb *);
void dasd_device_set_stop_bits(struct dasd_device *, int);
void dasd_device_remove_stop_bits(struct dasd_device *, int);
/* externals in dasd_devmap.c */ /* externals in dasd_devmap.c */
extern int dasd_max_devindex; extern int dasd_max_devindex;
extern int dasd_probeonly; extern int dasd_probeonly;
......
...@@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block) ...@@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block)
pr_info("%s: The DASD has been put in the quiesce " pr_info("%s: The DASD has been put in the quiesce "
"state\n", dev_name(&base->cdev->dev)); "state\n", dev_name(&base->cdev->dev));
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
base->stopped |= DASD_STOPPED_QUIESCE; dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
return 0; return 0;
} }
...@@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block) ...@@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block)
pr_info("%s: I/O operations have been resumed " pr_info("%s: I/O operations have been resumed "
"on the DASD\n", dev_name(&base->cdev->dev)); "on the DASD\n", dev_name(&base->cdev->dev));
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
base->stopped &= ~DASD_STOPPED_QUIESCE; dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
dasd_schedule_block_bh(block); dasd_schedule_block_bh(block);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment