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