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/cio/device_pgid.c | |
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/cio/device_pgid.c')
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 148 |
1 files changed, 95 insertions, 53 deletions
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 | } |