]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Gerald Schaefer <geraldsc@de.ibm.com> |
2 | Subject: qdio: fix hipersocket busy error handling | |
3 | References: bnc#484767,LTC#52204 | |
4 | ||
5 | Symptom: Lower performance of hipersocket connections in case of | |
6 | busy errors. | |
7 | Problem: The immediate retry for a busy condition is missing and the | |
8 | qeth notification about a failed send operation may indicate | |
9 | a wrong buffer position since a temporary SIGA error is not | |
10 | reported immediately. | |
11 | Solution: Retry immediately after a busy condition and report SIGA errors | |
12 | directly to qeth. | |
13 | ||
14 | Acked-by: John Jolly <jjolly@suse.de> | |
15 | --- | |
16 | drivers/s390/cio/qdio.h | 7 - | |
17 | drivers/s390/cio/qdio_main.c | 181 ++++++++++++++----------------------------- | |
18 | 2 files changed, 63 insertions(+), 125 deletions(-) | |
19 | ||
20 | Index: linux-2.6.27-SLE11_BRANCH/drivers/s390/cio/qdio.h | |
21 | =================================================================== | |
22 | --- linux-2.6.27-SLE11_BRANCH.orig/drivers/s390/cio/qdio.h | |
23 | +++ linux-2.6.27-SLE11_BRANCH/drivers/s390/cio/qdio.h | |
24 | @@ -14,7 +14,6 @@ | |
25 | #include "chsc.h" | |
26 | ||
27 | #define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */ | |
28 | -#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */ | |
29 | #define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */ | |
30 | ||
31 | /* | |
32 | @@ -195,12 +194,6 @@ struct qdio_input_q { | |
33 | }; | |
34 | ||
35 | struct qdio_output_q { | |
36 | - /* failed siga-w attempts*/ | |
37 | - atomic_t busy_siga_counter; | |
38 | - | |
39 | - /* start time of busy condition */ | |
40 | - u64 timestamp; | |
41 | - | |
42 | /* PCIs are enabled for the queue */ | |
43 | int pci_out_enabled; | |
44 | ||
45 | Index: linux-2.6.27-SLE11_BRANCH/drivers/s390/cio/qdio_main.c | |
46 | =================================================================== | |
47 | --- linux-2.6.27-SLE11_BRANCH.orig/drivers/s390/cio/qdio_main.c | |
48 | +++ linux-2.6.27-SLE11_BRANCH/drivers/s390/cio/qdio_main.c | |
49 | @@ -74,7 +74,7 @@ static inline int do_siga_input(struct s | |
50 | * Note: For IQDC unicast queues only the highest priority queue is processed. | |
51 | */ | |
52 | static inline int do_siga_output(unsigned long schid, unsigned long mask, | |
53 | - u32 *bb, unsigned int fc) | |
54 | + unsigned int *bb, unsigned int fc) | |
55 | { | |
56 | register unsigned long __fc asm("0") = fc; | |
57 | register unsigned long __schid asm("1") = schid; | |
58 | @@ -278,8 +278,7 @@ static int qdio_siga_sync(struct qdio_q | |
59 | if (!need_siga_sync(q)) | |
60 | return 0; | |
61 | ||
62 | - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:"); | |
63 | - DBF_DEV_HEX(DBF_INFO, q->irq_ptr, q, sizeof(void *)); | |
64 | + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); | |
65 | qdio_perf_stat_inc(&perf_stats.siga_sync); | |
66 | ||
67 | cc = do_siga_sync(q->irq_ptr->schid, output, input); | |
68 | @@ -306,43 +305,34 @@ static inline int qdio_siga_sync_all(str | |
69 | return qdio_siga_sync(q, ~0U, ~0U); | |
70 | } | |
71 | ||
72 | -static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit) | |
73 | +static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) | |
74 | { | |
75 | - unsigned int fc = 0; | |
76 | unsigned long schid; | |
77 | + unsigned int fc = 0; | |
78 | + u64 start_time = 0; | |
79 | + int cc; | |
80 | ||
81 | - if (!is_qebsm(q)) | |
82 | - schid = *((u32 *)&q->irq_ptr->schid); | |
83 | - else { | |
84 | + if (is_qebsm(q)) { | |
85 | schid = q->irq_ptr->sch_token; | |
86 | fc |= 0x80; | |
87 | } | |
88 | - return do_siga_output(schid, q->mask, busy_bit, fc); | |
89 | -} | |
90 | - | |
91 | -static int qdio_siga_output(struct qdio_q *q) | |
92 | -{ | |
93 | - int cc; | |
94 | - u32 busy_bit; | |
95 | - u64 start_time = 0; | |
96 | + else | |
97 | + schid = *((u32 *)&q->irq_ptr->schid); | |
98 | ||
99 | - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); | |
100 | - qdio_perf_stat_inc(&perf_stats.siga_out); | |
101 | again: | |
102 | - cc = qdio_do_siga_output(q, &busy_bit); | |
103 | - if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) { | |
104 | - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w bb:%2d", q->nr); | |
105 | + cc = do_siga_output(schid, q->mask, busy_bit, fc); | |
106 | ||
107 | - if (!start_time) | |
108 | + /* hipersocket busy condition */ | |
109 | + if (*busy_bit) { | |
110 | + WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); | |
111 | + | |
112 | + if (!start_time) { | |
113 | start_time = get_usecs(); | |
114 | - else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE) | |
115 | + goto again; | |
116 | + } | |
117 | + if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE) | |
118 | goto again; | |
119 | } | |
120 | - | |
121 | - if (cc == 2 && busy_bit) | |
122 | - cc |= QDIO_ERROR_SIGA_BUSY; | |
123 | - if (cc) | |
124 | - DBF_ERROR("%4x SIGA-W:%2d", SCH_NO(q), cc); | |
125 | return cc; | |
126 | } | |
127 | ||
128 | @@ -390,7 +380,7 @@ inline void qdio_stop_polling(struct qdi | |
129 | ||
130 | static void announce_buffer_error(struct qdio_q *q, int count) | |
131 | { | |
132 | - q->qdio_error = QDIO_ERROR_SLSB_STATE; | |
133 | + q->qdio_error |= QDIO_ERROR_SLSB_STATE; | |
134 | ||
135 | /* special handling for no target buffer empty */ | |
136 | if ((!q->is_input_q && | |
137 | @@ -707,69 +697,34 @@ static inline int qdio_outbound_q_moved( | |
138 | return 0; | |
139 | } | |
140 | ||
141 | -/* | |
142 | - * VM could present us cc=2 and busy bit set on SIGA-write | |
143 | - * during reconfiguration of their Guest LAN (only in iqdio mode, | |
144 | - * otherwise qdio is asynchronous and cc=2 and busy bit there will take | |
145 | - * the queues down immediately). | |
146 | - * | |
147 | - * Therefore qdio_siga_output will try for a short time constantly, | |
148 | - * if such a condition occurs. If it doesn't change, it will | |
149 | - * increase the busy_siga_counter and save the timestamp, and | |
150 | - * schedule the queue for later processing. qdio_outbound_processing | |
151 | - * will check out the counter. If non-zero, it will call qdio_kick_outbound_q | |
152 | - * as often as the value of the counter. This will attempt further SIGA | |
153 | - * instructions. For each successful SIGA, the counter is | |
154 | - * decreased, for failing SIGAs the counter remains the same, after | |
155 | - * all. After some time of no movement, qdio_kick_outbound_q will | |
156 | - * finally fail and reflect corresponding error codes to call | |
157 | - * the upper layer module and have it take the queues down. | |
158 | - * | |
159 | - * Note that this is a change from the original HiperSockets design | |
160 | - * (saying cc=2 and busy bit means take the queues down), but in | |
161 | - * these days Guest LAN didn't exist... excessive cc=2 with busy bit | |
162 | - * conditions will still take the queues down, but the threshold is | |
163 | - * higher due to the Guest LAN environment. | |
164 | - * | |
165 | - * Called from outbound tasklet and do_QDIO handler. | |
166 | - */ | |
167 | -static void qdio_kick_outbound_q(struct qdio_q *q) | |
168 | +static int qdio_kick_outbound_q(struct qdio_q *q) | |
169 | { | |
170 | - int rc; | |
171 | - | |
172 | - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickoutq:%1d", q->nr); | |
173 | + unsigned int busy_bit; | |
174 | + int cc; | |
175 | ||
176 | if (!need_siga_out(q)) | |
177 | - return; | |
178 | + return 0; | |
179 | ||
180 | - rc = qdio_siga_output(q); | |
181 | - switch (rc) { | |
182 | + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); | |
183 | + qdio_perf_stat_inc(&perf_stats.siga_out); | |
184 | + | |
185 | + cc = qdio_siga_output(q, &busy_bit); | |
186 | + switch (cc) { | |
187 | case 0: | |
188 | - /* TODO: improve error handling for CC=0 case */ | |
189 | - if (q->u.out.timestamp) | |
190 | - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "cc2 rslv:%4x", | |
191 | - atomic_read(&q->u.out.busy_siga_counter)); | |
192 | - /* went smooth this time, reset timestamp */ | |
193 | - q->u.out.timestamp = 0; | |
194 | break; | |
195 | - /* cc=2 and busy bit */ | |
196 | - case (2 | QDIO_ERROR_SIGA_BUSY): | |
197 | - atomic_inc(&q->u.out.busy_siga_counter); | |
198 | - | |
199 | - /* if the last siga was successful, save timestamp here */ | |
200 | - if (!q->u.out.timestamp) | |
201 | - q->u.out.timestamp = get_usecs(); | |
202 | - | |
203 | - /* if we're in time, don't touch qdio_error */ | |
204 | - if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) { | |
205 | - tasklet_schedule(&q->tasklet); | |
206 | - break; | |
207 | - } | |
208 | - DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); | |
209 | - default: | |
210 | - /* for plain cc=1, 2 or 3 */ | |
211 | - q->qdio_error = rc; | |
212 | + case 2: | |
213 | + if (busy_bit) { | |
214 | + DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr); | |
215 | + cc |= QDIO_ERROR_SIGA_BUSY; | |
216 | + } else | |
217 | + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr); | |
218 | + break; | |
219 | + case 1: | |
220 | + case 3: | |
221 | + DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc); | |
222 | + break; | |
223 | } | |
224 | + return cc; | |
225 | } | |
226 | ||
227 | static void qdio_kick_outbound_handler(struct qdio_q *q) | |
228 | @@ -799,17 +754,7 @@ static void qdio_kick_outbound_handler(s | |
229 | ||
230 | static void __qdio_outbound_processing(struct qdio_q *q) | |
231 | { | |
232 | - int siga_attempts; | |
233 | - | |
234 | qdio_perf_stat_inc(&perf_stats.tasklet_outbound); | |
235 | - | |
236 | - /* see comment in qdio_kick_outbound_q */ | |
237 | - siga_attempts = atomic_read(&q->u.out.busy_siga_counter); | |
238 | - while (siga_attempts--) { | |
239 | - atomic_dec(&q->u.out.busy_siga_counter); | |
240 | - qdio_kick_outbound_q(q); | |
241 | - } | |
242 | - | |
243 | BUG_ON(atomic_read(&q->nr_buf_used) < 0); | |
244 | ||
245 | if (qdio_outbound_q_moved(q)) | |
246 | @@ -1478,10 +1423,10 @@ static inline int buf_in_between(int buf | |
247 | * @bufnr: first buffer to process | |
248 | * @count: how many buffers are emptied | |
249 | */ | |
250 | -static void handle_inbound(struct qdio_q *q, unsigned int callflags, | |
251 | - int bufnr, int count) | |
252 | +static int handle_inbound(struct qdio_q *q, unsigned int callflags, | |
253 | + int bufnr, int count) | |
254 | { | |
255 | - int used, rc, diff; | |
256 | + int used, diff; | |
257 | ||
258 | if (!q->u.in.polling) | |
259 | goto set; | |
260 | @@ -1519,13 +1464,11 @@ set: | |
261 | ||
262 | /* no need to signal as long as the adapter had free buffers */ | |
263 | if (used) | |
264 | - return; | |
265 | + return 0; | |
266 | ||
267 | - if (need_siga_in(q)) { | |
268 | - rc = qdio_siga_input(q); | |
269 | - if (rc) | |
270 | - q->qdio_error = rc; | |
271 | - } | |
272 | + if (need_siga_in(q)) | |
273 | + return qdio_siga_input(q); | |
274 | + return 0; | |
275 | } | |
276 | ||
277 | /** | |
278 | @@ -1535,11 +1478,11 @@ set: | |
279 | * @bufnr: first buffer to process | |
280 | * @count: how many buffers are filled | |
281 | */ | |
282 | -static void handle_outbound(struct qdio_q *q, unsigned int callflags, | |
283 | - int bufnr, int count) | |
284 | +static int handle_outbound(struct qdio_q *q, unsigned int callflags, | |
285 | + int bufnr, int count) | |
286 | { | |
287 | unsigned char state; | |
288 | - int used; | |
289 | + int used, rc = 0; | |
290 | ||
291 | qdio_perf_stat_inc(&perf_stats.outbound_handler); | |
292 | ||
293 | @@ -1554,14 +1497,17 @@ static void handle_outbound(struct qdio_ | |
294 | ||
295 | if (queue_type(q) == QDIO_IQDIO_QFMT) { | |
296 | if (multicast_outbound(q)) | |
297 | - qdio_kick_outbound_q(q); | |
298 | + rc = qdio_kick_outbound_q(q); | |
299 | else | |
300 | /* | |
301 | * One siga-w per buffer required for unicast | |
302 | * HiperSockets. | |
303 | */ | |
304 | - while (count--) | |
305 | - qdio_kick_outbound_q(q); | |
306 | + while (count--) { | |
307 | + rc = qdio_kick_outbound_q(q); | |
308 | + if (rc) | |
309 | + goto out; | |
310 | + } | |
311 | goto out; | |
312 | } | |
313 | ||
314 | @@ -1573,7 +1519,7 @@ static void handle_outbound(struct qdio_ | |
315 | /* try to fast requeue buffers */ | |
316 | get_buf_state(q, prev_buf(bufnr), &state, 0); | |
317 | if (state != SLSB_CU_OUTPUT_PRIMED) | |
318 | - qdio_kick_outbound_q(q); | |
319 | + rc = qdio_kick_outbound_q(q); | |
320 | else { | |
321 | DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req"); | |
322 | qdio_perf_stat_inc(&perf_stats.fast_requeue); | |
323 | @@ -1581,6 +1527,7 @@ static void handle_outbound(struct qdio_ | |
324 | out: | |
325 | /* Fixme: could wait forever if called from process context */ | |
326 | tasklet_schedule(&q->tasklet); | |
327 | + return rc; | |
328 | } | |
329 | ||
330 | /** | |
331 | @@ -1619,14 +1566,12 @@ int do_QDIO(struct ccw_device *cdev, uns | |
332 | return -EBUSY; | |
333 | ||
334 | if (callflags & QDIO_FLAG_SYNC_INPUT) | |
335 | - handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr, | |
336 | - count); | |
337 | + return handle_inbound(irq_ptr->input_qs[q_nr], | |
338 | + callflags, bufnr, count); | |
339 | else if (callflags & QDIO_FLAG_SYNC_OUTPUT) | |
340 | - handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr, | |
341 | - count); | |
342 | - else | |
343 | - return -EINVAL; | |
344 | - return 0; | |
345 | + return handle_outbound(irq_ptr->output_qs[q_nr], | |
346 | + callflags, bufnr, count); | |
347 | + return -EINVAL; | |
348 | } | |
349 | EXPORT_SYMBOL_GPL(do_QDIO); | |
350 |