mthca_mcg.c revision 225736
1219131Srwatson/*
2219131Srwatson * Copyright (c) 2004 Topspin Communications.  All rights reserved.
3219131Srwatson *
4219131Srwatson * This software is available to you under a choice of one of two
5219131Srwatson * licenses.  You may choose to be licensed under the terms of the GNU
6219131Srwatson * General Public License (GPL) Version 2, available from the file
7219131Srwatson * COPYING in the main directory of this source tree, or the
8219131Srwatson * OpenIB.org BSD license below:
9219131Srwatson *
10219131Srwatson *     Redistribution and use in source and binary forms, with or
11219131Srwatson *     without modification, are permitted provided that the following
12219131Srwatson *     conditions are met:
13219131Srwatson *
14219131Srwatson *      - Redistributions of source code must retain the above
15219131Srwatson *        copyright notice, this list of conditions and the following
16219131Srwatson *        disclaimer.
17219131Srwatson *
18219131Srwatson *      - Redistributions in binary form must reproduce the above
19219131Srwatson *        copyright notice, this list of conditions and the following
20219131Srwatson *        disclaimer in the documentation and/or other materials
21219131Srwatson *        provided with the distribution.
22219131Srwatson *
23219131Srwatson * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219131Srwatson * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219131Srwatson * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219131Srwatson * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219131Srwatson * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219131Srwatson * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219131Srwatson * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219131Srwatson * SOFTWARE.
31219131Srwatson */
32219131Srwatson
33219131Srwatson#include <linux/string.h>
34219131Srwatson#include <linux/slab.h>
35219131Srwatson
36219131Srwatson#include "mthca_dev.h"
37219131Srwatson#include "mthca_cmd.h"
38219131Srwatson
39219131Srwatsonstruct mthca_mgm {
40219131Srwatson	__be32 next_gid_index;
41219131Srwatson	u32    reserved[3];
42219131Srwatson	u8     gid[16];
43219131Srwatson	__be32 qp[MTHCA_QP_PER_MGM];
44219131Srwatson};
45219131Srwatson
46219131Srwatsonstatic const u8 zero_gid[16];	/* automatically initialized to 0 */
47219131Srwatson
48219131Srwatson/*
49219131Srwatson * Caller must hold MCG table semaphore.  gid and mgm parameters must
50219131Srwatson * be properly aligned for command interface.
51219131Srwatson *
52219131Srwatson *  Returns 0 unless a firmware command error occurs.
53219131Srwatson *
54219131Srwatson * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1
55219131Srwatson * and *mgm holds MGM entry.
56219131Srwatson *
57219131Srwatson * if GID is found in AMGM, *index = index in AMGM, *prev = index of
58219131Srwatson * previous entry in hash chain and *mgm holds AMGM entry.
59219131Srwatson *
60219131Srwatson * If no AMGM exists for given gid, *index = -1, *prev = index of last
61219131Srwatson * entry in hash chain and *mgm holds end of hash chain.
62219131Srwatson */
63219131Srwatsonstatic int find_mgm(struct mthca_dev *dev,
64219131Srwatson		    u8 *gid, struct mthca_mailbox *mgm_mailbox,
65219131Srwatson		    u16 *hash, int *prev, int *index)
66219131Srwatson{
67219131Srwatson	struct mthca_mailbox *mailbox;
68219131Srwatson	struct mthca_mgm *mgm = mgm_mailbox->buf;
69219131Srwatson	u8 *mgid;
70219131Srwatson	int err;
71219131Srwatson	u8 status;
72219131Srwatson
73219131Srwatson	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
74219131Srwatson	if (IS_ERR(mailbox))
75219131Srwatson		return -ENOMEM;
76219131Srwatson	mgid = mailbox->buf;
77219131Srwatson
78219131Srwatson	memcpy(mgid, gid, 16);
79219131Srwatson
80219131Srwatson	err = mthca_MGID_HASH(dev, mailbox, hash, &status);
81250154Sjilles	if (err)
82219131Srwatson		goto out;
83219131Srwatson	if (status) {
84219131Srwatson		mthca_err(dev, "MGID_HASH returned status %02x\n", status);
85219131Srwatson		err = -EINVAL;
86219131Srwatson		goto out;
87219131Srwatson	}
88219131Srwatson
89219131Srwatson	if (0)
90219131Srwatson		mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
91219131Srwatson
92219131Srwatson	*index = *hash;
93219131Srwatson	*prev  = -1;
94219131Srwatson
95219131Srwatson	do {
96219131Srwatson		err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status);
97219131Srwatson		if (err)
98219131Srwatson			goto out;
99219131Srwatson		if (status) {
100219131Srwatson			mthca_err(dev, "READ_MGM returned status %02x\n", status);
101219131Srwatson			err = -EINVAL;
102219131Srwatson			goto out;
103219131Srwatson		}
104247667Spjd
105219131Srwatson		if (!memcmp(mgm->gid, zero_gid, 16)) {
106247667Spjd			if (*index != *hash) {
107219131Srwatson				mthca_err(dev, "Found zero MGID in AMGM.\n");
108219131Srwatson				err = -EINVAL;
109219131Srwatson			}
110219131Srwatson			goto out;
111219131Srwatson		}
112247602Spjd
113247602Spjd		if (!memcmp(mgm->gid, gid, 16))
114219131Srwatson			goto out;
115247602Spjd
116247602Spjd		*prev = *index;
117255219Spjd		*index = be32_to_cpu(mgm->next_gid_index) >> 6;
118247602Spjd	} while (*index);
119219131Srwatson
120219131Srwatson	*index = -1;
121219131Srwatson
122219131Srwatson out:
123255374Spjd	mthca_free_mailbox(dev, mailbox);
124219131Srwatson	return err;
125219131Srwatson}
126219131Srwatson
127219131Srwatsonint mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
128219131Srwatson{
129219131Srwatson	struct mthca_dev *dev = to_mdev(ibqp->device);
130219131Srwatson	struct mthca_mailbox *mailbox;
131219131Srwatson	struct mthca_mgm *mgm;
132219131Srwatson	u16 hash;
133247667Spjd	int index, prev;
134219131Srwatson	int link = 0;
135247667Spjd	int i;
136219131Srwatson	int err;
137219131Srwatson	u8 status;
138219131Srwatson
139219131Srwatson	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
140219131Srwatson	if (IS_ERR(mailbox))
141219131Srwatson		return PTR_ERR(mailbox);
142219131Srwatson	mgm = mailbox->buf;
143219131Srwatson
144219131Srwatson	mutex_lock(&dev->mcg_table.mutex);
145219131Srwatson
146219131Srwatson	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
147219131Srwatson	if (err)
148219131Srwatson		goto out;
149219131Srwatson
150219131Srwatson	if (index != -1) {
151219131Srwatson		if (!memcmp(mgm->gid, zero_gid, 16))
152219131Srwatson			memcpy(mgm->gid, gid->raw, 16);
153219131Srwatson	} else {
154219131Srwatson		link = 1;
155219131Srwatson
156219131Srwatson		index = mthca_alloc(&dev->mcg_table.alloc);
157219131Srwatson		if (index == -1) {
158219131Srwatson			mthca_err(dev, "No AMGM entries left\n");
159219131Srwatson			err = -ENOMEM;
160219131Srwatson			goto out;
161219131Srwatson		}
162219131Srwatson
163219131Srwatson		err = mthca_READ_MGM(dev, index, mailbox, &status);
164219131Srwatson		if (err)
165219131Srwatson			goto out;
166219131Srwatson		if (status) {
167219131Srwatson			mthca_err(dev, "READ_MGM returned status %02x\n", status);
168219131Srwatson			err = -EINVAL;
169219131Srwatson			goto out;
170219131Srwatson		}
171219131Srwatson		memset(mgm, 0, sizeof *mgm);
172219131Srwatson		memcpy(mgm->gid, gid->raw, 16);
173219131Srwatson	}
174219131Srwatson
175219131Srwatson	for (i = 0; i < MTHCA_QP_PER_MGM; ++i)
176219131Srwatson		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {
177219131Srwatson			mthca_dbg(dev, "QP %06x already a member of MGM\n",
178219131Srwatson				  ibqp->qp_num);
179219131Srwatson			err = 0;
180219131Srwatson			goto out;
181219131Srwatson		} else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {
182219131Srwatson			mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));
183219131Srwatson			break;
184219131Srwatson		}
185219131Srwatson
186219131Srwatson	if (i == MTHCA_QP_PER_MGM) {
187219131Srwatson		mthca_err(dev, "MGM at index %x is full.\n", index);
188219131Srwatson		err = -ENOMEM;
189219131Srwatson		goto out;
190219131Srwatson	}
191219131Srwatson
192219131Srwatson	err = mthca_WRITE_MGM(dev, index, mailbox, &status);
193219131Srwatson	if (err)
194219131Srwatson		goto out;
195219131Srwatson	if (status) {
196219131Srwatson		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
197219131Srwatson		err = -EINVAL;
198219131Srwatson		goto out;
199219131Srwatson	}
200224852Srwatson
201219131Srwatson	if (!link)
202219131Srwatson		goto out;
203219131Srwatson
204219131Srwatson	err = mthca_READ_MGM(dev, prev, mailbox, &status);
205219131Srwatson	if (err)
206219131Srwatson		goto out;
207219131Srwatson	if (status) {
208219131Srwatson		mthca_err(dev, "READ_MGM returned status %02x\n", status);
209219131Srwatson		err = -EINVAL;
210219131Srwatson		goto out;
211219131Srwatson	}
212219131Srwatson
213219131Srwatson	mgm->next_gid_index = cpu_to_be32(index << 6);
214219131Srwatson
215219131Srwatson	err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
216219131Srwatson	if (err)
217219131Srwatson		goto out;
218219131Srwatson	if (status) {
219219131Srwatson		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
220219131Srwatson		err = -EINVAL;
221219131Srwatson	}
222219131Srwatson
223219131Srwatson out:
224219131Srwatson	if (err && link && index != -1) {
225219131Srwatson		BUG_ON(index < dev->limits.num_mgms);
226219131Srwatson		mthca_free(&dev->mcg_table.alloc, index);
227219131Srwatson	}
228219131Srwatson	mutex_unlock(&dev->mcg_table.mutex);
229219131Srwatson
230219131Srwatson	mthca_free_mailbox(dev, mailbox);
231219131Srwatson	return err;
232219131Srwatson}
233219131Srwatson
234219131Srwatsonint mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
235219131Srwatson{
236219131Srwatson	struct mthca_dev *dev = to_mdev(ibqp->device);
237219131Srwatson	struct mthca_mailbox *mailbox;
238219131Srwatson	struct mthca_mgm *mgm;
239219131Srwatson	u16 hash;
240219131Srwatson	int prev, index;
241219131Srwatson	int i, loc;
242219131Srwatson	int err;
243247602Spjd	u8 status;
244219131Srwatson
245219131Srwatson	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
246219131Srwatson	if (IS_ERR(mailbox))
247219131Srwatson		return PTR_ERR(mailbox);
248219131Srwatson	mgm = mailbox->buf;
249219131Srwatson
250219131Srwatson	mutex_lock(&dev->mcg_table.mutex);
251219131Srwatson
252219131Srwatson	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
253219131Srwatson	if (err)
254219131Srwatson		goto out;
255219131Srwatson
256219131Srwatson	if (index == -1) {
257219131Srwatson		mthca_err(dev, "MGID %pI6 not found\n", gid->raw);
258219131Srwatson		err = -EINVAL;
259219131Srwatson		goto out;
260219131Srwatson	}
261219131Srwatson
262219131Srwatson	for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {
263219131Srwatson		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))
264219131Srwatson			loc = i;
265219131Srwatson		if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))
266219131Srwatson			break;
267219131Srwatson	}
268219131Srwatson
269219131Srwatson	if (loc == -1) {
270219131Srwatson		mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);
271219131Srwatson		err = -EINVAL;
272219131Srwatson		goto out;
273219131Srwatson	}
274219131Srwatson
275219131Srwatson	mgm->qp[loc]   = mgm->qp[i - 1];
276219131Srwatson	mgm->qp[i - 1] = 0;
277219131Srwatson
278219131Srwatson	err = mthca_WRITE_MGM(dev, index, mailbox, &status);
279219131Srwatson	if (err)
280219131Srwatson		goto out;
281219131Srwatson	if (status) {
282219131Srwatson		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
283219131Srwatson		err = -EINVAL;
284219131Srwatson		goto out;
285219131Srwatson	}
286219131Srwatson
287219131Srwatson	if (i != 1)
288219131Srwatson		goto out;
289219131Srwatson
290219131Srwatson	if (prev == -1) {
291219131Srwatson		/* Remove entry from MGM */
292219131Srwatson		int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;
293219131Srwatson		if (amgm_index_to_free) {
294219131Srwatson			err = mthca_READ_MGM(dev, amgm_index_to_free,
295219131Srwatson					     mailbox, &status);
296219131Srwatson			if (err)
297219131Srwatson				goto out;
298219131Srwatson			if (status) {
299219131Srwatson				mthca_err(dev, "READ_MGM returned status %02x\n",
300219131Srwatson					  status);
301219131Srwatson				err = -EINVAL;
302219131Srwatson				goto out;
303219131Srwatson			}
304219131Srwatson		} else
305219131Srwatson			memset(mgm->gid, 0, 16);
306219131Srwatson
307219131Srwatson		err = mthca_WRITE_MGM(dev, index, mailbox, &status);
308219131Srwatson		if (err)
309219131Srwatson			goto out;
310219131Srwatson		if (status) {
311219131Srwatson			mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
312219131Srwatson			err = -EINVAL;
313219131Srwatson			goto out;
314219131Srwatson		}
315219131Srwatson		if (amgm_index_to_free) {
316219131Srwatson			BUG_ON(amgm_index_to_free < dev->limits.num_mgms);
317219131Srwatson			mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);
318219131Srwatson		}
319219131Srwatson	} else {
320219131Srwatson		/* Remove entry from AMGM */
321247602Spjd		int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
322247602Spjd		err = mthca_READ_MGM(dev, prev, mailbox, &status);
323219131Srwatson		if (err)
324247602Spjd			goto out;
325219131Srwatson		if (status) {
326219131Srwatson			mthca_err(dev, "READ_MGM returned status %02x\n", status);
327219131Srwatson			err = -EINVAL;
328219131Srwatson			goto out;
329219131Srwatson		}
330219131Srwatson
331219131Srwatson		mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);
332219131Srwatson
333219131Srwatson		err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
334219131Srwatson		if (err)
335219131Srwatson			goto out;
336219131Srwatson		if (status) {
337219131Srwatson			mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
338243610Spjd			err = -EINVAL;
339243610Spjd			goto out;
340243610Spjd		}
341243610Spjd		BUG_ON(index < dev->limits.num_mgms);
342243610Spjd		mthca_free(&dev->mcg_table.alloc, index);
343219131Srwatson	}
344219131Srwatson
345219131Srwatson out:
346219131Srwatson	mutex_unlock(&dev->mcg_table.mutex);
347219131Srwatson
348219131Srwatson	mthca_free_mailbox(dev, mailbox);
349219131Srwatson	return err;
350219131Srwatson}
351219131Srwatson
352219131Srwatsonint mthca_init_mcg_table(struct mthca_dev *dev)
353219131Srwatson{
354219131Srwatson	int err;
355219131Srwatson	int table_size = dev->limits.num_mgms + dev->limits.num_amgms;
356219131Srwatson
357219131Srwatson	err = mthca_alloc_init(&dev->mcg_table.alloc,
358219131Srwatson			       table_size,
359219131Srwatson			       table_size - 1,
360219131Srwatson			       dev->limits.num_mgms);
361219131Srwatson	if (err)
362219131Srwatson		return err;
363219131Srwatson
364219131Srwatson	mutex_init(&dev->mcg_table.mutex);
365219131Srwatson
366219131Srwatson	return 0;
367219131Srwatson}
368219131Srwatson
369219131Srwatsonvoid mthca_cleanup_mcg_table(struct mthca_dev *dev)
370219131Srwatson{
371219131Srwatson	mthca_alloc_cleanup(&dev->mcg_table.alloc);
372219131Srwatson}
373219131Srwatson