1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2018 Mellanox Technologies */
4 #include <linux/mlx5/vport.h>
5 #include <linux/list.h>
6 #include "lib/devcom.h"
9 static LIST_HEAD(devcom_dev_list);
10 static LIST_HEAD(devcom_comp_list);
11 /* protect device list */
12 static DEFINE_MUTEX(dev_list_lock);
13 /* protect component list */
14 static DEFINE_MUTEX(comp_list_lock);
16 #define devcom_for_each_component(iter) \
17 list_for_each_entry(iter, &devcom_comp_list, comp_list)
19 struct mlx5_devcom_dev {
20 struct list_head list;
21 struct mlx5_core_dev *dev;
25 struct mlx5_devcom_comp {
26 struct list_head comp_list;
27 enum mlx5_devcom_component id;
29 struct list_head comp_dev_list_head;
30 mlx5_devcom_event_handler_t handler;
33 struct rw_semaphore sem;
34 struct lock_class_key lock_key;
37 struct mlx5_devcom_comp_dev {
38 struct list_head list;
39 struct mlx5_devcom_comp *comp;
40 struct mlx5_devcom_dev *devc;
44 static bool devcom_dev_exists(struct mlx5_core_dev *dev)
46 struct mlx5_devcom_dev *iter;
48 list_for_each_entry(iter, &devcom_dev_list, list)
55 static struct mlx5_devcom_dev *
56 mlx5_devcom_dev_alloc(struct mlx5_core_dev *dev)
58 struct mlx5_devcom_dev *devc;
60 devc = kzalloc(sizeof(*devc), GFP_KERNEL);
65 kref_init(&devc->ref);
69 struct mlx5_devcom_dev *
70 mlx5_devcom_register_device(struct mlx5_core_dev *dev)
72 struct mlx5_devcom_dev *devc;
74 mutex_lock(&dev_list_lock);
76 if (devcom_dev_exists(dev)) {
77 devc = ERR_PTR(-EEXIST);
81 devc = mlx5_devcom_dev_alloc(dev);
83 devc = ERR_PTR(-ENOMEM);
87 list_add_tail(&devc->list, &devcom_dev_list);
89 mutex_unlock(&dev_list_lock);
94 mlx5_devcom_dev_release(struct kref *ref)
96 struct mlx5_devcom_dev *devc = container_of(ref, struct mlx5_devcom_dev, ref);
98 mutex_lock(&dev_list_lock);
99 list_del(&devc->list);
100 mutex_unlock(&dev_list_lock);
104 void mlx5_devcom_unregister_device(struct mlx5_devcom_dev *devc)
106 if (!IS_ERR_OR_NULL(devc))
107 kref_put(&devc->ref, mlx5_devcom_dev_release);
110 static struct mlx5_devcom_comp *
111 mlx5_devcom_comp_alloc(u64 id, u64 key, mlx5_devcom_event_handler_t handler)
113 struct mlx5_devcom_comp *comp;
115 comp = kzalloc(sizeof(*comp), GFP_KERNEL);
117 return ERR_PTR(-ENOMEM);
121 comp->handler = handler;
122 init_rwsem(&comp->sem);
123 lockdep_register_key(&comp->lock_key);
124 lockdep_set_class(&comp->sem, &comp->lock_key);
125 kref_init(&comp->ref);
126 INIT_LIST_HEAD(&comp->comp_dev_list_head);
132 mlx5_devcom_comp_release(struct kref *ref)
134 struct mlx5_devcom_comp *comp = container_of(ref, struct mlx5_devcom_comp, ref);
136 mutex_lock(&comp_list_lock);
137 list_del(&comp->comp_list);
138 mutex_unlock(&comp_list_lock);
139 lockdep_unregister_key(&comp->lock_key);
143 static struct mlx5_devcom_comp_dev *
144 devcom_alloc_comp_dev(struct mlx5_devcom_dev *devc,
145 struct mlx5_devcom_comp *comp,
148 struct mlx5_devcom_comp_dev *devcom;
150 devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
152 return ERR_PTR(-ENOMEM);
154 kref_get(&devc->ref);
157 rcu_assign_pointer(devcom->data, data);
159 down_write(&comp->sem);
160 list_add_tail(&devcom->list, &comp->comp_dev_list_head);
161 up_write(&comp->sem);
167 devcom_free_comp_dev(struct mlx5_devcom_comp_dev *devcom)
169 struct mlx5_devcom_comp *comp = devcom->comp;
171 down_write(&comp->sem);
172 list_del(&devcom->list);
173 up_write(&comp->sem);
175 kref_put(&devcom->devc->ref, mlx5_devcom_dev_release);
177 kref_put(&comp->ref, mlx5_devcom_comp_release);
181 devcom_component_equal(struct mlx5_devcom_comp *devcom,
182 enum mlx5_devcom_component id,
185 return devcom->id == id && devcom->key == key;
188 static struct mlx5_devcom_comp *
189 devcom_component_get(struct mlx5_devcom_dev *devc,
190 enum mlx5_devcom_component id,
192 mlx5_devcom_event_handler_t handler)
194 struct mlx5_devcom_comp *comp;
196 devcom_for_each_component(comp) {
197 if (devcom_component_equal(comp, id, key)) {
198 if (handler == comp->handler) {
199 kref_get(&comp->ref);
203 mlx5_core_err(devc->dev,
204 "Cannot register existing devcom component with different handler\n");
205 return ERR_PTR(-EINVAL);
212 struct mlx5_devcom_comp_dev *
213 mlx5_devcom_register_component(struct mlx5_devcom_dev *devc,
214 enum mlx5_devcom_component id,
216 mlx5_devcom_event_handler_t handler,
219 struct mlx5_devcom_comp_dev *devcom;
220 struct mlx5_devcom_comp *comp;
222 if (IS_ERR_OR_NULL(devc))
223 return ERR_PTR(-EINVAL);
225 mutex_lock(&comp_list_lock);
226 comp = devcom_component_get(devc, id, key, handler);
228 devcom = ERR_PTR(-EINVAL);
233 comp = mlx5_devcom_comp_alloc(id, key, handler);
235 devcom = ERR_CAST(comp);
238 list_add_tail(&comp->comp_list, &devcom_comp_list);
240 mutex_unlock(&comp_list_lock);
242 devcom = devcom_alloc_comp_dev(devc, comp, data);
244 kref_put(&comp->ref, mlx5_devcom_comp_release);
249 mutex_unlock(&comp_list_lock);
253 void mlx5_devcom_unregister_component(struct mlx5_devcom_comp_dev *devcom)
255 if (!IS_ERR_OR_NULL(devcom))
256 devcom_free_comp_dev(devcom);
259 int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom)
261 struct mlx5_devcom_comp *comp = devcom->comp;
263 return kref_read(&comp->ref);
266 int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
267 int event, int rollback_event,
270 struct mlx5_devcom_comp_dev *pos;
271 struct mlx5_devcom_comp *comp;
275 if (IS_ERR_OR_NULL(devcom))
279 down_write(&comp->sem);
280 list_for_each_entry(pos, &comp->comp_dev_list_head, list) {
281 data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
283 if (pos != devcom && data) {
284 err = comp->handler(event, data, event_data);
290 up_write(&comp->sem);
294 if (list_entry_is_head(pos, &comp->comp_dev_list_head, list))
296 pos = list_prev_entry(pos, list);
297 list_for_each_entry_from_reverse(pos, &comp->comp_dev_list_head, list) {
298 data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
300 if (pos != devcom && data)
301 comp->handler(rollback_event, data, event_data);
304 up_write(&comp->sem);
308 void mlx5_devcom_comp_set_ready(struct mlx5_devcom_comp_dev *devcom, bool ready)
310 WARN_ON(!rwsem_is_locked(&devcom->comp->sem));
312 WRITE_ONCE(devcom->comp->ready, ready);
315 bool mlx5_devcom_comp_is_ready(struct mlx5_devcom_comp_dev *devcom)
317 if (IS_ERR_OR_NULL(devcom))
320 return READ_ONCE(devcom->comp->ready);
323 bool mlx5_devcom_for_each_peer_begin(struct mlx5_devcom_comp_dev *devcom)
325 struct mlx5_devcom_comp *comp;
327 if (IS_ERR_OR_NULL(devcom))
331 down_read(&comp->sem);
332 if (!READ_ONCE(comp->ready)) {
340 void mlx5_devcom_for_each_peer_end(struct mlx5_devcom_comp_dev *devcom)
342 up_read(&devcom->comp->sem);
345 void *mlx5_devcom_get_next_peer_data(struct mlx5_devcom_comp_dev *devcom,
346 struct mlx5_devcom_comp_dev **pos)
348 struct mlx5_devcom_comp *comp = devcom->comp;
349 struct mlx5_devcom_comp_dev *tmp;
352 tmp = list_prepare_entry(*pos, &comp->comp_dev_list_head, list);
354 list_for_each_entry_continue(tmp, &comp->comp_dev_list_head, list) {
356 data = rcu_dereference_protected(tmp->data, lockdep_is_held(&comp->sem));
362 if (list_entry_is_head(tmp, &comp->comp_dev_list_head, list))
369 void *mlx5_devcom_get_next_peer_data_rcu(struct mlx5_devcom_comp_dev *devcom,
370 struct mlx5_devcom_comp_dev **pos)
372 struct mlx5_devcom_comp *comp = devcom->comp;
373 struct mlx5_devcom_comp_dev *tmp;
376 tmp = list_prepare_entry(*pos, &comp->comp_dev_list_head, list);
378 list_for_each_entry_continue(tmp, &comp->comp_dev_list_head, list) {
380 /* This can change concurrently, however 'data' pointer will remain
381 * valid for the duration of RCU read section.
383 if (!READ_ONCE(comp->ready))
385 data = rcu_dereference(tmp->data);
391 if (list_entry_is_head(tmp, &comp->comp_dev_list_head, list))
398 void mlx5_devcom_comp_lock(struct mlx5_devcom_comp_dev *devcom)
400 if (IS_ERR_OR_NULL(devcom))
402 down_write(&devcom->comp->sem);
405 void mlx5_devcom_comp_unlock(struct mlx5_devcom_comp_dev *devcom)
407 if (IS_ERR_OR_NULL(devcom))
409 up_write(&devcom->comp->sem);
412 int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom)
414 if (IS_ERR_OR_NULL(devcom))
416 return down_write_trylock(&devcom->comp->sem);