1 /*
2 * Copyright (c) 2009-2010 Chelsio, Inc. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
12 * conditions are met:
13 *
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer.
17 *
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32 /* Crude resource management */
33 #include <linux/spinlock.h>
34 #include <linux/genalloc.h>
35 #include <linux/ratelimit.h>
36 #include "iw_cxgb4.h"
37
c4iw_init_qid_table(struct c4iw_rdev * rdev)38 static int c4iw_init_qid_table(struct c4iw_rdev *rdev)
39 {
40 u32 i;
41
42 if (c4iw_id_table_alloc(&rdev->resource.qid_table,
43 rdev->lldi.vr->qp.start,
44 rdev->lldi.vr->qp.size,
45 rdev->lldi.vr->qp.size, 0))
46 return -ENOMEM;
47
48 for (i = rdev->lldi.vr->qp.start;
49 i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++)
50 if (!(i & rdev->qpmask))
51 c4iw_id_free(&rdev->resource.qid_table, i);
52 return 0;
53 }
54
55 /* nr_* must be power of 2 */
c4iw_init_resource(struct c4iw_rdev * rdev,u32 nr_tpt,u32 nr_pdid,u32 nr_srqt)56 int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt,
57 u32 nr_pdid, u32 nr_srqt)
58 {
59 int err = 0;
60 err = c4iw_id_table_alloc(&rdev->resource.tpt_table, 0, nr_tpt, 1,
61 C4IW_ID_TABLE_F_RANDOM);
62 if (err)
63 goto tpt_err;
64 err = c4iw_init_qid_table(rdev);
65 if (err)
66 goto qid_err;
67 err = c4iw_id_table_alloc(&rdev->resource.pdid_table, 0,
68 nr_pdid, 1, 0);
69 if (err)
70 goto pdid_err;
71 if (!nr_srqt)
72 err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0,
73 1, 1, 0);
74 else
75 err = c4iw_id_table_alloc(&rdev->resource.srq_table, 0,
76 nr_srqt, 0, 0);
77 if (err)
78 goto srq_err;
79 return 0;
80 srq_err:
81 c4iw_id_table_free(&rdev->resource.pdid_table);
82 pdid_err:
83 c4iw_id_table_free(&rdev->resource.qid_table);
84 qid_err:
85 c4iw_id_table_free(&rdev->resource.tpt_table);
86 tpt_err:
87 return -ENOMEM;
88 }
89
90 /*
91 * returns 0 if no resource available
92 */
c4iw_get_resource(struct c4iw_id_table * id_table)93 u32 c4iw_get_resource(struct c4iw_id_table *id_table)
94 {
95 u32 entry;
96 entry = c4iw_id_alloc(id_table);
97 if (entry == (u32)(-1))
98 return 0;
99 return entry;
100 }
101
c4iw_put_resource(struct c4iw_id_table * id_table,u32 entry)102 void c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry)
103 {
104 pr_debug("entry 0x%x\n", entry);
105 c4iw_id_free(id_table, entry);
106 }
107
c4iw_get_cqid(struct c4iw_rdev * rdev,struct c4iw_dev_ucontext * uctx)108 u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
109 {
110 struct c4iw_qid_list *entry;
111 u32 qid;
112 int i;
113
114 mutex_lock(&uctx->lock);
115 if (!list_empty(&uctx->cqids)) {
116 entry = list_entry(uctx->cqids.next, struct c4iw_qid_list,
117 entry);
118 list_del(&entry->entry);
119 qid = entry->qid;
120 kfree(entry);
121 } else {
122 qid = c4iw_get_resource(&rdev->resource.qid_table);
123 if (!qid)
124 goto out;
125 mutex_lock(&rdev->stats.lock);
126 rdev->stats.qid.cur += rdev->qpmask + 1;
127 mutex_unlock(&rdev->stats.lock);
128 for (i = qid+1; i & rdev->qpmask; i++) {
129 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
130 if (!entry)
131 goto out;
132 entry->qid = i;
133 list_add_tail(&entry->entry, &uctx->cqids);
134 }
135
136 /*
137 * now put the same ids on the qp list since they all
138 * map to the same db/gts page.
139 */
140 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
141 if (!entry)
142 goto out;
143 entry->qid = qid;
144 list_add_tail(&entry->entry, &uctx->qpids);
145 for (i = qid+1; i & rdev->qpmask; i++) {
146 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
147 if (!entry)
148 goto out;
149 entry->qid = i;
150 list_add_tail(&entry->entry, &uctx->qpids);
151 }
152 }
153 out:
154 mutex_unlock(&uctx->lock);
155 pr_debug("qid 0x%x\n", qid);
156 mutex_lock(&rdev->stats.lock);
157 if (rdev->stats.qid.cur > rdev->stats.qid.max)
158 rdev->stats.qid.max = rdev->stats.qid.cur;
159 mutex_unlock(&rdev->stats.lock);
160 return qid;
161 }
162
c4iw_put_cqid(struct c4iw_rdev * rdev,u32 qid,struct c4iw_dev_ucontext * uctx)163 void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid,
164 struct c4iw_dev_ucontext *uctx)
165 {
166 struct c4iw_qid_list *entry;
167
168 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
169 if (!entry)
170 return;
171 pr_debug("qid 0x%x\n", qid);
172 entry->qid = qid;
173 mutex_lock(&uctx->lock);
174 list_add_tail(&entry->entry, &uctx->cqids);
175 mutex_unlock(&uctx->lock);
176 }
177
c4iw_get_qpid(struct c4iw_rdev * rdev,struct c4iw_dev_ucontext * uctx)178 u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
179 {
180 struct c4iw_qid_list *entry;
181 u32 qid;
182 int i;
183
184 mutex_lock(&uctx->lock);
185 if (!list_empty(&uctx->qpids)) {
186 entry = list_entry(uctx->qpids.next, struct c4iw_qid_list,
187 entry);
188 list_del(&entry->entry);
189 qid = entry->qid;
190 kfree(entry);
191 } else {
192 qid = c4iw_get_resource(&rdev->resource.qid_table);
193 if (!qid) {
194 mutex_lock(&rdev->stats.lock);
195 rdev->stats.qid.fail++;
196 mutex_unlock(&rdev->stats.lock);
197 goto out;
198 }
199 mutex_lock(&rdev->stats.lock);
200 rdev->stats.qid.cur += rdev->qpmask + 1;
201 mutex_unlock(&rdev->stats.lock);
202 for (i = qid+1; i & rdev->qpmask; i++) {
203 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
204 if (!entry)
205 goto out;
206 entry->qid = i;
207 list_add_tail(&entry->entry, &uctx->qpids);
208 }
209
210 /*
211 * now put the same ids on the cq list since they all
212 * map to the same db/gts page.
213 */
214 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
215 if (!entry)
216 goto out;
217 entry->qid = qid;
218 list_add_tail(&entry->entry, &uctx->cqids);
219 for (i = qid; i & rdev->qpmask; i++) {
220 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
221 if (!entry)
222 goto out;
223 entry->qid = i;
224 list_add_tail(&entry->entry, &uctx->cqids);
225 }
226 }
227 out:
228 mutex_unlock(&uctx->lock);
229 pr_debug("qid 0x%x\n", qid);
230 mutex_lock(&rdev->stats.lock);
231 if (rdev->stats.qid.cur > rdev->stats.qid.max)
232 rdev->stats.qid.max = rdev->stats.qid.cur;
233 mutex_unlock(&rdev->stats.lock);
234 return qid;
235 }
236
c4iw_put_qpid(struct c4iw_rdev * rdev,u32 qid,struct c4iw_dev_ucontext * uctx)237 void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid,
238 struct c4iw_dev_ucontext *uctx)
239 {
240 struct c4iw_qid_list *entry;
241
242 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
243 if (!entry)
244 return;
245 pr_debug("qid 0x%x\n", qid);
246 entry->qid = qid;
247 mutex_lock(&uctx->lock);
248 list_add_tail(&entry->entry, &uctx->qpids);
249 mutex_unlock(&uctx->lock);
250 }
251
c4iw_destroy_resource(struct c4iw_resource * rscp)252 void c4iw_destroy_resource(struct c4iw_resource *rscp)
253 {
254 c4iw_id_table_free(&rscp->tpt_table);
255 c4iw_id_table_free(&rscp->qid_table);
256 c4iw_id_table_free(&rscp->pdid_table);
257 }
258
259 /*
260 * PBL Memory Manager. Uses Linux generic allocator.
261 */
262
263 #define MIN_PBL_SHIFT 8 /* 256B == min PBL size (32 entries) */
264
c4iw_pblpool_alloc(struct c4iw_rdev * rdev,int size)265 u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
266 {
267 unsigned long addr = gen_pool_alloc(rdev->pbl_pool, size);
268 pr_debug("addr 0x%x size %d\n", (u32)addr, size);
269 mutex_lock(&rdev->stats.lock);
270 if (addr) {
271 rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT);
272 if (rdev->stats.pbl.cur > rdev->stats.pbl.max)
273 rdev->stats.pbl.max = rdev->stats.pbl.cur;
274 kref_get(&rdev->pbl_kref);
275 } else
276 rdev->stats.pbl.fail++;
277 mutex_unlock(&rdev->stats.lock);
278 return (u32)addr;
279 }
280
destroy_pblpool(struct kref * kref)281 static void destroy_pblpool(struct kref *kref)
282 {
283 struct c4iw_rdev *rdev;
284
285 rdev = container_of(kref, struct c4iw_rdev, pbl_kref);
286 gen_pool_destroy(rdev->pbl_pool);
287 complete(&rdev->pbl_compl);
288 }
289
c4iw_pblpool_free(struct c4iw_rdev * rdev,u32 addr,int size)290 void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
291 {
292 pr_debug("addr 0x%x size %d\n", addr, size);
293 mutex_lock(&rdev->stats.lock);
294 rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT);
295 mutex_unlock(&rdev->stats.lock);
296 gen_pool_free(rdev->pbl_pool, (unsigned long)addr, size);
297 kref_put(&rdev->pbl_kref, destroy_pblpool);
298 }
299
c4iw_pblpool_create(struct c4iw_rdev * rdev)300 int c4iw_pblpool_create(struct c4iw_rdev *rdev)
301 {
302 unsigned pbl_start, pbl_chunk, pbl_top;
303
304 rdev->pbl_pool = gen_pool_create(MIN_PBL_SHIFT, -1);
305 if (!rdev->pbl_pool)
306 return -ENOMEM;
307
308 pbl_start = rdev->lldi.vr->pbl.start;
309 pbl_chunk = rdev->lldi.vr->pbl.size;
310 pbl_top = pbl_start + pbl_chunk;
311
312 while (pbl_start < pbl_top) {
313 pbl_chunk = min(pbl_top - pbl_start + 1, pbl_chunk);
314 if (gen_pool_add(rdev->pbl_pool, pbl_start, pbl_chunk, -1)) {
315 pr_debug("failed to add PBL chunk (%x/%x)\n",
316 pbl_start, pbl_chunk);
317 if (pbl_chunk <= 1024 << MIN_PBL_SHIFT) {
318 pr_warn("Failed to add all PBL chunks (%x/%x)\n",
319 pbl_start, pbl_top - pbl_start);
320 return 0;
321 }
322 pbl_chunk >>= 1;
323 } else {
324 pr_debug("added PBL chunk (%x/%x)\n",
325 pbl_start, pbl_chunk);
326 pbl_start += pbl_chunk;
327 }
328 }
329
330 return 0;
331 }
332
c4iw_pblpool_destroy(struct c4iw_rdev * rdev)333 void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
334 {
335 kref_put(&rdev->pbl_kref, destroy_pblpool);
336 }
337
338 /*
339 * RQT Memory Manager. Uses Linux generic allocator.
340 */
341
342 #define MIN_RQT_SHIFT 10 /* 1KB == min RQT size (16 entries) */
343
c4iw_rqtpool_alloc(struct c4iw_rdev * rdev,int size)344 u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
345 {
346 unsigned long addr = gen_pool_alloc(rdev->rqt_pool, size << 6);
347 pr_debug("addr 0x%x size %d\n", (u32)addr, size << 6);
348 if (!addr)
349 pr_warn_ratelimited("%s: Out of RQT memory\n",
350 pci_name(rdev->lldi.pdev));
351 mutex_lock(&rdev->stats.lock);
352 if (addr) {
353 rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT);
354 if (rdev->stats.rqt.cur > rdev->stats.rqt.max)
355 rdev->stats.rqt.max = rdev->stats.rqt.cur;
356 kref_get(&rdev->rqt_kref);
357 } else
358 rdev->stats.rqt.fail++;
359 mutex_unlock(&rdev->stats.lock);
360 return (u32)addr;
361 }
362
destroy_rqtpool(struct kref * kref)363 static void destroy_rqtpool(struct kref *kref)
364 {
365 struct c4iw_rdev *rdev;
366
367 rdev = container_of(kref, struct c4iw_rdev, rqt_kref);
368 gen_pool_destroy(rdev->rqt_pool);
369 complete(&rdev->rqt_compl);
370 }
371
c4iw_rqtpool_free(struct c4iw_rdev * rdev,u32 addr,int size)372 void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
373 {
374 pr_debug("addr 0x%x size %d\n", addr, size << 6);
375 mutex_lock(&rdev->stats.lock);
376 rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT);
377 mutex_unlock(&rdev->stats.lock);
378 gen_pool_free(rdev->rqt_pool, (unsigned long)addr, size << 6);
379 kref_put(&rdev->rqt_kref, destroy_rqtpool);
380 }
381
c4iw_rqtpool_create(struct c4iw_rdev * rdev)382 int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
383 {
384 unsigned rqt_start, rqt_chunk, rqt_top;
385 int skip = 0;
386
387 rdev->rqt_pool = gen_pool_create(MIN_RQT_SHIFT, -1);
388 if (!rdev->rqt_pool)
389 return -ENOMEM;
390
391 /*
392 * If SRQs are supported, then never use the first RQE from
393 * the RQT region. This is because HW uses RQT index 0 as NULL.
394 */
395 if (rdev->lldi.vr->srq.size)
396 skip = T4_RQT_ENTRY_SIZE;
397
398 rqt_start = rdev->lldi.vr->rq.start + skip;
399 rqt_chunk = rdev->lldi.vr->rq.size - skip;
400 rqt_top = rqt_start + rqt_chunk;
401
402 while (rqt_start < rqt_top) {
403 rqt_chunk = min(rqt_top - rqt_start + 1, rqt_chunk);
404 if (gen_pool_add(rdev->rqt_pool, rqt_start, rqt_chunk, -1)) {
405 pr_debug("failed to add RQT chunk (%x/%x)\n",
406 rqt_start, rqt_chunk);
407 if (rqt_chunk <= 1024 << MIN_RQT_SHIFT) {
408 pr_warn("Failed to add all RQT chunks (%x/%x)\n",
409 rqt_start, rqt_top - rqt_start);
410 return 0;
411 }
412 rqt_chunk >>= 1;
413 } else {
414 pr_debug("added RQT chunk (%x/%x)\n",
415 rqt_start, rqt_chunk);
416 rqt_start += rqt_chunk;
417 }
418 }
419 return 0;
420 }
421
c4iw_rqtpool_destroy(struct c4iw_rdev * rdev)422 void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
423 {
424 kref_put(&rdev->rqt_kref, destroy_rqtpool);
425 }
426
c4iw_alloc_srq_idx(struct c4iw_rdev * rdev)427 int c4iw_alloc_srq_idx(struct c4iw_rdev *rdev)
428 {
429 int idx;
430
431 idx = c4iw_id_alloc(&rdev->resource.srq_table);
432 mutex_lock(&rdev->stats.lock);
433 if (idx == -1) {
434 rdev->stats.srqt.fail++;
435 mutex_unlock(&rdev->stats.lock);
436 return -ENOMEM;
437 }
438 rdev->stats.srqt.cur++;
439 if (rdev->stats.srqt.cur > rdev->stats.srqt.max)
440 rdev->stats.srqt.max = rdev->stats.srqt.cur;
441 mutex_unlock(&rdev->stats.lock);
442 return idx;
443 }
444
c4iw_free_srq_idx(struct c4iw_rdev * rdev,int idx)445 void c4iw_free_srq_idx(struct c4iw_rdev *rdev, int idx)
446 {
447 c4iw_id_free(&rdev->resource.srq_table, idx);
448 mutex_lock(&rdev->stats.lock);
449 rdev->stats.srqt.cur--;
450 mutex_unlock(&rdev->stats.lock);
451 }
452
453 /*
454 * On-Chip QP Memory.
455 */
456 #define MIN_OCQP_SHIFT 12 /* 4KB == min ocqp size */
457
c4iw_ocqp_pool_alloc(struct c4iw_rdev * rdev,int size)458 u32 c4iw_ocqp_pool_alloc(struct c4iw_rdev *rdev, int size)
459 {
460 unsigned long addr = gen_pool_alloc(rdev->ocqp_pool, size);
461 pr_debug("addr 0x%x size %d\n", (u32)addr, size);
462 if (addr) {
463 mutex_lock(&rdev->stats.lock);
464 rdev->stats.ocqp.cur += roundup(size, 1 << MIN_OCQP_SHIFT);
465 if (rdev->stats.ocqp.cur > rdev->stats.ocqp.max)
466 rdev->stats.ocqp.max = rdev->stats.ocqp.cur;
467 mutex_unlock(&rdev->stats.lock);
468 }
469 return (u32)addr;
470 }
471
c4iw_ocqp_pool_free(struct c4iw_rdev * rdev,u32 addr,int size)472 void c4iw_ocqp_pool_free(struct c4iw_rdev *rdev, u32 addr, int size)
473 {
474 pr_debug("addr 0x%x size %d\n", addr, size);
475 mutex_lock(&rdev->stats.lock);
476 rdev->stats.ocqp.cur -= roundup(size, 1 << MIN_OCQP_SHIFT);
477 mutex_unlock(&rdev->stats.lock);
478 gen_pool_free(rdev->ocqp_pool, (unsigned long)addr, size);
479 }
480
c4iw_ocqp_pool_create(struct c4iw_rdev * rdev)481 int c4iw_ocqp_pool_create(struct c4iw_rdev *rdev)
482 {
483 unsigned start, chunk, top;
484
485 rdev->ocqp_pool = gen_pool_create(MIN_OCQP_SHIFT, -1);
486 if (!rdev->ocqp_pool)
487 return -ENOMEM;
488
489 start = rdev->lldi.vr->ocq.start;
490 chunk = rdev->lldi.vr->ocq.size;
491 top = start + chunk;
492
493 while (start < top) {
494 chunk = min(top - start + 1, chunk);
495 if (gen_pool_add(rdev->ocqp_pool, start, chunk, -1)) {
496 pr_debug("failed to add OCQP chunk (%x/%x)\n",
497 start, chunk);
498 if (chunk <= 1024 << MIN_OCQP_SHIFT) {
499 pr_warn("Failed to add all OCQP chunks (%x/%x)\n",
500 start, top - start);
501 return 0;
502 }
503 chunk >>= 1;
504 } else {
505 pr_debug("added OCQP chunk (%x/%x)\n",
506 start, chunk);
507 start += chunk;
508 }
509 }
510 return 0;
511 }
512
c4iw_ocqp_pool_destroy(struct c4iw_rdev * rdev)513 void c4iw_ocqp_pool_destroy(struct c4iw_rdev *rdev)
514 {
515 gen_pool_destroy(rdev->ocqp_pool);
516 }
517