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