diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2009-12-07 06:51:31 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2009-12-07 06:51:31 -0500 |
commit | 52ef0608e3ee4a511725e443c4b572fece22b353 (patch) | |
tree | 08a2d1f3a3015ec4026f229a1994a31d1b7d50e2 /drivers/s390 | |
parent | 454e1fa1ebae7cff707b2e3f12b775c263c8408b (diff) |
[S390] cio: use sense-pgid operation for path verification
Set-pgid operations fail for some device types under z/VM for which
the hypervisor has already set the pgid. Also reserved devices or
changed pgids are not correctly recognized. Fix these problems by
using a combination of sense-pgid and set-pgid and by also accepting
pre-defined pgid settings.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/device.h | 3 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 41 | ||||
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 148 | ||||
-rw-r--r-- | drivers/s390/cio/io_sch.h | 1 |
4 files changed, 99 insertions, 94 deletions
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 4e1775cf9739..2df519bb877e 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h | |||
@@ -110,9 +110,6 @@ void ccw_device_sense_id_start(struct ccw_device *); | |||
110 | void ccw_device_sense_id_done(struct ccw_device *, int); | 110 | void ccw_device_sense_id_done(struct ccw_device *, int); |
111 | 111 | ||
112 | /* Function prototypes for path grouping stuff. */ | 112 | /* Function prototypes for path grouping stuff. */ |
113 | void ccw_device_sense_pgid_start(struct ccw_device *); | ||
114 | void ccw_device_sense_pgid_done(struct ccw_device *, int); | ||
115 | |||
116 | void ccw_device_verify_start(struct ccw_device *); | 113 | void ccw_device_verify_start(struct ccw_device *); |
117 | void ccw_device_verify_done(struct ccw_device *, int); | 114 | void ccw_device_verify_done(struct ccw_device *, int); |
118 | 115 | ||
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index d6e315dc0f98..8d565ff85e43 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -395,33 +395,6 @@ ccw_device_done(struct ccw_device *cdev, int state) | |||
395 | } | 395 | } |
396 | 396 | ||
397 | /* | 397 | /* |
398 | * Function called from device_pgid.c after sense path ground has completed. | ||
399 | */ | ||
400 | void | ||
401 | ccw_device_sense_pgid_done(struct ccw_device *cdev, int err) | ||
402 | { | ||
403 | struct subchannel *sch; | ||
404 | |||
405 | sch = to_subchannel(cdev->dev.parent); | ||
406 | switch (err) { | ||
407 | case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */ | ||
408 | case 0: /* success */ | ||
409 | case -EACCES: /* partial success, some paths not operational */ | ||
410 | break; | ||
411 | case -ETIME: /* Sense path group id stopped by timeout. */ | ||
412 | case -EUSERS: /* device is reserved for someone else. */ | ||
413 | ccw_device_done(cdev, DEV_STATE_BOXED); | ||
414 | return; | ||
415 | default: | ||
416 | ccw_device_done(cdev, DEV_STATE_NOT_OPER); | ||
417 | return; | ||
418 | } | ||
419 | /* Start Path Group verification. */ | ||
420 | cdev->private->state = DEV_STATE_VERIFY; | ||
421 | ccw_device_verify_start(cdev); | ||
422 | } | ||
423 | |||
424 | /* | ||
425 | * Start device recognition. | 398 | * Start device recognition. |
426 | */ | 399 | */ |
427 | void ccw_device_recognition(struct ccw_device *cdev) | 400 | void ccw_device_recognition(struct ccw_device *cdev) |
@@ -503,6 +476,7 @@ callback: | |||
503 | } | 476 | } |
504 | break; | 477 | break; |
505 | case -ETIME: | 478 | case -ETIME: |
479 | case -EUSERS: | ||
506 | /* Reset oper notify indication after verify error. */ | 480 | /* Reset oper notify indication after verify error. */ |
507 | cdev->private->flags.donotify = 0; | 481 | cdev->private->flags.donotify = 0; |
508 | ccw_device_done(cdev, DEV_STATE_BOXED); | 482 | ccw_device_done(cdev, DEV_STATE_BOXED); |
@@ -540,16 +514,9 @@ ccw_device_online(struct ccw_device *cdev) | |||
540 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); | 514 | dev_fsm_event(cdev, DEV_EVENT_NOTOPER); |
541 | return ret; | 515 | return ret; |
542 | } | 516 | } |
543 | /* Do we want to do path grouping? */ | 517 | /* Start initial path verification. */ |
544 | if (!cdev->private->options.pgroup) { | 518 | cdev->private->state = DEV_STATE_VERIFY; |
545 | /* Start initial path verification. */ | 519 | ccw_device_verify_start(cdev); |
546 | cdev->private->state = DEV_STATE_VERIFY; | ||
547 | ccw_device_verify_start(cdev); | ||
548 | return 0; | ||
549 | } | ||
550 | /* Do a SensePGID first. */ | ||
551 | cdev->private->state = DEV_STATE_SENSE_PGID; | ||
552 | ccw_device_sense_pgid_start(cdev); | ||
553 | return 0; | 520 | return 0; |
554 | } | 521 | } |
555 | 522 | ||
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 3323042ba755..4d54abd82b8c 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c | |||
@@ -141,8 +141,8 @@ static void spid_do(struct ccw_device *cdev) | |||
141 | struct ccw_request *req = &cdev->private->req; | 141 | struct ccw_request *req = &cdev->private->req; |
142 | u8 fn; | 142 | u8 fn; |
143 | 143 | ||
144 | /* Adjust lpm if paths are not set in pam. */ | 144 | /* Use next available path that is not already in correct state. */ |
145 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); | 145 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm); |
146 | if (!req->lpm) | 146 | if (!req->lpm) |
147 | goto out_nopath; | 147 | goto out_nopath; |
148 | /* Channel program setup. */ | 148 | /* Channel program setup. */ |
@@ -199,6 +199,19 @@ err: | |||
199 | verify_done(cdev, rc); | 199 | verify_done(cdev, rc); |
200 | } | 200 | } |
201 | 201 | ||
202 | static void spid_start(struct ccw_device *cdev) | ||
203 | { | ||
204 | struct ccw_request *req = &cdev->private->req; | ||
205 | |||
206 | /* Initialize request data. */ | ||
207 | memset(req, 0, sizeof(*req)); | ||
208 | req->timeout = PGID_TIMEOUT; | ||
209 | req->maxretries = PGID_RETRIES; | ||
210 | req->lpm = 0x80; | ||
211 | req->callback = spid_callback; | ||
212 | spid_do(cdev); | ||
213 | } | ||
214 | |||
202 | static int pgid_cmp(struct pgid *p1, struct pgid *p2) | 215 | static int pgid_cmp(struct pgid *p1, struct pgid *p2) |
203 | { | 216 | { |
204 | return memcmp((char *) p1 + 1, (char *) p2 + 1, | 217 | return memcmp((char *) p1 + 1, (char *) p2 + 1, |
@@ -241,6 +254,40 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, | |||
241 | *p = first; | 254 | *p = first; |
242 | } | 255 | } |
243 | 256 | ||
257 | static u8 pgid_to_vpm(struct ccw_device *cdev) | ||
258 | { | ||
259 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
260 | struct pgid *pgid; | ||
261 | int i; | ||
262 | int lpm; | ||
263 | u8 vpm = 0; | ||
264 | |||
265 | /* Set VPM bits for paths which are already in the target state. */ | ||
266 | for (i = 0; i < 8; i++) { | ||
267 | lpm = 0x80 >> i; | ||
268 | if ((cdev->private->pgid_valid_mask & lpm) == 0) | ||
269 | continue; | ||
270 | pgid = &cdev->private->pgid[i]; | ||
271 | if (sch->opm & lpm) { | ||
272 | if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED) | ||
273 | continue; | ||
274 | } else { | ||
275 | if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED) | ||
276 | continue; | ||
277 | } | ||
278 | if (cdev->private->flags.mpath) { | ||
279 | if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH) | ||
280 | continue; | ||
281 | } else { | ||
282 | if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) | ||
283 | continue; | ||
284 | } | ||
285 | vpm |= lpm; | ||
286 | } | ||
287 | |||
288 | return vpm; | ||
289 | } | ||
290 | |||
244 | static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) | 291 | static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) |
245 | { | 292 | { |
246 | int i; | 293 | int i; |
@@ -255,6 +302,7 @@ static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) | |||
255 | static void snid_done(struct ccw_device *cdev, int rc) | 302 | static void snid_done(struct ccw_device *cdev, int rc) |
256 | { | 303 | { |
257 | struct ccw_dev_id *id = &cdev->private->dev_id; | 304 | struct ccw_dev_id *id = &cdev->private->dev_id; |
305 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
258 | struct pgid *pgid; | 306 | struct pgid *pgid; |
259 | int mismatch = 0; | 307 | int mismatch = 0; |
260 | int reserved = 0; | 308 | int reserved = 0; |
@@ -263,18 +311,38 @@ static void snid_done(struct ccw_device *cdev, int rc) | |||
263 | if (rc) | 311 | if (rc) |
264 | goto out; | 312 | goto out; |
265 | pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); | 313 | pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); |
266 | if (!mismatch) { | ||
267 | pgid_fill(cdev, pgid); | ||
268 | cdev->private->flags.pgid_rdy = 1; | ||
269 | } | ||
270 | if (reserved) | 314 | if (reserved) |
271 | rc = -EUSERS; | 315 | rc = -EUSERS; |
316 | else if (mismatch) | ||
317 | rc = -EOPNOTSUPP; | ||
318 | else { | ||
319 | sch->vpm = pgid_to_vpm(cdev); | ||
320 | pgid_fill(cdev, pgid); | ||
321 | } | ||
272 | out: | 322 | out: |
273 | CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x mism=%d " | 323 | CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " |
274 | "rsvd=%d reset=%d\n", id->ssid, id->devno, rc, | 324 | "mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc, |
275 | cdev->private->pgid_valid_mask, mismatch, reserved, | 325 | cdev->private->pgid_valid_mask, sch->vpm, mismatch, |
276 | reset); | 326 | reserved, reset); |
277 | ccw_device_sense_pgid_done(cdev, rc); | 327 | switch (rc) { |
328 | case 0: | ||
329 | /* Anything left to do? */ | ||
330 | if (sch->vpm == sch->schib.pmcw.pam) { | ||
331 | verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); | ||
332 | return; | ||
333 | } | ||
334 | /* Perform path-grouping. */ | ||
335 | spid_start(cdev); | ||
336 | break; | ||
337 | case -EOPNOTSUPP: | ||
338 | /* Path-grouping not supported. */ | ||
339 | cdev->private->flags.pgroup = 0; | ||
340 | cdev->private->flags.mpath = 0; | ||
341 | verify_start(cdev); | ||
342 | break; | ||
343 | default: | ||
344 | verify_done(cdev, rc); | ||
345 | } | ||
278 | } | 346 | } |
279 | 347 | ||
280 | /* | 348 | /* |
@@ -333,33 +401,6 @@ err: | |||
333 | snid_done(cdev, rc); | 401 | snid_done(cdev, rc); |
334 | } | 402 | } |
335 | 403 | ||
336 | /** | ||
337 | * ccw_device_sense_pgid_start - perform SENSE PGID | ||
338 | * @cdev: ccw device | ||
339 | * | ||
340 | * Execute a SENSE PGID channel program on each path to @cdev to update its | ||
341 | * PGID information. When finished, call ccw_device_sense_id_done with a | ||
342 | * return code specifying the result. | ||
343 | */ | ||
344 | void ccw_device_sense_pgid_start(struct ccw_device *cdev) | ||
345 | { | ||
346 | struct ccw_request *req = &cdev->private->req; | ||
347 | |||
348 | CIO_TRACE_EVENT(4, "snid"); | ||
349 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); | ||
350 | /* Initialize PGID data. */ | ||
351 | memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); | ||
352 | cdev->private->flags.pgid_rdy = 0; | ||
353 | cdev->private->pgid_valid_mask = 0; | ||
354 | /* Initialize request data. */ | ||
355 | memset(req, 0, sizeof(*req)); | ||
356 | req->timeout = PGID_TIMEOUT; | ||
357 | req->maxretries = PGID_RETRIES; | ||
358 | req->callback = snid_callback; | ||
359 | req->lpm = 0x80; | ||
360 | snid_do(cdev); | ||
361 | } | ||
362 | |||
363 | /* | 404 | /* |
364 | * Perform path verification. | 405 | * Perform path verification. |
365 | */ | 406 | */ |
@@ -367,6 +408,7 @@ static void verify_start(struct ccw_device *cdev) | |||
367 | { | 408 | { |
368 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 409 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
369 | struct ccw_request *req = &cdev->private->req; | 410 | struct ccw_request *req = &cdev->private->req; |
411 | struct ccw_dev_id *devid = &cdev->private->dev_id; | ||
370 | 412 | ||
371 | sch->vpm = 0; | 413 | sch->vpm = 0; |
372 | /* Initialize request data. */ | 414 | /* Initialize request data. */ |
@@ -375,9 +417,13 @@ static void verify_start(struct ccw_device *cdev) | |||
375 | req->maxretries = PGID_RETRIES; | 417 | req->maxretries = PGID_RETRIES; |
376 | req->lpm = 0x80; | 418 | req->lpm = 0x80; |
377 | if (cdev->private->flags.pgroup) { | 419 | if (cdev->private->flags.pgroup) { |
378 | req->callback = spid_callback; | 420 | CIO_TRACE_EVENT(4, "snid"); |
379 | spid_do(cdev); | 421 | CIO_HEX_EVENT(4, devid, sizeof(*devid)); |
422 | req->callback = snid_callback; | ||
423 | snid_do(cdev); | ||
380 | } else { | 424 | } else { |
425 | CIO_TRACE_EVENT(4, "nop"); | ||
426 | CIO_HEX_EVENT(4, devid, sizeof(*devid)); | ||
381 | req->filter = nop_filter; | 427 | req->filter = nop_filter; |
382 | req->callback = nop_callback; | 428 | req->callback = nop_callback; |
383 | nop_do(cdev); | 429 | nop_do(cdev); |
@@ -398,19 +444,15 @@ void ccw_device_verify_start(struct ccw_device *cdev) | |||
398 | { | 444 | { |
399 | CIO_TRACE_EVENT(4, "vrfy"); | 445 | CIO_TRACE_EVENT(4, "vrfy"); |
400 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); | 446 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); |
401 | if (!cdev->private->flags.pgid_rdy) { | 447 | /* Initialize PGID data. */ |
402 | /* No pathgrouping possible. */ | 448 | memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); |
403 | cdev->private->flags.pgroup = 0; | 449 | cdev->private->pgid_valid_mask = 0; |
404 | cdev->private->flags.mpath = 0; | 450 | /* |
405 | } else { | 451 | * Initialize pathgroup and multipath state with target values. |
406 | /* | 452 | * They may change in the course of path verification. |
407 | * Initialize pathgroup and multipath state with target values. | 453 | */ |
408 | * They may change in the course of path verification. | 454 | cdev->private->flags.pgroup = cdev->private->options.pgroup; |
409 | */ | 455 | cdev->private->flags.mpath = cdev->private->options.mpath; |
410 | cdev->private->flags.pgroup = cdev->private->options.pgroup; | ||
411 | cdev->private->flags.mpath = cdev->private->options.mpath; | ||
412 | |||
413 | } | ||
414 | cdev->private->flags.doverify = 0; | 456 | cdev->private->flags.doverify = 0; |
415 | verify_start(cdev); | 457 | verify_start(cdev); |
416 | } | 458 | } |
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index b387c80d1888..0559479073cc 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h | |||
@@ -166,7 +166,6 @@ struct ccw_device_private { | |||
166 | unsigned int recog_done:1; /* dev. recog. complete */ | 166 | unsigned int recog_done:1; /* dev. recog. complete */ |
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 */ | ||
170 | unsigned int pgroup:1; /* pathgroup is set up */ | 169 | unsigned int pgroup:1; /* pathgroup is set up */ |
171 | unsigned int mpath:1; /* multipathing is set up */ | 170 | unsigned int mpath:1; /* multipathing is set up */ |
172 | } __attribute__((packed)) flags; | 171 | } __attribute__((packed)) flags; |