diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r-- | drivers/s390/cio/chsc.c | 291 |
1 files changed, 134 insertions, 157 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 4cbb1a6ca33c..1aaddea673e0 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * drivers/s390/cio/chsc.c | 2 | * drivers/s390/cio/chsc.c |
3 | * S/390 common I/O routines -- channel subsystem call | 3 | * S/390 common I/O routines -- channel subsystem call |
4 | * | 4 | * |
5 | * Copyright IBM Corp. 1999,2008 | 5 | * Copyright IBM Corp. 1999,2010 |
6 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | 6 | * Author(s): Ingo Adlung (adlung@de.ibm.com) |
7 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 7 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
8 | * Arnd Bergmann (arndb@de.ibm.com) | 8 | * Arnd Bergmann (arndb@de.ibm.com) |
@@ -29,8 +29,8 @@ | |||
29 | #include "chsc.h" | 29 | #include "chsc.h" |
30 | 30 | ||
31 | static void *sei_page; | 31 | static void *sei_page; |
32 | static DEFINE_SPINLOCK(siosl_lock); | 32 | static void *chsc_page; |
33 | static DEFINE_SPINLOCK(sda_lock); | 33 | static DEFINE_SPINLOCK(chsc_page_lock); |
34 | 34 | ||
35 | /** | 35 | /** |
36 | * chsc_error_from_response() - convert a chsc response to an error | 36 | * chsc_error_from_response() - convert a chsc response to an error |
@@ -85,17 +85,15 @@ struct chsc_ssd_area { | |||
85 | 85 | ||
86 | int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) | 86 | int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) |
87 | { | 87 | { |
88 | unsigned long page; | ||
89 | struct chsc_ssd_area *ssd_area; | 88 | struct chsc_ssd_area *ssd_area; |
90 | int ccode; | 89 | int ccode; |
91 | int ret; | 90 | int ret; |
92 | int i; | 91 | int i; |
93 | int mask; | 92 | int mask; |
94 | 93 | ||
95 | page = get_zeroed_page(GFP_KERNEL | GFP_DMA); | 94 | spin_lock_irq(&chsc_page_lock); |
96 | if (!page) | 95 | memset(chsc_page, 0, PAGE_SIZE); |
97 | return -ENOMEM; | 96 | ssd_area = chsc_page; |
98 | ssd_area = (struct chsc_ssd_area *) page; | ||
99 | ssd_area->request.length = 0x0010; | 97 | ssd_area->request.length = 0x0010; |
100 | ssd_area->request.code = 0x0004; | 98 | ssd_area->request.code = 0x0004; |
101 | ssd_area->ssid = schid.ssid; | 99 | ssd_area->ssid = schid.ssid; |
@@ -106,25 +104,25 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) | |||
106 | /* Check response. */ | 104 | /* Check response. */ |
107 | if (ccode > 0) { | 105 | if (ccode > 0) { |
108 | ret = (ccode == 3) ? -ENODEV : -EBUSY; | 106 | ret = (ccode == 3) ? -ENODEV : -EBUSY; |
109 | goto out_free; | 107 | goto out; |
110 | } | 108 | } |
111 | ret = chsc_error_from_response(ssd_area->response.code); | 109 | ret = chsc_error_from_response(ssd_area->response.code); |
112 | if (ret != 0) { | 110 | if (ret != 0) { |
113 | CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", | 111 | CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", |
114 | schid.ssid, schid.sch_no, | 112 | schid.ssid, schid.sch_no, |
115 | ssd_area->response.code); | 113 | ssd_area->response.code); |
116 | goto out_free; | 114 | goto out; |
117 | } | 115 | } |
118 | if (!ssd_area->sch_valid) { | 116 | if (!ssd_area->sch_valid) { |
119 | ret = -ENODEV; | 117 | ret = -ENODEV; |
120 | goto out_free; | 118 | goto out; |
121 | } | 119 | } |
122 | /* Copy data */ | 120 | /* Copy data */ |
123 | ret = 0; | 121 | ret = 0; |
124 | memset(ssd, 0, sizeof(struct chsc_ssd_info)); | 122 | memset(ssd, 0, sizeof(struct chsc_ssd_info)); |
125 | if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && | 123 | if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && |
126 | (ssd_area->st != SUBCHANNEL_TYPE_MSG)) | 124 | (ssd_area->st != SUBCHANNEL_TYPE_MSG)) |
127 | goto out_free; | 125 | goto out; |
128 | ssd->path_mask = ssd_area->path_mask; | 126 | ssd->path_mask = ssd_area->path_mask; |
129 | ssd->fla_valid_mask = ssd_area->fla_valid_mask; | 127 | ssd->fla_valid_mask = ssd_area->fla_valid_mask; |
130 | for (i = 0; i < 8; i++) { | 128 | for (i = 0; i < 8; i++) { |
@@ -136,8 +134,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) | |||
136 | if (ssd_area->fla_valid_mask & mask) | 134 | if (ssd_area->fla_valid_mask & mask) |
137 | ssd->fla[i] = ssd_area->fla[i]; | 135 | ssd->fla[i] = ssd_area->fla[i]; |
138 | } | 136 | } |
139 | out_free: | 137 | out: |
140 | free_page(page); | 138 | spin_unlock_irq(&chsc_page_lock); |
141 | return ret; | 139 | return ret; |
142 | } | 140 | } |
143 | 141 | ||
@@ -497,6 +495,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) | |||
497 | */ | 495 | */ |
498 | int chsc_chp_vary(struct chp_id chpid, int on) | 496 | int chsc_chp_vary(struct chp_id chpid, int on) |
499 | { | 497 | { |
498 | struct channel_path *chp = chpid_to_chp(chpid); | ||
500 | struct chp_link link; | 499 | struct chp_link link; |
501 | 500 | ||
502 | memset(&link, 0, sizeof(struct chp_link)); | 501 | memset(&link, 0, sizeof(struct chp_link)); |
@@ -506,11 +505,12 @@ int chsc_chp_vary(struct chp_id chpid, int on) | |||
506 | /* | 505 | /* |
507 | * Redo PathVerification on the devices the chpid connects to | 506 | * Redo PathVerification on the devices the chpid connects to |
508 | */ | 507 | */ |
509 | 508 | if (on) { | |
510 | if (on) | 509 | /* Try to update the channel path descritor. */ |
510 | chsc_determine_base_channel_path_desc(chpid, &chp->desc); | ||
511 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, | 511 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, |
512 | __s390_vary_chpid_on, &link); | 512 | __s390_vary_chpid_on, &link); |
513 | else | 513 | } else |
514 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, | 514 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, |
515 | NULL, &link); | 515 | NULL, &link); |
516 | 516 | ||
@@ -552,7 +552,7 @@ cleanup: | |||
552 | return ret; | 552 | return ret; |
553 | } | 553 | } |
554 | 554 | ||
555 | int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | 555 | int __chsc_do_secm(struct channel_subsystem *css, int enable) |
556 | { | 556 | { |
557 | struct { | 557 | struct { |
558 | struct chsc_header request; | 558 | struct chsc_header request; |
@@ -573,7 +573,9 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
573 | } __attribute__ ((packed)) *secm_area; | 573 | } __attribute__ ((packed)) *secm_area; |
574 | int ret, ccode; | 574 | int ret, ccode; |
575 | 575 | ||
576 | secm_area = page; | 576 | spin_lock_irq(&chsc_page_lock); |
577 | memset(chsc_page, 0, PAGE_SIZE); | ||
578 | secm_area = chsc_page; | ||
577 | secm_area->request.length = 0x0050; | 579 | secm_area->request.length = 0x0050; |
578 | secm_area->request.code = 0x0016; | 580 | secm_area->request.code = 0x0016; |
579 | 581 | ||
@@ -584,8 +586,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
584 | secm_area->operation_code = enable ? 0 : 1; | 586 | secm_area->operation_code = enable ? 0 : 1; |
585 | 587 | ||
586 | ccode = chsc(secm_area); | 588 | ccode = chsc(secm_area); |
587 | if (ccode > 0) | 589 | if (ccode > 0) { |
588 | return (ccode == 3) ? -ENODEV : -EBUSY; | 590 | ret = (ccode == 3) ? -ENODEV : -EBUSY; |
591 | goto out; | ||
592 | } | ||
589 | 593 | ||
590 | switch (secm_area->response.code) { | 594 | switch (secm_area->response.code) { |
591 | case 0x0102: | 595 | case 0x0102: |
@@ -598,37 +602,32 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
598 | if (ret != 0) | 602 | if (ret != 0) |
599 | CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", | 603 | CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", |
600 | secm_area->response.code); | 604 | secm_area->response.code); |
605 | out: | ||
606 | spin_unlock_irq(&chsc_page_lock); | ||
601 | return ret; | 607 | return ret; |
602 | } | 608 | } |
603 | 609 | ||
604 | int | 610 | int |
605 | chsc_secm(struct channel_subsystem *css, int enable) | 611 | chsc_secm(struct channel_subsystem *css, int enable) |
606 | { | 612 | { |
607 | void *secm_area; | ||
608 | int ret; | 613 | int ret; |
609 | 614 | ||
610 | secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
611 | if (!secm_area) | ||
612 | return -ENOMEM; | ||
613 | |||
614 | if (enable && !css->cm_enabled) { | 615 | if (enable && !css->cm_enabled) { |
615 | css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 616 | css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
616 | css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 617 | css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
617 | if (!css->cub_addr1 || !css->cub_addr2) { | 618 | if (!css->cub_addr1 || !css->cub_addr2) { |
618 | free_page((unsigned long)css->cub_addr1); | 619 | free_page((unsigned long)css->cub_addr1); |
619 | free_page((unsigned long)css->cub_addr2); | 620 | free_page((unsigned long)css->cub_addr2); |
620 | free_page((unsigned long)secm_area); | ||
621 | return -ENOMEM; | 621 | return -ENOMEM; |
622 | } | 622 | } |
623 | } | 623 | } |
624 | ret = __chsc_do_secm(css, enable, secm_area); | 624 | ret = __chsc_do_secm(css, enable); |
625 | if (!ret) { | 625 | if (!ret) { |
626 | css->cm_enabled = enable; | 626 | css->cm_enabled = enable; |
627 | if (css->cm_enabled) { | 627 | if (css->cm_enabled) { |
628 | ret = chsc_add_cmg_attr(css); | 628 | ret = chsc_add_cmg_attr(css); |
629 | if (ret) { | 629 | if (ret) { |
630 | memset(secm_area, 0, PAGE_SIZE); | 630 | __chsc_do_secm(css, 0); |
631 | __chsc_do_secm(css, 0, secm_area); | ||
632 | css->cm_enabled = 0; | 631 | css->cm_enabled = 0; |
633 | } | 632 | } |
634 | } else | 633 | } else |
@@ -638,44 +637,24 @@ chsc_secm(struct channel_subsystem *css, int enable) | |||
638 | free_page((unsigned long)css->cub_addr1); | 637 | free_page((unsigned long)css->cub_addr1); |
639 | free_page((unsigned long)css->cub_addr2); | 638 | free_page((unsigned long)css->cub_addr2); |
640 | } | 639 | } |
641 | free_page((unsigned long)secm_area); | ||
642 | return ret; | 640 | return ret; |
643 | } | 641 | } |
644 | 642 | ||
645 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | 643 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, |
646 | int c, int m, | 644 | int c, int m, void *page) |
647 | struct chsc_response_struct *resp) | ||
648 | { | 645 | { |
646 | struct chsc_scpd *scpd_area; | ||
649 | int ccode, ret; | 647 | int ccode, ret; |
650 | 648 | ||
651 | struct { | ||
652 | struct chsc_header request; | ||
653 | u32 : 2; | ||
654 | u32 m : 1; | ||
655 | u32 c : 1; | ||
656 | u32 fmt : 4; | ||
657 | u32 cssid : 8; | ||
658 | u32 : 4; | ||
659 | u32 rfmt : 4; | ||
660 | u32 first_chpid : 8; | ||
661 | u32 : 24; | ||
662 | u32 last_chpid : 8; | ||
663 | u32 zeroes1; | ||
664 | struct chsc_header response; | ||
665 | u8 data[PAGE_SIZE - 20]; | ||
666 | } __attribute__ ((packed)) *scpd_area; | ||
667 | |||
668 | if ((rfmt == 1) && !css_general_characteristics.fcs) | 649 | if ((rfmt == 1) && !css_general_characteristics.fcs) |
669 | return -EINVAL; | 650 | return -EINVAL; |
670 | if ((rfmt == 2) && !css_general_characteristics.cib) | 651 | if ((rfmt == 2) && !css_general_characteristics.cib) |
671 | return -EINVAL; | 652 | return -EINVAL; |
672 | scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
673 | if (!scpd_area) | ||
674 | return -ENOMEM; | ||
675 | 653 | ||
654 | memset(page, 0, PAGE_SIZE); | ||
655 | scpd_area = page; | ||
676 | scpd_area->request.length = 0x0010; | 656 | scpd_area->request.length = 0x0010; |
677 | scpd_area->request.code = 0x0002; | 657 | scpd_area->request.code = 0x0002; |
678 | |||
679 | scpd_area->cssid = chpid.cssid; | 658 | scpd_area->cssid = chpid.cssid; |
680 | scpd_area->first_chpid = chpid.id; | 659 | scpd_area->first_chpid = chpid.id; |
681 | scpd_area->last_chpid = chpid.id; | 660 | scpd_area->last_chpid = chpid.id; |
@@ -685,20 +664,13 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | |||
685 | scpd_area->rfmt = rfmt; | 664 | scpd_area->rfmt = rfmt; |
686 | 665 | ||
687 | ccode = chsc(scpd_area); | 666 | ccode = chsc(scpd_area); |
688 | if (ccode > 0) { | 667 | if (ccode > 0) |
689 | ret = (ccode == 3) ? -ENODEV : -EBUSY; | 668 | return (ccode == 3) ? -ENODEV : -EBUSY; |
690 | goto out; | ||
691 | } | ||
692 | 669 | ||
693 | ret = chsc_error_from_response(scpd_area->response.code); | 670 | ret = chsc_error_from_response(scpd_area->response.code); |
694 | if (ret == 0) | 671 | if (ret) |
695 | /* Success. */ | ||
696 | memcpy(resp, &scpd_area->response, scpd_area->response.length); | ||
697 | else | ||
698 | CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", | 672 | CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", |
699 | scpd_area->response.code); | 673 | scpd_area->response.code); |
700 | out: | ||
701 | free_page((unsigned long)scpd_area); | ||
702 | return ret; | 674 | return ret; |
703 | } | 675 | } |
704 | EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); | 676 | EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); |
@@ -707,17 +679,19 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid, | |||
707 | struct channel_path_desc *desc) | 679 | struct channel_path_desc *desc) |
708 | { | 680 | { |
709 | struct chsc_response_struct *chsc_resp; | 681 | struct chsc_response_struct *chsc_resp; |
682 | struct chsc_scpd *scpd_area; | ||
683 | unsigned long flags; | ||
710 | int ret; | 684 | int ret; |
711 | 685 | ||
712 | chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); | 686 | spin_lock_irqsave(&chsc_page_lock, flags); |
713 | if (!chsc_resp) | 687 | scpd_area = chsc_page; |
714 | return -ENOMEM; | 688 | ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area); |
715 | ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp); | ||
716 | if (ret) | 689 | if (ret) |
717 | goto out_free; | 690 | goto out; |
691 | chsc_resp = (void *)&scpd_area->response; | ||
718 | memcpy(desc, &chsc_resp->data, sizeof(*desc)); | 692 | memcpy(desc, &chsc_resp->data, sizeof(*desc)); |
719 | out_free: | 693 | out: |
720 | kfree(chsc_resp); | 694 | spin_unlock_irqrestore(&chsc_page_lock, flags); |
721 | return ret; | 695 | return ret; |
722 | } | 696 | } |
723 | 697 | ||
@@ -725,33 +699,22 @@ static void | |||
725 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | 699 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, |
726 | struct cmg_chars *chars) | 700 | struct cmg_chars *chars) |
727 | { | 701 | { |
728 | switch (chp->cmg) { | 702 | struct cmg_chars *cmg_chars; |
729 | case 2: | 703 | int i, mask; |
730 | case 3: | 704 | |
731 | chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), | 705 | cmg_chars = chp->cmg_chars; |
732 | GFP_KERNEL); | 706 | for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { |
733 | if (chp->cmg_chars) { | 707 | mask = 0x80 >> (i + 3); |
734 | int i, mask; | 708 | if (cmcv & mask) |
735 | struct cmg_chars *cmg_chars; | 709 | cmg_chars->values[i] = chars->values[i]; |
736 | 710 | else | |
737 | cmg_chars = chp->cmg_chars; | 711 | cmg_chars->values[i] = 0; |
738 | for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { | ||
739 | mask = 0x80 >> (i + 3); | ||
740 | if (cmcv & mask) | ||
741 | cmg_chars->values[i] = chars->values[i]; | ||
742 | else | ||
743 | cmg_chars->values[i] = 0; | ||
744 | } | ||
745 | } | ||
746 | break; | ||
747 | default: | ||
748 | /* No cmg-dependent data. */ | ||
749 | break; | ||
750 | } | 712 | } |
751 | } | 713 | } |
752 | 714 | ||
753 | int chsc_get_channel_measurement_chars(struct channel_path *chp) | 715 | int chsc_get_channel_measurement_chars(struct channel_path *chp) |
754 | { | 716 | { |
717 | struct cmg_chars *cmg_chars; | ||
755 | int ccode, ret; | 718 | int ccode, ret; |
756 | 719 | ||
757 | struct { | 720 | struct { |
@@ -775,13 +738,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) | |||
775 | u32 data[NR_MEASUREMENT_CHARS]; | 738 | u32 data[NR_MEASUREMENT_CHARS]; |
776 | } __attribute__ ((packed)) *scmc_area; | 739 | } __attribute__ ((packed)) *scmc_area; |
777 | 740 | ||
778 | scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 741 | chp->cmg_chars = NULL; |
779 | if (!scmc_area) | 742 | cmg_chars = kmalloc(sizeof(*cmg_chars), GFP_KERNEL); |
743 | if (!cmg_chars) | ||
780 | return -ENOMEM; | 744 | return -ENOMEM; |
781 | 745 | ||
746 | spin_lock_irq(&chsc_page_lock); | ||
747 | memset(chsc_page, 0, PAGE_SIZE); | ||
748 | scmc_area = chsc_page; | ||
782 | scmc_area->request.length = 0x0010; | 749 | scmc_area->request.length = 0x0010; |
783 | scmc_area->request.code = 0x0022; | 750 | scmc_area->request.code = 0x0022; |
784 | |||
785 | scmc_area->first_chpid = chp->chpid.id; | 751 | scmc_area->first_chpid = chp->chpid.id; |
786 | scmc_area->last_chpid = chp->chpid.id; | 752 | scmc_area->last_chpid = chp->chpid.id; |
787 | 753 | ||
@@ -792,53 +758,65 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) | |||
792 | } | 758 | } |
793 | 759 | ||
794 | ret = chsc_error_from_response(scmc_area->response.code); | 760 | ret = chsc_error_from_response(scmc_area->response.code); |
795 | if (ret == 0) { | 761 | if (ret) { |
796 | /* Success. */ | ||
797 | if (!scmc_area->not_valid) { | ||
798 | chp->cmg = scmc_area->cmg; | ||
799 | chp->shared = scmc_area->shared; | ||
800 | chsc_initialize_cmg_chars(chp, scmc_area->cmcv, | ||
801 | (struct cmg_chars *) | ||
802 | &scmc_area->data); | ||
803 | } else { | ||
804 | chp->cmg = -1; | ||
805 | chp->shared = -1; | ||
806 | } | ||
807 | } else { | ||
808 | CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n", | 762 | CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n", |
809 | scmc_area->response.code); | 763 | scmc_area->response.code); |
764 | goto out; | ||
765 | } | ||
766 | if (scmc_area->not_valid) { | ||
767 | chp->cmg = -1; | ||
768 | chp->shared = -1; | ||
769 | goto out; | ||
810 | } | 770 | } |
771 | chp->cmg = scmc_area->cmg; | ||
772 | chp->shared = scmc_area->shared; | ||
773 | if (chp->cmg != 2 && chp->cmg != 3) { | ||
774 | /* No cmg-dependent data. */ | ||
775 | goto out; | ||
776 | } | ||
777 | chp->cmg_chars = cmg_chars; | ||
778 | chsc_initialize_cmg_chars(chp, scmc_area->cmcv, | ||
779 | (struct cmg_chars *) &scmc_area->data); | ||
811 | out: | 780 | out: |
812 | free_page((unsigned long)scmc_area); | 781 | spin_unlock_irq(&chsc_page_lock); |
782 | if (!chp->cmg_chars) | ||
783 | kfree(cmg_chars); | ||
784 | |||
813 | return ret; | 785 | return ret; |
814 | } | 786 | } |
815 | 787 | ||
816 | int __init chsc_alloc_sei_area(void) | 788 | int __init chsc_init(void) |
817 | { | 789 | { |
818 | int ret; | 790 | int ret; |
819 | 791 | ||
820 | sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 792 | sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
821 | if (!sei_page) { | 793 | chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
822 | CIO_MSG_EVENT(0, "Can't allocate page for processing of " | 794 | if (!sei_page || !chsc_page) { |
823 | "chsc machine checks!\n"); | 795 | ret = -ENOMEM; |
824 | return -ENOMEM; | 796 | goto out_err; |
825 | } | 797 | } |
826 | ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); | 798 | ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); |
827 | if (ret) | 799 | if (ret) |
828 | kfree(sei_page); | 800 | goto out_err; |
801 | return ret; | ||
802 | out_err: | ||
803 | free_page((unsigned long)chsc_page); | ||
804 | free_page((unsigned long)sei_page); | ||
829 | return ret; | 805 | return ret; |
830 | } | 806 | } |
831 | 807 | ||
832 | void __init chsc_free_sei_area(void) | 808 | void __init chsc_init_cleanup(void) |
833 | { | 809 | { |
834 | crw_unregister_handler(CRW_RSC_CSS); | 810 | crw_unregister_handler(CRW_RSC_CSS); |
835 | kfree(sei_page); | 811 | free_page((unsigned long)chsc_page); |
812 | free_page((unsigned long)sei_page); | ||
836 | } | 813 | } |
837 | 814 | ||
838 | int chsc_enable_facility(int operation_code) | 815 | int chsc_enable_facility(int operation_code) |
839 | { | 816 | { |
817 | unsigned long flags; | ||
840 | int ret; | 818 | int ret; |
841 | static struct { | 819 | struct { |
842 | struct chsc_header request; | 820 | struct chsc_header request; |
843 | u8 reserved1:4; | 821 | u8 reserved1:4; |
844 | u8 format:4; | 822 | u8 format:4; |
@@ -851,32 +829,33 @@ int chsc_enable_facility(int operation_code) | |||
851 | u32 reserved5:4; | 829 | u32 reserved5:4; |
852 | u32 format2:4; | 830 | u32 format2:4; |
853 | u32 reserved6:24; | 831 | u32 reserved6:24; |
854 | } __attribute__ ((packed, aligned(4096))) sda_area; | 832 | } __attribute__ ((packed)) *sda_area; |
855 | 833 | ||
856 | spin_lock(&sda_lock); | 834 | spin_lock_irqsave(&chsc_page_lock, flags); |
857 | memset(&sda_area, 0, sizeof(sda_area)); | 835 | memset(chsc_page, 0, PAGE_SIZE); |
858 | sda_area.request.length = 0x0400; | 836 | sda_area = chsc_page; |
859 | sda_area.request.code = 0x0031; | 837 | sda_area->request.length = 0x0400; |
860 | sda_area.operation_code = operation_code; | 838 | sda_area->request.code = 0x0031; |
839 | sda_area->operation_code = operation_code; | ||
861 | 840 | ||
862 | ret = chsc(&sda_area); | 841 | ret = chsc(sda_area); |
863 | if (ret > 0) { | 842 | if (ret > 0) { |
864 | ret = (ret == 3) ? -ENODEV : -EBUSY; | 843 | ret = (ret == 3) ? -ENODEV : -EBUSY; |
865 | goto out; | 844 | goto out; |
866 | } | 845 | } |
867 | 846 | ||
868 | switch (sda_area.response.code) { | 847 | switch (sda_area->response.code) { |
869 | case 0x0101: | 848 | case 0x0101: |
870 | ret = -EOPNOTSUPP; | 849 | ret = -EOPNOTSUPP; |
871 | break; | 850 | break; |
872 | default: | 851 | default: |
873 | ret = chsc_error_from_response(sda_area.response.code); | 852 | ret = chsc_error_from_response(sda_area->response.code); |
874 | } | 853 | } |
875 | if (ret != 0) | 854 | if (ret != 0) |
876 | CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", | 855 | CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", |
877 | operation_code, sda_area.response.code); | 856 | operation_code, sda_area->response.code); |
878 | out: | 857 | out: |
879 | spin_unlock(&sda_lock); | 858 | spin_unlock_irqrestore(&chsc_page_lock, flags); |
880 | return ret; | 859 | return ret; |
881 | } | 860 | } |
882 | 861 | ||
@@ -895,13 +874,12 @@ chsc_determine_css_characteristics(void) | |||
895 | struct chsc_header response; | 874 | struct chsc_header response; |
896 | u32 reserved4; | 875 | u32 reserved4; |
897 | u32 general_char[510]; | 876 | u32 general_char[510]; |
898 | u32 chsc_char[518]; | 877 | u32 chsc_char[508]; |
899 | } __attribute__ ((packed)) *scsc_area; | 878 | } __attribute__ ((packed)) *scsc_area; |
900 | 879 | ||
901 | scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 880 | spin_lock_irq(&chsc_page_lock); |
902 | if (!scsc_area) | 881 | memset(chsc_page, 0, PAGE_SIZE); |
903 | return -ENOMEM; | 882 | scsc_area = chsc_page; |
904 | |||
905 | scsc_area->request.length = 0x0010; | 883 | scsc_area->request.length = 0x0010; |
906 | scsc_area->request.code = 0x0010; | 884 | scsc_area->request.code = 0x0010; |
907 | 885 | ||
@@ -921,7 +899,7 @@ chsc_determine_css_characteristics(void) | |||
921 | CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", | 899 | CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", |
922 | scsc_area->response.code); | 900 | scsc_area->response.code); |
923 | exit: | 901 | exit: |
924 | free_page ((unsigned long) scsc_area); | 902 | spin_unlock_irq(&chsc_page_lock); |
925 | return result; | 903 | return result; |
926 | } | 904 | } |
927 | 905 | ||
@@ -976,29 +954,29 @@ int chsc_sstpi(void *page, void *result, size_t size) | |||
976 | return (rr->response.code == 0x0001) ? 0 : -EIO; | 954 | return (rr->response.code == 0x0001) ? 0 : -EIO; |
977 | } | 955 | } |
978 | 956 | ||
979 | static struct { | ||
980 | struct chsc_header request; | ||
981 | u32 word1; | ||
982 | struct subchannel_id sid; | ||
983 | u32 word3; | ||
984 | struct chsc_header response; | ||
985 | u32 word[11]; | ||
986 | } __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE))); | ||
987 | |||
988 | int chsc_siosl(struct subchannel_id schid) | 957 | int chsc_siosl(struct subchannel_id schid) |
989 | { | 958 | { |
959 | struct { | ||
960 | struct chsc_header request; | ||
961 | u32 word1; | ||
962 | struct subchannel_id sid; | ||
963 | u32 word3; | ||
964 | struct chsc_header response; | ||
965 | u32 word[11]; | ||
966 | } __attribute__ ((packed)) *siosl_area; | ||
990 | unsigned long flags; | 967 | unsigned long flags; |
991 | int ccode; | 968 | int ccode; |
992 | int rc; | 969 | int rc; |
993 | 970 | ||
994 | spin_lock_irqsave(&siosl_lock, flags); | 971 | spin_lock_irqsave(&chsc_page_lock, flags); |
995 | memset(&siosl_area, 0, sizeof(siosl_area)); | 972 | memset(chsc_page, 0, PAGE_SIZE); |
996 | siosl_area.request.length = 0x0010; | 973 | siosl_area = chsc_page; |
997 | siosl_area.request.code = 0x0046; | 974 | siosl_area->request.length = 0x0010; |
998 | siosl_area.word1 = 0x80000000; | 975 | siosl_area->request.code = 0x0046; |
999 | siosl_area.sid = schid; | 976 | siosl_area->word1 = 0x80000000; |
977 | siosl_area->sid = schid; | ||
1000 | 978 | ||
1001 | ccode = chsc(&siosl_area); | 979 | ccode = chsc(siosl_area); |
1002 | if (ccode > 0) { | 980 | if (ccode > 0) { |
1003 | if (ccode == 3) | 981 | if (ccode == 3) |
1004 | rc = -ENODEV; | 982 | rc = -ENODEV; |
@@ -1008,17 +986,16 @@ int chsc_siosl(struct subchannel_id schid) | |||
1008 | schid.ssid, schid.sch_no, ccode); | 986 | schid.ssid, schid.sch_no, ccode); |
1009 | goto out; | 987 | goto out; |
1010 | } | 988 | } |
1011 | rc = chsc_error_from_response(siosl_area.response.code); | 989 | rc = chsc_error_from_response(siosl_area->response.code); |
1012 | if (rc) | 990 | if (rc) |
1013 | CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", | 991 | CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", |
1014 | schid.ssid, schid.sch_no, | 992 | schid.ssid, schid.sch_no, |
1015 | siosl_area.response.code); | 993 | siosl_area->response.code); |
1016 | else | 994 | else |
1017 | CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", | 995 | CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", |
1018 | schid.ssid, schid.sch_no); | 996 | schid.ssid, schid.sch_no); |
1019 | out: | 997 | out: |
1020 | spin_unlock_irqrestore(&siosl_lock, flags); | 998 | spin_unlock_irqrestore(&chsc_page_lock, flags); |
1021 | |||
1022 | return rc; | 999 | return rc; |
1023 | } | 1000 | } |
1024 | EXPORT_SYMBOL_GPL(chsc_siosl); | 1001 | EXPORT_SYMBOL_GPL(chsc_siosl); |