]>
Commit | Line | Data |
---|---|---|
6522108f | 1 | /* |
87427da5 | 2 | * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. |
6522108f BS |
3 | * Copyright (c) 2005, 2006 PathScale, Inc. 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 | ||
82524746 | 34 | #include <linux/rculist.h> |
d43c36dc | 35 | #include <linux/sched.h> |
6522108f BS |
36 | |
37 | #include "ipath_verbs.h" | |
38 | ||
39 | /* | |
40 | * Global table of GID to attached QPs. | |
41 | * The table is global to all ipath devices since a send from one QP/device | |
42 | * needs to be locally routed to any locally attached QPs on the same | |
43 | * or different device. | |
44 | */ | |
45 | static struct rb_root mcast_tree; | |
46 | static DEFINE_SPINLOCK(mcast_lock); | |
47 | ||
48 | /** | |
49 | * ipath_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct | |
50 | * @qp: the QP to link | |
51 | */ | |
52 | static struct ipath_mcast_qp *ipath_mcast_qp_alloc(struct ipath_qp *qp) | |
53 | { | |
54 | struct ipath_mcast_qp *mqp; | |
55 | ||
56 | mqp = kmalloc(sizeof *mqp, GFP_KERNEL); | |
57 | if (!mqp) | |
58 | goto bail; | |
59 | ||
60 | mqp->qp = qp; | |
61 | atomic_inc(&qp->refcount); | |
62 | ||
63 | bail: | |
64 | return mqp; | |
65 | } | |
66 | ||
67 | static void ipath_mcast_qp_free(struct ipath_mcast_qp *mqp) | |
68 | { | |
69 | struct ipath_qp *qp = mqp->qp; | |
70 | ||
71 | /* Notify ipath_destroy_qp() if it is waiting. */ | |
72 | if (atomic_dec_and_test(&qp->refcount)) | |
73 | wake_up(&qp->wait); | |
74 | ||
75 | kfree(mqp); | |
76 | } | |
77 | ||
78 | /** | |
79 | * ipath_mcast_alloc - allocate the multicast GID structure | |
80 | * @mgid: the multicast GID | |
81 | * | |
82 | * A list of QPs will be attached to this structure. | |
83 | */ | |
84 | static struct ipath_mcast *ipath_mcast_alloc(union ib_gid *mgid) | |
85 | { | |
86 | struct ipath_mcast *mcast; | |
87 | ||
88 | mcast = kmalloc(sizeof *mcast, GFP_KERNEL); | |
89 | if (!mcast) | |
90 | goto bail; | |
91 | ||
92 | mcast->mgid = *mgid; | |
93 | INIT_LIST_HEAD(&mcast->qp_list); | |
94 | init_waitqueue_head(&mcast->wait); | |
95 | atomic_set(&mcast->refcount, 0); | |
fe62546a | 96 | mcast->n_attached = 0; |
6522108f BS |
97 | |
98 | bail: | |
99 | return mcast; | |
100 | } | |
101 | ||
102 | static void ipath_mcast_free(struct ipath_mcast *mcast) | |
103 | { | |
104 | struct ipath_mcast_qp *p, *tmp; | |
105 | ||
106 | list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) | |
107 | ipath_mcast_qp_free(p); | |
108 | ||
109 | kfree(mcast); | |
110 | } | |
111 | ||
112 | /** | |
113 | * ipath_mcast_find - search the global table for the given multicast GID | |
114 | * @mgid: the multicast GID to search for | |
115 | * | |
116 | * Returns NULL if not found. | |
117 | * | |
118 | * The caller is responsible for decrementing the reference count if found. | |
119 | */ | |
120 | struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid) | |
121 | { | |
122 | struct rb_node *n; | |
123 | unsigned long flags; | |
124 | struct ipath_mcast *mcast; | |
125 | ||
126 | spin_lock_irqsave(&mcast_lock, flags); | |
127 | n = mcast_tree.rb_node; | |
128 | while (n) { | |
129 | int ret; | |
130 | ||
131 | mcast = rb_entry(n, struct ipath_mcast, rb_node); | |
132 | ||
133 | ret = memcmp(mgid->raw, mcast->mgid.raw, | |
134 | sizeof(union ib_gid)); | |
135 | if (ret < 0) | |
136 | n = n->rb_left; | |
137 | else if (ret > 0) | |
138 | n = n->rb_right; | |
139 | else { | |
140 | atomic_inc(&mcast->refcount); | |
141 | spin_unlock_irqrestore(&mcast_lock, flags); | |
142 | goto bail; | |
143 | } | |
144 | } | |
145 | spin_unlock_irqrestore(&mcast_lock, flags); | |
146 | ||
147 | mcast = NULL; | |
148 | ||
149 | bail: | |
150 | return mcast; | |
151 | } | |
152 | ||
153 | /** | |
154 | * ipath_mcast_add - insert mcast GID into table and attach QP struct | |
155 | * @mcast: the mcast GID table | |
156 | * @mqp: the QP to attach | |
157 | * | |
158 | * Return zero if both were added. Return EEXIST if the GID was already in | |
159 | * the table but the QP was added. Return ESRCH if the QP was already | |
160 | * attached and neither structure was added. | |
161 | */ | |
fe62546a BS |
162 | static int ipath_mcast_add(struct ipath_ibdev *dev, |
163 | struct ipath_mcast *mcast, | |
6522108f BS |
164 | struct ipath_mcast_qp *mqp) |
165 | { | |
166 | struct rb_node **n = &mcast_tree.rb_node; | |
167 | struct rb_node *pn = NULL; | |
6522108f BS |
168 | int ret; |
169 | ||
8b8c8bca | 170 | spin_lock_irq(&mcast_lock); |
6522108f BS |
171 | |
172 | while (*n) { | |
173 | struct ipath_mcast *tmcast; | |
174 | struct ipath_mcast_qp *p; | |
175 | ||
176 | pn = *n; | |
177 | tmcast = rb_entry(pn, struct ipath_mcast, rb_node); | |
178 | ||
179 | ret = memcmp(mcast->mgid.raw, tmcast->mgid.raw, | |
180 | sizeof(union ib_gid)); | |
181 | if (ret < 0) { | |
182 | n = &pn->rb_left; | |
183 | continue; | |
184 | } | |
185 | if (ret > 0) { | |
186 | n = &pn->rb_right; | |
187 | continue; | |
188 | } | |
189 | ||
190 | /* Search the QP list to see if this is already there. */ | |
191 | list_for_each_entry_rcu(p, &tmcast->qp_list, list) { | |
192 | if (p->qp == mqp->qp) { | |
6522108f BS |
193 | ret = ESRCH; |
194 | goto bail; | |
195 | } | |
196 | } | |
fe62546a BS |
197 | if (tmcast->n_attached == ib_ipath_max_mcast_qp_attached) { |
198 | ret = ENOMEM; | |
199 | goto bail; | |
200 | } | |
201 | ||
202 | tmcast->n_attached++; | |
203 | ||
6522108f | 204 | list_add_tail_rcu(&mqp->list, &tmcast->qp_list); |
6522108f BS |
205 | ret = EEXIST; |
206 | goto bail; | |
207 | } | |
208 | ||
c27fef26 | 209 | spin_lock(&dev->n_mcast_grps_lock); |
fe62546a | 210 | if (dev->n_mcast_grps_allocated == ib_ipath_max_mcast_grps) { |
c27fef26 | 211 | spin_unlock(&dev->n_mcast_grps_lock); |
fe62546a BS |
212 | ret = ENOMEM; |
213 | goto bail; | |
214 | } | |
215 | ||
216 | dev->n_mcast_grps_allocated++; | |
c27fef26 | 217 | spin_unlock(&dev->n_mcast_grps_lock); |
fe62546a | 218 | |
092260b8 BS |
219 | mcast->n_attached++; |
220 | ||
6522108f BS |
221 | list_add_tail_rcu(&mqp->list, &mcast->qp_list); |
222 | ||
223 | atomic_inc(&mcast->refcount); | |
224 | rb_link_node(&mcast->rb_node, pn, n); | |
225 | rb_insert_color(&mcast->rb_node, &mcast_tree); | |
226 | ||
6522108f BS |
227 | ret = 0; |
228 | ||
229 | bail: | |
8b8c8bca | 230 | spin_unlock_irq(&mcast_lock); |
fe62546a | 231 | |
6522108f BS |
232 | return ret; |
233 | } | |
234 | ||
235 | int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) | |
236 | { | |
237 | struct ipath_qp *qp = to_iqp(ibqp); | |
fe62546a | 238 | struct ipath_ibdev *dev = to_idev(ibqp->device); |
6522108f BS |
239 | struct ipath_mcast *mcast; |
240 | struct ipath_mcast_qp *mqp; | |
241 | int ret; | |
242 | ||
243 | /* | |
244 | * Allocate data structures since its better to do this outside of | |
245 | * spin locks and it will most likely be needed. | |
246 | */ | |
247 | mcast = ipath_mcast_alloc(gid); | |
248 | if (mcast == NULL) { | |
249 | ret = -ENOMEM; | |
250 | goto bail; | |
251 | } | |
252 | mqp = ipath_mcast_qp_alloc(qp); | |
253 | if (mqp == NULL) { | |
254 | ipath_mcast_free(mcast); | |
255 | ret = -ENOMEM; | |
256 | goto bail; | |
257 | } | |
fe62546a | 258 | switch (ipath_mcast_add(dev, mcast, mqp)) { |
6522108f BS |
259 | case ESRCH: |
260 | /* Neither was used: can't attach the same QP twice. */ | |
261 | ipath_mcast_qp_free(mqp); | |
262 | ipath_mcast_free(mcast); | |
263 | ret = -EINVAL; | |
264 | goto bail; | |
265 | case EEXIST: /* The mcast wasn't used */ | |
266 | ipath_mcast_free(mcast); | |
267 | break; | |
fe62546a BS |
268 | case ENOMEM: |
269 | /* Exceeded the maximum number of mcast groups. */ | |
270 | ipath_mcast_qp_free(mqp); | |
271 | ipath_mcast_free(mcast); | |
272 | ret = -ENOMEM; | |
273 | goto bail; | |
6522108f BS |
274 | default: |
275 | break; | |
276 | } | |
277 | ||
278 | ret = 0; | |
279 | ||
280 | bail: | |
281 | return ret; | |
282 | } | |
283 | ||
284 | int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) | |
285 | { | |
286 | struct ipath_qp *qp = to_iqp(ibqp); | |
fe62546a | 287 | struct ipath_ibdev *dev = to_idev(ibqp->device); |
6522108f BS |
288 | struct ipath_mcast *mcast = NULL; |
289 | struct ipath_mcast_qp *p, *tmp; | |
290 | struct rb_node *n; | |
6522108f BS |
291 | int last = 0; |
292 | int ret; | |
293 | ||
8b8c8bca | 294 | spin_lock_irq(&mcast_lock); |
6522108f BS |
295 | |
296 | /* Find the GID in the mcast table. */ | |
297 | n = mcast_tree.rb_node; | |
298 | while (1) { | |
299 | if (n == NULL) { | |
8b8c8bca | 300 | spin_unlock_irq(&mcast_lock); |
9edbd990 | 301 | ret = -EINVAL; |
6522108f BS |
302 | goto bail; |
303 | } | |
304 | ||
305 | mcast = rb_entry(n, struct ipath_mcast, rb_node); | |
306 | ret = memcmp(gid->raw, mcast->mgid.raw, | |
307 | sizeof(union ib_gid)); | |
308 | if (ret < 0) | |
309 | n = n->rb_left; | |
310 | else if (ret > 0) | |
311 | n = n->rb_right; | |
312 | else | |
313 | break; | |
314 | } | |
315 | ||
316 | /* Search the QP list. */ | |
317 | list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) { | |
318 | if (p->qp != qp) | |
319 | continue; | |
320 | /* | |
321 | * We found it, so remove it, but don't poison the forward | |
322 | * link until we are sure there are no list walkers. | |
323 | */ | |
324 | list_del_rcu(&p->list); | |
fe62546a | 325 | mcast->n_attached--; |
6522108f BS |
326 | |
327 | /* If this was the last attached QP, remove the GID too. */ | |
328 | if (list_empty(&mcast->qp_list)) { | |
329 | rb_erase(&mcast->rb_node, &mcast_tree); | |
330 | last = 1; | |
331 | } | |
332 | break; | |
333 | } | |
334 | ||
8b8c8bca | 335 | spin_unlock_irq(&mcast_lock); |
6522108f BS |
336 | |
337 | if (p) { | |
338 | /* | |
339 | * Wait for any list walkers to finish before freeing the | |
340 | * list element. | |
341 | */ | |
342 | wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1); | |
343 | ipath_mcast_qp_free(p); | |
344 | } | |
345 | if (last) { | |
346 | atomic_dec(&mcast->refcount); | |
347 | wait_event(mcast->wait, !atomic_read(&mcast->refcount)); | |
348 | ipath_mcast_free(mcast); | |
8b8c8bca | 349 | spin_lock_irq(&dev->n_mcast_grps_lock); |
fe62546a | 350 | dev->n_mcast_grps_allocated--; |
8b8c8bca | 351 | spin_unlock_irq(&dev->n_mcast_grps_lock); |
6522108f BS |
352 | } |
353 | ||
354 | ret = 0; | |
355 | ||
356 | bail: | |
357 | return ret; | |
358 | } | |
359 | ||
360 | int ipath_mcast_tree_empty(void) | |
361 | { | |
362 | return mcast_tree.rb_node == NULL; | |
363 | } |