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 /* caller should hold mc_grp_pool->pool_lock */
11 static struct rxe_mc_grp *create_grp(struct rxe_dev *rxe,
12 				     struct rxe_pool *pool,
13 				     union ib_gid *mgid)
14 {
15 	int err;
16 	struct rxe_mc_grp *grp;
17 
18 	grp = rxe_alloc_locked(&rxe->mc_grp_pool);
19 	if (!grp)
20 		return ERR_PTR(-ENOMEM);
21 
22 	INIT_LIST_HEAD(&grp->qp_list);
23 	spin_lock_init(&grp->mcg_lock);
24 	grp->rxe = rxe;
25 	rxe_add_key_locked(grp, mgid);
26 
27 	err = rxe_mcast_add(rxe, mgid);
28 	if (unlikely(err)) {
29 		rxe_drop_key_locked(grp);
30 		rxe_drop_ref(grp);
31 		return ERR_PTR(err);
32 	}
33 
34 	return grp;
35 }
36 
37 int rxe_mcast_get_grp(struct rxe_dev *rxe, union ib_gid *mgid,
38 		      struct rxe_mc_grp **grp_p)
39 {
40 	int err;
41 	struct rxe_mc_grp *grp;
42 	struct rxe_pool *pool = &rxe->mc_grp_pool;
43 
44 	if (rxe->attr.max_mcast_qp_attach == 0)
45 		return -EINVAL;
46 
47 	write_lock_bh(&pool->pool_lock);
48 
49 	grp = rxe_pool_get_key_locked(pool, mgid);
50 	if (grp)
51 		goto done;
52 
53 	grp = create_grp(rxe, pool, mgid);
54 	if (IS_ERR(grp)) {
55 		write_unlock_bh(&pool->pool_lock);
56 		err = PTR_ERR(grp);
57 		return err;
58 	}
59 
60 done:
61 	write_unlock_bh(&pool->pool_lock);
62 	*grp_p = grp;
63 	return 0;
64 }
65 
66 int rxe_mcast_add_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
67 			   struct rxe_mc_grp *grp)
68 {
69 	int err;
70 	struct rxe_mc_elem *elem;
71 
72 	/* check to see of the qp is already a member of the group */
73 	spin_lock_bh(&qp->grp_lock);
74 	spin_lock_bh(&grp->mcg_lock);
75 	list_for_each_entry(elem, &grp->qp_list, qp_list) {
76 		if (elem->qp == qp) {
77 			err = 0;
78 			goto out;
79 		}
80 	}
81 
82 	if (grp->num_qp >= rxe->attr.max_mcast_qp_attach) {
83 		err = -ENOMEM;
84 		goto out;
85 	}
86 
87 	elem = rxe_alloc_locked(&rxe->mc_elem_pool);
88 	if (!elem) {
89 		err = -ENOMEM;
90 		goto out;
91 	}
92 
93 	/* each qp holds a ref on the grp */
94 	rxe_add_ref(grp);
95 
96 	grp->num_qp++;
97 	elem->qp = qp;
98 	elem->grp = grp;
99 
100 	list_add(&elem->qp_list, &grp->qp_list);
101 	list_add(&elem->grp_list, &qp->grp_list);
102 
103 	err = 0;
104 out:
105 	spin_unlock_bh(&grp->mcg_lock);
106 	spin_unlock_bh(&qp->grp_lock);
107 	return err;
108 }
109 
110 int rxe_mcast_drop_grp_elem(struct rxe_dev *rxe, struct rxe_qp *qp,
111 			    union ib_gid *mgid)
112 {
113 	struct rxe_mc_grp *grp;
114 	struct rxe_mc_elem *elem, *tmp;
115 
116 	grp = rxe_pool_get_key(&rxe->mc_grp_pool, mgid);
117 	if (!grp)
118 		goto err1;
119 
120 	spin_lock_bh(&qp->grp_lock);
121 	spin_lock_bh(&grp->mcg_lock);
122 
123 	list_for_each_entry_safe(elem, tmp, &grp->qp_list, qp_list) {
124 		if (elem->qp == qp) {
125 			list_del(&elem->qp_list);
126 			list_del(&elem->grp_list);
127 			grp->num_qp--;
128 
129 			spin_unlock_bh(&grp->mcg_lock);
130 			spin_unlock_bh(&qp->grp_lock);
131 			rxe_drop_ref(elem);
132 			rxe_drop_ref(grp);	/* ref held by QP */
133 			rxe_drop_ref(grp);	/* ref from get_key */
134 			return 0;
135 		}
136 	}
137 
138 	spin_unlock_bh(&grp->mcg_lock);
139 	spin_unlock_bh(&qp->grp_lock);
140 	rxe_drop_ref(grp);			/* ref from get_key */
141 err1:
142 	return -EINVAL;
143 }
144 
145 void rxe_drop_all_mcast_groups(struct rxe_qp *qp)
146 {
147 	struct rxe_mc_grp *grp;
148 	struct rxe_mc_elem *elem;
149 
150 	while (1) {
151 		spin_lock_bh(&qp->grp_lock);
152 		if (list_empty(&qp->grp_list)) {
153 			spin_unlock_bh(&qp->grp_lock);
154 			break;
155 		}
156 		elem = list_first_entry(&qp->grp_list, struct rxe_mc_elem,
157 					grp_list);
158 		list_del(&elem->grp_list);
159 		spin_unlock_bh(&qp->grp_lock);
160 
161 		grp = elem->grp;
162 		spin_lock_bh(&grp->mcg_lock);
163 		list_del(&elem->qp_list);
164 		grp->num_qp--;
165 		spin_unlock_bh(&grp->mcg_lock);
166 		rxe_drop_ref(grp);
167 		rxe_drop_ref(elem);
168 	}
169 }
170 
171 void rxe_mc_cleanup(struct rxe_pool_elem *elem)
172 {
173 	struct rxe_mc_grp *grp = container_of(elem, typeof(*grp), elem);
174 	struct rxe_dev *rxe = grp->rxe;
175 
176 	rxe_drop_key(grp);
177 	rxe_mcast_delete(rxe, &grp->mgid);
178 }
179