File Index
Files and directories
Sign in
FILE

devcom.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c C
  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 <linux/list.h>
  6#include "lib/devcom.h"
  7#include "lib/mlx5.h"
  8#include "mlx5_core.h"
  9
 10static LIST_HEAD(devcom_dev_list);
 11static LIST_HEAD(devcom_comp_list);
 12/* protect device list */
 13static DEFINE_MUTEX(dev_list_lock);
 14/* protect component list */
 15static DEFINE_MUTEX(comp_list_lock);
 16
 17#define devcom_for_each_component(iter) \
 18	list_for_each_entry(iter, &devcom_comp_list, comp_list)
 19
 20struct mlx5_devcom_dev {
 21	struct list_head list;
 22	struct mlx5_core_dev *dev;
 23	struct kref ref;
 24};
 25
 26struct mlx5_devcom_key {
 27	u32 flags;
 28	union mlx5_devcom_match_key key;
 29	possible_net_t net;
 30};
 31
 32struct mlx5_devcom_comp {
 33	struct list_head comp_list;
 34	enum mlx5_devcom_component id;
 35	struct list_head comp_dev_list_head;
 36	struct mlx5_devcom_key key;
 37	mlx5_devcom_event_handler_t handler;
 38	struct kref ref;
 39	bool ready;
 40	struct rw_semaphore sem;
 41	struct lock_class_key lock_key;
 42};
 43
 44struct mlx5_devcom_comp_dev {
 45	struct list_head list;
 46	struct mlx5_devcom_comp *comp;
 47	struct mlx5_devcom_dev *devc;
 48	void __rcu *data;
 49};
 50
 51static bool devcom_dev_exists(struct mlx5_core_dev *dev)
 52{
 53	struct mlx5_devcom_dev *iter;
 54
 55	list_for_each_entry(iter, &devcom_dev_list, list)
 56		if (iter->dev == dev)
 57			return true;
 58
 59	return false;
 60}
 61
 62static struct mlx5_devcom_dev *
 63mlx5_devcom_dev_alloc(struct mlx5_core_dev *dev)
 64{
 65	struct mlx5_devcom_dev *devc;
 66
 67	devc = kzalloc(sizeof(*devc), GFP_KERNEL);
 68	if (!devc)
 69		return NULL;
 70
 71	devc->dev = dev;
 72	kref_init(&devc->ref);
 73	return devc;
 74}
 75
 76struct mlx5_devcom_dev *
 77mlx5_devcom_register_device(struct mlx5_core_dev *dev)
 78{
 79	struct mlx5_devcom_dev *devc = NULL;
 80
 81	mutex_lock(&dev_list_lock);
 82
 83	if (devcom_dev_exists(dev)) {
 84		mlx5_core_err(dev, "devcom device already exists");
 85		goto out;
 86	}
 87
 88	devc = mlx5_devcom_dev_alloc(dev);
 89	if (!devc)
 90		goto out;
 91
 92	list_add_tail(&devc->list, &devcom_dev_list);
 93out:
 94	mutex_unlock(&dev_list_lock);
 95	return devc;
 96}
 97
 98static void
 99mlx5_devcom_dev_release(struct kref *ref)
