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"
7 static LIST_HEAD(devcom_list);
9 #define devcom_for_each_component(priv, comp, iter) \
11 comp = &(priv)->components[iter], iter < MLX5_DEVCOM_NUM_COMPONENTS; \
14 struct mlx5_devcom_component {
17 } device[MLX5_DEVCOM_PORTS_SUPPORTED];
19 mlx5_devcom_event_handler_t handler;
20 struct rw_semaphore sem;
24 struct mlx5_devcom_list {
25 struct list_head list;
27 struct mlx5_devcom_component components[MLX5_DEVCOM_NUM_COMPONENTS];
28 struct mlx5_core_dev *devs[MLX5_DEVCOM_PORTS_SUPPORTED];
32 struct mlx5_devcom_list *priv;
36 static struct mlx5_devcom_list *mlx5_devcom_list_alloc(void)
38 struct mlx5_devcom_component *comp;
39 struct mlx5_devcom_list *priv;
42 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
46 devcom_for_each_component(priv, comp, i)
47 init_rwsem(&comp->sem);
52 static struct mlx5_devcom *mlx5_devcom_alloc(struct mlx5_devcom_list *priv,
55 struct mlx5_devcom *devcom;
57 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
66 /* Must be called with intf_mutex held */
67 struct mlx5_devcom *mlx5_devcom_register_device(struct mlx5_core_dev *dev)
69 struct mlx5_devcom_list *priv = NULL, *iter;
70 struct mlx5_devcom *devcom = NULL;
71 bool new_priv = false;
75 if (!mlx5_core_is_pf(dev))
77 if (MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_DEVCOM_PORTS_SUPPORTED)
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;
85 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
87 tmp_dev = iter->devs[i];
95 sguid1 = mlx5_query_nic_system_image_guid(tmp_dev);
104 priv = mlx5_devcom_list_alloc();
106 return ERR_PTR(-ENOMEM);
112 priv->devs[idx] = dev;
113 devcom = mlx5_devcom_alloc(priv, idx);
116 return ERR_PTR(-ENOMEM);
120 list_add(&priv->list, &devcom_list);
125 /* Must be called with intf_mutex held */
126 void mlx5_devcom_unregister_device(struct mlx5_devcom *devcom)
128 struct mlx5_devcom_list *priv;
131 if (IS_ERR_OR_NULL(devcom))
135 priv->devs[devcom->idx] = NULL;
139 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
143 if (i != MLX5_DEVCOM_PORTS_SUPPORTED)
146 list_del(&priv->list);
150 void mlx5_devcom_register_component(struct mlx5_devcom *devcom,
151 enum mlx5_devcom_components id,
152 mlx5_devcom_event_handler_t handler,
155 struct mlx5_devcom_component *comp;
157 if (IS_ERR_OR_NULL(devcom))
162 comp = &devcom->priv->components[id];
163 down_write(&comp->sem);
164 comp->handler = handler;
165 rcu_assign_pointer(comp->device[devcom->idx].data, data);
166 up_write(&comp->sem);
169 void mlx5_devcom_unregister_component(struct mlx5_devcom *devcom,
170 enum mlx5_devcom_components id)
172 struct mlx5_devcom_component *comp;
174 if (IS_ERR_OR_NULL(devcom))
177 comp = &devcom->priv->components[id];
178 down_write(&comp->sem);
179 RCU_INIT_POINTER(comp->device[devcom->idx].data, NULL);
180 up_write(&comp->sem);
184 int mlx5_devcom_send_event(struct mlx5_devcom *devcom,
185 enum mlx5_devcom_components id,
189 struct mlx5_devcom_component *comp;
190 int err = -ENODEV, i;
192 if (IS_ERR_OR_NULL(devcom))
195 comp = &devcom->priv->components[id];
196 down_write(&comp->sem);
197 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++) {
198 void *data = rcu_dereference_protected(comp->device[i].data,
199 lockdep_is_held(&comp->sem));
201 if (i != devcom->idx && data) {
202 err = comp->handler(event, data, event_data);
207 up_write(&comp->sem);
211 void mlx5_devcom_set_paired(struct mlx5_devcom *devcom,
212 enum mlx5_devcom_components id,
215 struct mlx5_devcom_component *comp;
217 comp = &devcom->priv->components[id];
218 WARN_ON(!rwsem_is_locked(&comp->sem));
220 WRITE_ONCE(comp->paired, paired);
223 bool mlx5_devcom_is_paired(struct mlx5_devcom *devcom,
224 enum mlx5_devcom_components id)
226 if (IS_ERR_OR_NULL(devcom))
229 return READ_ONCE(devcom->priv->components[id].paired);
232 void *mlx5_devcom_get_peer_data(struct mlx5_devcom *devcom,
233 enum mlx5_devcom_components id)
235 struct mlx5_devcom_component *comp;
238 if (IS_ERR_OR_NULL(devcom))
241 comp = &devcom->priv->components[id];
242 down_read(&comp->sem);
243 if (!READ_ONCE(comp->paired)) {
248 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
249 if (i != devcom->idx)
252 return rcu_dereference_protected(comp->device[i].data, lockdep_is_held(&comp->sem));
255 void *mlx5_devcom_get_peer_data_rcu(struct mlx5_devcom *devcom, enum mlx5_devcom_components id)
257 struct mlx5_devcom_component *comp;
260 if (IS_ERR_OR_NULL(devcom))
263 for (i = 0; i < MLX5_DEVCOM_PORTS_SUPPORTED; i++)
264 if (i != devcom->idx)
267 comp = &devcom->priv->components[id];
268 /* This can change concurrently, however 'data' pointer will remain
269 * valid for the duration of RCU read section.
271 if (!READ_ONCE(comp->paired))
274 return rcu_dereference(comp->device[i].data);
277 void mlx5_devcom_release_peer_data(struct mlx5_devcom *devcom,
278 enum mlx5_devcom_components id)
280 struct mlx5_devcom_component *comp = &devcom->priv->components[id];