| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // 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_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->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->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_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]);
- devlinks_xa_for_each_registered_get(net, index, devlink) {
- if (strcmp(devlink->dev->bus->name, busname) == 0 &&
- strcmp(dev_name(devlink->dev), devname) == 0) {
- 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;
- }
- 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]))
- 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,
- };
|