xref: /linux/net/devlink/core.c (revision 9053637e0da783efdb37bbfea6a27b856c0228d7)
1687125b5SJakub Kicinski // SPDX-License-Identifier: GPL-2.0-or-later
2687125b5SJakub Kicinski /*
3687125b5SJakub Kicinski  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4687125b5SJakub Kicinski  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5687125b5SJakub Kicinski  */
6687125b5SJakub Kicinski 
7687125b5SJakub Kicinski #include <net/genetlink.h>
8687125b5SJakub Kicinski 
9687125b5SJakub Kicinski #include "devl_internal.h"
10687125b5SJakub Kicinski 
11687125b5SJakub Kicinski DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
12687125b5SJakub Kicinski 
13687125b5SJakub Kicinski void *devlink_priv(struct devlink *devlink)
14687125b5SJakub Kicinski {
15687125b5SJakub Kicinski 	return &devlink->priv;
16687125b5SJakub Kicinski }
17687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_priv);
18687125b5SJakub Kicinski 
19687125b5SJakub Kicinski struct devlink *priv_to_devlink(void *priv)
20687125b5SJakub Kicinski {
21687125b5SJakub Kicinski 	return container_of(priv, struct devlink, priv);
22687125b5SJakub Kicinski }
23687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(priv_to_devlink);
24687125b5SJakub Kicinski 
25687125b5SJakub Kicinski struct device *devlink_to_dev(const struct devlink *devlink)
26687125b5SJakub Kicinski {
27687125b5SJakub Kicinski 	return devlink->dev;
28687125b5SJakub Kicinski }
29687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_to_dev);
30687125b5SJakub Kicinski 
31687125b5SJakub Kicinski struct net *devlink_net(const struct devlink *devlink)
32687125b5SJakub Kicinski {
33687125b5SJakub Kicinski 	return read_pnet(&devlink->_net);
34687125b5SJakub Kicinski }
35687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_net);
36687125b5SJakub Kicinski 
37687125b5SJakub Kicinski void devl_assert_locked(struct devlink *devlink)
38687125b5SJakub Kicinski {
39687125b5SJakub Kicinski 	lockdep_assert_held(&devlink->lock);
40687125b5SJakub Kicinski }
41687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devl_assert_locked);
42687125b5SJakub Kicinski 
43687125b5SJakub Kicinski #ifdef CONFIG_LOCKDEP
44687125b5SJakub Kicinski /* For use in conjunction with LOCKDEP only e.g. rcu_dereference_protected() */
45687125b5SJakub Kicinski bool devl_lock_is_held(struct devlink *devlink)
46687125b5SJakub Kicinski {
47687125b5SJakub Kicinski 	return lockdep_is_held(&devlink->lock);
48687125b5SJakub Kicinski }
49687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devl_lock_is_held);
50687125b5SJakub Kicinski #endif
51687125b5SJakub Kicinski 
52687125b5SJakub Kicinski void devl_lock(struct devlink *devlink)
53687125b5SJakub Kicinski {
54687125b5SJakub Kicinski 	mutex_lock(&devlink->lock);
55687125b5SJakub Kicinski }
56687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devl_lock);
57687125b5SJakub Kicinski 
58687125b5SJakub Kicinski int devl_trylock(struct devlink *devlink)
59687125b5SJakub Kicinski {
60687125b5SJakub Kicinski 	return mutex_trylock(&devlink->lock);
61687125b5SJakub Kicinski }
62687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devl_trylock);
63687125b5SJakub Kicinski 
64687125b5SJakub Kicinski void devl_unlock(struct devlink *devlink)
65687125b5SJakub Kicinski {
66687125b5SJakub Kicinski 	mutex_unlock(&devlink->lock);
67687125b5SJakub Kicinski }
68687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devl_unlock);
69687125b5SJakub Kicinski 
70ed539ba6SJakub Kicinski /**
71ed539ba6SJakub Kicinski  * devlink_try_get() - try to obtain a reference on a devlink instance
72ed539ba6SJakub Kicinski  * @devlink: instance to reference
73ed539ba6SJakub Kicinski  *
74ed539ba6SJakub Kicinski  * Obtain a reference on a devlink instance. A reference on a devlink instance
75ed539ba6SJakub Kicinski  * only implies that it's safe to take the instance lock. It does not imply
76ed539ba6SJakub Kicinski  * that the instance is registered, use devl_is_registered() after taking
77ed539ba6SJakub Kicinski  * the instance lock to check registration status.
78ed539ba6SJakub Kicinski  */
79687125b5SJakub Kicinski struct devlink *__must_check devlink_try_get(struct devlink *devlink)
80687125b5SJakub Kicinski {
81687125b5SJakub Kicinski 	if (refcount_inc_not_zero(&devlink->refcount))
82687125b5SJakub Kicinski 		return devlink;
83687125b5SJakub Kicinski 	return NULL;
84687125b5SJakub Kicinski }
85687125b5SJakub Kicinski 
86687125b5SJakub Kicinski void devlink_put(struct devlink *devlink)
87687125b5SJakub Kicinski {
88687125b5SJakub Kicinski 	if (refcount_dec_and_test(&devlink->refcount))
89*9053637eSJakub Kicinski 		kfree_rcu(devlink, rcu);
90687125b5SJakub Kicinski }
91687125b5SJakub Kicinski 
92d7727819SJakub Kicinski struct devlink *devlinks_xa_find_get(struct net *net, unsigned long *indexp)
93687125b5SJakub Kicinski {
94d7727819SJakub Kicinski 	struct devlink *devlink = NULL;
95687125b5SJakub Kicinski 
96687125b5SJakub Kicinski 	rcu_read_lock();
97687125b5SJakub Kicinski retry:
98d7727819SJakub Kicinski 	devlink = xa_find(&devlinks, indexp, ULONG_MAX, DEVLINK_REGISTERED);
99687125b5SJakub Kicinski 	if (!devlink)
100687125b5SJakub Kicinski 		goto unlock;
101687125b5SJakub Kicinski 
102687125b5SJakub Kicinski 	if (!devlink_try_get(devlink))
103d7727819SJakub Kicinski 		goto next;
104687125b5SJakub Kicinski 	if (!net_eq(devlink_net(devlink), net)) {
105687125b5SJakub Kicinski 		devlink_put(devlink);
106d7727819SJakub Kicinski 		goto next;
107687125b5SJakub Kicinski 	}
108687125b5SJakub Kicinski unlock:
109687125b5SJakub Kicinski 	rcu_read_unlock();
110687125b5SJakub Kicinski 	return devlink;
111687125b5SJakub Kicinski 
112d7727819SJakub Kicinski next:
113d7727819SJakub Kicinski 	(*indexp)++;
114d7727819SJakub Kicinski 	goto retry;
115687125b5SJakub Kicinski }
116687125b5SJakub Kicinski 
117687125b5SJakub Kicinski /**
118687125b5SJakub Kicinski  *	devlink_set_features - Set devlink supported features
119687125b5SJakub Kicinski  *
120687125b5SJakub Kicinski  *	@devlink: devlink
121687125b5SJakub Kicinski  *	@features: devlink support features
122687125b5SJakub Kicinski  *
123687125b5SJakub Kicinski  *	This interface allows us to set reload ops separatelly from
124687125b5SJakub Kicinski  *	the devlink_alloc.
125687125b5SJakub Kicinski  */
126687125b5SJakub Kicinski void devlink_set_features(struct devlink *devlink, u64 features)
127687125b5SJakub Kicinski {
128687125b5SJakub Kicinski 	ASSERT_DEVLINK_NOT_REGISTERED(devlink);
129687125b5SJakub Kicinski 
130687125b5SJakub Kicinski 	WARN_ON(features & DEVLINK_F_RELOAD &&
131687125b5SJakub Kicinski 		!devlink_reload_supported(devlink->ops));
132687125b5SJakub Kicinski 	devlink->features = features;
133687125b5SJakub Kicinski }
134687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_set_features);
135687125b5SJakub Kicinski 
136687125b5SJakub Kicinski /**
137*9053637eSJakub Kicinski  * devl_register - Register devlink instance
138687125b5SJakub Kicinski  * @devlink: devlink
139687125b5SJakub Kicinski  */
140*9053637eSJakub Kicinski int devl_register(struct devlink *devlink)
141687125b5SJakub Kicinski {
142687125b5SJakub Kicinski 	ASSERT_DEVLINK_NOT_REGISTERED(devlink);
143*9053637eSJakub Kicinski 	devl_assert_locked(devlink);
144687125b5SJakub Kicinski 
145687125b5SJakub Kicinski 	xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
146687125b5SJakub Kicinski 	devlink_notify_register(devlink);
147*9053637eSJakub Kicinski 
148*9053637eSJakub Kicinski 	return 0;
149*9053637eSJakub Kicinski }
150*9053637eSJakub Kicinski EXPORT_SYMBOL_GPL(devl_register);
151*9053637eSJakub Kicinski 
152*9053637eSJakub Kicinski void devlink_register(struct devlink *devlink)
153*9053637eSJakub Kicinski {
154*9053637eSJakub Kicinski 	devl_lock(devlink);
155*9053637eSJakub Kicinski 	devl_register(devlink);
156*9053637eSJakub Kicinski 	devl_unlock(devlink);
157687125b5SJakub Kicinski }
158687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_register);
159687125b5SJakub Kicinski 
160687125b5SJakub Kicinski /**
161*9053637eSJakub Kicinski  * devl_unregister - Unregister devlink instance
162687125b5SJakub Kicinski  * @devlink: devlink
163687125b5SJakub Kicinski  */
164*9053637eSJakub Kicinski void devl_unregister(struct devlink *devlink)
165687125b5SJakub Kicinski {
166687125b5SJakub Kicinski 	ASSERT_DEVLINK_REGISTERED(devlink);
167*9053637eSJakub Kicinski 	devl_assert_locked(devlink);
168687125b5SJakub Kicinski 
169687125b5SJakub Kicinski 	devlink_notify_unregister(devlink);
170687125b5SJakub Kicinski 	xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
171*9053637eSJakub Kicinski }
172*9053637eSJakub Kicinski EXPORT_SYMBOL_GPL(devl_unregister);
173*9053637eSJakub Kicinski 
174*9053637eSJakub Kicinski void devlink_unregister(struct devlink *devlink)
175*9053637eSJakub Kicinski {
176*9053637eSJakub Kicinski 	devl_lock(devlink);
177*9053637eSJakub Kicinski 	devl_unregister(devlink);
178*9053637eSJakub Kicinski 	devl_unlock(devlink);
179687125b5SJakub Kicinski }
180687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_unregister);
181687125b5SJakub Kicinski 
182687125b5SJakub Kicinski /**
183687125b5SJakub Kicinski  *	devlink_alloc_ns - Allocate new devlink instance resources
184687125b5SJakub Kicinski  *	in specific namespace
185687125b5SJakub Kicinski  *
186687125b5SJakub Kicinski  *	@ops: ops
187687125b5SJakub Kicinski  *	@priv_size: size of user private data
188687125b5SJakub Kicinski  *	@net: net namespace
189687125b5SJakub Kicinski  *	@dev: parent device
190687125b5SJakub Kicinski  *
191687125b5SJakub Kicinski  *	Allocate new devlink instance resources, including devlink index
192687125b5SJakub Kicinski  *	and name.
193687125b5SJakub Kicinski  */
194687125b5SJakub Kicinski struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
195687125b5SJakub Kicinski 				 size_t priv_size, struct net *net,
196687125b5SJakub Kicinski 				 struct device *dev)
197687125b5SJakub Kicinski {
198687125b5SJakub Kicinski 	struct devlink *devlink;
199687125b5SJakub Kicinski 	static u32 last_id;
200687125b5SJakub Kicinski 	int ret;
201687125b5SJakub Kicinski 
202687125b5SJakub Kicinski 	WARN_ON(!ops || !dev);
203687125b5SJakub Kicinski 	if (!devlink_reload_actions_valid(ops))
204687125b5SJakub Kicinski 		return NULL;
205687125b5SJakub Kicinski 
206687125b5SJakub Kicinski 	devlink = kzalloc(sizeof(*devlink) + priv_size, GFP_KERNEL);
207687125b5SJakub Kicinski 	if (!devlink)
208687125b5SJakub Kicinski 		return NULL;
209687125b5SJakub Kicinski 
210687125b5SJakub Kicinski 	ret = xa_alloc_cyclic(&devlinks, &devlink->index, devlink, xa_limit_31b,
211687125b5SJakub Kicinski 			      &last_id, GFP_KERNEL);
212687125b5SJakub Kicinski 	if (ret < 0)
213687125b5SJakub Kicinski 		goto err_xa_alloc;
214687125b5SJakub Kicinski 
215687125b5SJakub Kicinski 	devlink->netdevice_nb.notifier_call = devlink_port_netdevice_event;
216687125b5SJakub Kicinski 	ret = register_netdevice_notifier_net(net, &devlink->netdevice_nb);
217687125b5SJakub Kicinski 	if (ret)
218687125b5SJakub Kicinski 		goto err_register_netdevice_notifier;
219687125b5SJakub Kicinski 
220687125b5SJakub Kicinski 	devlink->dev = dev;
221687125b5SJakub Kicinski 	devlink->ops = ops;
222687125b5SJakub Kicinski 	xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC);
223687125b5SJakub Kicinski 	xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC);
224687125b5SJakub Kicinski 	write_pnet(&devlink->_net, net);
225687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->rate_list);
226687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->linecard_list);
227687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->sb_list);
228687125b5SJakub Kicinski 	INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list);
229687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->resource_list);
230687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->param_list);
231687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->region_list);
232687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->reporter_list);
233687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->trap_list);
234687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->trap_group_list);
235687125b5SJakub Kicinski 	INIT_LIST_HEAD(&devlink->trap_policer_list);
236687125b5SJakub Kicinski 	lockdep_register_key(&devlink->lock_key);
237687125b5SJakub Kicinski 	mutex_init(&devlink->lock);
238687125b5SJakub Kicinski 	lockdep_set_class(&devlink->lock, &devlink->lock_key);
239687125b5SJakub Kicinski 	mutex_init(&devlink->reporters_lock);
240687125b5SJakub Kicinski 	mutex_init(&devlink->linecards_lock);
241687125b5SJakub Kicinski 	refcount_set(&devlink->refcount, 1);
242687125b5SJakub Kicinski 
243687125b5SJakub Kicinski 	return devlink;
244687125b5SJakub Kicinski 
245687125b5SJakub Kicinski err_register_netdevice_notifier:
246687125b5SJakub Kicinski 	xa_erase(&devlinks, devlink->index);
247687125b5SJakub Kicinski err_xa_alloc:
248687125b5SJakub Kicinski 	kfree(devlink);
249687125b5SJakub Kicinski 	return NULL;
250687125b5SJakub Kicinski }
251687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_alloc_ns);
252687125b5SJakub Kicinski 
253687125b5SJakub Kicinski /**
254687125b5SJakub Kicinski  *	devlink_free - Free devlink instance resources
255687125b5SJakub Kicinski  *
256687125b5SJakub Kicinski  *	@devlink: devlink
257687125b5SJakub Kicinski  */
258687125b5SJakub Kicinski void devlink_free(struct devlink *devlink)
259687125b5SJakub Kicinski {
260687125b5SJakub Kicinski 	ASSERT_DEVLINK_NOT_REGISTERED(devlink);
261687125b5SJakub Kicinski 
262687125b5SJakub Kicinski 	mutex_destroy(&devlink->linecards_lock);
263687125b5SJakub Kicinski 	mutex_destroy(&devlink->reporters_lock);
264687125b5SJakub Kicinski 	mutex_destroy(&devlink->lock);
265687125b5SJakub Kicinski 	lockdep_unregister_key(&devlink->lock_key);
266687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->trap_policer_list));
267687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->trap_group_list));
268687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->trap_list));
269687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->reporter_list));
270687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->region_list));
271687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->param_list));
272687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->resource_list));
273687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->dpipe_table_list));
274687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->sb_list));
275687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->rate_list));
276687125b5SJakub Kicinski 	WARN_ON(!list_empty(&devlink->linecard_list));
277687125b5SJakub Kicinski 	WARN_ON(!xa_empty(&devlink->ports));
278687125b5SJakub Kicinski 
279687125b5SJakub Kicinski 	xa_destroy(&devlink->snapshot_ids);
280687125b5SJakub Kicinski 	xa_destroy(&devlink->ports);
281687125b5SJakub Kicinski 
282687125b5SJakub Kicinski 	WARN_ON_ONCE(unregister_netdevice_notifier_net(devlink_net(devlink),
283687125b5SJakub Kicinski 						       &devlink->netdevice_nb));
284687125b5SJakub Kicinski 
285687125b5SJakub Kicinski 	xa_erase(&devlinks, devlink->index);
286687125b5SJakub Kicinski 
287*9053637eSJakub Kicinski 	devlink_put(devlink);
288687125b5SJakub Kicinski }
289687125b5SJakub Kicinski EXPORT_SYMBOL_GPL(devlink_free);
290687125b5SJakub Kicinski 
291687125b5SJakub Kicinski static void __net_exit devlink_pernet_pre_exit(struct net *net)
292687125b5SJakub Kicinski {
293687125b5SJakub Kicinski 	struct devlink *devlink;
294687125b5SJakub Kicinski 	u32 actions_performed;
295687125b5SJakub Kicinski 	unsigned long index;
296687125b5SJakub Kicinski 	int err;
297687125b5SJakub Kicinski 
298687125b5SJakub Kicinski 	/* In case network namespace is getting destroyed, reload
299687125b5SJakub Kicinski 	 * all devlink instances from this namespace into init_net.
300687125b5SJakub Kicinski 	 */
301687125b5SJakub Kicinski 	devlinks_xa_for_each_registered_get(net, index, devlink) {
302687125b5SJakub Kicinski 		WARN_ON(!(devlink->features & DEVLINK_F_RELOAD));
3037a54a519SJakub Kicinski 		devl_lock(devlink);
304ed539ba6SJakub Kicinski 		err = 0;
305ed539ba6SJakub Kicinski 		if (devl_is_registered(devlink))
306687125b5SJakub Kicinski 			err = devlink_reload(devlink, &init_net,
307687125b5SJakub Kicinski 					     DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
308687125b5SJakub Kicinski 					     DEVLINK_RELOAD_LIMIT_UNSPEC,
309687125b5SJakub Kicinski 					     &actions_performed, NULL);
3107a54a519SJakub Kicinski 		devl_unlock(devlink);
3117a54a519SJakub Kicinski 		devlink_put(devlink);
3127a54a519SJakub Kicinski 
313687125b5SJakub Kicinski 		if (err && err != -EOPNOTSUPP)
314687125b5SJakub Kicinski 			pr_warn("Failed to reload devlink instance into init_net\n");
315687125b5SJakub Kicinski 	}
316687125b5SJakub Kicinski }
317687125b5SJakub Kicinski 
318687125b5SJakub Kicinski static struct pernet_operations devlink_pernet_ops __net_initdata = {
319687125b5SJakub Kicinski 	.pre_exit = devlink_pernet_pre_exit,
320687125b5SJakub Kicinski };
321687125b5SJakub Kicinski 
322687125b5SJakub Kicinski static int __init devlink_init(void)
323687125b5SJakub Kicinski {
324687125b5SJakub Kicinski 	int err;
325687125b5SJakub Kicinski 
326687125b5SJakub Kicinski 	err = genl_register_family(&devlink_nl_family);
327687125b5SJakub Kicinski 	if (err)
328687125b5SJakub Kicinski 		goto out;
329687125b5SJakub Kicinski 	err = register_pernet_subsys(&devlink_pernet_ops);
330687125b5SJakub Kicinski 
331687125b5SJakub Kicinski out:
332687125b5SJakub Kicinski 	WARN_ON(err);
333687125b5SJakub Kicinski 	return err;
334687125b5SJakub Kicinski }
335687125b5SJakub Kicinski 
336687125b5SJakub Kicinski subsys_initcall(devlink_init);
337