// SPDX-License-Identifier: GPL-2.0-or-later/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include <net/devlink.h> #include "devl_internal.h" static LIST_HEAD(shd_list);static DEFINE_MUTEX(shd_mutex); /* Protects shd_list and shd->list */ /* This structure represents a shared devlink instance, * there is one created per identifier (e.g., serial number). */struct devlink_shd { struct list_head list; /* Node in shd list */ const char *id; /* Identifier string (e.g., serial number) */ refcount_t refcount; /* Reference count */ size_t priv_size; /* Size of driver private data */ char priv[] __aligned(NETDEV_ALIGN) __counted_by(priv_size);}; static struct devlink_shd *devlink_shd_lookup(const char *id){ struct devlink_shd *shd; list_for_each_entry(shd, &shd_list, list) { if (!strcmp(shd->id, id)) return shd; } return NULL;} static struct devlink_shd *devlink_shd_create(const char *id, const struct devlink_ops *ops, size_t priv_size, const struct device_driver *driver){ struct devlink_shd *shd; struct devlink *devlink; devlink = __devlink_alloc(ops, sizeof(struct devlink_shd) + priv_size, &init_net, NULL, driver); if (!devlink) return NULL; shd = devlink_priv(devlink); shd->id = kstrdup(id, GFP_KERNEL); if (!shd->id) goto err_devlink_free; shd->priv_size = priv_size; refcount_set(&shd->refcount, 1); devl_lock(devlink); devl_register(devlink); devl_unlock(devlink); list_add_tail(&shd->list, &shd_list); return shd; err_devlink_free: devlink_free(devlink); return NULL;} static void devlink_shd_destroy(struct devlink_shd *shd){ struct devlink *devlink = priv_to_devlink(shd); list_del(&shd->list); devl_lock(devlink); devl_unregister(devlink); devl_unlock(devlink); kfree(shd->id); devlink_free(devlink);} /** * devlink_shd_get - Get or create a shared devlink instance * @id: Identifier string (e.g., serial number) for the shared instance * @ops: Devlink operations structure * @priv_size: Size of private data structure * @driver: Driver associated with the shared devlink instance * * Get an existing shared devlink instance identified by @id, or create * a new one if it doesn't exist. Return the devlink instance with a * reference held. The caller must call devlink_shd_put() when done. * * All callers sharing the same @id must pass identical @ops, @priv_size * and @driver. A mismatch triggers a warning and returns NULL. * * Return: Pointer to the shared devlink instance on success, * NULL on failure */struct devlink *devlink_shd_get(const char *id, const struct devlink_ops *ops, size_t priv_size, const struct device_driver *driver){ struct devlink *devlink; struct devlink_shd *shd; mutex_lock(&shd_mutex); shd = devlink_shd_lookup(id); if (!shd) { shd = devlink_shd_create(id, ops, priv_size, driver); goto unlock; } devlink = priv_to_devlink(shd); if (WARN_ON_ONCE(devlink->ops != ops || shd->priv_size != priv_size || devlink->dev_driver != driver)) { shd = NULL; goto unlock; } refcount_inc(&shd->refcount); unlock: mutex_unlock(&shd_mutex); return shd ? priv_to_devlink(shd) : NULL;}EXPORT_SYMBOL_GPL(devlink_shd_get); /** * devlink_shd_put - Release a reference on a shared devlink instance * @devlink: Shared devlink instance * * Release a reference on a shared devlink instance obtained via * devlink_shd_get(). */void devlink_shd_put(struct devlink *devlink){ struct devlink_shd *shd; mutex_lock(&shd_mutex); shd = devlink_priv(devlink); if (refcount_dec_and_test(&shd->refcount)) devlink_shd_destroy(shd); mutex_unlock(&shd_mutex);}EXPORT_SYMBOL_GPL(devlink_shd_put); /** * devlink_shd_get_priv - Get private data from shared devlink instance * @devlink: Devlink instance * * Returns a pointer to the driver's private data structure within * the shared devlink instance. * * Return: Pointer to private data */void *devlink_shd_get_priv(struct devlink *devlink){ struct devlink_shd *shd = devlink_priv(devlink); return shd->priv;}EXPORT_SYMBOL_GPL(devlink_shd_get_priv);
// SPDX-License-Identifier: GPL-2.0-or-later
/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
#include <net/devlink.h>
#include "devl_internal.h"
static LIST_HEAD(shd_list);
static DEFINE_MUTEX(shd_mutex); /* Protects shd_list and shd->list */
/* This structure represents a shared devlink instance,
* there is one created per identifier (e.g., serial number).
*/
struct devlink_shd {
struct list_head list; /* Node in shd list */
const char *id; /* Identifier string (e.g., serial number) */
refcount_t refcount; /* Reference count */
size_t priv_size; /* Size of driver private data */
char priv[] __aligned(NETDEV_ALIGN) __counted_by(priv_size);
};
static struct devlink_shd *devlink_shd_lookup(const char *id)
{
struct devlink_shd *shd;
list_for_each_entry(shd, &shd_list, list) {
if (!strcmp(shd->id, id))
return shd;
}
return NULL;
}
static struct devlink_shd *devlink_shd_create(const char *id,
const struct devlink_ops *ops,
size_t priv_size,
const struct device_driver *driver)
{
struct devlink_shd *shd;
struct devlink *devlink;
devlink = __devlink_alloc(ops, sizeof(struct devlink_shd) + priv_size,
&init_net, NULL, driver);
if (!devlink)
return NULL;
shd = devlink_priv(devlink);
shd->id = kstrdup(id, GFP_KERNEL);
if (!shd->id)
goto err_devlink_free;
shd->priv_size = priv_size;
refcount_set(&shd->refcount, 1);
devl_lock(devlink);
devl_register(devlink);
devl_unlock(devlink);
list_add_tail(&shd->list, &shd_list);
return shd;
err_devlink_free:
devlink_free(devlink);
return NULL;
}
static void devlink_shd_destroy(struct devlink_shd *shd)
{
struct devlink *devlink = priv_to_devlink(shd);
list_del(&shd->list);
devl_lock(devlink);
devl_unregister(devlink);
devl_unlock(devlink);
kfree(shd->id);
devlink_free(devlink);
}
/**
* devlink_shd_get - Get or create a shared devlink instance
* @id: Identifier string (e.g., serial number) for the shared instance
* @ops: Devlink operations structure
* @priv_size: Size of private data structure
* @driver: Driver associated with the shared devlink instance
*
* Get an existing shared devlink instance identified by @id, or create
* a new one if it doesn't exist. Return the devlink instance with a
* reference held. The caller must call devlink_shd_put() when done.
*
* All callers sharing the same @id must pass identical @ops, @priv_size
* and @driver. A mismatch triggers a warning and returns NULL.
*
* Return: Pointer to the shared devlink instance on success,
* NULL on failure
*/
struct devlink *devlink_shd_get(const char *id,
const struct devlink_ops *ops,
size_t priv_size,
const struct device_driver *driver)
{
struct devlink *devlink;
struct devlink_shd *shd;
mutex_lock(&shd_mutex);
shd = devlink_shd_lookup(id);
if (!shd) {
shd = devlink_shd_create(id, ops, priv_size, driver);
goto unlock;
}
devlink = priv_to_devlink(shd);
if (WARN_ON_ONCE(devlink->ops != ops ||
shd->priv_size != priv_size ||
devlink->dev_driver != driver)) {
shd = NULL;
goto unlock;
}
refcount_inc(&shd->refcount);
unlock:
mutex_unlock(&shd_mutex);
return shd ? priv_to_devlink(shd) : NULL;
}
EXPORT_SYMBOL_GPL(devlink_shd_get);
/**
* devlink_shd_put - Release a reference on a shared devlink instance
* @devlink: Shared devlink instance
*
* Release a reference on a shared devlink instance obtained via
* devlink_shd_get().
*/
void devlink_shd_put(struct devlink *devlink)
{
struct devlink_shd *shd;
mutex_lock(&shd_mutex);
shd = devlink_priv(devlink);
if (refcount_dec_and_test(&shd->refcount))
devlink_shd_destroy(shd);
mutex_unlock(&shd_mutex);
}
EXPORT_SYMBOL_GPL(devlink_shd_put);
/**
* devlink_shd_get_priv - Get private data from shared devlink instance
* @devlink: Devlink instance
*
* Returns a pointer to the driver's private data structure within
* the shared devlink instance.
*
* Return: Pointer to private data
*/
void *devlink_shd_get_priv(struct devlink *devlink)
{
struct devlink_shd *shd = devlink_priv(devlink);
return shd->priv;
}
EXPORT_SYMBOL_GPL(devlink_shd_get_priv);