Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
8static LIST_HEAD(shd_list);
9static 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 */
14struct 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
22static 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
34static 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
62err_devlink_free:
63 devlink_free(devlink);
64 return NULL;
65}
66
67static 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 */
96struct 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
121unlock:
122 mutex_unlock(&shd_mutex);
123 return shd ? priv_to_devlink(shd) : NULL;
124}
125EXPORT_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 */
134void 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}
144EXPORT_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 */
155void *devlink_shd_get_priv(struct devlink *devlink)
156{
157 struct devlink_shd *shd = devlink_priv(devlink);
158
159 return shd->priv;
160}
161EXPORT_SYMBOL_GPL(devlink_shd_get_priv);