mirror of https://github.com/torvalds/linux.git
162 lines
4.1 KiB
C
162 lines
4.1 KiB
C
// 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);
|