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 *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 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 comp->device[devcom->idx].data = NULL;
190 up_write(&comp->sem);
191 }
192
mlx5_devcom_send_event(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,int event,void * event_data)193 int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
194 enum mlx5_devcom_components id,
195 int event,
196 void *event_data)
197 {
198 struct mlx5_devcom_component *comp;
199 int err = -ENODEV, i;
200
201 if (IS_ERR_OR_NULL(devcom))
202 return err;
203
204 comp = &devcom->priv->components[id];
205 down_write(&comp->sem);
206 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
207 if (i != devcom->idx && comp->device[i].data) {
208 err = comp->handler(event, comp->device[i].data,
209 event_data);
210 break;
211 }
212
213 up_write(&comp->sem);
214 return err;
215 }
216
mlx5_devcom_set_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,bool paired)217 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
218 enum mlx5_devcom_components id,
219 bool paired)
220 {
221 struct mlx5_devcom_component *comp;
222
223 comp = &devcom->priv->components[id];
224 WARN_ON(!rwsem_is_locked(&comp->sem));
225
226 comp->paired = paired;
227 }
228
mlx5_devcom_is_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)229 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
230 enum mlx5_devcom_components id)
231 {
232 if (IS_ERR_OR_NULL(devcom))
233 return false;
234
235 return devcom->priv->components[id].paired;
236 }
237
mlx5_devcom_get_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)238 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
239 enum mlx5_devcom_components id)
240 {
241 struct mlx5_devcom_component *comp;
242 int i;
243
244 if (IS_ERR_OR_NULL(devcom))
245 return NULL;
246
247 comp = &devcom->priv->components[id];
248 down_read(&comp->sem);
249 if (!comp->paired) {
250 up_read(&comp->sem);
251 return NULL;
252 }
253
254 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
255 if (i != devcom->idx)
256 break;
257
258 return comp->device[i].data;
259 }
260
mlx5_devcom_release_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)261 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
262 enum mlx5_devcom_components id)
263 {
264 struct mlx5_devcom_component *comp = &devcom->priv->components[id];
265
266 up_read(&comp->sem);
267 }
268