aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/cio.c
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2008-12-25 07:39:13 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2008-12-25 07:39:10 -0500
commit13952ec12dfeea793ff83c2a96139ed57eb0b897 (patch)
tree3311df62a1794bf95f78b2c57f25f0a79dc84523 /drivers/s390/cio/cio.c
parentcdb912a40df8b8507ab60b3d52f9980c0ba1f44d (diff)
[S390] cio: introduce cio_commit_config
To change the configuration of a subchannel we alter the modifiable bits of the subchannel's schib field and issue a modify subchannel. There can be the case that not all changes were applied -or worse- quietly overwritten by the hardware. With the next store subchannel we obtain the current state of the hardware but lose our target configuration. With this patch we introduce a subchannel_config structure which contains the target subchannel configuration. Additionally the msch wrapper cio_modify is replaced with cio_commit_config which copies the desired changes to a temporary schib. msch is then called with the temporary schib. This schib is only written back to the subchannel if all changes were applied. Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/cio.c')
-rw-r--r--drivers/s390/cio/cio.c134
1 files changed, 73 insertions, 61 deletions
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 9bdb463765c7..8f1cec499532 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -330,30 +330,70 @@ cio_cancel (struct subchannel *sch)
330 } 330 }
331} 331}
332 332
333
334static void cio_apply_config(struct subchannel *sch, struct schib *schib)
335{
336 schib->pmcw.intparm = sch->config.intparm;
337 schib->pmcw.mbi = sch->config.mbi;
338 schib->pmcw.isc = sch->config.isc;
339 schib->pmcw.ena = sch->config.ena;
340 schib->pmcw.mme = sch->config.mme;
341 schib->pmcw.mp = sch->config.mp;
342 schib->pmcw.csense = sch->config.csense;
343 schib->pmcw.mbfc = sch->config.mbfc;
344 if (sch->config.mbfc)
345 schib->mba = sch->config.mba;
346}
347
348static int cio_check_config(struct subchannel *sch, struct schib *schib)
349{
350 return (schib->pmcw.intparm == sch->config.intparm) &&
351 (schib->pmcw.mbi == sch->config.mbi) &&
352 (schib->pmcw.isc == sch->config.isc) &&
353 (schib->pmcw.ena == sch->config.ena) &&
354 (schib->pmcw.mme == sch->config.mme) &&
355 (schib->pmcw.mp == sch->config.mp) &&
356 (schib->pmcw.csense == sch->config.csense) &&
357 (schib->pmcw.mbfc == sch->config.mbfc) &&
358 (!sch->config.mbfc || (schib->mba == sch->config.mba));
359}
360
333/* 361/*
334 * Function: cio_modify 362 * cio_commit_config - apply configuration to the subchannel
335 * Issues a "Modify Subchannel" on the specified subchannel
336 */ 363 */
337int 364int cio_commit_config(struct subchannel *sch)
338cio_modify (struct subchannel *sch)
339{ 365{
340 int ccode, retry, ret; 366 struct schib schib;
367 int ccode, retry, ret = 0;
368
369 if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
370 return -ENODEV;
341 371
342 ret = 0;
343 for (retry = 0; retry < 5; retry++) { 372 for (retry = 0; retry < 5; retry++) {
344 ccode = msch_err (sch->schid, &sch->schib); 373 /* copy desired changes to local schib */
345 if (ccode < 0) /* -EIO if msch gets a program check. */ 374 cio_apply_config(sch, &schib);
375 ccode = msch_err(sch->schid, &schib);
376 if (ccode < 0) /* -EIO if msch gets a program check. */
346 return ccode; 377 return ccode;
347 switch (ccode) { 378 switch (ccode) {
348 case 0: /* successfull */ 379 case 0: /* successfull */
349 return 0; 380 if (stsch(sch->schid, &schib) ||
350 case 1: /* status pending */ 381 !css_sch_is_valid(&schib))
382 return -ENODEV;
383 if (cio_check_config(sch, &schib)) {
384 /* commit changes from local schib */
385 memcpy(&sch->schib, &schib, sizeof(schib));
386 return 0;
387 }
388 ret = -EAGAIN;
389 break;
390 case 1: /* status pending */
351 return -EBUSY; 391 return -EBUSY;
352 case 2: /* busy */ 392 case 2: /* busy */
353 udelay (100); /* allow for recovery */ 393 udelay(100); /* allow for recovery */
354 ret = -EBUSY; 394 ret = -EBUSY;
355 break; 395 break;
356 case 3: /* not operational */ 396 case 3: /* not operational */
357 return -ENODEV; 397 return -ENODEV;
358 } 398 }
359 } 399 }
@@ -396,32 +436,24 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
396 if (cio_update_schib(sch)) 436 if (cio_update_schib(sch))
397 return -ENODEV; 437 return -ENODEV;
398 438
399 for (retry = 5, ret = 0; retry > 0; retry--) { 439 sch->config.ena = 1;
400 sch->schib.pmcw.ena = 1; 440 sch->config.isc = sch->isc;
401 sch->schib.pmcw.isc = sch->isc; 441 sch->config.intparm = intparm;
402 sch->schib.pmcw.intparm = intparm; 442
403 ret = cio_modify(sch); 443 for (retry = 0; retry < 3; retry++) {
404 if (ret == -ENODEV) 444 ret = cio_commit_config(sch);
405 break; 445 if (ret == -EIO) {
406 if (ret == -EIO)
407 /* 446 /*
408 * Got a program check in cio_modify. Try without 447 * Got a program check in msch. Try without
409 * the concurrent sense bit the next time. 448 * the concurrent sense bit the next time.
410 */ 449 */
411 sch->schib.pmcw.csense = 0; 450 sch->config.csense = 0;
412 if (ret == 0) { 451 } else if (ret == -EBUSY) {
413 if (cio_update_schib(sch)) {
414 ret = -ENODEV;
415 break;
416 }
417 if (sch->schib.pmcw.ena)
418 break;
419 }
420 if (ret == -EBUSY) {
421 struct irb irb; 452 struct irb irb;
422 if (tsch(sch->schid, &irb) != 0) 453 if (tsch(sch->schid, &irb) != 0)
423 break; 454 break;
424 } 455 } else
456 break;
425 } 457 }
426 sprintf (dbf_txt, "ret:%d", ret); 458 sprintf (dbf_txt, "ret:%d", ret);
427 CIO_TRACE_EVENT (2, dbf_txt); 459 CIO_TRACE_EVENT (2, dbf_txt);
@@ -436,7 +468,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
436int cio_disable_subchannel(struct subchannel *sch) 468int cio_disable_subchannel(struct subchannel *sch)
437{ 469{
438 char dbf_txt[15]; 470 char dbf_txt[15];
439 int retry;
440 int ret; 471 int ret;
441 472
442 CIO_TRACE_EVENT (2, "dissch"); 473 CIO_TRACE_EVENT (2, "dissch");
@@ -454,27 +485,9 @@ int cio_disable_subchannel(struct subchannel *sch)
454 */ 485 */
455 return -EBUSY; 486 return -EBUSY;
456 487
457 for (retry = 5, ret = 0; retry > 0; retry--) { 488 sch->config.ena = 0;
458 sch->schib.pmcw.ena = 0; 489 ret = cio_commit_config(sch);
459 ret = cio_modify(sch); 490
460 if (ret == -ENODEV)
461 break;
462 if (ret == -EBUSY)
463 /*
464 * The subchannel is busy or status pending.
465 * We'll disable when the next interrupt was delivered
466 * via the state machine.
467 */
468 break;
469 if (ret == 0) {
470 if (cio_update_schib(sch)) {
471 ret = -ENODEV;
472 break;
473 }
474 if (!sch->schib.pmcw.ena)
475 break;
476 }
477 }
478 sprintf (dbf_txt, "ret:%d", ret); 491 sprintf (dbf_txt, "ret:%d", ret);
479 CIO_TRACE_EVENT (2, dbf_txt); 492 CIO_TRACE_EVENT (2, dbf_txt);
480 return ret; 493 return ret;
@@ -817,10 +830,9 @@ cio_probe_console(void)
817 * enable console I/O-interrupt subclass 830 * enable console I/O-interrupt subclass
818 */ 831 */
819 isc_register(CONSOLE_ISC); 832 isc_register(CONSOLE_ISC);
820 console_subchannel.schib.pmcw.isc = CONSOLE_ISC; 833 console_subchannel.config.isc = CONSOLE_ISC;
821 console_subchannel.schib.pmcw.intparm = 834 console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel;
822 (u32)(addr_t)&console_subchannel; 835 ret = cio_commit_config(&console_subchannel);
823 ret = cio_modify(&console_subchannel);
824 if (ret) { 836 if (ret) {
825 isc_unregister(CONSOLE_ISC); 837 isc_unregister(CONSOLE_ISC);
826 console_subchannel_in_use = 0; 838 console_subchannel_in_use = 0;
@@ -832,8 +844,8 @@ cio_probe_console(void)
832void 844void
833cio_release_console(void) 845cio_release_console(void)
834{ 846{
835 console_subchannel.schib.pmcw.intparm = 0; 847 console_subchannel.config.intparm = 0;
836 cio_modify(&console_subchannel); 848 cio_commit_config(&console_subchannel);
837 isc_unregister(CONSOLE_ISC); 849 isc_unregister(CONSOLE_ISC);
838 console_subchannel_in_use = 0; 850 console_subchannel_in_use = 0;
839} 851}