Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-y := core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
resource.o param.o region.o health.o trap.o rate.o linecard.o sh_dev.o
+602
View File
@@ -0,0 +1,602 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include <net/genetlink.h>
#define CREATE_TRACE_POINTS
#include <trace/events/devlink.h>
#include "devl_internal.h"
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
static struct devlink *devlinks_xa_get(unsigned long index)
{
struct devlink *devlink;
rcu_read_lock();
devlink = xa_find(&devlinks, &index, index, DEVLINK_REGISTERED);
if (!devlink || !devlink_try_get(devlink))
devlink = NULL;
rcu_read_unlock();
return devlink;
}
/* devlink_rels xarray contains 1:1 relationships between
* devlink object and related nested devlink instance.
* The xarray index is used to get the nested object from
* the nested-in object code.
*/
static DEFINE_XARRAY_FLAGS(devlink_rels, XA_FLAGS_ALLOC1);
#define DEVLINK_REL_IN_USE XA_MARK_0
struct devlink_rel {
u32 index;
refcount_t refcount;
u32 devlink_index;
struct {
u32 devlink_index;
u32 obj_index;
devlink_rel_notify_cb_t *notify_cb;
devlink_rel_cleanup_cb_t *cleanup_cb;
struct delayed_work notify_work;
} nested_in;
};
static void devlink_rel_free(struct devlink_rel *rel)
{
xa_erase(&devlink_rels, rel->index);
kfree(rel);
}
static void __devlink_rel_get(struct devlink_rel *rel)
{
refcount_inc(&rel->refcount);
}
static void __devlink_rel_put(struct devlink_rel *rel)
{
if (refcount_dec_and_test(&rel->refcount))
devlink_rel_free(rel);
}
static void devlink_rel_nested_in_notify_work(struct work_struct *work)
{
struct devlink_rel *rel = container_of(work, struct devlink_rel,
nested_in.notify_work.work);
struct devlink *devlink;
devlink = devlinks_xa_get(rel->nested_in.devlink_index);
if (!devlink)
goto rel_put;
if (!devl_trylock(devlink)) {
devlink_put(devlink);
goto reschedule_work;
}
if (!devl_is_registered(devlink)) {
devl_unlock(devlink);
devlink_put(devlink);
goto rel_put;
}
if (!xa_get_mark(&devlink_rels, rel->index, DEVLINK_REL_IN_USE))
rel->nested_in.cleanup_cb(devlink, rel->nested_in.obj_index, rel->index);
rel->nested_in.notify_cb(devlink, rel->nested_in.obj_index);
devl_unlock(devlink);
devlink_put(devlink);
rel_put:
__devlink_rel_put(rel);
return;
reschedule_work:
schedule_delayed_work(&rel->nested_in.notify_work, 1);
}
static void devlink_rel_nested_in_notify_work_schedule(struct devlink_rel *rel)
{
__devlink_rel_get(rel);
schedule_delayed_work(&rel->nested_in.notify_work, 0);
}
static struct devlink_rel *devlink_rel_alloc(void)
{
struct devlink_rel *rel;
static u32 next;
int err;
rel = kzalloc_obj(*rel);
if (!rel)
return ERR_PTR(-ENOMEM);
err = xa_alloc_cyclic(&devlink_rels, &rel->index, rel,
xa_limit_32b, &next, GFP_KERNEL);
if (err < 0) {
kfree(rel);
return ERR_PTR(err);
}
refcount_set(&rel->refcount, 1);
INIT_DELAYED_WORK(&rel->nested_in.notify_work,
&devlink_rel_nested_in_notify_work);
return rel;
}
static void devlink_rel_put(struct devlink *devlink)
{
struct devlink_rel *rel = devlink->rel;
if (!rel)
return;
xa_clear_mark(&devlink_rels, rel->index, DEVLINK_REL_IN_USE);
devlink_rel_nested_in_notify_work_schedule(rel);
__devlink_rel_put(rel);
devlink->rel = NULL;
}
void devlink_rel_nested_in_clear(u32 rel_index)
{
xa_clear_mark(&devlink_rels, rel_index, DEVLINK_REL_IN_USE);
}
int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index,
u32 obj_index, devlink_rel_notify_cb_t *notify_cb,
devlink_rel_cleanup_cb_t *cleanup_cb,
struct devlink *devlink)
{
struct devlink_rel *rel = devlink_rel_alloc();
ASSERT_DEVLINK_NOT_REGISTERED(devlink);
if (IS_ERR(rel))
return PTR_ERR(rel);
rel->devlink_index = devlink->index;
rel->nested_in.devlink_index = devlink_index;
rel->nested_in.obj_index = obj_index;
rel->nested_in.notify_cb = notify_cb;
rel->nested_in.cleanup_cb = cleanup_cb;
*rel_index = rel->index;
xa_set_mark(&devlink_rels, rel->index, DEVLINK_REL_IN_USE);
devlink->rel = rel;
return 0;
}
/**
* devlink_rel_nested_in_notify - Notify the object this devlink
* instance is nested in.
* @devlink: devlink
*
* This is called upon network namespace change of devlink instance.
* In case this devlink instance is nested in another devlink object,
* a notification of a change of this object should be sent
* over netlink. The parent devlink instance lock needs to be
* taken during the notification preparation.
* Since the parent may or may not be locked, 'work' is utilized.
*/
void devlink_rel_nested_in_notify(struct devlink *devlink)
{
struct devlink_rel *rel = devlink->rel;
if (!rel)
return;
devlink_rel_nested_in_notify_work_schedule(rel);
}
static struct devlink_rel *devlink_rel_find(unsigned long rel_index)
{
return xa_find(&devlink_rels, &rel_index, rel_index,
DEVLINK_REL_IN_USE);
}
static struct devlink *devlink_rel_devlink_get(u32 rel_index)
{
struct devlink_rel *rel;
u32 devlink_index;
if (!rel_index)
return NULL;
xa_lock(&devlink_rels);
rel = devlink_rel_find(rel_index);
if (rel)
devlink_index = rel->devlink_index;
xa_unlock(&devlink_rels);
if (!rel)
return NULL;
return devlinks_xa_get(devlink_index);
}
int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink,
u32 rel_index, int attrtype,
bool *msg_updated)
{
struct net *net = devlink_net(devlink);
struct devlink *rel_devlink;
int err;
rel_devlink = devlink_rel_devlink_get(rel_index);
if (!rel_devlink)
return 0;
err = devlink_nl_put_nested_handle(msg, net, rel_devlink, attrtype);
devlink_put(rel_devlink);
if (!err && msg_updated)
*msg_updated = true;
return err;
}
void *devlink_priv(struct devlink *devlink)
{
return &devlink->priv;
}
EXPORT_SYMBOL_GPL(devlink_priv);
struct devlink *priv_to_devlink(void *priv)
{
return container_of(priv, struct devlink, priv);
}
EXPORT_SYMBOL_GPL(priv_to_devlink);
struct device *devlink_to_dev(const struct devlink *devlink)
{
return devlink->dev;
}
EXPORT_SYMBOL_GPL(devlink_to_dev);
const char *devlink_bus_name(const struct devlink *devlink)
{
return devlink->dev ? devlink->dev->bus->name : DEVLINK_INDEX_BUS_NAME;
}
EXPORT_SYMBOL_GPL(devlink_bus_name);
const char *devlink_dev_name(const struct devlink *devlink)
{
return devlink->dev ? dev_name(devlink->dev) : devlink->dev_name_index;
}
EXPORT_SYMBOL_GPL(devlink_dev_name);
const char *devlink_dev_driver_name(const struct devlink *devlink)
{
return devlink->dev_driver->name;
}
EXPORT_SYMBOL_GPL(devlink_dev_driver_name);
struct net *devlink_net(const struct devlink *devlink)
{
return read_pnet(&devlink->_net);
}
EXPORT_SYMBOL_GPL(devlink_net);
void devl_assert_locked(struct devlink *devlink)
{
lockdep_assert_held(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_assert_locked);
#ifdef CONFIG_LOCKDEP
/* For use in conjunction with LOCKDEP only e.g. rcu_dereference_protected() */
bool devl_lock_is_held(struct devlink *devlink)
{
return lockdep_is_held(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_lock_is_held);
#endif
void devl_lock(struct devlink *devlink)
{
mutex_lock(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_lock);
int devl_trylock(struct devlink *devlink)
{
return mutex_trylock(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_trylock);
void devl_unlock(struct devlink *devlink)
{
mutex_unlock(&devlink->lock);
}
EXPORT_SYMBOL_GPL(devl_unlock);
/**
* devlink_try_get() - try to obtain a reference on a devlink instance
* @devlink: instance to reference
*
* Obtain a reference on a devlink instance. A reference on a devlink instance
* only implies that it's safe to take the instance lock. It does not imply
* that the instance is registered, use devl_is_registered() after taking
* the instance lock to check registration status.
*/
struct devlink *__must_check devlink_try_get(struct devlink *devlink)
{
if (refcount_inc_not_zero(&devlink->refcount))
return devlink;
return NULL;
}
static void devlink_release(struct work_struct *work)
{
struct devlink *devlink;
devlink = container_of(to_rcu_work(work), struct devlink, rwork);
mutex_destroy(&devlink->lock);
lockdep_unregister_key(&devlink->lock_key);
if (devlink->dev)
put_device(devlink->dev);
else
kfree(devlink->dev_name_index);
kvfree(devlink);
}
void devlink_put(struct devlink *devlink)
{
if (refcount_dec_and_test(&devlink->refcount))
queue_rcu_work(system_percpu_wq, &devlink->rwork);
}
static struct devlink *__devlinks_xa_find_get(struct net *net,
unsigned long *indexp,
unsigned long end)
{
struct devlink *devlink = NULL;
rcu_read_lock();
retry:
devlink = xa_find(&devlinks, indexp, end, DEVLINK_REGISTERED);
if (!devlink)
goto unlock;
if (!devlink_try_get(devlink))
goto next;
if (!net_eq(devlink_net(devlink), net)) {
devlink_put(devlink);
goto next;
}
unlock:
rcu_read_unlock();
return devlink;
next:
(*indexp)++;
goto retry;
}
struct devlink *devlinks_xa_find_get(struct net *net, unsigned long *indexp)
{
return __devlinks_xa_find_get(net, indexp, ULONG_MAX);
}
struct devlink *devlinks_xa_lookup_get(struct net *net, unsigned long index)
{
return __devlinks_xa_find_get(net, &index, index);
}
/**
* devl_register - Register devlink instance
* @devlink: devlink
*/
int devl_register(struct devlink *devlink)
{
ASSERT_DEVLINK_NOT_REGISTERED(devlink);
devl_assert_locked(devlink);
xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
devlink_notify_register(devlink);
devlink_rel_nested_in_notify(devlink);
return 0;
}
EXPORT_SYMBOL_GPL(devl_register);
void devlink_register(struct devlink *devlink)
{
devl_lock(devlink);
devl_register(devlink);
devl_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devlink_register);
/**
* devl_unregister - Unregister devlink instance
* @devlink: devlink
*/
void devl_unregister(struct devlink *devlink)
{
ASSERT_DEVLINK_REGISTERED(devlink);
devl_assert_locked(devlink);
devlink_notify_unregister(devlink);
xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
devlink_rel_put(devlink);
}
EXPORT_SYMBOL_GPL(devl_unregister);
void devlink_unregister(struct devlink *devlink)
{
devl_lock(devlink);
devl_unregister(devlink);
devl_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devlink_unregister);
struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
struct net *net, struct device *dev,
const struct device_driver *dev_driver)
{
struct devlink *devlink;
static u32 last_id;
int ret;
WARN_ON(!ops || !dev_driver);
if (!devlink_reload_actions_valid(ops))
return NULL;
devlink = kvzalloc_flex(*devlink, priv, priv_size);
if (!devlink)
return NULL;
ret = xa_alloc_cyclic(&devlinks, &devlink->index, devlink, xa_limit_31b,
&last_id, GFP_KERNEL);
if (ret < 0)
goto err_xa_alloc;
if (dev) {
devlink->dev = get_device(dev);
} else {
devlink->dev_name_index = kasprintf(GFP_KERNEL, "%u", devlink->index);
if (!devlink->dev_name_index)
goto err_kasprintf;
}
devlink->ops = ops;
devlink->dev_driver = dev_driver;
xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC);
xa_init_flags(&devlink->params, XA_FLAGS_ALLOC);
xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC);
xa_init_flags(&devlink->nested_rels, XA_FLAGS_ALLOC);
write_pnet(&devlink->_net, net);
INIT_LIST_HEAD(&devlink->rate_list);
INIT_LIST_HEAD(&devlink->linecard_list);
INIT_LIST_HEAD(&devlink->sb_list);
INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list);
INIT_LIST_HEAD(&devlink->resource_list);
INIT_LIST_HEAD(&devlink->region_list);
INIT_LIST_HEAD(&devlink->reporter_list);
INIT_LIST_HEAD(&devlink->trap_list);
INIT_LIST_HEAD(&devlink->trap_group_list);
INIT_LIST_HEAD(&devlink->trap_policer_list);
INIT_RCU_WORK(&devlink->rwork, devlink_release);
lockdep_register_key(&devlink->lock_key);
mutex_init(&devlink->lock);
lockdep_set_class(&devlink->lock, &devlink->lock_key);
refcount_set(&devlink->refcount, 1);
return devlink;
err_kasprintf:
xa_erase(&devlinks, devlink->index);
err_xa_alloc:
kvfree(devlink);
return NULL;
}
/**
* devlink_alloc_ns - Allocate new devlink instance resources
* in specific namespace
*
* @ops: ops
* @priv_size: size of user private data
* @net: net namespace
* @dev: parent device
*
* Allocate new devlink instance resources, including devlink index
* and name.
*/
struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
size_t priv_size, struct net *net,
struct device *dev)
{
WARN_ON(!dev);
return __devlink_alloc(ops, priv_size, net, dev, dev->driver);
}
EXPORT_SYMBOL_GPL(devlink_alloc_ns);
/**
* devlink_free - Free devlink instance resources
*
* @devlink: devlink
*/
void devlink_free(struct devlink *devlink)
{
ASSERT_DEVLINK_NOT_REGISTERED(devlink);
devlink_rel_put(devlink);
WARN_ON(!list_empty(&devlink->trap_policer_list));
WARN_ON(!list_empty(&devlink->trap_group_list));
WARN_ON(!list_empty(&devlink->trap_list));
WARN_ON(!list_empty(&devlink->reporter_list));
WARN_ON(!list_empty(&devlink->region_list));
WARN_ON(!list_empty(&devlink->resource_list));
WARN_ON(!list_empty(&devlink->dpipe_table_list));
WARN_ON(!list_empty(&devlink->sb_list));
WARN_ON(devlink_rates_check(devlink, NULL, NULL));
WARN_ON(!list_empty(&devlink->linecard_list));
WARN_ON(!xa_empty(&devlink->ports));
xa_destroy(&devlink->nested_rels);
xa_destroy(&devlink->snapshot_ids);
xa_destroy(&devlink->params);
xa_destroy(&devlink->ports);
xa_erase(&devlinks, devlink->index);
devlink_put(devlink);
}
EXPORT_SYMBOL_GPL(devlink_free);
static void __net_exit devlink_pernet_pre_exit(struct net *net)
{
struct devlink *devlink;
u32 actions_performed;
unsigned long index;
int err;
/* In case network namespace is getting destroyed, reload
* all devlink instances from this namespace into init_net.
*/
devlinks_xa_for_each_registered_get(net, index, devlink) {
devl_dev_lock(devlink, true);
err = 0;
if (devl_is_registered(devlink))
err = devlink_reload(devlink, &init_net,
DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
DEVLINK_RELOAD_LIMIT_UNSPEC,
&actions_performed, NULL);
devl_dev_unlock(devlink, true);
devlink_put(devlink);
if (err && err != -EOPNOTSUPP)
pr_warn("Failed to reload devlink instance into init_net\n");
}
}
static struct pernet_operations devlink_pernet_ops __net_initdata = {
.pre_exit = devlink_pernet_pre_exit,
};
static struct notifier_block devlink_port_netdevice_nb = {
.notifier_call = devlink_port_netdevice_event,
};
static int __init devlink_init(void)
{
int err;
err = register_pernet_subsys(&devlink_pernet_ops);
if (err)
goto out;
err = genl_register_family(&devlink_nl_family);
if (err)
goto out_unreg_pernet_subsys;
err = register_netdevice_notifier(&devlink_port_netdevice_nb);
if (!err)
return 0;
genl_unregister_family(&devlink_nl_family);
out_unreg_pernet_subsys:
unregister_pernet_subsys(&devlink_pernet_ops);
out:
WARN_ON(err);
return err;
}
subsys_initcall(devlink_init);
File diff suppressed because it is too large Load Diff
+333
View File
@@ -0,0 +1,333 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
#include <net/devlink.h>
#include <net/net_namespace.h>
#include <net/rtnetlink.h>
#include <rdma/ib_verbs.h>
#include "netlink_gen.h"
struct devlink_rel;
#define DEVLINK_REGISTERED XA_MARK_1
#define DEVLINK_RELOAD_STATS_ARRAY_SIZE \
(__DEVLINK_RELOAD_LIMIT_MAX * __DEVLINK_RELOAD_ACTION_MAX)
struct devlink_dev_stats {
u32 reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];
u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];
};
struct devlink {
u32 index;
struct xarray ports;
struct list_head rate_list;
struct list_head sb_list;
struct list_head dpipe_table_list;
struct list_head resource_list;
struct xarray params;
struct list_head region_list;
struct list_head reporter_list;
struct devlink_dpipe_headers *dpipe_headers;
struct list_head trap_list;
struct list_head trap_group_list;
struct list_head trap_policer_list;
struct list_head linecard_list;
const struct devlink_ops *ops;
struct xarray snapshot_ids;
struct devlink_dev_stats stats;
struct device *dev;
const char *dev_name_index;
const struct device_driver *dev_driver;
possible_net_t _net;
/* Serializes access to devlink instance specific objects such as
* port, sb, dpipe, resource, params, region, traps and more.
*/
struct mutex lock;
struct lock_class_key lock_key;
u8 reload_failed:1;
refcount_t refcount;
struct rcu_work rwork;
struct devlink_rel *rel;
struct xarray nested_rels;
char priv[] __aligned(NETDEV_ALIGN);
};
extern struct xarray devlinks;
extern struct genl_family devlink_nl_family;
struct devlink *__devlink_alloc(const struct devlink_ops *ops, size_t priv_size,
struct net *net, struct device *dev,
const struct device_driver *dev_driver);
#define devl_warn(devlink, format, args...) \
do { \
if ((devlink)->dev) \
dev_warn((devlink)->dev, format, ##args); \
else \
pr_warn("devlink (%s): " format, \
devlink_dev_name(devlink), ##args); \
} while (0)
/* devlink instances are open to the access from the user space after
* devlink_register() call. Such logical barrier allows us to have certain
* expectations related to locking.
*
* Before *_register() - we are in initialization stage and no parallel
* access possible to the devlink instance. All drivers perform that phase
* by implicitly holding device_lock.
*
* After *_register() - users and driver can access devlink instance at
* the same time.
*/
#define ASSERT_DEVLINK_REGISTERED(d) \
WARN_ON_ONCE(!xa_get_mark(&devlinks, (d)->index, DEVLINK_REGISTERED))
#define ASSERT_DEVLINK_NOT_REGISTERED(d) \
WARN_ON_ONCE(xa_get_mark(&devlinks, (d)->index, DEVLINK_REGISTERED))
/* Iterate over devlink pointers which were possible to get reference to.
* devlink_put() needs to be called for each iterated devlink pointer
* in loop body in order to release the reference.
*/
#define devlinks_xa_for_each_registered_get(net, index, devlink) \
for (index = 0; (devlink = devlinks_xa_find_get(net, &index)); index++)
struct devlink *devlinks_xa_find_get(struct net *net, unsigned long *indexp);
struct devlink *devlinks_xa_lookup_get(struct net *net, unsigned long index);
static inline bool __devl_is_registered(struct devlink *devlink)
{
return xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
}
static inline bool devl_is_registered(struct devlink *devlink)
{
devl_assert_locked(devlink);
return __devl_is_registered(devlink);
}
static inline void devl_dev_lock(struct devlink *devlink, bool dev_lock)
{
if (dev_lock && devlink->dev)
device_lock(devlink->dev);
devl_lock(devlink);
}
static inline void devl_dev_unlock(struct devlink *devlink, bool dev_lock)
{
devl_unlock(devlink);
if (dev_lock && devlink->dev)
device_unlock(devlink->dev);
}
typedef void devlink_rel_notify_cb_t(struct devlink *devlink, u32 obj_index);
typedef void devlink_rel_cleanup_cb_t(struct devlink *devlink, u32 obj_index,
u32 rel_index);
void devlink_rel_nested_in_clear(u32 rel_index);
int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index,
u32 obj_index, devlink_rel_notify_cb_t *notify_cb,
devlink_rel_cleanup_cb_t *cleanup_cb,
struct devlink *devlink);
void devlink_rel_nested_in_notify(struct devlink *devlink);
int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink,
u32 rel_index, int attrtype,
bool *msg_updated);
/* Netlink */
enum devlink_multicast_groups {
DEVLINK_MCGRP_CONFIG,
};
/* state held across netlink dumps */
struct devlink_nl_dump_state {
unsigned long instance;
int idx;
union {
/* DEVLINK_CMD_REGION_READ */
struct {
u64 start_offset;
};
/* DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET */
struct {
u64 dump_ts;
};
/* DEVLINK_CMD_RESOURCE_DUMP */
struct {
u32 index;
bool index_valid;
} port_ctx;
};
};
typedef int devlink_nl_dump_one_func_t(struct sk_buff *msg,
struct devlink *devlink,
struct netlink_callback *cb,
int flags);
struct devlink *
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
bool dev_lock);
int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
devlink_nl_dump_one_func_t *dump_one);
static inline struct devlink_nl_dump_state *
devlink_dump_state(struct netlink_callback *cb)
{
NL_ASSERT_CTX_FITS(struct devlink_nl_dump_state);
return (struct devlink_nl_dump_state *)cb->ctx;
}
static inline int
devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink)
{
if (nla_put_string(msg, DEVLINK_ATTR_BUS_NAME, devlink_bus_name(devlink)))
return -EMSGSIZE;
if (nla_put_string(msg, DEVLINK_ATTR_DEV_NAME, devlink_dev_name(devlink)))
return -EMSGSIZE;
if (nla_put_uint(msg, DEVLINK_ATTR_INDEX, devlink->index))
return -EMSGSIZE;
return 0;
}
static inline int devlink_nl_put_u64(struct sk_buff *msg, int attrtype, u64 val)
{
return nla_put_u64_64bit(msg, attrtype, val, DEVLINK_ATTR_PAD);
}
int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net,
struct devlink *devlink, int attrtype);
int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info);
static inline bool devlink_nl_notify_need(struct devlink *devlink)
{
return genl_has_listeners(&devlink_nl_family, devlink_net(devlink),
DEVLINK_MCGRP_CONFIG);
}
struct devlink_obj_desc {
struct rcu_head rcu;
const char *bus_name;
const char *dev_name;
unsigned int port_index;
bool port_index_valid;
unsigned int devlink_index;
bool devlink_index_valid;
long data[];
};
static inline void devlink_nl_obj_desc_init(struct devlink_obj_desc *desc,
struct devlink *devlink)
{
memset(desc, 0, sizeof(*desc));
desc->bus_name = devlink_bus_name(devlink);
desc->dev_name = devlink_dev_name(devlink);
desc->devlink_index = devlink->index;
desc->devlink_index_valid = true;
}
static inline void devlink_nl_obj_desc_port_set(struct devlink_obj_desc *desc,
struct devlink_port *devlink_port)
{
desc->port_index = devlink_port->index;
desc->port_index_valid = true;
}
int devlink_nl_notify_filter(struct sock *dsk, struct sk_buff *skb, void *data);
static inline void devlink_nl_notify_send_desc(struct devlink *devlink,
struct sk_buff *msg,
struct devlink_obj_desc *desc)
{
genlmsg_multicast_netns_filtered(&devlink_nl_family,
devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG,
GFP_KERNEL,
devlink_nl_notify_filter, desc);
}
static inline void devlink_nl_notify_send(struct devlink *devlink,
struct sk_buff *msg)
{
struct devlink_obj_desc desc;
devlink_nl_obj_desc_init(&desc, devlink);
devlink_nl_notify_send_desc(devlink, msg, &desc);
}
/* Notify */
void devlink_notify_register(struct devlink *devlink);
void devlink_notify_unregister(struct devlink *devlink);
void devlink_ports_notify_register(struct devlink *devlink);
void devlink_ports_notify_unregister(struct devlink *devlink);
void devlink_params_notify_register(struct devlink *devlink);
void devlink_params_notify_unregister(struct devlink *devlink);
void devlink_regions_notify_register(struct devlink *devlink);
void devlink_regions_notify_unregister(struct devlink *devlink);
void devlink_trap_policers_notify_register(struct devlink *devlink);
void devlink_trap_policers_notify_unregister(struct devlink *devlink);
void devlink_trap_groups_notify_register(struct devlink *devlink);
void devlink_trap_groups_notify_unregister(struct devlink *devlink);
void devlink_traps_notify_register(struct devlink *devlink);
void devlink_traps_notify_unregister(struct devlink *devlink);
void devlink_rates_notify_register(struct devlink *devlink);
void devlink_rates_notify_unregister(struct devlink *devlink);
void devlink_linecards_notify_register(struct devlink *devlink);
void devlink_linecards_notify_unregister(struct devlink *devlink);
/* Ports */
#define ASSERT_DEVLINK_PORT_INITIALIZED(devlink_port) \
WARN_ON_ONCE(!(devlink_port)->initialized)
struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
unsigned int port_index);
int devlink_port_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr);
struct devlink_port *
devlink_port_get_from_info(struct devlink *devlink, struct genl_info *info);
struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,
struct nlattr **attrs);
/* Reload */
bool devlink_reload_actions_valid(const struct devlink_ops *ops);
int devlink_reload(struct devlink *devlink, struct net *dest_net,
enum devlink_reload_action action,
enum devlink_reload_limit limit,
u32 *actions_performed, struct netlink_ext_ack *extack);
static inline bool devlink_reload_supported(const struct devlink_ops *ops)
{
return ops->reload_down && ops->reload_up;
}
/* Params */
void devlink_params_driverinit_load_new(struct devlink *devlink);
/* Resources */
struct devlink_resource;
int devlink_resources_validate(struct devlink *devlink,
struct devlink_resource *resource,
struct genl_info *info);
/* Rates */
bool devlink_rate_is_node(const struct devlink_rate *devlink_rate);
int devlink_rates_check(struct devlink *devlink,
bool (*rate_filter)(const struct devlink_rate *),
struct netlink_ext_ack *extack);
/* Linecards */
unsigned int devlink_linecard_index(struct devlink_linecard *linecard);
+915
View File
@@ -0,0 +1,915 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include "devl_internal.h"
static struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = {
{
.name = "destination mac",
.id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC,
.bitwidth = 48,
},
};
struct devlink_dpipe_header devlink_dpipe_header_ethernet = {
.name = "ethernet",
.id = DEVLINK_DPIPE_HEADER_ETHERNET,
.fields = devlink_dpipe_fields_ethernet,
.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ethernet),
.global = true,
};
EXPORT_SYMBOL_GPL(devlink_dpipe_header_ethernet);
static struct devlink_dpipe_field devlink_dpipe_fields_ipv4[] = {
{
.name = "destination ip",
.id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP,
.bitwidth = 32,
},
};
struct devlink_dpipe_header devlink_dpipe_header_ipv4 = {
.name = "ipv4",
.id = DEVLINK_DPIPE_HEADER_IPV4,
.fields = devlink_dpipe_fields_ipv4,
.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv4),
.global = true,
};
EXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv4);
static struct devlink_dpipe_field devlink_dpipe_fields_ipv6[] = {
{
.name = "destination ip",
.id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP,
.bitwidth = 128,
},
};
struct devlink_dpipe_header devlink_dpipe_header_ipv6 = {
.name = "ipv6",
.id = DEVLINK_DPIPE_HEADER_IPV6,
.fields = devlink_dpipe_fields_ipv6,
.fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv6),
.global = true,
};
EXPORT_SYMBOL_GPL(devlink_dpipe_header_ipv6);
int devlink_dpipe_match_put(struct sk_buff *skb,
struct devlink_dpipe_match *match)
{
struct devlink_dpipe_header *header = match->header;
struct devlink_dpipe_field *field = &header->fields[match->field_id];
struct nlattr *match_attr;
match_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_MATCH);
if (!match_attr)
return -EMSGSIZE;
if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_MATCH_TYPE, match->type) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, match->header_index) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
goto nla_put_failure;
nla_nest_end(skb, match_attr);
return 0;
nla_put_failure:
nla_nest_cancel(skb, match_attr);
return -EMSGSIZE;
}
EXPORT_SYMBOL_GPL(devlink_dpipe_match_put);
static int devlink_dpipe_matches_put(struct devlink_dpipe_table *table,
struct sk_buff *skb)
{
struct nlattr *matches_attr;
matches_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_TABLE_MATCHES);
if (!matches_attr)
return -EMSGSIZE;
if (table->table_ops->matches_dump(table->priv, skb))
goto nla_put_failure;
nla_nest_end(skb, matches_attr);
return 0;
nla_put_failure:
nla_nest_cancel(skb, matches_attr);
return -EMSGSIZE;
}
int devlink_dpipe_action_put(struct sk_buff *skb,
struct devlink_dpipe_action *action)
{
struct devlink_dpipe_header *header = action->header;
struct devlink_dpipe_field *field = &header->fields[action->field_id];
struct nlattr *action_attr;
action_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_ACTION);
if (!action_attr)
return -EMSGSIZE;
if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_ACTION_TYPE, action->type) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, action->header_index) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
goto nla_put_failure;
nla_nest_end(skb, action_attr);
return 0;
nla_put_failure:
nla_nest_cancel(skb, action_attr);
return -EMSGSIZE;
}
EXPORT_SYMBOL_GPL(devlink_dpipe_action_put);
static int devlink_dpipe_actions_put(struct devlink_dpipe_table *table,
struct sk_buff *skb)
{
struct nlattr *actions_attr;
actions_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_TABLE_ACTIONS);
if (!actions_attr)
return -EMSGSIZE;
if (table->table_ops->actions_dump(table->priv, skb))
goto nla_put_failure;
nla_nest_end(skb, actions_attr);
return 0;
nla_put_failure:
nla_nest_cancel(skb, actions_attr);
return -EMSGSIZE;
}
static int devlink_dpipe_table_put(struct sk_buff *skb,
struct devlink_dpipe_table *table)
{
struct nlattr *table_attr;
u64 table_size;
table_size = table->table_ops->size_get(table->priv);
table_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_TABLE);
if (!table_attr)
return -EMSGSIZE;
if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) ||
devlink_nl_put_u64(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table_size))
goto nla_put_failure;
if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED,
table->counters_enabled))
goto nla_put_failure;
if (table->resource_valid) {
if (devlink_nl_put_u64(skb, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID,
table->resource_id) ||
devlink_nl_put_u64(skb, DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS,
table->resource_units))
goto nla_put_failure;
}
if (devlink_dpipe_matches_put(table, skb))
goto nla_put_failure;
if (devlink_dpipe_actions_put(table, skb))
goto nla_put_failure;
nla_nest_end(skb, table_attr);
return 0;
nla_put_failure:
nla_nest_cancel(skb, table_attr);
return -EMSGSIZE;
}
static int devlink_dpipe_send_and_alloc_skb(struct sk_buff **pskb,
struct genl_info *info)
{
int err;
if (*pskb) {
err = genlmsg_reply(*pskb, info);
if (err)
return err;
}
*pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!*pskb)
return -ENOMEM;
return 0;
}
static int devlink_dpipe_tables_fill(struct genl_info *info,
enum devlink_command cmd, int flags,
struct list_head *dpipe_tables,
const char *table_name)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_dpipe_table *table;
struct nlattr *tables_attr;
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
bool incomplete;
void *hdr;
int i;
int err;
table = list_first_entry(dpipe_tables,
struct devlink_dpipe_table, list);
start_again:
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
if (err)
return err;
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
&devlink_nl_family, NLM_F_MULTI, cmd);
if (!hdr) {
nlmsg_free(skb);
return -EMSGSIZE;
}
if (devlink_nl_put_handle(skb, devlink))
goto nla_put_failure;
tables_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_TABLES);
if (!tables_attr)
goto nla_put_failure;
i = 0;
incomplete = false;
list_for_each_entry_from(table, dpipe_tables, list) {
if (!table_name) {
err = devlink_dpipe_table_put(skb, table);
if (err) {
if (!i)
goto err_table_put;
incomplete = true;
break;
}
} else {
if (!strcmp(table->name, table_name)) {
err = devlink_dpipe_table_put(skb, table);
if (err)
break;
}
}
i++;
}
nla_nest_end(skb, tables_attr);
genlmsg_end(skb, hdr);
if (incomplete)
goto start_again;
send_done:
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
NLMSG_DONE, 0, flags | NLM_F_MULTI);
if (!nlh) {
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
if (err)
return err;
goto send_done;
}
return genlmsg_reply(skb, info);
nla_put_failure:
err = -EMSGSIZE;
err_table_put:
nlmsg_free(skb);
return err;
}
int devlink_nl_dpipe_table_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
const char *table_name = NULL;
if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME])
table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
return devlink_dpipe_tables_fill(info, DEVLINK_CMD_DPIPE_TABLE_GET, 0,
&devlink->dpipe_table_list,
table_name);
}
static int devlink_dpipe_value_put(struct sk_buff *skb,
struct devlink_dpipe_value *value)
{
if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE,
value->value_size, value->value))
return -EMSGSIZE;
if (value->mask)
if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE_MASK,
value->value_size, value->mask))
return -EMSGSIZE;
if (value->mapping_valid)
if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_VALUE_MAPPING,
value->mapping_value))
return -EMSGSIZE;
return 0;
}
static int devlink_dpipe_action_value_put(struct sk_buff *skb,
struct devlink_dpipe_value *value)
{
if (!value->action)
return -EINVAL;
if (devlink_dpipe_action_put(skb, value->action))
return -EMSGSIZE;
if (devlink_dpipe_value_put(skb, value))
return -EMSGSIZE;
return 0;
}
static int devlink_dpipe_action_values_put(struct sk_buff *skb,
struct devlink_dpipe_value *values,
unsigned int values_count)
{
struct nlattr *action_attr;
int i;
int err;
for (i = 0; i < values_count; i++) {
action_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_ACTION_VALUE);
if (!action_attr)
return -EMSGSIZE;
err = devlink_dpipe_action_value_put(skb, &values[i]);
if (err)
goto err_action_value_put;
nla_nest_end(skb, action_attr);
}
return 0;
err_action_value_put:
nla_nest_cancel(skb, action_attr);
return err;
}
static int devlink_dpipe_match_value_put(struct sk_buff *skb,
struct devlink_dpipe_value *value)
{
if (!value->match)
return -EINVAL;
if (devlink_dpipe_match_put(skb, value->match))
return -EMSGSIZE;
if (devlink_dpipe_value_put(skb, value))
return -EMSGSIZE;
return 0;
}
static int devlink_dpipe_match_values_put(struct sk_buff *skb,
struct devlink_dpipe_value *values,
unsigned int values_count)
{
struct nlattr *match_attr;
int i;
int err;
for (i = 0; i < values_count; i++) {
match_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_MATCH_VALUE);
if (!match_attr)
return -EMSGSIZE;
err = devlink_dpipe_match_value_put(skb, &values[i]);
if (err)
goto err_match_value_put;
nla_nest_end(skb, match_attr);
}
return 0;
err_match_value_put:
nla_nest_cancel(skb, match_attr);
return err;
}
static int devlink_dpipe_entry_put(struct sk_buff *skb,
struct devlink_dpipe_entry *entry)
{
struct nlattr *entry_attr, *matches_attr, *actions_attr;
int err;
entry_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_ENTRY);
if (!entry_attr)
return -EMSGSIZE;
if (devlink_nl_put_u64(skb, DEVLINK_ATTR_DPIPE_ENTRY_INDEX, entry->index))
goto nla_put_failure;
if (entry->counter_valid)
if (devlink_nl_put_u64(skb, DEVLINK_ATTR_DPIPE_ENTRY_COUNTER,
entry->counter))
goto nla_put_failure;
matches_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES);
if (!matches_attr)
goto nla_put_failure;
err = devlink_dpipe_match_values_put(skb, entry->match_values,
entry->match_values_count);
if (err) {
nla_nest_cancel(skb, matches_attr);
goto err_match_values_put;
}
nla_nest_end(skb, matches_attr);
actions_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES);
if (!actions_attr)
goto nla_put_failure;
err = devlink_dpipe_action_values_put(skb, entry->action_values,
entry->action_values_count);
if (err) {
nla_nest_cancel(skb, actions_attr);
goto err_action_values_put;
}
nla_nest_end(skb, actions_attr);
nla_nest_end(skb, entry_attr);
return 0;
nla_put_failure:
err = -EMSGSIZE;
err_match_values_put:
err_action_values_put:
nla_nest_cancel(skb, entry_attr);
return err;
}
static struct devlink_dpipe_table *
devlink_dpipe_table_find(struct list_head *dpipe_tables,
const char *table_name, struct devlink *devlink)
{
struct devlink_dpipe_table *table;
list_for_each_entry_rcu(table, dpipe_tables, list,
lockdep_is_held(&devlink->lock)) {
if (!strcmp(table->name, table_name))
return table;
}
return NULL;
}
int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx)
{
struct devlink *devlink;
int err;
err = devlink_dpipe_send_and_alloc_skb(&dump_ctx->skb,
dump_ctx->info);
if (err)
return err;
dump_ctx->hdr = genlmsg_put(dump_ctx->skb,
dump_ctx->info->snd_portid,
dump_ctx->info->snd_seq,
&devlink_nl_family, NLM_F_MULTI,
dump_ctx->cmd);
if (!dump_ctx->hdr)
goto nla_put_failure;
devlink = dump_ctx->info->user_ptr[0];
if (devlink_nl_put_handle(dump_ctx->skb, devlink))
goto nla_put_failure;
dump_ctx->nest = nla_nest_start_noflag(dump_ctx->skb,
DEVLINK_ATTR_DPIPE_ENTRIES);
if (!dump_ctx->nest)
goto nla_put_failure;
return 0;
nla_put_failure:
nlmsg_free(dump_ctx->skb);
return -EMSGSIZE;
}
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_prepare);
int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx,
struct devlink_dpipe_entry *entry)
{
return devlink_dpipe_entry_put(dump_ctx->skb, entry);
}
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_append);
int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx)
{
nla_nest_end(dump_ctx->skb, dump_ctx->nest);
genlmsg_end(dump_ctx->skb, dump_ctx->hdr);
return 0;
}
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close);
void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry)
{
unsigned int value_count, value_index;
struct devlink_dpipe_value *value;
value = entry->action_values;
value_count = entry->action_values_count;
for (value_index = 0; value_index < value_count; value_index++) {
kfree(value[value_index].value);
kfree(value[value_index].mask);
}
value = entry->match_values;
value_count = entry->match_values_count;
for (value_index = 0; value_index < value_count; value_index++) {
kfree(value[value_index].value);
kfree(value[value_index].mask);
}
}
EXPORT_SYMBOL_GPL(devlink_dpipe_entry_clear);
static int devlink_dpipe_entries_fill(struct genl_info *info,
enum devlink_command cmd, int flags,
struct devlink_dpipe_table *table)
{
struct devlink_dpipe_dump_ctx dump_ctx;
struct nlmsghdr *nlh;
int err;
dump_ctx.skb = NULL;
dump_ctx.cmd = cmd;
dump_ctx.info = info;
err = table->table_ops->entries_dump(table->priv,
table->counters_enabled,
&dump_ctx);
if (err)
return err;
send_done:
nlh = nlmsg_put(dump_ctx.skb, info->snd_portid, info->snd_seq,
NLMSG_DONE, 0, flags | NLM_F_MULTI);
if (!nlh) {
err = devlink_dpipe_send_and_alloc_skb(&dump_ctx.skb, info);
if (err)
return err;
goto send_done;
}
return genlmsg_reply(dump_ctx.skb, info);
}
int devlink_nl_dpipe_entries_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_dpipe_table *table;
const char *table_name;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME))
return -EINVAL;
table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
table_name, devlink);
if (!table)
return -EINVAL;
if (!table->table_ops->entries_dump)
return -EINVAL;
return devlink_dpipe_entries_fill(info, DEVLINK_CMD_DPIPE_ENTRIES_GET,
0, table);
}
static int devlink_dpipe_fields_put(struct sk_buff *skb,
const struct devlink_dpipe_header *header)
{
struct devlink_dpipe_field *field;
struct nlattr *field_attr;
int i;
for (i = 0; i < header->fields_count; i++) {
field = &header->fields[i];
field_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_FIELD);
if (!field_attr)
return -EMSGSIZE;
if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_FIELD_NAME, field->name) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, field->bitwidth) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, field->mapping_type))
goto nla_put_failure;
nla_nest_end(skb, field_attr);
}
return 0;
nla_put_failure:
nla_nest_cancel(skb, field_attr);
return -EMSGSIZE;
}
static int devlink_dpipe_header_put(struct sk_buff *skb,
struct devlink_dpipe_header *header)
{
struct nlattr *fields_attr, *header_attr;
int err;
header_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_HEADER);
if (!header_attr)
return -EMSGSIZE;
if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_HEADER_NAME, header->name) ||
nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) ||
nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global))
goto nla_put_failure;
fields_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_DPIPE_HEADER_FIELDS);
if (!fields_attr)
goto nla_put_failure;
err = devlink_dpipe_fields_put(skb, header);
if (err) {
nla_nest_cancel(skb, fields_attr);
goto nla_put_failure;
}
nla_nest_end(skb, fields_attr);
nla_nest_end(skb, header_attr);
return 0;
nla_put_failure:
err = -EMSGSIZE;
nla_nest_cancel(skb, header_attr);
return err;
}
static int devlink_dpipe_headers_fill(struct genl_info *info,
enum devlink_command cmd, int flags,
struct devlink_dpipe_headers *
dpipe_headers)
{
struct devlink *devlink = info->user_ptr[0];
struct nlattr *headers_attr;
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
void *hdr;
int i, j;
int err;
i = 0;
start_again:
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
if (err)
return err;
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
&devlink_nl_family, NLM_F_MULTI, cmd);
if (!hdr) {
nlmsg_free(skb);
return -EMSGSIZE;
}
if (devlink_nl_put_handle(skb, devlink))
goto nla_put_failure;
headers_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_DPIPE_HEADERS);
if (!headers_attr)
goto nla_put_failure;
j = 0;
for (; i < dpipe_headers->headers_count; i++) {
err = devlink_dpipe_header_put(skb, dpipe_headers->headers[i]);
if (err) {
if (!j)
goto err_table_put;
break;
}
j++;
}
nla_nest_end(skb, headers_attr);
genlmsg_end(skb, hdr);
if (i != dpipe_headers->headers_count)
goto start_again;
send_done:
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
NLMSG_DONE, 0, flags | NLM_F_MULTI);
if (!nlh) {
err = devlink_dpipe_send_and_alloc_skb(&skb, info);
if (err)
return err;
goto send_done;
}
return genlmsg_reply(skb, info);
nla_put_failure:
err = -EMSGSIZE;
err_table_put:
nlmsg_free(skb);
return err;
}
int devlink_nl_dpipe_headers_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
if (!devlink->dpipe_headers)
return -EOPNOTSUPP;
return devlink_dpipe_headers_fill(info, DEVLINK_CMD_DPIPE_HEADERS_GET,
0, devlink->dpipe_headers);
}
static int devlink_dpipe_table_counters_set(struct devlink *devlink,
const char *table_name,
bool enable)
{
struct devlink_dpipe_table *table;
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
table_name, devlink);
if (!table)
return -EINVAL;
if (table->counter_control_extern)
return -EOPNOTSUPP;
if (!(table->counters_enabled ^ enable))
return 0;
table->counters_enabled = enable;
if (table->table_ops->counters_set_update)
table->table_ops->counters_set_update(table->priv, enable);
return 0;
}
int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
const char *table_name;
bool counters_enable;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_DPIPE_TABLE_NAME) ||
GENL_REQ_ATTR_CHECK(info,
DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED))
return -EINVAL;
table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]);
counters_enable = !!nla_get_u8(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]);
return devlink_dpipe_table_counters_set(devlink, table_name,
counters_enable);
}
/**
* devl_dpipe_headers_register - register dpipe headers
*
* @devlink: devlink
* @dpipe_headers: dpipe header array
*
* Register the headers supported by hardware.
*/
void devl_dpipe_headers_register(struct devlink *devlink,
struct devlink_dpipe_headers *dpipe_headers)
{
lockdep_assert_held(&devlink->lock);
devlink->dpipe_headers = dpipe_headers;
}
EXPORT_SYMBOL_GPL(devl_dpipe_headers_register);
/**
* devl_dpipe_headers_unregister - unregister dpipe headers
*
* @devlink: devlink
*
* Unregister the headers supported by hardware.
*/
void devl_dpipe_headers_unregister(struct devlink *devlink)
{
lockdep_assert_held(&devlink->lock);
devlink->dpipe_headers = NULL;
}
EXPORT_SYMBOL_GPL(devl_dpipe_headers_unregister);
/**
* devlink_dpipe_table_counter_enabled - check if counter allocation
* required
* @devlink: devlink
* @table_name: tables name
*
* Used by driver to check if counter allocation is required.
* After counter allocation is turned on the table entries
* are updated to include counter statistics.
*
* After that point on the driver must respect the counter
* state so that each entry added to the table is added
* with a counter.
*/
bool devlink_dpipe_table_counter_enabled(struct devlink *devlink,
const char *table_name)
{
struct devlink_dpipe_table *table;
bool enabled;
rcu_read_lock();
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
table_name, devlink);
enabled = false;
if (table)
enabled = table->counters_enabled;
rcu_read_unlock();
return enabled;
}
EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled);
/**
* devl_dpipe_table_register - register dpipe table
*
* @devlink: devlink
* @table_name: table name
* @table_ops: table ops
* @priv: priv
* @counter_control_extern: external control for counters
*/
int devl_dpipe_table_register(struct devlink *devlink,
const char *table_name,
const struct devlink_dpipe_table_ops *table_ops,
void *priv, bool counter_control_extern)
{
struct devlink_dpipe_table *table;
lockdep_assert_held(&devlink->lock);
if (WARN_ON(!table_ops->size_get))
return -EINVAL;
if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name,
devlink))
return -EEXIST;
table = kzalloc_obj(*table);
if (!table)
return -ENOMEM;
table->name = table_name;
table->table_ops = table_ops;
table->priv = priv;
table->counter_control_extern = counter_control_extern;
list_add_tail_rcu(&table->list, &devlink->dpipe_table_list);
return 0;
}
EXPORT_SYMBOL_GPL(devl_dpipe_table_register);
/**
* devl_dpipe_table_unregister - unregister dpipe table
*
* @devlink: devlink
* @table_name: table name
*/
void devl_dpipe_table_unregister(struct devlink *devlink,
const char *table_name)
{
struct devlink_dpipe_table *table;
lockdep_assert_held(&devlink->lock);
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
table_name, devlink);
if (!table)
return;
list_del_rcu(&table->list);
kfree_rcu(table, rcu);
}
EXPORT_SYMBOL_GPL(devl_dpipe_table_unregister);
/**
* devl_dpipe_table_resource_set - set the resource id
*
* @devlink: devlink
* @table_name: table name
* @resource_id: resource id
* @resource_units: number of resource's units consumed per table's entry
*/
int devl_dpipe_table_resource_set(struct devlink *devlink,
const char *table_name, u64 resource_id,
u64 resource_units)
{
struct devlink_dpipe_table *table;
table = devlink_dpipe_table_find(&devlink->dpipe_table_list,
table_name, devlink);
if (!table)
return -EINVAL;
table->resource_id = resource_id;
table->resource_units = resource_units;
table->resource_valid = true;
return 0;
}
EXPORT_SYMBOL_GPL(devl_dpipe_table_resource_set);
File diff suppressed because it is too large Load Diff
+627
View File
@@ -0,0 +1,627 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include "devl_internal.h"
struct devlink_linecard {
struct list_head list;
struct devlink *devlink;
unsigned int index;
const struct devlink_linecard_ops *ops;
void *priv;
enum devlink_linecard_state state;
struct mutex state_lock; /* Protects state */
const char *type;
struct devlink_linecard_type *types;
unsigned int types_count;
u32 rel_index;
};
unsigned int devlink_linecard_index(struct devlink_linecard *linecard)
{
return linecard->index;
}
static struct devlink_linecard *
devlink_linecard_get_by_index(struct devlink *devlink,
unsigned int linecard_index)
{
struct devlink_linecard *devlink_linecard;
list_for_each_entry(devlink_linecard, &devlink->linecard_list, list) {
if (devlink_linecard->index == linecard_index)
return devlink_linecard;
}
return NULL;
}
static bool devlink_linecard_index_exists(struct devlink *devlink,
unsigned int linecard_index)
{
return devlink_linecard_get_by_index(devlink, linecard_index);
}
static struct devlink_linecard *
devlink_linecard_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
{
if (attrs[DEVLINK_ATTR_LINECARD_INDEX]) {
u32 linecard_index = nla_get_u32(attrs[DEVLINK_ATTR_LINECARD_INDEX]);
struct devlink_linecard *linecard;
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
if (!linecard)
return ERR_PTR(-ENODEV);
return linecard;
}
return ERR_PTR(-EINVAL);
}
static struct devlink_linecard *
devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info)
{
return devlink_linecard_get_from_attrs(devlink, info->attrs);
}
struct devlink_linecard_type {
const char *type;
const void *priv;
};
static int devlink_nl_linecard_fill(struct sk_buff *msg,
struct devlink *devlink,
struct devlink_linecard *linecard,
enum devlink_command cmd, u32 portid,
u32 seq, int flags,
struct netlink_ext_ack *extack)
{
struct devlink_linecard_type *linecard_type;
struct nlattr *attr;
void *hdr;
int i;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, linecard->index))
goto nla_put_failure;
if (nla_put_u8(msg, DEVLINK_ATTR_LINECARD_STATE, linecard->state))
goto nla_put_failure;
if (linecard->type &&
nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE, linecard->type))
goto nla_put_failure;
if (linecard->types_count) {
attr = nla_nest_start(msg,
DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES);
if (!attr)
goto nla_put_failure;
for (i = 0; i < linecard->types_count; i++) {
linecard_type = &linecard->types[i];
if (nla_put_string(msg, DEVLINK_ATTR_LINECARD_TYPE,
linecard_type->type)) {
nla_nest_cancel(msg, attr);
goto nla_put_failure;
}
}
nla_nest_end(msg, attr);
}
if (devlink_rel_devlink_handle_put(msg, devlink,
linecard->rel_index,
DEVLINK_ATTR_NESTED_DEVLINK,
NULL))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
static void devlink_linecard_notify(struct devlink_linecard *linecard,
enum devlink_command cmd)
{
struct devlink *devlink = linecard->devlink;
struct sk_buff *msg;
int err;
WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&
cmd != DEVLINK_CMD_LINECARD_DEL);
if (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
return;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
err = devlink_nl_linecard_fill(msg, devlink, linecard, cmd, 0, 0, 0,
NULL);
if (err) {
nlmsg_free(msg);
return;
}
devlink_nl_notify_send(devlink, msg);
}
void devlink_linecards_notify_register(struct devlink *devlink)
{
struct devlink_linecard *linecard;
list_for_each_entry(linecard, &devlink->linecard_list, list)
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
}
void devlink_linecards_notify_unregister(struct devlink *devlink)
{
struct devlink_linecard *linecard;
list_for_each_entry_reverse(linecard, &devlink->linecard_list, list)
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
}
int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_linecard *linecard;
struct sk_buff *msg;
int err;
linecard = devlink_linecard_get_from_info(devlink, info);
if (IS_ERR(linecard))
return PTR_ERR(linecard);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
mutex_lock(&linecard->state_lock);
err = devlink_nl_linecard_fill(msg, devlink, linecard,
DEVLINK_CMD_LINECARD_NEW,
info->snd_portid, info->snd_seq, 0,
info->extack);
mutex_unlock(&linecard->state_lock);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int devlink_nl_linecard_get_dump_one(struct sk_buff *msg,
struct devlink *devlink,
struct netlink_callback *cb,
int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_linecard *linecard;
int idx = 0;
int err = 0;
list_for_each_entry(linecard, &devlink->linecard_list, list) {
if (idx < state->idx) {
idx++;
continue;
}
mutex_lock(&linecard->state_lock);
err = devlink_nl_linecard_fill(msg, devlink, linecard,
DEVLINK_CMD_LINECARD_NEW,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, flags,
cb->extack);
mutex_unlock(&linecard->state_lock);
if (err) {
state->idx = idx;
break;
}
idx++;
}
return err;
}
int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb, devlink_nl_linecard_get_dump_one);
}
static struct devlink_linecard_type *
devlink_linecard_type_lookup(struct devlink_linecard *linecard,
const char *type)
{
struct devlink_linecard_type *linecard_type;
int i;
for (i = 0; i < linecard->types_count; i++) {
linecard_type = &linecard->types[i];
if (!strcmp(type, linecard_type->type))
return linecard_type;
}
return NULL;
}
static int devlink_linecard_type_set(struct devlink_linecard *linecard,
const char *type,
struct netlink_ext_ack *extack)
{
const struct devlink_linecard_ops *ops = linecard->ops;
struct devlink_linecard_type *linecard_type;
int err;
mutex_lock(&linecard->state_lock);
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
err = -EBUSY;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
err = -EBUSY;
goto out;
}
linecard_type = devlink_linecard_type_lookup(linecard, type);
if (!linecard_type) {
NL_SET_ERR_MSG(extack, "Unsupported line card type provided");
err = -EINVAL;
goto out;
}
if (linecard->state != DEVLINK_LINECARD_STATE_UNPROVISIONED &&
linecard->state != DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
NL_SET_ERR_MSG(extack, "Line card already provisioned");
err = -EBUSY;
/* Check if the line card is provisioned in the same
* way the user asks. In case it is, make the operation
* to return success.
*/
if (ops->same_provision &&
ops->same_provision(linecard, linecard->priv,
linecard_type->type,
linecard_type->priv))
err = 0;
goto out;
}
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING;
linecard->type = linecard_type->type;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
err = ops->provision(linecard, linecard->priv, linecard_type->type,
linecard_type->priv, extack);
if (err) {
/* Provisioning failed. Assume the linecard is unprovisioned
* for future operations.
*/
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
return err;
out:
mutex_unlock(&linecard->state_lock);
return err;
}
static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
struct netlink_ext_ack *extack)
{
int err;
mutex_lock(&linecard->state_lock);
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING) {
NL_SET_ERR_MSG(extack, "Line card is currently being provisioned");
err = -EBUSY;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONING) {
NL_SET_ERR_MSG(extack, "Line card is currently being unprovisioned");
err = -EBUSY;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_PROVISIONING_FAILED) {
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
err = 0;
goto out;
}
if (linecard->state == DEVLINK_LINECARD_STATE_UNPROVISIONED) {
NL_SET_ERR_MSG(extack, "Line card is not provisioned");
err = 0;
goto out;
}
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONING;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
err = linecard->ops->unprovision(linecard, linecard->priv,
extack);
if (err) {
/* Unprovisioning failed. Assume the linecard is unprovisioned
* for future operations.
*/
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
return err;
out:
mutex_unlock(&linecard->state_lock);
return err;
}
int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct netlink_ext_ack *extack = info->extack;
struct devlink *devlink = info->user_ptr[0];
struct devlink_linecard *linecard;
int err;
linecard = devlink_linecard_get_from_info(devlink, info);
if (IS_ERR(linecard))
return PTR_ERR(linecard);
if (info->attrs[DEVLINK_ATTR_LINECARD_TYPE]) {
const char *type;
type = nla_data(info->attrs[DEVLINK_ATTR_LINECARD_TYPE]);
if (strcmp(type, "")) {
err = devlink_linecard_type_set(linecard, type, extack);
if (err)
return err;
} else {
err = devlink_linecard_type_unset(linecard, extack);
if (err)
return err;
}
}
return 0;
}
static int devlink_linecard_types_init(struct devlink_linecard *linecard)
{
struct devlink_linecard_type *linecard_type;
unsigned int count;
int i;
count = linecard->ops->types_count(linecard, linecard->priv);
linecard->types = kmalloc_objs(*linecard_type, count);
if (!linecard->types)
return -ENOMEM;
linecard->types_count = count;
for (i = 0; i < count; i++) {
linecard_type = &linecard->types[i];
linecard->ops->types_get(linecard, linecard->priv, i,
&linecard_type->type,
&linecard_type->priv);
}
return 0;
}
static void devlink_linecard_types_fini(struct devlink_linecard *linecard)
{
kfree(linecard->types);
}
/**
* devl_linecard_create - Create devlink linecard
*
* @devlink: devlink
* @linecard_index: driver-specific numerical identifier of the linecard
* @ops: linecards ops
* @priv: user priv pointer
*
* Create devlink linecard instance with provided linecard index.
* Caller can use any indexing, even hw-related one.
*
* Return: Line card structure or an ERR_PTR() encoded error code.
*/
struct devlink_linecard *
devl_linecard_create(struct devlink *devlink, unsigned int linecard_index,
const struct devlink_linecard_ops *ops, void *priv)
{
struct devlink_linecard *linecard;
int err;
if (WARN_ON(!ops || !ops->provision || !ops->unprovision ||
!ops->types_count || !ops->types_get))
return ERR_PTR(-EINVAL);
if (devlink_linecard_index_exists(devlink, linecard_index))
return ERR_PTR(-EEXIST);
linecard = kzalloc_obj(*linecard);
if (!linecard)
return ERR_PTR(-ENOMEM);
linecard->devlink = devlink;
linecard->index = linecard_index;
linecard->ops = ops;
linecard->priv = priv;
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
mutex_init(&linecard->state_lock);
err = devlink_linecard_types_init(linecard);
if (err) {
mutex_destroy(&linecard->state_lock);
kfree(linecard);
return ERR_PTR(err);
}
list_add_tail(&linecard->list, &devlink->linecard_list);
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
return linecard;
}
EXPORT_SYMBOL_GPL(devl_linecard_create);
/**
* devl_linecard_destroy - Destroy devlink linecard
*
* @linecard: devlink linecard
*/
void devl_linecard_destroy(struct devlink_linecard *linecard)
{
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_DEL);
list_del(&linecard->list);
devlink_linecard_types_fini(linecard);
mutex_destroy(&linecard->state_lock);
kfree(linecard);
}
EXPORT_SYMBOL_GPL(devl_linecard_destroy);
/**
* devlink_linecard_provision_set - Set provisioning on linecard
*
* @linecard: devlink linecard
* @type: linecard type
*
* This is either called directly from the provision() op call or
* as a result of the provision() op call asynchronously.
*/
void devlink_linecard_provision_set(struct devlink_linecard *linecard,
const char *type)
{
mutex_lock(&linecard->state_lock);
WARN_ON(linecard->type && strcmp(linecard->type, type));
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
linecard->type = type;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_provision_set);
/**
* devlink_linecard_provision_clear - Clear provisioning on linecard
*
* @linecard: devlink linecard
*
* This is either called directly from the unprovision() op call or
* as a result of the unprovision() op call asynchronously.
*/
void devlink_linecard_provision_clear(struct devlink_linecard *linecard)
{
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED;
linecard->type = NULL;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear);
/**
* devlink_linecard_provision_fail - Fail provisioning on linecard
*
* @linecard: devlink linecard
*
* This is either called directly from the provision() op call or
* as a result of the provision() op call asynchronously.
*/
void devlink_linecard_provision_fail(struct devlink_linecard *linecard)
{
mutex_lock(&linecard->state_lock);
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_provision_fail);
/**
* devlink_linecard_activate - Set linecard active
*
* @linecard: devlink linecard
*/
void devlink_linecard_activate(struct devlink_linecard *linecard)
{
mutex_lock(&linecard->state_lock);
WARN_ON(linecard->state != DEVLINK_LINECARD_STATE_PROVISIONED);
linecard->state = DEVLINK_LINECARD_STATE_ACTIVE;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_activate);
/**
* devlink_linecard_deactivate - Set linecard inactive
*
* @linecard: devlink linecard
*/
void devlink_linecard_deactivate(struct devlink_linecard *linecard)
{
mutex_lock(&linecard->state_lock);
switch (linecard->state) {
case DEVLINK_LINECARD_STATE_ACTIVE:
linecard->state = DEVLINK_LINECARD_STATE_PROVISIONED;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
break;
case DEVLINK_LINECARD_STATE_UNPROVISIONING:
/* Line card is being deactivated as part
* of unprovisioning flow.
*/
break;
default:
WARN_ON(1);
break;
}
mutex_unlock(&linecard->state_lock);
}
EXPORT_SYMBOL_GPL(devlink_linecard_deactivate);
static void devlink_linecard_rel_notify_cb(struct devlink *devlink,
u32 linecard_index)
{
struct devlink_linecard *linecard;
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
if (!linecard)
return;
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
}
static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink,
u32 linecard_index, u32 rel_index)
{
struct devlink_linecard *linecard;
linecard = devlink_linecard_get_by_index(devlink, linecard_index);
if (linecard && linecard->rel_index == rel_index)
linecard->rel_index = 0;
}
/**
* devlink_linecard_nested_dl_set - Attach/detach nested devlink
* instance to linecard.
*
* @linecard: devlink linecard
* @nested_devlink: devlink instance to attach or NULL to detach
*/
int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard,
struct devlink *nested_devlink)
{
return devlink_rel_nested_in_add(&linecard->rel_index,
linecard->devlink->index,
linecard->index,
devlink_linecard_rel_notify_cb,
devlink_linecard_rel_cleanup_cb,
nested_devlink);
}
EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set);
+412
View File
@@ -0,0 +1,412 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include <net/genetlink.h>
#include <net/sock.h>
#include "devl_internal.h"
#define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
#define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1)
#define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2)
static const struct genl_multicast_group devlink_nl_mcgrps[] = {
[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
};
struct devlink_nl_sock_priv {
struct devlink_obj_desc __rcu *flt;
spinlock_t flt_lock; /* Protects flt. */
};
static void devlink_nl_sock_priv_init(void *priv)
{
struct devlink_nl_sock_priv *sk_priv = priv;
spin_lock_init(&sk_priv->flt_lock);
}
static void devlink_nl_sock_priv_destroy(void *priv)
{
struct devlink_nl_sock_priv *sk_priv = priv;
struct devlink_obj_desc *flt;
flt = rcu_dereference_protected(sk_priv->flt, true);
kfree_rcu(flt, rcu);
}
int devlink_nl_notify_filter_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_nl_sock_priv *sk_priv;
struct nlattr **attrs = info->attrs;
struct devlink_obj_desc *flt;
size_t data_offset = 0;
size_t data_size = 0;
char *pos;
if (attrs[DEVLINK_ATTR_BUS_NAME])
data_size = size_add(data_size,
nla_len(attrs[DEVLINK_ATTR_BUS_NAME]) + 1);
if (attrs[DEVLINK_ATTR_DEV_NAME])
data_size = size_add(data_size,
nla_len(attrs[DEVLINK_ATTR_DEV_NAME]) + 1);
flt = kzalloc(size_add(sizeof(*flt), data_size), GFP_KERNEL);
if (!flt)
return -ENOMEM;
pos = (char *) flt->data;
if (attrs[DEVLINK_ATTR_BUS_NAME]) {
data_offset += nla_strscpy(pos,
attrs[DEVLINK_ATTR_BUS_NAME],
data_size) + 1;
flt->bus_name = pos;
pos += data_offset;
}
if (attrs[DEVLINK_ATTR_DEV_NAME]) {
nla_strscpy(pos, attrs[DEVLINK_ATTR_DEV_NAME],
data_size - data_offset);
flt->dev_name = pos;
}
if (attrs[DEVLINK_ATTR_INDEX]) {
flt->devlink_index = nla_get_uint(attrs[DEVLINK_ATTR_INDEX]);
flt->devlink_index_valid = true;
}
if (attrs[DEVLINK_ATTR_PORT_INDEX]) {
flt->port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);
flt->port_index_valid = true;
}
/* Don't attach empty filter. */
if (!flt->bus_name && !flt->dev_name &&
!flt->devlink_index_valid && !flt->port_index_valid) {
kfree(flt);
flt = NULL;
}
sk_priv = genl_sk_priv_get(&devlink_nl_family, NETLINK_CB(skb).sk);
if (IS_ERR(sk_priv)) {
kfree(flt);
return PTR_ERR(sk_priv);
}
spin_lock(&sk_priv->flt_lock);
flt = rcu_replace_pointer(sk_priv->flt, flt,
lockdep_is_held(&sk_priv->flt_lock));
spin_unlock(&sk_priv->flt_lock);
kfree_rcu(flt, rcu);
return 0;
}
static bool devlink_obj_desc_match(const struct devlink_obj_desc *desc,
const struct devlink_obj_desc *flt)
{
if (desc->devlink_index_valid && flt->devlink_index_valid &&
desc->devlink_index != flt->devlink_index)
return false;
if (desc->bus_name && flt->bus_name &&
strcmp(desc->bus_name, flt->bus_name))
return false;
if (desc->dev_name && flt->dev_name &&
strcmp(desc->dev_name, flt->dev_name))
return false;
if (desc->port_index_valid && flt->port_index_valid &&
desc->port_index != flt->port_index)
return false;
return true;
}
int devlink_nl_notify_filter(struct sock *dsk, struct sk_buff *skb, void *data)
{
struct devlink_obj_desc *desc = data;
struct devlink_nl_sock_priv *sk_priv;
struct devlink_obj_desc *flt;
int ret = 0;
rcu_read_lock();
sk_priv = __genl_sk_priv_get(&devlink_nl_family, dsk);
if (!IS_ERR_OR_NULL(sk_priv)) {
flt = rcu_dereference(sk_priv->flt);
if (flt)
ret = !devlink_obj_desc_match(desc, flt);
}
rcu_read_unlock();
return ret;
}
int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net,
struct devlink *devlink, int attrtype)
{
struct nlattr *nested_attr;
struct net *devl_net;
nested_attr = nla_nest_start(msg, attrtype);
if (!nested_attr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
rcu_read_lock();
devl_net = read_pnet_rcu(&devlink->_net);
if (!net_eq(net, devl_net)) {
int id = peernet2id_alloc(net, devl_net, GFP_ATOMIC);
rcu_read_unlock();
if (nla_put_s32(msg, DEVLINK_ATTR_NETNS_ID, id))
return -EMSGSIZE;
} else {
rcu_read_unlock();
}
nla_nest_end(msg, nested_attr);
return 0;
nla_put_failure:
nla_nest_cancel(msg, nested_attr);
return -EMSGSIZE;
}
int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info)
{
int err;
if (*msg) {
err = genlmsg_reply(*msg, info);
if (err)
return err;
}
*msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!*msg)
return -ENOMEM;
return 0;
}
struct devlink *
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
bool dev_lock)
{
struct devlink *devlink;
unsigned long index;
char *busname;
char *devname;
if (attrs[DEVLINK_ATTR_INDEX]) {
if (attrs[DEVLINK_ATTR_BUS_NAME] ||
attrs[DEVLINK_ATTR_DEV_NAME])
return ERR_PTR(-EINVAL);
index = nla_get_u32(attrs[DEVLINK_ATTR_INDEX]);
devlink = devlinks_xa_lookup_get(net, index);
if (!devlink)
return ERR_PTR(-ENODEV);
goto found;
}
if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])
return ERR_PTR(-EINVAL);
busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]);
if (!strcmp(busname, DEVLINK_INDEX_BUS_NAME)) {
if (kstrtoul(devname, 10, &index))
return ERR_PTR(-ENODEV);
devlink = devlinks_xa_lookup_get(net, index);
if (!devlink)
return ERR_PTR(-ENODEV);
goto found;
}
devlinks_xa_for_each_registered_get(net, index, devlink) {
if (strcmp(devlink_bus_name(devlink), busname) == 0 &&
strcmp(devlink_dev_name(devlink), devname) == 0)
goto found;
devlink_put(devlink);
}
return ERR_PTR(-ENODEV);
found:
devl_dev_lock(devlink, dev_lock);
if (devl_is_registered(devlink))
return devlink;
devl_dev_unlock(devlink, dev_lock);
devlink_put(devlink);
return ERR_PTR(-ENODEV);
}
static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
u8 flags)
{
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
struct devlink_port *devlink_port;
struct devlink *devlink;
int err;
devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs,
dev_lock);
if (IS_ERR(devlink))
return PTR_ERR(devlink);
info->user_ptr[0] = devlink;
if (flags & DEVLINK_NL_FLAG_NEED_PORT) {
devlink_port = devlink_port_get_from_info(devlink, info);
if (IS_ERR(devlink_port)) {
err = PTR_ERR(devlink_port);
goto unlock;
}
info->user_ptr[1] = devlink_port;
} else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
devlink_port = devlink_port_get_from_info(devlink, info);
if (!IS_ERR(devlink_port))
info->user_ptr[1] = devlink_port;
}
return 0;
unlock:
devl_dev_unlock(devlink, dev_lock);
devlink_put(devlink);
return err;
}
int devlink_nl_pre_doit(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
return __devlink_nl_pre_doit(skb, info, 0);
}
int devlink_nl_pre_doit_port(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_PORT);
}
int devlink_nl_pre_doit_dev_lock(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEV_LOCK);
}
int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
struct sk_buff *skb,
struct genl_info *info)
{
return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT);
}
static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info,
u8 flags)
{
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
struct devlink *devlink;
devlink = info->user_ptr[0];
devl_dev_unlock(devlink, dev_lock);
devlink_put(devlink);
}
void devlink_nl_post_doit(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
__devlink_nl_post_doit(skb, info, 0);
}
void
devlink_nl_post_doit_dev_lock(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info)
{
__devlink_nl_post_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEV_LOCK);
}
static int devlink_nl_inst_single_dumpit(struct sk_buff *msg,
struct netlink_callback *cb, int flags,
devlink_nl_dump_one_func_t *dump_one,
struct nlattr **attrs)
{
struct devlink *devlink;
int err;
devlink = devlink_get_from_attrs_lock(sock_net(msg->sk), attrs, false);
if (IS_ERR(devlink))
return PTR_ERR(devlink);
err = dump_one(msg, devlink, cb, flags | NLM_F_DUMP_FILTERED);
devl_unlock(devlink);
devlink_put(devlink);
if (err != -EMSGSIZE)
return err;
return msg->len;
}
static int devlink_nl_inst_iter_dumpit(struct sk_buff *msg,
struct netlink_callback *cb, int flags,
devlink_nl_dump_one_func_t *dump_one)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink *devlink;
int err = 0;
while ((devlink = devlinks_xa_find_get(sock_net(msg->sk),
&state->instance))) {
devl_lock(devlink);
if (devl_is_registered(devlink))
err = dump_one(msg, devlink, cb, flags);
else
err = 0;
devl_unlock(devlink);
devlink_put(devlink);
if (err)
break;
state->instance++;
/* restart sub-object walk for the next instance */
state->idx = 0;
state->port_ctx.index = 0;
state->port_ctx.index_valid = false;
}
if (err != -EMSGSIZE)
return err;
return msg->len;
}
int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
devlink_nl_dump_one_func_t *dump_one)
{
const struct genl_info *info = genl_info_dump(cb);
struct nlattr **attrs = info->attrs;
int flags = NLM_F_MULTI;
if (attrs &&
(attrs[DEVLINK_ATTR_BUS_NAME] || attrs[DEVLINK_ATTR_DEV_NAME] ||
attrs[DEVLINK_ATTR_INDEX]))
return devlink_nl_inst_single_dumpit(msg, cb, flags, dump_one,
attrs);
else
return devlink_nl_inst_iter_dumpit(msg, cb, flags, dump_one);
}
struct genl_family devlink_nl_family __ro_after_init = {
.name = DEVLINK_GENL_NAME,
.version = DEVLINK_GENL_VERSION,
.netnsok = true,
.parallel_ops = true,
.module = THIS_MODULE,
.split_ops = devlink_nl_ops,
.n_split_ops = ARRAY_SIZE(devlink_nl_ops),
.resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1,
.mcgrps = devlink_nl_mcgrps,
.n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps),
.sock_priv_size = sizeof(struct devlink_nl_sock_priv),
.sock_priv_init = devlink_nl_sock_priv_init,
.sock_priv_destroy = devlink_nl_sock_priv_destroy,
};
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/devlink.yaml */
/* YNL-GEN kernel header */
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_DEVLINK_GEN_H
#define _LINUX_DEVLINK_GEN_H
#include <net/netlink.h>
#include <net/genetlink.h>
#include <uapi/linux/devlink.h>
/* Common nested types */
extern const struct nla_policy devlink_dl_port_function_nl_policy[DEVLINK_PORT_FN_ATTR_CAPS + 1];
extern const struct nla_policy devlink_dl_rate_tc_bws_nl_policy[DEVLINK_RATE_TC_ATTR_BW + 1];
extern const struct nla_policy devlink_dl_selftest_id_nl_policy[DEVLINK_ATTR_SELFTEST_ID_FLASH + 1];
/* Ops table for devlink */
extern const struct genl_split_ops devlink_nl_ops[75];
int devlink_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_pre_doit_port(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_pre_doit_dev_lock(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
void
devlink_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info);
void
devlink_nl_post_doit_dev_lock(const struct genl_split_ops *ops,
struct sk_buff *skb, struct genl_info *info);
int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
int devlink_nl_port_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_port_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_port_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_port_new_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_port_del_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_port_split_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_port_unsplit_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_sb_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_sb_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
int devlink_nl_sb_pool_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_sb_pool_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_sb_port_pool_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_sb_port_pool_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_sb_port_pool_set_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_sb_tc_pool_bind_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_sb_occ_max_clear_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_dpipe_table_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_dpipe_entries_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_dpipe_headers_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_resource_dump_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_param_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_param_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_param_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_region_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_region_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_region_new_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_region_del_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_region_read_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_port_param_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_port_param_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_port_param_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_info_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_info_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_flash_update_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_trap_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_trap_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_trap_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_trap_group_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_trap_group_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_trap_group_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_trap_policer_get_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_trap_policer_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_trap_policer_set_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
struct genl_info *info);
int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_rate_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_linecard_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_selftests_get_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_selftests_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb);
int devlink_nl_selftests_run_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_notify_filter_set_doit(struct sk_buff *skb,
struct genl_info *info);
#endif /* _LINUX_DEVLINK_GEN_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+854
View File
@@ -0,0 +1,854 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include "devl_internal.h"
static inline bool
devlink_rate_is_leaf(struct devlink_rate *devlink_rate)
{
return devlink_rate->type == DEVLINK_RATE_TYPE_LEAF;
}
bool devlink_rate_is_node(const struct devlink_rate *devlink_rate)
{
return devlink_rate->type == DEVLINK_RATE_TYPE_NODE;
}
static struct devlink_rate *
devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info)
{
struct devlink_rate *devlink_rate;
struct devlink_port *devlink_port;
devlink_port = devlink_port_get_from_attrs(devlink, info->attrs);
if (IS_ERR(devlink_port))
return ERR_CAST(devlink_port);
devlink_rate = devlink_port->devlink_rate;
return devlink_rate ?: ERR_PTR(-ENODEV);
}
static struct devlink_rate *
devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name)
{
struct devlink_rate *devlink_rate;
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
if (devlink_rate_is_node(devlink_rate) &&
!strcmp(node_name, devlink_rate->name))
return devlink_rate;
}
return ERR_PTR(-ENODEV);
}
static struct devlink_rate *
devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
{
const char *rate_node_name;
size_t len;
if (!attrs[DEVLINK_ATTR_RATE_NODE_NAME])
return ERR_PTR(-EINVAL);
rate_node_name = nla_data(attrs[DEVLINK_ATTR_RATE_NODE_NAME]);
len = strlen(rate_node_name);
/* Name cannot be empty or decimal number */
if (!len || strspn(rate_node_name, "0123456789") == len)
return ERR_PTR(-EINVAL);
return devlink_rate_node_get_by_name(devlink, rate_node_name);
}
static struct devlink_rate *
devlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info)
{
return devlink_rate_node_get_from_attrs(devlink, info->attrs);
}
static struct devlink_rate *
devlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info)
{
struct nlattr **attrs = info->attrs;
if (attrs[DEVLINK_ATTR_PORT_INDEX])
return devlink_rate_leaf_get_from_info(devlink, info);
else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME])
return devlink_rate_node_get_from_info(devlink, info);
else
return ERR_PTR(-EINVAL);
}
static int devlink_rate_put_tc_bws(struct sk_buff *msg, u32 *tc_bw)
{
struct nlattr *nla_tc_bw;
int i;
for (i = 0; i < DEVLINK_RATE_TCS_MAX; i++) {
nla_tc_bw = nla_nest_start(msg, DEVLINK_ATTR_RATE_TC_BWS);
if (!nla_tc_bw)
return -EMSGSIZE;
if (nla_put_u8(msg, DEVLINK_RATE_TC_ATTR_INDEX, i) ||
nla_put_u32(msg, DEVLINK_RATE_TC_ATTR_BW, tc_bw[i]))
goto nla_put_failure;
nla_nest_end(msg, nla_tc_bw);
}
return 0;
nla_put_failure:
nla_nest_cancel(msg, nla_tc_bw);
return -EMSGSIZE;
}
static int devlink_nl_rate_fill(struct sk_buff *msg,
struct devlink_rate *devlink_rate,
enum devlink_command cmd, u32 portid, u32 seq,
int flags, struct netlink_ext_ack *extack)
{
struct devlink *devlink = devlink_rate->devlink;
void *hdr;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_RATE_TYPE, devlink_rate->type))
goto nla_put_failure;
if (devlink_rate_is_leaf(devlink_rate)) {
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,
devlink_rate->devlink_port->index))
goto nla_put_failure;
} else if (devlink_rate_is_node(devlink_rate)) {
if (nla_put_string(msg, DEVLINK_ATTR_RATE_NODE_NAME,
devlink_rate->name))
goto nla_put_failure;
}
if (devlink_nl_put_u64(msg, DEVLINK_ATTR_RATE_TX_SHARE,
devlink_rate->tx_share))
goto nla_put_failure;
if (devlink_nl_put_u64(msg, DEVLINK_ATTR_RATE_TX_MAX,
devlink_rate->tx_max))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_PRIORITY,
devlink_rate->tx_priority))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_RATE_TX_WEIGHT,
devlink_rate->tx_weight))
goto nla_put_failure;
if (devlink_rate->parent)
if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,
devlink_rate->parent->name))
goto nla_put_failure;
if (devlink_rate_put_tc_bws(msg, devlink_rate->tc_bw))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
static void devlink_rate_notify(struct devlink_rate *devlink_rate,
enum devlink_command cmd)
{
struct devlink *devlink = devlink_rate->devlink;
struct sk_buff *msg;
int err;
WARN_ON(cmd != DEVLINK_CMD_RATE_NEW && cmd != DEVLINK_CMD_RATE_DEL);
if (!devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
return;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
err = devlink_nl_rate_fill(msg, devlink_rate, cmd, 0, 0, 0, NULL);
if (err) {
nlmsg_free(msg);
return;
}
devlink_nl_notify_send(devlink, msg);
}
void devlink_rates_notify_register(struct devlink *devlink)
{
struct devlink_rate *rate_node;
list_for_each_entry(rate_node, &devlink->rate_list, list)
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
}
void devlink_rates_notify_unregister(struct devlink *devlink)
{
struct devlink_rate *rate_node;
list_for_each_entry_reverse(rate_node, &devlink->rate_list, list)
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
}
static int
devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
struct netlink_callback *cb, int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_rate *devlink_rate;
int idx = 0;
int err = 0;
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;
u32 id = NETLINK_CB(cb->skb).portid;
if (idx < state->idx) {
idx++;
continue;
}
err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id,
cb->nlh->nlmsg_seq, flags, NULL);
if (err) {
state->idx = idx;
break;
}
idx++;
}
return err;
}
int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb, devlink_nl_rate_get_dump_one);
}
int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_rate *devlink_rate;
struct sk_buff *msg;
int err;
devlink_rate = devlink_rate_get_from_info(devlink, info);
if (IS_ERR(devlink_rate))
return PTR_ERR(devlink_rate);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,
info->snd_portid, info->snd_seq, 0,
info->extack);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static bool
devlink_rate_is_parent_node(struct devlink_rate *devlink_rate,
struct devlink_rate *parent)
{
while (parent) {
if (parent == devlink_rate)
return true;
parent = parent->parent;
}
return false;
}
static int
devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate,
struct genl_info *info,
struct nlattr *nla_parent)
{
struct devlink *devlink = devlink_rate->devlink;
const char *parent_name = nla_data(nla_parent);
const struct devlink_ops *ops = devlink->ops;
size_t len = strlen(parent_name);
struct devlink_rate *parent;
int err = -EOPNOTSUPP;
parent = devlink_rate->parent;
if (parent && !len) {
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_parent_set(devlink_rate, NULL,
devlink_rate->priv, NULL,
info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_parent_set(devlink_rate, NULL,
devlink_rate->priv, NULL,
info->extack);
if (err)
return err;
refcount_dec(&parent->refcnt);
devlink_rate->parent = NULL;
} else if (len) {
parent = devlink_rate_node_get_by_name(devlink, parent_name);
if (IS_ERR(parent))
return -ENODEV;
if (parent == devlink_rate) {
NL_SET_ERR_MSG(info->extack, "Parent to self is not allowed");
return -EINVAL;
}
if (devlink_rate_is_node(devlink_rate) &&
devlink_rate_is_parent_node(devlink_rate, parent->parent)) {
NL_SET_ERR_MSG(info->extack, "Node is already a parent of parent node.");
return -EEXIST;
}
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_parent_set(devlink_rate, parent,
devlink_rate->priv, parent->priv,
info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_parent_set(devlink_rate, parent,
devlink_rate->priv, parent->priv,
info->extack);
if (err)
return err;
if (devlink_rate->parent)
/* we're reassigning to other parent in this case */
refcount_dec(&devlink_rate->parent->refcnt);
refcount_inc(&parent->refcnt);
devlink_rate->parent = parent;
}
return 0;
}
static int devlink_nl_rate_tc_bw_parse(struct nlattr *parent_nest, u32 *tc_bw,
unsigned long *bitmap,
struct netlink_ext_ack *extack)
{
struct nlattr *tb[DEVLINK_RATE_TC_ATTR_MAX + 1];
u8 tc_index;
int err;
err = nla_parse_nested(tb, DEVLINK_RATE_TC_ATTR_MAX, parent_nest,
devlink_dl_rate_tc_bws_nl_policy, extack);
if (err)
return err;
if (!tb[DEVLINK_RATE_TC_ATTR_INDEX]) {
NL_SET_ERR_ATTR_MISS(extack, parent_nest,
DEVLINK_RATE_TC_ATTR_INDEX);
return -EINVAL;
}
tc_index = nla_get_u8(tb[DEVLINK_RATE_TC_ATTR_INDEX]);
if (!tb[DEVLINK_RATE_TC_ATTR_BW]) {
NL_SET_ERR_ATTR_MISS(extack, parent_nest,
DEVLINK_RATE_TC_ATTR_BW);
return -EINVAL;
}
if (test_and_set_bit(tc_index, bitmap)) {
NL_SET_ERR_MSG_FMT(extack,
"Duplicate traffic class index specified (%u)",
tc_index);
return -EINVAL;
}
tc_bw[tc_index] = nla_get_u32(tb[DEVLINK_RATE_TC_ATTR_BW]);
return 0;
}
static int devlink_nl_rate_tc_bw_set(struct devlink_rate *devlink_rate,
struct genl_info *info)
{
DECLARE_BITMAP(bitmap, DEVLINK_RATE_TCS_MAX) = {};
struct devlink *devlink = devlink_rate->devlink;
const struct devlink_ops *ops = devlink->ops;
u32 tc_bw[DEVLINK_RATE_TCS_MAX] = {};
int rem, err = -EOPNOTSUPP, i;
struct nlattr *attr;
nlmsg_for_each_attr_type(attr, DEVLINK_ATTR_RATE_TC_BWS, info->nlhdr,
GENL_HDRLEN, rem) {
err = devlink_nl_rate_tc_bw_parse(attr, tc_bw, bitmap,
info->extack);
if (err)
return err;
}
for (i = 0; i < DEVLINK_RATE_TCS_MAX; i++) {
if (!test_bit(i, bitmap)) {
NL_SET_ERR_MSG_FMT(info->extack,
"Bandwidth values must be specified for all %u traffic classes",
DEVLINK_RATE_TCS_MAX);
return -EINVAL;
}
}
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_tc_bw_set(devlink_rate, devlink_rate->priv,
tc_bw, info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_tc_bw_set(devlink_rate, devlink_rate->priv,
tc_bw, info->extack);
if (err)
return err;
memcpy(devlink_rate->tc_bw, tc_bw, sizeof(tc_bw));
return 0;
}
static int devlink_nl_rate_set(struct devlink_rate *devlink_rate,
const struct devlink_ops *ops,
struct genl_info *info)
{
struct nlattr *nla_parent, **attrs = info->attrs;
int err = -EOPNOTSUPP;
u32 priority;
u32 weight;
u64 rate;
if (attrs[DEVLINK_ATTR_RATE_TX_SHARE]) {
rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_SHARE]);
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_tx_share_set(devlink_rate, devlink_rate->priv,
rate, info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_tx_share_set(devlink_rate, devlink_rate->priv,
rate, info->extack);
if (err)
return err;
devlink_rate->tx_share = rate;
}
if (attrs[DEVLINK_ATTR_RATE_TX_MAX]) {
rate = nla_get_u64(attrs[DEVLINK_ATTR_RATE_TX_MAX]);
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_tx_max_set(devlink_rate, devlink_rate->priv,
rate, info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_tx_max_set(devlink_rate, devlink_rate->priv,
rate, info->extack);
if (err)
return err;
devlink_rate->tx_max = rate;
}
if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]) {
priority = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_PRIORITY]);
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_tx_priority_set(devlink_rate, devlink_rate->priv,
priority, info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_tx_priority_set(devlink_rate, devlink_rate->priv,
priority, info->extack);
if (err)
return err;
devlink_rate->tx_priority = priority;
}
if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]) {
weight = nla_get_u32(attrs[DEVLINK_ATTR_RATE_TX_WEIGHT]);
if (devlink_rate_is_leaf(devlink_rate))
err = ops->rate_leaf_tx_weight_set(devlink_rate, devlink_rate->priv,
weight, info->extack);
else if (devlink_rate_is_node(devlink_rate))
err = ops->rate_node_tx_weight_set(devlink_rate, devlink_rate->priv,
weight, info->extack);
if (err)
return err;
devlink_rate->tx_weight = weight;
}
if (attrs[DEVLINK_ATTR_RATE_TC_BWS]) {
err = devlink_nl_rate_tc_bw_set(devlink_rate, info);
if (err)
return err;
}
/* Keep parent setting last because it takes a reference. This function
* has no rollback, so failing after taking the ref would leak it.
*/
nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME];
if (nla_parent) {
err = devlink_nl_rate_parent_node_set(devlink_rate, info,
nla_parent);
if (err)
return err;
}
return 0;
}
static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops,
struct genl_info *info,
enum devlink_rate_type type)
{
struct nlattr **attrs = info->attrs;
if (type == DEVLINK_RATE_TYPE_LEAF) {
if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_leaf_tx_share_set) {
NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the leafs");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_leaf_tx_max_set) {
NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the leafs");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] &&
!ops->rate_leaf_parent_set) {
NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the leafs");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_leaf_tx_priority_set) {
NL_SET_ERR_MSG_ATTR(info->extack,
attrs[DEVLINK_ATTR_RATE_TX_PRIORITY],
"TX priority set isn't supported for the leafs");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_leaf_tx_weight_set) {
NL_SET_ERR_MSG_ATTR(info->extack,
attrs[DEVLINK_ATTR_RATE_TX_WEIGHT],
"TX weight set isn't supported for the leafs");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TC_BWS] &&
!ops->rate_leaf_tc_bw_set) {
NL_SET_ERR_MSG_ATTR(info->extack,
attrs[DEVLINK_ATTR_RATE_TC_BWS],
"TC bandwidth set isn't supported for the leafs");
return false;
}
} else if (type == DEVLINK_RATE_TYPE_NODE) {
if (attrs[DEVLINK_ATTR_RATE_TX_SHARE] && !ops->rate_node_tx_share_set) {
NL_SET_ERR_MSG(info->extack, "TX share set isn't supported for the nodes");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TX_MAX] && !ops->rate_node_tx_max_set) {
NL_SET_ERR_MSG(info->extack, "TX max set isn't supported for the nodes");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] &&
!ops->rate_node_parent_set) {
NL_SET_ERR_MSG(info->extack, "Parent set isn't supported for the nodes");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TX_PRIORITY] && !ops->rate_node_tx_priority_set) {
NL_SET_ERR_MSG_ATTR(info->extack,
attrs[DEVLINK_ATTR_RATE_TX_PRIORITY],
"TX priority set isn't supported for the nodes");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TX_WEIGHT] && !ops->rate_node_tx_weight_set) {
NL_SET_ERR_MSG_ATTR(info->extack,
attrs[DEVLINK_ATTR_RATE_TX_WEIGHT],
"TX weight set isn't supported for the nodes");
return false;
}
if (attrs[DEVLINK_ATTR_RATE_TC_BWS] &&
!ops->rate_node_tc_bw_set) {
NL_SET_ERR_MSG_ATTR(info->extack,
attrs[DEVLINK_ATTR_RATE_TC_BWS],
"TC bandwidth set isn't supported for the nodes");
return false;
}
} else {
WARN(1, "Unknown type of rate object");
return false;
}
return true;
}
int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_rate *devlink_rate;
const struct devlink_ops *ops;
int err;
devlink_rate = devlink_rate_get_from_info(devlink, info);
if (IS_ERR(devlink_rate))
return PTR_ERR(devlink_rate);
ops = devlink->ops;
if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type))
return -EOPNOTSUPP;
err = devlink_nl_rate_set(devlink_rate, ops, info);
if (!err)
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
return err;
}
int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_rate *rate_node;
const struct devlink_ops *ops;
int err;
ops = devlink->ops;
if (!ops || !ops->rate_node_new || !ops->rate_node_del) {
NL_SET_ERR_MSG(info->extack, "Rate nodes aren't supported");
return -EOPNOTSUPP;
}
if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE))
return -EOPNOTSUPP;
rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs);
if (!IS_ERR(rate_node))
return -EEXIST;
else if (rate_node == ERR_PTR(-EINVAL))
return -EINVAL;
rate_node = kzalloc_obj(*rate_node);
if (!rate_node)
return -ENOMEM;
rate_node->devlink = devlink;
rate_node->type = DEVLINK_RATE_TYPE_NODE;
rate_node->name = nla_strdup(info->attrs[DEVLINK_ATTR_RATE_NODE_NAME], GFP_KERNEL);
if (!rate_node->name) {
err = -ENOMEM;
goto err_strdup;
}
err = ops->rate_node_new(rate_node, &rate_node->priv, info->extack);
if (err)
goto err_node_new;
err = devlink_nl_rate_set(rate_node, ops, info);
if (err)
goto err_rate_set;
refcount_set(&rate_node->refcnt, 1);
list_add(&rate_node->list, &devlink->rate_list);
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
return 0;
err_rate_set:
ops->rate_node_del(rate_node, rate_node->priv, info->extack);
err_node_new:
kfree(rate_node->name);
err_strdup:
kfree(rate_node);
return err;
}
int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_rate *rate_node;
int err;
rate_node = devlink_rate_node_get_from_info(devlink, info);
if (IS_ERR(rate_node))
return PTR_ERR(rate_node);
if (refcount_read(&rate_node->refcnt) > 1) {
NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node.");
return -EBUSY;
}
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
err = devlink->ops->rate_node_del(rate_node, rate_node->priv,
info->extack);
if (rate_node->parent)
refcount_dec(&rate_node->parent->refcnt);
list_del(&rate_node->list);
kfree(rate_node->name);
kfree(rate_node);
return err;
}
int devlink_rates_check(struct devlink *devlink,
bool (*rate_filter)(const struct devlink_rate *),
struct netlink_ext_ack *extack)
{
struct devlink_rate *devlink_rate;
list_for_each_entry(devlink_rate, &devlink->rate_list, list)
if (!rate_filter || rate_filter(devlink_rate)) {
if (extack)
NL_SET_ERR_MSG(extack, "Rate node(s) exists.");
return -EBUSY;
}
return 0;
}
/**
* devl_rate_node_create - create devlink rate node
* @devlink: devlink instance
* @priv: driver private data
* @node_name: name of the resulting node
* @parent: parent devlink_rate struct
*
* Create devlink rate object of type node
*/
struct devlink_rate *
devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
struct devlink_rate *parent)
{
struct devlink_rate *rate_node;
rate_node = devlink_rate_node_get_by_name(devlink, node_name);
if (!IS_ERR(rate_node))
return ERR_PTR(-EEXIST);
rate_node = kzalloc_obj(*rate_node);
if (!rate_node)
return ERR_PTR(-ENOMEM);
rate_node->type = DEVLINK_RATE_TYPE_NODE;
rate_node->devlink = devlink;
rate_node->priv = priv;
rate_node->name = kstrdup(node_name, GFP_KERNEL);
if (!rate_node->name) {
kfree(rate_node);
return ERR_PTR(-ENOMEM);
}
if (parent) {
rate_node->parent = parent;
refcount_inc(&rate_node->parent->refcnt);
}
refcount_set(&rate_node->refcnt, 1);
list_add(&rate_node->list, &devlink->rate_list);
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
return rate_node;
}
EXPORT_SYMBOL_GPL(devl_rate_node_create);
/**
* devl_rate_leaf_create - create devlink rate leaf
* @devlink_port: devlink port object to create rate object on
* @priv: driver private data
* @parent: parent devlink_rate struct
*
* Create devlink rate object of type leaf on provided @devlink_port.
*/
int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
struct devlink_rate *parent)
{
struct devlink *devlink = devlink_port->devlink;
struct devlink_rate *devlink_rate;
devl_assert_locked(devlink_port->devlink);
if (WARN_ON(devlink_port->devlink_rate))
return -EBUSY;
devlink_rate = kzalloc_obj(*devlink_rate);
if (!devlink_rate)
return -ENOMEM;
if (parent) {
devlink_rate->parent = parent;
refcount_inc(&devlink_rate->parent->refcnt);
}
devlink_rate->type = DEVLINK_RATE_TYPE_LEAF;
devlink_rate->devlink = devlink;
devlink_rate->devlink_port = devlink_port;
devlink_rate->priv = priv;
list_add_tail(&devlink_rate->list, &devlink->rate_list);
devlink_port->devlink_rate = devlink_rate;
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
return 0;
}
EXPORT_SYMBOL_GPL(devl_rate_leaf_create);
/**
* devl_rate_leaf_destroy - destroy devlink rate leaf
*
* @devlink_port: devlink port linked to the rate object
*
* Destroy the devlink rate object of type leaf on provided @devlink_port.
*/
void devl_rate_leaf_destroy(struct devlink_port *devlink_port)
{
struct devlink_rate *devlink_rate = devlink_port->devlink_rate;
devl_assert_locked(devlink_port->devlink);
if (!devlink_rate)
return;
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL);
if (devlink_rate->parent)
refcount_dec(&devlink_rate->parent->refcnt);
list_del(&devlink_rate->list);
devlink_port->devlink_rate = NULL;
kfree(devlink_rate);
}
EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy);
/**
* devl_rate_nodes_destroy - destroy all devlink rate nodes on device
* @devlink: devlink instance
*
* Unset parent for all rate objects and destroy all rate nodes
* on specified device.
*/
void devl_rate_nodes_destroy(struct devlink *devlink)
{
const struct devlink_ops *ops = devlink->ops;
struct devlink_rate *devlink_rate, *tmp;
devl_assert_locked(devlink);
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
if (!devlink_rate->parent)
continue;
if (devlink_rate_is_leaf(devlink_rate))
ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv,
NULL, NULL);
else if (devlink_rate_is_node(devlink_rate))
ops->rate_node_parent_set(devlink_rate, NULL, devlink_rate->priv,
NULL, NULL);
refcount_dec(&devlink_rate->parent->refcnt);
devlink_rate->parent = NULL;
}
list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) {
if (devlink_rate_is_node(devlink_rate)) {
ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL);
list_del(&devlink_rate->list);
kfree(devlink_rate->name);
kfree(devlink_rate);
}
}
}
EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy);
File diff suppressed because it is too large Load Diff
+730
View File
@@ -0,0 +1,730 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include "devl_internal.h"
/**
* struct devlink_resource - devlink resource
* @name: name of the resource
* @id: id, per devlink instance
* @size: size of the resource
* @size_new: updated size of the resource, reload is needed
* @size_valid: valid in case the total size of the resource is valid
* including its children
* @parent: parent resource
* @size_params: size parameters
* @list: parent list
* @resource_list: list of child resources
* @occ_get: occupancy getter callback
* @occ_get_priv: occupancy getter callback priv
*/
struct devlink_resource {
const char *name;
u64 id;
u64 size;
u64 size_new;
bool size_valid;
struct devlink_resource *parent;
struct devlink_resource_size_params size_params;
struct list_head list;
struct list_head resource_list;
devlink_resource_occ_get_t *occ_get;
void *occ_get_priv;
};
static struct devlink_resource *
__devlink_resource_find(struct list_head *resource_list_head,
struct devlink_resource *resource,
u64 resource_id)
{
struct list_head *resource_list;
if (resource)
resource_list = &resource->resource_list;
else
resource_list = resource_list_head;
list_for_each_entry(resource, resource_list, list) {
struct devlink_resource *child_resource;
if (resource->id == resource_id)
return resource;
child_resource = __devlink_resource_find(resource_list_head,
resource,
resource_id);
if (child_resource)
return child_resource;
}
return NULL;
}
static struct devlink_resource *
devlink_resource_find(struct devlink *devlink,
struct devlink_resource *resource, u64 resource_id)
{
return __devlink_resource_find(&devlink->resource_list,
resource, resource_id);
}
static void
devlink_resource_validate_children(struct devlink_resource *resource)
{
struct devlink_resource *child_resource;
bool size_valid = true;
u64 parts_size = 0;
if (list_empty(&resource->resource_list))
goto out;
list_for_each_entry(child_resource, &resource->resource_list, list)
parts_size += child_resource->size_new;
if (parts_size > resource->size_new)
size_valid = false;
out:
resource->size_valid = size_valid;
}
static int
devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
struct netlink_ext_ack *extack)
{
u64 reminder;
int err = 0;
if (size > resource->size_params.size_max) {
NL_SET_ERR_MSG(extack, "Size larger than maximum");
err = -EINVAL;
}
if (size < resource->size_params.size_min) {
NL_SET_ERR_MSG(extack, "Size smaller than minimum");
err = -EINVAL;
}
div64_u64_rem(size, resource->size_params.size_granularity, &reminder);
if (reminder) {
NL_SET_ERR_MSG(extack, "Wrong granularity");
err = -EINVAL;
}
return err;
}
int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_resource *resource;
u64 resource_id;
u64 size;
int err;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_ID) ||
GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_RESOURCE_SIZE))
return -EINVAL;
resource_id = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_ID]);
resource = devlink_resource_find(devlink, NULL, resource_id);
if (!resource)
return -EINVAL;
size = nla_get_u64(info->attrs[DEVLINK_ATTR_RESOURCE_SIZE]);
err = devlink_resource_validate_size(resource, size, info->extack);
if (err)
return err;
resource->size_new = size;
devlink_resource_validate_children(resource);
if (resource->parent)
devlink_resource_validate_children(resource->parent);
return 0;
}
static int
devlink_resource_size_params_put(struct devlink_resource *resource,
struct sk_buff *skb)
{
struct devlink_resource_size_params *size_params;
size_params = &resource->size_params;
if (devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_GRAN,
size_params->size_granularity) ||
devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MAX,
size_params->size_max) ||
devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_MIN,
size_params->size_min) ||
nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_UNIT, size_params->unit))
return -EMSGSIZE;
return 0;
}
static int devlink_resource_occ_put(struct devlink_resource *resource,
struct sk_buff *skb)
{
if (!resource->occ_get)
return 0;
return devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_OCC,
resource->occ_get(resource->occ_get_priv));
}
static int devlink_resource_put(struct devlink *devlink, struct sk_buff *skb,
struct devlink_resource *resource)
{
struct devlink_resource *child_resource;
struct nlattr *child_resource_attr;
struct nlattr *resource_attr;
resource_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE);
if (!resource_attr)
return -EMSGSIZE;
if (nla_put_string(skb, DEVLINK_ATTR_RESOURCE_NAME, resource->name) ||
devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE, resource->size) ||
devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_ID, resource->id))
goto nla_put_failure;
if (resource->size != resource->size_new &&
devlink_nl_put_u64(skb, DEVLINK_ATTR_RESOURCE_SIZE_NEW,
resource->size_new))
goto nla_put_failure;
if (devlink_resource_occ_put(resource, skb))
goto nla_put_failure;
if (devlink_resource_size_params_put(resource, skb))
goto nla_put_failure;
if (list_empty(&resource->resource_list))
goto out;
if (nla_put_u8(skb, DEVLINK_ATTR_RESOURCE_SIZE_VALID,
resource->size_valid))
goto nla_put_failure;
child_resource_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_RESOURCE_LIST);
if (!child_resource_attr)
goto nla_put_failure;
list_for_each_entry(child_resource, &resource->resource_list, list) {
if (devlink_resource_put(devlink, skb, child_resource))
goto resource_put_failure;
}
nla_nest_end(skb, child_resource_attr);
out:
nla_nest_end(skb, resource_attr);
return 0;
resource_put_failure:
nla_nest_cancel(skb, child_resource_attr);
nla_put_failure:
nla_nest_cancel(skb, resource_attr);
return -EMSGSIZE;
}
static int devlink_resource_list_fill(struct sk_buff *skb,
struct devlink *devlink,
struct list_head *resource_list_head,
int *idx)
{
struct devlink_resource *resource;
int i = 0;
int err;
list_for_each_entry(resource, resource_list_head, list) {
if (i < *idx) {
i++;
continue;
}
err = devlink_resource_put(devlink, skb, resource);
if (err) {
*idx = i;
return err;
}
i++;
}
*idx = 0;
return 0;
}
static int devlink_resource_fill(struct genl_info *info,
enum devlink_command cmd, int flags)
{
struct devlink_port *devlink_port = info->user_ptr[1];
struct devlink *devlink = info->user_ptr[0];
struct devlink_resource *resource;
struct list_head *resource_list;
struct nlattr *resources_attr;
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
bool incomplete;
void *hdr;
int i;
int err;
resource_list = devlink_port ?
&devlink_port->resource_list : &devlink->resource_list;
resource = list_first_entry(resource_list,
struct devlink_resource, list);
start_again:
err = devlink_nl_msg_reply_and_new(&skb, info);
if (err)
return err;
hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
&devlink_nl_family, NLM_F_MULTI, cmd);
if (!hdr) {
nlmsg_free(skb);
return -EMSGSIZE;
}
if (devlink_nl_put_handle(skb, devlink))
goto nla_put_failure;
if (devlink_port &&
nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
goto nla_put_failure;
resources_attr = nla_nest_start_noflag(skb,
DEVLINK_ATTR_RESOURCE_LIST);
if (!resources_attr)
goto nla_put_failure;
incomplete = false;
i = 0;
list_for_each_entry_from(resource, resource_list, list) {
err = devlink_resource_put(devlink, skb, resource);
if (err) {
if (!i)
goto err_resource_put;
incomplete = true;
break;
}
i++;
}
nla_nest_end(skb, resources_attr);
genlmsg_end(skb, hdr);
if (incomplete)
goto start_again;
send_done:
nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq,
NLMSG_DONE, 0, flags | NLM_F_MULTI);
if (!nlh) {
err = devlink_nl_msg_reply_and_new(&skb, info);
if (err)
return err;
goto send_done;
}
return genlmsg_reply(skb, info);
nla_put_failure:
err = -EMSGSIZE;
err_resource_put:
nlmsg_free(skb);
return err;
}
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink_port *devlink_port = info->user_ptr[1];
struct devlink *devlink = info->user_ptr[0];
struct list_head *resource_list;
if (info->attrs[DEVLINK_ATTR_PORT_INDEX] && !devlink_port)
return -ENODEV;
resource_list = devlink_port ?
&devlink_port->resource_list : &devlink->resource_list;
if (list_empty(resource_list))
return -EOPNOTSUPP;
return devlink_resource_fill(info, DEVLINK_CMD_RESOURCE_DUMP, 0);
}
static int
devlink_resource_dump_fill_one(struct sk_buff *skb, struct devlink *devlink,
struct devlink_port *devlink_port,
struct netlink_callback *cb, int flags, int *idx)
{
struct list_head *resource_list;
struct nlattr *resources_attr;
int start_idx = *idx;
void *hdr;
int err;
resource_list = devlink_port ?
&devlink_port->resource_list : &devlink->resource_list;
if (list_empty(resource_list))
return 0;
err = -EMSGSIZE;
hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
&devlink_nl_family, flags, DEVLINK_CMD_RESOURCE_DUMP);
if (!hdr)
return err;
if (devlink_nl_put_handle(skb, devlink))
goto nla_put_failure;
if (devlink_port &&
nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
goto nla_put_failure;
resources_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_RESOURCE_LIST);
if (!resources_attr)
goto nla_put_failure;
err = devlink_resource_list_fill(skb, devlink, resource_list, idx);
if (err) {
if (*idx == start_idx)
goto resource_list_cancel;
nla_nest_end(skb, resources_attr);
genlmsg_end(skb, hdr);
return err;
}
nla_nest_end(skb, resources_attr);
genlmsg_end(skb, hdr);
return 0;
resource_list_cancel:
nla_nest_cancel(skb, resources_attr);
nla_put_failure:
genlmsg_cancel(skb, hdr);
return err;
}
static int
devlink_nl_resource_dump_one(struct sk_buff *skb, struct devlink *devlink,
struct netlink_callback *cb, int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
const struct genl_info *info = genl_info_dump(cb);
struct devlink_port *devlink_port;
struct nlattr *scope_attr = NULL;
unsigned long port_idx;
u32 scope = 0;
int err;
if (info->attrs && info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK]) {
scope_attr = info->attrs[DEVLINK_ATTR_RESOURCE_SCOPE_MASK];
scope = nla_get_u32(scope_attr);
if (!scope) {
NL_SET_ERR_MSG_ATTR(info->extack, scope_attr,
"empty resource scope selection");
return -EINVAL;
}
}
if (!state->port_ctx.index_valid &&
(!scope || (scope & DEVLINK_RESOURCE_SCOPE_DEV))) {
err = devlink_resource_dump_fill_one(skb, devlink, NULL,
cb, flags, &state->idx);
if (err)
return err;
state->idx = 0;
}
if (scope && !(scope & DEVLINK_RESOURCE_SCOPE_PORT))
goto out;
/* Check in case port was removed between dump callbacks. */
if (state->port_ctx.index_valid &&
!xa_load(&devlink->ports, state->port_ctx.index))
state->idx = 0;
state->port_ctx.index_valid = true;
xa_for_each_start(&devlink->ports, port_idx, devlink_port,
state->port_ctx.index) {
err = devlink_resource_dump_fill_one(skb, devlink, devlink_port,
cb, flags, &state->idx);
if (err) {
state->port_ctx.index = port_idx;
return err;
}
state->idx = 0;
}
out:
state->port_ctx.index_valid = false;
state->port_ctx.index = 0;
return 0;
}
int devlink_nl_resource_dump_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb, devlink_nl_resource_dump_one);
}
int devlink_resources_validate(struct devlink *devlink,
struct devlink_resource *resource,
struct genl_info *info)
{
struct list_head *resource_list;
int err = 0;
if (resource)
resource_list = &resource->resource_list;
else
resource_list = &devlink->resource_list;
list_for_each_entry(resource, resource_list, list) {
if (!resource->size_valid)
return -EINVAL;
err = devlink_resources_validate(devlink, resource, info);
if (err)
return err;
}
return err;
}
static int
__devl_resource_register(struct devlink *devlink,
struct list_head *resource_list_head,
const char *resource_name, u64 resource_size,
u64 resource_id, u64 parent_resource_id,
const struct devlink_resource_size_params *params)
{
struct devlink_resource *resource;
struct list_head *resource_list;
bool top_hierarchy;
WARN_ON(resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP);
lockdep_assert_held(&devlink->lock);
top_hierarchy = parent_resource_id == DEVLINK_RESOURCE_ID_PARENT_TOP;
resource = __devlink_resource_find(resource_list_head, NULL,
resource_id);
if (resource)
return -EEXIST;
resource = kzalloc_obj(*resource);
if (!resource)
return -ENOMEM;
if (top_hierarchy) {
resource_list = resource_list_head;
} else {
struct devlink_resource *parent_resource;
parent_resource = __devlink_resource_find(resource_list_head,
NULL,
parent_resource_id);
if (parent_resource) {
resource_list = &parent_resource->resource_list;
resource->parent = parent_resource;
} else {
kfree(resource);
return -EINVAL;
}
}
resource->name = resource_name;
resource->size = resource_size;
resource->size_new = resource_size;
resource->id = resource_id;
resource->size_valid = true;
memcpy(&resource->size_params, params, sizeof(resource->size_params));
INIT_LIST_HEAD(&resource->resource_list);
list_add_tail(&resource->list, resource_list);
return 0;
}
/**
* devl_resource_register - devlink resource register
*
* @devlink: devlink
* @resource_name: resource's name
* @resource_size: resource's size
* @resource_id: resource's id
* @parent_resource_id: resource's parent id
* @params: size parameters
*
* Generic resources should reuse the same names across drivers.
* Please see the generic resources list at:
* Documentation/networking/devlink/devlink-resource.rst
*
* Return: 0 on success, negative error code otherwise.
*/
int devl_resource_register(struct devlink *devlink, const char *resource_name,
u64 resource_size, u64 resource_id,
u64 parent_resource_id,
const struct devlink_resource_size_params *params)
{
return __devl_resource_register(devlink, &devlink->resource_list,
resource_name, resource_size,
resource_id, parent_resource_id,
params);
}
EXPORT_SYMBOL_GPL(devl_resource_register);
static void devlink_resource_unregister(struct devlink_resource *resource)
{
struct devlink_resource *tmp, *child_resource;
list_for_each_entry_safe(child_resource, tmp, &resource->resource_list,
list) {
devlink_resource_unregister(child_resource);
list_del(&child_resource->list);
kfree(child_resource);
}
}
static void
__devl_resources_unregister(struct devlink *devlink,
struct list_head *resource_list_head)
{
struct devlink_resource *tmp, *child_resource;
lockdep_assert_held(&devlink->lock);
list_for_each_entry_safe(child_resource, tmp, resource_list_head,
list) {
devlink_resource_unregister(child_resource);
list_del(&child_resource->list);
kfree(child_resource);
}
}
/**
* devl_resources_unregister - free all resources
*
* @devlink: devlink
*/
void devl_resources_unregister(struct devlink *devlink)
{
__devl_resources_unregister(devlink, &devlink->resource_list);
}
EXPORT_SYMBOL_GPL(devl_resources_unregister);
/**
* devlink_resources_unregister - free all resources
*
* @devlink: devlink
*
* Context: Takes and release devlink->lock <mutex>.
*/
void devlink_resources_unregister(struct devlink *devlink)
{
devl_lock(devlink);
devl_resources_unregister(devlink);
devl_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devlink_resources_unregister);
/**
* devl_resource_size_get - get and update size
*
* @devlink: devlink
* @resource_id: the requested resource id
* @p_resource_size: ptr to update
*/
int devl_resource_size_get(struct devlink *devlink,
u64 resource_id,
u64 *p_resource_size)
{
struct devlink_resource *resource;
lockdep_assert_held(&devlink->lock);
resource = devlink_resource_find(devlink, NULL, resource_id);
if (!resource)
return -EINVAL;
*p_resource_size = resource->size_new;
resource->size = resource->size_new;
return 0;
}
EXPORT_SYMBOL_GPL(devl_resource_size_get);
/**
* devl_resource_occ_get_register - register occupancy getter
*
* @devlink: devlink
* @resource_id: resource id
* @occ_get: occupancy getter callback
* @occ_get_priv: occupancy getter callback priv
*/
void devl_resource_occ_get_register(struct devlink *devlink,
u64 resource_id,
devlink_resource_occ_get_t *occ_get,
void *occ_get_priv)
{
struct devlink_resource *resource;
lockdep_assert_held(&devlink->lock);
resource = devlink_resource_find(devlink, NULL, resource_id);
if (WARN_ON(!resource))
return;
WARN_ON(resource->occ_get);
resource->occ_get = occ_get;
resource->occ_get_priv = occ_get_priv;
}
EXPORT_SYMBOL_GPL(devl_resource_occ_get_register);
/**
* devl_resource_occ_get_unregister - unregister occupancy getter
*
* @devlink: devlink
* @resource_id: resource id
*/
void devl_resource_occ_get_unregister(struct devlink *devlink,
u64 resource_id)
{
struct devlink_resource *resource;
lockdep_assert_held(&devlink->lock);
resource = devlink_resource_find(devlink, NULL, resource_id);
if (WARN_ON(!resource))
return;
WARN_ON(!resource->occ_get);
resource->occ_get = NULL;
resource->occ_get_priv = NULL;
}
EXPORT_SYMBOL_GPL(devl_resource_occ_get_unregister);
/**
* devl_port_resource_register - devlink port resource register
*
* @devlink_port: devlink port
* @resource_name: resource's name
* @resource_size: resource's size
* @resource_id: resource's id
* @parent_resource_id: resource's parent id
* @params: size parameters
*
* Generic resources should reuse the same names across drivers.
* Please see the generic resources list at:
* Documentation/networking/devlink/devlink-resource.rst
*
* Return: 0 on success, negative error code otherwise.
*/
int
devl_port_resource_register(struct devlink_port *devlink_port,
const char *resource_name,
u64 resource_size, u64 resource_id,
u64 parent_resource_id,
const struct devlink_resource_size_params *params)
{
return __devl_resource_register(devlink_port->devlink,
&devlink_port->resource_list,
resource_name, resource_size,
resource_id, parent_resource_id,
params);
}
EXPORT_SYMBOL_GPL(devl_port_resource_register);
/**
* devl_port_resources_unregister - unregister all devlink port resources
*
* @devlink_port: devlink port
*/
void devl_port_resources_unregister(struct devlink_port *devlink_port)
{
__devl_resources_unregister(devlink_port->devlink,
&devlink_port->resource_list);
}
EXPORT_SYMBOL_GPL(devl_port_resources_unregister);
+995
View File
@@ -0,0 +1,995 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*/
#include "devl_internal.h"
struct devlink_sb {
struct list_head list;
unsigned int index;
u32 size;
u16 ingress_pools_count;
u16 egress_pools_count;
u16 ingress_tc_count;
u16 egress_tc_count;
};
static u16 devlink_sb_pool_count(struct devlink_sb *devlink_sb)
{
return devlink_sb->ingress_pools_count + devlink_sb->egress_pools_count;
}
static struct devlink_sb *devlink_sb_get_by_index(struct devlink *devlink,
unsigned int sb_index)
{
struct devlink_sb *devlink_sb;
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
if (devlink_sb->index == sb_index)
return devlink_sb;
}
return NULL;
}
static bool devlink_sb_index_exists(struct devlink *devlink,
unsigned int sb_index)
{
return devlink_sb_get_by_index(devlink, sb_index);
}
static struct devlink_sb *devlink_sb_get_from_attrs(struct devlink *devlink,
struct nlattr **attrs)
{
if (attrs[DEVLINK_ATTR_SB_INDEX]) {
u32 sb_index = nla_get_u32(attrs[DEVLINK_ATTR_SB_INDEX]);
struct devlink_sb *devlink_sb;
devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
if (!devlink_sb)
return ERR_PTR(-ENODEV);
return devlink_sb;
}
return ERR_PTR(-EINVAL);
}
static struct devlink_sb *devlink_sb_get_from_info(struct devlink *devlink,
struct genl_info *info)
{
return devlink_sb_get_from_attrs(devlink, info->attrs);
}
static int devlink_sb_pool_index_get_from_attrs(struct devlink_sb *devlink_sb,
struct nlattr **attrs,
u16 *p_pool_index)
{
u16 val;
if (!attrs[DEVLINK_ATTR_SB_POOL_INDEX])
return -EINVAL;
val = nla_get_u16(attrs[DEVLINK_ATTR_SB_POOL_INDEX]);
if (val >= devlink_sb_pool_count(devlink_sb))
return -EINVAL;
*p_pool_index = val;
return 0;
}
static int devlink_sb_pool_index_get_from_info(struct devlink_sb *devlink_sb,
struct genl_info *info,
u16 *p_pool_index)
{
return devlink_sb_pool_index_get_from_attrs(devlink_sb, info->attrs,
p_pool_index);
}
static int
devlink_sb_pool_type_get_from_attrs(struct nlattr **attrs,
enum devlink_sb_pool_type *p_pool_type)
{
u8 val;
if (!attrs[DEVLINK_ATTR_SB_POOL_TYPE])
return -EINVAL;
val = nla_get_u8(attrs[DEVLINK_ATTR_SB_POOL_TYPE]);
if (val != DEVLINK_SB_POOL_TYPE_INGRESS &&
val != DEVLINK_SB_POOL_TYPE_EGRESS)
return -EINVAL;
*p_pool_type = val;
return 0;
}
static int
devlink_sb_pool_type_get_from_info(struct genl_info *info,
enum devlink_sb_pool_type *p_pool_type)
{
return devlink_sb_pool_type_get_from_attrs(info->attrs, p_pool_type);
}
static int
devlink_sb_th_type_get_from_attrs(struct nlattr **attrs,
enum devlink_sb_threshold_type *p_th_type)
{
u8 val;
if (!attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
return -EINVAL;
val = nla_get_u8(attrs[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE]);
if (val != DEVLINK_SB_THRESHOLD_TYPE_STATIC &&
val != DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC)
return -EINVAL;
*p_th_type = val;
return 0;
}
static int
devlink_sb_th_type_get_from_info(struct genl_info *info,
enum devlink_sb_threshold_type *p_th_type)
{
return devlink_sb_th_type_get_from_attrs(info->attrs, p_th_type);
}
static int
devlink_sb_tc_index_get_from_attrs(struct devlink_sb *devlink_sb,
struct nlattr **attrs,
enum devlink_sb_pool_type pool_type,
u16 *p_tc_index)
{
u16 val;
if (!attrs[DEVLINK_ATTR_SB_TC_INDEX])
return -EINVAL;
val = nla_get_u16(attrs[DEVLINK_ATTR_SB_TC_INDEX]);
if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS &&
val >= devlink_sb->ingress_tc_count)
return -EINVAL;
if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS &&
val >= devlink_sb->egress_tc_count)
return -EINVAL;
*p_tc_index = val;
return 0;
}
static int
devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,
struct genl_info *info,
enum devlink_sb_pool_type pool_type,
u16 *p_tc_index)
{
return devlink_sb_tc_index_get_from_attrs(devlink_sb, info->attrs,
pool_type, p_tc_index);
}
static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
struct devlink_sb *devlink_sb,
enum devlink_command cmd, u32 portid,
u32 seq, int flags)
{
void *hdr;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_SIZE, devlink_sb->size))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_INGRESS_POOL_COUNT,
devlink_sb->ingress_pools_count))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_EGRESS_POOL_COUNT,
devlink_sb->egress_pools_count))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_INGRESS_TC_COUNT,
devlink_sb->ingress_tc_count))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_EGRESS_TC_COUNT,
devlink_sb->egress_tc_count))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
int devlink_nl_sb_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_sb_fill(msg, devlink, devlink_sb,
DEVLINK_CMD_SB_NEW,
info->snd_portid, info->snd_seq, 0);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int
devlink_nl_sb_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
struct netlink_callback *cb, int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_sb *devlink_sb;
int idx = 0;
int err = 0;
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
if (idx < state->idx) {
idx++;
continue;
}
err = devlink_nl_sb_fill(msg, devlink, devlink_sb,
DEVLINK_CMD_SB_NEW,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, flags);
if (err) {
state->idx = idx;
break;
}
idx++;
}
return err;
}
int devlink_nl_sb_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb, devlink_nl_sb_get_dump_one);
}
static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
struct devlink_sb *devlink_sb,
u16 pool_index, enum devlink_command cmd,
u32 portid, u32 seq, int flags)
{
struct devlink_sb_pool_info pool_info;
void *hdr;
int err;
err = devlink->ops->sb_pool_get(devlink, devlink_sb->index,
pool_index, &pool_info);
if (err)
return err;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_POOL_INDEX, pool_index))
goto nla_put_failure;
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_TYPE, pool_info.pool_type))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_SIZE, pool_info.size))
goto nla_put_failure;
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
pool_info.threshold_type))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_POOL_CELL_SIZE,
pool_info.cell_size))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
int devlink_nl_sb_pool_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
u16 pool_index;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
&pool_index);
if (err)
return err;
if (!devlink->ops->sb_pool_get)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_sb_pool_fill(msg, devlink, devlink_sb, pool_index,
DEVLINK_CMD_SB_POOL_NEW,
info->snd_portid, info->snd_seq, 0);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int __sb_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx,
struct devlink *devlink,
struct devlink_sb *devlink_sb,
u32 portid, u32 seq, int flags)
{
u16 pool_count = devlink_sb_pool_count(devlink_sb);
u16 pool_index;
int err;
for (pool_index = 0; pool_index < pool_count; pool_index++) {
if (*p_idx < start) {
(*p_idx)++;
continue;
}
err = devlink_nl_sb_pool_fill(msg, devlink,
devlink_sb,
pool_index,
DEVLINK_CMD_SB_POOL_NEW,
portid, seq, flags);
if (err)
return err;
(*p_idx)++;
}
return 0;
}
static int
devlink_nl_sb_pool_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
struct netlink_callback *cb, int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_sb *devlink_sb;
int err = 0;
int idx = 0;
if (!devlink->ops->sb_pool_get)
return 0;
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
err = __sb_pool_get_dumpit(msg, state->idx, &idx,
devlink, devlink_sb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, flags);
if (err == -EOPNOTSUPP) {
err = 0;
} else if (err) {
state->idx = idx;
break;
}
}
return err;
}
int devlink_nl_sb_pool_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb, devlink_nl_sb_pool_get_dump_one);
}
static int devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index,
u16 pool_index, u32 size,
enum devlink_sb_threshold_type threshold_type,
struct netlink_ext_ack *extack)
{
const struct devlink_ops *ops = devlink->ops;
if (ops->sb_pool_set)
return ops->sb_pool_set(devlink, sb_index, pool_index,
size, threshold_type, extack);
return -EOPNOTSUPP;
}
int devlink_nl_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
enum devlink_sb_threshold_type threshold_type;
struct devlink_sb *devlink_sb;
u16 pool_index;
u32 size;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
&pool_index);
if (err)
return err;
err = devlink_sb_th_type_get_from_info(info, &threshold_type);
if (err)
return err;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_POOL_SIZE))
return -EINVAL;
size = nla_get_u32(info->attrs[DEVLINK_ATTR_SB_POOL_SIZE]);
return devlink_sb_pool_set(devlink, devlink_sb->index,
pool_index, size, threshold_type,
info->extack);
}
static int devlink_nl_sb_port_pool_fill(struct sk_buff *msg,
struct devlink *devlink,
struct devlink_port *devlink_port,
struct devlink_sb *devlink_sb,
u16 pool_index,
enum devlink_command cmd,
u32 portid, u32 seq, int flags)
{
const struct devlink_ops *ops = devlink->ops;
u32 threshold;
void *hdr;
int err;
err = ops->sb_port_pool_get(devlink_port, devlink_sb->index,
pool_index, &threshold);
if (err)
return err;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_POOL_INDEX, pool_index))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_THRESHOLD, threshold))
goto nla_put_failure;
if (ops->sb_occ_port_pool_get) {
u32 cur;
u32 max;
err = ops->sb_occ_port_pool_get(devlink_port, devlink_sb->index,
pool_index, &cur, &max);
if (err && err != -EOPNOTSUPP)
goto sb_occ_get_failure;
if (!err) {
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_CUR, cur))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_MAX, max))
goto nla_put_failure;
}
}
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
err = -EMSGSIZE;
sb_occ_get_failure:
genlmsg_cancel(msg, hdr);
return err;
}
int devlink_nl_sb_port_pool_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_port *devlink_port = info->user_ptr[1];
struct devlink *devlink = devlink_port->devlink;
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
u16 pool_index;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
&pool_index);
if (err)
return err;
if (!devlink->ops->sb_port_pool_get)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_sb_port_pool_fill(msg, devlink, devlink_port,
devlink_sb, pool_index,
DEVLINK_CMD_SB_PORT_POOL_NEW,
info->snd_portid, info->snd_seq, 0);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int __sb_port_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx,
struct devlink *devlink,
struct devlink_sb *devlink_sb,
u32 portid, u32 seq, int flags)
{
struct devlink_port *devlink_port;
u16 pool_count = devlink_sb_pool_count(devlink_sb);
unsigned long port_index;
u16 pool_index;
int err;
xa_for_each(&devlink->ports, port_index, devlink_port) {
for (pool_index = 0; pool_index < pool_count; pool_index++) {
if (*p_idx < start) {
(*p_idx)++;
continue;
}
err = devlink_nl_sb_port_pool_fill(msg, devlink,
devlink_port,
devlink_sb,
pool_index,
DEVLINK_CMD_SB_PORT_POOL_NEW,
portid, seq, flags);
if (err)
return err;
(*p_idx)++;
}
}
return 0;
}
static int
devlink_nl_sb_port_pool_get_dump_one(struct sk_buff *msg,
struct devlink *devlink,
struct netlink_callback *cb, int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_sb *devlink_sb;
int idx = 0;
int err = 0;
if (!devlink->ops->sb_port_pool_get)
return 0;
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
err = __sb_port_pool_get_dumpit(msg, state->idx, &idx,
devlink, devlink_sb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, flags);
if (err == -EOPNOTSUPP) {
err = 0;
} else if (err) {
state->idx = idx;
break;
}
}
return err;
}
int devlink_nl_sb_port_pool_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb, devlink_nl_sb_port_pool_get_dump_one);
}
static int devlink_sb_port_pool_set(struct devlink_port *devlink_port,
unsigned int sb_index, u16 pool_index,
u32 threshold,
struct netlink_ext_ack *extack)
{
const struct devlink_ops *ops = devlink_port->devlink->ops;
if (ops->sb_port_pool_set)
return ops->sb_port_pool_set(devlink_port, sb_index,
pool_index, threshold, extack);
return -EOPNOTSUPP;
}
int devlink_nl_sb_port_pool_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_port *devlink_port = info->user_ptr[1];
struct devlink *devlink = info->user_ptr[0];
struct devlink_sb *devlink_sb;
u16 pool_index;
u32 threshold;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
&pool_index);
if (err)
return err;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_THRESHOLD))
return -EINVAL;
threshold = nla_get_u32(info->attrs[DEVLINK_ATTR_SB_THRESHOLD]);
return devlink_sb_port_pool_set(devlink_port, devlink_sb->index,
pool_index, threshold, info->extack);
}
static int
devlink_nl_sb_tc_pool_bind_fill(struct sk_buff *msg, struct devlink *devlink,
struct devlink_port *devlink_port,
struct devlink_sb *devlink_sb, u16 tc_index,
enum devlink_sb_pool_type pool_type,
enum devlink_command cmd,
u32 portid, u32 seq, int flags)
{
const struct devlink_ops *ops = devlink->ops;
u16 pool_index;
u32 threshold;
void *hdr;
int err;
err = ops->sb_tc_pool_bind_get(devlink_port, devlink_sb->index,
tc_index, pool_type,
&pool_index, &threshold);
if (err)
return err;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_INDEX, devlink_sb->index))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_TC_INDEX, tc_index))
goto nla_put_failure;
if (nla_put_u8(msg, DEVLINK_ATTR_SB_POOL_TYPE, pool_type))
goto nla_put_failure;
if (nla_put_u16(msg, DEVLINK_ATTR_SB_POOL_INDEX, pool_index))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_THRESHOLD, threshold))
goto nla_put_failure;
if (ops->sb_occ_tc_port_bind_get) {
u32 cur;
u32 max;
err = ops->sb_occ_tc_port_bind_get(devlink_port,
devlink_sb->index,
tc_index, pool_type,
&cur, &max);
if (err && err != -EOPNOTSUPP)
return err;
if (!err) {
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_CUR, cur))
goto nla_put_failure;
if (nla_put_u32(msg, DEVLINK_ATTR_SB_OCC_MAX, max))
goto nla_put_failure;
}
}
genlmsg_end(msg, hdr);
return 0;
nla_put_failure:
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
}
int devlink_nl_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_port *devlink_port = info->user_ptr[1];
struct devlink *devlink = devlink_port->devlink;
struct devlink_sb *devlink_sb;
struct sk_buff *msg;
enum devlink_sb_pool_type pool_type;
u16 tc_index;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
err = devlink_sb_pool_type_get_from_info(info, &pool_type);
if (err)
return err;
err = devlink_sb_tc_index_get_from_info(devlink_sb, info,
pool_type, &tc_index);
if (err)
return err;
if (!devlink->ops->sb_tc_pool_bind_get)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink, devlink_port,
devlink_sb, tc_index, pool_type,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
info->snd_portid,
info->snd_seq, 0);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int __sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
int start, int *p_idx,
struct devlink *devlink,
struct devlink_sb *devlink_sb,
u32 portid, u32 seq, int flags)
{
struct devlink_port *devlink_port;
unsigned long port_index;
u16 tc_index;
int err;
xa_for_each(&devlink->ports, port_index, devlink_port) {
for (tc_index = 0;
tc_index < devlink_sb->ingress_tc_count; tc_index++) {
if (*p_idx < start) {
(*p_idx)++;
continue;
}
err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink,
devlink_port,
devlink_sb,
tc_index,
DEVLINK_SB_POOL_TYPE_INGRESS,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
portid, seq,
flags);
if (err)
return err;
(*p_idx)++;
}
for (tc_index = 0;
tc_index < devlink_sb->egress_tc_count; tc_index++) {
if (*p_idx < start) {
(*p_idx)++;
continue;
}
err = devlink_nl_sb_tc_pool_bind_fill(msg, devlink,
devlink_port,
devlink_sb,
tc_index,
DEVLINK_SB_POOL_TYPE_EGRESS,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
portid, seq,
flags);
if (err)
return err;
(*p_idx)++;
}
}
return 0;
}
static int devlink_nl_sb_tc_pool_bind_get_dump_one(struct sk_buff *msg,
struct devlink *devlink,
struct netlink_callback *cb,
int flags)
{
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
struct devlink_sb *devlink_sb;
int idx = 0;
int err = 0;
if (!devlink->ops->sb_tc_pool_bind_get)
return 0;
list_for_each_entry(devlink_sb, &devlink->sb_list, list) {
err = __sb_tc_pool_bind_get_dumpit(msg, state->idx, &idx,
devlink, devlink_sb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, flags);
if (err == -EOPNOTSUPP) {
err = 0;
} else if (err) {
state->idx = idx;
break;
}
}
return err;
}
int devlink_nl_sb_tc_pool_bind_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
return devlink_nl_dumpit(skb, cb,
devlink_nl_sb_tc_pool_bind_get_dump_one);
}
static int devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port,
unsigned int sb_index, u16 tc_index,
enum devlink_sb_pool_type pool_type,
u16 pool_index, u32 threshold,
struct netlink_ext_ack *extack)
{
const struct devlink_ops *ops = devlink_port->devlink->ops;
if (ops->sb_tc_pool_bind_set)
return ops->sb_tc_pool_bind_set(devlink_port, sb_index,
tc_index, pool_type,
pool_index, threshold, extack);
return -EOPNOTSUPP;
}
int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink_port *devlink_port = info->user_ptr[1];
struct devlink *devlink = info->user_ptr[0];
enum devlink_sb_pool_type pool_type;
struct devlink_sb *devlink_sb;
u16 tc_index;
u16 pool_index;
u32 threshold;
int err;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
err = devlink_sb_pool_type_get_from_info(info, &pool_type);
if (err)
return err;
err = devlink_sb_tc_index_get_from_info(devlink_sb, info,
pool_type, &tc_index);
if (err)
return err;
err = devlink_sb_pool_index_get_from_info(devlink_sb, info,
&pool_index);
if (err)
return err;
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SB_THRESHOLD))
return -EINVAL;
threshold = nla_get_u32(info->attrs[DEVLINK_ATTR_SB_THRESHOLD]);
return devlink_sb_tc_pool_bind_set(devlink_port, devlink_sb->index,
tc_index, pool_type,
pool_index, threshold, info->extack);
}
int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
const struct devlink_ops *ops = devlink->ops;
struct devlink_sb *devlink_sb;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
if (ops->sb_occ_snapshot)
return ops->sb_occ_snapshot(devlink, devlink_sb->index);
return -EOPNOTSUPP;
}
int devlink_nl_sb_occ_max_clear_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
const struct devlink_ops *ops = devlink->ops;
struct devlink_sb *devlink_sb;
devlink_sb = devlink_sb_get_from_info(devlink, info);
if (IS_ERR(devlink_sb))
return PTR_ERR(devlink_sb);
if (ops->sb_occ_max_clear)
return ops->sb_occ_max_clear(devlink, devlink_sb->index);
return -EOPNOTSUPP;
}
int devl_sb_register(struct devlink *devlink, unsigned int sb_index,
u32 size, u16 ingress_pools_count,
u16 egress_pools_count, u16 ingress_tc_count,
u16 egress_tc_count)
{
struct devlink_sb *devlink_sb;
lockdep_assert_held(&devlink->lock);
if (devlink_sb_index_exists(devlink, sb_index))
return -EEXIST;
devlink_sb = kzalloc_obj(*devlink_sb);
if (!devlink_sb)
return -ENOMEM;
devlink_sb->index = sb_index;
devlink_sb->size = size;
devlink_sb->ingress_pools_count = ingress_pools_count;
devlink_sb->egress_pools_count = egress_pools_count;
devlink_sb->ingress_tc_count = ingress_tc_count;
devlink_sb->egress_tc_count = egress_tc_count;
list_add_tail(&devlink_sb->list, &devlink->sb_list);
return 0;
}
EXPORT_SYMBOL_GPL(devl_sb_register);
int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
u32 size, u16 ingress_pools_count,
u16 egress_pools_count, u16 ingress_tc_count,
u16 egress_tc_count)
{
int err;
devl_lock(devlink);
err = devl_sb_register(devlink, sb_index, size, ingress_pools_count,
egress_pools_count, ingress_tc_count,
egress_tc_count);
devl_unlock(devlink);
return err;
}
EXPORT_SYMBOL_GPL(devlink_sb_register);
void devl_sb_unregister(struct devlink *devlink, unsigned int sb_index)
{
struct devlink_sb *devlink_sb;
lockdep_assert_held(&devlink->lock);
devlink_sb = devlink_sb_get_by_index(devlink, sb_index);
WARN_ON(!devlink_sb);
list_del(&devlink_sb->list);
kfree(devlink_sb);
}
EXPORT_SYMBOL_GPL(devl_sb_unregister);
void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index)
{
devl_lock(devlink);
devl_sb_unregister(devlink, sb_index);
devl_unlock(devlink);
}
EXPORT_SYMBOL_GPL(devlink_sb_unregister);
+161
View File
@@ -0,0 +1,161 @@
// 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);
File diff suppressed because it is too large Load Diff