diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r-- | drivers/s390/cio/chsc.c | 345 |
1 files changed, 187 insertions, 158 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 4cbb1a6ca33c..75c3f1f8fd43 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 | ||
@@ -328,6 +326,36 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | |||
328 | s390_process_res_acc(&link); | 326 | s390_process_res_acc(&link); |
329 | } | 327 | } |
330 | 328 | ||
329 | static void chsc_process_sei_chp_avail(struct chsc_sei_area *sei_area) | ||
330 | { | ||
331 | struct channel_path *chp; | ||
332 | struct chp_id chpid; | ||
333 | u8 *data; | ||
334 | int num; | ||
335 | |||
336 | CIO_CRW_EVENT(4, "chsc: channel path availability information\n"); | ||
337 | if (sei_area->rs != 0) | ||
338 | return; | ||
339 | data = sei_area->ccdf; | ||
340 | chp_id_init(&chpid); | ||
341 | for (num = 0; num <= __MAX_CHPID; num++) { | ||
342 | if (!chp_test_bit(data, num)) | ||
343 | continue; | ||
344 | chpid.id = num; | ||
345 | |||
346 | CIO_CRW_EVENT(4, "Update information for channel path " | ||
347 | "%x.%02x\n", chpid.cssid, chpid.id); | ||
348 | chp = chpid_to_chp(chpid); | ||
349 | if (!chp) { | ||
350 | chp_new(chpid); | ||
351 | continue; | ||
352 | } | ||
353 | mutex_lock(&chp->lock); | ||
354 | chsc_determine_base_channel_path_desc(chpid, &chp->desc); | ||
355 | mutex_unlock(&chp->lock); | ||
356 | } | ||
357 | } | ||
358 | |||
331 | struct chp_config_data { | 359 | struct chp_config_data { |
332 | u8 map[32]; | 360 | u8 map[32]; |
333 | u8 op; | 361 | u8 op; |
@@ -378,9 +406,12 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area) | |||
378 | case 1: /* link incident*/ | 406 | case 1: /* link incident*/ |
379 | chsc_process_sei_link_incident(sei_area); | 407 | chsc_process_sei_link_incident(sei_area); |
380 | break; | 408 | break; |
381 | case 2: /* i/o resource accessibiliy */ | 409 | case 2: /* i/o resource accessibility */ |
382 | chsc_process_sei_res_acc(sei_area); | 410 | chsc_process_sei_res_acc(sei_area); |
383 | break; | 411 | break; |
412 | case 7: /* channel-path-availability information */ | ||
413 | chsc_process_sei_chp_avail(sei_area); | ||
414 | break; | ||
384 | case 8: /* channel-path-configuration notification */ | 415 | case 8: /* channel-path-configuration notification */ |
385 | chsc_process_sei_chp_config(sei_area); | 416 | chsc_process_sei_chp_config(sei_area); |
386 | break; | 417 | break; |
@@ -497,6 +528,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) | |||
497 | */ | 528 | */ |
498 | int chsc_chp_vary(struct chp_id chpid, int on) | 529 | int chsc_chp_vary(struct chp_id chpid, int on) |
499 | { | 530 | { |
531 | struct channel_path *chp = chpid_to_chp(chpid); | ||
500 | struct chp_link link; | 532 | struct chp_link link; |
501 | 533 | ||
502 | memset(&link, 0, sizeof(struct chp_link)); | 534 | memset(&link, 0, sizeof(struct chp_link)); |
@@ -506,11 +538,12 @@ int chsc_chp_vary(struct chp_id chpid, int on) | |||
506 | /* | 538 | /* |
507 | * Redo PathVerification on the devices the chpid connects to | 539 | * Redo PathVerification on the devices the chpid connects to |
508 | */ | 540 | */ |
509 | 541 | if (on) { | |
510 | if (on) | 542 | /* Try to update the channel path descritor. */ |
543 | chsc_determine_base_channel_path_desc(chpid, &chp->desc); | ||
511 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, | 544 | for_each_subchannel_staged(s390_subchannel_vary_chpid_on, |
512 | __s390_vary_chpid_on, &link); | 545 | __s390_vary_chpid_on, &link); |
513 | else | 546 | } else |
514 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, | 547 | for_each_subchannel_staged(s390_subchannel_vary_chpid_off, |
515 | NULL, &link); | 548 | NULL, &link); |
516 | 549 | ||
@@ -552,7 +585,7 @@ cleanup: | |||
552 | return ret; | 585 | return ret; |
553 | } | 586 | } |
554 | 587 | ||
555 | int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | 588 | int __chsc_do_secm(struct channel_subsystem *css, int enable) |
556 | { | 589 | { |
557 | struct { | 590 | struct { |
558 | struct chsc_header request; | 591 | struct chsc_header request; |
@@ -573,7 +606,9 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
573 | } __attribute__ ((packed)) *secm_area; | 606 | } __attribute__ ((packed)) *secm_area; |
574 | int ret, ccode; | 607 | int ret, ccode; |
575 | 608 | ||
576 | secm_area = page; | 609 | spin_lock_irq(&chsc_page_lock); |
610 | memset(chsc_page, 0, PAGE_SIZE); | ||
611 | secm_area = chsc_page; | ||
577 | secm_area->request.length = 0x0050; | 612 | secm_area->request.length = 0x0050; |
578 | secm_area->request.code = 0x0016; | 613 | secm_area->request.code = 0x0016; |
579 | 614 | ||
@@ -584,8 +619,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
584 | secm_area->operation_code = enable ? 0 : 1; | 619 | secm_area->operation_code = enable ? 0 : 1; |
585 | 620 | ||
586 | ccode = chsc(secm_area); | 621 | ccode = chsc(secm_area); |
587 | if (ccode > 0) | 622 | if (ccode > 0) { |
588 | return (ccode == 3) ? -ENODEV : -EBUSY; | 623 | ret = (ccode == 3) ? -ENODEV : -EBUSY; |
624 | goto out; | ||
625 | } | ||
589 | 626 | ||
590 | switch (secm_area->response.code) { | 627 | switch (secm_area->response.code) { |
591 | case 0x0102: | 628 | case 0x0102: |
@@ -598,37 +635,32 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) | |||
598 | if (ret != 0) | 635 | if (ret != 0) |
599 | CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", | 636 | CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", |
600 | secm_area->response.code); | 637 | secm_area->response.code); |
638 | out: | ||
639 | spin_unlock_irq(&chsc_page_lock); | ||
601 | return ret; | 640 | return ret; |
602 | } | 641 | } |
603 | 642 | ||
604 | int | 643 | int |
605 | chsc_secm(struct channel_subsystem *css, int enable) | 644 | chsc_secm(struct channel_subsystem *css, int enable) |
606 | { | 645 | { |
607 | void *secm_area; | ||
608 | int ret; | 646 | int ret; |
609 | 647 | ||
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) { | 648 | if (enable && !css->cm_enabled) { |
615 | css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 649 | css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
616 | css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 650 | css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
617 | if (!css->cub_addr1 || !css->cub_addr2) { | 651 | if (!css->cub_addr1 || !css->cub_addr2) { |
618 | free_page((unsigned long)css->cub_addr1); | 652 | free_page((unsigned long)css->cub_addr1); |
619 | free_page((unsigned long)css->cub_addr2); | 653 | free_page((unsigned long)css->cub_addr2); |
620 | free_page((unsigned long)secm_area); | ||
621 | return -ENOMEM; | 654 | return -ENOMEM; |
622 | } | 655 | } |
623 | } | 656 | } |
624 | ret = __chsc_do_secm(css, enable, secm_area); | 657 | ret = __chsc_do_secm(css, enable); |
625 | if (!ret) { | 658 | if (!ret) { |
626 | css->cm_enabled = enable; | 659 | css->cm_enabled = enable; |
627 | if (css->cm_enabled) { | 660 | if (css->cm_enabled) { |
628 | ret = chsc_add_cmg_attr(css); | 661 | ret = chsc_add_cmg_attr(css); |
629 | if (ret) { | 662 | if (ret) { |
630 | memset(secm_area, 0, PAGE_SIZE); | 663 | __chsc_do_secm(css, 0); |
631 | __chsc_do_secm(css, 0, secm_area); | ||
632 | css->cm_enabled = 0; | 664 | css->cm_enabled = 0; |
633 | } | 665 | } |
634 | } else | 666 | } else |
@@ -638,44 +670,24 @@ chsc_secm(struct channel_subsystem *css, int enable) | |||
638 | free_page((unsigned long)css->cub_addr1); | 670 | free_page((unsigned long)css->cub_addr1); |
639 | free_page((unsigned long)css->cub_addr2); | 671 | free_page((unsigned long)css->cub_addr2); |
640 | } | 672 | } |
641 | free_page((unsigned long)secm_area); | ||
642 | return ret; | 673 | return ret; |
643 | } | 674 | } |
644 | 675 | ||
645 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | 676 | int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, |
646 | int c, int m, | 677 | int c, int m, void *page) |
647 | struct chsc_response_struct *resp) | ||
648 | { | 678 | { |
679 | struct chsc_scpd *scpd_area; | ||
649 | int ccode, ret; | 680 | int ccode, ret; |
650 | 681 | ||
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) | 682 | if ((rfmt == 1) && !css_general_characteristics.fcs) |
669 | return -EINVAL; | 683 | return -EINVAL; |
670 | if ((rfmt == 2) && !css_general_characteristics.cib) | 684 | if ((rfmt == 2) && !css_general_characteristics.cib) |
671 | return -EINVAL; | 685 | return -EINVAL; |
672 | scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
673 | if (!scpd_area) | ||
674 | return -ENOMEM; | ||
675 | 686 | ||
687 | memset(page, 0, PAGE_SIZE); | ||
688 | scpd_area = page; | ||
676 | scpd_area->request.length = 0x0010; | 689 | scpd_area->request.length = 0x0010; |
677 | scpd_area->request.code = 0x0002; | 690 | scpd_area->request.code = 0x0002; |
678 | |||
679 | scpd_area->cssid = chpid.cssid; | 691 | scpd_area->cssid = chpid.cssid; |
680 | scpd_area->first_chpid = chpid.id; | 692 | scpd_area->first_chpid = chpid.id; |
681 | scpd_area->last_chpid = chpid.id; | 693 | scpd_area->last_chpid = chpid.id; |
@@ -685,20 +697,13 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | |||
685 | scpd_area->rfmt = rfmt; | 697 | scpd_area->rfmt = rfmt; |
686 | 698 | ||
687 | ccode = chsc(scpd_area); | 699 | ccode = chsc(scpd_area); |
688 | if (ccode > 0) { | 700 | if (ccode > 0) |
689 | ret = (ccode == 3) ? -ENODEV : -EBUSY; | 701 | return (ccode == 3) ? -ENODEV : -EBUSY; |
690 | goto out; | ||
691 | } | ||
692 | 702 | ||
693 | ret = chsc_error_from_response(scpd_area->response.code); | 703 | ret = chsc_error_from_response(scpd_area->response.code); |
694 | if (ret == 0) | 704 | 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", | 705 | CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", |
699 | scpd_area->response.code); | 706 | scpd_area->response.code); |
700 | out: | ||
701 | free_page((unsigned long)scpd_area); | ||
702 | return ret; | 707 | return ret; |
703 | } | 708 | } |
704 | EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); | 709 | EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); |
@@ -707,17 +712,38 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid, | |||
707 | struct channel_path_desc *desc) | 712 | struct channel_path_desc *desc) |
708 | { | 713 | { |
709 | struct chsc_response_struct *chsc_resp; | 714 | struct chsc_response_struct *chsc_resp; |
715 | struct chsc_scpd *scpd_area; | ||
716 | unsigned long flags; | ||
710 | int ret; | 717 | int ret; |
711 | 718 | ||
712 | chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); | 719 | spin_lock_irqsave(&chsc_page_lock, flags); |
713 | if (!chsc_resp) | 720 | scpd_area = chsc_page; |
714 | return -ENOMEM; | 721 | 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); | 722 | if (ret) |
723 | goto out; | ||
724 | chsc_resp = (void *)&scpd_area->response; | ||
725 | memcpy(desc, &chsc_resp->data, sizeof(*desc)); | ||
726 | out: | ||
727 | spin_unlock_irqrestore(&chsc_page_lock, flags); | ||
728 | return ret; | ||
729 | } | ||
730 | |||
731 | int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, | ||
732 | struct channel_path_desc_fmt1 *desc) | ||
733 | { | ||
734 | struct chsc_response_struct *chsc_resp; | ||
735 | struct chsc_scpd *scpd_area; | ||
736 | int ret; | ||
737 | |||
738 | spin_lock_irq(&chsc_page_lock); | ||
739 | scpd_area = chsc_page; | ||
740 | ret = chsc_determine_channel_path_desc(chpid, 0, 0, 1, 0, scpd_area); | ||
716 | if (ret) | 741 | if (ret) |
717 | goto out_free; | 742 | goto out; |
743 | chsc_resp = (void *)&scpd_area->response; | ||
718 | memcpy(desc, &chsc_resp->data, sizeof(*desc)); | 744 | memcpy(desc, &chsc_resp->data, sizeof(*desc)); |
719 | out_free: | 745 | out: |
720 | kfree(chsc_resp); | 746 | spin_unlock_irq(&chsc_page_lock); |
721 | return ret; | 747 | return ret; |
722 | } | 748 | } |
723 | 749 | ||
@@ -725,33 +751,22 @@ static void | |||
725 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | 751 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, |
726 | struct cmg_chars *chars) | 752 | struct cmg_chars *chars) |
727 | { | 753 | { |
728 | switch (chp->cmg) { | 754 | struct cmg_chars *cmg_chars; |
729 | case 2: | 755 | int i, mask; |
730 | case 3: | 756 | |
731 | chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), | 757 | cmg_chars = chp->cmg_chars; |
732 | GFP_KERNEL); | 758 | for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { |
733 | if (chp->cmg_chars) { | 759 | mask = 0x80 >> (i + 3); |
734 | int i, mask; | 760 | if (cmcv & mask) |
735 | struct cmg_chars *cmg_chars; | 761 | cmg_chars->values[i] = chars->values[i]; |
736 | 762 | else | |
737 | cmg_chars = chp->cmg_chars; | 763 | 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 | } | 764 | } |
751 | } | 765 | } |
752 | 766 | ||
753 | int chsc_get_channel_measurement_chars(struct channel_path *chp) | 767 | int chsc_get_channel_measurement_chars(struct channel_path *chp) |
754 | { | 768 | { |
769 | struct cmg_chars *cmg_chars; | ||
755 | int ccode, ret; | 770 | int ccode, ret; |
756 | 771 | ||
757 | struct { | 772 | struct { |
@@ -775,13 +790,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) | |||
775 | u32 data[NR_MEASUREMENT_CHARS]; | 790 | u32 data[NR_MEASUREMENT_CHARS]; |
776 | } __attribute__ ((packed)) *scmc_area; | 791 | } __attribute__ ((packed)) *scmc_area; |
777 | 792 | ||
778 | scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 793 | chp->cmg_chars = NULL; |
779 | if (!scmc_area) | 794 | cmg_chars = kmalloc(sizeof(*cmg_chars), GFP_KERNEL); |
795 | if (!cmg_chars) | ||
780 | return -ENOMEM; | 796 | return -ENOMEM; |
781 | 797 | ||
798 | spin_lock_irq(&chsc_page_lock); | ||
799 | memset(chsc_page, 0, PAGE_SIZE); | ||
800 | scmc_area = chsc_page; | ||
782 | scmc_area->request.length = 0x0010; | 801 | scmc_area->request.length = 0x0010; |
783 | scmc_area->request.code = 0x0022; | 802 | scmc_area->request.code = 0x0022; |
784 | |||
785 | scmc_area->first_chpid = chp->chpid.id; | 803 | scmc_area->first_chpid = chp->chpid.id; |
786 | scmc_area->last_chpid = chp->chpid.id; | 804 | scmc_area->last_chpid = chp->chpid.id; |
787 | 805 | ||
@@ -792,53 +810,65 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) | |||
792 | } | 810 | } |
793 | 811 | ||
794 | ret = chsc_error_from_response(scmc_area->response.code); | 812 | ret = chsc_error_from_response(scmc_area->response.code); |
795 | if (ret == 0) { | 813 | 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", | 814 | CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n", |
809 | scmc_area->response.code); | 815 | scmc_area->response.code); |
816 | goto out; | ||
817 | } | ||
818 | if (scmc_area->not_valid) { | ||
819 | chp->cmg = -1; | ||
820 | chp->shared = -1; | ||
821 | goto out; | ||
810 | } | 822 | } |
823 | chp->cmg = scmc_area->cmg; | ||
824 | chp->shared = scmc_area->shared; | ||
825 | if (chp->cmg != 2 && chp->cmg != 3) { | ||
826 | /* No cmg-dependent data. */ | ||
827 | goto out; | ||
828 | } | ||
829 | chp->cmg_chars = cmg_chars; | ||
830 | chsc_initialize_cmg_chars(chp, scmc_area->cmcv, | ||
831 | (struct cmg_chars *) &scmc_area->data); | ||
811 | out: | 832 | out: |
812 | free_page((unsigned long)scmc_area); | 833 | spin_unlock_irq(&chsc_page_lock); |
834 | if (!chp->cmg_chars) | ||
835 | kfree(cmg_chars); | ||
836 | |||
813 | return ret; | 837 | return ret; |
814 | } | 838 | } |
815 | 839 | ||
816 | int __init chsc_alloc_sei_area(void) | 840 | int __init chsc_init(void) |
817 | { | 841 | { |
818 | int ret; | 842 | int ret; |
819 | 843 | ||
820 | sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 844 | sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
821 | if (!sei_page) { | 845 | chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); |
822 | CIO_MSG_EVENT(0, "Can't allocate page for processing of " | 846 | if (!sei_page || !chsc_page) { |
823 | "chsc machine checks!\n"); | 847 | ret = -ENOMEM; |
824 | return -ENOMEM; | 848 | goto out_err; |
825 | } | 849 | } |
826 | ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); | 850 | ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); |
827 | if (ret) | 851 | if (ret) |
828 | kfree(sei_page); | 852 | goto out_err; |
853 | return ret; | ||
854 | out_err: | ||
855 | free_page((unsigned long)chsc_page); | ||
856 | free_page((unsigned long)sei_page); | ||
829 | return ret; | 857 | return ret; |
830 | } | 858 | } |
831 | 859 | ||
832 | void __init chsc_free_sei_area(void) | 860 | void __init chsc_init_cleanup(void) |
833 | { | 861 | { |
834 | crw_unregister_handler(CRW_RSC_CSS); | 862 | crw_unregister_handler(CRW_RSC_CSS); |
835 | kfree(sei_page); | 863 | free_page((unsigned long)chsc_page); |
864 | free_page((unsigned long)sei_page); | ||
836 | } | 865 | } |
837 | 866 | ||
838 | int chsc_enable_facility(int operation_code) | 867 | int chsc_enable_facility(int operation_code) |
839 | { | 868 | { |
869 | unsigned long flags; | ||
840 | int ret; | 870 | int ret; |
841 | static struct { | 871 | struct { |
842 | struct chsc_header request; | 872 | struct chsc_header request; |
843 | u8 reserved1:4; | 873 | u8 reserved1:4; |
844 | u8 format:4; | 874 | u8 format:4; |
@@ -851,32 +881,33 @@ int chsc_enable_facility(int operation_code) | |||
851 | u32 reserved5:4; | 881 | u32 reserved5:4; |
852 | u32 format2:4; | 882 | u32 format2:4; |
853 | u32 reserved6:24; | 883 | u32 reserved6:24; |
854 | } __attribute__ ((packed, aligned(4096))) sda_area; | 884 | } __attribute__ ((packed)) *sda_area; |
855 | 885 | ||
856 | spin_lock(&sda_lock); | 886 | spin_lock_irqsave(&chsc_page_lock, flags); |
857 | memset(&sda_area, 0, sizeof(sda_area)); | 887 | memset(chsc_page, 0, PAGE_SIZE); |
858 | sda_area.request.length = 0x0400; | 888 | sda_area = chsc_page; |
859 | sda_area.request.code = 0x0031; | 889 | sda_area->request.length = 0x0400; |
860 | sda_area.operation_code = operation_code; | 890 | sda_area->request.code = 0x0031; |
891 | sda_area->operation_code = operation_code; | ||
861 | 892 | ||
862 | ret = chsc(&sda_area); | 893 | ret = chsc(sda_area); |
863 | if (ret > 0) { | 894 | if (ret > 0) { |
864 | ret = (ret == 3) ? -ENODEV : -EBUSY; | 895 | ret = (ret == 3) ? -ENODEV : -EBUSY; |
865 | goto out; | 896 | goto out; |
866 | } | 897 | } |
867 | 898 | ||
868 | switch (sda_area.response.code) { | 899 | switch (sda_area->response.code) { |
869 | case 0x0101: | 900 | case 0x0101: |
870 | ret = -EOPNOTSUPP; | 901 | ret = -EOPNOTSUPP; |
871 | break; | 902 | break; |
872 | default: | 903 | default: |
873 | ret = chsc_error_from_response(sda_area.response.code); | 904 | ret = chsc_error_from_response(sda_area->response.code); |
874 | } | 905 | } |
875 | if (ret != 0) | 906 | if (ret != 0) |
876 | CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", | 907 | CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", |
877 | operation_code, sda_area.response.code); | 908 | operation_code, sda_area->response.code); |
878 | out: | 909 | out: |
879 | spin_unlock(&sda_lock); | 910 | spin_unlock_irqrestore(&chsc_page_lock, flags); |
880 | return ret; | 911 | return ret; |
881 | } | 912 | } |
882 | 913 | ||
@@ -895,13 +926,12 @@ chsc_determine_css_characteristics(void) | |||
895 | struct chsc_header response; | 926 | struct chsc_header response; |
896 | u32 reserved4; | 927 | u32 reserved4; |
897 | u32 general_char[510]; | 928 | u32 general_char[510]; |
898 | u32 chsc_char[518]; | 929 | u32 chsc_char[508]; |
899 | } __attribute__ ((packed)) *scsc_area; | 930 | } __attribute__ ((packed)) *scsc_area; |
900 | 931 | ||
901 | scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 932 | spin_lock_irq(&chsc_page_lock); |
902 | if (!scsc_area) | 933 | memset(chsc_page, 0, PAGE_SIZE); |
903 | return -ENOMEM; | 934 | scsc_area = chsc_page; |
904 | |||
905 | scsc_area->request.length = 0x0010; | 935 | scsc_area->request.length = 0x0010; |
906 | scsc_area->request.code = 0x0010; | 936 | scsc_area->request.code = 0x0010; |
907 | 937 | ||
@@ -921,7 +951,7 @@ chsc_determine_css_characteristics(void) | |||
921 | CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", | 951 | CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", |
922 | scsc_area->response.code); | 952 | scsc_area->response.code); |
923 | exit: | 953 | exit: |
924 | free_page ((unsigned long) scsc_area); | 954 | spin_unlock_irq(&chsc_page_lock); |
925 | return result; | 955 | return result; |
926 | } | 956 | } |
927 | 957 | ||
@@ -976,29 +1006,29 @@ int chsc_sstpi(void *page, void *result, size_t size) | |||
976 | return (rr->response.code == 0x0001) ? 0 : -EIO; | 1006 | return (rr->response.code == 0x0001) ? 0 : -EIO; |
977 | } | 1007 | } |
978 | 1008 | ||
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) | 1009 | int chsc_siosl(struct subchannel_id schid) |
989 | { | 1010 | { |
1011 | struct { | ||
1012 | struct chsc_header request; | ||
1013 | u32 word1; | ||
1014 | struct subchannel_id sid; | ||
1015 | u32 word3; | ||
1016 | struct chsc_header response; | ||
1017 | u32 word[11]; | ||
1018 | } __attribute__ ((packed)) *siosl_area; | ||
990 | unsigned long flags; | 1019 | unsigned long flags; |
991 | int ccode; | 1020 | int ccode; |
992 | int rc; | 1021 | int rc; |
993 | 1022 | ||
994 | spin_lock_irqsave(&siosl_lock, flags); | 1023 | spin_lock_irqsave(&chsc_page_lock, flags); |
995 | memset(&siosl_area, 0, sizeof(siosl_area)); | 1024 | memset(chsc_page, 0, PAGE_SIZE); |
996 | siosl_area.request.length = 0x0010; | 1025 | siosl_area = chsc_page; |
997 | siosl_area.request.code = 0x0046; | 1026 | siosl_area->request.length = 0x0010; |
998 | siosl_area.word1 = 0x80000000; | 1027 | siosl_area->request.code = 0x0046; |
999 | siosl_area.sid = schid; | 1028 | siosl_area->word1 = 0x80000000; |
1029 | siosl_area->sid = schid; | ||
1000 | 1030 | ||
1001 | ccode = chsc(&siosl_area); | 1031 | ccode = chsc(siosl_area); |
1002 | if (ccode > 0) { | 1032 | if (ccode > 0) { |
1003 | if (ccode == 3) | 1033 | if (ccode == 3) |
1004 | rc = -ENODEV; | 1034 | rc = -ENODEV; |
@@ -1008,17 +1038,16 @@ int chsc_siosl(struct subchannel_id schid) | |||
1008 | schid.ssid, schid.sch_no, ccode); | 1038 | schid.ssid, schid.sch_no, ccode); |
1009 | goto out; | 1039 | goto out; |
1010 | } | 1040 | } |
1011 | rc = chsc_error_from_response(siosl_area.response.code); | 1041 | rc = chsc_error_from_response(siosl_area->response.code); |
1012 | if (rc) | 1042 | if (rc) |
1013 | CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", | 1043 | CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", |
1014 | schid.ssid, schid.sch_no, | 1044 | schid.ssid, schid.sch_no, |
1015 | siosl_area.response.code); | 1045 | siosl_area->response.code); |
1016 | else | 1046 | else |
1017 | CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", | 1047 | CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", |
1018 | schid.ssid, schid.sch_no); | 1048 | schid.ssid, schid.sch_no); |
1019 | out: | 1049 | out: |
1020 | spin_unlock_irqrestore(&siosl_lock, flags); | 1050 | spin_unlock_irqrestore(&chsc_page_lock, flags); |
1021 | |||
1022 | return rc; | 1051 | return rc; |
1023 | } | 1052 | } |
1024 | EXPORT_SYMBOL_GPL(chsc_siosl); | 1053 | EXPORT_SYMBOL_GPL(chsc_siosl); |