diff options
-rw-r--r-- | drivers/s390/cio/chsc.c | 232 |
1 files changed, 112 insertions, 120 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 0260f12231e0..c6db7f44689a 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -461,144 +461,136 @@ __get_chpid_from_lir(void *data) | |||
461 | return (u16) (lir->indesc[0]&0x000000ff); | 461 | return (u16) (lir->indesc[0]&0x000000ff); |
462 | } | 462 | } |
463 | 463 | ||
464 | int | 464 | struct chsc_sei_area { |
465 | chsc_process_crw(void) | 465 | struct chsc_header request; |
466 | u32 reserved1; | ||
467 | u32 reserved2; | ||
468 | u32 reserved3; | ||
469 | struct chsc_header response; | ||
470 | u32 reserved4; | ||
471 | u8 flags; | ||
472 | u8 vf; /* validity flags */ | ||
473 | u8 rs; /* reporting source */ | ||
474 | u8 cc; /* content code */ | ||
475 | u16 fla; /* full link address */ | ||
476 | u16 rsid; /* reporting source id */ | ||
477 | u32 reserved5; | ||
478 | u32 reserved6; | ||
479 | u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */ | ||
480 | /* ccdf has to be big enough for a link-incident record */ | ||
481 | } __attribute__ ((packed)); | ||
482 | |||
483 | static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | ||
484 | { | ||
485 | int chpid; | ||
486 | |||
487 | CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", | ||
488 | sei_area->rs, sei_area->rsid); | ||
489 | if (sei_area->rs != 4) | ||
490 | return 0; | ||
491 | chpid = __get_chpid_from_lir(sei_area->ccdf); | ||
492 | if (chpid < 0) | ||
493 | CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); | ||
494 | else | ||
495 | s390_set_chpid_offline(chpid); | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | ||
466 | { | 501 | { |
467 | int chpid, ret; | ||
468 | struct res_acc_data res_data; | 502 | struct res_acc_data res_data; |
469 | struct { | 503 | struct device *dev; |
470 | struct chsc_header request; | 504 | int status; |
471 | u32 reserved1; | 505 | int rc; |
472 | u32 reserved2; | 506 | |
473 | u32 reserved3; | 507 | CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " |
474 | struct chsc_header response; | 508 | "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); |
475 | u32 reserved4; | 509 | if (sei_area->rs != 4) |
476 | u8 flags; | 510 | return 0; |
477 | u8 vf; /* validity flags */ | 511 | /* allocate a new channel path structure, if needed */ |
478 | u8 rs; /* reporting source */ | 512 | status = get_chp_status(sei_area->rsid); |
479 | u8 cc; /* content code */ | 513 | if (status < 0) |
480 | u16 fla; /* full link address */ | 514 | new_channel_path(sei_area->rsid); |
481 | u16 rsid; /* reporting source id */ | 515 | else if (!status) |
482 | u32 reserved5; | 516 | return 0; |
483 | u32 reserved6; | 517 | dev = get_device(&css[0]->chps[sei_area->rsid]->dev); |
484 | u32 ccdf[96]; /* content-code dependent field */ | 518 | memset(&res_data, 0, sizeof(struct res_acc_data)); |
485 | /* ccdf has to be big enough for a link-incident record */ | 519 | res_data.chp = to_channelpath(dev); |
486 | } __attribute__ ((packed)) *sei_area; | 520 | if ((sei_area->vf & 0xc0) != 0) { |
521 | res_data.fla = sei_area->fla; | ||
522 | if ((sei_area->vf & 0xc0) == 0xc0) | ||
523 | /* full link address */ | ||
524 | res_data.fla_mask = 0xffff; | ||
525 | else | ||
526 | /* link address */ | ||
527 | res_data.fla_mask = 0xff00; | ||
528 | } | ||
529 | rc = s390_process_res_acc(&res_data); | ||
530 | put_device(dev); | ||
531 | |||
532 | return rc; | ||
533 | } | ||
534 | |||
535 | static int chsc_process_sei(struct chsc_sei_area *sei_area) | ||
536 | { | ||
537 | int rc; | ||
538 | |||
539 | /* Check if we might have lost some information. */ | ||
540 | if (sei_area->flags & 0x40) | ||
541 | CIO_CRW_EVENT(2, "chsc: event overflow\n"); | ||
542 | /* which kind of information was stored? */ | ||
543 | rc = 0; | ||
544 | switch (sei_area->cc) { | ||
545 | case 1: /* link incident*/ | ||
546 | rc = chsc_process_sei_link_incident(sei_area); | ||
547 | break; | ||
548 | case 2: /* i/o resource accessibiliy */ | ||
549 | rc = chsc_process_sei_res_acc(sei_area); | ||
550 | break; | ||
551 | default: /* other stuff */ | ||
552 | CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", | ||
553 | sei_area->cc); | ||
554 | break; | ||
555 | } | ||
556 | |||
557 | return rc; | ||
558 | } | ||
559 | |||
560 | int chsc_process_crw(void) | ||
561 | { | ||
562 | struct chsc_sei_area *sei_area; | ||
563 | int ret; | ||
564 | int rc; | ||
487 | 565 | ||
488 | if (!sei_page) | 566 | if (!sei_page) |
489 | return 0; | 567 | return 0; |
490 | /* | 568 | /* Access to sei_page is serialized through machine check handler |
491 | * build the chsc request block for store event information | 569 | * thread, so no need for locking. */ |
492 | * and do the call | ||
493 | * This function is only called by the machine check handler thread, | ||
494 | * so we don't need locking for the sei_page. | ||
495 | */ | ||
496 | sei_area = sei_page; | 570 | sei_area = sei_page; |
497 | 571 | ||
498 | CIO_TRACE_EVENT( 2, "prcss"); | 572 | CIO_TRACE_EVENT( 2, "prcss"); |
499 | ret = 0; | 573 | ret = 0; |
500 | do { | 574 | do { |
501 | int ccode, status; | ||
502 | struct device *dev; | ||
503 | memset(sei_area, 0, sizeof(*sei_area)); | 575 | memset(sei_area, 0, sizeof(*sei_area)); |
504 | memset(&res_data, 0, sizeof(struct res_acc_data)); | ||
505 | sei_area->request.length = 0x0010; | 576 | sei_area->request.length = 0x0010; |
506 | sei_area->request.code = 0x000e; | 577 | sei_area->request.code = 0x000e; |
578 | if (chsc(sei_area)) | ||
579 | break; | ||
507 | 580 | ||
508 | ccode = chsc(sei_area); | 581 | if (sei_area->response.code == 0x0001) { |
509 | if (ccode > 0) | 582 | CIO_CRW_EVENT(4, "chsc: sei successful\n"); |
510 | return 0; | 583 | rc = chsc_process_sei(sei_area); |
511 | 584 | if (rc) | |
512 | switch (sei_area->response.code) { | 585 | ret = rc; |
513 | /* for debug purposes, check for problems */ | 586 | } else { |
514 | case 0x0001: | 587 | CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", |
515 | CIO_CRW_EVENT(4, "chsc_process_crw: event information " | ||
516 | "successfully stored\n"); | ||
517 | break; /* everything ok */ | ||
518 | case 0x0002: | ||
519 | CIO_CRW_EVENT(2, | ||
520 | "chsc_process_crw: invalid command!\n"); | ||
521 | return 0; | ||
522 | case 0x0003: | ||
523 | CIO_CRW_EVENT(2, "chsc_process_crw: error in chsc " | ||
524 | "request block!\n"); | ||
525 | return 0; | ||
526 | case 0x0005: | ||
527 | CIO_CRW_EVENT(2, "chsc_process_crw: no event " | ||
528 | "information stored\n"); | ||
529 | return 0; | ||
530 | default: | ||
531 | CIO_CRW_EVENT(2, "chsc_process_crw: chsc response %d\n", | ||
532 | sei_area->response.code); | 588 | sei_area->response.code); |
533 | return 0; | 589 | ret = 0; |
534 | } | ||
535 | |||
536 | /* Check if we might have lost some information. */ | ||
537 | if (sei_area->flags & 0x40) | ||
538 | CIO_CRW_EVENT(2, "chsc_process_crw: Event information " | ||
539 | "has been lost due to overflow!\n"); | ||
540 | |||
541 | if (sei_area->rs != 4) { | ||
542 | CIO_CRW_EVENT(2, "chsc_process_crw: reporting source " | ||
543 | "(%04X) isn't a chpid!\n", | ||
544 | sei_area->rsid); | ||
545 | continue; | ||
546 | } | ||
547 | |||
548 | /* which kind of information was stored? */ | ||
549 | switch (sei_area->cc) { | ||
550 | case 1: /* link incident*/ | ||
551 | CIO_CRW_EVENT(4, "chsc_process_crw: " | ||
552 | "channel subsystem reports link incident," | ||
553 | " reporting source is chpid %x\n", | ||
554 | sei_area->rsid); | ||
555 | chpid = __get_chpid_from_lir(sei_area->ccdf); | ||
556 | if (chpid < 0) | ||
557 | CIO_CRW_EVENT(4, "%s: Invalid LIR, skipping\n", | ||
558 | __FUNCTION__); | ||
559 | else | ||
560 | s390_set_chpid_offline(chpid); | ||
561 | break; | ||
562 | |||
563 | case 2: /* i/o resource accessibiliy */ | ||
564 | CIO_CRW_EVENT(4, "chsc_process_crw: " | ||
565 | "channel subsystem reports some I/O " | ||
566 | "devices may have become accessible\n"); | ||
567 | pr_debug("Data received after sei: \n"); | ||
568 | pr_debug("Validity flags: %x\n", sei_area->vf); | ||
569 | |||
570 | /* allocate a new channel path structure, if needed */ | ||
571 | status = get_chp_status(sei_area->rsid); | ||
572 | if (status < 0) | ||
573 | new_channel_path(sei_area->rsid); | ||
574 | else if (!status) | ||
575 | break; | ||
576 | dev = get_device(&css[0]->chps[sei_area->rsid]->dev); | ||
577 | res_data.chp = to_channelpath(dev); | ||
578 | pr_debug("chpid: %x", sei_area->rsid); | ||
579 | if ((sei_area->vf & 0xc0) != 0) { | ||
580 | res_data.fla = sei_area->fla; | ||
581 | if ((sei_area->vf & 0xc0) == 0xc0) { | ||
582 | pr_debug(" full link addr: %x", | ||
583 | sei_area->fla); | ||
584 | res_data.fla_mask = 0xffff; | ||
585 | } else { | ||
586 | pr_debug(" link addr: %x", | ||
587 | sei_area->fla); | ||
588 | res_data.fla_mask = 0xff00; | ||
589 | } | ||
590 | } | ||
591 | ret = s390_process_res_acc(&res_data); | ||
592 | pr_debug("\n\n"); | ||
593 | put_device(dev); | ||
594 | break; | ||
595 | |||
596 | default: /* other stuff */ | ||
597 | CIO_CRW_EVENT(4, "chsc_process_crw: event %d\n", | ||
598 | sei_area->cc); | ||
599 | break; | 590 | break; |
600 | } | 591 | } |
601 | } while (sei_area->flags & 0x80); | 592 | } while (sei_area->flags & 0x80); |
593 | |||
602 | return ret; | 594 | return ret; |
603 | } | 595 | } |
604 | 596 | ||