161 lines · 4.1 KB · C
RawDownload
// 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);