aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2011-01-05 06:48:03 -0500
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2011-01-05 06:47:30 -0500
commita4d26c6aeceea330ee5e0fb6b017d57e3b252d29 (patch)
treeeed358de48ff28e4fba73d9925abafa2699a7b6c /drivers
parentef19298b406f93af4bb249f0776deb8366e97532 (diff)
[S390] dasd: do path verification for paths added at runtime
When a new path is added at runtime, the CIO layer will call the drivers path_event callback. The DASD device driver uses this callback to trigger a path verification for the new path. The driver will use only those paths for I/O, which have been successfully verified. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/s390/block/dasd.c202
-rw-r--r--drivers/s390/block/dasd_3990_erp.c16
-rw-r--r--drivers/s390/block/dasd_devmap.c1
-rw-r--r--drivers/s390/block/dasd_diag.c1
-rw-r--r--drivers/s390/block/dasd_eckd.c261
-rw-r--r--drivers/s390/block/dasd_eckd.h9
-rw-r--r--drivers/s390/block/dasd_erp.c3
-rw-r--r--drivers/s390/block/dasd_fba.c3
-rw-r--r--drivers/s390/block/dasd_int.h22
9 files changed, 418 insertions, 100 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 605f96f154a5..8f2067bc88c0 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -913,6 +913,11 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
913 cqr->startclk = get_clock(); 913 cqr->startclk = get_clock();
914 cqr->starttime = jiffies; 914 cqr->starttime = jiffies;
915 cqr->retries--; 915 cqr->retries--;
916 if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
917 cqr->lpm &= device->path_data.opm;
918 if (!cqr->lpm)
919 cqr->lpm = device->path_data.opm;
920 }
916 if (cqr->cpmode == 1) { 921 if (cqr->cpmode == 1) {
917 rc = ccw_device_tm_start(device->cdev, cqr->cpaddr, 922 rc = ccw_device_tm_start(device->cdev, cqr->cpaddr,
918 (long) cqr, cqr->lpm); 923 (long) cqr, cqr->lpm);
@@ -925,35 +930,53 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
925 cqr->status = DASD_CQR_IN_IO; 930 cqr->status = DASD_CQR_IN_IO;
926 break; 931 break;
927 case -EBUSY: 932 case -EBUSY:
928 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 933 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
929 "start_IO: device busy, retry later"); 934 "start_IO: device busy, retry later");
930 break; 935 break;
931 case -ETIMEDOUT: 936 case -ETIMEDOUT:
932 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 937 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
933 "start_IO: request timeout, retry later"); 938 "start_IO: request timeout, retry later");
934 break; 939 break;
935 case -EACCES: 940 case -EACCES:
936 /* -EACCES indicates that the request used only a 941 /* -EACCES indicates that the request used only a subset of the
937 * subset of the available pathes and all these 942 * available paths and all these paths are gone. If the lpm of
938 * pathes are gone. 943 * this request was only a subset of the opm (e.g. the ppm) then
939 * Do a retry with all available pathes. 944 * we just do a retry with all available paths.
945 * If we already use the full opm, something is amiss, and we
946 * need a full path verification.
940 */ 947 */
941 cqr->lpm = LPM_ANYPATH; 948 if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
942 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 949 DBF_DEV_EVENT(DBF_WARNING, device,
943 "start_IO: selected pathes gone," 950 "start_IO: selected paths gone (%x)",
944 " retry on all pathes"); 951 cqr->lpm);
952 } else if (cqr->lpm != device->path_data.opm) {
953 cqr->lpm = device->path_data.opm;
954 DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
955 "start_IO: selected paths gone,"
956 " retry on all paths");
957 } else {
958 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
959 "start_IO: all paths in opm gone,"
960 " do path verification");
961 dasd_generic_last_path_gone(device);
962 device->path_data.opm = 0;
963 device->path_data.ppm = 0;
964 device->path_data.npm = 0;
965 device->path_data.tbvpm =
966 ccw_device_get_path_mask(device->cdev);
967 }
945 break; 968 break;
946 case -ENODEV: 969 case -ENODEV:
947 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 970 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
948 "start_IO: -ENODEV device gone, retry"); 971 "start_IO: -ENODEV device gone, retry");
949 break; 972 break;
950 case -EIO: 973 case -EIO:
951 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 974 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
952 "start_IO: -EIO device gone, retry"); 975 "start_IO: -EIO device gone, retry");
953 break; 976 break;
954 case -EINVAL: 977 case -EINVAL:
955 /* most likely caused in power management context */ 978 /* most likely caused in power management context */
956 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 979 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
957 "start_IO: -EINVAL device currently " 980 "start_IO: -EINVAL device currently "
958 "not accessible"); 981 "not accessible");
959 break; 982 break;
@@ -1175,12 +1198,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
1175 */ 1198 */
1176 if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags) && 1199 if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags) &&
1177 cqr->retries > 0) { 1200 cqr->retries > 0) {
1178 if (cqr->lpm == LPM_ANYPATH) 1201 if (cqr->lpm == device->path_data.opm)
1179 DBF_DEV_EVENT(DBF_DEBUG, device, 1202 DBF_DEV_EVENT(DBF_DEBUG, device,
1180 "default ERP in fastpath " 1203 "default ERP in fastpath "
1181 "(%i retries left)", 1204 "(%i retries left)",
1182 cqr->retries); 1205 cqr->retries);
1183 cqr->lpm = LPM_ANYPATH; 1206 if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
1207 cqr->lpm = device->path_data.opm;
1184 cqr->status = DASD_CQR_QUEUED; 1208 cqr->status = DASD_CQR_QUEUED;
1185 next = cqr; 1209 next = cqr;
1186 } else 1210 } else
@@ -1364,8 +1388,14 @@ static void __dasd_device_start_head(struct dasd_device *device)
1364 cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); 1388 cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
1365 if (cqr->status != DASD_CQR_QUEUED) 1389 if (cqr->status != DASD_CQR_QUEUED)
1366 return; 1390 return;
1367 /* when device is stopped, return request to previous layer */ 1391 /* when device is stopped, return request to previous layer
1368 if (device->stopped) { 1392 * exception: only the disconnect or unresumed bits are set and the
1393 * cqr is a path verification request
1394 */
1395 if (device->stopped &&
1396 !(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM))
1397 && test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) {
1398 cqr->intrc = -EAGAIN;
1369 cqr->status = DASD_CQR_CLEARED; 1399 cqr->status = DASD_CQR_CLEARED;
1370 dasd_schedule_device_bh(device); 1400 dasd_schedule_device_bh(device);
1371 return; 1401 return;
@@ -1381,6 +1411,23 @@ static void __dasd_device_start_head(struct dasd_device *device)
1381 dasd_device_set_timer(device, 50); 1411 dasd_device_set_timer(device, 50);
1382} 1412}
1383 1413
1414static void __dasd_device_check_path_events(struct dasd_device *device)
1415{
1416 int rc;
1417
1418 if (device->path_data.tbvpm) {
1419 if (device->stopped & ~(DASD_STOPPED_DC_WAIT |
1420 DASD_UNRESUMED_PM))
1421 return;
1422 rc = device->discipline->verify_path(
1423 device, device->path_data.tbvpm);
1424 if (rc)
1425 dasd_device_set_timer(device, 50);
1426 else
1427 device->path_data.tbvpm = 0;
1428 }
1429};
1430
1384/* 1431/*
1385 * Go through all request on the dasd_device request queue, 1432 * Go through all request on the dasd_device request queue,
1386 * terminate them on the cdev if necessary, and return them to the 1433 * terminate them on the cdev if necessary, and return them to the
@@ -1455,6 +1502,7 @@ static void dasd_device_tasklet(struct dasd_device *device)
1455 __dasd_device_check_expire(device); 1502 __dasd_device_check_expire(device);
1456 /* find final requests on ccw queue */ 1503 /* find final requests on ccw queue */
1457 __dasd_device_process_ccw_queue(device, &final_queue); 1504 __dasd_device_process_ccw_queue(device, &final_queue);
1505 __dasd_device_check_path_events(device);
1458 spin_unlock_irq(get_ccwdev_lock(device->cdev)); 1506 spin_unlock_irq(get_ccwdev_lock(device->cdev));
1459 /* Now call the callback function of requests with final status */ 1507 /* Now call the callback function of requests with final status */
1460 __dasd_device_process_final_queue(device, &final_queue); 1508 __dasd_device_process_final_queue(device, &final_queue);
@@ -2586,10 +2634,53 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
2586 return 0; 2634 return 0;
2587} 2635}
2588 2636
2637int dasd_generic_last_path_gone(struct dasd_device *device)
2638{
2639 struct dasd_ccw_req *cqr;
2640
2641 dev_warn(&device->cdev->dev, "No operational channel path is left "
2642 "for the device\n");
2643 DBF_DEV_EVENT(DBF_WARNING, device, "%s", "last path gone");
2644 /* First of all call extended error reporting. */
2645 dasd_eer_write(device, NULL, DASD_EER_NOPATH);
2646
2647 if (device->state < DASD_STATE_BASIC)
2648 return 0;
2649 /* Device is active. We want to keep it. */
2650 list_for_each_entry(cqr, &device->ccw_queue, devlist)
2651 if ((cqr->status == DASD_CQR_IN_IO) ||
2652 (cqr->status == DASD_CQR_CLEAR_PENDING)) {
2653 cqr->status = DASD_CQR_QUEUED;
2654 cqr->retries++;
2655 }
2656 dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
2657 dasd_device_clear_timer(device);
2658 dasd_schedule_device_bh(device);
2659 return 1;
2660}
2661EXPORT_SYMBOL_GPL(dasd_generic_last_path_gone);
2662
2663int dasd_generic_path_operational(struct dasd_device *device)
2664{
2665 dev_info(&device->cdev->dev, "A channel path to the device has become "
2666 "operational\n");
2667 DBF_DEV_EVENT(DBF_WARNING, device, "%s", "path operational");
2668 dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
2669 if (device->stopped & DASD_UNRESUMED_PM) {
2670 dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
2671 dasd_restore_device(device);
2672 return 1;
2673 }
2674 dasd_schedule_device_bh(device);
2675 if (device->block)
2676 dasd_schedule_block_bh(device->block);
2677 return 1;
2678}
2679EXPORT_SYMBOL_GPL(dasd_generic_path_operational);
2680
2589int dasd_generic_notify(struct ccw_device *cdev, int event) 2681int dasd_generic_notify(struct ccw_device *cdev, int event)
2590{ 2682{
2591 struct dasd_device *device; 2683 struct dasd_device *device;
2592 struct dasd_ccw_req *cqr;
2593 int ret; 2684 int ret;
2594 2685
2595 device = dasd_device_from_cdev_locked(cdev); 2686 device = dasd_device_from_cdev_locked(cdev);
@@ -2600,41 +2691,64 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
2600 case CIO_GONE: 2691 case CIO_GONE:
2601 case CIO_BOXED: 2692 case CIO_BOXED:
2602 case CIO_NO_PATH: 2693 case CIO_NO_PATH:
2603 /* First of all call extended error reporting. */ 2694 device->path_data.opm = 0;
2604 dasd_eer_write(device, NULL, DASD_EER_NOPATH); 2695 device->path_data.ppm = 0;
2605 2696 device->path_data.npm = 0;
2606 if (device->state < DASD_STATE_BASIC) 2697 ret = dasd_generic_last_path_gone(device);
2607 break;
2608 /* Device is active. We want to keep it. */
2609 list_for_each_entry(cqr, &device->ccw_queue, devlist)
2610 if (cqr->status == DASD_CQR_IN_IO) {
2611 cqr->status = DASD_CQR_QUEUED;
2612 cqr->retries++;
2613 }
2614 dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
2615 dasd_device_clear_timer(device);
2616 dasd_schedule_device_bh(device);
2617 ret = 1;
2618 break; 2698 break;
2619 case CIO_OPER: 2699 case CIO_OPER:
2620 /* FIXME: add a sanity check. */
2621 dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
2622 if (device->stopped & DASD_UNRESUMED_PM) {
2623 dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
2624 dasd_restore_device(device);
2625 ret = 1;
2626 break;
2627 }
2628 dasd_schedule_device_bh(device);
2629 if (device->block)
2630 dasd_schedule_block_bh(device->block);
2631 ret = 1; 2700 ret = 1;
2701 if (device->path_data.opm)
2702 ret = dasd_generic_path_operational(device);
2632 break; 2703 break;
2633 } 2704 }
2634 dasd_put_device(device); 2705 dasd_put_device(device);
2635 return ret; 2706 return ret;
2636} 2707}
2637 2708
2709void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
2710{
2711 int chp;
2712 __u8 oldopm, eventlpm;
2713 struct dasd_device *device;
2714
2715 device = dasd_device_from_cdev_locked(cdev);
2716 if (IS_ERR(device))
2717 return;
2718 for (chp = 0; chp < 8; chp++) {
2719 eventlpm = 0x80 >> chp;
2720 if (path_event[chp] & PE_PATH_GONE) {
2721 oldopm = device->path_data.opm;
2722 device->path_data.opm &= ~eventlpm;
2723 device->path_data.ppm &= ~eventlpm;
2724 device->path_data.npm &= ~eventlpm;
2725 if (oldopm && !device->path_data.opm)
2726 dasd_generic_last_path_gone(device);
2727 }
2728 if (path_event[chp] & PE_PATH_AVAILABLE) {
2729 device->path_data.opm &= ~eventlpm;
2730 device->path_data.ppm &= ~eventlpm;
2731 device->path_data.npm &= ~eventlpm;
2732 device->path_data.tbvpm |= eventlpm;
2733 dasd_schedule_device_bh(device);
2734 }
2735 }
2736 dasd_put_device(device);
2737}
2738EXPORT_SYMBOL_GPL(dasd_generic_path_event);
2739
2740int dasd_generic_verify_path(struct dasd_device *device, __u8 lpm)
2741{
2742 if (!device->path_data.opm && lpm) {
2743 device->path_data.opm = lpm;
2744 dasd_generic_path_operational(device);
2745 } else
2746 device->path_data.opm |= lpm;
2747 return 0;
2748}
2749EXPORT_SYMBOL_GPL(dasd_generic_verify_path);
2750
2751
2638int dasd_generic_pm_freeze(struct ccw_device *cdev) 2752int dasd_generic_pm_freeze(struct ccw_device *cdev)
2639{ 2753{
2640 struct dasd_ccw_req *cqr, *n; 2754 struct dasd_ccw_req *cqr, *n;
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 968c76cf7127..1654a24817be 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -152,9 +152,9 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
152 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); 152 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
153 opm = ccw_device_get_path_mask(device->cdev); 153 opm = ccw_device_get_path_mask(device->cdev);
154 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); 154 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
155 //FIXME: start with get_opm ?
156 if (erp->lpm == 0) 155 if (erp->lpm == 0)
157 erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum); 156 erp->lpm = device->path_data.opm &
157 ~(erp->irb.esw.esw0.sublog.lpum);
158 else 158 else
159 erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum); 159 erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum);
160 160
@@ -270,10 +270,11 @@ static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
270{ 270{
271 erp->function = dasd_3990_erp_action_1; 271 erp->function = dasd_3990_erp_action_1;
272 dasd_3990_erp_alternate_path(erp); 272 dasd_3990_erp_alternate_path(erp);
273 if (erp->status == DASD_CQR_FAILED) { 273 if (erp->status == DASD_CQR_FAILED &&
274 !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) {
274 erp->status = DASD_CQR_FILLED; 275 erp->status = DASD_CQR_FILLED;
275 erp->retries = 10; 276 erp->retries = 10;
276 erp->lpm = LPM_ANYPATH; 277 erp->lpm = erp->startdev->path_data.opm;
277 erp->function = dasd_3990_erp_action_1_sec; 278 erp->function = dasd_3990_erp_action_1_sec;
278 } 279 }
279 return erp; 280 return erp;
@@ -1907,15 +1908,14 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense)
1907static void 1908static void
1908dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) 1909dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
1909{ 1910{
1910
1911 if (sense[25] & DASD_SENSE_BIT_3) { 1911 if (sense[25] & DASD_SENSE_BIT_3) {
1912 dasd_3990_erp_alternate_path(erp); 1912 dasd_3990_erp_alternate_path(erp);
1913 1913
1914 if (erp->status == DASD_CQR_FAILED) { 1914 if (erp->status == DASD_CQR_FAILED &&
1915 !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) {
1915 /* reset the lpm and the status to be able to 1916 /* reset the lpm and the status to be able to
1916 * try further actions. */ 1917 * try further actions. */
1917 1918 erp->lpm = erp->startdev->path_data.opm;
1918 erp->lpm = 0;
1919 erp->status = DASD_CQR_NEED_ERP; 1919 erp->status = DASD_CQR_NEED_ERP;
1920 } 1920 }
1921 } 1921 }
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 8d41f3ed38d7..0001df8ad3e6 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -639,6 +639,7 @@ dasd_put_device_wake(struct dasd_device *device)
639{ 639{
640 wake_up(&dasd_delete_wq); 640 wake_up(&dasd_delete_wq);
641} 641}
642EXPORT_SYMBOL_GPL(dasd_put_device_wake);
642 643
643/* 644/*
644 * Return dasd_device structure associated with cdev. 645 * Return dasd_device structure associated with cdev.
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index a3a5db58df18..29143eda9dd9 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -619,6 +619,7 @@ static struct dasd_discipline dasd_diag_discipline = {
619 .ebcname = "DIAG", 619 .ebcname = "DIAG",
620 .max_blocks = DIAG_MAX_BLOCKS, 620 .max_blocks = DIAG_MAX_BLOCKS,
621 .check_device = dasd_diag_check_device, 621 .check_device = dasd_diag_check_device,
622 .verify_path = dasd_generic_verify_path,
622 .fill_geometry = dasd_diag_fill_geometry, 623 .fill_geometry = dasd_diag_fill_geometry,
623 .start_IO = dasd_start_diag, 624 .start_IO = dasd_start_diag,
624 .term_IO = dasd_diag_term_IO, 625 .term_IO = dasd_diag_term_IO,
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 549443af121c..a1ebf5722ae5 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -90,6 +90,18 @@ static struct {
90} *dasd_reserve_req; 90} *dasd_reserve_req;
91static DEFINE_MUTEX(dasd_reserve_mutex); 91static DEFINE_MUTEX(dasd_reserve_mutex);
92 92
93/* definitions for the path verification worker */
94struct path_verification_work_data {
95 struct work_struct worker;
96 struct dasd_device *device;
97 struct dasd_ccw_req cqr;
98 struct ccw1 ccw;
99 __u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE];
100 int isglobal;
101 __u8 tbvpm;
102};
103static struct path_verification_work_data *path_verification_worker;
104static DEFINE_MUTEX(dasd_path_verification_mutex);
93 105
94/* initial attempt at a probe function. this can be simplified once 106/* initial attempt at a probe function. this can be simplified once
95 * the other detection code is gone */ 107 * the other detection code is gone */
@@ -755,26 +767,27 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid)
755 return -EINVAL; 767 return -EINVAL;
756} 768}
757 769
758static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, 770static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
759 void *rcd_buffer, 771 struct dasd_ccw_req *cqr,
760 struct ciw *ciw, __u8 lpm) 772 __u8 *rcd_buffer,
773 __u8 lpm)
761{ 774{
762 struct dasd_ccw_req *cqr;
763 struct ccw1 *ccw; 775 struct ccw1 *ccw;
764 776 /*
765 cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count, 777 * buffer has to start with EBCDIC "V1.0" to show
766 device); 778 * support for virtual device SNEQ
767 779 */
768 if (IS_ERR(cqr)) { 780 rcd_buffer[0] = 0xE5;
769 DBF_DEV_EVENT(DBF_WARNING, device, "%s", 781 rcd_buffer[1] = 0xF1;
770 "Could not allocate RCD request"); 782 rcd_buffer[2] = 0x4B;
771 return cqr; 783 rcd_buffer[3] = 0xF0;
772 }
773 784
774 ccw = cqr->cpaddr; 785 ccw = cqr->cpaddr;
775 ccw->cmd_code = ciw->cmd; 786 ccw->cmd_code = DASD_ECKD_CCW_RCD;
787 ccw->flags = 0;
776 ccw->cda = (__u32)(addr_t)rcd_buffer; 788 ccw->cda = (__u32)(addr_t)rcd_buffer;
777 ccw->count = ciw->count; 789 ccw->count = DASD_ECKD_RCD_DATA_SIZE;
790 cqr->magic = DASD_ECKD_MAGIC;
778 791
779 cqr->startdev = device; 792 cqr->startdev = device;
780 cqr->memdev = device; 793 cqr->memdev = device;
@@ -784,7 +797,29 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
784 cqr->retries = 256; 797 cqr->retries = 256;
785 cqr->buildclk = get_clock(); 798 cqr->buildclk = get_clock();
786 cqr->status = DASD_CQR_FILLED; 799 cqr->status = DASD_CQR_FILLED;
787 return cqr; 800 set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
801}
802
803static int dasd_eckd_read_conf_immediately(struct dasd_device *device,
804 struct dasd_ccw_req *cqr,
805 __u8 *rcd_buffer,
806 __u8 lpm)
807{
808 struct ciw *ciw;
809 int rc;
810 /*
811 * sanity check: scan for RCD command in extended SenseID data
812 * some devices do not support RCD
813 */
814 ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
815 if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD)
816 return -EOPNOTSUPP;
817
818 dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm);
819 clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
820 cqr->retries = 5;
821 rc = dasd_sleep_on_immediatly(cqr);
822 return rc;
788} 823}
789 824
790static int dasd_eckd_read_conf_lpm(struct dasd_device *device, 825static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
@@ -797,32 +832,29 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
797 struct dasd_ccw_req *cqr; 832 struct dasd_ccw_req *cqr;
798 833
799 /* 834 /*
800 * scan for RCD command in extended SenseID data 835 * sanity check: scan for RCD command in extended SenseID data
836 * some devices do not support RCD
801 */ 837 */
802 ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); 838 ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
803 if (!ciw || ciw->cmd == 0) { 839 if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) {
804 ret = -EOPNOTSUPP; 840 ret = -EOPNOTSUPP;
805 goto out_error; 841 goto out_error;
806 } 842 }
807 rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); 843 rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA);
808 if (!rcd_buf) { 844 if (!rcd_buf) {
809 ret = -ENOMEM; 845 ret = -ENOMEM;
810 goto out_error; 846 goto out_error;
811 } 847 }
812 848 cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */,
813 /* 849 0, /* use rcd_buf as data ara */
814 * buffer has to start with EBCDIC "V1.0" to show 850 device);
815 * support for virtual device SNEQ
816 */
817 rcd_buf[0] = 0xE5;
818 rcd_buf[1] = 0xF1;
819 rcd_buf[2] = 0x4B;
820 rcd_buf[3] = 0xF0;
821 cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
822 if (IS_ERR(cqr)) { 851 if (IS_ERR(cqr)) {
823 ret = PTR_ERR(cqr); 852 DBF_DEV_EVENT(DBF_WARNING, device, "%s",
853 "Could not allocate RCD request");
854 ret = -ENOMEM;
824 goto out_error; 855 goto out_error;
825 } 856 }
857 dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
826 ret = dasd_sleep_on(cqr); 858 ret = dasd_sleep_on(cqr);
827 /* 859 /*
828 * on success we update the user input parms 860 * on success we update the user input parms
@@ -831,7 +863,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
831 if (ret) 863 if (ret)
832 goto out_error; 864 goto out_error;
833 865
834 *rcd_buffer_size = ciw->count; 866 *rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE;
835 *rcd_buffer = rcd_buf; 867 *rcd_buffer = rcd_buf;
836 return 0; 868 return 0;
837out_error: 869out_error:
@@ -901,18 +933,18 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
901 void *conf_data; 933 void *conf_data;
902 int conf_len, conf_data_saved; 934 int conf_len, conf_data_saved;
903 int rc; 935 int rc;
904 __u8 lpm; 936 __u8 lpm, opm;
905 struct dasd_eckd_private *private; 937 struct dasd_eckd_private *private;
906 struct dasd_eckd_path *path_data; 938 struct dasd_path *path_data;
907 939
908 private = (struct dasd_eckd_private *) device->private; 940 private = (struct dasd_eckd_private *) device->private;
909 path_data = (struct dasd_eckd_path *) &private->path_data; 941 path_data = &device->path_data;
910 path_data->opm = ccw_device_get_path_mask(device->cdev); 942 opm = ccw_device_get_path_mask(device->cdev);
911 lpm = 0x80; 943 lpm = 0x80;
912 conf_data_saved = 0; 944 conf_data_saved = 0;
913 /* get configuration data per operational path */ 945 /* get configuration data per operational path */
914 for (lpm = 0x80; lpm; lpm>>= 1) { 946 for (lpm = 0x80; lpm; lpm>>= 1) {
915 if (lpm & path_data->opm){ 947 if (lpm & opm) {
916 rc = dasd_eckd_read_conf_lpm(device, &conf_data, 948 rc = dasd_eckd_read_conf_lpm(device, &conf_data,
917 &conf_len, lpm); 949 &conf_len, lpm);
918 if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ 950 if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */
@@ -925,6 +957,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
925 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", 957 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
926 "No configuration data " 958 "No configuration data "
927 "retrieved"); 959 "retrieved");
960 /* no further analysis possible */
961 path_data->opm |= lpm;
928 continue; /* no error */ 962 continue; /* no error */
929 } 963 }
930 /* save first valid configuration data */ 964 /* save first valid configuration data */
@@ -948,6 +982,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
948 path_data->ppm |= lpm; 982 path_data->ppm |= lpm;
949 break; 983 break;
950 } 984 }
985 path_data->opm |= lpm;
951 if (conf_data != private->conf_data) 986 if (conf_data != private->conf_data)
952 kfree(conf_data); 987 kfree(conf_data);
953 } 988 }
@@ -955,6 +990,140 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
955 return 0; 990 return 0;
956} 991}
957 992
993static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
994{
995 struct dasd_eckd_private *private;
996 int mdc;
997 u32 fcx_max_data;
998
999 private = (struct dasd_eckd_private *) device->private;
1000 if (private->fcx_max_data) {
1001 mdc = ccw_device_get_mdc(device->cdev, lpm);
1002 if ((mdc < 0)) {
1003 dev_warn(&device->cdev->dev,
1004 "Detecting the maximum data size for zHPF "
1005 "requests failed (rc=%d) for a new path %x\n",
1006 mdc, lpm);
1007 return mdc;
1008 }
1009 fcx_max_data = mdc * FCX_MAX_DATA_FACTOR;
1010 if (fcx_max_data < private->fcx_max_data) {
1011 dev_warn(&device->cdev->dev,
1012 "The maximum data size for zHPF requests %u "
1013 "on a new path %x is below the active maximum "
1014 "%u\n", fcx_max_data, lpm,
1015 private->fcx_max_data);
1016 return -EACCES;
1017 }
1018 }
1019 return 0;
1020}
1021
1022static void do_path_verification_work(struct work_struct *work)
1023{
1024 struct path_verification_work_data *data;
1025 struct dasd_device *device;
1026 __u8 lpm, opm, npm, ppm, epm;
1027 unsigned long flags;
1028 int rc;
1029
1030 data = container_of(work, struct path_verification_work_data, worker);
1031 device = data->device;
1032
1033 opm = 0;
1034 npm = 0;
1035 ppm = 0;
1036 epm = 0;
1037 for (lpm = 0x80; lpm; lpm >>= 1) {
1038 if (lpm & data->tbvpm) {
1039 memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer));
1040 memset(&data->cqr, 0, sizeof(data->cqr));
1041 data->cqr.cpaddr = &data->ccw;
1042 rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
1043 data->rcd_buffer,
1044 lpm);
1045 if (!rc) {
1046 switch (dasd_eckd_path_access(data->rcd_buffer,
1047 DASD_ECKD_RCD_DATA_SIZE)) {
1048 case 0x02:
1049 npm |= lpm;
1050 break;
1051 case 0x03:
1052 ppm |= lpm;
1053 break;
1054 }
1055 opm |= lpm;
1056 } else if (rc == -EOPNOTSUPP) {
1057 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
1058 "path verification: No configuration "
1059 "data retrieved");
1060 opm |= lpm;
1061 } else if (rc == -EAGAIN) {
1062 DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
1063 "path verification: device is stopped,"
1064 " try again later");
1065 epm |= lpm;
1066 } else {
1067 dev_warn(&device->cdev->dev,
1068 "Reading device feature codes failed "
1069 "(rc=%d) for new path %x\n", rc, lpm);
1070 continue;
1071 }
1072 if (verify_fcx_max_data(device, lpm)) {
1073 opm &= ~lpm;
1074 npm &= ~lpm;
1075 ppm &= ~lpm;
1076 }
1077 }
1078 }
1079 /*
1080 * There is a small chance that a path is lost again between
1081 * above path verification and the following modification of
1082 * the device opm mask. We could avoid that race here by using
1083 * yet another path mask, but we rather deal with this unlikely
1084 * situation in dasd_start_IO.
1085 */
1086 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
1087 if (!device->path_data.opm && opm) {
1088 device->path_data.opm = opm;
1089 dasd_generic_path_operational(device);
1090 } else
1091 device->path_data.opm |= opm;
1092 device->path_data.npm |= npm;
1093 device->path_data.ppm |= ppm;
1094 device->path_data.tbvpm |= epm;
1095 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
1096
1097 dasd_put_device(device);
1098 if (data->isglobal)
1099 mutex_unlock(&dasd_path_verification_mutex);
1100 else
1101 kfree(data);
1102}
1103
1104static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm)
1105{
1106 struct path_verification_work_data *data;
1107
1108 data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA);
1109 if (!data) {
1110 if (mutex_trylock(&dasd_path_verification_mutex)) {
1111 data = path_verification_worker;
1112 data->isglobal = 1;
1113 } else
1114 return -ENOMEM;
1115 } else {
1116 memset(data, 0, sizeof(*data));
1117 data->isglobal = 0;
1118 }
1119 INIT_WORK(&data->worker, do_path_verification_work);
1120 dasd_get_device(device);
1121 data->device = device;
1122 data->tbvpm = lpm;
1123 schedule_work(&data->worker);
1124 return 0;
1125}
1126
958static int dasd_eckd_read_features(struct dasd_device *device) 1127static int dasd_eckd_read_features(struct dasd_device *device)
959{ 1128{
960 struct dasd_psf_prssd_data *prssdp; 1129 struct dasd_psf_prssd_data *prssdp;
@@ -1749,6 +1918,7 @@ static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
1749 if (cqr->block && (cqr->startdev != cqr->block->base)) { 1918 if (cqr->block && (cqr->startdev != cqr->block->base)) {
1750 dasd_eckd_reset_ccw_to_base_io(cqr); 1919 dasd_eckd_reset_ccw_to_base_io(cqr);
1751 cqr->startdev = cqr->block->base; 1920 cqr->startdev = cqr->block->base;
1921 cqr->lpm = cqr->block->base->path_data.opm;
1752 } 1922 }
1753}; 1923};
1754 1924
@@ -2017,7 +2187,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
2017 cqr->memdev = startdev; 2187 cqr->memdev = startdev;
2018 cqr->block = block; 2188 cqr->block = block;
2019 cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 2189 cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
2020 cqr->lpm = private->path_data.ppm; 2190 cqr->lpm = startdev->path_data.ppm;
2021 cqr->retries = 256; 2191 cqr->retries = 256;
2022 cqr->buildclk = get_clock(); 2192 cqr->buildclk = get_clock();
2023 cqr->status = DASD_CQR_FILLED; 2193 cqr->status = DASD_CQR_FILLED;
@@ -2194,7 +2364,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
2194 cqr->memdev = startdev; 2364 cqr->memdev = startdev;
2195 cqr->block = block; 2365 cqr->block = block;
2196 cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 2366 cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
2197 cqr->lpm = private->path_data.ppm; 2367 cqr->lpm = startdev->path_data.ppm;
2198 cqr->retries = 256; 2368 cqr->retries = 256;
2199 cqr->buildclk = get_clock(); 2369 cqr->buildclk = get_clock();
2200 cqr->status = DASD_CQR_FILLED; 2370 cqr->status = DASD_CQR_FILLED;
@@ -2484,7 +2654,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
2484 cqr->memdev = startdev; 2654 cqr->memdev = startdev;
2485 cqr->block = block; 2655 cqr->block = block;
2486 cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ 2656 cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
2487 cqr->lpm = private->path_data.ppm; 2657 cqr->lpm = startdev->path_data.ppm;
2488 cqr->retries = 256; 2658 cqr->retries = 256;
2489 cqr->buildclk = get_clock(); 2659 cqr->buildclk = get_clock();
2490 cqr->status = DASD_CQR_FILLED; 2660 cqr->status = DASD_CQR_FILLED;
@@ -3624,6 +3794,7 @@ static struct ccw_driver dasd_eckd_driver = {
3624 .set_offline = dasd_generic_set_offline, 3794 .set_offline = dasd_generic_set_offline,
3625 .set_online = dasd_eckd_set_online, 3795 .set_online = dasd_eckd_set_online,
3626 .notify = dasd_generic_notify, 3796 .notify = dasd_generic_notify,
3797 .path_event = dasd_generic_path_event,
3627 .freeze = dasd_generic_pm_freeze, 3798 .freeze = dasd_generic_pm_freeze,
3628 .thaw = dasd_generic_restore_device, 3799 .thaw = dasd_generic_restore_device,
3629 .restore = dasd_generic_restore_device, 3800 .restore = dasd_generic_restore_device,
@@ -3651,6 +3822,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
3651 .check_device = dasd_eckd_check_characteristics, 3822 .check_device = dasd_eckd_check_characteristics,
3652 .uncheck_device = dasd_eckd_uncheck_device, 3823 .uncheck_device = dasd_eckd_uncheck_device,
3653 .do_analysis = dasd_eckd_do_analysis, 3824 .do_analysis = dasd_eckd_do_analysis,
3825 .verify_path = dasd_eckd_verify_path,
3654 .ready_to_online = dasd_eckd_ready_to_online, 3826 .ready_to_online = dasd_eckd_ready_to_online,
3655 .online_to_ready = dasd_eckd_online_to_ready, 3827 .online_to_ready = dasd_eckd_online_to_ready,
3656 .fill_geometry = dasd_eckd_fill_geometry, 3828 .fill_geometry = dasd_eckd_fill_geometry,
@@ -3683,11 +3855,19 @@ dasd_eckd_init(void)
3683 GFP_KERNEL | GFP_DMA); 3855 GFP_KERNEL | GFP_DMA);
3684 if (!dasd_reserve_req) 3856 if (!dasd_reserve_req)
3685 return -ENOMEM; 3857 return -ENOMEM;
3858 path_verification_worker = kmalloc(sizeof(*path_verification_worker),
3859 GFP_KERNEL | GFP_DMA);
3860 if (!path_verification_worker) {
3861 kfree(dasd_reserve_req);
3862 return -ENOMEM;
3863 }
3686 ret = ccw_driver_register(&dasd_eckd_driver); 3864 ret = ccw_driver_register(&dasd_eckd_driver);
3687 if (!ret) 3865 if (!ret)
3688 wait_for_device_probe(); 3866 wait_for_device_probe();
3689 else 3867 else {
3868 kfree(path_verification_worker);
3690 kfree(dasd_reserve_req); 3869 kfree(dasd_reserve_req);
3870 }
3691 return ret; 3871 return ret;
3692} 3872}
3693 3873
@@ -3695,6 +3875,7 @@ static void __exit
3695dasd_eckd_cleanup(void) 3875dasd_eckd_cleanup(void)
3696{ 3876{
3697 ccw_driver_unregister(&dasd_eckd_driver); 3877 ccw_driver_unregister(&dasd_eckd_driver);
3878 kfree(path_verification_worker);
3698 kfree(dasd_reserve_req); 3879 kfree(dasd_reserve_req);
3699} 3880}
3700 3881
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 2150aed541be..5051f374cbcb 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -45,6 +45,7 @@
45#define DASD_ECKD_CCW_PFX 0xE7 45#define DASD_ECKD_CCW_PFX 0xE7
46#define DASD_ECKD_CCW_PFX_READ 0xEA 46#define DASD_ECKD_CCW_PFX_READ 0xEA
47#define DASD_ECKD_CCW_RSCK 0xF9 47#define DASD_ECKD_CCW_RSCK 0xF9
48#define DASD_ECKD_CCW_RCD 0xFA
48 49
49/* 50/*
50 * Perform Subsystem Function / Sub-Orders 51 * Perform Subsystem Function / Sub-Orders
@@ -59,6 +60,7 @@
59 60
60 61
61#define FCX_MAX_DATA_FACTOR 65536 62#define FCX_MAX_DATA_FACTOR 65536
63#define DASD_ECKD_RCD_DATA_SIZE 256
62 64
63 65
64/***************************************************************************** 66/*****************************************************************************
@@ -335,12 +337,6 @@ struct dasd_gneq {
335 __u8 reserved2[22]; 337 __u8 reserved2[22];
336} __attribute__ ((packed)); 338} __attribute__ ((packed));
337 339
338struct dasd_eckd_path {
339 __u8 opm;
340 __u8 ppm;
341 __u8 npm;
342};
343
344struct dasd_rssd_features { 340struct dasd_rssd_features {
345 char feature[256]; 341 char feature[256];
346} __attribute__((packed)); 342} __attribute__((packed));
@@ -446,7 +442,6 @@ struct dasd_eckd_private {
446 struct vd_sneq *vdsneq; 442 struct vd_sneq *vdsneq;
447 struct dasd_gneq *gneq; 443 struct dasd_gneq *gneq;
448 444
449 struct dasd_eckd_path path_data;
450 struct eckd_count count_area[5]; 445 struct eckd_count count_area[5];
451 int init_cqr_status; 446 int init_cqr_status;
452 int uses_cdl; 447 int uses_cdl;
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c
index 7656384a811d..0eafe2e421e7 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -96,7 +96,8 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr)
96 DBF_DEV_EVENT(DBF_DEBUG, device, 96 DBF_DEV_EVENT(DBF_DEBUG, device,
97 "default ERP called (%i retries left)", 97 "default ERP called (%i retries left)",
98 cqr->retries); 98 cqr->retries);
99 cqr->lpm = LPM_ANYPATH; 99 if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
100 cqr->lpm = device->path_data.opm;
100 cqr->status = DASD_CQR_FILLED; 101 cqr->status = DASD_CQR_FILLED;
101 } else { 102 } else {
102 pr_err("%s: default ERP has run out of retries and failed\n", 103 pr_err("%s: default ERP has run out of retries and failed\n",
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index bec5486e0e6d..86bacda2c5f6 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -73,6 +73,7 @@ static struct ccw_driver dasd_fba_driver = {
73 .set_offline = dasd_generic_set_offline, 73 .set_offline = dasd_generic_set_offline,
74 .set_online = dasd_fba_set_online, 74 .set_online = dasd_fba_set_online,
75 .notify = dasd_generic_notify, 75 .notify = dasd_generic_notify,
76 .path_event = dasd_generic_path_event,
76 .freeze = dasd_generic_pm_freeze, 77 .freeze = dasd_generic_pm_freeze,
77 .thaw = dasd_generic_restore_device, 78 .thaw = dasd_generic_restore_device,
78 .restore = dasd_generic_restore_device, 79 .restore = dasd_generic_restore_device,
@@ -164,6 +165,7 @@ dasd_fba_check_characteristics(struct dasd_device *device)
164 } 165 }
165 166
166 device->default_expires = DASD_EXPIRES; 167 device->default_expires = DASD_EXPIRES;
168 device->path_data.opm = LPM_ANYPATH;
167 169
168 readonly = dasd_device_is_ro(device); 170 readonly = dasd_device_is_ro(device);
169 if (readonly) 171 if (readonly)
@@ -596,6 +598,7 @@ static struct dasd_discipline dasd_fba_discipline = {
596 .max_blocks = 96, 598 .max_blocks = 96,
597 .check_device = dasd_fba_check_characteristics, 599 .check_device = dasd_fba_check_characteristics,
598 .do_analysis = dasd_fba_do_analysis, 600 .do_analysis = dasd_fba_do_analysis,
601 .verify_path = dasd_generic_verify_path,
599 .fill_geometry = dasd_fba_fill_geometry, 602 .fill_geometry = dasd_fba_fill_geometry,
600 .start_IO = dasd_start_IO, 603 .start_IO = dasd_start_IO,
601 .term_IO = dasd_term_IO, 604 .term_IO = dasd_term_IO,
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 500678d7116c..ba038ef57606 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -231,6 +231,7 @@ struct dasd_ccw_req {
231/* per dasd_ccw_req flags */ 231/* per dasd_ccw_req flags */
232#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ 232#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
233#define DASD_CQR_FLAGS_FAILFAST 1 /* FAILFAST */ 233#define DASD_CQR_FLAGS_FAILFAST 1 /* FAILFAST */
234#define DASD_CQR_VERIFY_PATH 2 /* path verification request */
234 235
235/* Signature for error recovery functions. */ 236/* Signature for error recovery functions. */
236typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); 237typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *);
@@ -287,6 +288,14 @@ struct dasd_discipline {
287 int (*do_analysis) (struct dasd_block *); 288 int (*do_analysis) (struct dasd_block *);
288 289
289 /* 290 /*
291 * This function is called, when new paths become available.
292 * Disciplins may use this callback to do necessary setup work,
293 * e.g. verify that new path is compatible with the current
294 * configuration.
295 */
296 int (*verify_path)(struct dasd_device *, __u8);
297
298 /*
290 * Last things to do when a device is set online, and first things 299 * Last things to do when a device is set online, and first things
291 * when it is set offline. 300 * when it is set offline.
292 */ 301 */
@@ -362,6 +371,13 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer;
362#define DASD_EER_STATECHANGE 3 371#define DASD_EER_STATECHANGE 3
363#define DASD_EER_PPRCSUSPEND 4 372#define DASD_EER_PPRCSUSPEND 4
364 373
374struct dasd_path {
375 __u8 opm;
376 __u8 tbvpm;
377 __u8 ppm;
378 __u8 npm;
379};
380
365struct dasd_device { 381struct dasd_device {
366 /* Block device stuff. */ 382 /* Block device stuff. */
367 struct dasd_block *block; 383 struct dasd_block *block;
@@ -377,6 +393,7 @@ struct dasd_device {
377 struct dasd_discipline *discipline; 393 struct dasd_discipline *discipline;
378 struct dasd_discipline *base_discipline; 394 struct dasd_discipline *base_discipline;
379 char *private; 395 char *private;
396 struct dasd_path path_data;
380 397
381 /* Device state and target state. */ 398 /* Device state and target state. */
382 int state, target; 399 int state, target;
@@ -620,10 +637,15 @@ void dasd_generic_remove (struct ccw_device *cdev);
620int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *); 637int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
621int dasd_generic_set_offline (struct ccw_device *cdev); 638int dasd_generic_set_offline (struct ccw_device *cdev);
622int dasd_generic_notify(struct ccw_device *, int); 639int dasd_generic_notify(struct ccw_device *, int);
640int dasd_generic_last_path_gone(struct dasd_device *);
641int dasd_generic_path_operational(struct dasd_device *);
642
623void dasd_generic_handle_state_change(struct dasd_device *); 643void dasd_generic_handle_state_change(struct dasd_device *);
624int dasd_generic_pm_freeze(struct ccw_device *); 644int dasd_generic_pm_freeze(struct ccw_device *);
625int dasd_generic_restore_device(struct ccw_device *); 645int dasd_generic_restore_device(struct ccw_device *);
626enum uc_todo dasd_generic_uc_handler(struct ccw_device *, struct irb *); 646enum uc_todo dasd_generic_uc_handler(struct ccw_device *, struct irb *);
647void dasd_generic_path_event(struct ccw_device *, int *);
648int dasd_generic_verify_path(struct dasd_device *, __u8);
627 649
628int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); 650int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
629char *dasd_get_sense(struct irb *); 651char *dasd_get_sense(struct irb *);