1/*
2 * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
3 * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
4 *
5 * This software is available to you under a choice of one of two
6 * licenses.  You may choose to be licensed under the terms of the GNU
7 * General Public License (GPL) Version 2, available from the file
8 * COPYING in the main directory of this source tree, or the
9 * OpenIB.org BSD license below:
10 *
11 *     Redistribution and use in source and binary forms, with or
12 *     without modification, are permitted provided that the following
13 *     conditions are met:
14 *
15 *      - Redistributions of source code must retain the above
16 *        copyright notice, this list of conditions and the following
17 *        disclaimer.
18 *
19 *      - Redistributions in binary form must reproduce the above
20 *        copyright notice, this list of conditions and the following
21 *        disclaimer in the documentation and/or other materials
22 *        provided with the distribution.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 * SOFTWARE.
32 */
33
34#include <linux/init.h>
35#include <linux/string.h>
36#include <linux/slab.h>
37
38#include <linux/mlx4/cmd.h>
39#include <linux/mlx4/driver.h>
40
41#include "mlx4.h"
42
43#define MGM_QPN_MASK       0x00FFFFFF
44#define MGM_BLCK_LB_BIT    30
45
46struct mlx4_mgm {
47	__be32			next_gid_index;
48	__be32			members_count;
49	u32			reserved[2];
50	u8			gid[16];
51	__be32			qp[MLX4_QP_PER_MGM];
52};
53
54static const u8 zero_gid[16];	/* automatically initialized to 0 */
55
56static int mlx4_READ_MCG(struct mlx4_dev *dev, int index,
57			 struct mlx4_cmd_mailbox *mailbox)
58{
59	return mlx4_cmd_box(dev, 0, mailbox->dma, index, 0, MLX4_CMD_READ_MCG,
60			    MLX4_CMD_TIME_CLASS_A);
61}
62
63static int mlx4_WRITE_MCG(struct mlx4_dev *dev, int index,
64			  struct mlx4_cmd_mailbox *mailbox)
65{
66	return mlx4_cmd(dev, mailbox->dma, index, 0, MLX4_CMD_WRITE_MCG,
67			MLX4_CMD_TIME_CLASS_A);
68}
69
70static int mlx4_MGID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
71			  u16 *hash)
72{
73	u64 imm;
74	int err;
75
76	err = mlx4_cmd_imm(dev, mailbox->dma, &imm, 0, 0, MLX4_CMD_MGID_HASH,
77			   MLX4_CMD_TIME_CLASS_A);
78
79	if (!err)
80		*hash = imm;
81
82	return err;
83}
84
85/*
86 * Caller must hold MCG table semaphore.  gid and mgm parameters must
87 * be properly aligned for command interface.
88 *
89 *  Returns 0 unless a firmware command error occurs.
90 *
91 * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1
92 * and *mgm holds MGM entry.
93 *
94 * if GID is found in AMGM, *index = index in AMGM, *prev = index of
95 * previous entry in hash chain and *mgm holds AMGM entry.
96 *
97 * If no AMGM exists for given gid, *index = -1, *prev = index of last
98 * entry in hash chain and *mgm holds end of hash chain.
99 */
100static int find_mgm(struct mlx4_dev *dev,
101		    u8 *gid, enum mlx4_mcast_prot prot,
102		    struct mlx4_cmd_mailbox *mgm_mailbox,
103		    u16 *hash, int *prev, int *index)
104{
105	struct mlx4_cmd_mailbox *mailbox;
106	struct mlx4_mgm *mgm = mgm_mailbox->buf;
107	u8 *mgid;
108	int err;
109
110	mailbox = mlx4_alloc_cmd_mailbox(dev);
111	if (IS_ERR(mailbox))
112		return -ENOMEM;
113	mgid = mailbox->buf;
114
115	memcpy(mgid, gid, 16);
116
117	err = mlx4_MGID_HASH(dev, mailbox, hash);
118	mlx4_free_cmd_mailbox(dev, mailbox);
119	if (err)
120		return err;
121
122	if (0)
123		mlx4_dbg(dev, "Hash for %pI6 is %04x\n", gid, *hash);
124
125	*index = *hash;
126	*prev  = -1;
127
128	do {
129		err = mlx4_READ_MCG(dev, *index, mgm_mailbox);
130		if (err)
131			return err;
132
133		if (!memcmp(mgm->gid, zero_gid, 16)) {
134			if (*index != *hash) {
135				mlx4_err(dev, "Found zero MGID in AMGM.\n");
136				err = -EINVAL;
137			}
138			return err;
139		}
140
141		if (!memcmp(mgm->gid, gid, 16) &&
142				(prot == be32_to_cpu(mgm->members_count) >> 30))
143			return err;
144
145		*prev = *index;
146		*index = be32_to_cpu(mgm->next_gid_index) >> 6;
147	} while (*index);
148
149	*index = -1;
150	return err;
151}
152
153int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
154			  int block_mcast_loopback, enum mlx4_mcast_prot prot)
155{
156	struct mlx4_priv *priv = mlx4_priv(dev);
157	struct mlx4_cmd_mailbox *mailbox;
158	struct mlx4_mgm *mgm;
159	u32 members_count;
160	u16 hash;
161	int index, prev;
162	int link = 0;
163	int i;
164	int err;
165
166	mailbox = mlx4_alloc_cmd_mailbox(dev);
167	if (IS_ERR(mailbox))
168		return PTR_ERR(mailbox);
169	mgm = mailbox->buf;
170
171	mutex_lock(&priv->mcg_table.mutex);
172
173	err = find_mgm(dev, gid, prot, mailbox, &hash, &prev, &index);
174	if (err)
175		goto out;
176
177	if (index != -1) {
178		if (!memcmp(mgm->gid, zero_gid, 16))
179			memcpy(mgm->gid, gid, 16);
180	} else {
181		link = 1;
182
183		index = mlx4_bitmap_alloc(&priv->mcg_table.bitmap);
184		if (index == -1) {
185			mlx4_err(dev, "No AMGM entries left\n");
186			err = -ENOMEM;
187			goto out;
188		}
189		index += dev->caps.num_mgms;
190
191		memset(mgm, 0, sizeof *mgm);
192		memcpy(mgm->gid, gid, 16);
193	}
194
195	members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
196	if (members_count == MLX4_QP_PER_MGM) {
197		mlx4_err(dev, "MGM at index %x is full.\n", index);
198		err = -ENOMEM;
199		goto out;
200	}
201
202	for (i = 0; i < members_count; ++i)
203		if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) {
204			mlx4_dbg(dev, "QP %06x already a member of MGM\n", qp->qpn);
205			err = 0;
206			goto out;
207		}
208
209	mgm->qp[members_count++] = cpu_to_be32((qp->qpn & MGM_QPN_MASK) |
210					       (!!mlx4_blck_lb << MGM_BLCK_LB_BIT));
211
212	mgm->members_count = cpu_to_be32(members_count | ((u32) prot << 30));
213
214	err = mlx4_WRITE_MCG(dev, index, mailbox);
215	if (err)
216		goto out;
217
218	if (!link)
219		goto out;
220
221	err = mlx4_READ_MCG(dev, prev, mailbox);
222	if (err)
223		goto out;
224
225	mgm->next_gid_index = cpu_to_be32(index << 6);
226
227	err = mlx4_WRITE_MCG(dev, prev, mailbox);
228	if (err)
229		goto out;
230
231out:
232	if (err && link && index != -1) {
233		if (index < dev->caps.num_mgms)
234			mlx4_warn(dev, "Got AMGM index %d < %d",
235				  index, dev->caps.num_mgms);
236		else
237			mlx4_bitmap_free(&priv->mcg_table.bitmap,
238					 index - dev->caps.num_mgms);
239	}
240	mutex_unlock(&priv->mcg_table.mutex);
241
242	mlx4_free_cmd_mailbox(dev, mailbox);
243	return err;
244}
245EXPORT_SYMBOL_GPL(mlx4_multicast_attach);
246
247int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
248						enum mlx4_mcast_prot prot)
249{
250	struct mlx4_priv *priv = mlx4_priv(dev);
251	struct mlx4_cmd_mailbox *mailbox;
252	struct mlx4_mgm *mgm;
253	u32 members_count;
254	u16 hash;
255	int prev, index;
256	int i, loc;
257	int err;
258
259	mailbox = mlx4_alloc_cmd_mailbox(dev);
260	if (IS_ERR(mailbox))
261		return PTR_ERR(mailbox);
262	mgm = mailbox->buf;
263
264	mutex_lock(&priv->mcg_table.mutex);
265
266	err = find_mgm(dev, gid, prot, mailbox, &hash, &prev, &index);
267	if (err)
268		goto out;
269
270	if (index == -1) {
271		mlx4_err(dev, "MGID %pI6 not found\n", gid);
272		err = -EINVAL;
273		goto out;
274	}
275
276	members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
277	for (loc = -1, i = 0; i < members_count; ++i)
278		if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn)
279			loc = i;
280
281	if (loc == -1) {
282		mlx4_err(dev, "QP %06x not found in MGM\n", qp->qpn);
283		err = -EINVAL;
284		goto out;
285	}
286
287
288	mgm->members_count = cpu_to_be32(--members_count | ((u32) prot << 30));
289	mgm->qp[loc]       = mgm->qp[i - 1];
290	mgm->qp[i - 1]     = 0;
291
292	if (i != 1) {
293		err = mlx4_WRITE_MCG(dev, index, mailbox);
294		goto out;
295	}
296
297	if (prev == -1) {
298		/* Remove entry from MGM */
299		int amgm_index = be32_to_cpu(mgm->next_gid_index) >> 6;
300		if (amgm_index) {
301			err = mlx4_READ_MCG(dev, amgm_index, mailbox);
302			if (err)
303				goto out;
304		} else
305			memset(mgm->gid, 0, 16);
306
307		err = mlx4_WRITE_MCG(dev, index, mailbox);
308		if (err)
309			goto out;
310
311		if (amgm_index) {
312			if (amgm_index < dev->caps.num_mgms)
313				mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d",
314					  index, amgm_index, dev->caps.num_mgms);
315			else
316				mlx4_bitmap_free(&priv->mcg_table.bitmap,
317						 amgm_index - dev->caps.num_mgms);
318		}
319	} else {
320		/* Remove entry from AMGM */
321		int cur_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
322		err = mlx4_READ_MCG(dev, prev, mailbox);
323		if (err)
324			goto out;
325
326		mgm->next_gid_index = cpu_to_be32(cur_next_index << 6);
327
328		err = mlx4_WRITE_MCG(dev, prev, mailbox);
329		if (err)
330			goto out;
331
332		if (index < dev->caps.num_mgms)
333			mlx4_warn(dev, "entry %d had next AMGM index %d < %d",
334				  prev, index, dev->caps.num_mgms);
335		else
336			mlx4_bitmap_free(&priv->mcg_table.bitmap,
337					 index - dev->caps.num_mgms);
338	}
339
340out:
341	mutex_unlock(&priv->mcg_table.mutex);
342
343	mlx4_free_cmd_mailbox(dev, mailbox);
344	return err;
345}
346EXPORT_SYMBOL_GPL(mlx4_multicast_detach);
347
348int mlx4_init_mcg_table(struct mlx4_dev *dev)
349{
350	struct mlx4_priv *priv = mlx4_priv(dev);
351	int err;
352
353	err = mlx4_bitmap_init(&priv->mcg_table.bitmap, dev->caps.num_amgms,
354			       dev->caps.num_amgms - 1, 0, 0);
355	if (err)
356		return err;
357
358	mutex_init(&priv->mcg_table.mutex);
359
360	return 0;
361}
362
363void mlx4_cleanup_mcg_table(struct mlx4_dev *dev)
364{
365	mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap);
366}
367