1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004 Topspin Communications.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff */
32219820Sjeff
33219820Sjeff#include <linux/string.h>
34219820Sjeff#include <linux/slab.h>
35219820Sjeff
36219820Sjeff#include "mthca_dev.h"
37219820Sjeff#include "mthca_cmd.h"
38219820Sjeff
39219820Sjeffstruct mthca_mgm {
40219820Sjeff	__be32 next_gid_index;
41219820Sjeff	u32    reserved[3];
42219820Sjeff	u8     gid[16];
43219820Sjeff	__be32 qp[MTHCA_QP_PER_MGM];
44219820Sjeff};
45219820Sjeff
46219820Sjeffstatic const u8 zero_gid[16];	/* automatically initialized to 0 */
47219820Sjeff
48219820Sjeff/*
49219820Sjeff * Caller must hold MCG table semaphore.  gid and mgm parameters must
50219820Sjeff * be properly aligned for command interface.
51219820Sjeff *
52219820Sjeff *  Returns 0 unless a firmware command error occurs.
53219820Sjeff *
54219820Sjeff * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1
55219820Sjeff * and *mgm holds MGM entry.
56219820Sjeff *
57219820Sjeff * if GID is found in AMGM, *index = index in AMGM, *prev = index of
58219820Sjeff * previous entry in hash chain and *mgm holds AMGM entry.
59219820Sjeff *
60219820Sjeff * If no AMGM exists for given gid, *index = -1, *prev = index of last
61219820Sjeff * entry in hash chain and *mgm holds end of hash chain.
62219820Sjeff */
63219820Sjeffstatic int find_mgm(struct mthca_dev *dev,
64219820Sjeff		    u8 *gid, struct mthca_mailbox *mgm_mailbox,
65219820Sjeff		    u16 *hash, int *prev, int *index)
66219820Sjeff{
67219820Sjeff	struct mthca_mailbox *mailbox;
68219820Sjeff	struct mthca_mgm *mgm = mgm_mailbox->buf;
69219820Sjeff	u8 *mgid;
70219820Sjeff	int err;
71219820Sjeff	u8 status;
72219820Sjeff
73219820Sjeff	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
74219820Sjeff	if (IS_ERR(mailbox))
75219820Sjeff		return -ENOMEM;
76219820Sjeff	mgid = mailbox->buf;
77219820Sjeff
78219820Sjeff	memcpy(mgid, gid, 16);
79219820Sjeff
80219820Sjeff	err = mthca_MGID_HASH(dev, mailbox, hash, &status);
81219820Sjeff	if (err)
82219820Sjeff		goto out;
83219820Sjeff	if (status) {
84219820Sjeff		mthca_err(dev, "MGID_HASH returned status %02x\n", status);
85219820Sjeff		err = -EINVAL;
86219820Sjeff		goto out;
87219820Sjeff	}
88219820Sjeff
89219820Sjeff	if (0)
90219820Sjeff		mthca_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
91219820Sjeff
92219820Sjeff	*index = *hash;
93219820Sjeff	*prev  = -1;
94219820Sjeff
95219820Sjeff	do {
96219820Sjeff		err = mthca_READ_MGM(dev, *index, mgm_mailbox, &status);
97219820Sjeff		if (err)
98219820Sjeff			goto out;
99219820Sjeff		if (status) {
100219820Sjeff			mthca_err(dev, "READ_MGM returned status %02x\n", status);
101219820Sjeff			err = -EINVAL;
102219820Sjeff			goto out;
103219820Sjeff		}
104219820Sjeff
105219820Sjeff		if (!memcmp(mgm->gid, zero_gid, 16)) {
106219820Sjeff			if (*index != *hash) {
107219820Sjeff				mthca_err(dev, "Found zero MGID in AMGM.\n");
108219820Sjeff				err = -EINVAL;
109219820Sjeff			}
110219820Sjeff			goto out;
111219820Sjeff		}
112219820Sjeff
113219820Sjeff		if (!memcmp(mgm->gid, gid, 16))
114219820Sjeff			goto out;
115219820Sjeff
116219820Sjeff		*prev = *index;
117219820Sjeff		*index = be32_to_cpu(mgm->next_gid_index) >> 6;
118219820Sjeff	} while (*index);
119219820Sjeff
120219820Sjeff	*index = -1;
121219820Sjeff
122219820Sjeff out:
123219820Sjeff	mthca_free_mailbox(dev, mailbox);
124219820Sjeff	return err;
125219820Sjeff}
126219820Sjeff
127219820Sjeffint mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
128219820Sjeff{
129219820Sjeff	struct mthca_dev *dev = to_mdev(ibqp->device);
130219820Sjeff	struct mthca_mailbox *mailbox;
131219820Sjeff	struct mthca_mgm *mgm;
132219820Sjeff	u16 hash;
133219820Sjeff	int index, prev;
134219820Sjeff	int link = 0;
135219820Sjeff	int i;
136219820Sjeff	int err;
137219820Sjeff	u8 status;
138219820Sjeff
139219820Sjeff	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
140219820Sjeff	if (IS_ERR(mailbox))
141219820Sjeff		return PTR_ERR(mailbox);
142219820Sjeff	mgm = mailbox->buf;
143219820Sjeff
144219820Sjeff	mutex_lock(&dev->mcg_table.mutex);
145219820Sjeff
146219820Sjeff	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
147219820Sjeff	if (err)
148219820Sjeff		goto out;
149219820Sjeff
150219820Sjeff	if (index != -1) {
151219820Sjeff		if (!memcmp(mgm->gid, zero_gid, 16))
152219820Sjeff			memcpy(mgm->gid, gid->raw, 16);
153219820Sjeff	} else {
154219820Sjeff		link = 1;
155219820Sjeff
156219820Sjeff		index = mthca_alloc(&dev->mcg_table.alloc);
157219820Sjeff		if (index == -1) {
158219820Sjeff			mthca_err(dev, "No AMGM entries left\n");
159219820Sjeff			err = -ENOMEM;
160219820Sjeff			goto out;
161219820Sjeff		}
162219820Sjeff
163219820Sjeff		err = mthca_READ_MGM(dev, index, mailbox, &status);
164219820Sjeff		if (err)
165219820Sjeff			goto out;
166219820Sjeff		if (status) {
167219820Sjeff			mthca_err(dev, "READ_MGM returned status %02x\n", status);
168219820Sjeff			err = -EINVAL;
169219820Sjeff			goto out;
170219820Sjeff		}
171219820Sjeff		memset(mgm, 0, sizeof *mgm);
172219820Sjeff		memcpy(mgm->gid, gid->raw, 16);
173219820Sjeff	}
174219820Sjeff
175219820Sjeff	for (i = 0; i < MTHCA_QP_PER_MGM; ++i)
176219820Sjeff		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31))) {
177219820Sjeff			mthca_dbg(dev, "QP %06x already a member of MGM\n",
178219820Sjeff				  ibqp->qp_num);
179219820Sjeff			err = 0;
180219820Sjeff			goto out;
181219820Sjeff		} else if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {
182219820Sjeff			mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));
183219820Sjeff			break;
184219820Sjeff		}
185219820Sjeff
186219820Sjeff	if (i == MTHCA_QP_PER_MGM) {
187219820Sjeff		mthca_err(dev, "MGM at index %x is full.\n", index);
188219820Sjeff		err = -ENOMEM;
189219820Sjeff		goto out;
190219820Sjeff	}
191219820Sjeff
192219820Sjeff	err = mthca_WRITE_MGM(dev, index, mailbox, &status);
193219820Sjeff	if (err)
194219820Sjeff		goto out;
195219820Sjeff	if (status) {
196219820Sjeff		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
197219820Sjeff		err = -EINVAL;
198219820Sjeff		goto out;
199219820Sjeff	}
200219820Sjeff
201219820Sjeff	if (!link)
202219820Sjeff		goto out;
203219820Sjeff
204219820Sjeff	err = mthca_READ_MGM(dev, prev, mailbox, &status);
205219820Sjeff	if (err)
206219820Sjeff		goto out;
207219820Sjeff	if (status) {
208219820Sjeff		mthca_err(dev, "READ_MGM returned status %02x\n", status);
209219820Sjeff		err = -EINVAL;
210219820Sjeff		goto out;
211219820Sjeff	}
212219820Sjeff
213219820Sjeff	mgm->next_gid_index = cpu_to_be32(index << 6);
214219820Sjeff
215219820Sjeff	err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
216219820Sjeff	if (err)
217219820Sjeff		goto out;
218219820Sjeff	if (status) {
219219820Sjeff		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
220219820Sjeff		err = -EINVAL;
221219820Sjeff	}
222219820Sjeff
223219820Sjeff out:
224219820Sjeff	if (err && link && index != -1) {
225219820Sjeff		BUG_ON(index < dev->limits.num_mgms);
226219820Sjeff		mthca_free(&dev->mcg_table.alloc, index);
227219820Sjeff	}
228219820Sjeff	mutex_unlock(&dev->mcg_table.mutex);
229219820Sjeff
230219820Sjeff	mthca_free_mailbox(dev, mailbox);
231219820Sjeff	return err;
232219820Sjeff}
233219820Sjeff
234219820Sjeffint mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
235219820Sjeff{
236219820Sjeff	struct mthca_dev *dev = to_mdev(ibqp->device);
237219820Sjeff	struct mthca_mailbox *mailbox;
238219820Sjeff	struct mthca_mgm *mgm;
239219820Sjeff	u16 hash;
240219820Sjeff	int prev, index;
241219820Sjeff	int i, loc;
242219820Sjeff	int err;
243219820Sjeff	u8 status;
244219820Sjeff
245219820Sjeff	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
246219820Sjeff	if (IS_ERR(mailbox))
247219820Sjeff		return PTR_ERR(mailbox);
248219820Sjeff	mgm = mailbox->buf;
249219820Sjeff
250219820Sjeff	mutex_lock(&dev->mcg_table.mutex);
251219820Sjeff
252219820Sjeff	err = find_mgm(dev, gid->raw, mailbox, &hash, &prev, &index);
253219820Sjeff	if (err)
254219820Sjeff		goto out;
255219820Sjeff
256219820Sjeff	if (index == -1) {
257219820Sjeff		mthca_err(dev, "MGID %pI6 not found\n", gid->raw);
258219820Sjeff		err = -EINVAL;
259219820Sjeff		goto out;
260219820Sjeff	}
261219820Sjeff
262219820Sjeff	for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {
263219820Sjeff		if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))
264219820Sjeff			loc = i;
265219820Sjeff		if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))
266219820Sjeff			break;
267219820Sjeff	}
268219820Sjeff
269219820Sjeff	if (loc == -1) {
270219820Sjeff		mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);
271219820Sjeff		err = -EINVAL;
272219820Sjeff		goto out;
273219820Sjeff	}
274219820Sjeff
275219820Sjeff	mgm->qp[loc]   = mgm->qp[i - 1];
276219820Sjeff	mgm->qp[i - 1] = 0;
277219820Sjeff
278219820Sjeff	err = mthca_WRITE_MGM(dev, index, mailbox, &status);
279219820Sjeff	if (err)
280219820Sjeff		goto out;
281219820Sjeff	if (status) {
282219820Sjeff		mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
283219820Sjeff		err = -EINVAL;
284219820Sjeff		goto out;
285219820Sjeff	}
286219820Sjeff
287219820Sjeff	if (i != 1)
288219820Sjeff		goto out;
289219820Sjeff
290219820Sjeff	if (prev == -1) {
291219820Sjeff		/* Remove entry from MGM */
292219820Sjeff		int amgm_index_to_free = be32_to_cpu(mgm->next_gid_index) >> 6;
293219820Sjeff		if (amgm_index_to_free) {
294219820Sjeff			err = mthca_READ_MGM(dev, amgm_index_to_free,
295219820Sjeff					     mailbox, &status);
296219820Sjeff			if (err)
297219820Sjeff				goto out;
298219820Sjeff			if (status) {
299219820Sjeff				mthca_err(dev, "READ_MGM returned status %02x\n",
300219820Sjeff					  status);
301219820Sjeff				err = -EINVAL;
302219820Sjeff				goto out;
303219820Sjeff			}
304219820Sjeff		} else
305219820Sjeff			memset(mgm->gid, 0, 16);
306219820Sjeff
307219820Sjeff		err = mthca_WRITE_MGM(dev, index, mailbox, &status);
308219820Sjeff		if (err)
309219820Sjeff			goto out;
310219820Sjeff		if (status) {
311219820Sjeff			mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
312219820Sjeff			err = -EINVAL;
313219820Sjeff			goto out;
314219820Sjeff		}
315219820Sjeff		if (amgm_index_to_free) {
316219820Sjeff			BUG_ON(amgm_index_to_free < dev->limits.num_mgms);
317219820Sjeff			mthca_free(&dev->mcg_table.alloc, amgm_index_to_free);
318219820Sjeff		}
319219820Sjeff	} else {
320219820Sjeff		/* Remove entry from AMGM */
321219820Sjeff		int curr_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
322219820Sjeff		err = mthca_READ_MGM(dev, prev, mailbox, &status);
323219820Sjeff		if (err)
324219820Sjeff			goto out;
325219820Sjeff		if (status) {
326219820Sjeff			mthca_err(dev, "READ_MGM returned status %02x\n", status);
327219820Sjeff			err = -EINVAL;
328219820Sjeff			goto out;
329219820Sjeff		}
330219820Sjeff
331219820Sjeff		mgm->next_gid_index = cpu_to_be32(curr_next_index << 6);
332219820Sjeff
333219820Sjeff		err = mthca_WRITE_MGM(dev, prev, mailbox, &status);
334219820Sjeff		if (err)
335219820Sjeff			goto out;
336219820Sjeff		if (status) {
337219820Sjeff			mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
338219820Sjeff			err = -EINVAL;
339219820Sjeff			goto out;
340219820Sjeff		}
341219820Sjeff		BUG_ON(index < dev->limits.num_mgms);
342219820Sjeff		mthca_free(&dev->mcg_table.alloc, index);
343219820Sjeff	}
344219820Sjeff
345219820Sjeff out:
346219820Sjeff	mutex_unlock(&dev->mcg_table.mutex);
347219820Sjeff
348219820Sjeff	mthca_free_mailbox(dev, mailbox);
349219820Sjeff	return err;
350219820Sjeff}
351219820Sjeff
352219820Sjeffint mthca_init_mcg_table(struct mthca_dev *dev)
353219820Sjeff{
354219820Sjeff	int err;
355219820Sjeff	int table_size = dev->limits.num_mgms + dev->limits.num_amgms;
356219820Sjeff
357219820Sjeff	err = mthca_alloc_init(&dev->mcg_table.alloc,
358219820Sjeff			       table_size,
359219820Sjeff			       table_size - 1,
360219820Sjeff			       dev->limits.num_mgms);
361219820Sjeff	if (err)
362219820Sjeff		return err;
363219820Sjeff
364219820Sjeff	mutex_init(&dev->mcg_table.mutex);
365219820Sjeff
366219820Sjeff	return 0;
367219820Sjeff}
368219820Sjeff
369219820Sjeffvoid mthca_cleanup_mcg_table(struct mthca_dev *dev)
370219820Sjeff{
371219820Sjeff	mthca_alloc_cleanup(&dev->mcg_table.alloc);
372219820Sjeff}
373