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