xref: /linux/net/devlink/netlink.c (revision 3d0fe49454652117522f60bfbefb978ba0e5300b)
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 
12 static const struct genl_multicast_group devlink_nl_mcgrps[] = {
13 	[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
14 };
15 
16 int 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(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(&devlink->_net);
30 	if (!net_eq(net, devl_net)) {
31 		int id = peernet2id_alloc(net, devl_net, GFP_ATOMIC);
32 
33 		rcu_read_unlock();
34 		if (nla_put_s32(msg, DEVLINK_ATTR_NETNS_ID, id))
35 			return -EMSGSIZE;
36 	} else {
37 		rcu_read_unlock();
38 	}
39 
40 	nla_nest_end(msg, nested_attr);
41 	return 0;
42 
43 nla_put_failure:
44 	nla_nest_cancel(msg, nested_attr);
45 	return -EMSGSIZE;
46 }
47 
48 int 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(*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 
63 struct devlink *
64 devlink_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(-EINVAL);
73 
74 	busname = nla_data(attrs[DEVLINK_ATTR_BUS_NAME]);
75 	devname = nla_data(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(devlink->dev), devname) == 0)
82 			return devlink;
83 		devl_unlock(devlink);
84 		devlink_put(devlink);
85 	}
86 
87 	return ERR_PTR(-ENODEV);
88 }
89 
90 static 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(genl_info_net(info), info->attrs);
98 	if (IS_ERR(devlink))
99 		return PTR_ERR(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(devlink_port)) {
105 			err = PTR_ERR(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(devlink_port))
112 			info->user_ptr[1] = devlink_port;
113 	}
114 	return 0;
115 
116 unlock:
117 	devl_unlock(devlink);
118 	devlink_put(devlink);
119 	return err;
120 }
121 
122 int 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, 0);
126 }
127 
128 int 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 
134 int 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 
141 void 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 
151 static 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(sock_net(msg->sk), attrs);
160 	if (IS_ERR(devlink))
161 		return PTR_ERR(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 
172 static 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(sock_net(msg->sk),
181 					       &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 
206 int 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 
221 struct 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