1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /*
3  * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
4  * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
5  */
6 
7 #include "rxe.h"
8 #include "rxe_loc.h"
9 
10 int rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid,
11 		      struct rxe_mc_grp **grp_p)
12 {
13 	int err;
14 	struct rxe_mc_grp *grp;
15 
16 	if (rxe->attr.max_mcast_qp_attach == 0) {
17 		err = -EINVAL;
18 		goto err1;
19 	}
20 
21 	grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
22 	if (grp)
23 		goto done;
24 
25 	grp = rxe_alloc(&rxe->mc_grp_pool);
26 	if (!grp) {
27 		err = -ENOMEM;
28 		goto err1;
29 	}
30 
31 	INIT_LIST_HEAD(&grp->qp_list);
32 	spin_lock_init(&grp->mcg_lock);
33 	grp->rxe = rxe;
34 
35 	rxe_add_key(grp, mgid);
36 
37 	err = rxe_mcast_add(rxe, mgid);
38 	if (err)
39 		goto err2;
40 
41 done:
42 	*grp_p = grp;
43 	return 0;
44 
45 err2:
46 	rxe_drop_ref(grp);
47 err1:
48 	return err;
49 }
50 
51 int rxe_mcast_add_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
52 			   struct rxe_mc_grp *grp)
53 {
54 	int err;
55 	struct rxe_mc_elem *elem;
56 
57 	/* check to see of the qp is already a member of the group */
58 	spin_lock_bh(&qp->grp_lock);
59 	spin_lock_bh(&grp->mcg_lock);
60 	list_for_each_entry(elem, &grp->qp_list, qp_list) {
61 		if (elem->qp == qp) {
62 			err = 0;
63 			goto out;
64 		}
65 	}
66 
67 	if (grp->num_qp >= rxe->attr.max_mcast_qp_attach) {
68 		err = -ENOMEM;
69 		goto out;
70 	}
71 
72 	elem = rxe_alloc(&rxe->mc_elem_pool);
73 	if (!elem) {
74 		err = -ENOMEM;
75 		goto out;
76 	}
77 
78 	/* each qp holds a ref on the grp */
79 	rxe_add_ref(grp);
80 
81 	grp->num_qp++;
82 	elem->qp = qp;
83 	elem->grp = grp;
84 
85 	list_add(&elem->qp_list, &grp->qp_list);
86 	list_add(&elem->grp_list, &qp->grp_list);
87 
88 	err = 0;
89 out:
90 	spin_unlock_bh(&grp->mcg_lock);
91 	spin_unlock_bh(&qp->grp_lock);
92 	return err;
93 }
94 
95 int rxe_mcast_drop_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
96 			    union ib_gid *mgid)
97 {
98 	struct rxe_mc_grp *grp;
99 	struct rxe_mc_elem *elem, *tmp;
100 
101 	grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
102 	if (!grp)
103 		goto err1;
104 
105 	spin_lock_bh(&qp->grp_lock);
106 	spin_lock_bh(&grp->mcg_lock);
107 
108 	list_for_each_entry_safe(elem, tmp, &grp->qp_list, qp_list) {
109 		if (elem->qp == qp) {
110 			list_del(&elem->qp_list);
111 			list_del(&elem->grp_list);
112 			grp->num_qp--;
113 
114 			spin_unlock_bh(&grp->mcg_lock);
115 			spin_unlock_bh(&qp->grp_lock);
116 			rxe_drop_ref(elem);
117 			rxe_drop_ref(grp);	/* ref held by QP */
118 			rxe_drop_ref(grp);	/* ref from get_key */
119 			return 0;
120 		}
121 	}
122 
123 	spin_unlock_bh(&grp->mcg_lock);
124 	spin_unlock_bh(&qp->grp_lock);
125 	rxe_drop_ref(grp);			/* ref from get_key */
126 err1:
127 	return -EINVAL;
128 }
129 
130 void rxe_drop_all_mcast_groups(struct rxe_qp *qp)
131 {
132 	struct rxe_mc_grp *grp;
133 	struct rxe_mc_elem *elem;
134 
135 	while (1) {
136 		spin_lock_bh(&qp->grp_lock);
137 		if (list_empty(&qp->grp_list)) {
138 			spin_unlock_bh(&qp->grp_lock);
139 			break;
140 		}
141 		elem = list_first_entry(&qp->grp_list, struct rxe_mc_elem,
142 					grp_list);
143 		list_del(&elem->grp_list);
144 		spin_unlock_bh(&qp->grp_lock);
145 
146 		grp = elem->grp;
147 		spin_lock_bh(&grp->mcg_lock);
148 		list_del(&elem->qp_list);
149 		grp->num_qp--;
150 		spin_unlock_bh(&grp->mcg_lock);
151 		rxe_drop_ref(grp);
152 		rxe_drop_ref(elem);
153 	}
154 }
155 
156 void rxe_mc_cleanup(struct rxe_pool_entry *arg)
157 {
158 	struct rxe_mc_grp *grp = container_of(arg, typeof(*grp), pelem);
159 	struct rxe_dev *rxe = grp->rxe;
160 
161 	rxe_drop_key(grp);
162 	rxe_mcast_delete(rxe, &grp->mgid);
163 }
164