]> git.itanic.dy.fi Git - linux-stable/blob - drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
net/mlx5: Devcom, serialize devcom registration
[linux-stable] / drivers / net / ethernet / mellanox / mlx5 / core / lib / devcom.c
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
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
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 */
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 */
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
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
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
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
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
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
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
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
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 }