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