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