100{
101	struct mlx5_devcom_dev *devc = container_of(ref, struct mlx5_devcom_dev, ref);
102
103	mutex_lock(&dev_list_lock);
104	list_del(&devc->list);
105	mutex_unlock(&dev_list_lock);
106	kfree(devc);
107}
108
109void mlx5_devcom_unregister_device(struct mlx5_devcom_dev *devc)
110{
111	if (!devc)
112		return;
113
114	kref_put(&devc->ref, mlx5_devcom_dev_release);
115}
116
117static struct mlx5_devcom_comp *
118mlx5_devcom_comp_alloc(u64 id, const struct mlx5_devcom_match_attr *attr,
119		       mlx5_devcom_event_handler_t handler)
120{
121	struct mlx5_devcom_comp *comp;
122
123	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
124	if (!comp)
125		return NULL;
126
127	comp->id = id;
128	comp->key.key = attr->key;
129	comp->key.flags = attr->flags;
130	if (attr->flags & MLX5_DEVCOM_MATCH_FLAGS_NS)
131		write_pnet(&comp->key.net, attr->net);
132	comp->handler = handler;
133	init_rwsem(&comp->sem);
134	lockdep_register_key(&comp->lock_key);
135	lockdep_set_class(&comp->sem, &comp->lock_key);
136	kref_init(&comp->ref);
137	INIT_LIST_HEAD(&comp->comp_dev_list_head);
138
139	return comp;
140}
141
142static void
143mlx5_devcom_comp_release(struct kref *ref)
144{
145	struct mlx5_devcom_comp *comp = container_of(ref, struct mlx5_devcom_comp, ref);
146
147	mutex_lock(&comp_list_lock);
148	list_del(&comp->comp_list);
149	mutex_unlock(&comp_list_lock);
150	lockdep_unregister_key(&comp->lock_key);
151	kfree(comp);
152}
153
154static struct mlx5_devcom_comp_dev *
155devcom_alloc_comp_dev(struct mlx5_devcom_dev *devc,
156		      struct mlx5_devcom_comp *comp,
157		      void *data)
158{
159	struct mlx5_devcom_comp_dev *devcom;
160
161	devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
162	if (!devcom)
163		return NULL;
164
165	kref_get(&devc->ref);
166	devcom->devc = devc;
167	devcom->comp = comp;
168	rcu_assign_pointer(devcom->data, data);
169
170	down_write(&comp->sem);
171	list_add_tail(&devcom->list, &comp->comp_dev_list_head);
172	up_write(&comp->sem);
173
174	return devcom;
175}
176
177static void
178devcom_free_comp_dev(struct mlx5_devcom_comp_dev *devcom)
179{
180	struct mlx5_devcom_comp *comp = devcom->comp;
181
182	down_write(&comp->sem);
183	list_del(&devcom->list);
184	up_write(&comp->sem);
185
186	kref_put(&devcom->devc->ref, mlx5_devcom_dev_release);
187	kfree(devcom);
188	kref_put(&comp->ref, mlx5_devcom_comp_release);
189}
190
191static bool
192devcom_component_equal(struct mlx5_devcom_comp *devcom,
193		       enum mlx5_devcom_component id,
194		       const struct mlx5_devcom_match_attr *attr)
195{
196	if (devcom->id != id)
197		return false;
198
199	if (devcom->key.flags != attr->flags)
200		return false;
201
202	if (memcmp(&devcom->key.key, &attr->key, sizeof(devcom->key.key)))
203		return false;
204
205	if (devcom->key.flags & MLX5_DEVCOM_MATCH_FLAGS_NS &&
206	    !net_eq(read_pnet(&devcom->key.net), attr->net))
207		return false;
208
209	return true;
210}
211
212static struct mlx5_devcom_comp *
213devcom_component_get(struct mlx5_devcom_dev *devc,
214		     enum mlx5_devcom_component id,
215		     const struct mlx5_devcom_match_attr *attr,
216		     mlx5_devcom_event_handler_t handler)
217{
218	struct mlx5_devcom_comp *comp;
219
220	devcom_for_each_component(comp) {
221		if (devcom_component_equal(comp, id, attr)) {
222			if (handler == comp->handler) {
223				kref_get(&comp->ref);
224				return comp;
225			}
226
227			mlx5_core_err(devc->dev,
228				      "Cannot register existing devcom component with different handler\n");
229			return ERR_PTR(-EINVAL);
230		}
231	}
232
233	return NULL;
234}
235
236struct mlx5_devcom_comp_dev *
237mlx5_devcom_register_component(struct mlx5_devcom_dev *devc,
238			       enum mlx5_devcom_component id,
239			       const struct mlx5_devcom_match_attr *attr,
240			       mlx5_devcom_event_handler_t handler,
241			       void *data)
242{
243	struct mlx5_devcom_comp_dev *devcom = NULL;
244	struct mlx5_devcom_comp *comp;
245
246	if (!devc)
247		return NULL;
248
249	mutex_lock(&comp_list_lock);
250	comp = devcom_component_get(devc, id, attr, handler);
251	if (IS_ERR(comp))
252		goto out_unlock;
253
254	if (!comp) {
255		comp = mlx5_devcom_comp_alloc(id, attr, handler);
256		if (!comp)
257			goto out_unlock;
258
259		list_add_tail(&comp->comp_list, &devcom_comp_list);
260	}
261	mutex_unlock(&comp_list_lock);
262
263	devcom = devcom_alloc_comp_dev(devc, comp, data);
264	if (!devcom)
265		kref_put(&comp->ref, mlx5_devcom_comp_release);
266
267	return devcom;
268
269out_unlock:
270	mutex_unlock(&comp_list_lock);
271	return devcom;
272}
273
274void mlx5_devcom_unregister_component(struct mlx5_devcom_comp_dev *devcom)
275{
276	if (!devcom)
277		return;
278
279	devcom_free_comp_dev(devcom);
280}
281
282int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom)
283{
284	struct mlx5_devcom_comp *comp = devcom->comp;
285
286	return kref_read(&comp->ref);
287}
288
289int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
290			   int event, int rollback_event,
291			   void *event_data)
292{
293	struct mlx5_devcom_comp_dev *pos;
294	struct mlx5_devcom_comp *comp;
295	int err = 0;
296	void *data;
297
298	if (!devcom)
299		return -ENODEV;
300
301	comp = devcom->comp;
302	down_write(&comp->sem);
303	list_for_each_entry(pos, &comp->comp_dev_list_head, list) {
304		data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
305
306		if (pos != devcom && data) {
307			err = comp->handler(event, data, event_data);
308			if (err)
309				goto rollback;
310		}
311	}
312
313	up_write(&comp->sem);
314	return 0;
315
316rollback:
317	if (list_entry_is_head(pos, &comp->comp_dev_list_head, list))
318		goto out;
319	pos = list_prev_entry(pos, list);
320	list_for_each_entry_from_reverse(pos, &comp->comp_dev_list_head, list) {
321		data = rcu_dereference_protected(pos->data, lockdep_is_held(&comp->sem));
322
323		if (pos != devcom && data)
324			comp->handler(rollback_event, data, event_data);
325	}
326out:
327	up_write(&comp->sem);
328	return err;
329}
330
331void mlx5_devcom_comp_set_ready(struct mlx5_devcom_comp_dev *devcom, bool ready)
332{
333	WARN_ON(!rwsem_is_locked(&devcom->comp->sem));
334
335	WRITE_ONCE(devcom->comp->ready, ready);
336}
337
338bool mlx5_devcom_comp_is_ready(struct mlx5_devcom_comp_dev *devcom)
339{
340	if (!devcom)
341		return false;
342
343	return READ_ONCE(devcom->comp->ready);
344}
345
346bool mlx5_devcom_for_each_peer_begin(struct mlx5_devcom_comp_dev *devcom)
347{
348	struct mlx5_devcom_comp *comp;
349
350	if (!devcom)
351		return false;
352
353	comp = devcom->comp;
354	down_read(&comp->sem);
355	if (!READ_ONCE(comp->ready)) {
356		up_read(&comp->sem);
357		return false;
358	}
359
360	return true;
361}
362
363void mlx5_devcom_for_each_peer_end(struct mlx5_devcom_comp_dev *devcom)
364{
365	up_read(&devcom->comp->sem);
366}
367
368void *mlx5_devcom_get_next_peer_data(struct mlx5_devcom_comp_dev *devcom,
369				     struct mlx5_devcom_comp_dev **pos)
370{
371	struct mlx5_devcom_comp *comp = devcom->comp;
372	struct mlx5_devcom_comp_dev *tmp;
373	void *data;
374
375	tmp = list_prepare_entry(*pos, &comp->comp_dev_list_head, list);
376
377	list_for_each_entry_continue(tmp, &comp->comp_dev_list_head, list) {
378		if (tmp != devcom) {
379			data = rcu_dereference_protected(tmp->data, lockdep_is_held(&comp->sem));
380			if (data)
381				break;
382		}
383	}
384
385	if (list_entry_is_head(tmp, &comp->comp_dev_list_head, list))
386		return NULL;
387
388	*pos = tmp;
389	return data;
390}
391
392void *mlx5_devcom_get_next_peer_data_rcu(struct mlx5_devcom_comp_dev *devcom,
393					 struct mlx5_devcom_comp_dev **pos)
394{
395	struct mlx5_devcom_comp *comp = devcom->comp;
396	struct mlx5_devcom_comp_dev *tmp;
397	void *data;
398
399	tmp = list_prepare_entry(*pos, &comp->comp_dev_list_head, list);
400
401	list_for_each_entry_continue(tmp, &comp->comp_dev_list_head, list) {
402		if (tmp != devcom) {
403			/* This can change concurrently, however 'data' pointer will remain
404			 * valid for the duration of RCU read section.
405			 */
406			if (!READ_ONCE(comp->ready))
407				return NULL;
408			data = rcu_dereference(tmp->data);
409			if (data)
410				break;
411		}
412	}
413
414	if (list_entry_is_head(tmp, &comp->comp_dev_list_head, list))
415		return NULL;
416
417	*pos = tmp;
418	return data;
419}
420
421void mlx5_devcom_comp_lock(struct mlx5_devcom_comp_dev *devcom)
422{
423	if (!devcom)
424		return;
425	down_write(&devcom->comp->sem);
426}
427
428void mlx5_devcom_comp_unlock(struct mlx5_devcom_comp_dev *devcom)
429{
430	if (!devcom)
431		return;
432	up_write(&devcom->comp->sem);
433}
434
435int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom)
436{
437	if (!devcom)
438		return 0;
439	return down_write_trylock(&devcom->comp->sem);
440}
~/ Read-only file preview.