1 /*
2  * Copyright (c) 2004 Topspin Communications.  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 
33 #include <linux/string.h>
34 #include <linux/gfp.h>
35 
36 #include "mthca_dev.h"
37 #include "mthca_cmd.h"
38 
39 struct mthca_mgm {
40 	__be32 next_gid_index;
41 	u32    reserved[3];
42 	u8     gid[16];
43 	__be32 qp[MTHCA_QP_PER_MGM];
44 };
45 
46 static const u8 zero_gid[16];	/* automatically initialized to 0 */
47 
48 /*
49  * Caller must hold MCG table semaphore.  gid and mgm parameters must
50  * be properly aligned for command interface.
51  *
52  *  Returns 0 unless a firmware command error occurs.
53  *
54  * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1
55  * and *mgm holds MGM entry.
56  *
57  * if GID is found in AMGM, *index = index in AMGM, *prev = index of
58  * previous entry in hash chain and *mgm holds AMGM entry.
59  *
60  * If no AMGM exists for given gid, *index = -1, *prev = index of last
61  * entry in hash chain and *mgm holds end of hash chain.
62  */
find_mgm(struct mthca_dev * dev,u8 * gid,struct mthca_mailbox * mgm_mailbox,u16 * hash,int * prev,int * index)63 static int find_mgm(struct mthca_dev *dev,
64 		    u8 *gid, struct mthca_mailbox *mgm_mailbox,
65 		    u16 *hash, int *prev, int *index)
66 {
67 	struct mthca_mailbox *mailbox;
68 	struct mthca_mgm *mgm = mgm_mailbox->buf;
69 	u8 *mgid;
70 	int err;
71 
72 	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
73 	if (IS_ERR(mailbox))
74 		return -ENOMEM;
75 	mgid = mailbox->buf;
76 
77 	memcpy(mgid, gid, 16);
78 
79 	err = mthca_MGID_HASH(dev, mailbox, hash);
80 	if (err) {
81 		mthca_err(dev, "MGID_HASH failed (%d)\n", err);
82 		goto out;
83 	}
84 
85 	if (0)
86 		mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
87 
88 	*index = *hash;
89 	*prev  = -1;
90 
91 	do {
92 		err = mthca_READ_MGM(dev, *index, mgm_mailbox);
93 		if (err) {
94 			mthca_err(dev, "READ_MGM failed (%d)\n", err);
95 			goto out;
96 		}
97 
98 		if (!memcmp(mgm->gid, zero_gid, 16)) {
99 			if (*index != *hash) {
100 				mthca_err(dev, "Found zero MGID in AMGM.\n");
101 				err = -EINVAL;
102 			}
103 			goto out;
104 		}
105 
106 		if (!memcmp(mgm->gid, gid, 16))
107 			goto out;
108 
109 		*prev = *index;
110 		*index = be32_to_cpu(mgm->next_gid_index) >> 6;
111 	} while (*index);
112 
113 	*index = -1;
114 
115  out:
116 	mthca_free_mailbox(dev, mailbox);
117 	return err;
118 }
119 
mthca_multicast_attach(struct ib_qp * ibqp,union ib_gid * gid,u16 lid)120 int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
121 {
122 	struct mthca_dev *dev = to_mdev(ibqp->device);
123 	struct mthca_mailbox *mailbox;
124 	struct mthca_mgm *mgm;
125 	u16 hash;
126 	int index, prev;
127 	int link = 0;
128 	int i;
129 	int err;
130 
131 	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
132 	if (IS_ERR(mailbox))
133 		return PTR_ERR(mailbox);
134 	mgm = mailbox->buf;
135 
136 	mutex_lock(&dev->mcg_table.mutex);
137 
138 	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
139 	if (err)
140 		goto out;
141 
142 	if (index != -1) {
143 		if (!memcmp(mgm->gid, zero_gid, 16))
144 			memcpy(mgm->gid, gid->raw, 16);
145 	} else {
146 		link = 1;
147 
148 		index = mthca_alloc(&dev->mcg_table.alloc);
149 		if (index == -1) {
150 			mthca_err(dev, "No AMGM entries left\n");
151 			err = -ENOMEM;
152 			goto out;
153 		}
154 
155 		err = mthca_READ_MGM(dev, index, mailbox);
156 		if (err) {
157 			mthca_err(dev, "READ_MGM failed (%d)\n", err);
158 			goto out;
159 		}
160 		memset(mgm, 0, sizeof *mgm);
161 		memcpy(mgm->gid, gid->raw, 16);
162 	}
163 
164 	for (i = 0; i < MTHCA_QP_PER_MGM; ++i)
165 		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {
166 			mthca_dbg(dev, "QP %06x already a member of MGM\n",
167 				  ibqp->qp_num);
168 			err = 0;
169 			goto out;
170 		} else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {
171 			mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));
172 			break;
173 		}
174 
175 	if (i == MTHCA_QP_PER_MGM) {
176 		mthca_err(dev, "MGM at index %x is full.\n", index);
177 		err = -ENOMEM;
178 		goto out;
179 	}
180 
181 	err = mthca_WRITE_MGM(dev, index, mailbox);
182 	if (err) {
183 		mthca_err(dev, "WRITE_MGM failed %d\n", err);
184 		err = -EINVAL;
185 		goto out;
186 	}
187 
188 	if (!link)
189 		goto out;
190 
191 	err = mthca_READ_MGM(dev, prev, mailbox);
192 	if (err) {
193 		mthca_err(dev, "READ_MGM failed %d\n", err);
194 		goto out;
195 	}
196 
197 	mgm->next_gid_index = cpu_to_be32(index << 6);
198 
199 	err = mthca_WRITE_MGM(dev, prev, mailbox);
200 	if (err)
201 		mthca_err(dev, "WRITE_MGM returned %d\n", err);
202 
203  out:
204 	if (err && link && index != -1) {
205 		BUG_ON(index < dev->limits.num_mgms);
206 		mthca_free(&dev->mcg_table.alloc, index);
207 	}
208 	mutex_unlock(&dev->mcg_table.mutex);
209 
210 	mthca_free_mailbox(dev, mailbox);
211 	return err;
212 }
213 
mthca_multicast_detach(struct ib_qp * ibqp,union ib_gid * gid,u16 lid)214 int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
215 {
216 	struct mthca_dev *dev = to_mdev(ibqp->device);
217 	struct mthca_mailbox *mailbox;
218 	struct mthca_mgm *mgm;
219 	u16 hash;
220 	int prev, index;
221 	int i, loc;
222 	int err;
223 
224 	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
225 	if (IS_ERR(mailbox))
226 		return PTR_ERR(mailbox);
227 	mgm = mailbox->buf;
228 
229 	mutex_lock(&dev->mcg_table.mutex);
230 
231 	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
232 	if (err)
233 		goto out;
234 
235 	if (index == -1) {
236 		mthca_err(dev, "MGID %pI6 not found\n", gid->raw);
237 		err = -EINVAL;
238 		goto out;
239 	}
240 
241 	for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {
242 		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))
243 			loc = i;
244 		if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))
245 			break;
246 	}
247 
248 	if (loc == -1) {
249 		mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);
250 		err = -EINVAL;
251 		goto out;
252 	}
253 
254 	mgm->qp[loc]   = mgm->qp[i - 1];
255 	mgm->qp[i - 1] = 0;
256 
257 	err = mthca_WRITE_MGM(dev, index, mailbox);
258 	if (err) {
259 		mthca_err(dev, "WRITE_MGM returned %d\n", err);
260 		goto out;
261 	}
262 
263 	if (i != 1)
264 		goto out;
265 
266 	if (prev == -1) {
267 		/* Remove entry from MGM */
268 		int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;
269 		if (amgm_index_to_free) {
270 			err = mthca_READ_MGM(dev, amgm_index_to_free,
271 					     mailbox);
272 			if (err) {
273 				mthca_err(dev, "READ_MGM returned %d\n", err);
274 				goto out;
275 			}
276 		} else
277 			memset(mgm->gid, 0, 16);
278 
279 		err = mthca_WRITE_MGM(dev, index, mailbox);
280 		if (err) {
281 			mthca_err(dev, "WRITE_MGM returned %d\n", err);
282 			goto out;
283 		}
284 		if (amgm_index_to_free) {
285 			BUG_ON(amgm_index_to_free < dev->limits.num_mgms);
286 			mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);
287 		}
288 	} else {
289 		/* Remove entry from AMGM */
290 		int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
291 		err = mthca_READ_MGM(dev, prev, mailbox);
292 		if (err) {
293 			mthca_err(dev, "READ_MGM returned %d\n", err);
294 			goto out;
295 		}
296 
297 		mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);
298 
299 		err = mthca_WRITE_MGM(dev, prev, mailbox);
300 		if (err) {
301 			mthca_err(dev, "WRITE_MGM returned %d\n", err);
302 			goto out;
303 		}
304 		BUG_ON(index < dev->limits.num_mgms);
305 		mthca_free(&dev->mcg_table.alloc, index);
306 	}
307 
308  out:
309 	mutex_unlock(&dev->mcg_table.mutex);
310 
311 	mthca_free_mailbox(dev, mailbox);
312 	return err;
313 }
314 
mthca_init_mcg_table(struct mthca_dev * dev)315 int mthca_init_mcg_table(struct mthca_dev *dev)
316 {
317 	int err;
318 	int table_size = dev->limits.num_mgms + dev->limits.num_amgms;
319 
320 	err = mthca_alloc_init(&dev->mcg_table.alloc,
321 			       table_size,
322 			       table_size - 1,
323 			       dev->limits.num_mgms);
324 	if (err)
325 		return err;
326 
327 	mutex_init(&dev->mcg_table.mutex);
328 
329 	return 0;
330 }
331 
mthca_cleanup_mcg_table(struct mthca_dev * dev)332 void mthca_cleanup_mcg_table(struct mthca_dev *dev)
333 {
334 	mthca_alloc_cleanup(&dev->mcg_table.alloc);
335 }
336