diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2009-12-07 06:51:30 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:31 -0500 |
commit | 454e1fa1ebae7cff707b2e3f12b775c263c8408b (patch) | |
tree | eab290fcacd6faaf7e01e3a637cb9678bb367c66 | |
parent | 4257aaecffab77bad43e12057f56a5590b360f9f (diff) |
[S390] cio: split PGID settings and status
Split setting (driver wants feature enabled) and status (feature
setup was successful) for PGID related ccw device features so that
setup errors can be detected. Previously, incorrectly handled setup
errors could in rare cases lead to erratic I/O behavior and
permanently unusuable devices.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/ccwdev.h | 4 | ||||
-rw-r--r-- | drivers/s390/block/dasd.c | 7 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 12 | ||||
-rw-r--r-- | drivers/s390/char/tape_core.c | 3 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/device_ops.c | 27 | ||||
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 33 | ||||
-rw-r--r-- | drivers/s390/cio/io_sch.h | 4 |
8 files changed, 68 insertions, 24 deletions
diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index 2a5419551176..f4bd346a52d3 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h | |||
@@ -142,6 +142,8 @@ struct ccw1; | |||
142 | extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long); | 142 | extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long); |
143 | extern int ccw_device_set_options(struct ccw_device *, unsigned long); | 143 | extern int ccw_device_set_options(struct ccw_device *, unsigned long); |
144 | extern void ccw_device_clear_options(struct ccw_device *, unsigned long); | 144 | extern void ccw_device_clear_options(struct ccw_device *, unsigned long); |
145 | int ccw_device_is_pathgroup(struct ccw_device *cdev); | ||
146 | int ccw_device_is_multipath(struct ccw_device *cdev); | ||
145 | 147 | ||
146 | /* Allow for i/o completion notification after primary interrupt status. */ | 148 | /* Allow for i/o completion notification after primary interrupt status. */ |
147 | #define CCWDEV_EARLY_NOTIFICATION 0x0001 | 149 | #define CCWDEV_EARLY_NOTIFICATION 0x0001 |
@@ -151,6 +153,8 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long); | |||
151 | #define CCWDEV_DO_PATHGROUP 0x0004 | 153 | #define CCWDEV_DO_PATHGROUP 0x0004 |
152 | /* Allow forced onlining of boxed devices. */ | 154 | /* Allow forced onlining of boxed devices. */ |
153 | #define CCWDEV_ALLOW_FORCE 0x0008 | 155 | #define CCWDEV_ALLOW_FORCE 0x0008 |
156 | /* Try to use multipath mode. */ | ||
157 | #define CCWDEV_DO_MULTIPATH 0x0010 | ||
154 | 158 | ||
155 | extern int ccw_device_start(struct ccw_device *, struct ccw1 *, | 159 | extern int ccw_device_start(struct ccw_device *, struct ccw1 *, |
156 | unsigned long, __u8, unsigned long); | 160 | unsigned long, __u8, unsigned long); |
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index aaccc8ecfa8f..58ffbd1d04a1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -2208,13 +2208,6 @@ int dasd_generic_probe(struct ccw_device *cdev, | |||
2208 | { | 2208 | { |
2209 | int ret; | 2209 | int ret; |
2210 | 2210 | ||
2211 | ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); | ||
2212 | if (ret) { | ||
2213 | DBF_EVENT(DBF_WARNING, | ||
2214 | "dasd_generic_probe: could not set ccw-device options " | ||
2215 | "for %s\n", dev_name(&cdev->dev)); | ||
2216 | return ret; | ||
2217 | } | ||
2218 | ret = dasd_add_sysfs_files(cdev); | 2211 | ret = dasd_add_sysfs_files(cdev); |
2219 | if (ret) { | 2212 | if (ret) { |
2220 | DBF_EVENT(DBF_WARNING, | 2213 | DBF_EVENT(DBF_WARNING, |
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 417b97cd3f94..a8ec0731609e 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
@@ -86,7 +86,8 @@ dasd_eckd_probe (struct ccw_device *cdev) | |||
86 | int ret; | 86 | int ret; |
87 | 87 | ||
88 | /* set ECKD specific ccw-device options */ | 88 | /* set ECKD specific ccw-device options */ |
89 | ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE); | 89 | ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE | |
90 | CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH); | ||
90 | if (ret) { | 91 | if (ret) { |
91 | DBF_EVENT(DBF_WARNING, | 92 | DBF_EVENT(DBF_WARNING, |
92 | "dasd_eckd_probe: could not set ccw-device options " | 93 | "dasd_eckd_probe: could not set ccw-device options " |
@@ -1090,6 +1091,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device) | |||
1090 | struct dasd_block *block; | 1091 | struct dasd_block *block; |
1091 | int is_known, rc; | 1092 | int is_known, rc; |
1092 | 1093 | ||
1094 | if (!ccw_device_is_pathgroup(device->cdev)) { | ||
1095 | dev_warn(&device->cdev->dev, | ||
1096 | "A channel path group could not be established\n"); | ||
1097 | return -EIO; | ||
1098 | } | ||
1099 | if (!ccw_device_is_multipath(device->cdev)) { | ||
1100 | dev_info(&device->cdev->dev, | ||
1101 | "The DASD is not operating in multipath mode\n"); | ||
1102 | } | ||
1093 | private = (struct dasd_eckd_private *) device->private; | 1103 | private = (struct dasd_eckd_private *) device->private; |
1094 | if (!private) { | 1104 | if (!private) { |
1095 | private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); | 1105 | private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); |
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 5cd31e071647..27503a778fcb 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c | |||
@@ -579,7 +579,8 @@ tape_generic_probe(struct ccw_device *cdev) | |||
579 | device = tape_alloc_device(); | 579 | device = tape_alloc_device(); |
580 | if (IS_ERR(device)) | 580 | if (IS_ERR(device)) |
581 | return -ENODEV; | 581 | return -ENODEV; |
582 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); | 582 | ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | |
583 | CCWDEV_DO_MULTIPATH); | ||
583 | ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); | 584 | ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); |
584 | if (ret) { | 585 | if (ret) { |
585 | tape_put_device(device); | 586 | tape_put_device(device); |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 349d8c52c0d0..d6e315dc0f98 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -601,7 +601,7 @@ ccw_device_offline(struct ccw_device *cdev) | |||
601 | if (cdev->private->state != DEV_STATE_ONLINE) | 601 | if (cdev->private->state != DEV_STATE_ONLINE) |
602 | return -EINVAL; | 602 | return -EINVAL; |
603 | /* Are we doing path grouping? */ | 603 | /* Are we doing path grouping? */ |
604 | if (!cdev->private->options.pgroup) { | 604 | if (!cdev->private->flags.pgroup) { |
605 | /* No, set state offline immediately. */ | 605 | /* No, set state offline immediately. */ |
606 | ccw_device_done(cdev, DEV_STATE_OFFLINE); | 606 | ccw_device_done(cdev, DEV_STATE_OFFLINE); |
607 | return 0; | 607 | return 0; |
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 5ab90ec42318..d4be16acebe4 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c | |||
@@ -46,6 +46,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) | |||
46 | cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; | 46 | cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; |
47 | cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; | 47 | cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; |
48 | cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; | 48 | cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; |
49 | cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0; | ||
49 | return 0; | 50 | return 0; |
50 | } | 51 | } |
51 | 52 | ||
@@ -74,6 +75,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) | |||
74 | cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; | 75 | cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; |
75 | cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; | 76 | cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; |
76 | cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; | 77 | cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; |
78 | cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0; | ||
77 | return 0; | 79 | return 0; |
78 | } | 80 | } |
79 | 81 | ||
@@ -90,9 +92,34 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) | |||
90 | cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; | 92 | cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; |
91 | cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; | 93 | cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; |
92 | cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; | 94 | cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; |
95 | cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0; | ||
93 | } | 96 | } |
94 | 97 | ||
95 | /** | 98 | /** |
99 | * ccw_device_is_pathgroup - determine if paths to this device are grouped | ||
100 | * @cdev: ccw device | ||
101 | * | ||
102 | * Return non-zero if there is a path group, zero otherwise. | ||
103 | */ | ||
104 | int ccw_device_is_pathgroup(struct ccw_device *cdev) | ||
105 | { | ||
106 | return cdev->private->flags.pgroup; | ||
107 | } | ||
108 | EXPORT_SYMBOL(ccw_device_is_pathgroup); | ||
109 | |||
110 | /** | ||
111 | * ccw_device_is_multipath - determine if device is operating in multipath mode | ||
112 | * @cdev: ccw device | ||
113 | * | ||
114 | * Return non-zero if device is operating in multipath mode, zero otherwise. | ||
115 | */ | ||
116 | int ccw_device_is_multipath(struct ccw_device *cdev) | ||
117 | { | ||
118 | return cdev->private->flags.mpath; | ||
119 | } | ||
120 | EXPORT_SYMBOL(ccw_device_is_multipath); | ||
121 | |||
122 | /** | ||
96 | * ccw_device_clear() - terminate I/O request processing | 123 | * ccw_device_clear() - terminate I/O request processing |
97 | * @cdev: target ccw device | 124 | * @cdev: target ccw device |
98 | * @intparm: interruption parameter; value is only used if no I/O is | 125 | * @intparm: interruption parameter; value is only used if no I/O is |
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index ce493144b054..3323042ba755 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c | |||
@@ -30,8 +30,8 @@ static void verify_done(struct ccw_device *cdev, int rc) | |||
30 | { | 30 | { |
31 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 31 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
32 | struct ccw_dev_id *id = &cdev->private->dev_id; | 32 | struct ccw_dev_id *id = &cdev->private->dev_id; |
33 | int mpath = !cdev->private->flags.pgid_single; | 33 | int mpath = cdev->private->flags.mpath; |
34 | int pgroup = cdev->private->options.pgroup; | 34 | int pgroup = cdev->private->flags.pgroup; |
35 | 35 | ||
36 | if (rc) | 36 | if (rc) |
37 | goto out; | 37 | goto out; |
@@ -150,7 +150,7 @@ static void spid_do(struct ccw_device *cdev) | |||
150 | fn = SPID_FUNC_ESTABLISH; | 150 | fn = SPID_FUNC_ESTABLISH; |
151 | else | 151 | else |
152 | fn = SPID_FUNC_RESIGN; | 152 | fn = SPID_FUNC_RESIGN; |
153 | if (!cdev->private->flags.pgid_single) | 153 | if (cdev->private->flags.mpath) |
154 | fn |= SPID_FUNC_MULTI_PATH; | 154 | fn |= SPID_FUNC_MULTI_PATH; |
155 | spid_build_cp(cdev, fn); | 155 | spid_build_cp(cdev, fn); |
156 | ccw_request_start(cdev); | 156 | ccw_request_start(cdev); |
@@ -177,13 +177,13 @@ static void spid_callback(struct ccw_device *cdev, void *data, int rc) | |||
177 | case -EACCES: | 177 | case -EACCES: |
178 | break; | 178 | break; |
179 | case -EOPNOTSUPP: | 179 | case -EOPNOTSUPP: |
180 | if (!cdev->private->flags.pgid_single) { | 180 | if (cdev->private->flags.mpath) { |
181 | /* Try without multipathing. */ | 181 | /* Try without multipathing. */ |
182 | cdev->private->flags.pgid_single = 1; | 182 | cdev->private->flags.mpath = 0; |
183 | goto out_restart; | 183 | goto out_restart; |
184 | } | 184 | } |
185 | /* Try without pathgrouping. */ | 185 | /* Try without pathgrouping. */ |
186 | cdev->private->options.pgroup = 0; | 186 | cdev->private->flags.pgroup = 0; |
187 | goto out_restart; | 187 | goto out_restart; |
188 | default: | 188 | default: |
189 | goto err; | 189 | goto err; |
@@ -374,7 +374,7 @@ static void verify_start(struct ccw_device *cdev) | |||
374 | req->timeout = PGID_TIMEOUT; | 374 | req->timeout = PGID_TIMEOUT; |
375 | req->maxretries = PGID_RETRIES; | 375 | req->maxretries = PGID_RETRIES; |
376 | req->lpm = 0x80; | 376 | req->lpm = 0x80; |
377 | if (cdev->private->options.pgroup) { | 377 | if (cdev->private->flags.pgroup) { |
378 | req->callback = spid_callback; | 378 | req->callback = spid_callback; |
379 | spid_do(cdev); | 379 | spid_do(cdev); |
380 | } else { | 380 | } else { |
@@ -400,10 +400,17 @@ void ccw_device_verify_start(struct ccw_device *cdev) | |||
400 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); | 400 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); |
401 | if (!cdev->private->flags.pgid_rdy) { | 401 | if (!cdev->private->flags.pgid_rdy) { |
402 | /* No pathgrouping possible. */ | 402 | /* No pathgrouping possible. */ |
403 | cdev->private->options.pgroup = 0; | 403 | cdev->private->flags.pgroup = 0; |
404 | cdev->private->flags.pgid_single = 1; | 404 | cdev->private->flags.mpath = 0; |
405 | } else | 405 | } else { |
406 | cdev->private->flags.pgid_single = 0; | 406 | /* |
407 | * Initialize pathgroup and multipath state with target values. | ||
408 | * They may change in the course of path verification. | ||
409 | */ | ||
410 | cdev->private->flags.pgroup = cdev->private->options.pgroup; | ||
411 | cdev->private->flags.mpath = cdev->private->options.mpath; | ||
412 | |||
413 | } | ||
407 | cdev->private->flags.doverify = 0; | 414 | cdev->private->flags.doverify = 0; |
408 | verify_start(cdev); | 415 | verify_start(cdev); |
409 | } | 416 | } |
@@ -419,7 +426,7 @@ static void disband_callback(struct ccw_device *cdev, void *data, int rc) | |||
419 | if (rc) | 426 | if (rc) |
420 | goto out; | 427 | goto out; |
421 | /* Ensure consistent multipathing state at device and channel. */ | 428 | /* Ensure consistent multipathing state at device and channel. */ |
422 | cdev->private->flags.pgid_single = 1; | 429 | cdev->private->flags.mpath = 0; |
423 | if (sch->config.mp) { | 430 | if (sch->config.mp) { |
424 | sch->config.mp = 0; | 431 | sch->config.mp = 0; |
425 | rc = cio_commit_config(sch); | 432 | rc = cio_commit_config(sch); |
@@ -453,7 +460,7 @@ void ccw_device_disband_start(struct ccw_device *cdev) | |||
453 | req->lpm = sch->schib.pmcw.pam & sch->opm; | 460 | req->lpm = sch->schib.pmcw.pam & sch->opm; |
454 | req->callback = disband_callback; | 461 | req->callback = disband_callback; |
455 | fn = SPID_FUNC_DISBAND; | 462 | fn = SPID_FUNC_DISBAND; |
456 | if (!cdev->private->flags.pgid_single) | 463 | if (cdev->private->flags.mpath) |
457 | fn |= SPID_FUNC_MULTI_PATH; | 464 | fn |= SPID_FUNC_MULTI_PATH; |
458 | spid_build_cp(cdev, fn); | 465 | spid_build_cp(cdev, fn); |
459 | ccw_request_start(cdev); | 466 | ccw_request_start(cdev); |
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 8942dc092d0a..b387c80d1888 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h | |||
@@ -156,9 +156,9 @@ struct ccw_device_private { | |||
156 | unsigned int repall:1; /* report every interrupt status */ | 156 | unsigned int repall:1; /* report every interrupt status */ |
157 | unsigned int pgroup:1; /* do path grouping */ | 157 | unsigned int pgroup:1; /* do path grouping */ |
158 | unsigned int force:1; /* allow forced online */ | 158 | unsigned int force:1; /* allow forced online */ |
159 | unsigned int mpath:1; /* do multipathing */ | ||
159 | } __attribute__ ((packed)) options; | 160 | } __attribute__ ((packed)) options; |
160 | struct { | 161 | struct { |
161 | unsigned int pgid_single:1; /* use single path for Set PGID */ | ||
162 | unsigned int esid:1; /* Ext. SenseID supported by HW */ | 162 | unsigned int esid:1; /* Ext. SenseID supported by HW */ |
163 | unsigned int dosense:1; /* delayed SENSE required */ | 163 | unsigned int dosense:1; /* delayed SENSE required */ |
164 | unsigned int doverify:1; /* delayed path verification */ | 164 | unsigned int doverify:1; /* delayed path verification */ |
@@ -167,6 +167,8 @@ 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 resuming:1; /* recognition while resume */ | 168 | unsigned int resuming:1; /* recognition while resume */ |
169 | unsigned int pgid_rdy:1; /* pgids are ready */ | 169 | unsigned int pgid_rdy:1; /* pgids are ready */ |
170 | unsigned int pgroup:1; /* pathgroup is set up */ | ||
171 | unsigned int mpath:1; /* multipathing is set up */ | ||
170 | } __attribute__((packed)) flags; | 172 | } __attribute__((packed)) flags; |
171 | unsigned long intparm; /* user interruption parameter */ | 173 | unsigned long intparm; /* user interruption parameter */ |
172 | struct qdio_irq *qdio_data; | 174 | struct qdio_irq *qdio_data; |