xref: /linux/net/devlink/sh_dev.c (revision 91a4855d6c03e770e42f17c798a36a3c46e63de2)
1*1850e76bSJiri Pirko // SPDX-License-Identifier: GPL-2.0-or-later
2*1850e76bSJiri Pirko /* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
3*1850e76bSJiri Pirko 
4*1850e76bSJiri Pirko #include <net/devlink.h>
5*1850e76bSJiri Pirko 
6*1850e76bSJiri Pirko #include "devl_internal.h"
7*1850e76bSJiri Pirko 
8*1850e76bSJiri Pirko static LIST_HEAD(shd_list);
9*1850e76bSJiri Pirko static DEFINE_MUTEX(shd_mutex); /* Protects shd_list and shd->list */
10*1850e76bSJiri Pirko 
11*1850e76bSJiri Pirko /* This structure represents a shared devlink instance,
12*1850e76bSJiri Pirko  * there is one created per identifier (e.g., serial number).
13*1850e76bSJiri Pirko  */
14*1850e76bSJiri Pirko struct devlink_shd {
15*1850e76bSJiri Pirko 	struct list_head list; /* Node in shd list */
16*1850e76bSJiri Pirko 	const char *id; /* Identifier string (e.g., serial number) */
17*1850e76bSJiri Pirko 	refcount_t refcount; /* Reference count */
18*1850e76bSJiri Pirko 	size_t priv_size; /* Size of driver private data */
19*1850e76bSJiri Pirko 	char priv[] __aligned(NETDEV_ALIGN) __counted_by(priv_size);
20*1850e76bSJiri Pirko };
21*1850e76bSJiri Pirko 
22*1850e76bSJiri Pirko static struct devlink_shd *devlink_shd_lookup(const char *id)
23*1850e76bSJiri Pirko {
24*1850e76bSJiri Pirko 	struct devlink_shd *shd;
25*1850e76bSJiri Pirko 
26*1850e76bSJiri Pirko 	list_for_each_entry(shd, &shd_list, list) {
27*1850e76bSJiri Pirko 		if (!strcmp(shd->id, id))
28*1850e76bSJiri Pirko 			return shd;
29*1850e76bSJiri Pirko 	}
30*1850e76bSJiri Pirko 
31*1850e76bSJiri Pirko 	return NULL;
32*1850e76bSJiri Pirko }
33*1850e76bSJiri Pirko 
34*1850e76bSJiri Pirko static struct devlink_shd *devlink_shd_create(const char *id,
35*1850e76bSJiri Pirko 					      const struct devlink_ops *ops,
36*1850e76bSJiri Pirko 					      size_t priv_size,
37*1850e76bSJiri Pirko 					      const struct device_driver *driver)
38*1850e76bSJiri Pirko {
39*1850e76bSJiri Pirko 	struct devlink_shd *shd;
40*1850e76bSJiri Pirko 	struct devlink *devlink;
41*1850e76bSJiri Pirko 
42*1850e76bSJiri Pirko 	devlink = __devlink_alloc(ops, sizeof(struct devlink_shd) + priv_size,
43*1850e76bSJiri Pirko 				  &init_net, NULL, driver);
44*1850e76bSJiri Pirko 	if (!devlink)
45*1850e76bSJiri Pirko 		return NULL;
46*1850e76bSJiri Pirko 	shd = devlink_priv(devlink);
47*1850e76bSJiri Pirko 
48*1850e76bSJiri Pirko 	shd->id = kstrdup(id, GFP_KERNEL);
49*1850e76bSJiri Pirko 	if (!shd->id)
50*1850e76bSJiri Pirko 		goto err_devlink_free;
51*1850e76bSJiri Pirko 	shd->priv_size = priv_size;
52*1850e76bSJiri Pirko 	refcount_set(&shd->refcount, 1);
53*1850e76bSJiri Pirko 
54*1850e76bSJiri Pirko 	devl_lock(devlink);
55*1850e76bSJiri Pirko 	devl_register(devlink);
56*1850e76bSJiri Pirko 	devl_unlock(devlink);
57*1850e76bSJiri Pirko 
58*1850e76bSJiri Pirko 	list_add_tail(&shd->list, &shd_list);
59*1850e76bSJiri Pirko 
60*1850e76bSJiri Pirko 	return shd;
61*1850e76bSJiri Pirko 
62*1850e76bSJiri Pirko err_devlink_free:
63*1850e76bSJiri Pirko 	devlink_free(devlink);
64*1850e76bSJiri Pirko 	return NULL;
65*1850e76bSJiri Pirko }
66*1850e76bSJiri Pirko 
67*1850e76bSJiri Pirko static void devlink_shd_destroy(struct devlink_shd *shd)
68*1850e76bSJiri Pirko {
69*1850e76bSJiri Pirko 	struct devlink *devlink = priv_to_devlink(shd);
70*1850e76bSJiri Pirko 
71*1850e76bSJiri Pirko 	list_del(&shd->list);
72*1850e76bSJiri Pirko 	devl_lock(devlink);
73*1850e76bSJiri Pirko 	devl_unregister(devlink);
74*1850e76bSJiri Pirko 	devl_unlock(devlink);
75*1850e76bSJiri Pirko 	kfree(shd->id);
76*1850e76bSJiri Pirko 	devlink_free(devlink);
77*1850e76bSJiri Pirko }
78*1850e76bSJiri Pirko 
79*1850e76bSJiri Pirko /**
80*1850e76bSJiri Pirko  * devlink_shd_get - Get or create a shared devlink instance
81*1850e76bSJiri Pirko  * @id: Identifier string (e.g., serial number) for the shared instance
82*1850e76bSJiri Pirko  * @ops: Devlink operations structure
83*1850e76bSJiri Pirko  * @priv_size: Size of private data structure
84*1850e76bSJiri Pirko  * @driver: Driver associated with the shared devlink instance
85*1850e76bSJiri Pirko  *
86*1850e76bSJiri Pirko  * Get an existing shared devlink instance identified by @id, or create
87*1850e76bSJiri Pirko  * a new one if it doesn't exist. Return the devlink instance with a
88*1850e76bSJiri Pirko  * reference held. The caller must call devlink_shd_put() when done.
89*1850e76bSJiri Pirko  *
90*1850e76bSJiri Pirko  * All callers sharing the same @id must pass identical @ops, @priv_size
91*1850e76bSJiri Pirko  * and @driver. A mismatch triggers a warning and returns NULL.
92*1850e76bSJiri Pirko  *
93*1850e76bSJiri Pirko  * Return: Pointer to the shared devlink instance on success,
94*1850e76bSJiri Pirko  *         NULL on failure
95*1850e76bSJiri Pirko  */
96*1850e76bSJiri Pirko struct devlink *devlink_shd_get(const char *id,
97*1850e76bSJiri Pirko 				const struct devlink_ops *ops,
98*1850e76bSJiri Pirko 				size_t priv_size,
99*1850e76bSJiri Pirko 				const struct device_driver *driver)
100*1850e76bSJiri Pirko {
101*1850e76bSJiri Pirko 	struct devlink *devlink;
102*1850e76bSJiri Pirko 	struct devlink_shd *shd;
103*1850e76bSJiri Pirko 
104*1850e76bSJiri Pirko 	mutex_lock(&shd_mutex);
105*1850e76bSJiri Pirko 
106*1850e76bSJiri Pirko 	shd = devlink_shd_lookup(id);
107*1850e76bSJiri Pirko 	if (!shd) {
108*1850e76bSJiri Pirko 		shd = devlink_shd_create(id, ops, priv_size, driver);
109*1850e76bSJiri Pirko 		goto unlock;
110*1850e76bSJiri Pirko 	}
111*1850e76bSJiri Pirko 
112*1850e76bSJiri Pirko 	devlink = priv_to_devlink(shd);
113*1850e76bSJiri Pirko 	if (WARN_ON_ONCE(devlink->ops != ops ||
114*1850e76bSJiri Pirko 			 shd->priv_size != priv_size ||
115*1850e76bSJiri Pirko 			 devlink->dev_driver != driver)) {
116*1850e76bSJiri Pirko 		shd = NULL;
117*1850e76bSJiri Pirko 		goto unlock;
118*1850e76bSJiri Pirko 	}
119*1850e76bSJiri Pirko 	refcount_inc(&shd->refcount);
120*1850e76bSJiri Pirko 
121*1850e76bSJiri Pirko unlock:
122*1850e76bSJiri Pirko 	mutex_unlock(&shd_mutex);
123*1850e76bSJiri Pirko 	return shd ? priv_to_devlink(shd) : NULL;
124*1850e76bSJiri Pirko }
125*1850e76bSJiri Pirko EXPORT_SYMBOL_GPL(devlink_shd_get);
126*1850e76bSJiri Pirko 
127*1850e76bSJiri Pirko /**
128*1850e76bSJiri Pirko  * devlink_shd_put - Release a reference on a shared devlink instance
129*1850e76bSJiri Pirko  * @devlink: Shared devlink instance
130*1850e76bSJiri Pirko  *
131*1850e76bSJiri Pirko  * Release a reference on a shared devlink instance obtained via
132*1850e76bSJiri Pirko  * devlink_shd_get().
133*1850e76bSJiri Pirko  */
134*1850e76bSJiri Pirko void devlink_shd_put(struct devlink *devlink)
135*1850e76bSJiri Pirko {
136*1850e76bSJiri Pirko 	struct devlink_shd *shd;
137*1850e76bSJiri Pirko 
138*1850e76bSJiri Pirko 	mutex_lock(&shd_mutex);
139*1850e76bSJiri Pirko 	shd = devlink_priv(devlink);
140*1850e76bSJiri Pirko 	if (refcount_dec_and_test(&shd->refcount))
141*1850e76bSJiri Pirko 		devlink_shd_destroy(shd);
142*1850e76bSJiri Pirko 	mutex_unlock(&shd_mutex);
143*1850e76bSJiri Pirko }
144*1850e76bSJiri Pirko EXPORT_SYMBOL_GPL(devlink_shd_put);
145*1850e76bSJiri Pirko 
146*1850e76bSJiri Pirko /**
147*1850e76bSJiri Pirko  * devlink_shd_get_priv - Get private data from shared devlink instance
148*1850e76bSJiri Pirko  * @devlink: Devlink instance
149*1850e76bSJiri Pirko  *
150*1850e76bSJiri Pirko  * Returns a pointer to the driver's private data structure within
151*1850e76bSJiri Pirko  * the shared devlink instance.
152*1850e76bSJiri Pirko  *
153*1850e76bSJiri Pirko  * Return: Pointer to private data
154*1850e76bSJiri Pirko  */
155*1850e76bSJiri Pirko void *devlink_shd_get_priv(struct devlink *devlink)
156*1850e76bSJiri Pirko {
157*1850e76bSJiri Pirko 	struct devlink_shd *shd = devlink_priv(devlink);
158*1850e76bSJiri Pirko 
159*1850e76bSJiri Pirko 	return shd->priv;
160*1850e76bSJiri Pirko }
161*1850e76bSJiri Pirko EXPORT_SYMBOL_GPL(devlink_shd_get_priv);
162