resource.c revision 309378
1/*
2 * Copyright (c) 2009-2013 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 <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/10/sys/dev/cxgbe/iw_cxgbe/resource.c 309378 2016-12-01 23:38:52Z jhb $");
35
36#include "opt_inet.h"
37
38#ifdef TCP_OFFLOAD
39#include <linux/spinlock.h>
40#include "iw_cxgbe.h"
41
42static int c4iw_init_qid_table(struct c4iw_rdev *rdev)
43{
44	u32 i;
45
46	if (c4iw_id_table_alloc(&rdev->resource.qid_table,
47				rdev->adap->vres.qp.start,
48				rdev->adap->vres.qp.size,
49				rdev->adap->vres.qp.size, 0)) {
50		printf("%s: return ENOMEM\n", __func__);
51		return -ENOMEM;
52	}
53
54	for (i = rdev->adap->vres.qp.start;
55		i < rdev->adap->vres.qp.start + rdev->adap->vres.qp.size; i++)
56		if (!(i & rdev->qpmask))
57			c4iw_id_free(&rdev->resource.qid_table, i);
58	return 0;
59}
60
61/* nr_* must be power of 2 */
62int c4iw_init_resource(struct c4iw_rdev *rdev, u32 nr_tpt, u32 nr_pdid)
63{
64	int err = 0;
65	err = c4iw_id_table_alloc(&rdev->resource.tpt_table, 0, nr_tpt, 1,
66					C4IW_ID_TABLE_F_RANDOM);
67	if (err)
68		goto tpt_err;
69	err = c4iw_init_qid_table(rdev);
70	if (err)
71		goto qid_err;
72	err = c4iw_id_table_alloc(&rdev->resource.pdid_table, 0,
73					nr_pdid, 1, 0);
74	if (err)
75		goto pdid_err;
76	return 0;
77 pdid_err:
78	c4iw_id_table_free(&rdev->resource.qid_table);
79 qid_err:
80	c4iw_id_table_free(&rdev->resource.tpt_table);
81 tpt_err:
82	return -ENOMEM;
83}
84
85/*
86 * returns 0 if no resource available
87 */
88u32 c4iw_get_resource(struct c4iw_id_table *id_table)
89{
90	u32 entry;
91	entry = c4iw_id_alloc(id_table);
92	if (entry == (u32)(-1)) {
93		return 0;
94	}
95	return entry;
96}
97
98void c4iw_put_resource(struct c4iw_id_table *id_table, u32 entry)
99{
100	CTR2(KTR_IW_CXGBE, "%s entry 0x%x", __func__, entry);
101	c4iw_id_free(id_table, entry);
102}
103
104u32 c4iw_get_cqid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
105{
106	struct c4iw_qid_list *entry;
107	u32 qid;
108	int i;
109
110	mutex_lock(&uctx->lock);
111	if (!list_empty(&uctx->cqids)) {
112		entry = list_entry(uctx->cqids.next, struct c4iw_qid_list,
113				   entry);
114		list_del(&entry->entry);
115		qid = entry->qid;
116		kfree(entry);
117	} else {
118		qid = c4iw_get_resource(&rdev->resource.qid_table);
119		if (!qid)
120			goto out;
121		mutex_lock(&rdev->stats.lock);
122		rdev->stats.qid.cur += rdev->qpmask + 1;
123		mutex_unlock(&rdev->stats.lock);
124		for (i = qid+1; i & rdev->qpmask; i++) {
125			entry = kmalloc(sizeof *entry, GFP_KERNEL);
126			if (!entry)
127				goto out;
128			entry->qid = i;
129			list_add_tail(&entry->entry, &uctx->cqids);
130		}
131
132		/*
133		 * now put the same ids on the qp list since they all
134		 * map to the same db/gts page.
135		 */
136		entry = kmalloc(sizeof *entry, GFP_KERNEL);
137		if (!entry)
138			goto out;
139		entry->qid = qid;
140		list_add_tail(&entry->entry, &uctx->qpids);
141		for (i = qid+1; i & rdev->qpmask; i++) {
142			entry = kmalloc(sizeof *entry, GFP_KERNEL);
143			if (!entry)
144				goto out;
145			entry->qid = i;
146			list_add_tail(&entry->entry, &uctx->qpids);
147		}
148	}
149out:
150	mutex_unlock(&uctx->lock);
151	CTR2(KTR_IW_CXGBE, "%s: qid 0x%x", __func__, qid);
152	mutex_lock(&rdev->stats.lock);
153	if (rdev->stats.qid.cur > rdev->stats.qid.max)
154		rdev->stats.qid.max = rdev->stats.qid.cur;
155	mutex_unlock(&rdev->stats.lock);
156	return qid;
157}
158
159void c4iw_put_cqid(struct c4iw_rdev *rdev, u32 qid,
160		   struct c4iw_dev_ucontext *uctx)
161{
162	struct c4iw_qid_list *entry;
163
164	entry = kmalloc(sizeof *entry, GFP_KERNEL);
165	if (!entry)
166		return;
167	CTR2(KTR_IW_CXGBE, "%s qid 0x%x", __func__, qid);
168	entry->qid = qid;
169	mutex_lock(&uctx->lock);
170	list_add_tail(&entry->entry, &uctx->cqids);
171	mutex_unlock(&uctx->lock);
172}
173
174u32 c4iw_get_qpid(struct c4iw_rdev *rdev, struct c4iw_dev_ucontext *uctx)
175{
176	struct c4iw_qid_list *entry;
177	u32 qid;
178	int i;
179
180	mutex_lock(&uctx->lock);
181	if (!list_empty(&uctx->qpids)) {
182		entry = list_entry(uctx->qpids.next, struct c4iw_qid_list,
183				   entry);
184		list_del(&entry->entry);
185		qid = entry->qid;
186		kfree(entry);
187	} else {
188		qid = c4iw_get_resource(&rdev->resource.qid_table);
189		if (!qid)
190			goto out;
191		mutex_lock(&rdev->stats.lock);
192		rdev->stats.qid.cur += rdev->qpmask + 1;
193		mutex_unlock(&rdev->stats.lock);
194		for (i = qid+1; i & rdev->qpmask; i++) {
195			entry = kmalloc(sizeof *entry, GFP_KERNEL);
196			if (!entry)
197				goto out;
198			entry->qid = i;
199			list_add_tail(&entry->entry, &uctx->qpids);
200		}
201
202		/*
203		 * now put the same ids on the cq list since they all
204		 * map to the same db/gts page.
205		 */
206		entry = kmalloc(sizeof *entry, GFP_KERNEL);
207		if (!entry)
208			goto out;
209		entry->qid = qid;
210		list_add_tail(&entry->entry, &uctx->cqids);
211		for (i = qid; i & rdev->qpmask; i++) {
212			entry = kmalloc(sizeof *entry, GFP_KERNEL);
213			if (!entry)
214				goto out;
215			entry->qid = i;
216			list_add_tail(&entry->entry, &uctx->cqids);
217		}
218	}
219out:
220	mutex_unlock(&uctx->lock);
221	CTR2(KTR_IW_CXGBE, "%s qid 0x%x", __func__, qid);
222	mutex_lock(&rdev->stats.lock);
223	if (rdev->stats.qid.cur > rdev->stats.qid.max)
224		rdev->stats.qid.max = rdev->stats.qid.cur;
225	mutex_unlock(&rdev->stats.lock);
226	return qid;
227}
228
229void c4iw_put_qpid(struct c4iw_rdev *rdev, u32 qid,
230		   struct c4iw_dev_ucontext *uctx)
231{
232	struct c4iw_qid_list *entry;
233
234	entry = kmalloc(sizeof *entry, GFP_KERNEL);
235	if (!entry)
236		return;
237	CTR2(KTR_IW_CXGBE, "%s qid 0x%x", __func__, qid);
238	entry->qid = qid;
239	mutex_lock(&uctx->lock);
240	list_add_tail(&entry->entry, &uctx->qpids);
241	mutex_unlock(&uctx->lock);
242}
243
244void c4iw_destroy_resource(struct c4iw_resource *rscp)
245{
246	c4iw_id_table_free(&rscp->tpt_table);
247	c4iw_id_table_free(&rscp->qid_table);
248	c4iw_id_table_free(&rscp->pdid_table);
249}
250
251/* PBL Memory Manager. */
252
253#define MIN_PBL_SHIFT 5			/* 32B == min PBL size (4 entries) */
254
255u32 c4iw_pblpool_alloc(struct c4iw_rdev *rdev, int size)
256{
257	unsigned long addr;
258
259	vmem_xalloc(rdev->pbl_arena, roundup(size, (1 << MIN_PBL_SHIFT)),
260			4, 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX,
261			M_FIRSTFIT|M_NOWAIT, &addr);
262	CTR3(KTR_IW_CXGBE, "%s addr 0x%x size %d", __func__, (u32)addr, size);
263	mutex_lock(&rdev->stats.lock);
264	if (addr) {
265		rdev->stats.pbl.cur += roundup(size, 1 << MIN_PBL_SHIFT);
266		if (rdev->stats.pbl.cur > rdev->stats.pbl.max)
267			rdev->stats.pbl.max = rdev->stats.pbl.cur;
268	} else
269		rdev->stats.pbl.fail++;
270	mutex_unlock(&rdev->stats.lock);
271	return (u32)addr;
272}
273
274void c4iw_pblpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
275{
276	CTR3(KTR_IW_CXGBE, "%s addr 0x%x size %d", __func__, addr, size);
277	mutex_lock(&rdev->stats.lock);
278	rdev->stats.pbl.cur -= roundup(size, 1 << MIN_PBL_SHIFT);
279	mutex_unlock(&rdev->stats.lock);
280	vmem_xfree(rdev->pbl_arena, addr, roundup(size,(1 << MIN_PBL_SHIFT)));
281}
282
283int c4iw_pblpool_create(struct c4iw_rdev *rdev)
284{
285	rdev->pbl_arena = vmem_create("PBL_MEM_POOL",
286					rdev->adap->vres.pbl.start,
287					rdev->adap->vres.pbl.size,
288					1, 0, M_FIRSTFIT| M_NOWAIT);
289	if (!rdev->pbl_arena)
290		return -ENOMEM;
291
292	return 0;
293}
294
295void c4iw_pblpool_destroy(struct c4iw_rdev *rdev)
296{
297	vmem_destroy(rdev->pbl_arena);
298}
299
300/* RQT Memory Manager. */
301
302#define MIN_RQT_SHIFT 10	/* 1KB == min RQT size (16 entries) */
303
304u32 c4iw_rqtpool_alloc(struct c4iw_rdev *rdev, int size)
305{
306	unsigned long addr;
307
308	vmem_xalloc(rdev->rqt_arena,
309			roundup((size << 6),(1 << MIN_RQT_SHIFT)),
310			4, 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX,
311			M_FIRSTFIT|M_NOWAIT, &addr);
312	CTR3(KTR_IW_CXGBE, "%s addr 0x%x size %d", __func__, (u32)addr,
313	    size << 6);
314	if (!addr)
315		printf("%s: Out of RQT memory\n",
316		       device_get_nameunit(rdev->adap->dev));
317	mutex_lock(&rdev->stats.lock);
318	if (addr) {
319		rdev->stats.rqt.cur += roundup(size << 6, 1 << MIN_RQT_SHIFT);
320		if (rdev->stats.rqt.cur > rdev->stats.rqt.max)
321			rdev->stats.rqt.max = rdev->stats.rqt.cur;
322	} else
323		rdev->stats.rqt.fail++;
324	mutex_unlock(&rdev->stats.lock);
325	return (u32)addr;
326}
327
328void c4iw_rqtpool_free(struct c4iw_rdev *rdev, u32 addr, int size)
329{
330	CTR3(KTR_IW_CXGBE, "%s addr 0x%x size %d", __func__, addr, size << 6);
331	mutex_lock(&rdev->stats.lock);
332	rdev->stats.rqt.cur -= roundup(size << 6, 1 << MIN_RQT_SHIFT);
333	mutex_unlock(&rdev->stats.lock);
334	vmem_xfree(rdev->rqt_arena, addr,
335		       roundup((size << 6),(1 << MIN_RQT_SHIFT)));
336}
337
338int c4iw_rqtpool_create(struct c4iw_rdev *rdev)
339{
340	rdev->rqt_arena = vmem_create("RQT_MEM_POOL",
341					rdev->adap->vres.rq.start,
342					rdev->adap->vres.rq.size,
343					1, 0, M_FIRSTFIT| M_NOWAIT);
344	if (!rdev->rqt_arena)
345		return -ENOMEM;
346
347	return 0;
348}
349
350void c4iw_rqtpool_destroy(struct c4iw_rdev *rdev)
351{
352	vmem_destroy(rdev->rqt_arena);
353}
354#endif
355