diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2009-12-07 06:51:27 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:31 -0500 |
commit | 9679baaf85b6e4dc662160bbbca344287ea6580d (patch) | |
tree | 20cf246830da783a122064b3525d954df4bd4e1c /drivers | |
parent | 39f5360b3d68a8e96d280481d9c442e7c005c317 (diff) |
[S390] cio: use ccw request infrastructure for pgid
Use the newly introduced ccw request infrastructure to implement
pgid related operations: sense pgid, set pgid and disband pg.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/cio/device.c | 3 | ||||
-rw-r--r-- | drivers/s390/cio/device.h | 3 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 100 | ||||
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 876 | ||||
-rw-r--r-- | drivers/s390/cio/io_sch.h | 5 |
5 files changed, 383 insertions, 604 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index afa362ce9e85..6a9ac85065ed 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -957,9 +957,6 @@ void io_subchannel_init_config(struct subchannel *sch) | |||
957 | { | 957 | { |
958 | memset(&sch->config, 0, sizeof(sch->config)); | 958 | memset(&sch->config, 0, sizeof(sch->config)); |
959 | sch->config.csense = 1; | 959 | sch->config.csense = 1; |
960 | /* Use subchannel mp mode when there is more than 1 installed CHPID. */ | ||
961 | if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0) | ||
962 | sch->config.mp = 1; | ||
963 | } | 960 | } |
964 | 961 | ||
965 | static void io_subchannel_init_fields(struct subchannel *sch) | 962 | static void io_subchannel_init_fields(struct subchannel *sch) |
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 2815798c4165..ac6f55b4b74c 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h | |||
@@ -112,15 +112,12 @@ void ccw_device_sense_id_done(struct ccw_device *, int); | |||
112 | 112 | ||
113 | /* Function prototypes for path grouping stuff. */ | 113 | /* Function prototypes for path grouping stuff. */ |
114 | void ccw_device_sense_pgid_start(struct ccw_device *); | 114 | void ccw_device_sense_pgid_start(struct ccw_device *); |
115 | void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event); | ||
116 | void ccw_device_sense_pgid_done(struct ccw_device *, int); | 115 | void ccw_device_sense_pgid_done(struct ccw_device *, int); |
117 | 116 | ||
118 | void ccw_device_verify_start(struct ccw_device *); | 117 | void ccw_device_verify_start(struct ccw_device *); |
119 | void ccw_device_verify_irq(struct ccw_device *, enum dev_event); | ||
120 | void ccw_device_verify_done(struct ccw_device *, int); | 118 | void ccw_device_verify_done(struct ccw_device *, int); |
121 | 119 | ||
122 | void ccw_device_disband_start(struct ccw_device *); | 120 | void ccw_device_disband_start(struct ccw_device *); |
123 | void ccw_device_disband_irq(struct ccw_device *, enum dev_event); | ||
124 | void ccw_device_disband_done(struct ccw_device *, int); | 121 | void ccw_device_disband_done(struct ccw_device *, int); |
125 | 122 | ||
126 | int ccw_device_call_handler(struct ccw_device *); | 123 | int ccw_device_call_handler(struct ccw_device *); |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 6247d07d395e..c7439f5500f8 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -394,58 +394,6 @@ ccw_device_done(struct ccw_device *cdev, int state) | |||
394 | wake_up(&cdev->private->wait_q); | 394 | wake_up(&cdev->private->wait_q); |
395 | } | 395 | } |
396 | 396 | ||
397 | static int cmp_pgid(struct pgid *p1, struct pgid *p2) | ||
398 | { | ||
399 | char *c1; | ||
400 | char *c2; | ||
401 | |||
402 | c1 = (char *)p1; | ||
403 | c2 = (char *)p2; | ||
404 | |||
405 | return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1); | ||
406 | } | ||
407 | |||
408 | static void __ccw_device_get_common_pgid(struct ccw_device *cdev) | ||
409 | { | ||
410 | int i; | ||
411 | int last; | ||
412 | |||
413 | last = 0; | ||
414 | for (i = 0; i < 8; i++) { | ||
415 | if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET) | ||
416 | /* No PGID yet */ | ||
417 | continue; | ||
418 | if (cdev->private->pgid[last].inf.ps.state1 == | ||
419 | SNID_STATE1_RESET) { | ||
420 | /* First non-zero PGID */ | ||
421 | last = i; | ||
422 | continue; | ||
423 | } | ||
424 | if (cmp_pgid(&cdev->private->pgid[i], | ||
425 | &cdev->private->pgid[last]) == 0) | ||
426 | /* Non-conflicting PGIDs */ | ||
427 | continue; | ||
428 | |||
429 | /* PGID mismatch, can't pathgroup. */ | ||
430 | CIO_MSG_EVENT(0, "SNID - pgid mismatch for device " | ||
431 | "0.%x.%04x, can't pathgroup\n", | ||
432 | cdev->private->dev_id.ssid, | ||
433 | cdev->private->dev_id.devno); | ||
434 | cdev->private->options.pgroup = 0; | ||
435 | return; | ||
436 | } | ||
437 | if (cdev->private->pgid[last].inf.ps.state1 == | ||
438 | SNID_STATE1_RESET) | ||
439 | /* No previous pgid found */ | ||
440 | memcpy(&cdev->private->pgid[0], | ||
441 | &channel_subsystems[0]->global_pgid, | ||
442 | sizeof(struct pgid)); | ||
443 | else | ||
444 | /* Use existing pgid */ | ||
445 | memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last], | ||
446 | sizeof(struct pgid)); | ||
447 | } | ||
448 | |||
449 | /* | 397 | /* |
450 | * Function called from device_pgid.c after sense path ground has completed. | 398 | * Function called from device_pgid.c after sense path ground has completed. |
451 | */ | 399 | */ |
@@ -457,12 +405,8 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) | |||
457 | sch = to_subchannel(cdev->dev.parent); | 405 | sch = to_subchannel(cdev->dev.parent); |
458 | switch (err) { | 406 | switch (err) { |
459 | case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ | 407 | case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ |
460 | cdev->private->options.pgroup = 0; | ||
461 | break; | ||
462 | case 0: /* success */ | 408 | case 0: /* success */ |
463 | case -EACCES: /* partial success, some paths not operational */ | 409 | case -EACCES: /* partial success, some paths not operational */ |
464 | /* Check if all pgids are equal or 0. */ | ||
465 | __ccw_device_get_common_pgid(cdev); | ||
466 | break; | 410 | break; |
467 | case -ETIME: /* Sense path group id stopped by timeout. */ | 411 | case -ETIME: /* Sense path group id stopped by timeout. */ |
468 | case -EUSERS: /* device is reserved for someone else. */ | 412 | case -EUSERS: /* device is reserved for someone else. */ |
@@ -474,7 +418,6 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) | |||
474 | } | 418 | } |
475 | /* Start Path Group verification. */ | 419 | /* Start Path Group verification. */ |
476 | cdev->private->state = DEV_STATE_VERIFY; | 420 | cdev->private->state = DEV_STATE_VERIFY; |
477 | cdev->private->flags.doverify = 0; | ||
478 | ccw_device_verify_start(cdev); | 421 | ccw_device_verify_start(cdev); |
479 | } | 422 | } |
480 | 423 | ||
@@ -537,7 +480,6 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) | |||
537 | sch->lpm = sch->vpm; | 480 | sch->lpm = sch->vpm; |
538 | /* Repeat path verification? */ | 481 | /* Repeat path verification? */ |
539 | if (cdev->private->flags.doverify) { | 482 | if (cdev->private->flags.doverify) { |
540 | cdev->private->flags.doverify = 0; | ||
541 | ccw_device_verify_start(cdev); | 483 | ccw_device_verify_start(cdev); |
542 | return; | 484 | return; |
543 | } | 485 | } |
@@ -602,7 +544,6 @@ ccw_device_online(struct ccw_device *cdev) | |||
602 | if (!cdev->private->options.pgroup) { | 544 | if (!cdev->private->options.pgroup) { |
603 | /* Start initial path verification. */ | 545 | /* Start initial path verification. */ |
604 | cdev->private->state = DEV_STATE_VERIFY; | 546 | cdev->private->state = DEV_STATE_VERIFY; |
605 | cdev->private->flags.doverify = 0; | ||
606 | ccw_device_verify_start(cdev); | 547 | ccw_device_verify_start(cdev); |
607 | return 0; | 548 | return 0; |
608 | } | 549 | } |
@@ -624,7 +565,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err) | |||
624 | break; | 565 | break; |
625 | default: | 566 | default: |
626 | cdev->private->flags.donotify = 0; | 567 | cdev->private->flags.donotify = 0; |
627 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); | ||
628 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); | 568 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); |
629 | break; | 569 | break; |
630 | } | 570 | } |
@@ -673,27 +613,6 @@ ccw_device_offline(struct ccw_device *cdev) | |||
673 | } | 613 | } |
674 | 614 | ||
675 | /* | 615 | /* |
676 | * Handle timeout in device online/offline process. | ||
677 | */ | ||
678 | static void | ||
679 | ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event) | ||
680 | { | ||
681 | int ret; | ||
682 | |||
683 | ret = ccw_device_cancel_halt_clear(cdev); | ||
684 | switch (ret) { | ||
685 | case 0: | ||
686 | ccw_device_done(cdev, DEV_STATE_BOXED); | ||
687 | break; | ||
688 | case -ENODEV: | ||
689 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); | ||
690 | break; | ||
691 | default: | ||
692 | ccw_device_set_timeout(cdev, 3*HZ); | ||
693 | } | ||
694 | } | ||
695 | |||
696 | /* | ||
697 | * Handle not operational event in non-special state. | 616 | * Handle not operational event in non-special state. |
698 | */ | 617 | */ |
699 | static void ccw_device_generic_notoper(struct ccw_device *cdev, | 618 | static void ccw_device_generic_notoper(struct ccw_device *cdev, |
@@ -751,7 +670,6 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) | |||
751 | } | 670 | } |
752 | /* Device is idle, we can do the path verification. */ | 671 | /* Device is idle, we can do the path verification. */ |
753 | cdev->private->state = DEV_STATE_VERIFY; | 672 | cdev->private->state = DEV_STATE_VERIFY; |
754 | cdev->private->flags.doverify = 0; | ||
755 | ccw_device_verify_start(cdev); | 673 | ccw_device_verify_start(cdev); |
756 | } | 674 | } |
757 | 675 | ||
@@ -1103,9 +1021,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1103 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1021 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1104 | }, | 1022 | }, |
1105 | [DEV_STATE_SENSE_PGID] = { | 1023 | [DEV_STATE_SENSE_PGID] = { |
1106 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, | 1024 | [DEV_EVENT_NOTOPER] = ccw_device_request_event, |
1107 | [DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq, | 1025 | [DEV_EVENT_INTERRUPT] = ccw_device_request_event, |
1108 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, | 1026 | [DEV_EVENT_TIMEOUT] = ccw_device_request_event, |
1109 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1027 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1110 | }, | 1028 | }, |
1111 | [DEV_STATE_SENSE_ID] = { | 1029 | [DEV_STATE_SENSE_ID] = { |
@@ -1121,9 +1039,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1121 | [DEV_EVENT_VERIFY] = ccw_device_offline_verify, | 1039 | [DEV_EVENT_VERIFY] = ccw_device_offline_verify, |
1122 | }, | 1040 | }, |
1123 | [DEV_STATE_VERIFY] = { | 1041 | [DEV_STATE_VERIFY] = { |
1124 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, | 1042 | [DEV_EVENT_NOTOPER] = ccw_device_request_event, |
1125 | [DEV_EVENT_INTERRUPT] = ccw_device_verify_irq, | 1043 | [DEV_EVENT_INTERRUPT] = ccw_device_request_event, |
1126 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, | 1044 | [DEV_EVENT_TIMEOUT] = ccw_device_request_event, |
1127 | [DEV_EVENT_VERIFY] = ccw_device_delay_verify, | 1045 | [DEV_EVENT_VERIFY] = ccw_device_delay_verify, |
1128 | }, | 1046 | }, |
1129 | [DEV_STATE_ONLINE] = { | 1047 | [DEV_STATE_ONLINE] = { |
@@ -1139,9 +1057,9 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { | |||
1139 | [DEV_EVENT_VERIFY] = ccw_device_online_verify, | 1057 | [DEV_EVENT_VERIFY] = ccw_device_online_verify, |
1140 | }, | 1058 | }, |
1141 | [DEV_STATE_DISBAND_PGID] = { | 1059 | [DEV_STATE_DISBAND_PGID] = { |
1142 | [DEV_EVENT_NOTOPER] = ccw_device_generic_notoper, | 1060 | [DEV_EVENT_NOTOPER] = ccw_device_request_event, |
1143 | [DEV_EVENT_INTERRUPT] = ccw_device_disband_irq, | 1061 | [DEV_EVENT_INTERRUPT] = ccw_device_request_event, |
1144 | [DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout, | 1062 | [DEV_EVENT_TIMEOUT] = ccw_device_request_event, |
1145 | [DEV_EVENT_VERIFY] = ccw_device_nop, | 1063 | [DEV_EVENT_VERIFY] = ccw_device_nop, |
1146 | }, | 1064 | }, |
1147 | [DEV_STATE_BOXED] = { | 1065 | [DEV_STATE_BOXED] = { |
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index cb27bd4cc231..ce493144b054 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c | |||
@@ -1,594 +1,460 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/cio/device_pgid.c | 2 | * CCW device PGID and path verification I/O handling. |
3 | * | 3 | * |
4 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | 4 | * Copyright IBM Corp. 2002,2009 |
5 | * IBM Corporation | 5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
6 | * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | 7 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
8 | * | ||
9 | * Path Group ID functions. | ||
10 | */ | 8 | */ |
11 | 9 | ||
12 | #include <linux/module.h> | 10 | #include <linux/kernel.h> |
13 | #include <linux/init.h> | 11 | #include <linux/string.h> |
14 | 12 | #include <linux/types.h> | |
13 | #include <linux/errno.h> | ||
14 | #include <linux/bitops.h> | ||
15 | #include <asm/ccwdev.h> | 15 | #include <asm/ccwdev.h> |
16 | #include <asm/cio.h> | 16 | #include <asm/cio.h> |
17 | #include <asm/delay.h> | ||
18 | #include <asm/lowcore.h> | ||
19 | 17 | ||
20 | #include "cio.h" | 18 | #include "cio.h" |
21 | #include "cio_debug.h" | 19 | #include "cio_debug.h" |
22 | #include "css.h" | ||
23 | #include "device.h" | 20 | #include "device.h" |
24 | #include "ioasm.h" | ||
25 | #include "io_sch.h" | 21 | #include "io_sch.h" |
26 | 22 | ||
23 | #define PGID_RETRIES 5 | ||
24 | #define PGID_TIMEOUT (10 * HZ) | ||
25 | |||
27 | /* | 26 | /* |
28 | * Helper function called from interrupt context to decide whether an | 27 | * Process path verification data and report result. |
29 | * operation should be tried again. | ||
30 | */ | 28 | */ |
31 | static int __ccw_device_should_retry(union scsw *scsw) | 29 | static void verify_done(struct ccw_device *cdev, int rc) |
32 | { | 30 | { |
33 | /* CC is only valid if start function bit is set. */ | 31 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
34 | if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1) | 32 | struct ccw_dev_id *id = &cdev->private->dev_id; |
35 | return 1; | 33 | int mpath = !cdev->private->flags.pgid_single; |
36 | /* No more activity. For sense and set PGID we stubbornly try again. */ | 34 | int pgroup = cdev->private->options.pgroup; |
37 | if (!scsw->cmd.actl) | 35 | |
38 | return 1; | 36 | if (rc) |
39 | return 0; | 37 | goto out; |
38 | /* Ensure consistent multipathing state at device and channel. */ | ||
39 | if (sch->config.mp != mpath) { | ||
40 | sch->config.mp = mpath; | ||
41 | rc = cio_commit_config(sch); | ||
42 | } | ||
43 | out: | ||
44 | CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " | ||
45 | "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath, | ||
46 | sch->vpm); | ||
47 | ccw_device_verify_done(cdev, rc); | ||
40 | } | 48 | } |
41 | 49 | ||
42 | /* | 50 | /* |
43 | * Start Sense Path Group ID helper function. Used in ccw_device_recog | 51 | * Create channel program to perform a NOOP. |
44 | * and ccw_device_sense_pgid. | ||
45 | */ | 52 | */ |
46 | static int | 53 | static void nop_build_cp(struct ccw_device *cdev) |
47 | __ccw_device_sense_pgid_start(struct ccw_device *cdev) | ||
48 | { | 54 | { |
49 | struct subchannel *sch; | 55 | struct ccw_request *req = &cdev->private->req; |
50 | struct ccw1 *ccw; | 56 | struct ccw1 *cp = cdev->private->iccws; |
51 | int ret; | 57 | |
52 | int i; | 58 | cp->cmd_code = CCW_CMD_NOOP; |
53 | 59 | cp->cda = 0; | |
54 | sch = to_subchannel(cdev->dev.parent); | 60 | cp->count = 0; |
55 | /* Return if we already checked on all paths. */ | 61 | cp->flags = CCW_FLAG_SLI; |
56 | if (cdev->private->imask == 0) | 62 | req->cp = cp; |
57 | return (sch->lpm == 0) ? -ENODEV : -EACCES; | ||
58 | i = 8 - ffs(cdev->private->imask); | ||
59 | |||
60 | /* Setup sense path group id channel program. */ | ||
61 | ccw = cdev->private->iccws; | ||
62 | ccw->cmd_code = CCW_CMD_SENSE_PGID; | ||
63 | ccw->count = sizeof (struct pgid); | ||
64 | ccw->flags = CCW_FLAG_SLI; | ||
65 | |||
66 | /* Reset device status. */ | ||
67 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
68 | /* Try on every path. */ | ||
69 | ret = -ENODEV; | ||
70 | while (cdev->private->imask != 0) { | ||
71 | /* Try every path multiple times. */ | ||
72 | ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); | ||
73 | if (cdev->private->iretry > 0) { | ||
74 | cdev->private->iretry--; | ||
75 | /* Reset internal retry indication. */ | ||
76 | cdev->private->flags.intretry = 0; | ||
77 | ret = cio_start (sch, cdev->private->iccws, | ||
78 | cdev->private->imask); | ||
79 | /* ret is 0, -EBUSY, -EACCES or -ENODEV */ | ||
80 | if (ret != -EACCES) | ||
81 | return ret; | ||
82 | CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel " | ||
83 | "0.%x.%04x, lpm %02X, became 'not " | ||
84 | "operational'\n", | ||
85 | cdev->private->dev_id.devno, | ||
86 | sch->schid.ssid, | ||
87 | sch->schid.sch_no, cdev->private->imask); | ||
88 | |||
89 | } | ||
90 | cdev->private->imask >>= 1; | ||
91 | cdev->private->iretry = 5; | ||
92 | i++; | ||
93 | } | ||
94 | |||
95 | return ret; | ||
96 | } | 63 | } |
97 | 64 | ||
98 | void | 65 | /* |
99 | ccw_device_sense_pgid_start(struct ccw_device *cdev) | 66 | * Perform NOOP on a single path. |
67 | */ | ||
68 | static void nop_do(struct ccw_device *cdev) | ||
100 | { | 69 | { |
101 | int ret; | 70 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
102 | 71 | struct ccw_request *req = &cdev->private->req; | |
103 | /* Set a timeout of 60s */ | 72 | |
104 | ccw_device_set_timeout(cdev, 60*HZ); | 73 | /* Adjust lpm. */ |
105 | 74 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); | |
106 | cdev->private->state = DEV_STATE_SENSE_PGID; | 75 | if (!req->lpm) |
107 | cdev->private->imask = 0x80; | 76 | goto out_nopath; |
108 | cdev->private->iretry = 5; | 77 | nop_build_cp(cdev); |
109 | memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); | 78 | ccw_request_start(cdev); |
110 | ret = __ccw_device_sense_pgid_start(cdev); | 79 | return; |
111 | if (ret && ret != -EBUSY) | 80 | |
112 | ccw_device_sense_pgid_done(cdev, ret); | 81 | out_nopath: |
82 | verify_done(cdev, sch->vpm ? 0 : -EACCES); | ||
113 | } | 83 | } |
114 | 84 | ||
115 | /* | 85 | /* |
116 | * Called from interrupt context to check if a valid answer | 86 | * Adjust NOOP I/O status. |
117 | * to Sense Path Group ID was received. | ||
118 | */ | 87 | */ |
119 | static int | 88 | static enum io_status nop_filter(struct ccw_device *cdev, void *data, |
120 | __ccw_device_check_sense_pgid(struct ccw_device *cdev) | 89 | struct irb *irb, enum io_status status) |
121 | { | 90 | { |
122 | struct subchannel *sch; | 91 | /* Only subchannel status might indicate a path error. */ |
123 | struct irb *irb; | 92 | if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) |
124 | int i; | 93 | return IO_DONE; |
125 | 94 | return status; | |
126 | sch = to_subchannel(cdev->dev.parent); | ||
127 | irb = &cdev->private->irb; | ||
128 | if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { | ||
129 | /* Retry Sense PGID if requested. */ | ||
130 | if (cdev->private->flags.intretry) { | ||
131 | cdev->private->flags.intretry = 0; | ||
132 | return -EAGAIN; | ||
133 | } | ||
134 | return -ETIME; | ||
135 | } | ||
136 | if (irb->esw.esw0.erw.cons && | ||
137 | (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { | ||
138 | /* | ||
139 | * If the device doesn't support the Sense Path Group ID | ||
140 | * command further retries wouldn't help ... | ||
141 | */ | ||
142 | return -EOPNOTSUPP; | ||
143 | } | ||
144 | if (irb->esw.esw0.erw.cons) { | ||
145 | CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " | ||
146 | "lpum %02X, cnt %02d, sns : " | ||
147 | "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", | ||
148 | cdev->private->dev_id.ssid, | ||
149 | cdev->private->dev_id.devno, | ||
150 | irb->esw.esw0.sublog.lpum, | ||
151 | irb->esw.esw0.erw.scnt, | ||
152 | irb->ecw[0], irb->ecw[1], | ||
153 | irb->ecw[2], irb->ecw[3], | ||
154 | irb->ecw[4], irb->ecw[5], | ||
155 | irb->ecw[6], irb->ecw[7]); | ||
156 | return -EAGAIN; | ||
157 | } | ||
158 | if (irb->scsw.cmd.cc == 3) { | ||
159 | u8 lpm; | ||
160 | |||
161 | lpm = to_io_private(sch)->orb.cmd.lpm; | ||
162 | CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," | ||
163 | " lpm %02X, became 'not operational'\n", | ||
164 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
165 | sch->schid.sch_no, lpm); | ||
166 | return -EACCES; | ||
167 | } | ||
168 | i = 8 - ffs(cdev->private->imask); | ||
169 | if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { | ||
170 | CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " | ||
171 | "is reserved by someone else\n", | ||
172 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
173 | sch->schid.sch_no); | ||
174 | return -EUSERS; | ||
175 | } | ||
176 | return 0; | ||
177 | } | 95 | } |
178 | 96 | ||
179 | /* | 97 | /* |
180 | * Got interrupt for Sense Path Group ID. | 98 | * Process NOOP request result for a single path. |
181 | */ | 99 | */ |
182 | void | 100 | static void nop_callback(struct ccw_device *cdev, void *data, int rc) |
183 | ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) | ||
184 | { | 101 | { |
185 | struct subchannel *sch; | 102 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
186 | struct irb *irb; | 103 | struct ccw_request *req = &cdev->private->req; |
187 | int ret; | 104 | |
188 | 105 | if (rc == 0) | |
189 | irb = (struct irb *) __LC_IRB; | 106 | sch->vpm |= req->lpm; |
190 | 107 | else if (rc != -EACCES) | |
191 | if (irb->scsw.cmd.stctl == | 108 | goto err; |
192 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | 109 | req->lpm >>= 1; |
193 | if (__ccw_device_should_retry(&irb->scsw)) { | 110 | nop_do(cdev); |
194 | ret = __ccw_device_sense_pgid_start(cdev); | 111 | return; |
195 | if (ret && ret != -EBUSY) | 112 | |
196 | ccw_device_sense_pgid_done(cdev, ret); | 113 | err: |
197 | } | 114 | verify_done(cdev, rc); |
198 | return; | ||
199 | } | ||
200 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | ||
201 | return; | ||
202 | sch = to_subchannel(cdev->dev.parent); | ||
203 | ret = __ccw_device_check_sense_pgid(cdev); | ||
204 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
205 | switch (ret) { | ||
206 | /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ | ||
207 | case -EOPNOTSUPP: /* Sense Path Group ID not supported */ | ||
208 | ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); | ||
209 | break; | ||
210 | case -ETIME: /* Sense path group id stopped by timeout. */ | ||
211 | ccw_device_sense_pgid_done(cdev, -ETIME); | ||
212 | break; | ||
213 | case -EACCES: /* channel is not operational. */ | ||
214 | sch->lpm &= ~cdev->private->imask; | ||
215 | /* Fall through. */ | ||
216 | case 0: /* Sense Path Group ID successful. */ | ||
217 | cdev->private->imask >>= 1; | ||
218 | cdev->private->iretry = 5; | ||
219 | /* Fall through. */ | ||
220 | case -EAGAIN: /* Try again. */ | ||
221 | ret = __ccw_device_sense_pgid_start(cdev); | ||
222 | if (ret != 0 && ret != -EBUSY) | ||
223 | ccw_device_sense_pgid_done(cdev, ret); | ||
224 | break; | ||
225 | case -EUSERS: /* device is reserved for someone else. */ | ||
226 | ccw_device_sense_pgid_done(cdev, -EUSERS); | ||
227 | break; | ||
228 | } | ||
229 | } | 115 | } |
230 | 116 | ||
231 | /* | 117 | /* |
232 | * Path Group ID helper function. | 118 | * Create channel program to perform SET PGID on a single path. |
233 | */ | 119 | */ |
234 | static int | 120 | static void spid_build_cp(struct ccw_device *cdev, u8 fn) |
235 | __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) | ||
236 | { | 121 | { |
237 | struct subchannel *sch; | 122 | struct ccw_request *req = &cdev->private->req; |
238 | struct ccw1 *ccw; | 123 | struct ccw1 *cp = cdev->private->iccws; |
239 | int ret; | 124 | int i = 8 - ffs(req->lpm); |
240 | 125 | struct pgid *pgid = &cdev->private->pgid[i]; | |
241 | sch = to_subchannel(cdev->dev.parent); | 126 | |
242 | 127 | pgid->inf.fc = fn; | |
243 | /* Setup sense path group id channel program. */ | 128 | cp->cmd_code = CCW_CMD_SET_PGID; |
244 | cdev->private->pgid[0].inf.fc = func; | 129 | cp->cda = (u32) (addr_t) pgid; |
245 | ccw = cdev->private->iccws; | 130 | cp->count = sizeof(*pgid); |
246 | if (cdev->private->flags.pgid_single) | 131 | cp->flags = CCW_FLAG_SLI; |
247 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; | 132 | req->cp = cp; |
248 | else | ||
249 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; | ||
250 | ccw->cmd_code = CCW_CMD_SET_PGID; | ||
251 | ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); | ||
252 | ccw->count = sizeof (struct pgid); | ||
253 | ccw->flags = CCW_FLAG_SLI; | ||
254 | |||
255 | /* Reset device status. */ | ||
256 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
257 | |||
258 | /* Try multiple times. */ | ||
259 | ret = -EACCES; | ||
260 | if (cdev->private->iretry > 0) { | ||
261 | cdev->private->iretry--; | ||
262 | /* Reset internal retry indication. */ | ||
263 | cdev->private->flags.intretry = 0; | ||
264 | ret = cio_start (sch, cdev->private->iccws, | ||
265 | cdev->private->imask); | ||
266 | /* We expect an interrupt in case of success or busy | ||
267 | * indication. */ | ||
268 | if ((ret == 0) || (ret == -EBUSY)) | ||
269 | return ret; | ||
270 | } | ||
271 | /* PGID command failed on this path. */ | ||
272 | CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel " | ||
273 | "0.%x.%04x, lpm %02X, became 'not operational'\n", | ||
274 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
275 | sch->schid.sch_no, cdev->private->imask); | ||
276 | return ret; | ||
277 | } | 133 | } |
278 | 134 | ||
279 | /* | 135 | /* |
280 | * Helper function to send a nop ccw down a path. | 136 | * Perform establish/resign SET PGID on a single path. |
281 | */ | 137 | */ |
282 | static int __ccw_device_do_nop(struct ccw_device *cdev) | 138 | static void spid_do(struct ccw_device *cdev) |
283 | { | 139 | { |
284 | struct subchannel *sch; | 140 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
285 | struct ccw1 *ccw; | 141 | struct ccw_request *req = &cdev->private->req; |
286 | int ret; | 142 | u8 fn; |
287 | 143 | ||
288 | sch = to_subchannel(cdev->dev.parent); | 144 | /* Adjust lpm if paths are not set in pam. */ |
289 | 145 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); | |
290 | /* Setup nop channel program. */ | 146 | if (!req->lpm) |
291 | ccw = cdev->private->iccws; | 147 | goto out_nopath; |
292 | ccw->cmd_code = CCW_CMD_NOOP; | 148 | /* Channel program setup. */ |
293 | ccw->cda = 0; | 149 | if (req->lpm & sch->opm) |
294 | ccw->count = 0; | 150 | fn = SPID_FUNC_ESTABLISH; |
295 | ccw->flags = CCW_FLAG_SLI; | 151 | else |
296 | 152 | fn = SPID_FUNC_RESIGN; | |
297 | /* Reset device status. */ | 153 | if (!cdev->private->flags.pgid_single) |
298 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | 154 | fn |= SPID_FUNC_MULTI_PATH; |
299 | 155 | spid_build_cp(cdev, fn); | |
300 | /* Try multiple times. */ | 156 | ccw_request_start(cdev); |
301 | ret = -EACCES; | 157 | return; |
302 | if (cdev->private->iretry > 0) { | 158 | |
303 | cdev->private->iretry--; | 159 | out_nopath: |
304 | /* Reset internal retry indication. */ | 160 | verify_done(cdev, sch->vpm ? 0 : -EACCES); |
305 | cdev->private->flags.intretry = 0; | ||
306 | ret = cio_start (sch, cdev->private->iccws, | ||
307 | cdev->private->imask); | ||
308 | /* We expect an interrupt in case of success or busy | ||
309 | * indication. */ | ||
310 | if ((ret == 0) || (ret == -EBUSY)) | ||
311 | return ret; | ||
312 | } | ||
313 | /* nop command failed on this path. */ | ||
314 | CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel " | ||
315 | "0.%x.%04x, lpm %02X, became 'not operational'\n", | ||
316 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
317 | sch->schid.sch_no, cdev->private->imask); | ||
318 | return ret; | ||
319 | } | 161 | } |
320 | 162 | ||
163 | static void verify_start(struct ccw_device *cdev); | ||
321 | 164 | ||
322 | /* | 165 | /* |
323 | * Called from interrupt context to check if a valid answer | 166 | * Process SET PGID request result for a single path. |
324 | * to Set Path Group ID was received. | ||
325 | */ | 167 | */ |
326 | static int | 168 | static void spid_callback(struct ccw_device *cdev, void *data, int rc) |
327 | __ccw_device_check_pgid(struct ccw_device *cdev) | ||
328 | { | 169 | { |
329 | struct subchannel *sch; | 170 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
330 | struct irb *irb; | 171 | struct ccw_request *req = &cdev->private->req; |
331 | 172 | ||
332 | sch = to_subchannel(cdev->dev.parent); | 173 | switch (rc) { |
333 | irb = &cdev->private->irb; | 174 | case 0: |
334 | if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { | 175 | sch->vpm |= req->lpm & sch->opm; |
335 | /* Retry Set PGID if requested. */ | 176 | break; |
336 | if (cdev->private->flags.intretry) { | 177 | case -EACCES: |
337 | cdev->private->flags.intretry = 0; | 178 | break; |
338 | return -EAGAIN; | 179 | case -EOPNOTSUPP: |
180 | if (!cdev->private->flags.pgid_single) { | ||
181 | /* Try without multipathing. */ | ||
182 | cdev->private->flags.pgid_single = 1; | ||
183 | goto out_restart; | ||
339 | } | 184 | } |
340 | return -ETIME; | 185 | /* Try without pathgrouping. */ |
341 | } | 186 | cdev->private->options.pgroup = 0; |
342 | if (irb->esw.esw0.erw.cons) { | 187 | goto out_restart; |
343 | if (irb->ecw[0] & SNS0_CMD_REJECT) | 188 | default: |
344 | return -EOPNOTSUPP; | 189 | goto err; |
345 | /* Hmm, whatever happened, try again. */ | ||
346 | CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " | ||
347 | "cnt %02d, " | ||
348 | "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", | ||
349 | cdev->private->dev_id.ssid, | ||
350 | cdev->private->dev_id.devno, | ||
351 | irb->esw.esw0.erw.scnt, | ||
352 | irb->ecw[0], irb->ecw[1], | ||
353 | irb->ecw[2], irb->ecw[3], | ||
354 | irb->ecw[4], irb->ecw[5], | ||
355 | irb->ecw[6], irb->ecw[7]); | ||
356 | return -EAGAIN; | ||
357 | } | ||
358 | if (irb->scsw.cmd.cc == 3) { | ||
359 | CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x," | ||
360 | " lpm %02X, became 'not operational'\n", | ||
361 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
362 | sch->schid.sch_no, cdev->private->imask); | ||
363 | return -EACCES; | ||
364 | } | 190 | } |
365 | return 0; | 191 | req->lpm >>= 1; |
192 | spid_do(cdev); | ||
193 | return; | ||
194 | |||
195 | out_restart: | ||
196 | verify_start(cdev); | ||
197 | return; | ||
198 | err: | ||
199 | verify_done(cdev, rc); | ||
200 | } | ||
201 | |||
202 | static int pgid_cmp(struct pgid *p1, struct pgid *p2) | ||
203 | { | ||
204 | return memcmp((char *) p1 + 1, (char *) p2 + 1, | ||
205 | sizeof(struct pgid) - 1); | ||
366 | } | 206 | } |
367 | 207 | ||
368 | /* | 208 | /* |
369 | * Called from interrupt context to check the path status after a nop has | 209 | * Determine pathgroup state from PGID data. |
370 | * been send. | ||
371 | */ | 210 | */ |
372 | static int __ccw_device_check_nop(struct ccw_device *cdev) | 211 | static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, |
212 | int *mismatch, int *reserved, int *reset) | ||
373 | { | 213 | { |
374 | struct subchannel *sch; | 214 | struct pgid *pgid = &cdev->private->pgid[0]; |
375 | struct irb *irb; | 215 | struct pgid *first = NULL; |
376 | 216 | int lpm; | |
377 | sch = to_subchannel(cdev->dev.parent); | 217 | int i; |
378 | irb = &cdev->private->irb; | 218 | |
379 | if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { | 219 | *mismatch = 0; |
380 | /* Retry NOP if requested. */ | 220 | *reserved = 0; |
381 | if (cdev->private->flags.intretry) { | 221 | *reset = 0; |
382 | cdev->private->flags.intretry = 0; | 222 | for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { |
383 | return -EAGAIN; | 223 | if ((cdev->private->pgid_valid_mask & lpm) == 0) |
224 | continue; | ||
225 | if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) | ||
226 | *reserved = 1; | ||
227 | if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { | ||
228 | /* A PGID was reset. */ | ||
229 | *reset = 1; | ||
230 | continue; | ||
384 | } | 231 | } |
385 | return -ETIME; | 232 | if (!first) { |
386 | } | 233 | first = pgid; |
387 | if (irb->scsw.cmd.cc == 3) { | 234 | continue; |
388 | CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x," | 235 | } |
389 | " lpm %02X, became 'not operational'\n", | 236 | if (pgid_cmp(pgid, first) != 0) |
390 | cdev->private->dev_id.devno, sch->schid.ssid, | 237 | *mismatch = 1; |
391 | sch->schid.sch_no, cdev->private->imask); | ||
392 | return -EACCES; | ||
393 | } | 238 | } |
394 | return 0; | 239 | if (!first) |
240 | first = &channel_subsystems[0]->global_pgid; | ||
241 | *p = first; | ||
395 | } | 242 | } |
396 | 243 | ||
397 | static void | 244 | static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) |
398 | __ccw_device_verify_start(struct ccw_device *cdev) | ||
399 | { | 245 | { |
400 | struct subchannel *sch; | 246 | int i; |
401 | __u8 func; | 247 | |
402 | int ret; | 248 | for (i = 0; i < 8; i++) |
403 | 249 | memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid)); | |
404 | sch = to_subchannel(cdev->dev.parent); | 250 | } |
405 | /* Repeat for all paths. */ | 251 | |
406 | for (; cdev->private->imask; cdev->private->imask >>= 1, | 252 | /* |
407 | cdev->private->iretry = 5) { | 253 | * Process SENSE PGID data and report result. |
408 | if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) | 254 | */ |
409 | /* Path not available, try next. */ | 255 | static void snid_done(struct ccw_device *cdev, int rc) |
410 | continue; | 256 | { |
411 | if (cdev->private->options.pgroup) { | 257 | struct ccw_dev_id *id = &cdev->private->dev_id; |
412 | if (sch->opm & cdev->private->imask) | 258 | struct pgid *pgid; |
413 | func = SPID_FUNC_ESTABLISH; | 259 | int mismatch = 0; |
414 | else | 260 | int reserved = 0; |
415 | func = SPID_FUNC_RESIGN; | 261 | int reset = 0; |
416 | ret = __ccw_device_do_pgid(cdev, func); | 262 | |
417 | } else | 263 | if (rc) |
418 | ret = __ccw_device_do_nop(cdev); | 264 | goto out; |
419 | /* We expect an interrupt in case of success or busy | 265 | pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); |
420 | * indication. */ | 266 | if (!mismatch) { |
421 | if (ret == 0 || ret == -EBUSY) | 267 | pgid_fill(cdev, pgid); |
422 | return; | 268 | cdev->private->flags.pgid_rdy = 1; |
423 | /* Permanent path failure, try next. */ | ||
424 | } | 269 | } |
425 | /* Done with all paths. */ | 270 | if (reserved) |
426 | ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -EACCES); | 271 | rc = -EUSERS; |
272 | out: | ||
273 | CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x mism=%d " | ||
274 | "rsvd=%d reset=%d\n", id->ssid, id->devno, rc, | ||
275 | cdev->private->pgid_valid_mask, mismatch, reserved, | ||
276 | reset); | ||
277 | ccw_device_sense_pgid_done(cdev, rc); | ||
427 | } | 278 | } |
428 | 279 | ||
429 | /* | 280 | /* |
430 | * Got interrupt for Set Path Group ID. | 281 | * Create channel program to perform a SENSE PGID on a single path. |
431 | */ | 282 | */ |
432 | void | 283 | static void snid_build_cp(struct ccw_device *cdev) |
433 | ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) | ||
434 | { | 284 | { |
435 | struct subchannel *sch; | 285 | struct ccw_request *req = &cdev->private->req; |
436 | struct irb *irb; | 286 | struct ccw1 *cp = cdev->private->iccws; |
437 | int ret; | 287 | int i = 8 - ffs(req->lpm); |
288 | |||
289 | /* Channel program setup. */ | ||
290 | cp->cmd_code = CCW_CMD_SENSE_PGID; | ||
291 | cp->cda = (u32) (addr_t) &cdev->private->pgid[i]; | ||
292 | cp->count = sizeof(struct pgid); | ||
293 | cp->flags = CCW_FLAG_SLI; | ||
294 | req->cp = cp; | ||
295 | } | ||
438 | 296 | ||
439 | irb = (struct irb *) __LC_IRB; | 297 | /* |
298 | * Perform SENSE PGID on a single path. | ||
299 | */ | ||
300 | static void snid_do(struct ccw_device *cdev) | ||
301 | { | ||
302 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
303 | struct ccw_request *req = &cdev->private->req; | ||
304 | |||
305 | /* Adjust lpm if paths are not set in pam. */ | ||
306 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); | ||
307 | if (!req->lpm) | ||
308 | goto out_nopath; | ||
309 | snid_build_cp(cdev); | ||
310 | ccw_request_start(cdev); | ||
311 | return; | ||
312 | |||
313 | out_nopath: | ||
314 | snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); | ||
315 | } | ||
440 | 316 | ||
441 | if (irb->scsw.cmd.stctl == | 317 | /* |
442 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | 318 | * Process SENSE PGID request result for single path. |
443 | if (__ccw_device_should_retry(&irb->scsw)) | 319 | */ |
444 | __ccw_device_verify_start(cdev); | 320 | static void snid_callback(struct ccw_device *cdev, void *data, int rc) |
445 | return; | 321 | { |
446 | } | 322 | struct ccw_request *req = &cdev->private->req; |
447 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | 323 | |
448 | return; | 324 | if (rc == 0) |
449 | sch = to_subchannel(cdev->dev.parent); | 325 | cdev->private->pgid_valid_mask |= req->lpm; |
450 | if (cdev->private->options.pgroup) | 326 | else if (rc != -EACCES) |
451 | ret = __ccw_device_check_pgid(cdev); | 327 | goto err; |
452 | else | 328 | req->lpm >>= 1; |
453 | ret = __ccw_device_check_nop(cdev); | 329 | snid_do(cdev); |
454 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | 330 | return; |
331 | |||
332 | err: | ||
333 | snid_done(cdev, rc); | ||
334 | } | ||
455 | 335 | ||
456 | switch (ret) { | 336 | /** |
457 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | 337 | * ccw_device_sense_pgid_start - perform SENSE PGID |
458 | case 0: | 338 | * @cdev: ccw device |
459 | /* Path verification ccw finished successfully, update lpm. */ | 339 | * |
460 | sch->vpm |= sch->opm & cdev->private->imask; | 340 | * Execute a SENSE PGID channel program on each path to @cdev to update its |
461 | /* Go on with next path. */ | 341 | * PGID information. When finished, call ccw_device_sense_id_done with a |
462 | cdev->private->imask >>= 1; | 342 | * return code specifying the result. |
463 | cdev->private->iretry = 5; | 343 | */ |
464 | __ccw_device_verify_start(cdev); | 344 | void ccw_device_sense_pgid_start(struct ccw_device *cdev) |
465 | break; | 345 | { |
466 | case -EOPNOTSUPP: | 346 | struct ccw_request *req = &cdev->private->req; |
467 | /* | 347 | |
468 | * One of those strange devices which claim to be able | 348 | CIO_TRACE_EVENT(4, "snid"); |
469 | * to do multipathing but not for Set Path Group ID. | 349 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); |
470 | */ | 350 | /* Initialize PGID data. */ |
471 | if (cdev->private->flags.pgid_single) | 351 | memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); |
472 | cdev->private->options.pgroup = 0; | 352 | cdev->private->flags.pgid_rdy = 0; |
473 | else | 353 | cdev->private->pgid_valid_mask = 0; |
474 | cdev->private->flags.pgid_single = 1; | 354 | /* Initialize request data. */ |
475 | /* Retry */ | 355 | memset(req, 0, sizeof(*req)); |
476 | sch->vpm = 0; | 356 | req->timeout = PGID_TIMEOUT; |
477 | cdev->private->imask = 0x80; | 357 | req->maxretries = PGID_RETRIES; |
478 | cdev->private->iretry = 5; | 358 | req->callback = snid_callback; |
479 | /* fall through. */ | 359 | req->lpm = 0x80; |
480 | case -EAGAIN: /* Try again. */ | 360 | snid_do(cdev); |
481 | __ccw_device_verify_start(cdev); | ||
482 | break; | ||
483 | case -ETIME: /* Set path group id stopped by timeout. */ | ||
484 | ccw_device_verify_done(cdev, -ETIME); | ||
485 | break; | ||
486 | case -EACCES: /* channel is not operational. */ | ||
487 | cdev->private->imask >>= 1; | ||
488 | cdev->private->iretry = 5; | ||
489 | __ccw_device_verify_start(cdev); | ||
490 | break; | ||
491 | } | ||
492 | } | 361 | } |
493 | 362 | ||
494 | void | 363 | /* |
495 | ccw_device_verify_start(struct ccw_device *cdev) | 364 | * Perform path verification. |
365 | */ | ||
366 | static void verify_start(struct ccw_device *cdev) | ||
496 | { | 367 | { |
497 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 368 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
369 | struct ccw_request *req = &cdev->private->req; | ||
498 | 370 | ||
499 | cdev->private->flags.pgid_single = 0; | ||
500 | cdev->private->imask = 0x80; | ||
501 | cdev->private->iretry = 5; | ||
502 | |||
503 | /* Start with empty vpm. */ | ||
504 | sch->vpm = 0; | 371 | sch->vpm = 0; |
505 | 372 | /* Initialize request data. */ | |
506 | /* Get current pam. */ | 373 | memset(req, 0, sizeof(*req)); |
507 | if (cio_update_schib(sch)) { | 374 | req->timeout = PGID_TIMEOUT; |
508 | ccw_device_verify_done(cdev, -ENODEV); | 375 | req->maxretries = PGID_RETRIES; |
509 | return; | 376 | req->lpm = 0x80; |
377 | if (cdev->private->options.pgroup) { | ||
378 | req->callback = spid_callback; | ||
379 | spid_do(cdev); | ||
380 | } else { | ||
381 | req->filter = nop_filter; | ||
382 | req->callback = nop_callback; | ||
383 | nop_do(cdev); | ||
510 | } | 384 | } |
511 | /* After 60s path verification is considered to have failed. */ | ||
512 | ccw_device_set_timeout(cdev, 60*HZ); | ||
513 | __ccw_device_verify_start(cdev); | ||
514 | } | 385 | } |
515 | 386 | ||
516 | static void | 387 | /** |
517 | __ccw_device_disband_start(struct ccw_device *cdev) | 388 | * ccw_device_verify_start - perform path verification |
389 | * @cdev: ccw device | ||
390 | * | ||
391 | * Perform an I/O on each available channel path to @cdev to determine which | ||
392 | * paths are operational. The resulting path mask is stored in sch->vpm. | ||
393 | * If device options specify pathgrouping, establish a pathgroup for the | ||
394 | * operational paths. When finished, call ccw_device_verify_done with a | ||
395 | * return code specifying the result. | ||
396 | */ | ||
397 | void ccw_device_verify_start(struct ccw_device *cdev) | ||
518 | { | 398 | { |
519 | struct subchannel *sch; | 399 | CIO_TRACE_EVENT(4, "vrfy"); |
520 | int ret; | 400 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); |
521 | 401 | if (!cdev->private->flags.pgid_rdy) { | |
522 | sch = to_subchannel(cdev->dev.parent); | 402 | /* No pathgrouping possible. */ |
523 | while (cdev->private->imask != 0) { | 403 | cdev->private->options.pgroup = 0; |
524 | if (sch->lpm & cdev->private->imask) { | 404 | cdev->private->flags.pgid_single = 1; |
525 | ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); | 405 | } else |
526 | if (ret == 0) | 406 | cdev->private->flags.pgid_single = 0; |
527 | return; | 407 | cdev->private->flags.doverify = 0; |
528 | } | 408 | verify_start(cdev); |
529 | cdev->private->iretry = 5; | ||
530 | cdev->private->imask >>= 1; | ||
531 | } | ||
532 | ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); | ||
533 | } | 409 | } |
534 | 410 | ||
535 | /* | 411 | /* |
536 | * Got interrupt for Unset Path Group ID. | 412 | * Process disband SET PGID request result. |
537 | */ | 413 | */ |
538 | void | 414 | static void disband_callback(struct ccw_device *cdev, void *data, int rc) |
539 | ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) | ||
540 | { | 415 | { |
541 | struct subchannel *sch; | 416 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
542 | struct irb *irb; | 417 | struct ccw_dev_id *id = &cdev->private->dev_id; |
543 | int ret; | 418 | |
544 | 419 | if (rc) | |
545 | irb = (struct irb *) __LC_IRB; | 420 | goto out; |
546 | 421 | /* Ensure consistent multipathing state at device and channel. */ | |
547 | if (irb->scsw.cmd.stctl == | 422 | cdev->private->flags.pgid_single = 1; |
548 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | 423 | if (sch->config.mp) { |
549 | if (__ccw_device_should_retry(&irb->scsw)) | 424 | sch->config.mp = 0; |
550 | __ccw_device_disband_start(cdev); | 425 | rc = cio_commit_config(sch); |
551 | return; | ||
552 | } | ||
553 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | ||
554 | return; | ||
555 | sch = to_subchannel(cdev->dev.parent); | ||
556 | ret = __ccw_device_check_pgid(cdev); | ||
557 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
558 | switch (ret) { | ||
559 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | ||
560 | case 0: /* disband successful. */ | ||
561 | ccw_device_disband_done(cdev, ret); | ||
562 | break; | ||
563 | case -EOPNOTSUPP: | ||
564 | /* | ||
565 | * One of those strange devices which claim to be able | ||
566 | * to do multipathing but not for Unset Path Group ID. | ||
567 | */ | ||
568 | cdev->private->flags.pgid_single = 1; | ||
569 | /* fall through. */ | ||
570 | case -EAGAIN: /* Try again. */ | ||
571 | __ccw_device_disband_start(cdev); | ||
572 | break; | ||
573 | case -ETIME: /* Set path group id stopped by timeout. */ | ||
574 | ccw_device_disband_done(cdev, -ETIME); | ||
575 | break; | ||
576 | case -EACCES: /* channel is not operational. */ | ||
577 | cdev->private->imask >>= 1; | ||
578 | cdev->private->iretry = 5; | ||
579 | __ccw_device_disband_start(cdev); | ||
580 | break; | ||
581 | } | 426 | } |
427 | out: | ||
428 | CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno, | ||
429 | rc); | ||
430 | ccw_device_disband_done(cdev, rc); | ||
582 | } | 431 | } |
583 | 432 | ||
584 | void | 433 | /** |
585 | ccw_device_disband_start(struct ccw_device *cdev) | 434 | * ccw_device_disband_start - disband pathgroup |
435 | * @cdev: ccw device | ||
436 | * | ||
437 | * Execute a SET PGID channel program on @cdev to disband a previously | ||
438 | * established pathgroup. When finished, call ccw_device_disband_done with | ||
439 | * a return code specifying the result. | ||
440 | */ | ||
441 | void ccw_device_disband_start(struct ccw_device *cdev) | ||
586 | { | 442 | { |
587 | /* After 60s disbanding is considered to have failed. */ | 443 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
588 | ccw_device_set_timeout(cdev, 60*HZ); | 444 | struct ccw_request *req = &cdev->private->req; |
589 | 445 | u8 fn; | |
590 | cdev->private->flags.pgid_single = 0; | 446 | |
591 | cdev->private->iretry = 5; | 447 | CIO_TRACE_EVENT(4, "disb"); |
592 | cdev->private->imask = 0x80; | 448 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); |
593 | __ccw_device_disband_start(cdev); | 449 | /* Request setup. */ |
450 | memset(req, 0, sizeof(*req)); | ||
451 | req->timeout = PGID_TIMEOUT; | ||
452 | req->maxretries = PGID_RETRIES; | ||
453 | req->lpm = sch->schib.pmcw.pam & sch->opm; | ||
454 | req->callback = disband_callback; | ||
455 | fn = SPID_FUNC_DISBAND; | ||
456 | if (!cdev->private->flags.pgid_single) | ||
457 | fn |= SPID_FUNC_MULTI_PATH; | ||
458 | spid_build_cp(cdev, fn); | ||
459 | ccw_request_start(cdev); | ||
594 | } | 460 | } |
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index f9ff7683e242..78b5ad980cf3 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h | |||
@@ -149,8 +149,8 @@ struct ccw_device_private { | |||
149 | struct ccw_dev_id dev_id; /* device id */ | 149 | struct ccw_dev_id dev_id; /* device id */ |
150 | struct subchannel_id schid; /* subchannel number */ | 150 | struct subchannel_id schid; /* subchannel number */ |
151 | struct ccw_request req; /* internal I/O request */ | 151 | struct ccw_request req; /* internal I/O request */ |
152 | u8 imask; /* lpm mask for SNID/SID/SPGID */ | 152 | int iretry; |
153 | int iretry; /* retry counter SNID/SID/SPGID */ | 153 | u8 pgid_valid_mask; /* mask of valid PGIDs */ |
154 | struct { | 154 | struct { |
155 | unsigned int fast:1; /* post with "channel end" */ | 155 | unsigned int fast:1; /* post with "channel end" */ |
156 | unsigned int repall:1; /* report every interrupt status */ | 156 | unsigned int repall:1; /* report every interrupt status */ |
@@ -167,6 +167,7 @@ struct ccw_device_private { | |||
167 | unsigned int fake_irb:1; /* deliver faked irb */ | 167 | unsigned int fake_irb:1; /* deliver faked irb */ |
168 | unsigned int intretry:1; /* retry internal operation */ | 168 | unsigned int intretry:1; /* retry internal operation */ |
169 | unsigned int resuming:1; /* recognition while resume */ | 169 | unsigned int resuming:1; /* recognition while resume */ |
170 | unsigned int pgid_rdy:1; /* pgids are ready */ | ||
170 | } __attribute__((packed)) flags; | 171 | } __attribute__((packed)) flags; |
171 | unsigned long intparm; /* user interruption parameter */ | 172 | unsigned long intparm; /* user interruption parameter */ |
172 | struct qdio_irq *qdio_data; | 173 | struct qdio_irq *qdio_data; |