162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2016 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/sched.h>
862306a36Sopenharmony_ci#include <linux/rculist.h>
962306a36Sopenharmony_ci#include <rdma/rdma_vt.h>
1062306a36Sopenharmony_ci#include <rdma/rdmavt_qp.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "mcast.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/**
1562306a36Sopenharmony_ci * rvt_driver_mcast_init - init resources for multicast
1662306a36Sopenharmony_ci * @rdi: rvt dev struct
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * This is per device that registers with rdmavt
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_civoid rvt_driver_mcast_init(struct rvt_dev_info *rdi)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	/*
2362306a36Sopenharmony_ci	 * Anything that needs setup for multicast on a per driver or per rdi
2462306a36Sopenharmony_ci	 * basis should be done in here.
2562306a36Sopenharmony_ci	 */
2662306a36Sopenharmony_ci	spin_lock_init(&rdi->n_mcast_grps_lock);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/**
3062306a36Sopenharmony_ci * rvt_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct
3162306a36Sopenharmony_ci * @qp: the QP to link
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic struct rvt_mcast_qp *rvt_mcast_qp_alloc(struct rvt_qp *qp)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct rvt_mcast_qp *mqp;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	mqp = kmalloc(sizeof(*mqp), GFP_KERNEL);
3862306a36Sopenharmony_ci	if (!mqp)
3962306a36Sopenharmony_ci		goto bail;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	mqp->qp = qp;
4262306a36Sopenharmony_ci	rvt_get_qp(qp);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cibail:
4562306a36Sopenharmony_ci	return mqp;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void rvt_mcast_qp_free(struct rvt_mcast_qp *mqp)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct rvt_qp *qp = mqp->qp;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* Notify hfi1_destroy_qp() if it is waiting. */
5362306a36Sopenharmony_ci	rvt_put_qp(qp);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	kfree(mqp);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/**
5962306a36Sopenharmony_ci * rvt_mcast_alloc - allocate the multicast GID structure
6062306a36Sopenharmony_ci * @mgid: the multicast GID
6162306a36Sopenharmony_ci * @lid: the muilticast LID (host order)
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * A list of QPs will be attached to this structure.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic struct rvt_mcast *rvt_mcast_alloc(union ib_gid *mgid, u16 lid)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct rvt_mcast *mcast;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	mcast = kzalloc(sizeof(*mcast), GFP_KERNEL);
7062306a36Sopenharmony_ci	if (!mcast)
7162306a36Sopenharmony_ci		goto bail;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mcast->mcast_addr.mgid = *mgid;
7462306a36Sopenharmony_ci	mcast->mcast_addr.lid = lid;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	INIT_LIST_HEAD(&mcast->qp_list);
7762306a36Sopenharmony_ci	init_waitqueue_head(&mcast->wait);
7862306a36Sopenharmony_ci	atomic_set(&mcast->refcount, 0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cibail:
8162306a36Sopenharmony_ci	return mcast;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void rvt_mcast_free(struct rvt_mcast *mcast)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct rvt_mcast_qp *p, *tmp;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list)
8962306a36Sopenharmony_ci		rvt_mcast_qp_free(p);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	kfree(mcast);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/**
9562306a36Sopenharmony_ci * rvt_mcast_find - search the global table for the given multicast GID/LID
9662306a36Sopenharmony_ci * NOTE: It is valid to have 1 MLID with multiple MGIDs.  It is not valid
9762306a36Sopenharmony_ci * to have 1 MGID with multiple MLIDs.
9862306a36Sopenharmony_ci * @ibp: the IB port structure
9962306a36Sopenharmony_ci * @mgid: the multicast GID to search for
10062306a36Sopenharmony_ci * @lid: the multicast LID portion of the multicast address (host order)
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * The caller is responsible for decrementing the reference count if found.
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * Return: NULL if not found.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistruct rvt_mcast *rvt_mcast_find(struct rvt_ibport *ibp, union ib_gid *mgid,
10762306a36Sopenharmony_ci				 u16 lid)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct rb_node *n;
11062306a36Sopenharmony_ci	unsigned long flags;
11162306a36Sopenharmony_ci	struct rvt_mcast *found = NULL;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	spin_lock_irqsave(&ibp->lock, flags);
11462306a36Sopenharmony_ci	n = ibp->mcast_tree.rb_node;
11562306a36Sopenharmony_ci	while (n) {
11662306a36Sopenharmony_ci		int ret;
11762306a36Sopenharmony_ci		struct rvt_mcast *mcast;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		mcast = rb_entry(n, struct rvt_mcast, rb_node);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		ret = memcmp(mgid->raw, mcast->mcast_addr.mgid.raw,
12262306a36Sopenharmony_ci			     sizeof(*mgid));
12362306a36Sopenharmony_ci		if (ret < 0) {
12462306a36Sopenharmony_ci			n = n->rb_left;
12562306a36Sopenharmony_ci		} else if (ret > 0) {
12662306a36Sopenharmony_ci			n = n->rb_right;
12762306a36Sopenharmony_ci		} else {
12862306a36Sopenharmony_ci			/* MGID/MLID must match */
12962306a36Sopenharmony_ci			if (mcast->mcast_addr.lid == lid) {
13062306a36Sopenharmony_ci				atomic_inc(&mcast->refcount);
13162306a36Sopenharmony_ci				found = mcast;
13262306a36Sopenharmony_ci			}
13362306a36Sopenharmony_ci			break;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ibp->lock, flags);
13762306a36Sopenharmony_ci	return found;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ciEXPORT_SYMBOL(rvt_mcast_find);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci * rvt_mcast_add - insert mcast GID into table and attach QP struct
14362306a36Sopenharmony_ci * @mcast: the mcast GID table
14462306a36Sopenharmony_ci * @mqp: the QP to attach
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * Return: zero if both were added.  Return EEXIST if the GID was already in
14762306a36Sopenharmony_ci * the table but the QP was added.  Return ESRCH if the QP was already
14862306a36Sopenharmony_ci * attached and neither structure was added. Return EINVAL if the MGID was
14962306a36Sopenharmony_ci * found, but the MLID did NOT match.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic int rvt_mcast_add(struct rvt_dev_info *rdi, struct rvt_ibport *ibp,
15262306a36Sopenharmony_ci			 struct rvt_mcast *mcast, struct rvt_mcast_qp *mqp)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct rb_node **n = &ibp->mcast_tree.rb_node;
15562306a36Sopenharmony_ci	struct rb_node *pn = NULL;
15662306a36Sopenharmony_ci	int ret;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	spin_lock_irq(&ibp->lock);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	while (*n) {
16162306a36Sopenharmony_ci		struct rvt_mcast *tmcast;
16262306a36Sopenharmony_ci		struct rvt_mcast_qp *p;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		pn = *n;
16562306a36Sopenharmony_ci		tmcast = rb_entry(pn, struct rvt_mcast, rb_node);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		ret = memcmp(mcast->mcast_addr.mgid.raw,
16862306a36Sopenharmony_ci			     tmcast->mcast_addr.mgid.raw,
16962306a36Sopenharmony_ci			     sizeof(mcast->mcast_addr.mgid));
17062306a36Sopenharmony_ci		if (ret < 0) {
17162306a36Sopenharmony_ci			n = &pn->rb_left;
17262306a36Sopenharmony_ci			continue;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci		if (ret > 0) {
17562306a36Sopenharmony_ci			n = &pn->rb_right;
17662306a36Sopenharmony_ci			continue;
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		if (tmcast->mcast_addr.lid != mcast->mcast_addr.lid) {
18062306a36Sopenharmony_ci			ret = EINVAL;
18162306a36Sopenharmony_ci			goto bail;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		/* Search the QP list to see if this is already there. */
18562306a36Sopenharmony_ci		list_for_each_entry_rcu(p, &tmcast->qp_list, list) {
18662306a36Sopenharmony_ci			if (p->qp == mqp->qp) {
18762306a36Sopenharmony_ci				ret = ESRCH;
18862306a36Sopenharmony_ci				goto bail;
18962306a36Sopenharmony_ci			}
19062306a36Sopenharmony_ci		}
19162306a36Sopenharmony_ci		if (tmcast->n_attached ==
19262306a36Sopenharmony_ci		    rdi->dparms.props.max_mcast_qp_attach) {
19362306a36Sopenharmony_ci			ret = ENOMEM;
19462306a36Sopenharmony_ci			goto bail;
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		tmcast->n_attached++;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		list_add_tail_rcu(&mqp->list, &tmcast->qp_list);
20062306a36Sopenharmony_ci		ret = EEXIST;
20162306a36Sopenharmony_ci		goto bail;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	spin_lock(&rdi->n_mcast_grps_lock);
20562306a36Sopenharmony_ci	if (rdi->n_mcast_grps_allocated == rdi->dparms.props.max_mcast_grp) {
20662306a36Sopenharmony_ci		spin_unlock(&rdi->n_mcast_grps_lock);
20762306a36Sopenharmony_ci		ret = ENOMEM;
20862306a36Sopenharmony_ci		goto bail;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	rdi->n_mcast_grps_allocated++;
21262306a36Sopenharmony_ci	spin_unlock(&rdi->n_mcast_grps_lock);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	mcast->n_attached++;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	list_add_tail_rcu(&mqp->list, &mcast->qp_list);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	atomic_inc(&mcast->refcount);
21962306a36Sopenharmony_ci	rb_link_node(&mcast->rb_node, pn, n);
22062306a36Sopenharmony_ci	rb_insert_color(&mcast->rb_node, &ibp->mcast_tree);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ret = 0;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cibail:
22562306a36Sopenharmony_ci	spin_unlock_irq(&ibp->lock);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return ret;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * rvt_attach_mcast - attach a qp to a multicast group
23262306a36Sopenharmony_ci * @ibqp: Infiniband qp
23362306a36Sopenharmony_ci * @gid: multicast guid
23462306a36Sopenharmony_ci * @lid: multicast lid
23562306a36Sopenharmony_ci *
23662306a36Sopenharmony_ci * Return: 0 on success
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_ciint rvt_attach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
24162306a36Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
24262306a36Sopenharmony_ci	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
24362306a36Sopenharmony_ci	struct rvt_mcast *mcast;
24462306a36Sopenharmony_ci	struct rvt_mcast_qp *mqp;
24562306a36Sopenharmony_ci	int ret = -ENOMEM;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (ibqp->qp_num <= 1 || qp->state == IB_QPS_RESET)
24862306a36Sopenharmony_ci		return -EINVAL;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/*
25162306a36Sopenharmony_ci	 * Allocate data structures since its better to do this outside of
25262306a36Sopenharmony_ci	 * spin locks and it will most likely be needed.
25362306a36Sopenharmony_ci	 */
25462306a36Sopenharmony_ci	mcast = rvt_mcast_alloc(gid, lid);
25562306a36Sopenharmony_ci	if (!mcast)
25662306a36Sopenharmony_ci		return -ENOMEM;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	mqp = rvt_mcast_qp_alloc(qp);
25962306a36Sopenharmony_ci	if (!mqp)
26062306a36Sopenharmony_ci		goto bail_mcast;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	switch (rvt_mcast_add(rdi, ibp, mcast, mqp)) {
26362306a36Sopenharmony_ci	case ESRCH:
26462306a36Sopenharmony_ci		/* Neither was used: OK to attach the same QP twice. */
26562306a36Sopenharmony_ci		ret = 0;
26662306a36Sopenharmony_ci		goto bail_mqp;
26762306a36Sopenharmony_ci	case EEXIST: /* The mcast wasn't used */
26862306a36Sopenharmony_ci		ret = 0;
26962306a36Sopenharmony_ci		goto bail_mcast;
27062306a36Sopenharmony_ci	case ENOMEM:
27162306a36Sopenharmony_ci		/* Exceeded the maximum number of mcast groups. */
27262306a36Sopenharmony_ci		ret = -ENOMEM;
27362306a36Sopenharmony_ci		goto bail_mqp;
27462306a36Sopenharmony_ci	case EINVAL:
27562306a36Sopenharmony_ci		/* Invalid MGID/MLID pair */
27662306a36Sopenharmony_ci		ret = -EINVAL;
27762306a36Sopenharmony_ci		goto bail_mqp;
27862306a36Sopenharmony_ci	default:
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cibail_mqp:
28562306a36Sopenharmony_ci	rvt_mcast_qp_free(mqp);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cibail_mcast:
28862306a36Sopenharmony_ci	rvt_mcast_free(mcast);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return ret;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/**
29462306a36Sopenharmony_ci * rvt_detach_mcast - remove a qp from a multicast group
29562306a36Sopenharmony_ci * @ibqp: Infiniband qp
29662306a36Sopenharmony_ci * @gid: multicast guid
29762306a36Sopenharmony_ci * @lid: multicast lid
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci * Return: 0 on success
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_ciint rvt_detach_mcast(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct rvt_qp *qp = ibqp_to_rvtqp(ibqp);
30462306a36Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ibqp->device);
30562306a36Sopenharmony_ci	struct rvt_ibport *ibp = rdi->ports[qp->port_num - 1];
30662306a36Sopenharmony_ci	struct rvt_mcast *mcast = NULL;
30762306a36Sopenharmony_ci	struct rvt_mcast_qp *p, *tmp, *delp = NULL;
30862306a36Sopenharmony_ci	struct rb_node *n;
30962306a36Sopenharmony_ci	int last = 0;
31062306a36Sopenharmony_ci	int ret = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (ibqp->qp_num <= 1)
31362306a36Sopenharmony_ci		return -EINVAL;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	spin_lock_irq(&ibp->lock);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Find the GID in the mcast table. */
31862306a36Sopenharmony_ci	n = ibp->mcast_tree.rb_node;
31962306a36Sopenharmony_ci	while (1) {
32062306a36Sopenharmony_ci		if (!n) {
32162306a36Sopenharmony_ci			spin_unlock_irq(&ibp->lock);
32262306a36Sopenharmony_ci			return -EINVAL;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		mcast = rb_entry(n, struct rvt_mcast, rb_node);
32662306a36Sopenharmony_ci		ret = memcmp(gid->raw, mcast->mcast_addr.mgid.raw,
32762306a36Sopenharmony_ci			     sizeof(*gid));
32862306a36Sopenharmony_ci		if (ret < 0) {
32962306a36Sopenharmony_ci			n = n->rb_left;
33062306a36Sopenharmony_ci		} else if (ret > 0) {
33162306a36Sopenharmony_ci			n = n->rb_right;
33262306a36Sopenharmony_ci		} else {
33362306a36Sopenharmony_ci			/* MGID/MLID must match */
33462306a36Sopenharmony_ci			if (mcast->mcast_addr.lid != lid) {
33562306a36Sopenharmony_ci				spin_unlock_irq(&ibp->lock);
33662306a36Sopenharmony_ci				return -EINVAL;
33762306a36Sopenharmony_ci			}
33862306a36Sopenharmony_ci			break;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Search the QP list. */
34362306a36Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) {
34462306a36Sopenharmony_ci		if (p->qp != qp)
34562306a36Sopenharmony_ci			continue;
34662306a36Sopenharmony_ci		/*
34762306a36Sopenharmony_ci		 * We found it, so remove it, but don't poison the forward
34862306a36Sopenharmony_ci		 * link until we are sure there are no list walkers.
34962306a36Sopenharmony_ci		 */
35062306a36Sopenharmony_ci		list_del_rcu(&p->list);
35162306a36Sopenharmony_ci		mcast->n_attached--;
35262306a36Sopenharmony_ci		delp = p;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		/* If this was the last attached QP, remove the GID too. */
35562306a36Sopenharmony_ci		if (list_empty(&mcast->qp_list)) {
35662306a36Sopenharmony_ci			rb_erase(&mcast->rb_node, &ibp->mcast_tree);
35762306a36Sopenharmony_ci			last = 1;
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_unlock_irq(&ibp->lock);
36362306a36Sopenharmony_ci	/* QP not attached */
36462306a36Sopenharmony_ci	if (!delp)
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/*
36862306a36Sopenharmony_ci	 * Wait for any list walkers to finish before freeing the
36962306a36Sopenharmony_ci	 * list element.
37062306a36Sopenharmony_ci	 */
37162306a36Sopenharmony_ci	wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1);
37262306a36Sopenharmony_ci	rvt_mcast_qp_free(delp);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (last) {
37562306a36Sopenharmony_ci		atomic_dec(&mcast->refcount);
37662306a36Sopenharmony_ci		wait_event(mcast->wait, !atomic_read(&mcast->refcount));
37762306a36Sopenharmony_ci		rvt_mcast_free(mcast);
37862306a36Sopenharmony_ci		spin_lock_irq(&rdi->n_mcast_grps_lock);
37962306a36Sopenharmony_ci		rdi->n_mcast_grps_allocated--;
38062306a36Sopenharmony_ci		spin_unlock_irq(&rdi->n_mcast_grps_lock);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return 0;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/**
38762306a36Sopenharmony_ci * rvt_mcast_tree_empty - determine if any qps are attached to any mcast group
38862306a36Sopenharmony_ci * @rdi: rvt dev struct
38962306a36Sopenharmony_ci *
39062306a36Sopenharmony_ci * Return: in use count
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_ciint rvt_mcast_tree_empty(struct rvt_dev_info *rdi)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	int i;
39562306a36Sopenharmony_ci	int in_use = 0;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	for (i = 0; i < rdi->dparms.nports; i++)
39862306a36Sopenharmony_ci		if (rdi->ports[i]->mcast_tree.rb_node)
39962306a36Sopenharmony_ci			in_use++;
40062306a36Sopenharmony_ci	return in_use;
40162306a36Sopenharmony_ci}
402