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 *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
mlx5_devcom_list_alloc(void)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
mlx5_devcom_alloc(struct mlx5_devcom_list * priv,u8 idx)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 */
mlx5_devcom_register_device(struct mlx5_core_dev * dev)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 kfree(priv);
116 return ERR_PTR(-ENOMEM);
117 }
118
119 if (new_priv)
120 list_add(&priv->list, &devcom_list);
121
122 return devcom;
123 }
124
125 /* Must be called with intf_mutex held */
mlx5_devcom_unregister_device(struct mlx5_devcom * devcom)126 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
127 {
128 struct mlx5_devcom_list *priv;
129 int i;
130
131 if (IS_ERR_OR_NULL(devcom))
132 return;
133
134 priv = devcom->priv;
135 priv->devs[devcom->idx] = NULL;
136
137 kfree(devcom);
138
139 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
140 if (priv->devs[i])
141 break;
142
143 if (i != MLX5_DEVCOM_PORTS_SUPPORTED)
144 return;
145
146 list_del(&priv->list);
147 kfree(priv);
148 }
149
mlx5_devcom_register_component(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,mlx5_devcom_event_handler_t handler,void * data)150 void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
151 enum mlx5_devcom_components id,
152 mlx5_devcom_event_handler_t handler,
153 void *data)
154 {
155 struct mlx5_devcom_component *comp;
156
157 if (IS_ERR_OR_NULL(devcom))
158 return;
159
160 WARN_ON(!data);
161
162 comp = &devcom->priv->components[id];
163 down_write(&comp->sem);
164 comp->handler = handler;
165 comp->device[devcom->idx].data = data;
166 up_write(&comp->sem);
167 }
168
mlx5_devcom_unregister_component(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)169 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
170 enum mlx5_devcom_components id)
171 {
172 struct mlx5_devcom_component *comp;
173
174 if (IS_ERR_OR_NULL(devcom))
175 return;
176
177 comp = &devcom->priv->components[id];
178 down_write(&comp->sem);
179 comp->device[devcom->idx].data = NULL;
180 up_write(&comp->sem);
181 }
182
mlx5_devcom_send_event(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,int event,void * event_data)183 int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
184 enum mlx5_devcom_components id,
185 int event,
186 void *event_data)
187 {
188 struct mlx5_devcom_component *comp;
189 int err = -ENODEV, i;
190
191 if (IS_ERR_OR_NULL(devcom))
192 return err;
193
194 comp = &devcom->priv->components[id];
195 down_write(&comp->sem);
196 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
197 if (i != devcom->idx && comp->device[i].data) {
198 err = comp->handler(event, comp->device[i].data,
199 event_data);
200 break;
201 }
202
203 up_write(&comp->sem);
204 return err;
205 }
206
mlx5_devcom_set_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id,bool paired)207 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
208 enum mlx5_devcom_components id,
209 bool paired)
210 {
211 struct mlx5_devcom_component *comp;
212
213 comp = &devcom->priv->components[id];
214 WARN_ON(!rwsem_is_locked(&comp->sem));
215
216 comp->paired = paired;
217 }
218
mlx5_devcom_is_paired(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)219 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
220 enum mlx5_devcom_components id)
221 {
222 if (IS_ERR_OR_NULL(devcom))
223 return false;
224
225 return devcom->priv->components[id].paired;
226 }
227
mlx5_devcom_get_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)228 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
229 enum mlx5_devcom_components id)
230 {
231 struct mlx5_devcom_component *comp;
232 int i;
233
234 if (IS_ERR_OR_NULL(devcom))
235 return NULL;
236
237 comp = &devcom->priv->components[id];
238 down_read(&comp->sem);
239 if (!comp->paired) {
240 up_read(&comp->sem);
241 return NULL;
242 }
243
244 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
245 if (i != devcom->idx)
246 break;
247
248 return comp->device[i].data;
249 }
250
mlx5_devcom_release_peer_data(struct mlx5_devcom * devcom,enum mlx5_devcom_components id)251 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
252 enum mlx5_devcom_components id)
253 {
254 struct mlx5_devcom_component *comp = &devcom->priv->components[id];
255
256 up_read(&comp->sem);
257 }
258