18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright(c) 2016 Intel Corporation.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
118c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
168c2ecf20Sopenharmony_ci * General Public License for more details.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * BSD LICENSE
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
228c2ecf20Sopenharmony_ci * are met:
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *  - Redistributions of source code must retain the above copyright
258c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
268c2ecf20Sopenharmony_ci *  - Redistributions in binary form must reproduce the above copyright
278c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
288c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
298c2ecf20Sopenharmony_ci *    distribution.
308c2ecf20Sopenharmony_ci *  - Neither the name of Intel Corporation nor the names of its
318c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
328c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#include <linux/slab.h>
498c2ecf20Sopenharmony_ci#include <linux/sched.h>
508c2ecf20Sopenharmony_ci#include <linux/rculist.h>
518c2ecf20Sopenharmony_ci#include <rdma/rdma_vt.h>
528c2ecf20Sopenharmony_ci#include <rdma/rdmavt_qp.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include "mcast.h"
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * rvt_driver_mcast - init resources for multicast
588c2ecf20Sopenharmony_ci * @rdi: rvt dev struct
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * This is per device that registers with rdmavt
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_civoid rvt_driver_mcast_init(struct rvt_dev_info *rdi)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	/*
658c2ecf20Sopenharmony_ci	 * Anything that needs setup for multicast on a per driver or per rdi
668c2ecf20Sopenharmony_ci	 * basis should be done in here.
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	spin_lock_init(&rdi->n_mcast_grps_lock);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/**
728c2ecf20Sopenharmony_ci * mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
738c2ecf20Sopenharmony_ci * @qp: the QP to link
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistatic struct rvt_mcast_qp *rvt_mcast_qp_alloc(struct rvt_qp *qp)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct rvt_mcast_qp *mqp;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	mqp = kmalloc(sizeof(*mqp), GFP_KERNEL);
808c2ecf20Sopenharmony_ci	if (!mqp)
818c2ecf20Sopenharmony_ci		goto bail;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	mqp->qp = qp;
848c2ecf20Sopenharmony_ci	rvt_get_qp(qp);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cibail:
878c2ecf20Sopenharmony_ci	return mqp;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct rvt_qp *qp = mqp->qp;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Notify hfi1_destroy_qp() if it is waiting. */
958c2ecf20Sopenharmony_ci	rvt_put_qp(qp);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	kfree(mqp);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/**
1018c2ecf20Sopenharmony_ci * mcast_alloc - allocate the multicast GID structure
1028c2ecf20Sopenharmony_ci * @mgid: the multicast GID
1038c2ecf20Sopenharmony_ci * @lid: the muilticast LID (host order)
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * A list of QPs will be attached to this structure.
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic struct rvt_mcast *rvt_mcast_alloc(union ib_gid *mgid, u16 lid)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct rvt_mcast *mcast;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	mcast = kzalloc(sizeof(*mcast), GFP_KERNEL);
1128c2ecf20Sopenharmony_ci	if (!mcast)
1138c2ecf20Sopenharmony_ci		goto bail;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	mcast->mcast_addr.mgid = *mgid;
1168c2ecf20Sopenharmony_ci	mcast->mcast_addr.lid = lid;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mcast->qp_list);
1198c2ecf20Sopenharmony_ci	init_waitqueue_head(&mcast->wait);
1208c2ecf20Sopenharmony_ci	atomic_set(&mcast->refcount, 0);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cibail:
1238c2ecf20Sopenharmony_ci	return mcast;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void rvt_mcast_free(struct rvt_mcast *mcast)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct rvt_mcast_qp *p, *tmp;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
1318c2ecf20Sopenharmony_ci		rvt_mcast_qp_free(p);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	kfree(mcast);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/**
1378c2ecf20Sopenharmony_ci * rvt_mcast_find - search the global table for the given multicast GID/LID
1388c2ecf20Sopenharmony_ci * NOTE: It is valid to have 1 MLID with multiple MGIDs.  It is not valid
1398c2ecf20Sopenharmony_ci * to have 1 MGID with multiple MLIDs.
1408c2ecf20Sopenharmony_ci * @ibp: the IB port structure
1418c2ecf20Sopenharmony_ci * @mgid: the multicast GID to search for
1428c2ecf20Sopenharmony_ci * @lid: the multicast LID portion of the multicast address (host order)
1438c2ecf20Sopenharmony_ci *
1448c2ecf20Sopenharmony_ci * The caller is responsible for decrementing the reference count if found.
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * Return: NULL if not found.
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_cistruct rvt_mcast *rvt_mcast_find(struct rvt_ibport *ibp, union ib_gid *mgid,
1498c2ecf20Sopenharmony_ci				 u16 lid)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct rb_node *n;
1528c2ecf20Sopenharmony_ci	unsigned long flags;
1538c2ecf20Sopenharmony_ci	struct rvt_mcast *found = NULL;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ibp->lock, flags);
1568c2ecf20Sopenharmony_ci	n = ibp->mcast_tree.rb_node;
1578c2ecf20Sopenharmony_ci	while (n) {
1588c2ecf20Sopenharmony_ci		int ret;
1598c2ecf20Sopenharmony_ci		struct rvt_mcast *mcast;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		mcast = rb_entry(n, struct rvt_mcast, rb_node);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		ret = memcmp(mgid->raw, mcast->mcast_addr.mgid.raw,
1648c2ecf20Sopenharmony_ci			     sizeof(*mgid));
1658c2ecf20Sopenharmony_ci		if (ret < 0) {
1668c2ecf20Sopenharmony_ci			n = n->rb_left;
1678c2ecf20Sopenharmony_ci		} else if (ret > 0) {
1688c2ecf20Sopenharmony_ci			n = n->rb_right;
1698c2ecf20Sopenharmony_ci		} else {
1708c2ecf20Sopenharmony_ci			/* MGID/MLID must match */
1718c2ecf20Sopenharmony_ci			if (mcast->mcast_addr.lid == lid) {
1728c2ecf20Sopenharmony_ci				atomic_inc(&mcast->refcount);
1738c2ecf20Sopenharmony_ci				found = mcast;
1748c2ecf20Sopenharmony_ci			}
1758c2ecf20Sopenharmony_ci			break;
1768c2ecf20Sopenharmony_ci		}
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ibp->lock, flags);
1798c2ecf20Sopenharmony_ci	return found;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rvt_mcast_find);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/**
1848c2ecf20Sopenharmony_ci * mcast_add - insert mcast GID into table and attach QP struct
1858c2ecf20Sopenharmony_ci * @mcast: the mcast GID table
1868c2ecf20Sopenharmony_ci * @mqp: the QP to attach
1878c2ecf20Sopenharmony_ci *
1888c2ecf20Sopenharmony_ci * Return: zero if both were added.  Return EEXIST if the GID was already in
1898c2ecf20Sopenharmony_ci * the table but the QP was added.  Return ESRCH if the QP was already
1908c2ecf20Sopenharmony_ci * attached and neither structure was added. Return EINVAL if the MGID was
1918c2ecf20Sopenharmony_ci * found, but the MLID did NOT match.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_cistatic int rvt_mcast_add(struct rvt_dev_info *rdi, struct rvt_ibport *ibp,
1948c2ecf20Sopenharmony_ci			 struct rvt_mcast *mcast, struct rvt_mcast_qp *mqp)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct rb_node **n = &ibp->mcast_tree.rb_node;
1978c2ecf20Sopenharmony_ci	struct rb_node *pn = NULL;
1988c2ecf20Sopenharmony_ci	int ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	spin_lock_irq(&ibp->lock);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	while (*n) {
2038c2ecf20Sopenharmony_ci		struct rvt_mcast *tmcast;
2048c2ecf20Sopenharmony_ci		struct rvt_mcast_qp *p;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		pn = *n;
2078c2ecf20Sopenharmony_ci		tmcast = rb_entry(pn, struct rvt_mcast, rb_node);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		ret = memcmp(mcast->mcast_addr.mgid.raw,
2108c2ecf20Sopenharmony_ci			     tmcast->mcast_addr.mgid.raw,
2118c2ecf20Sopenharmony_ci			     sizeof(mcast->mcast_addr.mgid));
2128c2ecf20Sopenharmony_ci		if (ret < 0) {
2138c2ecf20Sopenharmony_ci			n = &pn->rb_left;
2148c2ecf20Sopenharmony_ci			continue;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci		if (ret > 0) {
2178c2ecf20Sopenharmony_ci			n = &pn->rb_right;
2188c2ecf20Sopenharmony_ci			continue;
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		if (tmcast->mcast_addr.lid != mcast->mcast_addr.lid) {
2228c2ecf20Sopenharmony_ci			ret = EINVAL;
2238c2ecf20Sopenharmony_ci			goto bail;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		/* Search the QP list to see if this is already there. */
2278c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
2288c2ecf20Sopenharmony_ci			if (p->qp == mqp->qp) {
2298c2ecf20Sopenharmony_ci				ret = ESRCH;
2308c2ecf20Sopenharmony_ci				goto bail;
2318c2ecf20Sopenharmony_ci			}
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci		if (tmcast->n_attached ==
2348c2ecf20Sopenharmony_ci		    rdi->dparms.props.max_mcast_qp_attach) {
2358c2ecf20Sopenharmony_ci			ret = ENOMEM;
2368c2ecf20Sopenharmony_ci			goto bail;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		tmcast->n_attached++;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
2428c2ecf20Sopenharmony_ci		ret = EEXIST;
2438c2ecf20Sopenharmony_ci		goto bail;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	spin_lock(&rdi->n_mcast_grps_lock);
2478c2ecf20Sopenharmony_ci	if (rdi->n_mcast_grps_allocated == rdi->dparms.props.max_mcast_grp) {
2488c2ecf20Sopenharmony_ci		spin_unlock(&rdi->n_mcast_grps_lock);
2498c2ecf20Sopenharmony_ci		ret = ENOMEM;
2508c2ecf20Sopenharmony_ci		goto bail;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	rdi->n_mcast_grps_allocated++;
2548c2ecf20Sopenharmony_ci	spin_unlock(&rdi->n_mcast_grps_lock);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	mcast->n_attached++;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	list_add_tail_rcu(&mqp->list, &mcast->qp_list);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	atomic_inc(&mcast->refcount);
2618c2ecf20Sopenharmony_ci	rb_link_node(&mcast->rb_node, pn, n);
2628c2ecf20Sopenharmony_ci	rb_insert_color(&mcast->rb_node, &ibp->mcast_tree);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	ret = 0;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cibail:
2678c2ecf20Sopenharmony_ci	spin_unlock_irq(&ibp->lock);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return ret;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/**
2738c2ecf20Sopenharmony_ci * rvt_attach_mcast - attach a qp to a multicast group
2748c2ecf20Sopenharmony_ci * @ibqp: Infiniband qp
2758c2ecf20Sopenharmony_ci * @gid: multicast guid
2768c2ecf20Sopenharmony_ci * @lid: multicast lid
2778c2ecf20Sopenharmony_ci *
2788c2ecf20Sopenharmony_ci * Return: 0 on success
2798c2ecf20Sopenharmony_ci */
2808c2ecf20Sopenharmony_ciint rvt_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
2838c2ecf20Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
2848c2ecf20Sopenharmony_ci	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
2858c2ecf20Sopenharmony_ci	struct rvt_mcast *mcast;
2868c2ecf20Sopenharmony_ci	struct rvt_mcast_qp *mqp;
2878c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (ibqp->qp_num <= 1 || qp->state == IB_QPS_RESET)
2908c2ecf20Sopenharmony_ci		return -EINVAL;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * Allocate data structures since its better to do this outside of
2948c2ecf20Sopenharmony_ci	 * spin locks and it will most likely be needed.
2958c2ecf20Sopenharmony_ci	 */
2968c2ecf20Sopenharmony_ci	mcast = rvt_mcast_alloc(gid, lid);
2978c2ecf20Sopenharmony_ci	if (!mcast)
2988c2ecf20Sopenharmony_ci		return -ENOMEM;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	mqp = rvt_mcast_qp_alloc(qp);
3018c2ecf20Sopenharmony_ci	if (!mqp)
3028c2ecf20Sopenharmony_ci		goto bail_mcast;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	switch (rvt_mcast_add(rdi, ibp, mcast, mqp)) {
3058c2ecf20Sopenharmony_ci	case ESRCH:
3068c2ecf20Sopenharmony_ci		/* Neither was used: OK to attach the same QP twice. */
3078c2ecf20Sopenharmony_ci		ret = 0;
3088c2ecf20Sopenharmony_ci		goto bail_mqp;
3098c2ecf20Sopenharmony_ci	case EEXIST: /* The mcast wasn't used */
3108c2ecf20Sopenharmony_ci		ret = 0;
3118c2ecf20Sopenharmony_ci		goto bail_mcast;
3128c2ecf20Sopenharmony_ci	case ENOMEM:
3138c2ecf20Sopenharmony_ci		/* Exceeded the maximum number of mcast groups. */
3148c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3158c2ecf20Sopenharmony_ci		goto bail_mqp;
3168c2ecf20Sopenharmony_ci	case EINVAL:
3178c2ecf20Sopenharmony_ci		/* Invalid MGID/MLID pair */
3188c2ecf20Sopenharmony_ci		ret = -EINVAL;
3198c2ecf20Sopenharmony_ci		goto bail_mqp;
3208c2ecf20Sopenharmony_ci	default:
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return 0;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cibail_mqp:
3278c2ecf20Sopenharmony_ci	rvt_mcast_qp_free(mqp);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cibail_mcast:
3308c2ecf20Sopenharmony_ci	rvt_mcast_free(mcast);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return ret;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * rvt_detach_mcast - remove a qp from a multicast group
3378c2ecf20Sopenharmony_ci * @ibqp: Infiniband qp
3388c2ecf20Sopenharmony_ci * @gid: multicast guid
3398c2ecf20Sopenharmony_ci * @lid: multicast lid
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * Return: 0 on success
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_ciint rvt_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
3468c2ecf20Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
3478c2ecf20Sopenharmony_ci	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
3488c2ecf20Sopenharmony_ci	struct rvt_mcast *mcast = NULL;
3498c2ecf20Sopenharmony_ci	struct rvt_mcast_qp *p, *tmp, *delp = NULL;
3508c2ecf20Sopenharmony_ci	struct rb_node *n;
3518c2ecf20Sopenharmony_ci	int last = 0;
3528c2ecf20Sopenharmony_ci	int ret = 0;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (ibqp->qp_num <= 1)
3558c2ecf20Sopenharmony_ci		return -EINVAL;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	spin_lock_irq(&ibp->lock);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Find the GID in the mcast table. */
3608c2ecf20Sopenharmony_ci	n = ibp->mcast_tree.rb_node;
3618c2ecf20Sopenharmony_ci	while (1) {
3628c2ecf20Sopenharmony_ci		if (!n) {
3638c2ecf20Sopenharmony_ci			spin_unlock_irq(&ibp->lock);
3648c2ecf20Sopenharmony_ci			return -EINVAL;
3658c2ecf20Sopenharmony_ci		}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		mcast = rb_entry(n, struct rvt_mcast, rb_node);
3688c2ecf20Sopenharmony_ci		ret = memcmp(gid->raw, mcast->mcast_addr.mgid.raw,
3698c2ecf20Sopenharmony_ci			     sizeof(*gid));
3708c2ecf20Sopenharmony_ci		if (ret < 0) {
3718c2ecf20Sopenharmony_ci			n = n->rb_left;
3728c2ecf20Sopenharmony_ci		} else if (ret > 0) {
3738c2ecf20Sopenharmony_ci			n = n->rb_right;
3748c2ecf20Sopenharmony_ci		} else {
3758c2ecf20Sopenharmony_ci			/* MGID/MLID must match */
3768c2ecf20Sopenharmony_ci			if (mcast->mcast_addr.lid != lid) {
3778c2ecf20Sopenharmony_ci				spin_unlock_irq(&ibp->lock);
3788c2ecf20Sopenharmony_ci				return -EINVAL;
3798c2ecf20Sopenharmony_ci			}
3808c2ecf20Sopenharmony_ci			break;
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* Search the QP list. */
3858c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
3868c2ecf20Sopenharmony_ci		if (p->qp != qp)
3878c2ecf20Sopenharmony_ci			continue;
3888c2ecf20Sopenharmony_ci		/*
3898c2ecf20Sopenharmony_ci		 * We found it, so remove it, but don't poison the forward
3908c2ecf20Sopenharmony_ci		 * link until we are sure there are no list walkers.
3918c2ecf20Sopenharmony_ci		 */
3928c2ecf20Sopenharmony_ci		list_del_rcu(&p->list);
3938c2ecf20Sopenharmony_ci		mcast->n_attached--;
3948c2ecf20Sopenharmony_ci		delp = p;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		/* If this was the last attached QP, remove the GID too. */
3978c2ecf20Sopenharmony_ci		if (list_empty(&mcast->qp_list)) {
3988c2ecf20Sopenharmony_ci			rb_erase(&mcast->rb_node, &ibp->mcast_tree);
3998c2ecf20Sopenharmony_ci			last = 1;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci		break;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	spin_unlock_irq(&ibp->lock);
4058c2ecf20Sopenharmony_ci	/* QP not attached */
4068c2ecf20Sopenharmony_ci	if (!delp)
4078c2ecf20Sopenharmony_ci		return -EINVAL;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/*
4108c2ecf20Sopenharmony_ci	 * Wait for any list walkers to finish before freeing the
4118c2ecf20Sopenharmony_ci	 * list element.
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
4148c2ecf20Sopenharmony_ci	rvt_mcast_qp_free(delp);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (last) {
4178c2ecf20Sopenharmony_ci		atomic_dec(&mcast->refcount);
4188c2ecf20Sopenharmony_ci		wait_event(mcast->wait, !atomic_read(&mcast->refcount));
4198c2ecf20Sopenharmony_ci		rvt_mcast_free(mcast);
4208c2ecf20Sopenharmony_ci		spin_lock_irq(&rdi->n_mcast_grps_lock);
4218c2ecf20Sopenharmony_ci		rdi->n_mcast_grps_allocated--;
4228c2ecf20Sopenharmony_ci		spin_unlock_irq(&rdi->n_mcast_grps_lock);
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci/**
4298c2ecf20Sopenharmony_ci *rvt_mast_tree_empty - determine if any qps are attached to any mcast group
4308c2ecf20Sopenharmony_ci *@rdi: rvt dev struct
4318c2ecf20Sopenharmony_ci *
4328c2ecf20Sopenharmony_ci * Return: in use count
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_ciint rvt_mcast_tree_empty(struct rvt_dev_info *rdi)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	int i;
4378c2ecf20Sopenharmony_ci	int in_use = 0;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	for (i = 0; i < rdi->dparms.nports; i++)
4408c2ecf20Sopenharmony_ci		if (rdi->ports[i]->mcast_tree.rb_node)
4418c2ecf20Sopenharmony_ci			in_use++;
4428c2ecf20Sopenharmony_ci	return in_use;
4438c2ecf20Sopenharmony_ci}
444