diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r-- | drivers/s390/cio/chsc.c | 270 |
1 files changed, 130 insertions, 140 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index cbab8d2ce5cf..6f05a44e3817 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -93,7 +93,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) | |||
93 | u16 sch; /* subchannel */ | 93 | u16 sch; /* subchannel */ |
94 | u8 chpid[8]; /* chpids 0-7 */ | 94 | u8 chpid[8]; /* chpids 0-7 */ |
95 | u16 fla[8]; /* full link addresses 0-7 */ | 95 | u16 fla[8]; /* full link addresses 0-7 */ |
96 | } *ssd_area; | 96 | } __attribute__ ((packed)) *ssd_area; |
97 | 97 | ||
98 | ssd_area = page; | 98 | ssd_area = page; |
99 | 99 | ||
@@ -277,7 +277,7 @@ out_unreg: | |||
277 | return 0; | 277 | return 0; |
278 | } | 278 | } |
279 | 279 | ||
280 | static inline void | 280 | static void |
281 | s390_set_chpid_offline( __u8 chpid) | 281 | s390_set_chpid_offline( __u8 chpid) |
282 | { | 282 | { |
283 | char dbf_txt[15]; | 283 | char dbf_txt[15]; |
@@ -338,7 +338,7 @@ s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch) | |||
338 | return 0x80 >> chp; | 338 | return 0x80 >> chp; |
339 | } | 339 | } |
340 | 340 | ||
341 | static inline int | 341 | static int |
342 | s390_process_res_acc_new_sch(struct subchannel_id schid) | 342 | s390_process_res_acc_new_sch(struct subchannel_id schid) |
343 | { | 343 | { |
344 | struct schib schib; | 344 | struct schib schib; |
@@ -444,7 +444,7 @@ __get_chpid_from_lir(void *data) | |||
444 | u32 andesc[28]; | 444 | u32 andesc[28]; |
445 | /* incident-specific information */ | 445 | /* incident-specific information */ |
446 | u32 isinfo[28]; | 446 | u32 isinfo[28]; |
447 | } *lir; | 447 | } __attribute__ ((packed)) *lir; |
448 | 448 | ||
449 | lir = data; | 449 | lir = data; |
450 | if (!(lir->iq&0x80)) | 450 | if (!(lir->iq&0x80)) |
@@ -461,154 +461,146 @@ __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 | } *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 | ||
605 | static inline int | 597 | static int |
606 | __chp_add_new_sch(struct subchannel_id schid) | 598 | __chp_add_new_sch(struct subchannel_id schid) |
607 | { | 599 | { |
608 | struct schib schib; | 600 | struct schib schib; |
609 | int ret; | 601 | int ret; |
610 | 602 | ||
611 | if (stsch(schid, &schib)) | 603 | if (stsch_err(schid, &schib)) |
612 | /* We're through */ | 604 | /* We're through */ |
613 | return need_rescan ? -EAGAIN : -ENXIO; | 605 | return need_rescan ? -EAGAIN : -ENXIO; |
614 | 606 | ||
@@ -709,7 +701,7 @@ chp_process_crw(int chpid, int on) | |||
709 | return chp_add(chpid); | 701 | return chp_add(chpid); |
710 | } | 702 | } |
711 | 703 | ||
712 | static inline int check_for_io_on_path(struct subchannel *sch, int index) | 704 | static int check_for_io_on_path(struct subchannel *sch, int index) |
713 | { | 705 | { |
714 | int cc; | 706 | int cc; |
715 | 707 | ||
@@ -741,7 +733,7 @@ static void terminate_internal_io(struct subchannel *sch) | |||
741 | sch->driver->termination(&sch->dev); | 733 | sch->driver->termination(&sch->dev); |
742 | } | 734 | } |
743 | 735 | ||
744 | static inline void | 736 | static void |
745 | __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) | 737 | __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) |
746 | { | 738 | { |
747 | int chp, old_lpm; | 739 | int chp, old_lpm; |
@@ -967,8 +959,8 @@ static struct bin_attribute chp_measurement_attr = { | |||
967 | static void | 959 | static void |
968 | chsc_remove_chp_cmg_attr(struct channel_path *chp) | 960 | chsc_remove_chp_cmg_attr(struct channel_path *chp) |
969 | { | 961 | { |
970 | sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_chars_attr); | 962 | device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); |
971 | sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_attr); | 963 | device_remove_bin_file(&chp->dev, &chp_measurement_attr); |
972 | } | 964 | } |
973 | 965 | ||
974 | static int | 966 | static int |
@@ -976,14 +968,12 @@ chsc_add_chp_cmg_attr(struct channel_path *chp) | |||
976 | { | 968 | { |
977 | int ret; | 969 | int ret; |
978 | 970 | ||
979 | ret = sysfs_create_bin_file(&chp->dev.kobj, | 971 | ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); |
980 | &chp_measurement_chars_attr); | ||
981 | if (ret) | 972 | if (ret) |
982 | return ret; | 973 | return ret; |
983 | ret = sysfs_create_bin_file(&chp->dev.kobj, &chp_measurement_attr); | 974 | ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); |
984 | if (ret) | 975 | if (ret) |
985 | sysfs_remove_bin_file(&chp->dev.kobj, | 976 | device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); |
986 | &chp_measurement_chars_attr); | ||
987 | return ret; | 977 | return ret; |
988 | } | 978 | } |
989 | 979 | ||
@@ -1042,7 +1032,7 @@ __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
1042 | u32 : 4; | 1032 | u32 : 4; |
1043 | u32 fmt : 4; | 1033 | u32 fmt : 4; |
1044 | u32 : 16; | 1034 | u32 : 16; |
1045 | } *secm_area; | 1035 | } __attribute__ ((packed)) *secm_area; |
1046 | int ret, ccode; | 1036 | int ret, ccode; |
1047 | 1037 | ||
1048 | secm_area = page; | 1038 | secm_area = page; |
@@ -1253,7 +1243,7 @@ chsc_determine_channel_path_description(int chpid, | |||
1253 | struct chsc_header response; | 1243 | struct chsc_header response; |
1254 | u32 zeroes2; | 1244 | u32 zeroes2; |
1255 | struct channel_path_desc desc; | 1245 | struct channel_path_desc desc; |
1256 | } *scpd_area; | 1246 | } __attribute__ ((packed)) *scpd_area; |
1257 | 1247 | ||
1258 | scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 1248 | scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
1259 | if (!scpd_area) | 1249 | if (!scpd_area) |
@@ -1350,7 +1340,7 @@ chsc_get_channel_measurement_chars(struct channel_path *chp) | |||
1350 | u32 cmg : 8; | 1340 | u32 cmg : 8; |
1351 | u32 zeroes3; | 1341 | u32 zeroes3; |
1352 | u32 data[NR_MEASUREMENT_CHARS]; | 1342 | u32 data[NR_MEASUREMENT_CHARS]; |
1353 | } *scmc_area; | 1343 | } __attribute__ ((packed)) *scmc_area; |
1354 | 1344 | ||
1355 | scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 1345 | scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
1356 | if (!scmc_area) | 1346 | if (!scmc_area) |
@@ -1517,7 +1507,7 @@ chsc_enable_facility(int operation_code) | |||
1517 | u32 reserved5:4; | 1507 | u32 reserved5:4; |
1518 | u32 format2:4; | 1508 | u32 format2:4; |
1519 | u32 reserved6:24; | 1509 | u32 reserved6:24; |
1520 | } *sda_area; | 1510 | } __attribute__ ((packed)) *sda_area; |
1521 | 1511 | ||
1522 | sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); | 1512 | sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); |
1523 | if (!sda_area) | 1513 | if (!sda_area) |
@@ -1569,7 +1559,7 @@ chsc_determine_css_characteristics(void) | |||
1569 | u32 reserved4; | 1559 | u32 reserved4; |
1570 | u32 general_char[510]; | 1560 | u32 general_char[510]; |
1571 | u32 chsc_char[518]; | 1561 | u32 chsc_char[518]; |
1572 | } *scsc_area; | 1562 | } __attribute__ ((packed)) *scsc_area; |
1573 | 1563 | ||
1574 | scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 1564 | scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
1575 | if (!scsc_area) { | 1565 | if (!scsc_area) { |