1*d164bf64SCai Huoqing // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
29fa25171SDennis Dalessandro /*
3fe314195SDennis Dalessandro  * Copyright(c) 2016 Intel Corporation.
49fa25171SDennis Dalessandro  */
59fa25171SDennis Dalessandro 
64e74080bSDennis Dalessandro #include <linux/slab.h>
74e74080bSDennis Dalessandro #include <linux/sched.h>
84e74080bSDennis Dalessandro #include <linux/rculist.h>
94e74080bSDennis Dalessandro #include <rdma/rdma_vt.h>
104e74080bSDennis Dalessandro #include <rdma/rdmavt_qp.h>
114e74080bSDennis Dalessandro 
129fa25171SDennis Dalessandro #include "mcast.h"
139fa25171SDennis Dalessandro 
1490793f71SDennis Dalessandro /**
152988ca08SMauro Carvalho Chehab  * rvt_driver_mcast_init - init resources for multicast
1690793f71SDennis Dalessandro  * @rdi: rvt dev struct
1790793f71SDennis Dalessandro  *
1890793f71SDennis Dalessandro  * This is per device that registers with rdmavt
1990793f71SDennis Dalessandro  */
rvt_driver_mcast_init(struct rvt_dev_info * rdi)204e74080bSDennis Dalessandro void rvt_driver_mcast_init(struct rvt_dev_info *rdi)
214e74080bSDennis Dalessandro {
224e74080bSDennis Dalessandro 	/*
234e74080bSDennis Dalessandro 	 * Anything that needs setup for multicast on a per driver or per rdi
244e74080bSDennis Dalessandro 	 * basis should be done in here.
254e74080bSDennis Dalessandro 	 */
264e74080bSDennis Dalessandro 	spin_lock_init(&rdi->n_mcast_grps_lock);
274e74080bSDennis Dalessandro }
284e74080bSDennis Dalessandro 
294e74080bSDennis Dalessandro /**
302988ca08SMauro Carvalho Chehab  * rvt_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
314e74080bSDennis Dalessandro  * @qp: the QP to link
324e74080bSDennis Dalessandro  */
rvt_mcast_qp_alloc(struct rvt_qp * qp)334e74080bSDennis Dalessandro static struct rvt_mcast_qp *rvt_mcast_qp_alloc(struct rvt_qp *qp)
344e74080bSDennis Dalessandro {
354e74080bSDennis Dalessandro 	struct rvt_mcast_qp *mqp;
364e74080bSDennis Dalessandro 
374e74080bSDennis Dalessandro 	mqp = kmalloc(sizeof(*mqp), GFP_KERNEL);
384e74080bSDennis Dalessandro 	if (!mqp)
394e74080bSDennis Dalessandro 		goto bail;
404e74080bSDennis Dalessandro 
414e74080bSDennis Dalessandro 	mqp->qp = qp;
42b44980f8SSebastian Sanchez 	rvt_get_qp(qp);
434e74080bSDennis Dalessandro 
444e74080bSDennis Dalessandro bail:
454e74080bSDennis Dalessandro 	return mqp;
464e74080bSDennis Dalessandro }
474e74080bSDennis Dalessandro 
rvt_mcast_qp_free(struct rvt_mcast_qp * mqp)484e74080bSDennis Dalessandro static void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp)
494e74080bSDennis Dalessandro {
504e74080bSDennis Dalessandro 	struct rvt_qp *qp = mqp->qp;
514e74080bSDennis Dalessandro 
524e74080bSDennis Dalessandro 	/* Notify hfi1_destroy_qp() if it is waiting. */
53b44980f8SSebastian Sanchez 	rvt_put_qp(qp);
544e74080bSDennis Dalessandro 
554e74080bSDennis Dalessandro 	kfree(mqp);
564e74080bSDennis Dalessandro }
574e74080bSDennis Dalessandro 
584e74080bSDennis Dalessandro /**
592988ca08SMauro Carvalho Chehab  * rvt_mcast_alloc - allocate the multicast GID structure
604e74080bSDennis Dalessandro  * @mgid: the multicast GID
61aad9ff97SMichael J. Ruhl  * @lid: the muilticast LID (host order)
624e74080bSDennis Dalessandro  *
634e74080bSDennis Dalessandro  * A list of QPs will be attached to this structure.
644e74080bSDennis Dalessandro  */
rvt_mcast_alloc(union ib_gid * mgid,u16 lid)65aad9ff97SMichael J. Ruhl static struct rvt_mcast *rvt_mcast_alloc(union ib_gid *mgid, u16 lid)
664e74080bSDennis Dalessandro {
674e74080bSDennis Dalessandro 	struct rvt_mcast *mcast;
684e74080bSDennis Dalessandro 
694e74080bSDennis Dalessandro 	mcast = kzalloc(sizeof(*mcast), GFP_KERNEL);
704e74080bSDennis Dalessandro 	if (!mcast)
714e74080bSDennis Dalessandro 		goto bail;
724e74080bSDennis Dalessandro 
73aad9ff97SMichael J. Ruhl 	mcast->mcast_addr.mgid = *mgid;
74aad9ff97SMichael J. Ruhl 	mcast->mcast_addr.lid = lid;
75aad9ff97SMichael J. Ruhl 
764e74080bSDennis Dalessandro 	INIT_LIST_HEAD(&mcast->qp_list);
774e74080bSDennis Dalessandro 	init_waitqueue_head(&mcast->wait);
784e74080bSDennis Dalessandro 	atomic_set(&mcast->refcount, 0);
794e74080bSDennis Dalessandro 
804e74080bSDennis Dalessandro bail:
814e74080bSDennis Dalessandro 	return mcast;
824e74080bSDennis Dalessandro }
834e74080bSDennis Dalessandro 
rvt_mcast_free(struct rvt_mcast * mcast)844e74080bSDennis Dalessandro static void rvt_mcast_free(struct rvt_mcast *mcast)
854e74080bSDennis Dalessandro {
864e74080bSDennis Dalessandro 	struct rvt_mcast_qp *p, *tmp;
874e74080bSDennis Dalessandro 
884e74080bSDennis Dalessandro 	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
894e74080bSDennis Dalessandro 		rvt_mcast_qp_free(p);
904e74080bSDennis Dalessandro 
914e74080bSDennis Dalessandro 	kfree(mcast);
924e74080bSDennis Dalessandro }
934e74080bSDennis Dalessandro 
944e74080bSDennis Dalessandro /**
95aad9ff97SMichael J. Ruhl  * rvt_mcast_find - search the global table for the given multicast GID/LID
96aad9ff97SMichael J. Ruhl  * NOTE: It is valid to have 1 MLID with multiple MGIDs.  It is not valid
97aad9ff97SMichael J. Ruhl  * to have 1 MGID with multiple MLIDs.
984e74080bSDennis Dalessandro  * @ibp: the IB port structure
994e74080bSDennis Dalessandro  * @mgid: the multicast GID to search for
100aad9ff97SMichael J. Ruhl  * @lid: the multicast LID portion of the multicast address (host order)
1014e74080bSDennis Dalessandro  *
1024e74080bSDennis Dalessandro  * The caller is responsible for decrementing the reference count if found.
10390793f71SDennis Dalessandro  *
10490793f71SDennis Dalessandro  * Return: NULL if not found.
1054e74080bSDennis Dalessandro  */
rvt_mcast_find(struct rvt_ibport * ibp,union ib_gid * mgid,u16 lid)106aad9ff97SMichael J. Ruhl struct rvt_mcast *rvt_mcast_find(struct rvt_ibport *ibp, union ib_gid *mgid,
107aad9ff97SMichael J. Ruhl 				 u16 lid)
1084e74080bSDennis Dalessandro {
1094e74080bSDennis Dalessandro 	struct rb_node *n;
1104e74080bSDennis Dalessandro 	unsigned long flags;
1114e74080bSDennis Dalessandro 	struct rvt_mcast *found = NULL;
1124e74080bSDennis Dalessandro 
1134e74080bSDennis Dalessandro 	spin_lock_irqsave(&ibp->lock, flags);
1144e74080bSDennis Dalessandro 	n = ibp->mcast_tree.rb_node;
1154e74080bSDennis Dalessandro 	while (n) {
1164e74080bSDennis Dalessandro 		int ret;
1174e74080bSDennis Dalessandro 		struct rvt_mcast *mcast;
1184e74080bSDennis Dalessandro 
1194e74080bSDennis Dalessandro 		mcast = rb_entry(n, struct rvt_mcast, rb_node);
1204e74080bSDennis Dalessandro 
121aad9ff97SMichael J. Ruhl 		ret = memcmp(mgid->raw, mcast->mcast_addr.mgid.raw,
122aad9ff97SMichael J. Ruhl 			     sizeof(*mgid));
1234e74080bSDennis Dalessandro 		if (ret < 0) {
1244e74080bSDennis Dalessandro 			n = n->rb_left;
1254e74080bSDennis Dalessandro 		} else if (ret > 0) {
1264e74080bSDennis Dalessandro 			n = n->rb_right;
1274e74080bSDennis Dalessandro 		} else {
128aad9ff97SMichael J. Ruhl 			/* MGID/MLID must match */
129aad9ff97SMichael J. Ruhl 			if (mcast->mcast_addr.lid == lid) {
1304e74080bSDennis Dalessandro 				atomic_inc(&mcast->refcount);
1314e74080bSDennis Dalessandro 				found = mcast;
132aad9ff97SMichael J. Ruhl 			}
1334e74080bSDennis Dalessandro 			break;
1344e74080bSDennis Dalessandro 		}
1354e74080bSDennis Dalessandro 	}
1364e74080bSDennis Dalessandro 	spin_unlock_irqrestore(&ibp->lock, flags);
1374e74080bSDennis Dalessandro 	return found;
1384e74080bSDennis Dalessandro }
1394e74080bSDennis Dalessandro EXPORT_SYMBOL(rvt_mcast_find);
1404e74080bSDennis Dalessandro 
14100d25ff6SLee Jones /*
1422988ca08SMauro Carvalho Chehab  * rvt_mcast_add - insert mcast GID into table and attach QP struct
1434e74080bSDennis Dalessandro  * @mcast: the mcast GID table
1444e74080bSDennis Dalessandro  * @mqp: the QP to attach
1454e74080bSDennis Dalessandro  *
14690793f71SDennis Dalessandro  * Return: zero if both were added.  Return EEXIST if the GID was already in
1474e74080bSDennis Dalessandro  * the table but the QP was added.  Return ESRCH if the QP was already
148aad9ff97SMichael J. Ruhl  * attached and neither structure was added. Return EINVAL if the MGID was
149aad9ff97SMichael J. Ruhl  * found, but the MLID did NOT match.
1504e74080bSDennis Dalessandro  */
rvt_mcast_add(struct rvt_dev_info * rdi,struct rvt_ibport * ibp,struct rvt_mcast * mcast,struct rvt_mcast_qp * mqp)1514e74080bSDennis Dalessandro static int rvt_mcast_add(struct rvt_dev_info *rdi, struct rvt_ibport *ibp,
1524e74080bSDennis Dalessandro 			 struct rvt_mcast *mcast, struct rvt_mcast_qp *mqp)
1534e74080bSDennis Dalessandro {
1544e74080bSDennis Dalessandro 	struct rb_node **n = &ibp->mcast_tree.rb_node;
1554e74080bSDennis Dalessandro 	struct rb_node *pn = NULL;
1564e74080bSDennis Dalessandro 	int ret;
1574e74080bSDennis Dalessandro 
1584e74080bSDennis Dalessandro 	spin_lock_irq(&ibp->lock);
1594e74080bSDennis Dalessandro 
1604e74080bSDennis Dalessandro 	while (*n) {
1614e74080bSDennis Dalessandro 		struct rvt_mcast *tmcast;
1624e74080bSDennis Dalessandro 		struct rvt_mcast_qp *p;
1634e74080bSDennis Dalessandro 
1644e74080bSDennis Dalessandro 		pn = *n;
1654e74080bSDennis Dalessandro 		tmcast = rb_entry(pn, struct rvt_mcast, rb_node);
1664e74080bSDennis Dalessandro 
167aad9ff97SMichael J. Ruhl 		ret = memcmp(mcast->mcast_addr.mgid.raw,
168aad9ff97SMichael J. Ruhl 			     tmcast->mcast_addr.mgid.raw,
169aad9ff97SMichael J. Ruhl 			     sizeof(mcast->mcast_addr.mgid));
1704e74080bSDennis Dalessandro 		if (ret < 0) {
1714e74080bSDennis Dalessandro 			n = &pn->rb_left;
1724e74080bSDennis Dalessandro 			continue;
1734e74080bSDennis Dalessandro 		}
1744e74080bSDennis Dalessandro 		if (ret > 0) {
1754e74080bSDennis Dalessandro 			n = &pn->rb_right;
1764e74080bSDennis Dalessandro 			continue;
1774e74080bSDennis Dalessandro 		}
1784e74080bSDennis Dalessandro 
179aad9ff97SMichael J. Ruhl 		if (tmcast->mcast_addr.lid != mcast->mcast_addr.lid) {
180aad9ff97SMichael J. Ruhl 			ret = EINVAL;
181aad9ff97SMichael J. Ruhl 			goto bail;
182aad9ff97SMichael J. Ruhl 		}
183aad9ff97SMichael J. Ruhl 
1844e74080bSDennis Dalessandro 		/* Search the QP list to see if this is already there. */
1854e74080bSDennis Dalessandro 		list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
1864e74080bSDennis Dalessandro 			if (p->qp == mqp->qp) {
1874e74080bSDennis Dalessandro 				ret = ESRCH;
1884e74080bSDennis Dalessandro 				goto bail;
1894e74080bSDennis Dalessandro 			}
1904e74080bSDennis Dalessandro 		}
1914e74080bSDennis Dalessandro 		if (tmcast->n_attached ==
1924e74080bSDennis Dalessandro 		    rdi->dparms.props.max_mcast_qp_attach) {
1934e74080bSDennis Dalessandro 			ret = ENOMEM;
1944e74080bSDennis Dalessandro 			goto bail;
1954e74080bSDennis Dalessandro 		}
1964e74080bSDennis Dalessandro 
1974e74080bSDennis Dalessandro 		tmcast->n_attached++;
1984e74080bSDennis Dalessandro 
1994e74080bSDennis Dalessandro 		list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
2004e74080bSDennis Dalessandro 		ret = EEXIST;
2014e74080bSDennis Dalessandro 		goto bail;
2024e74080bSDennis Dalessandro 	}
2034e74080bSDennis Dalessandro 
2044e74080bSDennis Dalessandro 	spin_lock(&rdi->n_mcast_grps_lock);
2054e74080bSDennis Dalessandro 	if (rdi->n_mcast_grps_allocated == rdi->dparms.props.max_mcast_grp) {
2064e74080bSDennis Dalessandro 		spin_unlock(&rdi->n_mcast_grps_lock);
2074e74080bSDennis Dalessandro 		ret = ENOMEM;
2084e74080bSDennis Dalessandro 		goto bail;
2094e74080bSDennis Dalessandro 	}
2104e74080bSDennis Dalessandro 
2114e74080bSDennis Dalessandro 	rdi->n_mcast_grps_allocated++;
2124e74080bSDennis Dalessandro 	spin_unlock(&rdi->n_mcast_grps_lock);
2134e74080bSDennis Dalessandro 
2144e74080bSDennis Dalessandro 	mcast->n_attached++;
2154e74080bSDennis Dalessandro 
2164e74080bSDennis Dalessandro 	list_add_tail_rcu(&mqp->list, &mcast->qp_list);
2174e74080bSDennis Dalessandro 
2184e74080bSDennis Dalessandro 	atomic_inc(&mcast->refcount);
2194e74080bSDennis Dalessandro 	rb_link_node(&mcast->rb_node, pn, n);
2204e74080bSDennis Dalessandro 	rb_insert_color(&mcast->rb_node, &ibp->mcast_tree);
2214e74080bSDennis Dalessandro 
2224e74080bSDennis Dalessandro 	ret = 0;
2234e74080bSDennis Dalessandro 
2244e74080bSDennis Dalessandro bail:
2254e74080bSDennis Dalessandro 	spin_unlock_irq(&ibp->lock);
2264e74080bSDennis Dalessandro 
2274e74080bSDennis Dalessandro 	return ret;
2284e74080bSDennis Dalessandro }
2294e74080bSDennis Dalessandro 
23090793f71SDennis Dalessandro /**
23190793f71SDennis Dalessandro  * rvt_attach_mcast - attach a qp to a multicast group
23290793f71SDennis Dalessandro  * @ibqp: Infiniband qp
2334f9a3018SRandy Dunlap  * @gid: multicast guid
23490793f71SDennis Dalessandro  * @lid: multicast lid
23590793f71SDennis Dalessandro  *
23690793f71SDennis Dalessandro  * Return: 0 on success
23790793f71SDennis Dalessandro  */
rvt_attach_mcast(struct ib_qp * ibqp,union ib_gid * gid,u16 lid)2389fa25171SDennis Dalessandro int rvt_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
2399fa25171SDennis Dalessandro {
2404e74080bSDennis Dalessandro 	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
2414e74080bSDennis Dalessandro 	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
2424e74080bSDennis Dalessandro 	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
2434e74080bSDennis Dalessandro 	struct rvt_mcast *mcast;
2444e74080bSDennis Dalessandro 	struct rvt_mcast_qp *mqp;
2454e74080bSDennis Dalessandro 	int ret = -ENOMEM;
2464e74080bSDennis Dalessandro 
2474e74080bSDennis Dalessandro 	if (ibqp->qp_num <= 1 || qp->state == IB_QPS_RESET)
2484e74080bSDennis Dalessandro 		return -EINVAL;
2494e74080bSDennis Dalessandro 
2504e74080bSDennis Dalessandro 	/*
2514e74080bSDennis Dalessandro 	 * Allocate data structures since its better to do this outside of
2524e74080bSDennis Dalessandro 	 * spin locks and it will most likely be needed.
2534e74080bSDennis Dalessandro 	 */
254aad9ff97SMichael J. Ruhl 	mcast = rvt_mcast_alloc(gid, lid);
2554e74080bSDennis Dalessandro 	if (!mcast)
2564e74080bSDennis Dalessandro 		return -ENOMEM;
2574e74080bSDennis Dalessandro 
2584e74080bSDennis Dalessandro 	mqp = rvt_mcast_qp_alloc(qp);
2594e74080bSDennis Dalessandro 	if (!mqp)
2604e74080bSDennis Dalessandro 		goto bail_mcast;
2614e74080bSDennis Dalessandro 
2624e74080bSDennis Dalessandro 	switch (rvt_mcast_add(rdi, ibp, mcast, mqp)) {
2634e74080bSDennis Dalessandro 	case ESRCH:
2644e74080bSDennis Dalessandro 		/* Neither was used: OK to attach the same QP twice. */
2654e74080bSDennis Dalessandro 		ret = 0;
2664e74080bSDennis Dalessandro 		goto bail_mqp;
2674e74080bSDennis Dalessandro 	case EEXIST: /* The mcast wasn't used */
2684e74080bSDennis Dalessandro 		ret = 0;
2694e74080bSDennis Dalessandro 		goto bail_mcast;
2704e74080bSDennis Dalessandro 	case ENOMEM:
2714e74080bSDennis Dalessandro 		/* Exceeded the maximum number of mcast groups. */
2724e74080bSDennis Dalessandro 		ret = -ENOMEM;
2734e74080bSDennis Dalessandro 		goto bail_mqp;
274aad9ff97SMichael J. Ruhl 	case EINVAL:
275aad9ff97SMichael J. Ruhl 		/* Invalid MGID/MLID pair */
276aad9ff97SMichael J. Ruhl 		ret = -EINVAL;
277aad9ff97SMichael J. Ruhl 		goto bail_mqp;
2784e74080bSDennis Dalessandro 	default:
2794e74080bSDennis Dalessandro 		break;
2804e74080bSDennis Dalessandro 	}
2814e74080bSDennis Dalessandro 
2824e74080bSDennis Dalessandro 	return 0;
2834e74080bSDennis Dalessandro 
2844e74080bSDennis Dalessandro bail_mqp:
2854e74080bSDennis Dalessandro 	rvt_mcast_qp_free(mqp);
2864e74080bSDennis Dalessandro 
2874e74080bSDennis Dalessandro bail_mcast:
2884e74080bSDennis Dalessandro 	rvt_mcast_free(mcast);
2894e74080bSDennis Dalessandro 
2904e74080bSDennis Dalessandro 	return ret;
2919fa25171SDennis Dalessandro }
2929fa25171SDennis Dalessandro 
29390793f71SDennis Dalessandro /**
29490793f71SDennis Dalessandro  * rvt_detach_mcast - remove a qp from a multicast group
29590793f71SDennis Dalessandro  * @ibqp: Infiniband qp
2964f9a3018SRandy Dunlap  * @gid: multicast guid
29790793f71SDennis Dalessandro  * @lid: multicast lid
29890793f71SDennis Dalessandro  *
29990793f71SDennis Dalessandro  * Return: 0 on success
30090793f71SDennis Dalessandro  */
rvt_detach_mcast(struct ib_qp * ibqp,union ib_gid * gid,u16 lid)3019fa25171SDennis Dalessandro int rvt_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
3029fa25171SDennis Dalessandro {
3034e74080bSDennis Dalessandro 	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
3044e74080bSDennis Dalessandro 	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
3054e74080bSDennis Dalessandro 	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
3064e74080bSDennis Dalessandro 	struct rvt_mcast *mcast = NULL;
3074e74080bSDennis Dalessandro 	struct rvt_mcast_qp *p, *tmp, *delp = NULL;
3084e74080bSDennis Dalessandro 	struct rb_node *n;
3094e74080bSDennis Dalessandro 	int last = 0;
3104e74080bSDennis Dalessandro 	int ret = 0;
3114e74080bSDennis Dalessandro 
312f9586abfSAlex Estrin 	if (ibqp->qp_num <= 1)
3134e74080bSDennis Dalessandro 		return -EINVAL;
3144e74080bSDennis Dalessandro 
3154e74080bSDennis Dalessandro 	spin_lock_irq(&ibp->lock);
3164e74080bSDennis Dalessandro 
3174e74080bSDennis Dalessandro 	/* Find the GID in the mcast table. */
3184e74080bSDennis Dalessandro 	n = ibp->mcast_tree.rb_node;
3194e74080bSDennis Dalessandro 	while (1) {
3204e74080bSDennis Dalessandro 		if (!n) {
3214e74080bSDennis Dalessandro 			spin_unlock_irq(&ibp->lock);
3224e74080bSDennis Dalessandro 			return -EINVAL;
3234e74080bSDennis Dalessandro 		}
3244e74080bSDennis Dalessandro 
3254e74080bSDennis Dalessandro 		mcast = rb_entry(n, struct rvt_mcast, rb_node);
326aad9ff97SMichael J. Ruhl 		ret = memcmp(gid->raw, mcast->mcast_addr.mgid.raw,
327aad9ff97SMichael J. Ruhl 			     sizeof(*gid));
328aad9ff97SMichael J. Ruhl 		if (ret < 0) {
3294e74080bSDennis Dalessandro 			n = n->rb_left;
330aad9ff97SMichael J. Ruhl 		} else if (ret > 0) {
3314e74080bSDennis Dalessandro 			n = n->rb_right;
332aad9ff97SMichael J. Ruhl 		} else {
333aad9ff97SMichael J. Ruhl 			/* MGID/MLID must match */
334aad9ff97SMichael J. Ruhl 			if (mcast->mcast_addr.lid != lid) {
335aad9ff97SMichael J. Ruhl 				spin_unlock_irq(&ibp->lock);
336aad9ff97SMichael J. Ruhl 				return -EINVAL;
337aad9ff97SMichael J. Ruhl 			}
3384e74080bSDennis Dalessandro 			break;
3394e74080bSDennis Dalessandro 		}
340aad9ff97SMichael J. Ruhl 	}
3414e74080bSDennis Dalessandro 
3424e74080bSDennis Dalessandro 	/* Search the QP list. */
3434e74080bSDennis Dalessandro 	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
3444e74080bSDennis Dalessandro 		if (p->qp != qp)
3454e74080bSDennis Dalessandro 			continue;
3464e74080bSDennis Dalessandro 		/*
3474e74080bSDennis Dalessandro 		 * We found it, so remove it, but don't poison the forward
3484e74080bSDennis Dalessandro 		 * link until we are sure there are no list walkers.
3494e74080bSDennis Dalessandro 		 */
3504e74080bSDennis Dalessandro 		list_del_rcu(&p->list);
3514e74080bSDennis Dalessandro 		mcast->n_attached--;
3524e74080bSDennis Dalessandro 		delp = p;
3534e74080bSDennis Dalessandro 
3544e74080bSDennis Dalessandro 		/* If this was the last attached QP, remove the GID too. */
3554e74080bSDennis Dalessandro 		if (list_empty(&mcast->qp_list)) {
3564e74080bSDennis Dalessandro 			rb_erase(&mcast->rb_node, &ibp->mcast_tree);
3574e74080bSDennis Dalessandro 			last = 1;
3584e74080bSDennis Dalessandro 		}
3594e74080bSDennis Dalessandro 		break;
3604e74080bSDennis Dalessandro 	}
3614e74080bSDennis Dalessandro 
3624e74080bSDennis Dalessandro 	spin_unlock_irq(&ibp->lock);
3634e74080bSDennis Dalessandro 	/* QP not attached */
3644e74080bSDennis Dalessandro 	if (!delp)
3654e74080bSDennis Dalessandro 		return -EINVAL;
3664e74080bSDennis Dalessandro 
3674e74080bSDennis Dalessandro 	/*
3684e74080bSDennis Dalessandro 	 * Wait for any list walkers to finish before freeing the
3694e74080bSDennis Dalessandro 	 * list element.
3704e74080bSDennis Dalessandro 	 */
3714e74080bSDennis Dalessandro 	wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
3724e74080bSDennis Dalessandro 	rvt_mcast_qp_free(delp);
3734e74080bSDennis Dalessandro 
3744e74080bSDennis Dalessandro 	if (last) {
3754e74080bSDennis Dalessandro 		atomic_dec(&mcast->refcount);
3764e74080bSDennis Dalessandro 		wait_event(mcast->wait, !atomic_read(&mcast->refcount));
3774e74080bSDennis Dalessandro 		rvt_mcast_free(mcast);
3784e74080bSDennis Dalessandro 		spin_lock_irq(&rdi->n_mcast_grps_lock);
3794e74080bSDennis Dalessandro 		rdi->n_mcast_grps_allocated--;
3804e74080bSDennis Dalessandro 		spin_unlock_irq(&rdi->n_mcast_grps_lock);
3814e74080bSDennis Dalessandro 	}
3824e74080bSDennis Dalessandro 
3834e74080bSDennis Dalessandro 	return 0;
3844e74080bSDennis Dalessandro }
3854e74080bSDennis Dalessandro 
38690793f71SDennis Dalessandro /**
3872988ca08SMauro Carvalho Chehab  * rvt_mcast_tree_empty - determine if any qps are attached to any mcast group
38890793f71SDennis Dalessandro  * @rdi: rvt dev struct
38990793f71SDennis Dalessandro  *
39090793f71SDennis Dalessandro  * Return: in use count
39190793f71SDennis Dalessandro  */
rvt_mcast_tree_empty(struct rvt_dev_info * rdi)3924e74080bSDennis Dalessandro int rvt_mcast_tree_empty(struct rvt_dev_info *rdi)
3934e74080bSDennis Dalessandro {
3944e74080bSDennis Dalessandro 	int i;
3954e74080bSDennis Dalessandro 	int in_use = 0;
3964e74080bSDennis Dalessandro 
3974e74080bSDennis Dalessandro 	for (i = 0; i < rdi->dparms.nports; i++)
3984e74080bSDennis Dalessandro 		if (rdi->ports[i]->mcast_tree.rb_node)
3994e74080bSDennis Dalessandro 			in_use++;
4004e74080bSDennis Dalessandro 	return in_use;
4019fa25171SDennis Dalessandro }
402