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