diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2008-12-25 07:39:13 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 07:39:10 -0500 |
commit | 13952ec12dfeea793ff83c2a96139ed57eb0b897 (patch) | |
tree | 3311df62a1794bf95f78b2c57f25f0a79dc84523 /drivers/s390/cio/cio.c | |
parent | cdb912a40df8b8507ab60b3d52f9980c0ba1f44d (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.c | 134 |
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 | |||
334 | static 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 | |||
348 | static 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 | */ |
337 | int | 364 | int cio_commit_config(struct subchannel *sch) |
338 | cio_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); | |||
436 | int cio_disable_subchannel(struct subchannel *sch) | 468 | int 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) | |||
832 | void | 844 | void |
833 | cio_release_console(void) | 845 | cio_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 | } |