1204076Spjd/*
2204076Spjd * Copyright (c) 2005 Topspin Communications.  All rights reserved.
3204076Spjd *
4204076Spjd * This software is available to you under a choice of one of two
5204076Spjd * licenses.  You may choose to be licensed under the terms of the GNU
6204076Spjd * General Public License (GPL) Version 2, available from the file
7204076Spjd * COPYING in the main directory of this source tree, or the
8204076Spjd * OpenIB.org BSD license below:
9204076Spjd *
10204076Spjd *     Redistribution and use in source and binary forms, with or
11204076Spjd *     without modification, are permitted provided that the following
12204076Spjd *     conditions are met:
13204076Spjd *
14204076Spjd *      - Redistributions of source code must retain the above
15204076Spjd *        copyright notice, this list of conditions and the following
16204076Spjd *        disclaimer.
17204076Spjd *
18204076Spjd *      - Redistributions in binary form must reproduce the above
19204076Spjd *        copyright notice, this list of conditions and the following
20204076Spjd *        disclaimer in the documentation and/or other materials
21204076Spjd *        provided with the distribution.
22204076Spjd *
23204076Spjd * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24204076Spjd * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25204076Spjd * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26204076Spjd * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27204076Spjd * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28204076Spjd * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29204076Spjd * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30204076Spjd * SOFTWARE.
31204076Spjd */
32204076Spjd
33204076Spjd#if HAVE_CONFIG_H
34204076Spjd#  include <config.h>
35204076Spjd#endif /* HAVE_CONFIG_H */
36204076Spjd
37204076Spjd#include <stdlib.h>
38204076Spjd#include <netinet/in.h>
39204076Spjd#include <pthread.h>
40204076Spjd#include <string.h>
41204076Spjd
42204076Spjd#include "mthca.h"
43204076Spjd
44204076Spjdstruct mthca_ah_page {
45204076Spjd	struct mthca_ah_page *prev, *next;
46225787Spjd	struct mthca_buf      buf;
47204076Spjd	struct ibv_mr 	     *mr;
48225787Spjd	int           	      use_cnt;
49225787Spjd	unsigned      	      free[0];
50225787Spjd};
51225787Spjd
52225787Spjdstatic struct mthca_ah_page *__add_page(struct mthca_pd *pd, int page_size, int per_page)
53225787Spjd{
54225787Spjd	struct mthca_ah_page *page;
55225787Spjd	int i;
56225787Spjd
57225787Spjd	page = malloc(sizeof *page + per_page * sizeof (int));
58214283Spjd	if (!page)
59214283Spjd		return NULL;
60214282Spjd
61214282Spjd	if (mthca_alloc_buf(&page->buf, page_size, page_size)) {
62214282Spjd		free(page);
63214282Spjd		return NULL;
64214282Spjd	}
65214282Spjd
66214282Spjd	page->mr = mthca_reg_mr(&pd->ibv_pd, page->buf.buf, page_size, 0);
67214282Spjd	if (!page->mr) {
68214282Spjd		mthca_free_buf(&page->buf);
69214282Spjd		free(page);
70214282Spjd		return NULL;
71214282Spjd	}
72214282Spjd
73214282Spjd	page->mr->context = pd->ibv_pd.context;
74214282Spjd
75214282Spjd	page->use_cnt = 0;
76214282Spjd	for (i = 0; i < per_page; ++i)
77214282Spjd		page->free[i] = ~0;
78214282Spjd
79214282Spjd	page->prev = NULL;
80214282Spjd	page->next = pd->ah_list;
81214282Spjd	pd->ah_list = page;
82214282Spjd	if (page->next)
83214282Spjd		page->next->prev = page;
84214282Spjd
85214282Spjd	return page;
86214282Spjd}
87204076Spjd
88204076Spjdint mthca_alloc_av(struct mthca_pd *pd, struct ibv_ah_attr *attr,
89204076Spjd		   struct mthca_ah *ah)
90204076Spjd{
91204076Spjd	if (mthca_is_memfree(pd->ibv_pd.context)) {
92204076Spjd		ah->av = malloc(sizeof *ah->av);
93204076Spjd		if (!ah->av)
94204076Spjd			return -1;
95204076Spjd	} else {
96204076Spjd		struct mthca_ah_page *page;
97204076Spjd		int ps;
98204076Spjd		int pp;
99204076Spjd		int i, j;
100204076Spjd
101204076Spjd		ps = to_mdev(pd->ibv_pd.context->device)->page_size;
102204076Spjd		pp = ps / (sizeof *ah->av * 8 * sizeof (int));
103204076Spjd
104204076Spjd		pthread_mutex_lock(&pd->ah_mutex);
105204076Spjd		for (page = pd->ah_list; page; page = page->next)
106204076Spjd			if (page->use_cnt < ps / sizeof *ah->av)
107204076Spjd				for (i = 0; i < pp; ++i)
108204076Spjd					if (page->free[i])
109204076Spjd						goto found;
110225787Spjd
111225787Spjd		page = __add_page(pd, ps, pp);
112204076Spjd		if (!page) {
113204076Spjd			pthread_mutex_unlock(&pd->ah_mutex);
114204076Spjd			return -1;
115204076Spjd		}
116204076Spjd
117204076Spjd	found:
118204076Spjd		++page->use_cnt;
119204076Spjd
120204076Spjd		for (i = 0, j = -1; i < pp; ++i)
121204076Spjd			if (page->free[i]) {
122204076Spjd				j = ffs(page->free[i]);
123204076Spjd				page->free[i] &= ~(1 << (j - 1));
124204076Spjd				ah->av = page->buf.buf +
125204076Spjd					(i * 8 * sizeof (int) + (j - 1)) * sizeof *ah->av;
126204076Spjd				break;
127204076Spjd			}
128204076Spjd
129204076Spjd		ah->key  = page->mr->lkey;
130204076Spjd		ah->page = page;
131204076Spjd
132204076Spjd		pthread_mutex_unlock(&pd->ah_mutex);
133204076Spjd	}
134204076Spjd
135204076Spjd	memset(ah->av, 0, sizeof *ah->av);
136204076Spjd
137204076Spjd	ah->av->port_pd = htonl(pd->pdn | (attr->port_num << 24));
138204076Spjd	ah->av->g_slid  = attr->src_path_bits;
139204076Spjd	ah->av->dlid    = htons(attr->dlid);
140204076Spjd	ah->av->msg_sr  = (3 << 4) | /* 2K message */
141204076Spjd		attr->static_rate;
142204076Spjd	ah->av->sl_tclass_flowlabel = htonl(attr->sl << 28);
143204076Spjd	if (attr->is_global) {
144204076Spjd		ah->av->g_slid |= 0x80;
145204076Spjd		/* XXX get gid_table length */
146204076Spjd		ah->av->gid_index = (attr->port_num - 1) * 32 +
147204076Spjd			attr->grh.sgid_index;
148204076Spjd		ah->av->hop_limit = attr->grh.hop_limit;
149204076Spjd		ah->av->sl_tclass_flowlabel |=
150204076Spjd			htonl((attr->grh.traffic_class << 20) |
151204076Spjd				    attr->grh.flow_label);
152204076Spjd		memcpy(ah->av->dgid, attr->grh.dgid.raw, 16);
153204076Spjd	} else {
154204076Spjd		/* Arbel workaround -- low byte of GID must be 2 */
155204076Spjd		ah->av->dgid[3] = htonl(2);
156204076Spjd	}
157204076Spjd
158204076Spjd	return 0;
159204076Spjd}
160204076Spjd
161204076Spjdvoid mthca_free_av(struct mthca_ah *ah)
162204076Spjd{
163204076Spjd	if (mthca_is_memfree(ah->ibv_ah.context)) {
164204076Spjd		free(ah->av);
165204076Spjd	} else {
166204076Spjd		struct mthca_pd *pd = to_mpd(ah->ibv_ah.pd);
167204076Spjd		struct mthca_ah_page *page;
168204076Spjd		int i;
169204076Spjd
170204076Spjd		pthread_mutex_lock(&pd->ah_mutex);
171204076Spjd
172204076Spjd		page = ah->page;
173204076Spjd		i = ((void *) ah->av - page->buf.buf) / sizeof *ah->av;
174204076Spjd		page->free[i / (8 * sizeof (int))] |= 1 << (i % (8 * sizeof (int)));
175204076Spjd
176204076Spjd		if (!--page->use_cnt) {
177204076Spjd			if (page->prev)
178204076Spjd				page->prev->next = page->next;
179204076Spjd			else
180204076Spjd				pd->ah_list = page->next;
181204076Spjd			if (page->next)
182204076Spjd				page->next->prev = page->prev;
183204076Spjd
184204076Spjd			mthca_dereg_mr(page->mr);
185204076Spjd			mthca_free_buf(&page->buf);
186204076Spjd			free(page);
187204076Spjd		}
188204076Spjd
189204076Spjd		pthread_mutex_unlock(&pd->ah_mutex);
190204076Spjd	}
191204076Spjd}
192204076Spjd