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