19fa25171SDennis Dalessandro /*
2fe314195SDennis Dalessandro  * Copyright(c) 2016 Intel Corporation.
39fa25171SDennis Dalessandro  *
49fa25171SDennis Dalessandro  * This file is provided under a dual BSD/GPLv2 license.  When using or
59fa25171SDennis Dalessandro  * redistributing this file, you may do so under either license.
69fa25171SDennis Dalessandro  *
79fa25171SDennis Dalessandro  * GPL LICENSE SUMMARY
89fa25171SDennis Dalessandro  *
99fa25171SDennis Dalessandro  * This program is free software; you can redistribute it and/or modify
109fa25171SDennis Dalessandro  * it under the terms of version 2 of the GNU General Public License as
119fa25171SDennis Dalessandro  * published by the Free Software Foundation.
129fa25171SDennis Dalessandro  *
139fa25171SDennis Dalessandro  * This program is distributed in the hope that it will be useful, but
149fa25171SDennis Dalessandro  * WITHOUT ANY WARRANTY; without even the implied warranty of
159fa25171SDennis Dalessandro  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
169fa25171SDennis Dalessandro  * General Public License for more details.
179fa25171SDennis Dalessandro  *
189fa25171SDennis Dalessandro  * BSD LICENSE
199fa25171SDennis Dalessandro  *
209fa25171SDennis Dalessandro  * Redistribution and use in source and binary forms, with or without
219fa25171SDennis Dalessandro  * modification, are permitted provided that the following conditions
229fa25171SDennis Dalessandro  * are met:
239fa25171SDennis Dalessandro  *
249fa25171SDennis Dalessandro  *  - Redistributions of source code must retain the above copyright
259fa25171SDennis Dalessandro  *    notice, this list of conditions and the following disclaimer.
269fa25171SDennis Dalessandro  *  - Redistributions in binary form must reproduce the above copyright
279fa25171SDennis Dalessandro  *    notice, this list of conditions and the following disclaimer in
289fa25171SDennis Dalessandro  *    the documentation and/or other materials provided with the
299fa25171SDennis Dalessandro  *    distribution.
309fa25171SDennis Dalessandro  *  - Neither the name of Intel Corporation nor the names of its
319fa25171SDennis Dalessandro  *    contributors may be used to endorse or promote products derived
329fa25171SDennis Dalessandro  *    from this software without specific prior written permission.
339fa25171SDennis Dalessandro  *
349fa25171SDennis Dalessandro  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
359fa25171SDennis Dalessandro  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
369fa25171SDennis Dalessandro  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
379fa25171SDennis Dalessandro  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
389fa25171SDennis Dalessandro  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
399fa25171SDennis Dalessandro  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
409fa25171SDennis Dalessandro  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
419fa25171SDennis Dalessandro  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
429fa25171SDennis Dalessandro  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
439fa25171SDennis Dalessandro  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
449fa25171SDennis Dalessandro  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
459fa25171SDennis Dalessandro  *
469fa25171SDennis Dalessandro  */
479fa25171SDennis Dalessandro 
484e74080bSDennis Dalessandro #include <linux/slab.h>
494e74080bSDennis Dalessandro #include <linux/sched.h>
504e74080bSDennis Dalessandro #include <linux/rculist.h>
514e74080bSDennis Dalessandro #include <rdma/rdma_vt.h>
524e74080bSDennis Dalessandro #include <rdma/rdmavt_qp.h>
534e74080bSDennis Dalessandro 
549fa25171SDennis Dalessandro #include "mcast.h"
559fa25171SDennis Dalessandro 
5690793f71SDennis Dalessandro /**
57*2988ca08SMauro Carvalho Chehab  * rvt_driver_mcast_init - init resources for multicast
5890793f71SDennis Dalessandro  * @rdi: rvt dev struct
5990793f71SDennis Dalessandro  *
6090793f71SDennis Dalessandro  * This is per device that registers with rdmavt
6190793f71SDennis Dalessandro  */
624e74080bSDennis Dalessandro void rvt_driver_mcast_init(struct rvt_dev_info *rdi)
634e74080bSDennis Dalessandro {
644e74080bSDennis Dalessandro 	/*
654e74080bSDennis Dalessandro 	 * Anything that needs setup for multicast on a per driver or per rdi
664e74080bSDennis Dalessandro 	 * basis should be done in here.
674e74080bSDennis Dalessandro 	 */
684e74080bSDennis Dalessandro 	spin_lock_init(&rdi->n_mcast_grps_lock);
694e74080bSDennis Dalessandro }
704e74080bSDennis Dalessandro 
714e74080bSDennis Dalessandro /**
72*2988ca08SMauro Carvalho Chehab  * rvt_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
734e74080bSDennis Dalessandro  * @qp: the QP to link
744e74080bSDennis Dalessandro  */
754e74080bSDennis Dalessandro static struct rvt_mcast_qp *rvt_mcast_qp_alloc(struct rvt_qp *qp)
764e74080bSDennis Dalessandro {
774e74080bSDennis Dalessandro 	struct rvt_mcast_qp *mqp;
784e74080bSDennis Dalessandro 
794e74080bSDennis Dalessandro 	mqp = kmalloc(sizeof(*mqp), GFP_KERNEL);
804e74080bSDennis Dalessandro 	if (!mqp)
814e74080bSDennis Dalessandro 		goto bail;
824e74080bSDennis Dalessandro 
834e74080bSDennis Dalessandro 	mqp->qp = qp;
84b44980f8SSebastian Sanchez 	rvt_get_qp(qp);
854e74080bSDennis Dalessandro 
864e74080bSDennis Dalessandro bail:
874e74080bSDennis Dalessandro 	return mqp;
884e74080bSDennis Dalessandro }
894e74080bSDennis Dalessandro 
904e74080bSDennis Dalessandro static void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp)
914e74080bSDennis Dalessandro {
924e74080bSDennis Dalessandro 	struct rvt_qp *qp = mqp->qp;
934e74080bSDennis Dalessandro 
944e74080bSDennis Dalessandro 	/* Notify hfi1_destroy_qp() if it is waiting. */
95b44980f8SSebastian Sanchez 	rvt_put_qp(qp);
964e74080bSDennis Dalessandro 
974e74080bSDennis Dalessandro 	kfree(mqp);
984e74080bSDennis Dalessandro }
994e74080bSDennis Dalessandro 
1004e74080bSDennis Dalessandro /**
101*2988ca08SMauro Carvalho Chehab  * rvt_mcast_alloc - allocate the multicast GID structure
1024e74080bSDennis Dalessandro  * @mgid: the multicast GID
103aad9ff97SMichael J. Ruhl  * @lid: the muilticast LID (host order)
1044e74080bSDennis Dalessandro  *
1054e74080bSDennis Dalessandro  * A list of QPs will be attached to this structure.
1064e74080bSDennis Dalessandro  */
107aad9ff97SMichael J. Ruhl static struct rvt_mcast *rvt_mcast_alloc(union ib_gid *mgid, u16 lid)
1084e74080bSDennis Dalessandro {
1094e74080bSDennis Dalessandro 	struct rvt_mcast *mcast;
1104e74080bSDennis Dalessandro 
1114e74080bSDennis Dalessandro 	mcast = kzalloc(sizeof(*mcast), GFP_KERNEL);
1124e74080bSDennis Dalessandro 	if (!mcast)
1134e74080bSDennis Dalessandro 		goto bail;
1144e74080bSDennis Dalessandro 
115aad9ff97SMichael J. Ruhl 	mcast->mcast_addr.mgid = *mgid;
116aad9ff97SMichael J. Ruhl 	mcast->mcast_addr.lid = lid;
117aad9ff97SMichael J. Ruhl 
1184e74080bSDennis Dalessandro 	INIT_LIST_HEAD(&mcast->qp_list);
1194e74080bSDennis Dalessandro 	init_waitqueue_head(&mcast->wait);
1204e74080bSDennis Dalessandro 	atomic_set(&mcast->refcount, 0);
1214e74080bSDennis Dalessandro 
1224e74080bSDennis Dalessandro bail:
1234e74080bSDennis Dalessandro 	return mcast;
1244e74080bSDennis Dalessandro }
1254e74080bSDennis Dalessandro 
1264e74080bSDennis Dalessandro static void rvt_mcast_free(struct rvt_mcast *mcast)
1274e74080bSDennis Dalessandro {
1284e74080bSDennis Dalessandro 	struct rvt_mcast_qp *p, *tmp;
1294e74080bSDennis Dalessandro 
1304e74080bSDennis Dalessandro 	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
1314e74080bSDennis Dalessandro 		rvt_mcast_qp_free(p);
1324e74080bSDennis Dalessandro 
1334e74080bSDennis Dalessandro 	kfree(mcast);
1344e74080bSDennis Dalessandro }
1354e74080bSDennis Dalessandro 
1364e74080bSDennis Dalessandro /**
137aad9ff97SMichael J. Ruhl  * rvt_mcast_find - search the global table for the given multicast GID/LID
138aad9ff97SMichael J. Ruhl  * NOTE: It is valid to have 1 MLID with multiple MGIDs.  It is not valid
139aad9ff97SMichael J. Ruhl  * to have 1 MGID with multiple MLIDs.
1404e74080bSDennis Dalessandro  * @ibp: the IB port structure
1414e74080bSDennis Dalessandro  * @mgid: the multicast GID to search for
142aad9ff97SMichael J. Ruhl  * @lid: the multicast LID portion of the multicast address (host order)
1434e74080bSDennis Dalessandro  *
1444e74080bSDennis Dalessandro  * The caller is responsible for decrementing the reference count if found.
14590793f71SDennis Dalessandro  *
14690793f71SDennis Dalessandro  * Return: NULL if not found.
1474e74080bSDennis Dalessandro  */
148aad9ff97SMichael J. Ruhl struct rvt_mcast *rvt_mcast_find(struct rvt_ibport *ibp, union ib_gid *mgid,
149aad9ff97SMichael J. Ruhl 				 u16 lid)
1504e74080bSDennis Dalessandro {
1514e74080bSDennis Dalessandro 	struct rb_node *n;
1524e74080bSDennis Dalessandro 	unsigned long flags;
1534e74080bSDennis Dalessandro 	struct rvt_mcast *found = NULL;
1544e74080bSDennis Dalessandro 
1554e74080bSDennis Dalessandro 	spin_lock_irqsave(&ibp->lock, flags);
1564e74080bSDennis Dalessandro 	n = ibp->mcast_tree.rb_node;
1574e74080bSDennis Dalessandro 	while (n) {
1584e74080bSDennis Dalessandro 		int ret;
1594e74080bSDennis Dalessandro 		struct rvt_mcast *mcast;
1604e74080bSDennis Dalessandro 
1614e74080bSDennis Dalessandro 		mcast = rb_entry(n, struct rvt_mcast, rb_node);
1624e74080bSDennis Dalessandro 
163aad9ff97SMichael J. Ruhl 		ret = memcmp(mgid->raw, mcast->mcast_addr.mgid.raw,
164aad9ff97SMichael J. Ruhl 			     sizeof(*mgid));
1654e74080bSDennis Dalessandro 		if (ret < 0) {
1664e74080bSDennis Dalessandro 			n = n->rb_left;
1674e74080bSDennis Dalessandro 		} else if (ret > 0) {
1684e74080bSDennis Dalessandro 			n = n->rb_right;
1694e74080bSDennis Dalessandro 		} else {
170aad9ff97SMichael J. Ruhl 			/* MGID/MLID must match */
171aad9ff97SMichael J. Ruhl 			if (mcast->mcast_addr.lid == lid) {
1724e74080bSDennis Dalessandro 				atomic_inc(&mcast->refcount);
1734e74080bSDennis Dalessandro 				found = mcast;
174aad9ff97SMichael J. Ruhl 			}
1754e74080bSDennis Dalessandro 			break;
1764e74080bSDennis Dalessandro 		}
1774e74080bSDennis Dalessandro 	}
1784e74080bSDennis Dalessandro 	spin_unlock_irqrestore(&ibp->lock, flags);
1794e74080bSDennis Dalessandro 	return found;
1804e74080bSDennis Dalessandro }
1814e74080bSDennis Dalessandro EXPORT_SYMBOL(rvt_mcast_find);
1824e74080bSDennis Dalessandro 
1834e74080bSDennis Dalessandro /**
184*2988ca08SMauro Carvalho Chehab  * rvt_mcast_add - insert mcast GID into table and attach QP struct
1854e74080bSDennis Dalessandro  * @mcast: the mcast GID table
1864e74080bSDennis Dalessandro  * @mqp: the QP to attach
1874e74080bSDennis Dalessandro  *
18890793f71SDennis Dalessandro  * Return: zero if both were added.  Return EEXIST if the GID was already in
1894e74080bSDennis Dalessandro  * the table but the QP was added.  Return ESRCH if the QP was already
190aad9ff97SMichael J. Ruhl  * attached and neither structure was added. Return EINVAL if the MGID was
191aad9ff97SMichael J. Ruhl  * found, but the MLID did NOT match.
1924e74080bSDennis Dalessandro  */
1934e74080bSDennis Dalessandro static int rvt_mcast_add(struct rvt_dev_info *rdi, struct rvt_ibport *ibp,
1944e74080bSDennis Dalessandro 			 struct rvt_mcast *mcast, struct rvt_mcast_qp *mqp)
1954e74080bSDennis Dalessandro {
1964e74080bSDennis Dalessandro 	struct rb_node **n = &ibp->mcast_tree.rb_node;
1974e74080bSDennis Dalessandro 	struct rb_node *pn = NULL;
1984e74080bSDennis Dalessandro 	int ret;
1994e74080bSDennis Dalessandro 
2004e74080bSDennis Dalessandro 	spin_lock_irq(&ibp->lock);
2014e74080bSDennis Dalessandro 
2024e74080bSDennis Dalessandro 	while (*n) {
2034e74080bSDennis Dalessandro 		struct rvt_mcast *tmcast;
2044e74080bSDennis Dalessandro 		struct rvt_mcast_qp *p;
2054e74080bSDennis Dalessandro 
2064e74080bSDennis Dalessandro 		pn = *n;
2074e74080bSDennis Dalessandro 		tmcast = rb_entry(pn, struct rvt_mcast, rb_node);
2084e74080bSDennis Dalessandro 
209aad9ff97SMichael J. Ruhl 		ret = memcmp(mcast->mcast_addr.mgid.raw,
210aad9ff97SMichael J. Ruhl 			     tmcast->mcast_addr.mgid.raw,
211aad9ff97SMichael J. Ruhl 			     sizeof(mcast->mcast_addr.mgid));
2124e74080bSDennis Dalessandro 		if (ret < 0) {
2134e74080bSDennis Dalessandro 			n = &pn->rb_left;
2144e74080bSDennis Dalessandro 			continue;
2154e74080bSDennis Dalessandro 		}
2164e74080bSDennis Dalessandro 		if (ret > 0) {
2174e74080bSDennis Dalessandro 			n = &pn->rb_right;
2184e74080bSDennis Dalessandro 			continue;
2194e74080bSDennis Dalessandro 		}
2204e74080bSDennis Dalessandro 
221aad9ff97SMichael J. Ruhl 		if (tmcast->mcast_addr.lid != mcast->mcast_addr.lid) {
222aad9ff97SMichael J. Ruhl 			ret = EINVAL;
223aad9ff97SMichael J. Ruhl 			goto bail;
224aad9ff97SMichael J. Ruhl 		}
225aad9ff97SMichael J. Ruhl 
2264e74080bSDennis Dalessandro 		/* Search the QP list to see if this is already there. */
2274e74080bSDennis Dalessandro 		list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
2284e74080bSDennis Dalessandro 			if (p->qp == mqp->qp) {
2294e74080bSDennis Dalessandro 				ret = ESRCH;
2304e74080bSDennis Dalessandro 				goto bail;
2314e74080bSDennis Dalessandro 			}
2324e74080bSDennis Dalessandro 		}
2334e74080bSDennis Dalessandro 		if (tmcast->n_attached ==
2344e74080bSDennis Dalessandro 		    rdi->dparms.props.max_mcast_qp_attach) {
2354e74080bSDennis Dalessandro 			ret = ENOMEM;
2364e74080bSDennis Dalessandro 			goto bail;
2374e74080bSDennis Dalessandro 		}
2384e74080bSDennis Dalessandro 
2394e74080bSDennis Dalessandro 		tmcast->n_attached++;
2404e74080bSDennis Dalessandro 
2414e74080bSDennis Dalessandro 		list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
2424e74080bSDennis Dalessandro 		ret = EEXIST;
2434e74080bSDennis Dalessandro 		goto bail;
2444e74080bSDennis Dalessandro 	}
2454e74080bSDennis Dalessandro 
2464e74080bSDennis Dalessandro 	spin_lock(&rdi->n_mcast_grps_lock);
2474e74080bSDennis Dalessandro 	if (rdi->n_mcast_grps_allocated == rdi->dparms.props.max_mcast_grp) {
2484e74080bSDennis Dalessandro 		spin_unlock(&rdi->n_mcast_grps_lock);
2494e74080bSDennis Dalessandro 		ret = ENOMEM;
2504e74080bSDennis Dalessandro 		goto bail;
2514e74080bSDennis Dalessandro 	}
2524e74080bSDennis Dalessandro 
2534e74080bSDennis Dalessandro 	rdi->n_mcast_grps_allocated++;
2544e74080bSDennis Dalessandro 	spin_unlock(&rdi->n_mcast_grps_lock);
2554e74080bSDennis Dalessandro 
2564e74080bSDennis Dalessandro 	mcast->n_attached++;
2574e74080bSDennis Dalessandro 
2584e74080bSDennis Dalessandro 	list_add_tail_rcu(&mqp->list, &mcast->qp_list);
2594e74080bSDennis Dalessandro 
2604e74080bSDennis Dalessandro 	atomic_inc(&mcast->refcount);
2614e74080bSDennis Dalessandro 	rb_link_node(&mcast->rb_node, pn, n);
2624e74080bSDennis Dalessandro 	rb_insert_color(&mcast->rb_node, &ibp->mcast_tree);
2634e74080bSDennis Dalessandro 
2644e74080bSDennis Dalessandro 	ret = 0;
2654e74080bSDennis Dalessandro 
2664e74080bSDennis Dalessandro bail:
2674e74080bSDennis Dalessandro 	spin_unlock_irq(&ibp->lock);
2684e74080bSDennis Dalessandro 
2694e74080bSDennis Dalessandro 	return ret;
2704e74080bSDennis Dalessandro }
2714e74080bSDennis Dalessandro 
27290793f71SDennis Dalessandro /**
27390793f71SDennis Dalessandro  * rvt_attach_mcast - attach a qp to a multicast group
27490793f71SDennis Dalessandro  * @ibqp: Infiniband qp
2754f9a3018SRandy Dunlap  * @gid: multicast guid
27690793f71SDennis Dalessandro  * @lid: multicast lid
27790793f71SDennis Dalessandro  *
27890793f71SDennis Dalessandro  * Return: 0 on success
27990793f71SDennis Dalessandro  */
2809fa25171SDennis Dalessandro int rvt_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
2819fa25171SDennis Dalessandro {
2824e74080bSDennis Dalessandro 	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
2834e74080bSDennis Dalessandro 	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
2844e74080bSDennis Dalessandro 	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
2854e74080bSDennis Dalessandro 	struct rvt_mcast *mcast;
2864e74080bSDennis Dalessandro 	struct rvt_mcast_qp *mqp;
2874e74080bSDennis Dalessandro 	int ret = -ENOMEM;
2884e74080bSDennis Dalessandro 
2894e74080bSDennis Dalessandro 	if (ibqp->qp_num <= 1 || qp->state == IB_QPS_RESET)
2904e74080bSDennis Dalessandro 		return -EINVAL;
2914e74080bSDennis Dalessandro 
2924e74080bSDennis Dalessandro 	/*
2934e74080bSDennis Dalessandro 	 * Allocate data structures since its better to do this outside of
2944e74080bSDennis Dalessandro 	 * spin locks and it will most likely be needed.
2954e74080bSDennis Dalessandro 	 */
296aad9ff97SMichael J. Ruhl 	mcast = rvt_mcast_alloc(gid, lid);
2974e74080bSDennis Dalessandro 	if (!mcast)
2984e74080bSDennis Dalessandro 		return -ENOMEM;
2994e74080bSDennis Dalessandro 
3004e74080bSDennis Dalessandro 	mqp = rvt_mcast_qp_alloc(qp);
3014e74080bSDennis Dalessandro 	if (!mqp)
3024e74080bSDennis Dalessandro 		goto bail_mcast;
3034e74080bSDennis Dalessandro 
3044e74080bSDennis Dalessandro 	switch (rvt_mcast_add(rdi, ibp, mcast, mqp)) {
3054e74080bSDennis Dalessandro 	case ESRCH:
3064e74080bSDennis Dalessandro 		/* Neither was used: OK to attach the same QP twice. */
3074e74080bSDennis Dalessandro 		ret = 0;
3084e74080bSDennis Dalessandro 		goto bail_mqp;
3094e74080bSDennis Dalessandro 	case EEXIST: /* The mcast wasn't used */
3104e74080bSDennis Dalessandro 		ret = 0;
3114e74080bSDennis Dalessandro 		goto bail_mcast;
3124e74080bSDennis Dalessandro 	case ENOMEM:
3134e74080bSDennis Dalessandro 		/* Exceeded the maximum number of mcast groups. */
3144e74080bSDennis Dalessandro 		ret = -ENOMEM;
3154e74080bSDennis Dalessandro 		goto bail_mqp;
316aad9ff97SMichael J. Ruhl 	case EINVAL:
317aad9ff97SMichael J. Ruhl 		/* Invalid MGID/MLID pair */
318aad9ff97SMichael J. Ruhl 		ret = -EINVAL;
319aad9ff97SMichael J. Ruhl 		goto bail_mqp;
3204e74080bSDennis Dalessandro 	default:
3214e74080bSDennis Dalessandro 		break;
3224e74080bSDennis Dalessandro 	}
3234e74080bSDennis Dalessandro 
3244e74080bSDennis Dalessandro 	return 0;
3254e74080bSDennis Dalessandro 
3264e74080bSDennis Dalessandro bail_mqp:
3274e74080bSDennis Dalessandro 	rvt_mcast_qp_free(mqp);
3284e74080bSDennis Dalessandro 
3294e74080bSDennis Dalessandro bail_mcast:
3304e74080bSDennis Dalessandro 	rvt_mcast_free(mcast);
3314e74080bSDennis Dalessandro 
3324e74080bSDennis Dalessandro 	return ret;
3339fa25171SDennis Dalessandro }
3349fa25171SDennis Dalessandro 
33590793f71SDennis Dalessandro /**
33690793f71SDennis Dalessandro  * rvt_detach_mcast - remove a qp from a multicast group
33790793f71SDennis Dalessandro  * @ibqp: Infiniband qp
3384f9a3018SRandy Dunlap  * @gid: multicast guid
33990793f71SDennis Dalessandro  * @lid: multicast lid
34090793f71SDennis Dalessandro  *
34190793f71SDennis Dalessandro  * Return: 0 on success
34290793f71SDennis Dalessandro  */
3439fa25171SDennis Dalessandro int rvt_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
3449fa25171SDennis Dalessandro {
3454e74080bSDennis Dalessandro 	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
3464e74080bSDennis Dalessandro 	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
3474e74080bSDennis Dalessandro 	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
3484e74080bSDennis Dalessandro 	struct rvt_mcast *mcast = NULL;
3494e74080bSDennis Dalessandro 	struct rvt_mcast_qp *p, *tmp, *delp = NULL;
3504e74080bSDennis Dalessandro 	struct rb_node *n;
3514e74080bSDennis Dalessandro 	int last = 0;
3524e74080bSDennis Dalessandro 	int ret = 0;
3534e74080bSDennis Dalessandro 
354f9586abfSAlex Estrin 	if (ibqp->qp_num <= 1)
3554e74080bSDennis Dalessandro 		return -EINVAL;
3564e74080bSDennis Dalessandro 
3574e74080bSDennis Dalessandro 	spin_lock_irq(&ibp->lock);
3584e74080bSDennis Dalessandro 
3594e74080bSDennis Dalessandro 	/* Find the GID in the mcast table. */
3604e74080bSDennis Dalessandro 	n = ibp->mcast_tree.rb_node;
3614e74080bSDennis Dalessandro 	while (1) {
3624e74080bSDennis Dalessandro 		if (!n) {
3634e74080bSDennis Dalessandro 			spin_unlock_irq(&ibp->lock);
3644e74080bSDennis Dalessandro 			return -EINVAL;
3654e74080bSDennis Dalessandro 		}
3664e74080bSDennis Dalessandro 
3674e74080bSDennis Dalessandro 		mcast = rb_entry(n, struct rvt_mcast, rb_node);
368aad9ff97SMichael J. Ruhl 		ret = memcmp(gid->raw, mcast->mcast_addr.mgid.raw,
369aad9ff97SMichael J. Ruhl 			     sizeof(*gid));
370aad9ff97SMichael J. Ruhl 		if (ret < 0) {
3714e74080bSDennis Dalessandro 			n = n->rb_left;
372aad9ff97SMichael J. Ruhl 		} else if (ret > 0) {
3734e74080bSDennis Dalessandro 			n = n->rb_right;
374aad9ff97SMichael J. Ruhl 		} else {
375aad9ff97SMichael J. Ruhl 			/* MGID/MLID must match */
376aad9ff97SMichael J. Ruhl 			if (mcast->mcast_addr.lid != lid) {
377aad9ff97SMichael J. Ruhl 				spin_unlock_irq(&ibp->lock);
378aad9ff97SMichael J. Ruhl 				return -EINVAL;
379aad9ff97SMichael J. Ruhl 			}
3804e74080bSDennis Dalessandro 			break;
3814e74080bSDennis Dalessandro 		}
382aad9ff97SMichael J. Ruhl 	}
3834e74080bSDennis Dalessandro 
3844e74080bSDennis Dalessandro 	/* Search the QP list. */
3854e74080bSDennis Dalessandro 	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
3864e74080bSDennis Dalessandro 		if (p->qp != qp)
3874e74080bSDennis Dalessandro 			continue;
3884e74080bSDennis Dalessandro 		/*
3894e74080bSDennis Dalessandro 		 * We found it, so remove it, but don't poison the forward
3904e74080bSDennis Dalessandro 		 * link until we are sure there are no list walkers.
3914e74080bSDennis Dalessandro 		 */
3924e74080bSDennis Dalessandro 		list_del_rcu(&p->list);
3934e74080bSDennis Dalessandro 		mcast->n_attached--;
3944e74080bSDennis Dalessandro 		delp = p;
3954e74080bSDennis Dalessandro 
3964e74080bSDennis Dalessandro 		/* If this was the last attached QP, remove the GID too. */
3974e74080bSDennis Dalessandro 		if (list_empty(&mcast->qp_list)) {
3984e74080bSDennis Dalessandro 			rb_erase(&mcast->rb_node, &ibp->mcast_tree);
3994e74080bSDennis Dalessandro 			last = 1;
4004e74080bSDennis Dalessandro 		}
4014e74080bSDennis Dalessandro 		break;
4024e74080bSDennis Dalessandro 	}
4034e74080bSDennis Dalessandro 
4044e74080bSDennis Dalessandro 	spin_unlock_irq(&ibp->lock);
4054e74080bSDennis Dalessandro 	/* QP not attached */
4064e74080bSDennis Dalessandro 	if (!delp)
4074e74080bSDennis Dalessandro 		return -EINVAL;
4084e74080bSDennis Dalessandro 
4094e74080bSDennis Dalessandro 	/*
4104e74080bSDennis Dalessandro 	 * Wait for any list walkers to finish before freeing the
4114e74080bSDennis Dalessandro 	 * list element.
4124e74080bSDennis Dalessandro 	 */
4134e74080bSDennis Dalessandro 	wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
4144e74080bSDennis Dalessandro 	rvt_mcast_qp_free(delp);
4154e74080bSDennis Dalessandro 
4164e74080bSDennis Dalessandro 	if (last) {
4174e74080bSDennis Dalessandro 		atomic_dec(&mcast->refcount);
4184e74080bSDennis Dalessandro 		wait_event(mcast->wait, !atomic_read(&mcast->refcount));
4194e74080bSDennis Dalessandro 		rvt_mcast_free(mcast);
4204e74080bSDennis Dalessandro 		spin_lock_irq(&rdi->n_mcast_grps_lock);
4214e74080bSDennis Dalessandro 		rdi->n_mcast_grps_allocated--;
4224e74080bSDennis Dalessandro 		spin_unlock_irq(&rdi->n_mcast_grps_lock);
4234e74080bSDennis Dalessandro 	}
4244e74080bSDennis Dalessandro 
4254e74080bSDennis Dalessandro 	return 0;
4264e74080bSDennis Dalessandro }
4274e74080bSDennis Dalessandro 
42890793f71SDennis Dalessandro /**
429*2988ca08SMauro Carvalho Chehab  * rvt_mcast_tree_empty - determine if any qps are attached to any mcast group
43090793f71SDennis Dalessandro  * @rdi: rvt dev struct
43190793f71SDennis Dalessandro  *
43290793f71SDennis Dalessandro  * Return: in use count
43390793f71SDennis Dalessandro  */
4344e74080bSDennis Dalessandro int rvt_mcast_tree_empty(struct rvt_dev_info *rdi)
4354e74080bSDennis Dalessandro {
4364e74080bSDennis Dalessandro 	int i;
4374e74080bSDennis Dalessandro 	int in_use = 0;
4384e74080bSDennis Dalessandro 
4394e74080bSDennis Dalessandro 	for (i = 0; i < rdi->dparms.nports; i++)
4404e74080bSDennis Dalessandro 		if (rdi->ports[i]->mcast_tree.rb_node)
4414e74080bSDennis Dalessandro 			in_use++;
4424e74080bSDennis Dalessandro 	return in_use;
4439fa25171SDennis Dalessandro }
444