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