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