#include <net/devlink.h>
#include "devl_internal.h"
static LIST_HEAD(shd_list);
static DEFINE_MUTEX(shd_mutex);
struct devlink_shd {
struct list_head list;
const char *id;
refcount_t refcount;
size_t priv_size;
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);
}
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);
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);
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);