18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
48c2ecf20Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "rxe.h"
88c2ecf20Sopenharmony_ci#include "rxe_loc.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciint rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid,
118c2ecf20Sopenharmony_ci		      struct rxe_mc_grp **grp_p)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	int err;
148c2ecf20Sopenharmony_ci	struct rxe_mc_grp *grp;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	if (rxe->attr.max_mcast_qp_attach == 0) {
178c2ecf20Sopenharmony_ci		err = -EINVAL;
188c2ecf20Sopenharmony_ci		goto err1;
198c2ecf20Sopenharmony_ci	}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
228c2ecf20Sopenharmony_ci	if (grp)
238c2ecf20Sopenharmony_ci		goto done;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	grp = rxe_alloc(&rxe->mc_grp_pool);
268c2ecf20Sopenharmony_ci	if (!grp) {
278c2ecf20Sopenharmony_ci		err = -ENOMEM;
288c2ecf20Sopenharmony_ci		goto err1;
298c2ecf20Sopenharmony_ci	}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&grp->qp_list);
328c2ecf20Sopenharmony_ci	spin_lock_init(&grp->mcg_lock);
338c2ecf20Sopenharmony_ci	grp->rxe = rxe;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	rxe_add_key(grp, mgid);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	err = rxe_mcast_add(rxe, mgid);
388c2ecf20Sopenharmony_ci	if (err)
398c2ecf20Sopenharmony_ci		goto err2;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cidone:
428c2ecf20Sopenharmony_ci	*grp_p = grp;
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cierr2:
468c2ecf20Sopenharmony_ci	rxe_drop_ref(grp);
478c2ecf20Sopenharmony_cierr1:
488c2ecf20Sopenharmony_ci	return err;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciint rxe_mcast_add_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
528c2ecf20Sopenharmony_ci			   struct rxe_mc_grp *grp)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int err;
558c2ecf20Sopenharmony_ci	struct rxe_mc_elem *elem;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* check to see of the qp is already a member of the group */
588c2ecf20Sopenharmony_ci	spin_lock_bh(&qp->grp_lock);
598c2ecf20Sopenharmony_ci	spin_lock_bh(&grp->mcg_lock);
608c2ecf20Sopenharmony_ci	list_for_each_entry(elem, &grp->qp_list, qp_list) {
618c2ecf20Sopenharmony_ci		if (elem->qp == qp) {
628c2ecf20Sopenharmony_ci			err = 0;
638c2ecf20Sopenharmony_ci			goto out;
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (grp->num_qp >= rxe->attr.max_mcast_qp_attach) {
688c2ecf20Sopenharmony_ci		err = -ENOMEM;
698c2ecf20Sopenharmony_ci		goto out;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	elem = rxe_alloc(&rxe->mc_elem_pool);
738c2ecf20Sopenharmony_ci	if (!elem) {
748c2ecf20Sopenharmony_ci		err = -ENOMEM;
758c2ecf20Sopenharmony_ci		goto out;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* each qp holds a ref on the grp */
798c2ecf20Sopenharmony_ci	rxe_add_ref(grp);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	grp->num_qp++;
828c2ecf20Sopenharmony_ci	elem->qp = qp;
838c2ecf20Sopenharmony_ci	elem->grp = grp;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	list_add(&elem->qp_list, &grp->qp_list);
868c2ecf20Sopenharmony_ci	list_add(&elem->grp_list, &qp->grp_list);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	err = 0;
898c2ecf20Sopenharmony_ciout:
908c2ecf20Sopenharmony_ci	spin_unlock_bh(&grp->mcg_lock);
918c2ecf20Sopenharmony_ci	spin_unlock_bh(&qp->grp_lock);
928c2ecf20Sopenharmony_ci	return err;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ciint rxe_mcast_drop_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
968c2ecf20Sopenharmony_ci			    union ib_gid *mgid)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct rxe_mc_grp *grp;
998c2ecf20Sopenharmony_ci	struct rxe_mc_elem *elem, *tmp;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
1028c2ecf20Sopenharmony_ci	if (!grp)
1038c2ecf20Sopenharmony_ci		goto err1;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	spin_lock_bh(&qp->grp_lock);
1068c2ecf20Sopenharmony_ci	spin_lock_bh(&grp->mcg_lock);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	list_for_each_entry_safe(elem, tmp, &grp->qp_list, qp_list) {
1098c2ecf20Sopenharmony_ci		if (elem->qp == qp) {
1108c2ecf20Sopenharmony_ci			list_del(&elem->qp_list);
1118c2ecf20Sopenharmony_ci			list_del(&elem->grp_list);
1128c2ecf20Sopenharmony_ci			grp->num_qp--;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci			spin_unlock_bh(&grp->mcg_lock);
1158c2ecf20Sopenharmony_ci			spin_unlock_bh(&qp->grp_lock);
1168c2ecf20Sopenharmony_ci			rxe_drop_ref(elem);
1178c2ecf20Sopenharmony_ci			rxe_drop_ref(grp);	/* ref held by QP */
1188c2ecf20Sopenharmony_ci			rxe_drop_ref(grp);	/* ref from get_key */
1198c2ecf20Sopenharmony_ci			return 0;
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	spin_unlock_bh(&grp->mcg_lock);
1248c2ecf20Sopenharmony_ci	spin_unlock_bh(&qp->grp_lock);
1258c2ecf20Sopenharmony_ci	rxe_drop_ref(grp);			/* ref from get_key */
1268c2ecf20Sopenharmony_cierr1:
1278c2ecf20Sopenharmony_ci	return -EINVAL;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid rxe_drop_all_mcast_groups(struct rxe_qp *qp)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct rxe_mc_grp *grp;
1338c2ecf20Sopenharmony_ci	struct rxe_mc_elem *elem;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	while (1) {
1368c2ecf20Sopenharmony_ci		spin_lock_bh(&qp->grp_lock);
1378c2ecf20Sopenharmony_ci		if (list_empty(&qp->grp_list)) {
1388c2ecf20Sopenharmony_ci			spin_unlock_bh(&qp->grp_lock);
1398c2ecf20Sopenharmony_ci			break;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci		elem = list_first_entry(&qp->grp_list, struct rxe_mc_elem,
1428c2ecf20Sopenharmony_ci					grp_list);
1438c2ecf20Sopenharmony_ci		list_del(&elem->grp_list);
1448c2ecf20Sopenharmony_ci		spin_unlock_bh(&qp->grp_lock);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		grp = elem->grp;
1478c2ecf20Sopenharmony_ci		spin_lock_bh(&grp->mcg_lock);
1488c2ecf20Sopenharmony_ci		list_del(&elem->qp_list);
1498c2ecf20Sopenharmony_ci		grp->num_qp--;
1508c2ecf20Sopenharmony_ci		spin_unlock_bh(&grp->mcg_lock);
1518c2ecf20Sopenharmony_ci		rxe_drop_ref(grp);
1528c2ecf20Sopenharmony_ci		rxe_drop_ref(elem);
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_civoid rxe_mc_cleanup(struct rxe_pool_entry *arg)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct rxe_mc_grp *grp = container_of(arg, typeof(*grp), pelem);
1598c2ecf20Sopenharmony_ci	struct rxe_dev *rxe = grp->rxe;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	rxe_drop_key(grp);
1628c2ecf20Sopenharmony_ci	rxe_mcast_delete(rxe, &grp->mgid);
1638c2ecf20Sopenharmony_ci}
164