1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2018 Mellanox Technologies */
4 #include <linux/mlx5/vport.h>
5 #include "lib/devcom.h"
8 static LIST_HEAD(devcom_list);
10 #define devcom_for_each_component(priv, comp, iter) \
12 comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \
15 struct mlx5_devcom_component {
18 } device[MLX5_DEVCOM_PORTS_SUPPORTED];
20 mlx5_devcom_event_handler_t handler;
21 struct rw_semaphore sem;
25 struct mlx5_devcom_list {
26 struct list_head list;
28 struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS];
29 struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED];
33 struct mlx5_devcom_list *priv;
37 static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void)
39 struct mlx5_devcom_component *comp;
40 struct mlx5_devcom_list *priv;
43 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
47 devcom_for_each_component(priv, comp, i)
48 init_rwsem(&comp->sem);
53 static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv,
56 struct mlx5_devcom *devcom;
58 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
67 /* Must be called with intf_mutex held */
68 struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)
70 struct mlx5_devcom_list *priv = NULL, *iter;
71 struct mlx5_devcom *devcom = NULL;
72 bool new_priv = false;
76 if (!mlx5_core_is_pf(dev))
78 if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED)
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;
87 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
89 tmp_dev = iter->devs[i];
97 sguid1 = mlx5_query_nic_system_image_guid(tmp_dev);
106 priv = mlx5_devcom_list_alloc();
108 devcom = ERR_PTR(-ENOMEM);
116 priv->devs[idx] = dev;
117 devcom = mlx5_devcom_alloc(priv, idx);
121 devcom = ERR_PTR(-ENOMEM);
126 list_add(&priv->list, &devcom_list);
128 mlx5_dev_list_unlock();
132 /* Must be called with intf_mutex held */
133 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
135 struct mlx5_devcom_list *priv;
138 if (IS_ERR_OR_NULL(devcom))
141 mlx5_dev_list_lock();
143 priv->devs[devcom->idx] = NULL;
147 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
151 if (i != MLX5_DEVCOM_PORTS_SUPPORTED)
154 list_del(&priv->list);
157 mlx5_dev_list_unlock();
160 void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
161 enum mlx5_devcom_components id,
162 mlx5_devcom_event_handler_t handler,
165 struct mlx5_devcom_component *comp;
167 if (IS_ERR_OR_NULL(devcom))
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);
179 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
180 enum mlx5_devcom_components id)
182 struct mlx5_devcom_component *comp;
184 if (IS_ERR_OR_NULL(devcom))
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);
194 int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
195 enum mlx5_devcom_components id,
199 struct mlx5_devcom_component *comp;
200 int err = -ENODEV, i;
202 if (IS_ERR_OR_NULL(devcom))
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));
211 if (i != devcom->idx && data) {
212 err = comp->handler(event, data, event_data);
217 up_write(&comp->sem);
221 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
222 enum mlx5_devcom_components id,
225 struct mlx5_devcom_component *comp;
227 comp = &devcom->priv->components[id];
228 WARN_ON(!rwsem_is_locked(&comp->sem));
230 WRITE_ONCE(comp->paired, paired);
233 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
234 enum mlx5_devcom_components id)
236 if (IS_ERR_OR_NULL(devcom))
239 return READ_ONCE(devcom->priv->components[id].paired);
242 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
243 enum mlx5_devcom_components id)
245 struct mlx5_devcom_component *comp;
248 if (IS_ERR_OR_NULL(devcom))
251 comp = &devcom->priv->components[id];
252 down_read(&comp->sem);
253 if (!READ_ONCE(comp->paired)) {
258 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
259 if (i != devcom->idx)
262 return rcu_dereference_protected(comp->device[i].data, lockdep_is_held(&comp->sem));
265 void *mlx5_devcom_get_peer_data_rcu(struct mlx5_devcom *devcom, enum mlx5_devcom_components id)
267 struct mlx5_devcom_component *comp;
270 if (IS_ERR_OR_NULL(devcom))
273 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
274 if (i != devcom->idx)
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.
281 if (!READ_ONCE(comp->paired))
284 return rcu_dereference(comp->device[i].data);
287 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
288 enum mlx5_devcom_components id)
290 struct mlx5_devcom_component *comp = &devcom->priv->components[id];