diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-07 17:50:50 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-07 17:50:50 -0500 |
commit | fb5131e1880ea1ba3ba7197cd5cc66c9c288f715 (patch) | |
tree | f0d9f25f9079727b9ead5a2b4cc85a0fea9b4668 /drivers/s390/cio | |
parent | d074b104cefcb6e8ded55a53e62fed59a246f55d (diff) | |
parent | 8e1023016cf17152972b98bce6c144834a4916d5 (diff) |
Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6: (65 commits)
[S390] prevent unneccesary loops_per_jiffy recalculation
[S390] cpuinfo: use get_online_cpus() instead of preempt_disable()
[S390] smp: remove cpu hotplug messages
[S390] mutex: enable spinning mutex on s390
[S390] mutex: Introduce arch_mutex_cpu_relax()
[S390] cio: fix ccwgroup unregistration race condition
[S390] perf: add DWARF register lookup for s390
[S390] cleanup ftrace backend functions
[S390] ptrace cleanup
[S390] smp/idle: call init_idle() before starting a new cpu
[S390] smp: delay idle task creation
[S390] dasd: Correct retry counter for terminated I/O.
[S390] dasd: Add support for raw ECKD access.
[S390] dasd: Prevent deadlock during suspend/resume.
[S390] dasd: Improve handling of stolen DASD reservation
[S390] dasd: do path verification for paths added at runtime
[S390] dasd: add High Performance FICON multitrack support
[S390] cio: reduce memory consumption of itcw structures
[S390] nmi: enable machine checks early
[S390] qeth: buffer count imbalance
...
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/ccwgroup.c | 78 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.c | 19 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.h | 18 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 8 | ||||
-rw-r--r-- | drivers/s390/cio/device_ops.c | 40 | ||||
-rw-r--r-- | drivers/s390/cio/itcw.c | 62 | ||||
-rw-r--r-- | drivers/s390/cio/qdio.h | 31 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_debug.c | 1 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 177 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_setup.c | 20 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 56 |
11 files changed, 300 insertions, 210 deletions
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 97b25d68e3e7..2864581d8ecb 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c | |||
@@ -67,6 +67,27 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) | |||
67 | } | 67 | } |
68 | 68 | ||
69 | /* | 69 | /* |
70 | * Remove references from ccw devices to ccw group device and from | ||
71 | * ccw group device to ccw devices. | ||
72 | */ | ||
73 | static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev) | ||
74 | { | ||
75 | struct ccw_device *cdev; | ||
76 | int i; | ||
77 | |||
78 | for (i = 0; i < gdev->count; i++) { | ||
79 | cdev = gdev->cdev[i]; | ||
80 | if (!cdev) | ||
81 | continue; | ||
82 | spin_lock_irq(cdev->ccwlock); | ||
83 | dev_set_drvdata(&cdev->dev, NULL); | ||
84 | spin_unlock_irq(cdev->ccwlock); | ||
85 | gdev->cdev[i] = NULL; | ||
86 | put_device(&cdev->dev); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | /* | ||
70 | * Provide an 'ungroup' attribute so the user can remove group devices no | 91 | * Provide an 'ungroup' attribute so the user can remove group devices no |
71 | * longer needed or accidentially created. Saves memory :) | 92 | * longer needed or accidentially created. Saves memory :) |
72 | */ | 93 | */ |
@@ -78,6 +99,7 @@ static void ccwgroup_ungroup_callback(struct device *dev) | |||
78 | if (device_is_registered(&gdev->dev)) { | 99 | if (device_is_registered(&gdev->dev)) { |
79 | __ccwgroup_remove_symlinks(gdev); | 100 | __ccwgroup_remove_symlinks(gdev); |
80 | device_unregister(dev); | 101 | device_unregister(dev); |
102 | __ccwgroup_remove_cdev_refs(gdev); | ||
81 | } | 103 | } |
82 | mutex_unlock(&gdev->reg_mutex); | 104 | mutex_unlock(&gdev->reg_mutex); |
83 | } | 105 | } |
@@ -116,21 +138,7 @@ static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store); | |||
116 | static void | 138 | static void |
117 | ccwgroup_release (struct device *dev) | 139 | ccwgroup_release (struct device *dev) |
118 | { | 140 | { |
119 | struct ccwgroup_device *gdev; | 141 | kfree(to_ccwgroupdev(dev)); |
120 | int i; | ||
121 | |||
122 | gdev = to_ccwgroupdev(dev); | ||
123 | |||
124 | for (i = 0; i < gdev->count; i++) { | ||
125 | if (gdev->cdev[i]) { | ||
126 | spin_lock_irq(gdev->cdev[i]->ccwlock); | ||
127 | if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) | ||
128 | dev_set_drvdata(&gdev->cdev[i]->dev, NULL); | ||
129 | spin_unlock_irq(gdev->cdev[i]->ccwlock); | ||
130 | put_device(&gdev->cdev[i]->dev); | ||
131 | } | ||
132 | } | ||
133 | kfree(gdev); | ||
134 | } | 142 | } |
135 | 143 | ||
136 | static int | 144 | static int |
@@ -639,6 +647,7 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) | |||
639 | mutex_lock(&gdev->reg_mutex); | 647 | mutex_lock(&gdev->reg_mutex); |
640 | __ccwgroup_remove_symlinks(gdev); | 648 | __ccwgroup_remove_symlinks(gdev); |
641 | device_unregister(dev); | 649 | device_unregister(dev); |
650 | __ccwgroup_remove_cdev_refs(gdev); | ||
642 | mutex_unlock(&gdev->reg_mutex); | 651 | mutex_unlock(&gdev->reg_mutex); |
643 | put_device(dev); | 652 | put_device(dev); |
644 | } | 653 | } |
@@ -660,25 +669,6 @@ int ccwgroup_probe_ccwdev(struct ccw_device *cdev) | |||
660 | return 0; | 669 | return 0; |
661 | } | 670 | } |
662 | 671 | ||
663 | static struct ccwgroup_device * | ||
664 | __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) | ||
665 | { | ||
666 | struct ccwgroup_device *gdev; | ||
667 | |||
668 | gdev = dev_get_drvdata(&cdev->dev); | ||
669 | if (gdev) { | ||
670 | if (get_device(&gdev->dev)) { | ||
671 | mutex_lock(&gdev->reg_mutex); | ||
672 | if (device_is_registered(&gdev->dev)) | ||
673 | return gdev; | ||
674 | mutex_unlock(&gdev->reg_mutex); | ||
675 | put_device(&gdev->dev); | ||
676 | } | ||
677 | return NULL; | ||
678 | } | ||
679 | return NULL; | ||
680 | } | ||
681 | |||
682 | /** | 672 | /** |
683 | * ccwgroup_remove_ccwdev() - remove function for slave devices | 673 | * ccwgroup_remove_ccwdev() - remove function for slave devices |
684 | * @cdev: ccw device to be removed | 674 | * @cdev: ccw device to be removed |
@@ -694,13 +684,25 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev) | |||
694 | /* Ignore offlining errors, device is gone anyway. */ | 684 | /* Ignore offlining errors, device is gone anyway. */ |
695 | ccw_device_set_offline(cdev); | 685 | ccw_device_set_offline(cdev); |
696 | /* If one of its devices is gone, the whole group is done for. */ | 686 | /* If one of its devices is gone, the whole group is done for. */ |
697 | gdev = __ccwgroup_get_gdev_by_cdev(cdev); | 687 | spin_lock_irq(cdev->ccwlock); |
698 | if (gdev) { | 688 | gdev = dev_get_drvdata(&cdev->dev); |
689 | if (!gdev) { | ||
690 | spin_unlock_irq(cdev->ccwlock); | ||
691 | return; | ||
692 | } | ||
693 | /* Get ccwgroup device reference for local processing. */ | ||
694 | get_device(&gdev->dev); | ||
695 | spin_unlock_irq(cdev->ccwlock); | ||
696 | /* Unregister group device. */ | ||
697 | mutex_lock(&gdev->reg_mutex); | ||
698 | if (device_is_registered(&gdev->dev)) { | ||
699 | __ccwgroup_remove_symlinks(gdev); | 699 | __ccwgroup_remove_symlinks(gdev); |
700 | device_unregister(&gdev->dev); | 700 | device_unregister(&gdev->dev); |
701 | mutex_unlock(&gdev->reg_mutex); | 701 | __ccwgroup_remove_cdev_refs(gdev); |
702 | put_device(&gdev->dev); | ||
703 | } | 702 | } |
703 | mutex_unlock(&gdev->reg_mutex); | ||
704 | /* Release ccwgroup device reference for local processing. */ | ||
705 | put_device(&gdev->dev); | ||
704 | } | 706 | } |
705 | 707 | ||
706 | MODULE_LICENSE("GPL"); | 708 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 1aaddea673e0..0689fcf23a11 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -695,6 +695,25 @@ out: | |||
695 | return ret; | 695 | return ret; |
696 | } | 696 | } |
697 | 697 | ||
698 | int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, | ||
699 | struct channel_path_desc_fmt1 *desc) | ||
700 | { | ||
701 | struct chsc_response_struct *chsc_resp; | ||
702 | struct chsc_scpd *scpd_area; | ||
703 | int ret; | ||
704 | |||
705 | spin_lock_irq(&chsc_page_lock); | ||
706 | scpd_area = chsc_page; | ||
707 | ret = chsc_determine_channel_path_desc(chpid, 0, 0, 1, 0, scpd_area); | ||
708 | if (ret) | ||
709 | goto out; | ||
710 | chsc_resp = (void *)&scpd_area->response; | ||
711 | memcpy(desc, &chsc_resp->data, sizeof(*desc)); | ||
712 | out: | ||
713 | spin_unlock_irq(&chsc_page_lock); | ||
714 | return ret; | ||
715 | } | ||
716 | |||
698 | static void | 717 | static void |
699 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, | 718 | chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, |
700 | struct cmg_chars *chars) | 719 | struct cmg_chars *chars) |
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 6693f5e3176f..3f15b2aaeaea 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h | |||
@@ -35,6 +35,22 @@ struct channel_path_desc { | |||
35 | u8 chpp; | 35 | u8 chpp; |
36 | } __attribute__ ((packed)); | 36 | } __attribute__ ((packed)); |
37 | 37 | ||
38 | struct channel_path_desc_fmt1 { | ||
39 | u8 flags; | ||
40 | u8 lsn; | ||
41 | u8 desc; | ||
42 | u8 chpid; | ||
43 | u32:24; | ||
44 | u8 chpp; | ||
45 | u32 unused[3]; | ||
46 | u16 mdc; | ||
47 | u16:13; | ||
48 | u8 r:1; | ||
49 | u8 s:1; | ||
50 | u8 f:1; | ||
51 | u32 zeros[2]; | ||
52 | } __attribute__ ((packed)); | ||
53 | |||
38 | struct channel_path; | 54 | struct channel_path; |
39 | 55 | ||
40 | struct css_chsc_char { | 56 | struct css_chsc_char { |
@@ -92,6 +108,8 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, | |||
92 | int c, int m, void *page); | 108 | int c, int m, void *page); |
93 | int chsc_determine_base_channel_path_desc(struct chp_id chpid, | 109 | int chsc_determine_base_channel_path_desc(struct chp_id chpid, |
94 | struct channel_path_desc *desc); | 110 | struct channel_path_desc *desc); |
111 | int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, | ||
112 | struct channel_path_desc_fmt1 *desc); | ||
95 | void chsc_chp_online(struct chp_id chpid); | 113 | void chsc_chp_online(struct chp_id chpid); |
96 | void chsc_chp_offline(struct chp_id chpid); | 114 | void chsc_chp_offline(struct chp_id chpid); |
97 | int chsc_get_channel_measurement_chars(struct channel_path *chp); | 115 | int chsc_get_channel_measurement_chars(struct channel_path *chp); |
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 825951b6b83f..24d8e97355b9 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -618,6 +618,7 @@ EXPORT_SYMBOL_GPL(css_schedule_reprobe); | |||
618 | static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) | 618 | static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) |
619 | { | 619 | { |
620 | struct subchannel_id mchk_schid; | 620 | struct subchannel_id mchk_schid; |
621 | struct subchannel *sch; | ||
621 | 622 | ||
622 | if (overflow) { | 623 | if (overflow) { |
623 | css_schedule_eval_all(); | 624 | css_schedule_eval_all(); |
@@ -637,6 +638,13 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) | |||
637 | if (crw1) | 638 | if (crw1) |
638 | mchk_schid.ssid = (crw1->rsid >> 4) & 3; | 639 | mchk_schid.ssid = (crw1->rsid >> 4) & 3; |
639 | 640 | ||
641 | if (crw0->erc == CRW_ERC_PMOD) { | ||
642 | sch = get_subchannel_by_schid(mchk_schid); | ||
643 | if (sch) { | ||
644 | css_update_ssd_info(sch); | ||
645 | put_device(&sch->dev); | ||
646 | } | ||
647 | } | ||
640 | /* | 648 | /* |
641 | * Since we are always presented with IPI in the CRW, we have to | 649 | * Since we are always presented with IPI in the CRW, we have to |
642 | * use stsch() to find out if the subchannel in question has come | 650 | * use stsch() to find out if the subchannel in question has come |
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 6da84543dfe9..651976b54af8 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c | |||
@@ -687,6 +687,46 @@ int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw, | |||
687 | EXPORT_SYMBOL(ccw_device_tm_start_timeout); | 687 | EXPORT_SYMBOL(ccw_device_tm_start_timeout); |
688 | 688 | ||
689 | /** | 689 | /** |
690 | * ccw_device_get_mdc - accumulate max data count | ||
691 | * @cdev: ccw device for which the max data count is accumulated | ||
692 | * @mask: mask of paths to use | ||
693 | * | ||
694 | * Return the number of 64K-bytes blocks all paths at least support | ||
695 | * for a transport command. Return values <= 0 indicate failures. | ||
696 | */ | ||
697 | int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) | ||
698 | { | ||
699 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
700 | struct channel_path_desc_fmt1 desc; | ||
701 | struct chp_id chpid; | ||
702 | int mdc = 0, ret, i; | ||
703 | |||
704 | /* Adjust requested path mask to excluded varied off paths. */ | ||
705 | if (mask) | ||
706 | mask &= sch->lpm; | ||
707 | else | ||
708 | mask = sch->lpm; | ||
709 | |||
710 | chp_id_init(&chpid); | ||
711 | for (i = 0; i < 8; i++) { | ||
712 | if (!(mask & (0x80 >> i))) | ||
713 | continue; | ||
714 | chpid.id = sch->schib.pmcw.chpid[i]; | ||
715 | ret = chsc_determine_fmt1_channel_path_desc(chpid, &desc); | ||
716 | if (ret) | ||
717 | return ret; | ||
718 | if (!desc.f) | ||
719 | return 0; | ||
720 | if (!desc.r) | ||
721 | mdc = 1; | ||
722 | mdc = mdc ? min(mdc, (int)desc.mdc) : desc.mdc; | ||
723 | } | ||
724 | |||
725 | return mdc; | ||
726 | } | ||
727 | EXPORT_SYMBOL(ccw_device_get_mdc); | ||
728 | |||
729 | /** | ||
690 | * ccw_device_tm_intrg - perform interrogate function | 730 | * ccw_device_tm_intrg - perform interrogate function |
691 | * @cdev: ccw device on which to perform the interrogate function | 731 | * @cdev: ccw device on which to perform the interrogate function |
692 | * | 732 | * |
diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c index a0ae29564774..358ee16d10a2 100644 --- a/drivers/s390/cio/itcw.c +++ b/drivers/s390/cio/itcw.c | |||
@@ -93,6 +93,7 @@ EXPORT_SYMBOL(itcw_get_tcw); | |||
93 | size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) | 93 | size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) |
94 | { | 94 | { |
95 | size_t len; | 95 | size_t len; |
96 | int cross_count; | ||
96 | 97 | ||
97 | /* Main data. */ | 98 | /* Main data. */ |
98 | len = sizeof(struct itcw); | 99 | len = sizeof(struct itcw); |
@@ -105,12 +106,27 @@ size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) | |||
105 | /* TSB */ sizeof(struct tsb) + | 106 | /* TSB */ sizeof(struct tsb) + |
106 | /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); | 107 | /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); |
107 | } | 108 | } |
109 | |||
108 | /* Maximum required alignment padding. */ | 110 | /* Maximum required alignment padding. */ |
109 | len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; | 111 | len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; |
110 | /* Maximum padding for structures that may not cross 4k boundary. */ | 112 | |
111 | if ((max_tidaws > 0) || (intrg_max_tidaws > 0)) | 113 | /* TIDAW lists may not cross a 4k boundary. To cross a |
112 | len += max(max_tidaws, intrg_max_tidaws) * | 114 | * boundary we need to add a TTIC TIDAW. We need to reserve |
113 | sizeof(struct tidaw) - 1; | 115 | * one additional TIDAW for a TTIC that we may need to add due |
116 | * to the placement of the data chunk in memory, and a further | ||
117 | * TIDAW for each page boundary that the TIDAW list may cross | ||
118 | * due to it's own size. | ||
119 | */ | ||
120 | if (max_tidaws) { | ||
121 | cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) | ||
122 | >> PAGE_SHIFT); | ||
123 | len += cross_count * sizeof(struct tidaw); | ||
124 | } | ||
125 | if (intrg_max_tidaws) { | ||
126 | cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) | ||
127 | >> PAGE_SHIFT); | ||
128 | len += cross_count * sizeof(struct tidaw); | ||
129 | } | ||
114 | return len; | 130 | return len; |
115 | } | 131 | } |
116 | EXPORT_SYMBOL(itcw_calc_size); | 132 | EXPORT_SYMBOL(itcw_calc_size); |
@@ -165,6 +181,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
165 | void *chunk; | 181 | void *chunk; |
166 | addr_t start; | 182 | addr_t start; |
167 | addr_t end; | 183 | addr_t end; |
184 | int cross_count; | ||
168 | 185 | ||
169 | /* Check for 2G limit. */ | 186 | /* Check for 2G limit. */ |
170 | start = (addr_t) buffer; | 187 | start = (addr_t) buffer; |
@@ -177,8 +194,17 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
177 | if (IS_ERR(chunk)) | 194 | if (IS_ERR(chunk)) |
178 | return chunk; | 195 | return chunk; |
179 | itcw = chunk; | 196 | itcw = chunk; |
180 | itcw->max_tidaws = max_tidaws; | 197 | /* allow for TTIC tidaws that may be needed to cross a page boundary */ |
181 | itcw->intrg_max_tidaws = intrg_max_tidaws; | 198 | cross_count = 0; |
199 | if (max_tidaws) | ||
200 | cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) | ||
201 | >> PAGE_SHIFT); | ||
202 | itcw->max_tidaws = max_tidaws + cross_count; | ||
203 | cross_count = 0; | ||
204 | if (intrg_max_tidaws) | ||
205 | cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) | ||
206 | >> PAGE_SHIFT); | ||
207 | itcw->intrg_max_tidaws = intrg_max_tidaws + cross_count; | ||
182 | /* Main TCW. */ | 208 | /* Main TCW. */ |
183 | chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); | 209 | chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); |
184 | if (IS_ERR(chunk)) | 210 | if (IS_ERR(chunk)) |
@@ -198,7 +224,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
198 | /* Data TIDAL. */ | 224 | /* Data TIDAL. */ |
199 | if (max_tidaws > 0) { | 225 | if (max_tidaws > 0) { |
200 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * | 226 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * |
201 | max_tidaws, 16, 1); | 227 | itcw->max_tidaws, 16, 0); |
202 | if (IS_ERR(chunk)) | 228 | if (IS_ERR(chunk)) |
203 | return chunk; | 229 | return chunk; |
204 | tcw_set_data(itcw->tcw, chunk, 1); | 230 | tcw_set_data(itcw->tcw, chunk, 1); |
@@ -206,7 +232,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
206 | /* Interrogate data TIDAL. */ | 232 | /* Interrogate data TIDAL. */ |
207 | if (intrg && (intrg_max_tidaws > 0)) { | 233 | if (intrg && (intrg_max_tidaws > 0)) { |
208 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * | 234 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * |
209 | intrg_max_tidaws, 16, 1); | 235 | itcw->intrg_max_tidaws, 16, 0); |
210 | if (IS_ERR(chunk)) | 236 | if (IS_ERR(chunk)) |
211 | return chunk; | 237 | return chunk; |
212 | tcw_set_data(itcw->intrg_tcw, chunk, 1); | 238 | tcw_set_data(itcw->intrg_tcw, chunk, 1); |
@@ -283,13 +309,29 @@ EXPORT_SYMBOL(itcw_add_dcw); | |||
283 | * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the | 309 | * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the |
284 | * available space. | 310 | * available space. |
285 | * | 311 | * |
286 | * Note: the tidaw-list is assumed to be contiguous with no ttics. The | 312 | * Note: TTIC tidaws are automatically added when needed, so explicitly calling |
287 | * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize. | 313 | * this interface with the TTIC flag is not supported. The last-tidaw flag |
314 | * for the last tidaw in the list will be set by itcw_finalize. | ||
288 | */ | 315 | */ |
289 | struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) | 316 | struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) |
290 | { | 317 | { |
318 | struct tidaw *following; | ||
319 | |||
291 | if (itcw->num_tidaws >= itcw->max_tidaws) | 320 | if (itcw->num_tidaws >= itcw->max_tidaws) |
292 | return ERR_PTR(-ENOSPC); | 321 | return ERR_PTR(-ENOSPC); |
322 | /* | ||
323 | * Is the tidaw, which follows the one we are about to fill, on the next | ||
324 | * page? Then we have to insert a TTIC tidaw first, that points to the | ||
325 | * tidaw on the new page. | ||
326 | */ | ||
327 | following = ((struct tidaw *) tcw_get_data(itcw->tcw)) | ||
328 | + itcw->num_tidaws + 1; | ||
329 | if (itcw->num_tidaws && !((unsigned long) following & ~PAGE_MASK)) { | ||
330 | tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, | ||
331 | TIDAW_FLAGS_TTIC, following, 0); | ||
332 | if (itcw->num_tidaws >= itcw->max_tidaws) | ||
333 | return ERR_PTR(-ENOSPC); | ||
334 | } | ||
293 | return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); | 335 | return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); |
294 | } | 336 | } |
295 | EXPORT_SYMBOL(itcw_add_tidaw); | 337 | EXPORT_SYMBOL(itcw_add_tidaw); |
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 0f4ef8769a3d..7bc643f3f5ab 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h | |||
@@ -91,6 +91,12 @@ enum qdio_irq_states { | |||
91 | #define AC1_SC_QEBSM_AVAILABLE 0x02 /* available for subchannel */ | 91 | #define AC1_SC_QEBSM_AVAILABLE 0x02 /* available for subchannel */ |
92 | #define AC1_SC_QEBSM_ENABLED 0x01 /* enabled for subchannel */ | 92 | #define AC1_SC_QEBSM_ENABLED 0x01 /* enabled for subchannel */ |
93 | 93 | ||
94 | /* SIGA flags */ | ||
95 | #define QDIO_SIGA_WRITE 0x00 | ||
96 | #define QDIO_SIGA_READ 0x01 | ||
97 | #define QDIO_SIGA_SYNC 0x02 | ||
98 | #define QDIO_SIGA_QEBSM_FLAG 0x80 | ||
99 | |||
94 | #ifdef CONFIG_64BIT | 100 | #ifdef CONFIG_64BIT |
95 | static inline int do_sqbs(u64 token, unsigned char state, int queue, | 101 | static inline int do_sqbs(u64 token, unsigned char state, int queue, |
96 | int *start, int *count) | 102 | int *start, int *count) |
@@ -142,10 +148,9 @@ struct siga_flag { | |||
142 | u8 input:1; | 148 | u8 input:1; |
143 | u8 output:1; | 149 | u8 output:1; |
144 | u8 sync:1; | 150 | u8 sync:1; |
145 | u8 no_sync_ti:1; | 151 | u8 sync_after_ai:1; |
146 | u8 no_sync_out_ti:1; | 152 | u8 sync_out_after_pci:1; |
147 | u8 no_sync_out_pci:1; | 153 | u8:3; |
148 | u8:2; | ||
149 | } __attribute__ ((packed)); | 154 | } __attribute__ ((packed)); |
150 | 155 | ||
151 | struct chsc_ssqd_area { | 156 | struct chsc_ssqd_area { |
@@ -202,6 +207,7 @@ struct qdio_dev_perf_stat { | |||
202 | unsigned int inbound_queue_full; | 207 | unsigned int inbound_queue_full; |
203 | unsigned int outbound_call; | 208 | unsigned int outbound_call; |
204 | unsigned int outbound_handler; | 209 | unsigned int outbound_handler; |
210 | unsigned int outbound_queue_full; | ||
205 | unsigned int fast_requeue; | 211 | unsigned int fast_requeue; |
206 | unsigned int target_full; | 212 | unsigned int target_full; |
207 | unsigned int eqbs; | 213 | unsigned int eqbs; |
@@ -245,10 +251,10 @@ struct qdio_input_q { | |||
245 | struct qdio_output_q { | 251 | struct qdio_output_q { |
246 | /* PCIs are enabled for the queue */ | 252 | /* PCIs are enabled for the queue */ |
247 | int pci_out_enabled; | 253 | int pci_out_enabled; |
248 | /* IQDIO: output multiple buffers (enhanced SIGA) */ | ||
249 | int use_enh_siga; | ||
250 | /* timer to check for more outbound work */ | 254 | /* timer to check for more outbound work */ |
251 | struct timer_list timer; | 255 | struct timer_list timer; |
256 | /* used SBALs before tasklet schedule */ | ||
257 | int scan_threshold; | ||
252 | }; | 258 | }; |
253 | 259 | ||
254 | /* | 260 | /* |
@@ -383,12 +389,13 @@ static inline int multicast_outbound(struct qdio_q *q) | |||
383 | (q->irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) | 389 | (q->irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) |
384 | #define is_qebsm(q) (q->irq_ptr->sch_token != 0) | 390 | #define is_qebsm(q) (q->irq_ptr->sch_token != 0) |
385 | 391 | ||
386 | #define need_siga_sync_thinint(q) (!q->irq_ptr->siga_flag.no_sync_ti) | ||
387 | #define need_siga_sync_out_thinint(q) (!q->irq_ptr->siga_flag.no_sync_out_ti) | ||
388 | #define need_siga_in(q) (q->irq_ptr->siga_flag.input) | 392 | #define need_siga_in(q) (q->irq_ptr->siga_flag.input) |
389 | #define need_siga_out(q) (q->irq_ptr->siga_flag.output) | 393 | #define need_siga_out(q) (q->irq_ptr->siga_flag.output) |
390 | #define need_siga_sync(q) (q->irq_ptr->siga_flag.sync) | 394 | #define need_siga_sync(q) (unlikely(q->irq_ptr->siga_flag.sync)) |
391 | #define siga_syncs_out_pci(q) (q->irq_ptr->siga_flag.no_sync_out_pci) | 395 | #define need_siga_sync_after_ai(q) \ |
396 | (unlikely(q->irq_ptr->siga_flag.sync_after_ai)) | ||
397 | #define need_siga_sync_out_after_pci(q) \ | ||
398 | (unlikely(q->irq_ptr->siga_flag.sync_out_after_pci)) | ||
392 | 399 | ||
393 | #define for_each_input_queue(irq_ptr, q, i) \ | 400 | #define for_each_input_queue(irq_ptr, q, i) \ |
394 | for (i = 0, q = irq_ptr->input_qs[0]; \ | 401 | for (i = 0, q = irq_ptr->input_qs[0]; \ |
@@ -423,9 +430,9 @@ struct indicator_t { | |||
423 | 430 | ||
424 | extern struct indicator_t *q_indicators; | 431 | extern struct indicator_t *q_indicators; |
425 | 432 | ||
426 | static inline int shared_ind(struct qdio_irq *irq_ptr) | 433 | static inline int shared_ind(u32 *dsci) |
427 | { | 434 | { |
428 | return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; | 435 | return dsci == &q_indicators[TIQDIO_SHARED_IND].ind; |
429 | } | 436 | } |
430 | 437 | ||
431 | /* prototypes for thin interrupt */ | 438 | /* prototypes for thin interrupt */ |
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 28868e7471a5..f8b03a636e49 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c | |||
@@ -151,6 +151,7 @@ static char *qperf_names[] = { | |||
151 | "Inbound queue full", | 151 | "Inbound queue full", |
152 | "Outbound calls", | 152 | "Outbound calls", |
153 | "Outbound handler", | 153 | "Outbound handler", |
154 | "Outbound queue full", | ||
154 | "Outbound fast_requeue", | 155 | "Outbound fast_requeue", |
155 | "Outbound target_full", | 156 | "Outbound target_full", |
156 | "QEBSM eqbs", | 157 | "QEBSM eqbs", |
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 5fcfa7f9e9ef..e9fff2b9bce2 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/timer.h> | 14 | #include <linux/timer.h> |
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/gfp.h> | 16 | #include <linux/gfp.h> |
17 | #include <linux/kernel_stat.h> | ||
17 | #include <asm/atomic.h> | 18 | #include <asm/atomic.h> |
18 | #include <asm/debug.h> | 19 | #include <asm/debug.h> |
19 | #include <asm/qdio.h> | 20 | #include <asm/qdio.h> |
@@ -29,11 +30,12 @@ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>,"\ | |||
29 | MODULE_DESCRIPTION("QDIO base support"); | 30 | MODULE_DESCRIPTION("QDIO base support"); |
30 | MODULE_LICENSE("GPL"); | 31 | MODULE_LICENSE("GPL"); |
31 | 32 | ||
32 | static inline int do_siga_sync(struct subchannel_id schid, | 33 | static inline int do_siga_sync(unsigned long schid, |
33 | unsigned int out_mask, unsigned int in_mask) | 34 | unsigned int out_mask, unsigned int in_mask, |
35 | unsigned int fc) | ||
34 | { | 36 | { |
35 | register unsigned long __fc asm ("0") = 2; | 37 | register unsigned long __fc asm ("0") = fc; |
36 | register struct subchannel_id __schid asm ("1") = schid; | 38 | register unsigned long __schid asm ("1") = schid; |
37 | register unsigned long out asm ("2") = out_mask; | 39 | register unsigned long out asm ("2") = out_mask; |
38 | register unsigned long in asm ("3") = in_mask; | 40 | register unsigned long in asm ("3") = in_mask; |
39 | int cc; | 41 | int cc; |
@@ -47,10 +49,11 @@ static inline int do_siga_sync(struct subchannel_id schid, | |||
47 | return cc; | 49 | return cc; |
48 | } | 50 | } |
49 | 51 | ||
50 | static inline int do_siga_input(struct subchannel_id schid, unsigned int mask) | 52 | static inline int do_siga_input(unsigned long schid, unsigned int mask, |
53 | unsigned int fc) | ||
51 | { | 54 | { |
52 | register unsigned long __fc asm ("0") = 1; | 55 | register unsigned long __fc asm ("0") = fc; |
53 | register struct subchannel_id __schid asm ("1") = schid; | 56 | register unsigned long __schid asm ("1") = schid; |
54 | register unsigned long __mask asm ("2") = mask; | 57 | register unsigned long __mask asm ("2") = mask; |
55 | int cc; | 58 | int cc; |
56 | 59 | ||
@@ -279,16 +282,20 @@ void qdio_init_buf_states(struct qdio_irq *irq_ptr) | |||
279 | static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, | 282 | static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, |
280 | unsigned int input) | 283 | unsigned int input) |
281 | { | 284 | { |
285 | unsigned long schid = *((u32 *) &q->irq_ptr->schid); | ||
286 | unsigned int fc = QDIO_SIGA_SYNC; | ||
282 | int cc; | 287 | int cc; |
283 | 288 | ||
284 | if (!need_siga_sync(q)) | ||
285 | return 0; | ||
286 | |||
287 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); | 289 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); |
288 | qperf_inc(q, siga_sync); | 290 | qperf_inc(q, siga_sync); |
289 | 291 | ||
290 | cc = do_siga_sync(q->irq_ptr->schid, output, input); | 292 | if (is_qebsm(q)) { |
291 | if (cc) | 293 | schid = q->irq_ptr->sch_token; |
294 | fc |= QDIO_SIGA_QEBSM_FLAG; | ||
295 | } | ||
296 | |||
297 | cc = do_siga_sync(schid, output, input, fc); | ||
298 | if (unlikely(cc)) | ||
292 | DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); | 299 | DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); |
293 | return cc; | 300 | return cc; |
294 | } | 301 | } |
@@ -301,38 +308,22 @@ static inline int qdio_siga_sync_q(struct qdio_q *q) | |||
301 | return qdio_siga_sync(q, q->mask, 0); | 308 | return qdio_siga_sync(q, q->mask, 0); |
302 | } | 309 | } |
303 | 310 | ||
304 | static inline int qdio_siga_sync_out(struct qdio_q *q) | ||
305 | { | ||
306 | return qdio_siga_sync(q, ~0U, 0); | ||
307 | } | ||
308 | |||
309 | static inline int qdio_siga_sync_all(struct qdio_q *q) | ||
310 | { | ||
311 | return qdio_siga_sync(q, ~0U, ~0U); | ||
312 | } | ||
313 | |||
314 | static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) | 311 | static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) |
315 | { | 312 | { |
316 | unsigned long schid; | 313 | unsigned long schid = *((u32 *) &q->irq_ptr->schid); |
317 | unsigned int fc = 0; | 314 | unsigned int fc = QDIO_SIGA_WRITE; |
318 | u64 start_time = 0; | 315 | u64 start_time = 0; |
319 | int cc; | 316 | int cc; |
320 | 317 | ||
321 | if (q->u.out.use_enh_siga) | ||
322 | fc = 3; | ||
323 | |||
324 | if (is_qebsm(q)) { | 318 | if (is_qebsm(q)) { |
325 | schid = q->irq_ptr->sch_token; | 319 | schid = q->irq_ptr->sch_token; |
326 | fc |= 0x80; | 320 | fc |= QDIO_SIGA_QEBSM_FLAG; |
327 | } | 321 | } |
328 | else | ||
329 | schid = *((u32 *)&q->irq_ptr->schid); | ||
330 | |||
331 | again: | 322 | again: |
332 | cc = do_siga_output(schid, q->mask, busy_bit, fc); | 323 | cc = do_siga_output(schid, q->mask, busy_bit, fc); |
333 | 324 | ||
334 | /* hipersocket busy condition */ | 325 | /* hipersocket busy condition */ |
335 | if (*busy_bit) { | 326 | if (unlikely(*busy_bit)) { |
336 | WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); | 327 | WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); |
337 | 328 | ||
338 | if (!start_time) { | 329 | if (!start_time) { |
@@ -347,32 +338,41 @@ again: | |||
347 | 338 | ||
348 | static inline int qdio_siga_input(struct qdio_q *q) | 339 | static inline int qdio_siga_input(struct qdio_q *q) |
349 | { | 340 | { |
341 | unsigned long schid = *((u32 *) &q->irq_ptr->schid); | ||
342 | unsigned int fc = QDIO_SIGA_READ; | ||
350 | int cc; | 343 | int cc; |
351 | 344 | ||
352 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); | 345 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); |
353 | qperf_inc(q, siga_read); | 346 | qperf_inc(q, siga_read); |
354 | 347 | ||
355 | cc = do_siga_input(q->irq_ptr->schid, q->mask); | 348 | if (is_qebsm(q)) { |
356 | if (cc) | 349 | schid = q->irq_ptr->sch_token; |
350 | fc |= QDIO_SIGA_QEBSM_FLAG; | ||
351 | } | ||
352 | |||
353 | cc = do_siga_input(schid, q->mask, fc); | ||
354 | if (unlikely(cc)) | ||
357 | DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); | 355 | DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); |
358 | return cc; | 356 | return cc; |
359 | } | 357 | } |
360 | 358 | ||
361 | static inline void qdio_sync_after_thinint(struct qdio_q *q) | 359 | #define qdio_siga_sync_out(q) qdio_siga_sync(q, ~0U, 0) |
360 | #define qdio_siga_sync_all(q) qdio_siga_sync(q, ~0U, ~0U) | ||
361 | |||
362 | static inline void qdio_sync_queues(struct qdio_q *q) | ||
362 | { | 363 | { |
363 | if (pci_out_supported(q)) { | 364 | /* PCI capable outbound queues will also be scanned so sync them too */ |
364 | if (need_siga_sync_thinint(q)) | 365 | if (pci_out_supported(q)) |
365 | qdio_siga_sync_all(q); | 366 | qdio_siga_sync_all(q); |
366 | else if (need_siga_sync_out_thinint(q)) | 367 | else |
367 | qdio_siga_sync_out(q); | ||
368 | } else | ||
369 | qdio_siga_sync_q(q); | 368 | qdio_siga_sync_q(q); |
370 | } | 369 | } |
371 | 370 | ||
372 | int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, | 371 | int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, |
373 | unsigned char *state) | 372 | unsigned char *state) |
374 | { | 373 | { |
375 | qdio_siga_sync_q(q); | 374 | if (need_siga_sync(q)) |
375 | qdio_siga_sync_q(q); | ||
376 | return get_buf_states(q, bufnr, state, 1, 0); | 376 | return get_buf_states(q, bufnr, state, 1, 0); |
377 | } | 377 | } |
378 | 378 | ||
@@ -549,7 +549,8 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) | |||
549 | if (!atomic_read(&q->nr_buf_used)) | 549 | if (!atomic_read(&q->nr_buf_used)) |
550 | return 1; | 550 | return 1; |
551 | 551 | ||
552 | qdio_siga_sync_q(q); | 552 | if (need_siga_sync(q)) |
553 | qdio_siga_sync_q(q); | ||
553 | get_buf_state(q, q->first_to_check, &state, 0); | 554 | get_buf_state(q, q->first_to_check, &state, 0); |
554 | 555 | ||
555 | if (state == SLSB_P_INPUT_PRIMED || state == SLSB_P_INPUT_ERROR) | 556 | if (state == SLSB_P_INPUT_PRIMED || state == SLSB_P_INPUT_ERROR) |
@@ -644,9 +645,12 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) | |||
644 | int count, stop; | 645 | int count, stop; |
645 | unsigned char state; | 646 | unsigned char state; |
646 | 647 | ||
647 | if (((queue_type(q) != QDIO_IQDIO_QFMT) && !pci_out_supported(q)) || | 648 | if (need_siga_sync(q)) |
648 | (queue_type(q) == QDIO_IQDIO_QFMT && multicast_outbound(q))) | 649 | if (((queue_type(q) != QDIO_IQDIO_QFMT) && |
649 | qdio_siga_sync_q(q); | 650 | !pci_out_supported(q)) || |
651 | (queue_type(q) == QDIO_IQDIO_QFMT && | ||
652 | multicast_outbound(q))) | ||
653 | qdio_siga_sync_q(q); | ||
650 | 654 | ||
651 | /* | 655 | /* |
652 | * Don't check 128 buffers, as otherwise qdio_inbound_q_moved | 656 | * Don't check 128 buffers, as otherwise qdio_inbound_q_moved |
@@ -818,7 +822,8 @@ static inline void qdio_check_outbound_after_thinint(struct qdio_q *q) | |||
818 | static void __tiqdio_inbound_processing(struct qdio_q *q) | 822 | static void __tiqdio_inbound_processing(struct qdio_q *q) |
819 | { | 823 | { |
820 | qperf_inc(q, tasklet_inbound); | 824 | qperf_inc(q, tasklet_inbound); |
821 | qdio_sync_after_thinint(q); | 825 | if (need_siga_sync(q) && need_siga_sync_after_ai(q)) |
826 | qdio_sync_queues(q); | ||
822 | 827 | ||
823 | /* | 828 | /* |
824 | * The interrupt could be caused by a PCI request. Check the | 829 | * The interrupt could be caused by a PCI request. Check the |
@@ -898,16 +903,14 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) | |||
898 | tasklet_schedule(&q->tasklet); | 903 | tasklet_schedule(&q->tasklet); |
899 | } | 904 | } |
900 | 905 | ||
901 | if (!(irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED)) | 906 | if (!pci_out_supported(q)) |
902 | return; | 907 | return; |
903 | 908 | ||
904 | for_each_output_queue(irq_ptr, q, i) { | 909 | for_each_output_queue(irq_ptr, q, i) { |
905 | if (qdio_outbound_q_done(q)) | 910 | if (qdio_outbound_q_done(q)) |
906 | continue; | 911 | continue; |
907 | 912 | if (need_siga_sync(q) && need_siga_sync_out_after_pci(q)) | |
908 | if (!siga_syncs_out_pci(q)) | ||
909 | qdio_siga_sync_q(q); | 913 | qdio_siga_sync_q(q); |
910 | |||
911 | tasklet_schedule(&q->tasklet); | 914 | tasklet_schedule(&q->tasklet); |
912 | } | 915 | } |
913 | } | 916 | } |
@@ -970,6 +973,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, | |||
970 | return; | 973 | return; |
971 | } | 974 | } |
972 | 975 | ||
976 | kstat_cpu(smp_processor_id()).irqs[IOINT_QDI]++; | ||
973 | if (irq_ptr->perf_stat_enabled) | 977 | if (irq_ptr->perf_stat_enabled) |
974 | irq_ptr->perf_stat.qdio_int++; | 978 | irq_ptr->perf_stat.qdio_int++; |
975 | 979 | ||
@@ -1273,7 +1277,6 @@ int qdio_establish(struct qdio_initialize *init_data) | |||
1273 | } | 1277 | } |
1274 | 1278 | ||
1275 | qdio_setup_ssqd_info(irq_ptr); | 1279 | qdio_setup_ssqd_info(irq_ptr); |
1276 | DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc); | ||
1277 | DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); | 1280 | DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); |
1278 | 1281 | ||
1279 | /* qebsm is now setup if available, initialize buffer states */ | 1282 | /* qebsm is now setup if available, initialize buffer states */ |
@@ -1445,52 +1448,38 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, | |||
1445 | used = atomic_add_return(count, &q->nr_buf_used); | 1448 | used = atomic_add_return(count, &q->nr_buf_used); |
1446 | BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q); | 1449 | BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q); |
1447 | 1450 | ||
1451 | if (used == QDIO_MAX_BUFFERS_PER_Q) | ||
1452 | qperf_inc(q, outbound_queue_full); | ||
1453 | |||
1448 | if (callflags & QDIO_FLAG_PCI_OUT) { | 1454 | if (callflags & QDIO_FLAG_PCI_OUT) { |
1449 | q->u.out.pci_out_enabled = 1; | 1455 | q->u.out.pci_out_enabled = 1; |
1450 | qperf_inc(q, pci_request_int); | 1456 | qperf_inc(q, pci_request_int); |
1451 | } | 1457 | } else |
1452 | else | ||
1453 | q->u.out.pci_out_enabled = 0; | 1458 | q->u.out.pci_out_enabled = 0; |
1454 | 1459 | ||
1455 | if (queue_type(q) == QDIO_IQDIO_QFMT) { | 1460 | if (queue_type(q) == QDIO_IQDIO_QFMT) { |
1456 | if (multicast_outbound(q)) | 1461 | /* One SIGA-W per buffer required for unicast HiperSockets. */ |
1462 | WARN_ON_ONCE(count > 1 && !multicast_outbound(q)); | ||
1463 | |||
1464 | rc = qdio_kick_outbound_q(q); | ||
1465 | } else if (need_siga_sync(q)) { | ||
1466 | rc = qdio_siga_sync_q(q); | ||
1467 | } else { | ||
1468 | /* try to fast requeue buffers */ | ||
1469 | get_buf_state(q, prev_buf(bufnr), &state, 0); | ||
1470 | if (state != SLSB_CU_OUTPUT_PRIMED) | ||
1457 | rc = qdio_kick_outbound_q(q); | 1471 | rc = qdio_kick_outbound_q(q); |
1458 | else | 1472 | else |
1459 | if ((q->irq_ptr->ssqd_desc.mmwc > 1) && | 1473 | qperf_inc(q, fast_requeue); |
1460 | (count > 1) && | ||
1461 | (count <= q->irq_ptr->ssqd_desc.mmwc)) { | ||
1462 | /* exploit enhanced SIGA */ | ||
1463 | q->u.out.use_enh_siga = 1; | ||
1464 | rc = qdio_kick_outbound_q(q); | ||
1465 | } else { | ||
1466 | /* | ||
1467 | * One siga-w per buffer required for unicast | ||
1468 | * HiperSockets. | ||
1469 | */ | ||
1470 | q->u.out.use_enh_siga = 0; | ||
1471 | while (count--) { | ||
1472 | rc = qdio_kick_outbound_q(q); | ||
1473 | if (rc) | ||
1474 | goto out; | ||
1475 | } | ||
1476 | } | ||
1477 | goto out; | ||
1478 | } | ||
1479 | |||
1480 | if (need_siga_sync(q)) { | ||
1481 | qdio_siga_sync_q(q); | ||
1482 | goto out; | ||
1483 | } | 1474 | } |
1484 | 1475 | ||
1485 | /* try to fast requeue buffers */ | 1476 | /* in case of SIGA errors we must process the error immediately */ |
1486 | get_buf_state(q, prev_buf(bufnr), &state, 0); | 1477 | if (used >= q->u.out.scan_threshold || rc) |
1487 | if (state != SLSB_CU_OUTPUT_PRIMED) | 1478 | tasklet_schedule(&q->tasklet); |
1488 | rc = qdio_kick_outbound_q(q); | ||
1489 | else | 1479 | else |
1490 | qperf_inc(q, fast_requeue); | 1480 | /* free the SBALs in case of no further traffic */ |
1491 | 1481 | if (!timer_pending(&q->u.out.timer)) | |
1492 | out: | 1482 | mod_timer(&q->u.out.timer, jiffies + HZ); |
1493 | tasklet_schedule(&q->tasklet); | ||
1494 | return rc; | 1483 | return rc; |
1495 | } | 1484 | } |
1496 | 1485 | ||
@@ -1550,7 +1539,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) | |||
1550 | 1539 | ||
1551 | WARN_ON(queue_irqs_enabled(q)); | 1540 | WARN_ON(queue_irqs_enabled(q)); |
1552 | 1541 | ||
1553 | if (!shared_ind(q->irq_ptr)) | 1542 | if (!shared_ind(q->irq_ptr->dsci)) |
1554 | xchg(q->irq_ptr->dsci, 0); | 1543 | xchg(q->irq_ptr->dsci, 0); |
1555 | 1544 | ||
1556 | qdio_stop_polling(q); | 1545 | qdio_stop_polling(q); |
@@ -1560,7 +1549,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) | |||
1560 | * We need to check again to not lose initiative after | 1549 | * We need to check again to not lose initiative after |
1561 | * resetting the ACK state. | 1550 | * resetting the ACK state. |
1562 | */ | 1551 | */ |
1563 | if (!shared_ind(q->irq_ptr) && *q->irq_ptr->dsci) | 1552 | if (!shared_ind(q->irq_ptr->dsci) && *q->irq_ptr->dsci) |
1564 | goto rescan; | 1553 | goto rescan; |
1565 | if (!qdio_inbound_q_done(q)) | 1554 | if (!qdio_inbound_q_done(q)) |
1566 | goto rescan; | 1555 | goto rescan; |
@@ -1600,12 +1589,14 @@ int qdio_get_next_buffers(struct ccw_device *cdev, int nr, int *bufnr, | |||
1600 | q = irq_ptr->input_qs[nr]; | 1589 | q = irq_ptr->input_qs[nr]; |
1601 | WARN_ON(queue_irqs_enabled(q)); | 1590 | WARN_ON(queue_irqs_enabled(q)); |
1602 | 1591 | ||
1603 | qdio_sync_after_thinint(q); | ||
1604 | |||
1605 | /* | 1592 | /* |
1606 | * The interrupt could be caused by a PCI request. Check the | 1593 | * Cannot rely on automatic sync after interrupt since queues may |
1607 | * PCI capable outbound queues. | 1594 | * also be examined without interrupt. |
1608 | */ | 1595 | */ |
1596 | if (need_siga_sync(q)) | ||
1597 | qdio_sync_queues(q); | ||
1598 | |||
1599 | /* check the PCI capable outbound queues. */ | ||
1609 | qdio_check_outbound_after_thinint(q); | 1600 | qdio_check_outbound_after_thinint(q); |
1610 | 1601 | ||
1611 | if (!qdio_inbound_q_moved(q)) | 1602 | if (!qdio_inbound_q_moved(q)) |
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index a13cf7ec64b2..89107d0938c4 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c | |||
@@ -178,6 +178,7 @@ static void setup_queues(struct qdio_irq *irq_ptr, | |||
178 | setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); | 178 | setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); |
179 | 179 | ||
180 | q->is_input_q = 0; | 180 | q->is_input_q = 0; |
181 | q->u.out.scan_threshold = qdio_init->scan_threshold; | ||
181 | setup_storage_lists(q, irq_ptr, output_sbal_array, i); | 182 | setup_storage_lists(q, irq_ptr, output_sbal_array, i); |
182 | output_sbal_array += QDIO_MAX_BUFFERS_PER_Q; | 183 | output_sbal_array += QDIO_MAX_BUFFERS_PER_Q; |
183 | 184 | ||
@@ -196,14 +197,10 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac) | |||
196 | irq_ptr->siga_flag.output = 1; | 197 | irq_ptr->siga_flag.output = 1; |
197 | if (qdioac & AC1_SIGA_SYNC_NEEDED) | 198 | if (qdioac & AC1_SIGA_SYNC_NEEDED) |
198 | irq_ptr->siga_flag.sync = 1; | 199 | irq_ptr->siga_flag.sync = 1; |
199 | if (qdioac & AC1_AUTOMATIC_SYNC_ON_THININT) | 200 | if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_THININT)) |
200 | irq_ptr->siga_flag.no_sync_ti = 1; | 201 | irq_ptr->siga_flag.sync_after_ai = 1; |
201 | if (qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI) | 202 | if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI)) |
202 | irq_ptr->siga_flag.no_sync_out_pci = 1; | 203 | irq_ptr->siga_flag.sync_out_after_pci = 1; |
203 | |||
204 | if (irq_ptr->siga_flag.no_sync_out_pci && | ||
205 | irq_ptr->siga_flag.no_sync_ti) | ||
206 | irq_ptr->siga_flag.no_sync_out_ti = 1; | ||
207 | } | 204 | } |
208 | 205 | ||
209 | static void check_and_setup_qebsm(struct qdio_irq *irq_ptr, | 206 | static void check_and_setup_qebsm(struct qdio_irq *irq_ptr, |
@@ -451,7 +448,7 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, | |||
451 | char s[80]; | 448 | char s[80]; |
452 | 449 | ||
453 | snprintf(s, 80, "qdio: %s %s on SC %x using " | 450 | snprintf(s, 80, "qdio: %s %s on SC %x using " |
454 | "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n", | 451 | "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s\n", |
455 | dev_name(&cdev->dev), | 452 | dev_name(&cdev->dev), |
456 | (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" : | 453 | (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" : |
457 | ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"), | 454 | ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"), |
@@ -463,9 +460,8 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, | |||
463 | (irq_ptr->siga_flag.input) ? "R" : " ", | 460 | (irq_ptr->siga_flag.input) ? "R" : " ", |
464 | (irq_ptr->siga_flag.output) ? "W" : " ", | 461 | (irq_ptr->siga_flag.output) ? "W" : " ", |
465 | (irq_ptr->siga_flag.sync) ? "S" : " ", | 462 | (irq_ptr->siga_flag.sync) ? "S" : " ", |
466 | (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ", | 463 | (irq_ptr->siga_flag.sync_after_ai) ? "A" : " ", |
467 | (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ", | 464 | (irq_ptr->siga_flag.sync_out_after_pci) ? "P" : " "); |
468 | (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " "); | ||
469 | printk(KERN_INFO "%s", s); | 465 | printk(KERN_INFO "%s", s); |
470 | } | 466 | } |
471 | 467 | ||
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5d9c66627b6e..5c4e741d8221 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -8,6 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | #include <linux/io.h> | 9 | #include <linux/io.h> |
10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
11 | #include <linux/kernel_stat.h> | ||
11 | #include <asm/atomic.h> | 12 | #include <asm/atomic.h> |
12 | #include <asm/debug.h> | 13 | #include <asm/debug.h> |
13 | #include <asm/qdio.h> | 14 | #include <asm/qdio.h> |
@@ -35,22 +36,8 @@ static u8 *tiqdio_alsi; | |||
35 | 36 | ||
36 | struct indicator_t *q_indicators; | 37 | struct indicator_t *q_indicators; |
37 | 38 | ||
38 | static int css_qdio_omit_svs; | ||
39 | |||
40 | static u64 last_ai_time; | 39 | static u64 last_ai_time; |
41 | 40 | ||
42 | static inline unsigned long do_clear_global_summary(void) | ||
43 | { | ||
44 | register unsigned long __fn asm("1") = 3; | ||
45 | register unsigned long __tmp asm("2"); | ||
46 | register unsigned long __time asm("3"); | ||
47 | |||
48 | asm volatile( | ||
49 | " .insn rre,0xb2650000,2,0" | ||
50 | : "+d" (__fn), "=d" (__tmp), "=d" (__time)); | ||
51 | return __time; | ||
52 | } | ||
53 | |||
54 | /* returns addr for the device state change indicator */ | 41 | /* returns addr for the device state change indicator */ |
55 | static u32 *get_indicator(void) | 42 | static u32 *get_indicator(void) |
56 | { | 43 | { |
@@ -83,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) | |||
83 | struct qdio_q *q; | 70 | struct qdio_q *q; |
84 | int i; | 71 | int i; |
85 | 72 | ||
86 | /* No TDD facility? If we must use SIGA-s we can also omit SVS. */ | ||
87 | if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync) | ||
88 | css_qdio_omit_svs = 1; | ||
89 | |||
90 | mutex_lock(&tiq_list_lock); | 73 | mutex_lock(&tiq_list_lock); |
91 | for_each_input_queue(irq_ptr, q, i) | 74 | for_each_input_queue(irq_ptr, q, i) |
92 | list_add_rcu(&q->entry, &tiq_list); | 75 | list_add_rcu(&q->entry, &tiq_list); |
@@ -112,9 +95,9 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) | |||
112 | } | 95 | } |
113 | } | 96 | } |
114 | 97 | ||
115 | static inline int shared_ind_used(void) | 98 | static inline u32 shared_ind_set(void) |
116 | { | 99 | { |
117 | return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count); | 100 | return q_indicators[TIQDIO_SHARED_IND].ind; |
118 | } | 101 | } |
119 | 102 | ||
120 | /** | 103 | /** |
@@ -124,20 +107,11 @@ static inline int shared_ind_used(void) | |||
124 | */ | 107 | */ |
125 | static void tiqdio_thinint_handler(void *alsi, void *data) | 108 | static void tiqdio_thinint_handler(void *alsi, void *data) |
126 | { | 109 | { |
110 | u32 si_used = shared_ind_set(); | ||
127 | struct qdio_q *q; | 111 | struct qdio_q *q; |
128 | 112 | ||
129 | last_ai_time = S390_lowcore.int_clock; | 113 | last_ai_time = S390_lowcore.int_clock; |
130 | 114 | kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; | |
131 | /* | ||
132 | * SVS only when needed: issue SVS to benefit from iqdio interrupt | ||
133 | * avoidance (SVS clears adapter interrupt suppression overwrite). | ||
134 | */ | ||
135 | if (!css_qdio_omit_svs) | ||
136 | do_clear_global_summary(); | ||
137 | |||
138 | /* reset local summary indicator */ | ||
139 | if (shared_ind_used()) | ||
140 | xchg(tiqdio_alsi, 0); | ||
141 | 115 | ||
142 | /* protect tiq_list entries, only changed in activate or shutdown */ | 116 | /* protect tiq_list entries, only changed in activate or shutdown */ |
143 | rcu_read_lock(); | 117 | rcu_read_lock(); |
@@ -146,7 +120,10 @@ static void tiqdio_thinint_handler(void *alsi, void *data) | |||
146 | list_for_each_entry_rcu(q, &tiq_list, entry) { | 120 | list_for_each_entry_rcu(q, &tiq_list, entry) { |
147 | 121 | ||
148 | /* only process queues from changed sets */ | 122 | /* only process queues from changed sets */ |
149 | if (!*q->irq_ptr->dsci) | 123 | if (unlikely(shared_ind(q->irq_ptr->dsci))) { |
124 | if (!si_used) | ||
125 | continue; | ||
126 | } else if (!*q->irq_ptr->dsci) | ||
150 | continue; | 127 | continue; |
151 | 128 | ||
152 | if (q->u.in.queue_start_poll) { | 129 | if (q->u.in.queue_start_poll) { |
@@ -162,7 +139,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data) | |||
162 | q->irq_ptr->int_parm); | 139 | q->irq_ptr->int_parm); |
163 | } else { | 140 | } else { |
164 | /* only clear it if the indicator is non-shared */ | 141 | /* only clear it if the indicator is non-shared */ |
165 | if (!shared_ind(q->irq_ptr)) | 142 | if (!shared_ind(q->irq_ptr->dsci)) |
166 | xchg(q->irq_ptr->dsci, 0); | 143 | xchg(q->irq_ptr->dsci, 0); |
167 | /* | 144 | /* |
168 | * Call inbound processing but not directly | 145 | * Call inbound processing but not directly |
@@ -178,13 +155,8 @@ static void tiqdio_thinint_handler(void *alsi, void *data) | |||
178 | * If the shared indicator was used clear it now after all queues | 155 | * If the shared indicator was used clear it now after all queues |
179 | * were processed. | 156 | * were processed. |
180 | */ | 157 | */ |
181 | if (shared_ind_used()) { | 158 | if (si_used && shared_ind_set()) |
182 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); | 159 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); |
183 | |||
184 | /* prevent racing */ | ||
185 | if (*tiqdio_alsi) | ||
186 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7); | ||
187 | } | ||
188 | } | 160 | } |
189 | 161 | ||
190 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) | 162 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) |
@@ -269,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr) | |||
269 | { | 241 | { |
270 | if (!is_thinint_irq(irq_ptr)) | 242 | if (!is_thinint_irq(irq_ptr)) |
271 | return 0; | 243 | return 0; |
272 | |||
273 | /* Check for aif time delay disablement. If installed, | ||
274 | * omit SVS even under LPAR | ||
275 | */ | ||
276 | if (css_general_characteristics.aif_tdd) | ||
277 | css_qdio_omit_svs = 1; | ||
278 | return set_subchannel_ind(irq_ptr, 0); | 244 | return set_subchannel_ind(irq_ptr, 0); |
279 | } | 245 | } |
280 | 246 | ||