Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/shaggy...
[linux-2.6] / drivers / infiniband / hw / mthca / mthca_mcg.c
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 }