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