1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2018 Mellanox Technologies */
3
4 #include <linux/mlx5/vport.h>
5 #include "lib/devcom.h"
6 #include "mlx5_core.h"
7
8 static LIST_HEAD(devcom_list);
9
10 #define devcom_for_each_component(priv, comp, iter) \
11 for (iter = 0; \
12 comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \
13 iter++)
14
15 struct mlx5_devcom_component {
16 struct {
17 void __rcu *data;
18 } device[MLX5_DEVCOM_PORTS_SUPPORTED];
19
20 mlx5_devcom_event_handler_t handler;
21 struct rw_semaphore sem;
22 bool paired;
23 };
24
25 struct mlx5_devcom_list {
26 struct list_head list;
27
28 struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS];
29 struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED];
30 };
31
32 struct mlx5_devcom {
33 struct mlx5_devcom_list *priv;
34 int idx;
35 };
36
mlx5_devcom_list_alloc(void)37 static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void)
38 {
39 struct mlx5_devcom_component *comp;
40 struct mlx5_devcom_list *priv;
41 int i;
42
43 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
44 if (!priv)
45 return NULL;
46
47 devcom_for_each_component(priv, comp, i)
48 init_rwsem(&comp->sem);
49
50 return priv;
51 }
52
mlx5_devcom_alloc(struct mlx5_devcom_list * priv,u8 idx)53 static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv,
54 u8 idx)
55 {
56 struct mlx5_devcom *devcom;
57
58 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
59 if (!devcom)
60 return NULL;
61
62 devcom->priv = priv;
63 devcom->idx = idx;
64 return devcom;
65 }
66
67 /* Must be called with intf_mutex held */
mlx5_devcom_register_device(struct mlx5_core_dev * dev)68 struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)
69 {
70 struct mlx5_devcom_list *priv = NULL, *iter;
71 struct mlx5_devcom *devcom = NULL;
72 bool new_priv = false;
73 u64 sguid0, sguid1;
74 int idx, i;
75
76 if (!mlx5_core_is_pf(dev))
77 return NULL;
78 if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED)
79 return NULL;
80
81 mlx5_dev_list_lock();
82 sguid0 = mlx5_query_nic_system_image_guid(dev);
83 list_for_each_entry(iter, &devcom_list, list) {
84 struct mlx5_core_dev *tmp_dev = NULL;
85
86 idx = -1;
87 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
88 if (iter->devs[i])
89 tmp_dev = iter->devs[i];
90 else
91 idx = i;
92 }
93
94 if (idx == -1)
95 continue;
96
97 sguid1 = mlx5_query_nic_system_image_guid(tmp_dev);
98 if (sguid0 != sguid1)
99 continue;
100
101 priv = iter;
102 break;
103 }
104
105 if (!priv) {
106 priv = mlx5_devcom_list_alloc();
107 if (!priv) {
108 devcom = ERR_PTR(-ENOMEM);
109 goto out;
110 }
111
112 idx = 0;
113 new_priv = true;
114 }
115
116 priv->devs[idx] = dev;
117 devcom = mlx5_devcom_alloc(priv, idx);
118 if (!devcom) {
119 if (new_priv)
120 kfree(priv);
121 devcom = ERR_PTR(-ENOMEM);
122 goto out;
123 }
124
125 if (new_priv)
126 list_add(&priv->list, &devcom_list);
127 out:
128 mlx5_dev_list_unlock();
129 return devcom;
130 }
131
132 /* Must be called with intf_mutex held */
mlx5_devcom_unregister_device(struct mlx5_devcom * devcom)133 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
134 {
135 struct mlx5_devcom_list *priv;
136 int i;
137
138 if (IS_ERR_OR_NULL(devcom))
139 return;
140
141 mlx5_dev_list_lock();
142 priv = devcom->priv;
143 priv->devs[devcom->idx] = NULL;
144
145 kfree(devcom);
146
147 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
148 if (priv->devs[i])
149 break;
150
151 if (i != MLX5_DEVCOM_PORTS_SUPPORTED)
152 goto out;
153
154 list_del(&priv->list);
155 kfree(priv);
156 out:
157 mlx5_dev_list_unlock();
158 }
159
mlx5_devcom_register_component(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,mlx5_devcom_event_handler_t handler,void * data)160 void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
161 enum mlx5_devcom_components id,
162 mlx5_devcom_event_handler_t handler,
163 void *data)
164 {
165 struct mlx5_devcom_component *comp;
166
167 if (IS_ERR_OR_NULL(devcom))
168 return;
169
170 WARN_ON(!data);
171
172 comp = &devcom->priv->components[id];
173 down_write(&comp->sem);
174 comp->handler = handler;
175 rcu_assign_pointer(comp->device[devcom->idx].data, data);
176 up_write(&comp->sem);
177 }
178
mlx5_devcom_unregister_component(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)179 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
180 enum mlx5_devcom_components id)
181 {
182 struct mlx5_devcom_component *comp;
183
184 if (IS_ERR_OR_NULL(devcom))
185 return;
186
187 comp = &devcom->priv->components[id];
188 down_write(&comp->sem);
189 RCU_INIT_POINTER(comp->device[devcom->idx].data, NULL);
190 up_write(&comp->sem);
191 synchronize_rcu();
192 }
193
mlx5_devcom_send_event(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,int event,void * event_data)194 int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
195 enum mlx5_devcom_components id,
196 int event,
197 void *event_data)
198 {
199 struct mlx5_devcom_component *comp;
200 int err = -ENODEV, i;
201
202 if (IS_ERR_OR_NULL(devcom))
203 return err;
204
205 comp = &devcom->priv->components[id];
206 down_write(&comp->sem);
207 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
208 void *data = rcu_dereference_protected(comp->device[i].data,
209 lockdep_is_held(&comp->sem));
210
211 if (i != devcom->idx && data) {
212 err = comp->handler(event, data, event_data);
213 break;
214 }
215 }
216
217 up_write(&comp->sem);
218 return err;
219 }
220
mlx5_devcom_set_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,bool paired)221 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
222 enum mlx5_devcom_components id,
223 bool paired)
224 {
225 struct mlx5_devcom_component *comp;
226
227 comp = &devcom->priv->components[id];
228 WARN_ON(!rwsem_is_locked(&comp->sem));
229
230 WRITE_ONCE(comp->paired, paired);
231 }
232
mlx5_devcom_is_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)233 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
234 enum mlx5_devcom_components id)
235 {
236 if (IS_ERR_OR_NULL(devcom))
237 return false;
238
239 return READ_ONCE(devcom->priv->components[id].paired);
240 }
241
mlx5_devcom_get_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)242 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
243 enum mlx5_devcom_components id)
244 {
245 struct mlx5_devcom_component *comp;
246 int i;
247
248 if (IS_ERR_OR_NULL(devcom))
249 return NULL;
250
251 comp = &devcom->priv->components[id];
252 down_read(&comp->sem);
253 if (!READ_ONCE(comp->paired)) {
254 up_read(&comp->sem);
255 return NULL;
256 }
257
258 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
259 if (i != devcom->idx)
260 break;
261
262 return rcu_dereference_protected(comp->device[i].data, lockdep_is_held(&comp->sem));
263 }
264
mlx5_devcom_get_peer_data_rcu(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)265 void *mlx5_devcom_get_peer_data_rcu(struct mlx5_devcom *devcom, enum mlx5_devcom_components id)
266 {
267 struct mlx5_devcom_component *comp;
268 int i;
269
270 if (IS_ERR_OR_NULL(devcom))
271 return NULL;
272
273 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
274 if (i != devcom->idx)
275 break;
276
277 comp = &devcom->priv->components[id];
278 /* This can change concurrently, however 'data' pointer will remain
279 * valid for the duration of RCU read section.
280 */
281 if (!READ_ONCE(comp->paired))
282 return NULL;
283
284 return rcu_dereference(comp->device[i].data);
285 }
286
mlx5_devcom_release_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)287 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
288 enum mlx5_devcom_components id)
289 {
290 struct mlx5_devcom_component *comp = &devcom->priv->components[id];
291
292 up_read(&comp->sem);
293 }
294