aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block
diff options
context:
space:
mode:
authorStefan Haberland <stefan.haberland@de.ibm.com>2010-05-17 04:00:10 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-05-17 04:00:16 -0400
commit501183f2ed74434e30a1b039b2f3af30f1f3f461 (patch)
tree785b6f2b2d5c7858e557354f71bdb0928fa098b1 /drivers/s390/block
parentf3cb31e495668eae568c584c666631e26c68bdea (diff)
[S390] dasd: add dynamic pav toleration
For base Parallel Access Volume (PAV) there is a fixed mapping of base and alias devices. With dynamic PAV this mapping can be changed so that an alias device is used with another base device. This patch enables the DASD device driver to tolerate dynamic PAV changes. Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block')
-rw-r--r--drivers/s390/block/dasd.c22
-rw-r--r--drivers/s390/block/dasd_3990_erp.c20
-rw-r--r--drivers/s390/block/dasd_alias.c8
-rw-r--r--drivers/s390/block/dasd_eckd.c70
-rw-r--r--drivers/s390/block/dasd_eckd.h2
-rw-r--r--drivers/s390/block/dasd_int.h5
6 files changed, 124 insertions, 3 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index fa2339cb168..0e86247d791 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -65,6 +65,7 @@ static void dasd_device_tasklet(struct dasd_device *);
65static void dasd_block_tasklet(struct dasd_block *); 65static void dasd_block_tasklet(struct dasd_block *);
66static void do_kick_device(struct work_struct *); 66static void do_kick_device(struct work_struct *);
67static void do_restore_device(struct work_struct *); 67static void do_restore_device(struct work_struct *);
68static void do_reload_device(struct work_struct *);
68static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); 69static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
69static void dasd_device_timeout(unsigned long); 70static void dasd_device_timeout(unsigned long);
70static void dasd_block_timeout(unsigned long); 71static void dasd_block_timeout(unsigned long);
@@ -115,6 +116,7 @@ struct dasd_device *dasd_alloc_device(void)
115 device->timer.data = (unsigned long) device; 116 device->timer.data = (unsigned long) device;
116 INIT_WORK(&device->kick_work, do_kick_device); 117 INIT_WORK(&device->kick_work, do_kick_device);
117 INIT_WORK(&device->restore_device, do_restore_device); 118 INIT_WORK(&device->restore_device, do_restore_device);
119 INIT_WORK(&device->reload_device, do_reload_device);
118 device->state = DASD_STATE_NEW; 120 device->state = DASD_STATE_NEW;
119 device->target = DASD_STATE_NEW; 121 device->target = DASD_STATE_NEW;
120 mutex_init(&device->state_mutex); 122 mutex_init(&device->state_mutex);
@@ -521,6 +523,26 @@ void dasd_kick_device(struct dasd_device *device)
521} 523}
522 524
523/* 525/*
526 * dasd_reload_device will schedule a call do do_reload_device to the kernel
527 * event daemon.
528 */
529static void do_reload_device(struct work_struct *work)
530{
531 struct dasd_device *device = container_of(work, struct dasd_device,
532 reload_device);
533 device->discipline->reload(device);
534 dasd_put_device(device);
535}
536
537void dasd_reload_device(struct dasd_device *device)
538{
539 dasd_get_device(device);
540 /* queue call to dasd_reload_device to the kernel event daemon. */
541 schedule_work(&device->reload_device);
542}
543EXPORT_SYMBOL(dasd_reload_device);
544
545/*
524 * dasd_restore_device will schedule a call do do_restore_device to the kernel 546 * dasd_restore_device will schedule a call do do_restore_device to the kernel
525 * event daemon. 547 * event daemon.
526 */ 548 */
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 6632649dd6a..85bfd879485 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1418,9 +1418,29 @@ static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
1418 struct dasd_ccw_req *erp) 1418 struct dasd_ccw_req *erp)
1419{ 1419{
1420 struct dasd_ccw_req *cqr = erp->refers; 1420 struct dasd_ccw_req *cqr = erp->refers;
1421 char *sense;
1421 1422
1422 if (cqr->block && 1423 if (cqr->block &&
1423 (cqr->block->base != cqr->startdev)) { 1424 (cqr->block->base != cqr->startdev)) {
1425
1426 sense = dasd_get_sense(&erp->refers->irb);
1427 /*
1428 * dynamic pav may have changed base alias mapping
1429 */
1430 if (!test_bit(DASD_FLAG_OFFLINE, &cqr->startdev->flags) && sense
1431 && (sense[0] == 0x10) && (sense[7] == 0x0F)
1432 && (sense[8] == 0x67)) {
1433 /*
1434 * remove device from alias handling to prevent new
1435 * requests from being scheduled on the
1436 * wrong alias device
1437 */
1438 dasd_alias_remove_device(cqr->startdev);
1439
1440 /* schedule worker to reload device */
1441 dasd_reload_device(cqr->startdev);
1442 }
1443
1424 if (cqr->startdev->features & DASD_FEATURE_ERPLOG) { 1444 if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
1425 DBF_DEV_EVENT(DBF_ERR, cqr->startdev, 1445 DBF_DEV_EVENT(DBF_ERR, cqr->startdev,
1426 "ERP on alias device for request %p," 1446 "ERP on alias device for request %p,"
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 8c4814258e9..a564b994111 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -642,6 +642,14 @@ int dasd_alias_add_device(struct dasd_device *device)
642 return rc; 642 return rc;
643} 643}
644 644
645int dasd_alias_update_add_device(struct dasd_device *device)
646{
647 struct dasd_eckd_private *private;
648 private = (struct dasd_eckd_private *) device->private;
649 private->lcu->flags |= UPDATE_PENDING;
650 return dasd_alias_add_device(device);
651}
652
645int dasd_alias_remove_device(struct dasd_device *device) 653int dasd_alias_remove_device(struct dasd_device *device)
646{ 654{
647 struct dasd_eckd_private *private; 655 struct dasd_eckd_private *private;
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 0cb23311685..4305c23c57a 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1451,6 +1451,7 @@ static int dasd_eckd_ready_to_online(struct dasd_device *device)
1451 1451
1452static int dasd_eckd_online_to_ready(struct dasd_device *device) 1452static int dasd_eckd_online_to_ready(struct dasd_device *device)
1453{ 1453{
1454 cancel_work_sync(&device->reload_device);
1454 return dasd_alias_remove_device(device); 1455 return dasd_alias_remove_device(device);
1455}; 1456};
1456 1457
@@ -1709,10 +1710,27 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
1709{ 1710{
1710 char mask; 1711 char mask;
1711 char *sense = NULL; 1712 char *sense = NULL;
1713 struct dasd_eckd_private *private;
1712 1714
1715 private = (struct dasd_eckd_private *) device->private;
1713 /* first of all check for state change pending interrupt */ 1716 /* first of all check for state change pending interrupt */
1714 mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 1717 mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
1715 if ((scsw_dstat(&irb->scsw) & mask) == mask) { 1718 if ((scsw_dstat(&irb->scsw) & mask) == mask) {
1719 /* for alias only and not in offline processing*/
1720 if (!device->block && private->lcu &&
1721 !test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
1722 /*
1723 * the state change could be caused by an alias
1724 * reassignment remove device from alias handling
1725 * to prevent new requests from being scheduled on
1726 * the wrong alias device
1727 */
1728 dasd_alias_remove_device(device);
1729
1730 /* schedule worker to reload device */
1731 dasd_reload_device(device);
1732 }
1733
1716 dasd_generic_handle_state_change(device); 1734 dasd_generic_handle_state_change(device);
1717 return; 1735 return;
1718 } 1736 }
@@ -3259,7 +3277,7 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
3259 dasd_eckd_dump_sense_ccw(device, req, irb); 3277 dasd_eckd_dump_sense_ccw(device, req, irb);
3260} 3278}
3261 3279
3262int dasd_eckd_pm_freeze(struct dasd_device *device) 3280static int dasd_eckd_pm_freeze(struct dasd_device *device)
3263{ 3281{
3264 /* 3282 /*
3265 * the device should be disconnected from our LCU structure 3283 * the device should be disconnected from our LCU structure
@@ -3272,7 +3290,7 @@ int dasd_eckd_pm_freeze(struct dasd_device *device)
3272 return 0; 3290 return 0;
3273} 3291}
3274 3292
3275int dasd_eckd_restore_device(struct dasd_device *device) 3293static int dasd_eckd_restore_device(struct dasd_device *device)
3276{ 3294{
3277 struct dasd_eckd_private *private; 3295 struct dasd_eckd_private *private;
3278 struct dasd_eckd_characteristics temp_rdc_data; 3296 struct dasd_eckd_characteristics temp_rdc_data;
@@ -3336,6 +3354,53 @@ out_err:
3336 return -1; 3354 return -1;
3337} 3355}
3338 3356
3357static int dasd_eckd_reload_device(struct dasd_device *device)
3358{
3359 struct dasd_eckd_private *private;
3360 int rc, old_base;
3361 char uid[60];
3362
3363 private = (struct dasd_eckd_private *) device->private;
3364 old_base = private->uid.base_unit_addr;
3365 /* Read Configuration Data */
3366 rc = dasd_eckd_read_conf(device);
3367 if (rc)
3368 goto out_err;
3369
3370 rc = dasd_eckd_generate_uid(device, &private->uid);
3371 if (rc)
3372 goto out_err;
3373
3374 dasd_set_uid(device->cdev, &private->uid);
3375
3376 /*
3377 * update unit address configuration and
3378 * add device to alias management
3379 */
3380 dasd_alias_update_add_device(device);
3381
3382 if (old_base != private->uid.base_unit_addr) {
3383 if (strlen(private->uid.vduit) > 0)
3384 snprintf(uid, 60, "%s.%s.%04x.%02x.%s",
3385 private->uid.vendor, private->uid.serial,
3386 private->uid.ssid, private->uid.base_unit_addr,
3387 private->uid.vduit);
3388 else
3389 snprintf(uid, 60, "%s.%s.%04x.%02x",
3390 private->uid.vendor, private->uid.serial,
3391 private->uid.ssid,
3392 private->uid.base_unit_addr);
3393
3394 dev_info(&device->cdev->dev,
3395 "An Alias device was reassigned to a new base device "
3396 "with UID: %s\n", uid);
3397 }
3398 return 0;
3399
3400out_err:
3401 return -1;
3402}
3403
3339static struct ccw_driver dasd_eckd_driver = { 3404static struct ccw_driver dasd_eckd_driver = {
3340 .name = "dasd-eckd", 3405 .name = "dasd-eckd",
3341 .owner = THIS_MODULE, 3406 .owner = THIS_MODULE,
@@ -3389,6 +3454,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
3389 .ioctl = dasd_eckd_ioctl, 3454 .ioctl = dasd_eckd_ioctl,
3390 .freeze = dasd_eckd_pm_freeze, 3455 .freeze = dasd_eckd_pm_freeze,
3391 .restore = dasd_eckd_restore_device, 3456 .restore = dasd_eckd_restore_device,
3457 .reload = dasd_eckd_reload_device,
3392}; 3458};
3393 3459
3394static int __init 3460static int __init
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 864d53c0420..dd6385a5af1 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -426,7 +426,6 @@ struct alias_pav_group {
426 struct dasd_device *next; 426 struct dasd_device *next;
427}; 427};
428 428
429
430struct dasd_eckd_private { 429struct dasd_eckd_private {
431 struct dasd_eckd_characteristics rdc_data; 430 struct dasd_eckd_characteristics rdc_data;
432 u8 *conf_data; 431 u8 *conf_data;
@@ -463,4 +462,5 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
463void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); 462void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
464void dasd_alias_lcu_setup_complete(struct dasd_device *); 463void dasd_alias_lcu_setup_complete(struct dasd_device *);
465void dasd_alias_wait_for_lcu_setup(struct dasd_device *); 464void dasd_alias_wait_for_lcu_setup(struct dasd_device *);
465int dasd_alias_update_add_device(struct dasd_device *);
466#endif /* DASD_ECKD_H */ 466#endif /* DASD_ECKD_H */
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index a91d4a97d4f..1ae7b121628 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -312,6 +312,9 @@ struct dasd_discipline {
312 /* suspend/resume functions */ 312 /* suspend/resume functions */
313 int (*freeze) (struct dasd_device *); 313 int (*freeze) (struct dasd_device *);
314 int (*restore) (struct dasd_device *); 314 int (*restore) (struct dasd_device *);
315
316 /* reload device after state change */
317 int (*reload) (struct dasd_device *);
315}; 318};
316 319
317extern struct dasd_discipline *dasd_diag_discipline_pointer; 320extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -386,6 +389,7 @@ struct dasd_device {
386 struct tasklet_struct tasklet; 389 struct tasklet_struct tasklet;
387 struct work_struct kick_work; 390 struct work_struct kick_work;
388 struct work_struct restore_device; 391 struct work_struct restore_device;
392 struct work_struct reload_device;
389 struct timer_list timer; 393 struct timer_list timer;
390 394
391 debug_info_t *debug_area; 395 debug_info_t *debug_area;
@@ -582,6 +586,7 @@ void dasd_enable_device(struct dasd_device *);
582void dasd_set_target_state(struct dasd_device *, int); 586void dasd_set_target_state(struct dasd_device *, int);
583void dasd_kick_device(struct dasd_device *); 587void dasd_kick_device(struct dasd_device *);
584void dasd_restore_device(struct dasd_device *); 588void dasd_restore_device(struct dasd_device *);
589void dasd_reload_device(struct dasd_device *);
585 590
586void dasd_add_request_head(struct dasd_ccw_req *); 591void dasd_add_request_head(struct dasd_ccw_req *);
587void dasd_add_request_tail(struct dasd_ccw_req *); 592void dasd_add_request_tail(struct dasd_ccw_req *);