Commit | Line | Data |
---|---|---|
7a291083 JBT |
1 | /* |
2 | * linux/drivers/net/ehea/ehea_qmr.c | |
3 | * | |
4 | * eHEA ethernet device driver for IBM eServer System p | |
5 | * | |
6 | * (C) Copyright IBM Corp. 2006 | |
7 | * | |
8 | * Authors: | |
9 | * Christoph Raisch <raisch@de.ibm.com> | |
10 | * Jan-Bernd Themann <themann@de.ibm.com> | |
11 | * Thomas Klein <tklein@de.ibm.com> | |
12 | * | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2, or (at your option) | |
17 | * any later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this program; if not, write to the Free Software | |
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
27 | */ | |
28 | ||
d7fe0f24 | 29 | #include <linux/mm.h> |
7a291083 JBT |
30 | #include "ehea.h" |
31 | #include "ehea_phyp.h" | |
32 | #include "ehea_qmr.h" | |
33 | ||
44c82152 TK |
34 | |
35 | struct ehea_busmap ehea_bmap = { 0, 0, NULL }; | |
44c82152 TK |
36 | |
37 | ||
7a291083 JBT |
38 | static void *hw_qpageit_get_inc(struct hw_queue *queue) |
39 | { | |
40 | void *retvalue = hw_qeit_get(queue); | |
41 | ||
42 | queue->current_q_offset += queue->pagesize; | |
43 | if (queue->current_q_offset > queue->queue_length) { | |
44 | queue->current_q_offset -= queue->pagesize; | |
45 | retvalue = NULL; | |
46 | } else if (((u64) retvalue) & (EHEA_PAGESIZE-1)) { | |
47 | ehea_error("not on pageboundary"); | |
48 | retvalue = NULL; | |
49 | } | |
50 | return retvalue; | |
51 | } | |
52 | ||
53 | static int hw_queue_ctor(struct hw_queue *queue, const u32 nr_of_pages, | |
54 | const u32 pagesize, const u32 qe_size) | |
55 | { | |
56 | int pages_per_kpage = PAGE_SIZE / pagesize; | |
57 | int i, k; | |
58 | ||
59 | if ((pagesize > PAGE_SIZE) || (!pages_per_kpage)) { | |
60 | ehea_error("pagesize conflict! kernel pagesize=%d, " | |
61 | "ehea pagesize=%d", (int)PAGE_SIZE, (int)pagesize); | |
62 | return -EINVAL; | |
63 | } | |
64 | ||
65 | queue->queue_length = nr_of_pages * pagesize; | |
f67c6275 | 66 | queue->queue_pages = kmalloc(nr_of_pages * sizeof(void *), GFP_KERNEL); |
7a291083 JBT |
67 | if (!queue->queue_pages) { |
68 | ehea_error("no mem for queue_pages"); | |
69 | return -ENOMEM; | |
70 | } | |
71 | ||
72 | /* | |
73 | * allocate pages for queue: | |
74 | * outer loop allocates whole kernel pages (page aligned) and | |
75 | * inner loop divides a kernel page into smaller hea queue pages | |
76 | */ | |
77 | i = 0; | |
78 | while (i < nr_of_pages) { | |
f67c6275 | 79 | u8 *kpage = (u8 *)get_zeroed_page(GFP_KERNEL); |
7a291083 JBT |
80 | if (!kpage) |
81 | goto out_nomem; | |
82 | for (k = 0; k < pages_per_kpage && i < nr_of_pages; k++) { | |
f67c6275 | 83 | (queue->queue_pages)[i] = (struct ehea_page *)kpage; |
7a291083 JBT |
84 | kpage += pagesize; |
85 | i++; | |
86 | } | |
87 | } | |
88 | ||
89 | queue->current_q_offset = 0; | |
90 | queue->qe_size = qe_size; | |
91 | queue->pagesize = pagesize; | |
92 | queue->toggle_state = 1; | |
93 | ||
94 | return 0; | |
95 | out_nomem: | |
96 | for (i = 0; i < nr_of_pages; i += pages_per_kpage) { | |
97 | if (!(queue->queue_pages)[i]) | |
98 | break; | |
99 | free_page((unsigned long)(queue->queue_pages)[i]); | |
100 | } | |
101 | return -ENOMEM; | |
102 | } | |
103 | ||
104 | static void hw_queue_dtor(struct hw_queue *queue) | |
105 | { | |
106 | int pages_per_kpage = PAGE_SIZE / queue->pagesize; | |
107 | int i, nr_pages; | |
108 | ||
109 | if (!queue || !queue->queue_pages) | |
110 | return; | |
111 | ||
112 | nr_pages = queue->queue_length / queue->pagesize; | |
113 | ||
114 | for (i = 0; i < nr_pages; i += pages_per_kpage) | |
115 | free_page((unsigned long)(queue->queue_pages)[i]); | |
116 | ||
117 | kfree(queue->queue_pages); | |
118 | } | |
119 | ||
120 | struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter, | |
121 | int nr_of_cqe, u64 eq_handle, u32 cq_token) | |
122 | { | |
123 | struct ehea_cq *cq; | |
124 | struct h_epa epa; | |
125 | u64 *cq_handle_ref, hret, rpage; | |
126 | u32 act_nr_of_entries, act_pages, counter; | |
127 | int ret; | |
128 | void *vpage; | |
129 | ||
130 | cq = kzalloc(sizeof(*cq), GFP_KERNEL); | |
131 | if (!cq) { | |
132 | ehea_error("no mem for cq"); | |
133 | goto out_nomem; | |
134 | } | |
135 | ||
136 | cq->attr.max_nr_of_cqes = nr_of_cqe; | |
137 | cq->attr.cq_token = cq_token; | |
138 | cq->attr.eq_handle = eq_handle; | |
139 | ||
140 | cq->adapter = adapter; | |
141 | ||
142 | cq_handle_ref = &cq->fw_handle; | |
143 | act_nr_of_entries = 0; | |
144 | act_pages = 0; | |
145 | ||
146 | hret = ehea_h_alloc_resource_cq(adapter->handle, &cq->attr, | |
147 | &cq->fw_handle, &cq->epas); | |
148 | if (hret != H_SUCCESS) { | |
149 | ehea_error("alloc_resource_cq failed"); | |
150 | goto out_freemem; | |
151 | } | |
152 | ||
153 | ret = hw_queue_ctor(&cq->hw_queue, cq->attr.nr_pages, | |
154 | EHEA_PAGESIZE, sizeof(struct ehea_cqe)); | |
155 | if (ret) | |
156 | goto out_freeres; | |
157 | ||
158 | for (counter = 0; counter < cq->attr.nr_pages; counter++) { | |
159 | vpage = hw_qpageit_get_inc(&cq->hw_queue); | |
160 | if (!vpage) { | |
161 | ehea_error("hw_qpageit_get_inc failed"); | |
162 | goto out_kill_hwq; | |
163 | } | |
164 | ||
165 | rpage = virt_to_abs(vpage); | |
166 | hret = ehea_h_register_rpage(adapter->handle, | |
167 | 0, EHEA_CQ_REGISTER_ORIG, | |
168 | cq->fw_handle, rpage, 1); | |
169 | if (hret < H_SUCCESS) { | |
170 | ehea_error("register_rpage_cq failed ehea_cq=%p " | |
171 | "hret=%lx counter=%i act_pages=%i", | |
172 | cq, hret, counter, cq->attr.nr_pages); | |
173 | goto out_kill_hwq; | |
174 | } | |
175 | ||
176 | if (counter == (cq->attr.nr_pages - 1)) { | |
177 | vpage = hw_qpageit_get_inc(&cq->hw_queue); | |
178 | ||
179 | if ((hret != H_SUCCESS) || (vpage)) { | |
180 | ehea_error("registration of pages not " | |
181 | "complete hret=%lx\n", hret); | |
182 | goto out_kill_hwq; | |
183 | } | |
184 | } else { | |
185 | if ((hret != H_PAGE_REGISTERED) || (!vpage)) { | |
186 | ehea_error("CQ: registration of page failed " | |
187 | "hret=%lx\n", hret); | |
188 | goto out_kill_hwq; | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | hw_qeit_reset(&cq->hw_queue); | |
194 | epa = cq->epas.kernel; | |
195 | ehea_reset_cq_ep(cq); | |
196 | ehea_reset_cq_n1(cq); | |
197 | ||
198 | return cq; | |
199 | ||
200 | out_kill_hwq: | |
201 | hw_queue_dtor(&cq->hw_queue); | |
202 | ||
203 | out_freeres: | |
e542aa6b | 204 | ehea_h_free_resource(adapter->handle, cq->fw_handle, FORCE_FREE); |
7a291083 JBT |
205 | |
206 | out_freemem: | |
207 | kfree(cq); | |
208 | ||
209 | out_nomem: | |
210 | return NULL; | |
211 | } | |
212 | ||
e542aa6b | 213 | u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force) |
7a291083 | 214 | { |
e542aa6b JBT |
215 | u64 hret; |
216 | u64 adapter_handle = cq->adapter->handle; | |
217 | ||
d1d25aab | 218 | /* deregister all previous registered pages */ |
e542aa6b JBT |
219 | hret = ehea_h_free_resource(adapter_handle, cq->fw_handle, force); |
220 | if (hret != H_SUCCESS) | |
221 | return hret; | |
222 | ||
223 | hw_queue_dtor(&cq->hw_queue); | |
224 | kfree(cq); | |
225 | ||
226 | return hret; | |
227 | } | |
7a291083 | 228 | |
e542aa6b JBT |
229 | int ehea_destroy_cq(struct ehea_cq *cq) |
230 | { | |
231 | u64 hret; | |
7a291083 JBT |
232 | if (!cq) |
233 | return 0; | |
234 | ||
28721c89 | 235 | hcp_epas_dtor(&cq->epas); |
f67c6275 DM |
236 | hret = ehea_destroy_cq_res(cq, NORMAL_FREE); |
237 | if (hret == H_R_STATE) { | |
e542aa6b JBT |
238 | ehea_error_data(cq->adapter, cq->fw_handle); |
239 | hret = ehea_destroy_cq_res(cq, FORCE_FREE); | |
240 | } | |
1b5135d9 | 241 | |
7a291083 JBT |
242 | if (hret != H_SUCCESS) { |
243 | ehea_error("destroy CQ failed"); | |
244 | return -EIO; | |
245 | } | |
246 | ||
7a291083 JBT |
247 | return 0; |
248 | } | |
249 | ||
250 | struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter, | |
251 | const enum ehea_eq_type type, | |
252 | const u32 max_nr_of_eqes, const u8 eqe_gen) | |
253 | { | |
254 | int ret, i; | |
255 | u64 hret, rpage; | |
256 | void *vpage; | |
257 | struct ehea_eq *eq; | |
258 | ||
259 | eq = kzalloc(sizeof(*eq), GFP_KERNEL); | |
260 | if (!eq) { | |
261 | ehea_error("no mem for eq"); | |
262 | return NULL; | |
263 | } | |
264 | ||
265 | eq->adapter = adapter; | |
266 | eq->attr.type = type; | |
267 | eq->attr.max_nr_of_eqes = max_nr_of_eqes; | |
268 | eq->attr.eqe_gen = eqe_gen; | |
269 | spin_lock_init(&eq->spinlock); | |
270 | ||
271 | hret = ehea_h_alloc_resource_eq(adapter->handle, | |
272 | &eq->attr, &eq->fw_handle); | |
273 | if (hret != H_SUCCESS) { | |
274 | ehea_error("alloc_resource_eq failed"); | |
275 | goto out_freemem; | |
276 | } | |
277 | ||
278 | ret = hw_queue_ctor(&eq->hw_queue, eq->attr.nr_pages, | |
279 | EHEA_PAGESIZE, sizeof(struct ehea_eqe)); | |
280 | if (ret) { | |
281 | ehea_error("can't allocate eq pages"); | |
282 | goto out_freeres; | |
283 | } | |
284 | ||
285 | for (i = 0; i < eq->attr.nr_pages; i++) { | |
286 | vpage = hw_qpageit_get_inc(&eq->hw_queue); | |
287 | if (!vpage) { | |
288 | ehea_error("hw_qpageit_get_inc failed"); | |
289 | hret = H_RESOURCE; | |
290 | goto out_kill_hwq; | |
291 | } | |
292 | ||
293 | rpage = virt_to_abs(vpage); | |
294 | ||
295 | hret = ehea_h_register_rpage(adapter->handle, 0, | |
296 | EHEA_EQ_REGISTER_ORIG, | |
297 | eq->fw_handle, rpage, 1); | |
298 | ||
299 | if (i == (eq->attr.nr_pages - 1)) { | |
300 | /* last page */ | |
301 | vpage = hw_qpageit_get_inc(&eq->hw_queue); | |
f67c6275 | 302 | if ((hret != H_SUCCESS) || (vpage)) |
7a291083 | 303 | goto out_kill_hwq; |
f67c6275 | 304 | |
7a291083 | 305 | } else { |
f67c6275 | 306 | if ((hret != H_PAGE_REGISTERED) || (!vpage)) |
7a291083 | 307 | goto out_kill_hwq; |
f67c6275 | 308 | |
7a291083 JBT |
309 | } |
310 | } | |
311 | ||
312 | hw_qeit_reset(&eq->hw_queue); | |
313 | return eq; | |
314 | ||
315 | out_kill_hwq: | |
316 | hw_queue_dtor(&eq->hw_queue); | |
317 | ||
318 | out_freeres: | |
e542aa6b | 319 | ehea_h_free_resource(adapter->handle, eq->fw_handle, FORCE_FREE); |
7a291083 JBT |
320 | |
321 | out_freemem: | |
322 | kfree(eq); | |
323 | return NULL; | |
324 | } | |
325 | ||
326 | struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq) | |
327 | { | |
328 | struct ehea_eqe *eqe; | |
329 | unsigned long flags; | |
330 | ||
331 | spin_lock_irqsave(&eq->spinlock, flags); | |
f67c6275 | 332 | eqe = (struct ehea_eqe *)hw_eqit_eq_get_inc_valid(&eq->hw_queue); |
7a291083 JBT |
333 | spin_unlock_irqrestore(&eq->spinlock, flags); |
334 | ||
335 | return eqe; | |
336 | } | |
337 | ||
e542aa6b | 338 | u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force) |
7a291083 JBT |
339 | { |
340 | u64 hret; | |
341 | unsigned long flags; | |
342 | ||
7a291083 JBT |
343 | spin_lock_irqsave(&eq->spinlock, flags); |
344 | ||
e542aa6b | 345 | hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle, force); |
7a291083 JBT |
346 | spin_unlock_irqrestore(&eq->spinlock, flags); |
347 | ||
e542aa6b JBT |
348 | if (hret != H_SUCCESS) |
349 | return hret; | |
7a291083 JBT |
350 | |
351 | hw_queue_dtor(&eq->hw_queue); | |
352 | kfree(eq); | |
353 | ||
e542aa6b JBT |
354 | return hret; |
355 | } | |
356 | ||
357 | int ehea_destroy_eq(struct ehea_eq *eq) | |
358 | { | |
359 | u64 hret; | |
360 | if (!eq) | |
361 | return 0; | |
362 | ||
28721c89 JBT |
363 | hcp_epas_dtor(&eq->epas); |
364 | ||
f67c6275 DM |
365 | hret = ehea_destroy_eq_res(eq, NORMAL_FREE); |
366 | if (hret == H_R_STATE) { | |
e542aa6b JBT |
367 | ehea_error_data(eq->adapter, eq->fw_handle); |
368 | hret = ehea_destroy_eq_res(eq, FORCE_FREE); | |
369 | } | |
370 | ||
371 | if (hret != H_SUCCESS) { | |
372 | ehea_error("destroy EQ failed"); | |
373 | return -EIO; | |
d1d25aab | 374 | } |
e542aa6b | 375 | |
7a291083 JBT |
376 | return 0; |
377 | } | |
378 | ||
379 | /** | |
380 | * allocates memory for a queue and registers pages in phyp | |
381 | */ | |
382 | int ehea_qp_alloc_register(struct ehea_qp *qp, struct hw_queue *hw_queue, | |
383 | int nr_pages, int wqe_size, int act_nr_sges, | |
384 | struct ehea_adapter *adapter, int h_call_q_selector) | |
385 | { | |
386 | u64 hret, rpage; | |
387 | int ret, cnt; | |
388 | void *vpage; | |
389 | ||
390 | ret = hw_queue_ctor(hw_queue, nr_pages, EHEA_PAGESIZE, wqe_size); | |
391 | if (ret) | |
392 | return ret; | |
393 | ||
394 | for (cnt = 0; cnt < nr_pages; cnt++) { | |
395 | vpage = hw_qpageit_get_inc(hw_queue); | |
396 | if (!vpage) { | |
397 | ehea_error("hw_qpageit_get_inc failed"); | |
398 | goto out_kill_hwq; | |
399 | } | |
400 | rpage = virt_to_abs(vpage); | |
401 | hret = ehea_h_register_rpage(adapter->handle, | |
402 | 0, h_call_q_selector, | |
403 | qp->fw_handle, rpage, 1); | |
404 | if (hret < H_SUCCESS) { | |
405 | ehea_error("register_rpage_qp failed"); | |
406 | goto out_kill_hwq; | |
407 | } | |
408 | } | |
409 | hw_qeit_reset(hw_queue); | |
410 | return 0; | |
411 | ||
412 | out_kill_hwq: | |
413 | hw_queue_dtor(hw_queue); | |
414 | return -EIO; | |
415 | } | |
416 | ||
417 | static inline u32 map_wqe_size(u8 wqe_enc_size) | |
418 | { | |
419 | return 128 << wqe_enc_size; | |
420 | } | |
421 | ||
422 | struct ehea_qp *ehea_create_qp(struct ehea_adapter *adapter, | |
423 | u32 pd, struct ehea_qp_init_attr *init_attr) | |
424 | { | |
425 | int ret; | |
426 | u64 hret; | |
427 | struct ehea_qp *qp; | |
428 | u32 wqe_size_in_bytes_sq, wqe_size_in_bytes_rq1; | |
429 | u32 wqe_size_in_bytes_rq2, wqe_size_in_bytes_rq3; | |
430 | ||
431 | ||
432 | qp = kzalloc(sizeof(*qp), GFP_KERNEL); | |
433 | if (!qp) { | |
434 | ehea_error("no mem for qp"); | |
435 | return NULL; | |
436 | } | |
437 | ||
438 | qp->adapter = adapter; | |
439 | ||
440 | hret = ehea_h_alloc_resource_qp(adapter->handle, init_attr, pd, | |
441 | &qp->fw_handle, &qp->epas); | |
442 | if (hret != H_SUCCESS) { | |
443 | ehea_error("ehea_h_alloc_resource_qp failed"); | |
444 | goto out_freemem; | |
445 | } | |
446 | ||
447 | wqe_size_in_bytes_sq = map_wqe_size(init_attr->act_wqe_size_enc_sq); | |
448 | wqe_size_in_bytes_rq1 = map_wqe_size(init_attr->act_wqe_size_enc_rq1); | |
449 | wqe_size_in_bytes_rq2 = map_wqe_size(init_attr->act_wqe_size_enc_rq2); | |
450 | wqe_size_in_bytes_rq3 = map_wqe_size(init_attr->act_wqe_size_enc_rq3); | |
451 | ||
452 | ret = ehea_qp_alloc_register(qp, &qp->hw_squeue, init_attr->nr_sq_pages, | |
453 | wqe_size_in_bytes_sq, | |
454 | init_attr->act_wqe_size_enc_sq, adapter, | |
455 | 0); | |
456 | if (ret) { | |
457 | ehea_error("can't register for sq ret=%x", ret); | |
458 | goto out_freeres; | |
459 | } | |
460 | ||
461 | ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue1, | |
462 | init_attr->nr_rq1_pages, | |
463 | wqe_size_in_bytes_rq1, | |
464 | init_attr->act_wqe_size_enc_rq1, | |
465 | adapter, 1); | |
466 | if (ret) { | |
467 | ehea_error("can't register for rq1 ret=%x", ret); | |
468 | goto out_kill_hwsq; | |
469 | } | |
470 | ||
471 | if (init_attr->rq_count > 1) { | |
472 | ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue2, | |
473 | init_attr->nr_rq2_pages, | |
474 | wqe_size_in_bytes_rq2, | |
475 | init_attr->act_wqe_size_enc_rq2, | |
476 | adapter, 2); | |
477 | if (ret) { | |
478 | ehea_error("can't register for rq2 ret=%x", ret); | |
479 | goto out_kill_hwr1q; | |
480 | } | |
481 | } | |
482 | ||
483 | if (init_attr->rq_count > 2) { | |
484 | ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue3, | |
485 | init_attr->nr_rq3_pages, | |
486 | wqe_size_in_bytes_rq3, | |
487 | init_attr->act_wqe_size_enc_rq3, | |
488 | adapter, 3); | |
489 | if (ret) { | |
490 | ehea_error("can't register for rq3 ret=%x", ret); | |
491 | goto out_kill_hwr2q; | |
492 | } | |
493 | } | |
494 | ||
495 | qp->init_attr = *init_attr; | |
496 | ||
497 | return qp; | |
498 | ||
499 | out_kill_hwr2q: | |
500 | hw_queue_dtor(&qp->hw_rqueue2); | |
501 | ||
502 | out_kill_hwr1q: | |
503 | hw_queue_dtor(&qp->hw_rqueue1); | |
504 | ||
505 | out_kill_hwsq: | |
506 | hw_queue_dtor(&qp->hw_squeue); | |
507 | ||
508 | out_freeres: | |
509 | ehea_h_disable_and_get_hea(adapter->handle, qp->fw_handle); | |
e542aa6b | 510 | ehea_h_free_resource(adapter->handle, qp->fw_handle, FORCE_FREE); |
7a291083 JBT |
511 | |
512 | out_freemem: | |
513 | kfree(qp); | |
514 | return NULL; | |
515 | } | |
516 | ||
e542aa6b | 517 | u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force) |
7a291083 | 518 | { |
d1d25aab JBT |
519 | u64 hret; |
520 | struct ehea_qp_init_attr *qp_attr = &qp->init_attr; | |
7a291083 | 521 | |
7a291083 | 522 | |
d1d25aab JBT |
523 | ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle); |
524 | hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle, force); | |
525 | if (hret != H_SUCCESS) | |
526 | return hret; | |
7a291083 | 527 | |
d1d25aab JBT |
528 | hw_queue_dtor(&qp->hw_squeue); |
529 | hw_queue_dtor(&qp->hw_rqueue1); | |
7a291083 | 530 | |
d1d25aab JBT |
531 | if (qp_attr->rq_count > 1) |
532 | hw_queue_dtor(&qp->hw_rqueue2); | |
533 | if (qp_attr->rq_count > 2) | |
534 | hw_queue_dtor(&qp->hw_rqueue3); | |
535 | kfree(qp); | |
7a291083 | 536 | |
d1d25aab | 537 | return hret; |
7a291083 JBT |
538 | } |
539 | ||
e542aa6b JBT |
540 | int ehea_destroy_qp(struct ehea_qp *qp) |
541 | { | |
d1d25aab JBT |
542 | u64 hret; |
543 | if (!qp) | |
544 | return 0; | |
e542aa6b | 545 | |
28721c89 JBT |
546 | hcp_epas_dtor(&qp->epas); |
547 | ||
f67c6275 DM |
548 | hret = ehea_destroy_qp_res(qp, NORMAL_FREE); |
549 | if (hret == H_R_STATE) { | |
d1d25aab JBT |
550 | ehea_error_data(qp->adapter, qp->fw_handle); |
551 | hret = ehea_destroy_qp_res(qp, FORCE_FREE); | |
552 | } | |
e542aa6b | 553 | |
d1d25aab JBT |
554 | if (hret != H_SUCCESS) { |
555 | ehea_error("destroy QP failed"); | |
556 | return -EIO; | |
557 | } | |
e542aa6b | 558 | |
d1d25aab | 559 | return 0; |
e542aa6b JBT |
560 | } |
561 | ||
f67c6275 | 562 | int ehea_create_busmap(void) |
44c82152 TK |
563 | { |
564 | u64 vaddr = EHEA_BUSMAP_START; | |
2c69448b | 565 | unsigned long high_section_index = 0; |
44c82152 TK |
566 | int i; |
567 | ||
568 | /* | |
569 | * Sections are not in ascending order -> Loop over all sections and | |
570 | * find the highest PFN to compute the required map size. | |
571 | */ | |
572 | ehea_bmap.valid_sections = 0; | |
573 | ||
574 | for (i = 0; i < NR_MEM_SECTIONS; i++) | |
2c69448b JBT |
575 | if (valid_section_nr(i)) |
576 | high_section_index = i; | |
44c82152 | 577 | |
2c69448b | 578 | ehea_bmap.entries = high_section_index + 1; |
44c82152 TK |
579 | ehea_bmap.vaddr = vmalloc(ehea_bmap.entries * sizeof(*ehea_bmap.vaddr)); |
580 | ||
581 | if (!ehea_bmap.vaddr) | |
582 | return -ENOMEM; | |
583 | ||
584 | for (i = 0 ; i < ehea_bmap.entries; i++) { | |
585 | unsigned long pfn = section_nr_to_pfn(i); | |
586 | ||
587 | if (pfn_valid(pfn)) { | |
588 | ehea_bmap.vaddr[i] = vaddr; | |
589 | vaddr += EHEA_SECTSIZE; | |
2c69448b | 590 | ehea_bmap.valid_sections++; |
44c82152 TK |
591 | } else |
592 | ehea_bmap.vaddr[i] = 0; | |
593 | } | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
f67c6275 | 598 | void ehea_destroy_busmap(void) |
44c82152 TK |
599 | { |
600 | vfree(ehea_bmap.vaddr); | |
601 | } | |
602 | ||
603 | u64 ehea_map_vaddr(void *caddr) | |
604 | { | |
605 | u64 mapped_addr; | |
606 | unsigned long index = __pa(caddr) >> SECTION_SIZE_BITS; | |
607 | ||
608 | if (likely(index < ehea_bmap.entries)) { | |
609 | mapped_addr = ehea_bmap.vaddr[index]; | |
610 | if (likely(mapped_addr)) | |
611 | mapped_addr |= (((unsigned long)caddr) | |
612 | & (EHEA_SECTSIZE - 1)); | |
613 | else | |
614 | mapped_addr = -1; | |
615 | } else | |
616 | mapped_addr = -1; | |
617 | ||
618 | if (unlikely(mapped_addr == -1)) | |
619 | if (!test_and_set_bit(__EHEA_STOP_XFER, &ehea_driver_flags)) | |
3bf76b81 | 620 | schedule_work(&ehea_rereg_mr_task); |
44c82152 TK |
621 | |
622 | return mapped_addr; | |
623 | } | |
624 | ||
e542aa6b | 625 | int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr) |
7a291083 | 626 | { |
44c82152 | 627 | int ret; |
7a291083 | 628 | u64 *pt; |
44c82152 TK |
629 | void *pg; |
630 | u64 hret, pt_abs, i, j, m, mr_len; | |
631 | u32 acc_ctrl = EHEA_MR_ACC_CTRL; | |
7a291083 | 632 | |
44c82152 | 633 | mr_len = ehea_bmap.valid_sections * EHEA_SECTSIZE; |
7a291083 | 634 | |
2c69448b | 635 | pt = kzalloc(PAGE_SIZE, GFP_KERNEL); |
7a291083 JBT |
636 | if (!pt) { |
637 | ehea_error("no mem"); | |
638 | ret = -ENOMEM; | |
639 | goto out; | |
640 | } | |
641 | pt_abs = virt_to_abs(pt); | |
642 | ||
44c82152 TK |
643 | hret = ehea_h_alloc_resource_mr(adapter->handle, |
644 | EHEA_BUSMAP_START, mr_len, | |
7a291083 | 645 | acc_ctrl, adapter->pd, |
e542aa6b | 646 | &mr->handle, &mr->lkey); |
7a291083 JBT |
647 | if (hret != H_SUCCESS) { |
648 | ehea_error("alloc_resource_mr failed"); | |
649 | ret = -EIO; | |
650 | goto out; | |
651 | } | |
652 | ||
44c82152 TK |
653 | for (i = 0 ; i < ehea_bmap.entries; i++) |
654 | if (ehea_bmap.vaddr[i]) { | |
655 | void *sectbase = __va(i << SECTION_SIZE_BITS); | |
656 | unsigned long k = 0; | |
657 | ||
2c69448b JBT |
658 | for (j = 0; j < (EHEA_PAGES_PER_SECTION / |
659 | EHEA_MAX_RPAGE); j++) { | |
44c82152 TK |
660 | |
661 | for (m = 0; m < EHEA_MAX_RPAGE; m++) { | |
662 | pg = sectbase + ((k++) * EHEA_PAGESIZE); | |
663 | pt[m] = virt_to_abs(pg); | |
664 | } | |
665 | ||
666 | hret = ehea_h_register_rpage_mr(adapter->handle, | |
667 | mr->handle, | |
668 | 0, 0, pt_abs, | |
669 | EHEA_MAX_RPAGE); | |
670 | if ((hret != H_SUCCESS) | |
671 | && (hret != H_PAGE_REGISTERED)) { | |
672 | ehea_h_free_resource(adapter->handle, | |
673 | mr->handle, | |
674 | FORCE_FREE); | |
675 | ehea_error("register_rpage_mr failed"); | |
676 | ret = -EIO; | |
677 | goto out; | |
678 | } | |
679 | } | |
7a291083 | 680 | } |
7a291083 JBT |
681 | |
682 | if (hret != H_SUCCESS) { | |
44c82152 TK |
683 | ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE); |
684 | ehea_error("registering mr failed"); | |
7a291083 JBT |
685 | ret = -EIO; |
686 | goto out; | |
687 | } | |
e542aa6b | 688 | |
44c82152 | 689 | mr->vaddr = EHEA_BUSMAP_START; |
e542aa6b | 690 | mr->adapter = adapter; |
7a291083 JBT |
691 | ret = 0; |
692 | out: | |
693 | kfree(pt); | |
694 | return ret; | |
695 | } | |
696 | ||
e542aa6b JBT |
697 | int ehea_rem_mr(struct ehea_mr *mr) |
698 | { | |
699 | u64 hret; | |
700 | ||
701 | if (!mr || !mr->adapter) | |
702 | return -EINVAL; | |
703 | ||
704 | hret = ehea_h_free_resource(mr->adapter->handle, mr->handle, | |
705 | FORCE_FREE); | |
706 | if (hret != H_SUCCESS) { | |
707 | ehea_error("destroy MR failed"); | |
708 | return -EIO; | |
709 | } | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
714 | int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr, | |
715 | struct ehea_mr *shared_mr) | |
716 | { | |
717 | u64 hret; | |
718 | ||
719 | hret = ehea_h_register_smr(adapter->handle, old_mr->handle, | |
720 | old_mr->vaddr, EHEA_MR_ACC_CTRL, | |
721 | adapter->pd, shared_mr); | |
722 | if (hret != H_SUCCESS) | |
723 | return -EIO; | |
724 | ||
725 | shared_mr->adapter = adapter; | |
726 | ||
727 | return 0; | |
728 | } | |
729 | ||
d2db9eea JBT |
730 | void print_error_data(u64 *data) |
731 | { | |
732 | int length; | |
733 | u64 type = EHEA_BMASK_GET(ERROR_DATA_TYPE, data[2]); | |
734 | u64 resource = data[1]; | |
735 | ||
736 | length = EHEA_BMASK_GET(ERROR_DATA_LENGTH, data[0]); | |
737 | ||
738 | if (length > EHEA_PAGESIZE) | |
739 | length = EHEA_PAGESIZE; | |
740 | ||
741 | if (type == 0x8) /* Queue Pair */ | |
742 | ehea_error("QP (resource=%lX) state: AER=0x%lX, AERR=0x%lX, " | |
743 | "port=%lX", resource, data[6], data[12], data[22]); | |
744 | ||
e542aa6b JBT |
745 | if (type == 0x4) /* Completion Queue */ |
746 | ehea_error("CQ (resource=%lX) state: AER=0x%lX", resource, | |
747 | data[6]); | |
748 | ||
749 | if (type == 0x3) /* Event Queue */ | |
750 | ehea_error("EQ (resource=%lX) state: AER=0x%lX", resource, | |
751 | data[6]); | |
752 | ||
d2db9eea JBT |
753 | ehea_dump(data, length, "error data"); |
754 | } | |
755 | ||
756 | void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle) | |
757 | { | |
758 | unsigned long ret; | |
759 | u64 *rblock; | |
760 | ||
761 | rblock = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
762 | if (!rblock) { | |
763 | ehea_error("Cannot allocate rblock memory."); | |
764 | return; | |
765 | } | |
7a291083 | 766 | |
d2db9eea JBT |
767 | ret = ehea_h_error_data(adapter->handle, |
768 | res_handle, | |
769 | rblock); | |
770 | ||
771 | if (ret == H_R_STATE) | |
772 | ehea_error("No error data is available: %lX.", res_handle); | |
773 | else if (ret == H_SUCCESS) | |
774 | print_error_data(rblock); | |
775 | else | |
776 | ehea_error("Error data could not be fetched: %lX", res_handle); | |
777 | ||
778 | kfree(rblock); | |
779 | } |