xref: /linux/drivers/infiniband/hw/mthca/mthca_mcg.c (revision 0ae28a35bcb7984838acbf28bfba9c030f8b74f0)
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  */
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 	u8 status;
72 
73 	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
74 	if (IS_ERR(mailbox))
75 		return -ENOMEM;
76 	mgid = mailbox->buf;
77 
78 	memcpy(mgid, gid, 16);
79 
80 	err = mthca_MGID_HASH(dev, mailbox, hash, &status);
81 	if (err)
82 		goto out;
83 	if (status) {
84 		mthca_err(dev, "MGID_HASH returned status %02x\n", status);
85 		err = -EINVAL;
86 		goto out;
87 	}
88 
89 	if (0)
90 		mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
91 
92 	*index = *hash;
93 	*prev  = -1;
94 
95 	do {
96 		err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status);
97 		if (err)
98 			goto out;
99 		if (status) {
100 			mthca_err(dev, "READ_MGM returned status %02x\n", status);
101 			err = -EINVAL;
102 			goto out;
103 		}
104 
105 		if (!memcmp(mgm->gid, zero_gid, 16)) {
106 			if (*index != *hash) {
107 				mthca_err(dev, "Found zero MGID in AMGM.\n");
108 				err = -EINVAL;
109 			}
110 			goto out;
111 		}
112 
113 		if (!memcmp(mgm->gid, gid, 16))
114 			goto out;
115 
116 		*prev = *index;
117 		*index = be32_to_cpu(mgm->next_gid_index) >> 6;
118 	} while (*index);
119 
120 	*index = -1;
121 
122  out:
123 	mthca_free_mailbox(dev, mailbox);
124 	return err;
125 }
126 
127 int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
128 {
129 	struct mthca_dev *dev = to_mdev(ibqp->device);
130 	struct mthca_mailbox *mailbox;
131 	struct mthca_mgm *mgm;
132 	u16 hash;
133 	int index, prev;
134 	int link = 0;
135 	int i;
136 	int err;
137 	u8 status;
138 
139 	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
140 	if (IS_ERR(mailbox))
141 		return PTR_ERR(mailbox);
142 	mgm = mailbox->buf;
143 
144 	mutex_lock(&dev->mcg_table.mutex);
145 
146 	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
147 	if (err)
148 		goto out;
149 
150 	if (index != -1) {
151 		if (!memcmp(mgm->gid, zero_gid, 16))
152 			memcpy(mgm->gid, gid->raw, 16);
153 	} else {
154 		link = 1;
155 
156 		index = mthca_alloc(&dev->mcg_table.alloc);
157 		if (index == -1) {
158 			mthca_err(dev, "No AMGM entries left\n");
159 			err = -ENOMEM;
160 			goto out;
161 		}
162 
163 		err = mthca_READ_MGM(dev, index, mailbox, &status);
164 		if (err)
165 			goto out;
166 		if (status) {
167 			mthca_err(dev, "READ_MGM returned status %02x\n", status);
168 			err = -EINVAL;
169 			goto out;
170 		}
171 		memset(mgm, 0, sizeof *mgm);
172 		memcpy(mgm->gid, gid->raw, 16);
173 	}
174 
175 	for (i = 0; i < MTHCA_QP_PER_MGM; ++i)
176 		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {
177 			mthca_dbg(dev, "QP %06x already a member of MGM\n",
178 				  ibqp->qp_num);
179 			err = 0;
180 			goto out;
181 		} else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {
182 			mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));
183 			break;
184 		}
185 
186 	if (i == MTHCA_QP_PER_MGM) {
187 		mthca_err(dev, "MGM at index %x is full.\n", index);
188 		err = -ENOMEM;
189 		goto out;
190 	}
191 
192 	err = mthca_WRITE_MGM(dev, index, mailbox, &status);
193 	if (err)
194 		goto out;
195 	if (status) {
196 		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
197 		err = -EINVAL;
198 		goto out;
199 	}
200 
201 	if (!link)
202 		goto out;
203 
204 	err = mthca_READ_MGM(dev, prev, mailbox, &status);
205 	if (err)
206 		goto out;
207 	if (status) {
208 		mthca_err(dev, "READ_MGM returned status %02x\n", status);
209 		err = -EINVAL;
210 		goto out;
211 	}
212 
213 	mgm->next_gid_index = cpu_to_be32(index << 6);
214 
215 	err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
216 	if (err)
217 		goto out;
218 	if (status) {
219 		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
220 		err = -EINVAL;
221 	}
222 
223  out:
224 	if (err && link && index != -1) {
225 		BUG_ON(index < dev->limits.num_mgms);
226 		mthca_free(&dev->mcg_table.alloc, index);
227 	}
228 	mutex_unlock(&dev->mcg_table.mutex);
229 
230 	mthca_free_mailbox(dev, mailbox);
231 	return err;
232 }
233 
234 int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
235 {
236 	struct mthca_dev *dev = to_mdev(ibqp->device);
237 	struct mthca_mailbox *mailbox;
238 	struct mthca_mgm *mgm;
239 	u16 hash;
240 	int prev, index;
241 	int i, loc;
242 	int err;
243 	u8 status;
244 
245 	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
246 	if (IS_ERR(mailbox))
247 		return PTR_ERR(mailbox);
248 	mgm = mailbox->buf;
249 
250 	mutex_lock(&dev->mcg_table.mutex);
251 
252 	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
253 	if (err)
254 		goto out;
255 
256 	if (index == -1) {
257 		mthca_err(dev, "MGID %pI6 not found\n", gid->raw);
258 		err = -EINVAL;
259 		goto out;
260 	}
261 
262 	for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {
263 		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))
264 			loc = i;
265 		if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))
266 			break;
267 	}
268 
269 	if (loc == -1) {
270 		mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);
271 		err = -EINVAL;
272 		goto out;
273 	}
274 
275 	mgm->qp[loc]   = mgm->qp[i - 1];
276 	mgm->qp[i - 1] = 0;
277 
278 	err = mthca_WRITE_MGM(dev, index, mailbox, &status);
279 	if (err)
280 		goto out;
281 	if (status) {
282 		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
283 		err = -EINVAL;
284 		goto out;
285 	}
286 
287 	if (i != 1)
288 		goto out;
289 
290 	if (prev == -1) {
291 		/* Remove entry from MGM */
292 		int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;
293 		if (amgm_index_to_free) {
294 			err = mthca_READ_MGM(dev, amgm_index_to_free,
295 					     mailbox, &status);
296 			if (err)
297 				goto out;
298 			if (status) {
299 				mthca_err(dev, "READ_MGM returned status %02x\n",
300 					  status);
301 				err = -EINVAL;
302 				goto out;
303 			}
304 		} else
305 			memset(mgm->gid, 0, 16);
306 
307 		err = mthca_WRITE_MGM(dev, index, mailbox, &status);
308 		if (err)
309 			goto out;
310 		if (status) {
311 			mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
312 			err = -EINVAL;
313 			goto out;
314 		}
315 		if (amgm_index_to_free) {
316 			BUG_ON(amgm_index_to_free < dev->limits.num_mgms);
317 			mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);
318 		}
319 	} else {
320 		/* Remove entry from AMGM */
321 		int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
322 		err = mthca_READ_MGM(dev, prev, mailbox, &status);
323 		if (err)
324 			goto out;
325 		if (status) {
326 			mthca_err(dev, "READ_MGM returned status %02x\n", status);
327 			err = -EINVAL;
328 			goto out;
329 		}
330 
331 		mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);
332 
333 		err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
334 		if (err)
335 			goto out;
336 		if (status) {
337 			mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
338 			err = -EINVAL;
339 			goto out;
340 		}
341 		BUG_ON(index < dev->limits.num_mgms);
342 		mthca_free(&dev->mcg_table.alloc, index);
343 	}
344 
345  out:
346 	mutex_unlock(&dev->mcg_table.mutex);
347 
348 	mthca_free_mailbox(dev, mailbox);
349 	return err;
350 }
351 
352 int mthca_init_mcg_table(struct mthca_dev *dev)
353 {
354 	int err;
355 	int table_size = dev->limits.num_mgms + dev->limits.num_amgms;
356 
357 	err = mthca_alloc_init(&dev->mcg_table.alloc,
358 			       table_size,
359 			       table_size - 1,
360 			       dev->limits.num_mgms);
361 	if (err)
362 		return err;
363 
364 	mutex_init(&dev->mcg_table.mutex);
365 
366 	return 0;
367 }
368 
369 void mthca_cleanup_mcg_table(struct mthca_dev *dev)
370 {
371 	mthca_alloc_cleanup(&dev->mcg_table.alloc);
372 }
373