1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 */
6
7#include <net/genetlink.h>
8#include <net/sock.h>
9
10#include "devl_internal.h"
11
12static const struct genl_multicast_group devlink_nl_mcgrps[] = {
13 [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
14};
15
16int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net,
17 struct devlink *devlink, int attrtype)
18{
19 struct nlattr *nested_attr;
20 struct net *devl_net;
21
22 nested_attr = nla_nest_start(skb: msg, attrtype);
23 if (!nested_attr)
24 return -EMSGSIZE;
25 if (devlink_nl_put_handle(msg, devlink))
26 goto nla_put_failure;
27
28 rcu_read_lock();
29 devl_net = read_pnet_rcu(pnet: &devlink->_net);
30 if (!net_eq(net1: net, net2: devl_net)) {
31 int id = peernet2id_alloc(net, peer: devl_net, GFP_ATOMIC);
32
33 rcu_read_unlock();
34 if (nla_put_s32(skb: msg, attrtype: DEVLINK_ATTR_NETNS_ID, value: id))
35 return -EMSGSIZE;
36 } else {
37 rcu_read_unlock();
38 }
39
40 nla_nest_end(skb: msg, start: nested_attr);
41 return 0;
42
43nla_put_failure:
44 nla_nest_cancel(skb: msg, start: nested_attr);
45 return -EMSGSIZE;
46}
47
48int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info)
49{
50 int err;
51
52 if (*msg) {
53 err = genlmsg_reply(skb: *msg, info);
54 if (err)
55 return err;
56 }
57 *msg = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
58 if (!*msg)
59 return -ENOMEM;
60 return 0;
61}
62
63struct devlink *
64devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs)
65{
66 struct devlink *devlink;
67 unsigned long index;
68 char *busname;
69 char *devname;
70
71 if (!attrs[DEVLINK_ATTR_BUS_NAME] || !attrs[DEVLINK_ATTR_DEV_NAME])
72 return ERR_PTR(error: -EINVAL);
73
74 busname = nla_data(nla: attrs[DEVLINK_ATTR_BUS_NAME]);
75 devname = nla_data(nla: attrs[DEVLINK_ATTR_DEV_NAME]);
76
77 devlinks_xa_for_each_registered_get(net, index, devlink) {
78 devl_lock(devlink);
79 if (devl_is_registered(devlink) &&
80 strcmp(devlink->dev->bus->name, busname) == 0 &&
81 strcmp(dev_name(dev: devlink->dev), devname) == 0)
82 return devlink;
83 devl_unlock(devlink);
84 devlink_put(devlink);
85 }
86
87 return ERR_PTR(error: -ENODEV);
88}
89
90static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
91 u8 flags)
92{
93 struct devlink_port *devlink_port;
94 struct devlink *devlink;
95 int err;
96
97 devlink = devlink_get_from_attrs_lock(net: genl_info_net(info), attrs: info->attrs);
98 if (IS_ERR(ptr: devlink))
99 return PTR_ERR(ptr: devlink);
100
101 info->user_ptr[0] = devlink;
102 if (flags & DEVLINK_NL_FLAG_NEED_PORT) {
103 devlink_port = devlink_port_get_from_info(devlink, info);
104 if (IS_ERR(ptr: devlink_port)) {
105 err = PTR_ERR(ptr: devlink_port);
106 goto unlock;
107 }
108 info->user_ptr[1] = devlink_port;
109 } else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
110 devlink_port = devlink_port_get_from_info(devlink, info);
111 if (!IS_ERR(ptr: devlink_port))
112 info->user_ptr[1] = devlink_port;
113 }
114 return 0;
115
116unlock:
117 devl_unlock(devlink);
118 devlink_put(devlink);
119 return err;
120}
121
122int devlink_nl_pre_doit(const struct genl_split_ops *ops,
123 struct sk_buff *skb, struct genl_info *info)
124{
125 return __devlink_nl_pre_doit(skb, info, flags: 0);
126}
127
128int devlink_nl_pre_doit_port(const struct genl_split_ops *ops,
129 struct sk_buff *skb, struct genl_info *info)
130{
131 return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_PORT);
132}
133
134int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
135 struct sk_buff *skb,
136 struct genl_info *info)
137{
138 return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT);
139}
140
141void devlink_nl_post_doit(const struct genl_split_ops *ops,
142 struct sk_buff *skb, struct genl_info *info)
143{
144 struct devlink *devlink;
145
146 devlink = info->user_ptr[0];
147 devl_unlock(devlink);
148 devlink_put(devlink);
149}
150
151static int devlink_nl_inst_single_dumpit(struct sk_buff *msg,
152 struct netlink_callback *cb, int flags,
153 devlink_nl_dump_one_func_t *dump_one,
154 struct nlattr **attrs)
155{
156 struct devlink *devlink;
157 int err;
158
159 devlink = devlink_get_from_attrs_lock(net: sock_net(sk: msg->sk), attrs);
160 if (IS_ERR(ptr: devlink))
161 return PTR_ERR(ptr: devlink);
162 err = dump_one(msg, devlink, cb, flags | NLM_F_DUMP_FILTERED);
163
164 devl_unlock(devlink);
165 devlink_put(devlink);
166
167 if (err != -EMSGSIZE)
168 return err;
169 return msg->len;
170}
171
172static int devlink_nl_inst_iter_dumpit(struct sk_buff *msg,
173 struct netlink_callback *cb, int flags,
174 devlink_nl_dump_one_func_t *dump_one)
175{
176 struct devlink_nl_dump_state *state = devlink_dump_state(cb);
177 struct devlink *devlink;
178 int err = 0;
179
180 while ((devlink = devlinks_xa_find_get(net: sock_net(sk: msg->sk),
181 indexp: &state->instance))) {
182 devl_lock(devlink);
183
184 if (devl_is_registered(devlink))
185 err = dump_one(msg, devlink, cb, flags);
186 else
187 err = 0;
188
189 devl_unlock(devlink);
190 devlink_put(devlink);
191
192 if (err)
193 break;
194
195 state->instance++;
196
197 /* restart sub-object walk for the next instance */
198 state->idx = 0;
199 }
200
201 if (err != -EMSGSIZE)
202 return err;
203 return msg->len;
204}
205
206int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
207 devlink_nl_dump_one_func_t *dump_one)
208{
209 const struct genl_info *info = genl_info_dump(cb);
210 struct nlattr **attrs = info->attrs;
211 int flags = NLM_F_MULTI;
212
213 if (attrs &&
214 (attrs[DEVLINK_ATTR_BUS_NAME] || attrs[DEVLINK_ATTR_DEV_NAME]))
215 return devlink_nl_inst_single_dumpit(msg, cb, flags, dump_one,
216 attrs);
217 else
218 return devlink_nl_inst_iter_dumpit(msg, cb, flags, dump_one);
219}
220
221struct genl_family devlink_nl_family __ro_after_init = {
222 .name = DEVLINK_GENL_NAME,
223 .version = DEVLINK_GENL_VERSION,
224 .netnsok = true,
225 .parallel_ops = true,
226 .module = THIS_MODULE,
227 .split_ops = devlink_nl_ops,
228 .n_split_ops = ARRAY_SIZE(devlink_nl_ops),
229 .resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1,
230 .mcgrps = devlink_nl_mcgrps,
231 .n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps),
232};
233

source code of linux/net/devlink/netlink.c