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