aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/device_pgid.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio/device_pgid.c')
-rw-r--r--drivers/s390/cio/device_pgid.c81
1 files changed, 42 insertions, 39 deletions
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 1693a102dcfe..8ca2d078848c 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -245,18 +245,17 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
245 memset(&cdev->private->irb, 0, sizeof(struct irb)); 245 memset(&cdev->private->irb, 0, sizeof(struct irb));
246 246
247 /* Try multiple times. */ 247 /* Try multiple times. */
248 ret = -ENODEV; 248 ret = -EACCES;
249 if (cdev->private->iretry > 0) { 249 if (cdev->private->iretry > 0) {
250 cdev->private->iretry--; 250 cdev->private->iretry--;
251 ret = cio_start (sch, cdev->private->iccws, 251 ret = cio_start (sch, cdev->private->iccws,
252 cdev->private->imask); 252 cdev->private->imask);
253 /* ret is 0, -EBUSY, -EACCES or -ENODEV */ 253 /* We expect an interrupt in case of success or busy
254 if ((ret != -EACCES) && (ret != -ENODEV)) 254 * indication. */
255 if ((ret == 0) || (ret == -EBUSY))
255 return ret; 256 return ret;
256 } 257 }
257 /* PGID command failed on this path. Switch it off. */ 258 /* PGID command failed on this path. */
258 sch->lpm &= ~cdev->private->imask;
259 sch->vpm &= ~cdev->private->imask;
260 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel " 259 CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
261 "0.%x.%04x, lpm %02X, became 'not operational'\n", 260 "0.%x.%04x, lpm %02X, became 'not operational'\n",
262 cdev->private->devno, sch->schid.ssid, 261 cdev->private->devno, sch->schid.ssid,
@@ -286,18 +285,17 @@ static int __ccw_device_do_nop(struct ccw_device *cdev)
286 memset(&cdev->private->irb, 0, sizeof(struct irb)); 285 memset(&cdev->private->irb, 0, sizeof(struct irb));
287 286
288 /* Try multiple times. */ 287 /* Try multiple times. */
289 ret = -ENODEV; 288 ret = -EACCES;
290 if (cdev->private->iretry > 0) { 289 if (cdev->private->iretry > 0) {
291 cdev->private->iretry--; 290 cdev->private->iretry--;
292 ret = cio_start (sch, cdev->private->iccws, 291 ret = cio_start (sch, cdev->private->iccws,
293 cdev->private->imask); 292 cdev->private->imask);
294 /* ret is 0, -EBUSY, -EACCES or -ENODEV */ 293 /* We expect an interrupt in case of success or busy
295 if ((ret != -EACCES) && (ret != -ENODEV)) 294 * indication. */
295 if ((ret == 0) || (ret == -EBUSY))
296 return ret; 296 return ret;
297 } 297 }
298 /* nop command failed on this path. Switch it off. */ 298 /* nop command failed on this path. */
299 sch->lpm &= ~cdev->private->imask;
300 sch->vpm &= ~cdev->private->imask;
301 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel " 299 CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
302 "0.%x.%04x, lpm %02X, became 'not operational'\n", 300 "0.%x.%04x, lpm %02X, became 'not operational'\n",
303 cdev->private->devno, sch->schid.ssid, 301 cdev->private->devno, sch->schid.ssid,
@@ -372,27 +370,32 @@ static void
372__ccw_device_verify_start(struct ccw_device *cdev) 370__ccw_device_verify_start(struct ccw_device *cdev)
373{ 371{
374 struct subchannel *sch; 372 struct subchannel *sch;
375 __u8 imask, func; 373 __u8 func;
376 int ret; 374 int ret;
377 375
378 sch = to_subchannel(cdev->dev.parent); 376 sch = to_subchannel(cdev->dev.parent);
379 while (sch->vpm != sch->lpm) { 377 /* Repeat for all paths. */
380 /* Find first unequal bit in vpm vs. lpm */ 378 for (; cdev->private->imask; cdev->private->imask >>= 1,
381 for (imask = 0x80; imask != 0; imask >>= 1) 379 cdev->private->iretry = 5) {
382 if ((sch->vpm & imask) != (sch->lpm & imask)) 380 if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
383 break; 381 /* Path not available, try next. */
384 cdev->private->imask = imask; 382 continue;
385 if (cdev->private->options.pgroup) { 383 if (cdev->private->options.pgroup) {
386 func = (sch->vpm & imask) ? 384 if (sch->opm & cdev->private->imask)
387 SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH; 385 func = SPID_FUNC_ESTABLISH;
386 else
387 func = SPID_FUNC_RESIGN;
388 ret = __ccw_device_do_pgid(cdev, func); 388 ret = __ccw_device_do_pgid(cdev, func);
389 } else 389 } else
390 ret = __ccw_device_do_nop(cdev); 390 ret = __ccw_device_do_nop(cdev);
391 /* We expect an interrupt in case of success or busy
392 * indication. */
391 if (ret == 0 || ret == -EBUSY) 393 if (ret == 0 || ret == -EBUSY)
392 return; 394 return;
393 cdev->private->iretry = 5; 395 /* Permanent path failure, try next. */
394 } 396 }
395 ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); 397 /* Done with all paths. */
398 ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
396} 399}
397 400
398/* 401/*
@@ -421,14 +424,14 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
421 else 424 else
422 ret = __ccw_device_check_nop(cdev); 425 ret = __ccw_device_check_nop(cdev);
423 memset(&cdev->private->irb, 0, sizeof(struct irb)); 426 memset(&cdev->private->irb, 0, sizeof(struct irb));
427
424 switch (ret) { 428 switch (ret) {
425 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ 429 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
426 case 0: 430 case 0:
427 /* Establish or Resign Path Group done. Update vpm. */ 431 /* Path verification ccw finished successfully, update lpm. */
428 if ((sch->lpm & cdev->private->imask) != 0) 432 sch->vpm |= sch->opm & cdev->private->imask;
429 sch->vpm |= cdev->private->imask; 433 /* Go on with next path. */
430 else 434 cdev->private->imask >>= 1;
431 sch->vpm &= ~cdev->private->imask;
432 cdev->private->iretry = 5; 435 cdev->private->iretry = 5;
433 __ccw_device_verify_start(cdev); 436 __ccw_device_verify_start(cdev);
434 break; 437 break;
@@ -441,6 +444,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
441 cdev->private->options.pgroup = 0; 444 cdev->private->options.pgroup = 0;
442 else 445 else
443 cdev->private->flags.pgid_single = 1; 446 cdev->private->flags.pgid_single = 1;
447 /* Retry */
448 sch->vpm = 0;
449 cdev->private->imask = 0x80;
450 cdev->private->iretry = 5;
444 /* fall through. */ 451 /* fall through. */
445 case -EAGAIN: /* Try again. */ 452 case -EAGAIN: /* Try again. */
446 __ccw_device_verify_start(cdev); 453 __ccw_device_verify_start(cdev);
@@ -449,8 +456,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
449 ccw_device_verify_done(cdev, -ETIME); 456 ccw_device_verify_done(cdev, -ETIME);
450 break; 457 break;
451 case -EACCES: /* channel is not operational. */ 458 case -EACCES: /* channel is not operational. */
452 sch->lpm &= ~cdev->private->imask; 459 cdev->private->imask >>= 1;
453 sch->vpm &= ~cdev->private->imask;
454 cdev->private->iretry = 5; 460 cdev->private->iretry = 5;
455 __ccw_device_verify_start(cdev); 461 __ccw_device_verify_start(cdev);
456 break; 462 break;
@@ -463,19 +469,17 @@ ccw_device_verify_start(struct ccw_device *cdev)
463 struct subchannel *sch = to_subchannel(cdev->dev.parent); 469 struct subchannel *sch = to_subchannel(cdev->dev.parent);
464 470
465 cdev->private->flags.pgid_single = 0; 471 cdev->private->flags.pgid_single = 0;
472 cdev->private->imask = 0x80;
466 cdev->private->iretry = 5; 473 cdev->private->iretry = 5;
467 /* 474
468 * Update sch->lpm with current values to catch paths becoming 475 /* Start with empty vpm. */
469 * available again. 476 sch->vpm = 0;
470 */ 477
478 /* Get current pam. */
471 if (stsch(sch->schid, &sch->schib)) { 479 if (stsch(sch->schid, &sch->schib)) {
472 ccw_device_verify_done(cdev, -ENODEV); 480 ccw_device_verify_done(cdev, -ENODEV);
473 return; 481 return;
474 } 482 }
475 sch->lpm = sch->schib.pmcw.pim &
476 sch->schib.pmcw.pam &
477 sch->schib.pmcw.pom &
478 sch->opm;
479 __ccw_device_verify_start(cdev); 483 __ccw_device_verify_start(cdev);
480} 484}
481 485
@@ -524,7 +528,6 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
524 switch (ret) { 528 switch (ret) {
525 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ 529 /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
526 case 0: /* disband successful. */ 530 case 0: /* disband successful. */
527 sch->vpm = 0;
528 ccw_device_disband_done(cdev, ret); 531 ccw_device_disband_done(cdev, ret);
529 break; 532 break;
530 case -EOPNOTSUPP: 533 case -EOPNOTSUPP: