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(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 | |
43 | nla_put_failure: |
44 | nla_nest_cancel(skb: msg, start: 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(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 | |
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(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 | |
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(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 | |
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, flags: 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(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 | |
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(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 | |
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 | |