1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* |
3 | * Copyright(c) 2016 Intel Corporation. |
4 | */ |
5 | |
6 | #include <linux/slab.h> |
7 | #include <linux/sched.h> |
8 | #include <linux/rculist.h> |
9 | #include <rdma/rdma_vt.h> |
10 | #include <rdma/rdmavt_qp.h> |
11 | |
12 | #include "mcast.h" |
13 | |
14 | /** |
15 | * rvt_driver_mcast_init - init resources for multicast |
16 | * @rdi: rvt dev struct |
17 | * |
18 | * This is per device that registers with rdmavt |
19 | */ |
20 | void rvt_driver_mcast_init(struct rvt_dev_info *rdi) |
21 | { |
22 | /* |
23 | * Anything that needs setup for multicast on a per driver or per rdi |
24 | * basis should be done in here. |
25 | */ |
26 | spin_lock_init(&rdi->n_mcast_grps_lock); |
27 | } |
28 | |
29 | /** |
30 | * rvt_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct |
31 | * @qp: the QP to link |
32 | */ |
33 | static struct rvt_mcast_qp *rvt_mcast_qp_alloc(struct rvt_qp *qp) |
34 | { |
35 | struct rvt_mcast_qp *mqp; |
36 | |
37 | mqp = kmalloc(size: sizeof(*mqp), GFP_KERNEL); |
38 | if (!mqp) |
39 | goto bail; |
40 | |
41 | mqp->qp = qp; |
42 | rvt_get_qp(qp); |
43 | |
44 | bail: |
45 | return mqp; |
46 | } |
47 | |
48 | static void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp) |
49 | { |
50 | struct rvt_qp *qp = mqp->qp; |
51 | |
52 | /* Notify hfi1_destroy_qp() if it is waiting. */ |
53 | rvt_put_qp(qp); |
54 | |
55 | kfree(objp: mqp); |
56 | } |
57 | |
58 | /** |
59 | * rvt_mcast_alloc - allocate the multicast GID structure |
60 | * @mgid: the multicast GID |
61 | * @lid: the muilticast LID (host order) |
62 | * |
63 | * A list of QPs will be attached to this structure. |
64 | */ |
65 | static struct rvt_mcast *rvt_mcast_alloc(union ib_gid *mgid, u16 lid) |
66 | { |
67 | struct rvt_mcast *mcast; |
68 | |
69 | mcast = kzalloc(size: sizeof(*mcast), GFP_KERNEL); |
70 | if (!mcast) |
71 | goto bail; |
72 | |
73 | mcast->mcast_addr.mgid = *mgid; |
74 | mcast->mcast_addr.lid = lid; |
75 | |
76 | INIT_LIST_HEAD(list: &mcast->qp_list); |
77 | init_waitqueue_head(&mcast->wait); |
78 | atomic_set(v: &mcast->refcount, i: 0); |
79 | |
80 | bail: |
81 | return mcast; |
82 | } |
83 | |
84 | static void rvt_mcast_free(struct rvt_mcast *mcast) |
85 | { |
86 | struct rvt_mcast_qp *p, *tmp; |
87 | |
88 | list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) |
89 | rvt_mcast_qp_free(mqp: p); |
90 | |
91 | kfree(objp: mcast); |
92 | } |
93 | |
94 | /** |
95 | * rvt_mcast_find - search the global table for the given multicast GID/LID |
96 | * NOTE: It is valid to have 1 MLID with multiple MGIDs. It is not valid |
97 | * to have 1 MGID with multiple MLIDs. |
98 | * @ibp: the IB port structure |
99 | * @mgid: the multicast GID to search for |
100 | * @lid: the multicast LID portion of the multicast address (host order) |
101 | * |
102 | * The caller is responsible for decrementing the reference count if found. |
103 | * |
104 | * Return: NULL if not found. |
105 | */ |
106 | struct rvt_mcast *rvt_mcast_find(struct rvt_ibport *ibp, union ib_gid *mgid, |
107 | u16 lid) |
108 | { |
109 | struct rb_node *n; |
110 | unsigned long flags; |
111 | struct rvt_mcast *found = NULL; |
112 | |
113 | spin_lock_irqsave(&ibp->lock, flags); |
114 | n = ibp->mcast_tree.rb_node; |
115 | while (n) { |
116 | int ret; |
117 | struct rvt_mcast *mcast; |
118 | |
119 | mcast = rb_entry(n, struct rvt_mcast, rb_node); |
120 | |
121 | ret = memcmp(p: mgid->raw, q: mcast->mcast_addr.mgid.raw, |
122 | size: sizeof(*mgid)); |
123 | if (ret < 0) { |
124 | n = n->rb_left; |
125 | } else if (ret > 0) { |
126 | n = n->rb_right; |
127 | } else { |
128 | /* MGID/MLID must match */ |
129 | if (mcast->mcast_addr.lid == lid) { |
130 | atomic_inc(v: &mcast->refcount); |
131 | found = mcast; |
132 | } |
133 | break; |
134 | } |
135 | } |
136 | spin_unlock_irqrestore(lock: &ibp->lock, flags); |
137 | return found; |
138 | } |
139 | EXPORT_SYMBOL(rvt_mcast_find); |
140 | |
141 | /* |
142 | * rvt_mcast_add - insert mcast GID into table and attach QP struct |
143 | * @mcast: the mcast GID table |
144 | * @mqp: the QP to attach |
145 | * |
146 | * Return: zero if both were added. Return EEXIST if the GID was already in |
147 | * the table but the QP was added. Return ESRCH if the QP was already |
148 | * attached and neither structure was added. Return EINVAL if the MGID was |
149 | * found, but the MLID did NOT match. |
150 | */ |
151 | static int rvt_mcast_add(struct rvt_dev_info *rdi, struct rvt_ibport *ibp, |
152 | struct rvt_mcast *mcast, struct rvt_mcast_qp *mqp) |
153 | { |
154 | struct rb_node **n = &ibp->mcast_tree.rb_node; |
155 | struct rb_node *pn = NULL; |
156 | int ret; |
157 | |
158 | spin_lock_irq(lock: &ibp->lock); |
159 | |
160 | while (*n) { |
161 | struct rvt_mcast *tmcast; |
162 | struct rvt_mcast_qp *p; |
163 | |
164 | pn = *n; |
165 | tmcast = rb_entry(pn, struct rvt_mcast, rb_node); |
166 | |
167 | ret = memcmp(p: mcast->mcast_addr.mgid.raw, |
168 | q: tmcast->mcast_addr.mgid.raw, |
169 | size: sizeof(mcast->mcast_addr.mgid)); |
170 | if (ret < 0) { |
171 | n = &pn->rb_left; |
172 | continue; |
173 | } |
174 | if (ret > 0) { |
175 | n = &pn->rb_right; |
176 | continue; |
177 | } |
178 | |
179 | if (tmcast->mcast_addr.lid != mcast->mcast_addr.lid) { |
180 | ret = EINVAL; |
181 | goto bail; |
182 | } |
183 | |
184 | /* Search the QP list to see if this is already there. */ |
185 | list_for_each_entry_rcu(p, &tmcast->qp_list, list) { |
186 | if (p->qp == mqp->qp) { |
187 | ret = ESRCH; |
188 | goto bail; |
189 | } |
190 | } |
191 | if (tmcast->n_attached == |
192 | rdi->dparms.props.max_mcast_qp_attach) { |
193 | ret = ENOMEM; |
194 | goto bail; |
195 | } |
196 | |
197 | tmcast->n_attached++; |
198 | |
199 | list_add_tail_rcu(new: &mqp->list, head: &tmcast->qp_list); |
200 | ret = EEXIST; |
201 | goto bail; |
202 | } |
203 | |
204 | spin_lock(lock: &rdi->n_mcast_grps_lock); |
205 | if (rdi->n_mcast_grps_allocated == rdi->dparms.props.max_mcast_grp) { |
206 | spin_unlock(lock: &rdi->n_mcast_grps_lock); |
207 | ret = ENOMEM; |
208 | goto bail; |
209 | } |
210 | |
211 | rdi->n_mcast_grps_allocated++; |
212 | spin_unlock(lock: &rdi->n_mcast_grps_lock); |
213 | |
214 | mcast->n_attached++; |
215 | |
216 | list_add_tail_rcu(new: &mqp->list, head: &mcast->qp_list); |
217 | |
218 | atomic_inc(v: &mcast->refcount); |
219 | rb_link_node(node: &mcast->rb_node, parent: pn, rb_link: n); |
220 | rb_insert_color(&mcast->rb_node, &ibp->mcast_tree); |
221 | |
222 | ret = 0; |
223 | |
224 | bail: |
225 | spin_unlock_irq(lock: &ibp->lock); |
226 | |
227 | return ret; |
228 | } |
229 | |
230 | /** |
231 | * rvt_attach_mcast - attach a qp to a multicast group |
232 | * @ibqp: Infiniband qp |
233 | * @gid: multicast guid |
234 | * @lid: multicast lid |
235 | * |
236 | * Return: 0 on success |
237 | */ |
238 | int rvt_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) |
239 | { |
240 | struct rvt_qp *qp = ibqp_to_rvtqp(ibqp); |
241 | struct rvt_dev_info *rdi = ib_to_rvt(ibdev: ibqp->device); |
242 | struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1]; |
243 | struct rvt_mcast *mcast; |
244 | struct rvt_mcast_qp *mqp; |
245 | int ret = -ENOMEM; |
246 | |
247 | if (ibqp->qp_num <= 1 || qp->state == IB_QPS_RESET) |
248 | return -EINVAL; |
249 | |
250 | /* |
251 | * Allocate data structures since its better to do this outside of |
252 | * spin locks and it will most likely be needed. |
253 | */ |
254 | mcast = rvt_mcast_alloc(mgid: gid, lid); |
255 | if (!mcast) |
256 | return -ENOMEM; |
257 | |
258 | mqp = rvt_mcast_qp_alloc(qp); |
259 | if (!mqp) |
260 | goto bail_mcast; |
261 | |
262 | switch (rvt_mcast_add(rdi, ibp, mcast, mqp)) { |
263 | case ESRCH: |
264 | /* Neither was used: OK to attach the same QP twice. */ |
265 | ret = 0; |
266 | goto bail_mqp; |
267 | case EEXIST: /* The mcast wasn't used */ |
268 | ret = 0; |
269 | goto bail_mcast; |
270 | case ENOMEM: |
271 | /* Exceeded the maximum number of mcast groups. */ |
272 | ret = -ENOMEM; |
273 | goto bail_mqp; |
274 | case EINVAL: |
275 | /* Invalid MGID/MLID pair */ |
276 | ret = -EINVAL; |
277 | goto bail_mqp; |
278 | default: |
279 | break; |
280 | } |
281 | |
282 | return 0; |
283 | |
284 | bail_mqp: |
285 | rvt_mcast_qp_free(mqp); |
286 | |
287 | bail_mcast: |
288 | rvt_mcast_free(mcast); |
289 | |
290 | return ret; |
291 | } |
292 | |
293 | /** |
294 | * rvt_detach_mcast - remove a qp from a multicast group |
295 | * @ibqp: Infiniband qp |
296 | * @gid: multicast guid |
297 | * @lid: multicast lid |
298 | * |
299 | * Return: 0 on success |
300 | */ |
301 | int rvt_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) |
302 | { |
303 | struct rvt_qp *qp = ibqp_to_rvtqp(ibqp); |
304 | struct rvt_dev_info *rdi = ib_to_rvt(ibdev: ibqp->device); |
305 | struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1]; |
306 | struct rvt_mcast *mcast = NULL; |
307 | struct rvt_mcast_qp *p, *tmp, *delp = NULL; |
308 | struct rb_node *n; |
309 | int last = 0; |
310 | int ret = 0; |
311 | |
312 | if (ibqp->qp_num <= 1) |
313 | return -EINVAL; |
314 | |
315 | spin_lock_irq(lock: &ibp->lock); |
316 | |
317 | /* Find the GID in the mcast table. */ |
318 | n = ibp->mcast_tree.rb_node; |
319 | while (1) { |
320 | if (!n) { |
321 | spin_unlock_irq(lock: &ibp->lock); |
322 | return -EINVAL; |
323 | } |
324 | |
325 | mcast = rb_entry(n, struct rvt_mcast, rb_node); |
326 | ret = memcmp(p: gid->raw, q: mcast->mcast_addr.mgid.raw, |
327 | size: sizeof(*gid)); |
328 | if (ret < 0) { |
329 | n = n->rb_left; |
330 | } else if (ret > 0) { |
331 | n = n->rb_right; |
332 | } else { |
333 | /* MGID/MLID must match */ |
334 | if (mcast->mcast_addr.lid != lid) { |
335 | spin_unlock_irq(lock: &ibp->lock); |
336 | return -EINVAL; |
337 | } |
338 | break; |
339 | } |
340 | } |
341 | |
342 | /* Search the QP list. */ |
343 | list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) { |
344 | if (p->qp != qp) |
345 | continue; |
346 | /* |
347 | * We found it, so remove it, but don't poison the forward |
348 | * link until we are sure there are no list walkers. |
349 | */ |
350 | list_del_rcu(entry: &p->list); |
351 | mcast->n_attached--; |
352 | delp = p; |
353 | |
354 | /* If this was the last attached QP, remove the GID too. */ |
355 | if (list_empty(head: &mcast->qp_list)) { |
356 | rb_erase(&mcast->rb_node, &ibp->mcast_tree); |
357 | last = 1; |
358 | } |
359 | break; |
360 | } |
361 | |
362 | spin_unlock_irq(lock: &ibp->lock); |
363 | /* QP not attached */ |
364 | if (!delp) |
365 | return -EINVAL; |
366 | |
367 | /* |
368 | * Wait for any list walkers to finish before freeing the |
369 | * list element. |
370 | */ |
371 | wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1); |
372 | rvt_mcast_qp_free(mqp: delp); |
373 | |
374 | if (last) { |
375 | atomic_dec(v: &mcast->refcount); |
376 | wait_event(mcast->wait, !atomic_read(&mcast->refcount)); |
377 | rvt_mcast_free(mcast); |
378 | spin_lock_irq(lock: &rdi->n_mcast_grps_lock); |
379 | rdi->n_mcast_grps_allocated--; |
380 | spin_unlock_irq(lock: &rdi->n_mcast_grps_lock); |
381 | } |
382 | |
383 | return 0; |
384 | } |
385 | |
386 | /** |
387 | * rvt_mcast_tree_empty - determine if any qps are attached to any mcast group |
388 | * @rdi: rvt dev struct |
389 | * |
390 | * Return: in use count |
391 | */ |
392 | int rvt_mcast_tree_empty(struct rvt_dev_info *rdi) |
393 | { |
394 | int i; |
395 | int in_use = 0; |
396 | |
397 | for (i = 0; i < rdi->dparms.nports; i++) |
398 | if (rdi->ports[i]->mcast_tree.rb_node) |
399 | in_use++; |
400 | return in_use; |
401 | } |
402 | |