Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Stefan Haberland and committed by
Martin Schwidefsky
501183f2 f3cb31e4

+124 -3
+22
drivers/s390/block/dasd.c
··· 65 65 static void dasd_block_tasklet(struct dasd_block *); 66 66 static void do_kick_device(struct work_struct *); 67 67 static void do_restore_device(struct work_struct *); 68 + static void do_reload_device(struct work_struct *); 68 69 static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); 69 70 static void dasd_device_timeout(unsigned long); 70 71 static void dasd_block_timeout(unsigned long); ··· 116 115 device->timer.data = (unsigned long) device; 117 116 INIT_WORK(&device->kick_work, do_kick_device); 118 117 INIT_WORK(&device->restore_device, do_restore_device); 118 + INIT_WORK(&device->reload_device, do_reload_device); 119 119 device->state = DASD_STATE_NEW; 120 120 device->target = DASD_STATE_NEW; 121 121 mutex_init(&device->state_mutex); ··· 521 519 /* queue call to dasd_kick_device to the kernel event daemon. */ 522 520 schedule_work(&device->kick_work); 523 521 } 522 + 523 + /* 524 + * dasd_reload_device will schedule a call do do_reload_device to the kernel 525 + * event daemon. 526 + */ 527 + static void do_reload_device(struct work_struct *work) 528 + { 529 + struct dasd_device *device = container_of(work, struct dasd_device, 530 + reload_device); 531 + device->discipline->reload(device); 532 + dasd_put_device(device); 533 + } 534 + 535 + void dasd_reload_device(struct dasd_device *device) 536 + { 537 + dasd_get_device(device); 538 + /* queue call to dasd_reload_device to the kernel event daemon. */ 539 + schedule_work(&device->reload_device); 540 + } 541 + EXPORT_SYMBOL(dasd_reload_device); 524 542 525 543 /* 526 544 * dasd_restore_device will schedule a call do do_restore_device to the kernel
+20
drivers/s390/block/dasd_3990_erp.c
··· 1418 1418 struct dasd_ccw_req *erp) 1419 1419 { 1420 1420 struct dasd_ccw_req *cqr = erp->refers; 1421 + char *sense; 1421 1422 1422 1423 if (cqr->block && 1423 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 1444 if (cqr->startdev->features & DASD_FEATURE_ERPLOG) { 1425 1445 DBF_DEV_EVENT(DBF_ERR, cqr->startdev, 1426 1446 "ERP on alias device for request %p,"
+8
drivers/s390/block/dasd_alias.c
··· 642 642 return rc; 643 643 } 644 644 645 + int 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 + 645 653 int dasd_alias_remove_device(struct dasd_device *device) 646 654 { 647 655 struct dasd_eckd_private *private;
+68 -2
drivers/s390/block/dasd_eckd.c
··· 1451 1451 1452 1452 static int dasd_eckd_online_to_ready(struct dasd_device *device) 1453 1453 { 1454 + cancel_work_sync(&device->reload_device); 1454 1455 return dasd_alias_remove_device(device); 1455 1456 }; 1456 1457 ··· 1710 1709 { 1711 1710 char mask; 1712 1711 char *sense = NULL; 1712 + struct dasd_eckd_private *private; 1713 1713 1714 + private = (struct dasd_eckd_private *) device->private; 1714 1715 /* first of all check for state change pending interrupt */ 1715 1716 mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 1716 1717 if ((scsw_dstat(&irb->scsw) & mask) == mask) { 1718 + /* for alias only and not in offline processing*/ 1719 + if (!device->block && private->lcu && 1720 + !test_bit(DASD_FLAG_OFFLINE, &device->flags)) { 1721 + /* 1722 + * the state change could be caused by an alias 1723 + * reassignment remove device from alias handling 1724 + * to prevent new requests from being scheduled on 1725 + * the wrong alias device 1726 + */ 1727 + dasd_alias_remove_device(device); 1728 + 1729 + /* schedule worker to reload device */ 1730 + dasd_reload_device(device); 1731 + } 1732 + 1717 1733 dasd_generic_handle_state_change(device); 1718 1734 return; 1719 1735 } ··· 3277 3259 dasd_eckd_dump_sense_ccw(device, req, irb); 3278 3260 } 3279 3261 3280 - int dasd_eckd_pm_freeze(struct dasd_device *device) 3262 + static int dasd_eckd_pm_freeze(struct dasd_device *device) 3281 3263 { 3282 3264 /* 3283 3265 * the device should be disconnected from our LCU structure ··· 3290 3272 return 0; 3291 3273 } 3292 3274 3293 - int dasd_eckd_restore_device(struct dasd_device *device) 3275 + static int dasd_eckd_restore_device(struct dasd_device *device) 3294 3276 { 3295 3277 struct dasd_eckd_private *private; 3296 3278 struct dasd_eckd_characteristics temp_rdc_data; ··· 3354 3336 return -1; 3355 3337 } 3356 3338 3339 + static int dasd_eckd_reload_device(struct dasd_device *device) 3340 + { 3341 + struct dasd_eckd_private *private; 3342 + int rc, old_base; 3343 + char uid[60]; 3344 + 3345 + private = (struct dasd_eckd_private *) device->private; 3346 + old_base = private->uid.base_unit_addr; 3347 + /* Read Configuration Data */ 3348 + rc = dasd_eckd_read_conf(device); 3349 + if (rc) 3350 + goto out_err; 3351 + 3352 + rc = dasd_eckd_generate_uid(device, &private->uid); 3353 + if (rc) 3354 + goto out_err; 3355 + 3356 + dasd_set_uid(device->cdev, &private->uid); 3357 + 3358 + /* 3359 + * update unit address configuration and 3360 + * add device to alias management 3361 + */ 3362 + dasd_alias_update_add_device(device); 3363 + 3364 + if (old_base != private->uid.base_unit_addr) { 3365 + if (strlen(private->uid.vduit) > 0) 3366 + snprintf(uid, 60, "%s.%s.%04x.%02x.%s", 3367 + private->uid.vendor, private->uid.serial, 3368 + private->uid.ssid, private->uid.base_unit_addr, 3369 + private->uid.vduit); 3370 + else 3371 + snprintf(uid, 60, "%s.%s.%04x.%02x", 3372 + private->uid.vendor, private->uid.serial, 3373 + private->uid.ssid, 3374 + private->uid.base_unit_addr); 3375 + 3376 + dev_info(&device->cdev->dev, 3377 + "An Alias device was reassigned to a new base device " 3378 + "with UID: %s\n", uid); 3379 + } 3380 + return 0; 3381 + 3382 + out_err: 3383 + return -1; 3384 + } 3385 + 3357 3386 static struct ccw_driver dasd_eckd_driver = { 3358 3387 .name = "dasd-eckd", 3359 3388 .owner = THIS_MODULE, ··· 3454 3389 .ioctl = dasd_eckd_ioctl, 3455 3390 .freeze = dasd_eckd_pm_freeze, 3456 3391 .restore = dasd_eckd_restore_device, 3392 + .reload = dasd_eckd_reload_device, 3457 3393 }; 3458 3394 3459 3395 static int __init
+1 -1
drivers/s390/block/dasd_eckd.h
··· 426 426 struct dasd_device *next; 427 427 }; 428 428 429 - 430 429 struct dasd_eckd_private { 431 430 struct dasd_eckd_characteristics rdc_data; 432 431 u8 *conf_data; ··· 462 463 void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); 463 464 void dasd_alias_lcu_setup_complete(struct dasd_device *); 464 465 void dasd_alias_wait_for_lcu_setup(struct dasd_device *); 466 + int dasd_alias_update_add_device(struct dasd_device *); 465 467 #endif /* DASD_ECKD_H */
+5
drivers/s390/block/dasd_int.h
··· 312 312 /* suspend/resume functions */ 313 313 int (*freeze) (struct dasd_device *); 314 314 int (*restore) (struct dasd_device *); 315 + 316 + /* reload device after state change */ 317 + int (*reload) (struct dasd_device *); 315 318 }; 316 319 317 320 extern struct dasd_discipline *dasd_diag_discipline_pointer; ··· 389 386 struct tasklet_struct tasklet; 390 387 struct work_struct kick_work; 391 388 struct work_struct restore_device; 389 + struct work_struct reload_device; 392 390 struct timer_list timer; 393 391 394 392 debug_info_t *debug_area; ··· 586 582 void dasd_set_target_state(struct dasd_device *, int); 587 583 void dasd_kick_device(struct dasd_device *); 588 584 void dasd_restore_device(struct dasd_device *); 585 + void dasd_reload_device(struct dasd_device *); 589 586 590 587 void dasd_add_request_head(struct dasd_ccw_req *); 591 588 void dasd_add_request_tail(struct dasd_ccw_req *);