diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2011-10-30 10:17:06 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-10-30 10:16:45 -0400 |
commit | 25f269f17316549e026c5dd0db7526411a504de6 (patch) | |
tree | 05897a8a28ff4cd6e81cc7ef12da405ab820c4f3 /drivers/s390/cio/qdio_main.c | |
parent | a2b86019826cb97fd964fbaf101410c64cd78681 (diff) |
[S390] qdio: EQBS retry after CCQ 96
Running under z/VM with QIOASSIST enabled, qdio queues could stall if EQBS
did not extract all SBAL states. Add an instant retry for EQBS and, if the
retry fails, set up a timer to ensure outstanding SBALs are processed later.
While at it, optimize qdio_do_eqbs and qdio_do_sqbs to eliminate 3 jumps on
the hot path.
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/qdio_main.c')
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 81 |
1 files changed, 41 insertions, 40 deletions
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index a76d6764ce67..7c567b2268a7 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c | |||
@@ -104,9 +104,12 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) | |||
104 | /* all done or next buffer state different */ | 104 | /* all done or next buffer state different */ |
105 | if (ccq == 0 || ccq == 32) | 105 | if (ccq == 0 || ccq == 32) |
106 | return 0; | 106 | return 0; |
107 | /* not all buffers processed */ | 107 | /* no buffer processed */ |
108 | if (ccq == 96 || ccq == 97) | 108 | if (ccq == 97) |
109 | return 1; | 109 | return 1; |
110 | /* not all buffers processed */ | ||
111 | if (ccq == 96) | ||
112 | return 2; | ||
110 | /* notify devices immediately */ | 113 | /* notify devices immediately */ |
111 | DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq); | 114 | DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq); |
112 | return -EIO; | 115 | return -EIO; |
@@ -126,10 +129,8 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) | |||
126 | static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, | 129 | static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, |
127 | int start, int count, int auto_ack) | 130 | int start, int count, int auto_ack) |
128 | { | 131 | { |
132 | int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0; | ||
129 | unsigned int ccq = 0; | 133 | unsigned int ccq = 0; |
130 | int tmp_count = count, tmp_start = start; | ||
131 | int nr = q->nr; | ||
132 | int rc; | ||
133 | 134 | ||
134 | BUG_ON(!q->irq_ptr->sch_token); | 135 | BUG_ON(!q->irq_ptr->sch_token); |
135 | qperf_inc(q, eqbs); | 136 | qperf_inc(q, eqbs); |
@@ -140,30 +141,34 @@ again: | |||
140 | ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count, | 141 | ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count, |
141 | auto_ack); | 142 | auto_ack); |
142 | rc = qdio_check_ccq(q, ccq); | 143 | rc = qdio_check_ccq(q, ccq); |
143 | 144 | if (!rc) | |
144 | /* At least one buffer was processed, return and extract the remaining | 145 | return count - tmp_count; |
145 | * buffers later. | ||
146 | */ | ||
147 | if ((ccq == 96) && (count != tmp_count)) { | ||
148 | qperf_inc(q, eqbs_partial); | ||
149 | return (count - tmp_count); | ||
150 | } | ||
151 | 146 | ||
152 | if (rc == 1) { | 147 | if (rc == 1) { |
153 | DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq); | 148 | DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq); |
154 | goto again; | 149 | goto again; |
155 | } | 150 | } |
156 | 151 | ||
157 | if (rc < 0) { | 152 | if (rc == 2) { |
158 | DBF_ERROR("%4x EQBS ERROR", SCH_NO(q)); | 153 | BUG_ON(tmp_count == count); |
159 | DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); | 154 | qperf_inc(q, eqbs_partial); |
160 | q->handler(q->irq_ptr->cdev, | 155 | DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x", |
161 | QDIO_ERROR_ACTIVATE_CHECK_CONDITION, | 156 | tmp_count); |
162 | q->nr, q->first_to_kick, count, | 157 | /* |
163 | q->irq_ptr->int_parm); | 158 | * Retry once, if that fails bail out and process the |
164 | return 0; | 159 | * extracted buffers before trying again. |
160 | */ | ||
161 | if (!retried++) | ||
162 | goto again; | ||
163 | else | ||
164 | return count - tmp_count; | ||
165 | } | 165 | } |
166 | return count - tmp_count; | 166 | |
167 | DBF_ERROR("%4x EQBS ERROR", SCH_NO(q)); | ||
168 | DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); | ||
169 | q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, | ||
170 | 0, -1, -1, q->irq_ptr->int_parm); | ||
171 | return 0; | ||
167 | } | 172 | } |
168 | 173 | ||
169 | /** | 174 | /** |
@@ -196,22 +201,22 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, | |||
196 | again: | 201 | again: |
197 | ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); | 202 | ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count); |
198 | rc = qdio_check_ccq(q, ccq); | 203 | rc = qdio_check_ccq(q, ccq); |
199 | if (rc == 1) { | 204 | if (!rc) { |
205 | WARN_ON(tmp_count); | ||
206 | return count - tmp_count; | ||
207 | } | ||
208 | |||
209 | if (rc == 1 || rc == 2) { | ||
200 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq); | 210 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq); |
201 | qperf_inc(q, sqbs_partial); | 211 | qperf_inc(q, sqbs_partial); |
202 | goto again; | 212 | goto again; |
203 | } | 213 | } |
204 | if (rc < 0) { | 214 | |
205 | DBF_ERROR("%4x SQBS ERROR", SCH_NO(q)); | 215 | DBF_ERROR("%4x SQBS ERROR", SCH_NO(q)); |
206 | DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); | 216 | DBF_ERROR("%3d%3d%2d", count, tmp_count, nr); |
207 | q->handler(q->irq_ptr->cdev, | 217 | q->handler(q->irq_ptr->cdev, QDIO_ERROR_ACTIVATE_CHECK_CONDITION, |
208 | QDIO_ERROR_ACTIVATE_CHECK_CONDITION, | 218 | 0, -1, -1, q->irq_ptr->int_parm); |
209 | q->nr, q->first_to_kick, count, | 219 | return 0; |
210 | q->irq_ptr->int_parm); | ||
211 | return 0; | ||
212 | } | ||
213 | WARN_ON(tmp_count); | ||
214 | return count - tmp_count; | ||
215 | } | 220 | } |
216 | 221 | ||
217 | /* returns number of examined buffers and their common state in *state */ | 222 | /* returns number of examined buffers and their common state in *state */ |
@@ -915,10 +920,6 @@ static void __qdio_outbound_processing(struct qdio_q *q) | |||
915 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) | 920 | if (!pci_out_supported(q) && !qdio_outbound_q_done(q)) |
916 | goto sched; | 921 | goto sched; |
917 | 922 | ||
918 | /* bail out for HiperSockets unicast queues */ | ||
919 | if (queue_type(q) == QDIO_IQDIO_QFMT && !multicast_outbound(q)) | ||
920 | return; | ||
921 | |||
922 | if ((queue_type(q) == QDIO_IQDIO_QFMT) && | 923 | if ((queue_type(q) == QDIO_IQDIO_QFMT) && |
923 | (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) | 924 | (atomic_read(&q->nr_buf_used)) > QDIO_IQDIO_POLL_LVL) |
924 | goto sched; | 925 | goto sched; |
@@ -928,8 +929,8 @@ static void __qdio_outbound_processing(struct qdio_q *q) | |||
928 | 929 | ||
929 | /* | 930 | /* |
930 | * Now we know that queue type is either qeth without pci enabled | 931 | * Now we know that queue type is either qeth without pci enabled |
931 | * or HiperSockets multicast. Make sure buffer switch from PRIMED to | 932 | * or HiperSockets. Make sure buffer switch from PRIMED to EMPTY |
932 | * EMPTY is noticed and outbound_handler is called after some time. | 933 | * is noticed and outbound_handler is called after some time. |
933 | */ | 934 | */ |
934 | if (qdio_outbound_q_done(q)) | 935 | if (qdio_outbound_q_done(q)) |
935 | del_timer(&q->u.out.timer); | 936 | del_timer(&q->u.out.timer); |