162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2022 Hewlett Packard Enterprise, Inc. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved. 562306a36Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * rxe_mcast.c implements driver support for multicast transport. 1062306a36Sopenharmony_ci * It is based on two data structures struct rxe_mcg ('mcg') and 1162306a36Sopenharmony_ci * struct rxe_mca ('mca'). An mcg is allocated each time a qp is 1262306a36Sopenharmony_ci * attached to a new mgid for the first time. These are indexed by 1362306a36Sopenharmony_ci * a red-black tree using the mgid. This data structure is searched 1462306a36Sopenharmony_ci * for the mcg when a multicast packet is received and when another 1562306a36Sopenharmony_ci * qp is attached to the same mgid. It is cleaned up when the last qp 1662306a36Sopenharmony_ci * is detached from the mcg. Each time a qp is attached to an mcg an 1762306a36Sopenharmony_ci * mca is created. It holds a pointer to the qp and is added to a list 1862306a36Sopenharmony_ci * of qp's that are attached to the mcg. The qp_list is used to replicate 1962306a36Sopenharmony_ci * mcast packets in the rxe receive path. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "rxe.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * rxe_mcast_add - add multicast address to rxe device 2662306a36Sopenharmony_ci * @rxe: rxe device object 2762306a36Sopenharmony_ci * @mgid: multicast address as a gid 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Returns 0 on success else an error 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic int rxe_mcast_add(struct rxe_dev *rxe, union ib_gid *mgid) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci unsigned char ll_addr[ETH_ALEN]; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci ipv6_eth_mc_map((struct in6_addr *)mgid->raw, ll_addr); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return dev_mc_add(rxe->ndev, ll_addr); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * rxe_mcast_del - delete multicast address from rxe device 4262306a36Sopenharmony_ci * @rxe: rxe device object 4362306a36Sopenharmony_ci * @mgid: multicast address as a gid 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Returns 0 on success else an error 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic int rxe_mcast_del(struct rxe_dev *rxe, union ib_gid *mgid) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned char ll_addr[ETH_ALEN]; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ipv6_eth_mc_map((struct in6_addr *)mgid->raw, ll_addr); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return dev_mc_del(rxe->ndev, ll_addr); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * __rxe_insert_mcg - insert an mcg into red-black tree (rxe->mcg_tree) 5862306a36Sopenharmony_ci * @mcg: mcg object with an embedded red-black tree node 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * Context: caller must hold a reference to mcg and rxe->mcg_lock and 6162306a36Sopenharmony_ci * is responsible to avoid adding the same mcg twice to the tree. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic void __rxe_insert_mcg(struct rxe_mcg *mcg) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct rb_root *tree = &mcg->rxe->mcg_tree; 6662306a36Sopenharmony_ci struct rb_node **link = &tree->rb_node; 6762306a36Sopenharmony_ci struct rb_node *node = NULL; 6862306a36Sopenharmony_ci struct rxe_mcg *tmp; 6962306a36Sopenharmony_ci int cmp; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci while (*link) { 7262306a36Sopenharmony_ci node = *link; 7362306a36Sopenharmony_ci tmp = rb_entry(node, struct rxe_mcg, node); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci cmp = memcmp(&tmp->mgid, &mcg->mgid, sizeof(mcg->mgid)); 7662306a36Sopenharmony_ci if (cmp > 0) 7762306a36Sopenharmony_ci link = &(*link)->rb_left; 7862306a36Sopenharmony_ci else 7962306a36Sopenharmony_ci link = &(*link)->rb_right; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rb_link_node(&mcg->node, node, link); 8362306a36Sopenharmony_ci rb_insert_color(&mcg->node, tree); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * __rxe_remove_mcg - remove an mcg from red-black tree holding lock 8862306a36Sopenharmony_ci * @mcg: mcast group object with an embedded red-black tree node 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * Context: caller must hold a reference to mcg and rxe->mcg_lock 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_cistatic void __rxe_remove_mcg(struct rxe_mcg *mcg) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci rb_erase(&mcg->node, &mcg->rxe->mcg_tree); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * __rxe_lookup_mcg - lookup mcg in rxe->mcg_tree while holding lock 9962306a36Sopenharmony_ci * @rxe: rxe device object 10062306a36Sopenharmony_ci * @mgid: multicast IP address 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Context: caller must hold rxe->mcg_lock 10362306a36Sopenharmony_ci * Returns: mcg on success and takes a ref to mcg else NULL 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic struct rxe_mcg *__rxe_lookup_mcg(struct rxe_dev *rxe, 10662306a36Sopenharmony_ci union ib_gid *mgid) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct rb_root *tree = &rxe->mcg_tree; 10962306a36Sopenharmony_ci struct rxe_mcg *mcg; 11062306a36Sopenharmony_ci struct rb_node *node; 11162306a36Sopenharmony_ci int cmp; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci node = tree->rb_node; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci while (node) { 11662306a36Sopenharmony_ci mcg = rb_entry(node, struct rxe_mcg, node); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci cmp = memcmp(&mcg->mgid, mgid, sizeof(*mgid)); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (cmp > 0) 12162306a36Sopenharmony_ci node = node->rb_left; 12262306a36Sopenharmony_ci else if (cmp < 0) 12362306a36Sopenharmony_ci node = node->rb_right; 12462306a36Sopenharmony_ci else 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (node) { 12962306a36Sopenharmony_ci kref_get(&mcg->ref_cnt); 13062306a36Sopenharmony_ci return mcg; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return NULL; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * rxe_lookup_mcg - lookup up mcg in red-back tree 13862306a36Sopenharmony_ci * @rxe: rxe device object 13962306a36Sopenharmony_ci * @mgid: multicast IP address 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Returns: mcg if found else NULL 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_cistruct rxe_mcg *rxe_lookup_mcg(struct rxe_dev *rxe, union ib_gid *mgid) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct rxe_mcg *mcg; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock_bh(&rxe->mcg_lock); 14862306a36Sopenharmony_ci mcg = __rxe_lookup_mcg(rxe, mgid); 14962306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return mcg; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * __rxe_init_mcg - initialize a new mcg 15662306a36Sopenharmony_ci * @rxe: rxe device 15762306a36Sopenharmony_ci * @mgid: multicast address as a gid 15862306a36Sopenharmony_ci * @mcg: new mcg object 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Context: caller should hold rxe->mcg lock 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic void __rxe_init_mcg(struct rxe_dev *rxe, union ib_gid *mgid, 16362306a36Sopenharmony_ci struct rxe_mcg *mcg) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci kref_init(&mcg->ref_cnt); 16662306a36Sopenharmony_ci memcpy(&mcg->mgid, mgid, sizeof(mcg->mgid)); 16762306a36Sopenharmony_ci INIT_LIST_HEAD(&mcg->qp_list); 16862306a36Sopenharmony_ci mcg->rxe = rxe; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* caller holds a ref on mcg but that will be 17162306a36Sopenharmony_ci * dropped when mcg goes out of scope. We need to take a ref 17262306a36Sopenharmony_ci * on the pointer that will be saved in the red-black tree 17362306a36Sopenharmony_ci * by __rxe_insert_mcg and used to lookup mcg from mgid later. 17462306a36Sopenharmony_ci * Inserting mcg makes it visible to outside so this should 17562306a36Sopenharmony_ci * be done last after the object is ready. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci kref_get(&mcg->ref_cnt); 17862306a36Sopenharmony_ci __rxe_insert_mcg(mcg); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * rxe_get_mcg - lookup or allocate a mcg 18362306a36Sopenharmony_ci * @rxe: rxe device object 18462306a36Sopenharmony_ci * @mgid: multicast IP address as a gid 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Returns: mcg on success else ERR_PTR(error) 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistatic struct rxe_mcg *rxe_get_mcg(struct rxe_dev *rxe, union ib_gid *mgid) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct rxe_mcg *mcg, *tmp; 19162306a36Sopenharmony_ci int err; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (rxe->attr.max_mcast_grp == 0) 19462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* check to see if mcg already exists */ 19762306a36Sopenharmony_ci mcg = rxe_lookup_mcg(rxe, mgid); 19862306a36Sopenharmony_ci if (mcg) 19962306a36Sopenharmony_ci return mcg; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* check to see if we have reached limit */ 20262306a36Sopenharmony_ci if (atomic_inc_return(&rxe->mcg_num) > rxe->attr.max_mcast_grp) { 20362306a36Sopenharmony_ci err = -ENOMEM; 20462306a36Sopenharmony_ci goto err_dec; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* speculative alloc of new mcg */ 20862306a36Sopenharmony_ci mcg = kzalloc(sizeof(*mcg), GFP_KERNEL); 20962306a36Sopenharmony_ci if (!mcg) { 21062306a36Sopenharmony_ci err = -ENOMEM; 21162306a36Sopenharmony_ci goto err_dec; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci spin_lock_bh(&rxe->mcg_lock); 21562306a36Sopenharmony_ci /* re-check to see if someone else just added it */ 21662306a36Sopenharmony_ci tmp = __rxe_lookup_mcg(rxe, mgid); 21762306a36Sopenharmony_ci if (tmp) { 21862306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 21962306a36Sopenharmony_ci atomic_dec(&rxe->mcg_num); 22062306a36Sopenharmony_ci kfree(mcg); 22162306a36Sopenharmony_ci return tmp; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci __rxe_init_mcg(rxe, mgid, mcg); 22562306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* add mcast address outside of lock */ 22862306a36Sopenharmony_ci err = rxe_mcast_add(rxe, mgid); 22962306a36Sopenharmony_ci if (!err) 23062306a36Sopenharmony_ci return mcg; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci kfree(mcg); 23362306a36Sopenharmony_cierr_dec: 23462306a36Sopenharmony_ci atomic_dec(&rxe->mcg_num); 23562306a36Sopenharmony_ci return ERR_PTR(err); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/** 23962306a36Sopenharmony_ci * rxe_cleanup_mcg - cleanup mcg for kref_put 24062306a36Sopenharmony_ci * @kref: struct kref embnedded in mcg 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_civoid rxe_cleanup_mcg(struct kref *kref) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct rxe_mcg *mcg = container_of(kref, typeof(*mcg), ref_cnt); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci kfree(mcg); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * __rxe_destroy_mcg - destroy mcg object holding rxe->mcg_lock 25162306a36Sopenharmony_ci * @mcg: the mcg object 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * Context: caller is holding rxe->mcg_lock 25462306a36Sopenharmony_ci * no qp's are attached to mcg 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic void __rxe_destroy_mcg(struct rxe_mcg *mcg) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct rxe_dev *rxe = mcg->rxe; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* remove mcg from red-black tree then drop ref */ 26162306a36Sopenharmony_ci __rxe_remove_mcg(mcg); 26262306a36Sopenharmony_ci kref_put(&mcg->ref_cnt, rxe_cleanup_mcg); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci atomic_dec(&rxe->mcg_num); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/** 26862306a36Sopenharmony_ci * rxe_destroy_mcg - destroy mcg object 26962306a36Sopenharmony_ci * @mcg: the mcg object 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * Context: no qp's are attached to mcg 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_cistatic void rxe_destroy_mcg(struct rxe_mcg *mcg) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci /* delete mcast address outside of lock */ 27662306a36Sopenharmony_ci rxe_mcast_del(mcg->rxe, &mcg->mgid); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci spin_lock_bh(&mcg->rxe->mcg_lock); 27962306a36Sopenharmony_ci __rxe_destroy_mcg(mcg); 28062306a36Sopenharmony_ci spin_unlock_bh(&mcg->rxe->mcg_lock); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/** 28462306a36Sopenharmony_ci * __rxe_init_mca - initialize a new mca holding lock 28562306a36Sopenharmony_ci * @qp: qp object 28662306a36Sopenharmony_ci * @mcg: mcg object 28762306a36Sopenharmony_ci * @mca: empty space for new mca 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * Context: caller must hold references on qp and mcg, rxe->mcg_lock 29062306a36Sopenharmony_ci * and pass memory for new mca 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * Returns: 0 on success else an error 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_cistatic int __rxe_init_mca(struct rxe_qp *qp, struct rxe_mcg *mcg, 29562306a36Sopenharmony_ci struct rxe_mca *mca) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct rxe_dev *rxe = to_rdev(qp->ibqp.device); 29862306a36Sopenharmony_ci int n; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci n = atomic_inc_return(&rxe->mcg_attach); 30162306a36Sopenharmony_ci if (n > rxe->attr.max_total_mcast_qp_attach) { 30262306a36Sopenharmony_ci atomic_dec(&rxe->mcg_attach); 30362306a36Sopenharmony_ci return -ENOMEM; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci n = atomic_inc_return(&mcg->qp_num); 30762306a36Sopenharmony_ci if (n > rxe->attr.max_mcast_qp_attach) { 30862306a36Sopenharmony_ci atomic_dec(&mcg->qp_num); 30962306a36Sopenharmony_ci atomic_dec(&rxe->mcg_attach); 31062306a36Sopenharmony_ci return -ENOMEM; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci atomic_inc(&qp->mcg_num); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci rxe_get(qp); 31662306a36Sopenharmony_ci mca->qp = qp; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci list_add_tail(&mca->qp_list, &mcg->qp_list); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/** 32462306a36Sopenharmony_ci * rxe_attach_mcg - attach qp to mcg if not already attached 32562306a36Sopenharmony_ci * @qp: qp object 32662306a36Sopenharmony_ci * @mcg: mcg object 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * Context: caller must hold reference on qp and mcg. 32962306a36Sopenharmony_ci * Returns: 0 on success else an error 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic int rxe_attach_mcg(struct rxe_mcg *mcg, struct rxe_qp *qp) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct rxe_dev *rxe = mcg->rxe; 33462306a36Sopenharmony_ci struct rxe_mca *mca, *tmp; 33562306a36Sopenharmony_ci int err; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* check to see if the qp is already a member of the group */ 33862306a36Sopenharmony_ci spin_lock_bh(&rxe->mcg_lock); 33962306a36Sopenharmony_ci list_for_each_entry(mca, &mcg->qp_list, qp_list) { 34062306a36Sopenharmony_ci if (mca->qp == qp) { 34162306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* speculative alloc new mca without using GFP_ATOMIC */ 34862306a36Sopenharmony_ci mca = kzalloc(sizeof(*mca), GFP_KERNEL); 34962306a36Sopenharmony_ci if (!mca) 35062306a36Sopenharmony_ci return -ENOMEM; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci spin_lock_bh(&rxe->mcg_lock); 35362306a36Sopenharmony_ci /* re-check to see if someone else just attached qp */ 35462306a36Sopenharmony_ci list_for_each_entry(tmp, &mcg->qp_list, qp_list) { 35562306a36Sopenharmony_ci if (tmp->qp == qp) { 35662306a36Sopenharmony_ci kfree(mca); 35762306a36Sopenharmony_ci err = 0; 35862306a36Sopenharmony_ci goto out; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci err = __rxe_init_mca(qp, mcg, mca); 36362306a36Sopenharmony_ci if (err) 36462306a36Sopenharmony_ci kfree(mca); 36562306a36Sopenharmony_ciout: 36662306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 36762306a36Sopenharmony_ci return err; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/** 37162306a36Sopenharmony_ci * __rxe_cleanup_mca - cleanup mca object holding lock 37262306a36Sopenharmony_ci * @mca: mca object 37362306a36Sopenharmony_ci * @mcg: mcg object 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci * Context: caller must hold a reference to mcg and rxe->mcg_lock 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_cistatic void __rxe_cleanup_mca(struct rxe_mca *mca, struct rxe_mcg *mcg) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci list_del(&mca->qp_list); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci atomic_dec(&mcg->qp_num); 38262306a36Sopenharmony_ci atomic_dec(&mcg->rxe->mcg_attach); 38362306a36Sopenharmony_ci atomic_dec(&mca->qp->mcg_num); 38462306a36Sopenharmony_ci rxe_put(mca->qp); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci kfree(mca); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** 39062306a36Sopenharmony_ci * rxe_detach_mcg - detach qp from mcg 39162306a36Sopenharmony_ci * @mcg: mcg object 39262306a36Sopenharmony_ci * @qp: qp object 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Returns: 0 on success else an error if qp is not attached. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_cistatic int rxe_detach_mcg(struct rxe_mcg *mcg, struct rxe_qp *qp) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct rxe_dev *rxe = mcg->rxe; 39962306a36Sopenharmony_ci struct rxe_mca *mca, *tmp; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci spin_lock_bh(&rxe->mcg_lock); 40262306a36Sopenharmony_ci list_for_each_entry_safe(mca, tmp, &mcg->qp_list, qp_list) { 40362306a36Sopenharmony_ci if (mca->qp == qp) { 40462306a36Sopenharmony_ci __rxe_cleanup_mca(mca, mcg); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* if the number of qp's attached to the 40762306a36Sopenharmony_ci * mcast group falls to zero go ahead and 40862306a36Sopenharmony_ci * tear it down. This will not free the 40962306a36Sopenharmony_ci * object since we are still holding a ref 41062306a36Sopenharmony_ci * from the caller 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci if (atomic_read(&mcg->qp_num) <= 0) 41362306a36Sopenharmony_ci __rxe_destroy_mcg(mcg); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* we didn't find the qp on the list */ 42162306a36Sopenharmony_ci spin_unlock_bh(&rxe->mcg_lock); 42262306a36Sopenharmony_ci return -EINVAL; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/** 42662306a36Sopenharmony_ci * rxe_attach_mcast - attach qp to multicast group (see IBA-11.3.1) 42762306a36Sopenharmony_ci * @ibqp: (IB) qp object 42862306a36Sopenharmony_ci * @mgid: multicast IP address 42962306a36Sopenharmony_ci * @mlid: multicast LID, ignored for RoCEv2 (see IBA-A17.5.6) 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * Returns: 0 on success else an errno 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ciint rxe_attach_mcast(struct ib_qp *ibqp, union ib_gid *mgid, u16 mlid) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci int err; 43662306a36Sopenharmony_ci struct rxe_dev *rxe = to_rdev(ibqp->device); 43762306a36Sopenharmony_ci struct rxe_qp *qp = to_rqp(ibqp); 43862306a36Sopenharmony_ci struct rxe_mcg *mcg; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* takes a ref on mcg if successful */ 44162306a36Sopenharmony_ci mcg = rxe_get_mcg(rxe, mgid); 44262306a36Sopenharmony_ci if (IS_ERR(mcg)) 44362306a36Sopenharmony_ci return PTR_ERR(mcg); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci err = rxe_attach_mcg(mcg, qp); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* if we failed to attach the first qp to mcg tear it down */ 44862306a36Sopenharmony_ci if (atomic_read(&mcg->qp_num) == 0) 44962306a36Sopenharmony_ci rxe_destroy_mcg(mcg); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci kref_put(&mcg->ref_cnt, rxe_cleanup_mcg); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return err; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/** 45762306a36Sopenharmony_ci * rxe_detach_mcast - detach qp from multicast group (see IBA-11.3.2) 45862306a36Sopenharmony_ci * @ibqp: address of (IB) qp object 45962306a36Sopenharmony_ci * @mgid: multicast IP address 46062306a36Sopenharmony_ci * @mlid: multicast LID, ignored for RoCEv2 (see IBA-A17.5.6) 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * Returns: 0 on success else an errno 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ciint rxe_detach_mcast(struct ib_qp *ibqp, union ib_gid *mgid, u16 mlid) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct rxe_dev *rxe = to_rdev(ibqp->device); 46762306a36Sopenharmony_ci struct rxe_qp *qp = to_rqp(ibqp); 46862306a36Sopenharmony_ci struct rxe_mcg *mcg; 46962306a36Sopenharmony_ci int err; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci mcg = rxe_lookup_mcg(rxe, mgid); 47262306a36Sopenharmony_ci if (!mcg) 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci err = rxe_detach_mcg(mcg, qp); 47662306a36Sopenharmony_ci kref_put(&mcg->ref_cnt, rxe_cleanup_mcg); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return err; 47962306a36Sopenharmony_ci} 480