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 */
20void 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 */
33static 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
44bail:
45 return mqp;
46}
47
48static 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 */
65static 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
80bail:
81 return mcast;
82}
83
84static 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 */
106struct 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}
139EXPORT_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 */
151static 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
224bail:
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 */
238int 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
284bail_mqp:
285 rvt_mcast_qp_free(mqp);
286
287bail_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 */
301int 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 */
392int 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

source code of linux/drivers/infiniband/sw/rdmavt/mcast.c