1219820Sjeff/*
2219820Sjeff * Copyright (c) 2005 Topspin Communications.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff */
32219820Sjeff
33219820Sjeff#if HAVE_CONFIG_H
34219820Sjeff#  include <config.h>
35219820Sjeff#endif /* HAVE_CONFIG_H */
36219820Sjeff
37219820Sjeff#include <stdlib.h>
38219820Sjeff#include <netinet/in.h>
39219820Sjeff#include <pthread.h>
40219820Sjeff#include <string.h>
41219820Sjeff
42219820Sjeff#include "mthca.h"
43219820Sjeff
44219820Sjeff#define MTHCA_FREE_MAP_SIZE (MTHCA_DB_REC_PER_PAGE / (SIZEOF_LONG * 8))
45219820Sjeff
46219820Sjeffstruct mthca_db_page {
47219820Sjeff	unsigned long		free[MTHCA_FREE_MAP_SIZE];
48219820Sjeff	struct mthca_buf	db_rec;
49219820Sjeff};
50219820Sjeff
51219820Sjeffstruct mthca_db_table {
52219820Sjeff	int 	       	     npages;
53219820Sjeff	int 	       	     max_group1;
54219820Sjeff	int 	       	     min_group2;
55219820Sjeff	pthread_mutex_t      mutex;
56219820Sjeff	struct mthca_db_page page[];
57219820Sjeff};
58219820Sjeff
59219820Sjeffint mthca_alloc_db(struct mthca_db_table *db_tab, enum mthca_db_type type,
60219820Sjeff		   uint32_t **db)
61219820Sjeff{
62219820Sjeff	int i, j, k;
63219820Sjeff	int group, start, end, dir;
64219820Sjeff	int ret = 0;
65219820Sjeff
66219820Sjeff	pthread_mutex_lock(&db_tab->mutex);
67219820Sjeff
68219820Sjeff	switch (type) {
69219820Sjeff	case MTHCA_DB_TYPE_CQ_ARM:
70219820Sjeff	case MTHCA_DB_TYPE_SQ:
71219820Sjeff		group = 0;
72219820Sjeff		start = 0;
73219820Sjeff		end   = db_tab->max_group1;
74219820Sjeff		dir   = 1;
75219820Sjeff		break;
76219820Sjeff
77219820Sjeff	case MTHCA_DB_TYPE_CQ_SET_CI:
78219820Sjeff	case MTHCA_DB_TYPE_RQ:
79219820Sjeff	case MTHCA_DB_TYPE_SRQ:
80219820Sjeff		group = 1;
81219820Sjeff		start = db_tab->npages - 1;
82219820Sjeff		end   = db_tab->min_group2;
83219820Sjeff		dir   = -1;
84219820Sjeff		break;
85219820Sjeff
86219820Sjeff	default:
87219820Sjeff		ret = -1;
88219820Sjeff		goto out;
89219820Sjeff	}
90219820Sjeff
91219820Sjeff	for (i = start; i != end; i += dir)
92219820Sjeff		if (db_tab->page[i].db_rec.buf)
93219820Sjeff			for (j = 0; j < MTHCA_FREE_MAP_SIZE; ++j)
94219820Sjeff				if (db_tab->page[i].free[j])
95219820Sjeff					goto found;
96219820Sjeff
97219820Sjeff	if (db_tab->max_group1 >= db_tab->min_group2 - 1) {
98219820Sjeff		ret = -1;
99219820Sjeff		goto out;
100219820Sjeff	}
101219820Sjeff
102219820Sjeff	if (mthca_alloc_buf(&db_tab->page[i].db_rec,
103219820Sjeff			    MTHCA_DB_REC_PAGE_SIZE,
104219820Sjeff			    MTHCA_DB_REC_PAGE_SIZE)) {
105219820Sjeff		ret = -1;
106219820Sjeff		goto out;
107219820Sjeff	}
108219820Sjeff
109219820Sjeff	memset(db_tab->page[i].db_rec.buf, 0, MTHCA_DB_REC_PAGE_SIZE);
110219820Sjeff	memset(db_tab->page[i].free, 0xff, sizeof db_tab->page[i].free);
111219820Sjeff
112219820Sjeff	if (group == 0)
113219820Sjeff		++db_tab->max_group1;
114219820Sjeff	else
115219820Sjeff		--db_tab->min_group2;
116219820Sjeff
117219820Sjefffound:
118219820Sjeff	for (j = 0; j < MTHCA_FREE_MAP_SIZE; ++j) {
119219820Sjeff		k = ffsl(db_tab->page[i].free[j]);
120219820Sjeff		if (k)
121219820Sjeff			break;
122219820Sjeff	}
123219820Sjeff
124219820Sjeff	if (!k) {
125219820Sjeff		ret = -1;
126219820Sjeff		goto out;
127219820Sjeff	}
128219820Sjeff
129219820Sjeff	--k;
130219820Sjeff	db_tab->page[i].free[j] &= ~(1UL << k);
131219820Sjeff
132219820Sjeff	j = j * SIZEOF_LONG * 8 + k;
133219820Sjeff	if (group == 1)
134219820Sjeff		j = MTHCA_DB_REC_PER_PAGE - 1 - j;
135219820Sjeff
136219820Sjeff	ret = i * MTHCA_DB_REC_PER_PAGE + j;
137219820Sjeff	*db = db_tab->page[i].db_rec.buf + j * 8;
138219820Sjeff
139219820Sjeffout:
140219820Sjeff	pthread_mutex_unlock(&db_tab->mutex);
141219820Sjeff	return ret;
142219820Sjeff}
143219820Sjeff
144219820Sjeffvoid mthca_set_db_qn(uint32_t *db, enum mthca_db_type type, uint32_t qn)
145219820Sjeff{
146219820Sjeff	db[1] = htonl((qn << 8) | (type << 5));
147219820Sjeff}
148219820Sjeff
149219820Sjeffvoid mthca_free_db(struct mthca_db_table *db_tab, enum mthca_db_type type, int db_index)
150219820Sjeff{
151219820Sjeff	int i, j;
152219820Sjeff	struct mthca_db_page *page;
153219820Sjeff
154219820Sjeff	i = db_index / MTHCA_DB_REC_PER_PAGE;
155219820Sjeff	j = db_index % MTHCA_DB_REC_PER_PAGE;
156219820Sjeff
157219820Sjeff	page = db_tab->page + i;
158219820Sjeff
159219820Sjeff	pthread_mutex_lock(&db_tab->mutex);
160219820Sjeff	*(uint64_t *) (page->db_rec.buf + j * 8) = 0;
161219820Sjeff
162219820Sjeff	if (i >= db_tab->min_group2)
163219820Sjeff		j = MTHCA_DB_REC_PER_PAGE - 1 - j;
164219820Sjeff
165219820Sjeff	page->free[j / (SIZEOF_LONG * 8)] |= 1UL << (j % (SIZEOF_LONG * 8));
166219820Sjeff
167219820Sjeff	pthread_mutex_unlock(&db_tab->mutex);
168219820Sjeff}
169219820Sjeff
170219820Sjeffstruct mthca_db_table *mthca_alloc_db_tab(int uarc_size)
171219820Sjeff{
172219820Sjeff	struct mthca_db_table *db_tab;
173219820Sjeff	int npages;
174219820Sjeff	int i;
175219820Sjeff
176219820Sjeff	npages = uarc_size / MTHCA_DB_REC_PAGE_SIZE;
177219820Sjeff	db_tab = malloc(sizeof (struct mthca_db_table) +
178219820Sjeff			npages * sizeof (struct mthca_db_page));
179219820Sjeff
180219820Sjeff	pthread_mutex_init(&db_tab->mutex, NULL);
181219820Sjeff
182219820Sjeff	db_tab->npages     = npages;
183219820Sjeff	db_tab->max_group1 = 0;
184219820Sjeff	db_tab->min_group2 = npages - 1;
185219820Sjeff
186219820Sjeff	for (i = 0; i < npages; ++i)
187219820Sjeff		db_tab->page[i].db_rec.buf = NULL;
188219820Sjeff
189219820Sjeff	return db_tab;
190219820Sjeff}
191219820Sjeff
192219820Sjeffvoid mthca_free_db_tab(struct mthca_db_table *db_tab)
193219820Sjeff{
194219820Sjeff	int i;
195219820Sjeff
196219820Sjeff	if (!db_tab)
197219820Sjeff		return;
198219820Sjeff
199219820Sjeff	for (i = 0; i < db_tab->npages; ++i)
200219820Sjeff		if (db_tab->page[i].db_rec.buf)
201219820Sjeff			mthca_free_buf(&db_tab->page[i].db_rec);
202219820Sjeff
203219820Sjeff	free(db_tab);
204219820Sjeff}
205