xref: /linux/drivers/infiniband/core/nldev.c (revision 140eb5227767c6754742020a16d2691222b9c19b)
1 /*
2  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. Neither the names of the copyright holders nor the names of its
13  *    contributors may be used to endorse or promote products derived from
14  *    this software without specific prior written permission.
15  *
16  * Alternatively, this software may be distributed under the terms of the
17  * GNU General Public License ("GPL") version 2 as published by the Free
18  * Software Foundation.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <linux/module.h>
34 #include <net/netlink.h>
35 #include <rdma/rdma_netlink.h>
36 
37 #include "core_priv.h"
38 
39 static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
40 	[RDMA_NLDEV_ATTR_DEV_INDEX]     = { .type = NLA_U32 },
41 	[RDMA_NLDEV_ATTR_DEV_NAME]	= { .type = NLA_NUL_STRING,
42 					    .len = IB_DEVICE_NAME_MAX - 1},
43 	[RDMA_NLDEV_ATTR_PORT_INDEX]	= { .type = NLA_U32 },
44 	[RDMA_NLDEV_ATTR_FW_VERSION]	= { .type = NLA_NUL_STRING,
45 					    .len = IB_FW_VERSION_NAME_MAX - 1},
46 	[RDMA_NLDEV_ATTR_NODE_GUID]	= { .type = NLA_U64 },
47 	[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID] = { .type = NLA_U64 },
48 	[RDMA_NLDEV_ATTR_SUBNET_PREFIX]	= { .type = NLA_U64 },
49 	[RDMA_NLDEV_ATTR_LID]		= { .type = NLA_U32 },
50 	[RDMA_NLDEV_ATTR_SM_LID]	= { .type = NLA_U32 },
51 	[RDMA_NLDEV_ATTR_LMC]		= { .type = NLA_U8 },
52 	[RDMA_NLDEV_ATTR_PORT_STATE]	= { .type = NLA_U8 },
53 	[RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = { .type = NLA_U8 },
54 	[RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = { .type = NLA_U8 },
55 };
56 
57 static int fill_dev_info(struct sk_buff *msg, struct ib_device *device)
58 {
59 	char fw[IB_FW_VERSION_NAME_MAX];
60 
61 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
62 		return -EMSGSIZE;
63 	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, device->name))
64 		return -EMSGSIZE;
65 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, rdma_end_port(device)))
66 		return -EMSGSIZE;
67 
68 	BUILD_BUG_ON(sizeof(device->attrs.device_cap_flags) != sizeof(u64));
69 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
70 			      device->attrs.device_cap_flags, 0))
71 		return -EMSGSIZE;
72 
73 	ib_get_device_fw_str(device, fw);
74 	/* Device without FW has strlen(fw) */
75 	if (strlen(fw) && nla_put_string(msg, RDMA_NLDEV_ATTR_FW_VERSION, fw))
76 		return -EMSGSIZE;
77 
78 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_NODE_GUID,
79 			      be64_to_cpu(device->node_guid), 0))
80 		return -EMSGSIZE;
81 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SYS_IMAGE_GUID,
82 			      be64_to_cpu(device->attrs.sys_image_guid), 0))
83 		return -EMSGSIZE;
84 	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_DEV_NODE_TYPE, device->node_type))
85 		return -EMSGSIZE;
86 	return 0;
87 }
88 
89 static int fill_port_info(struct sk_buff *msg,
90 			  struct ib_device *device, u32 port)
91 {
92 	struct ib_port_attr attr;
93 	int ret;
94 
95 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
96 		return -EMSGSIZE;
97 	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, device->name))
98 		return -EMSGSIZE;
99 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port))
100 		return -EMSGSIZE;
101 
102 	ret = ib_query_port(device, port, &attr);
103 	if (ret)
104 		return ret;
105 
106 	BUILD_BUG_ON(sizeof(attr.port_cap_flags) > sizeof(u64));
107 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
108 			      (u64)attr.port_cap_flags, 0))
109 		return -EMSGSIZE;
110 	if (rdma_protocol_ib(device, port) &&
111 	    nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SUBNET_PREFIX,
112 			      attr.subnet_prefix, 0))
113 		return -EMSGSIZE;
114 	if (rdma_protocol_ib(device, port)) {
115 		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_LID, attr.lid))
116 			return -EMSGSIZE;
117 		if (nla_put_u32(msg, RDMA_NLDEV_ATTR_SM_LID, attr.sm_lid))
118 			return -EMSGSIZE;
119 		if (nla_put_u8(msg, RDMA_NLDEV_ATTR_LMC, attr.lmc))
120 			return -EMSGSIZE;
121 	}
122 	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_STATE, attr.state))
123 		return -EMSGSIZE;
124 	if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state))
125 		return -EMSGSIZE;
126 	return 0;
127 }
128 
129 static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
130 			  struct netlink_ext_ack *extack)
131 {
132 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
133 	struct ib_device *device;
134 	struct sk_buff *msg;
135 	u32 index;
136 	int err;
137 
138 	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
139 			  nldev_policy, extack);
140 	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
141 		return -EINVAL;
142 
143 	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
144 
145 	device = ib_device_get_by_index(index);
146 	if (!device)
147 		return -EINVAL;
148 
149 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
150 	if (!msg) {
151 		err = -ENOMEM;
152 		goto err;
153 	}
154 
155 	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
156 			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
157 			0, 0);
158 
159 	err = fill_dev_info(msg, device);
160 	if (err)
161 		goto err_free;
162 
163 	nlmsg_end(msg, nlh);
164 
165 	put_device(&device->dev);
166 	return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
167 
168 err_free:
169 	nlmsg_free(msg);
170 err:
171 	put_device(&device->dev);
172 	return err;
173 }
174 
175 static int _nldev_get_dumpit(struct ib_device *device,
176 			     struct sk_buff *skb,
177 			     struct netlink_callback *cb,
178 			     unsigned int idx)
179 {
180 	int start = cb->args[0];
181 	struct nlmsghdr *nlh;
182 
183 	if (idx < start)
184 		return 0;
185 
186 	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
187 			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
188 			0, NLM_F_MULTI);
189 
190 	if (fill_dev_info(skb, device)) {
191 		nlmsg_cancel(skb, nlh);
192 		goto out;
193 	}
194 
195 	nlmsg_end(skb, nlh);
196 
197 	idx++;
198 
199 out:	cb->args[0] = idx;
200 	return skb->len;
201 }
202 
203 static int nldev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
204 {
205 	/*
206 	 * There is no need to take lock, because
207 	 * we are relying on ib_core's lists_rwsem
208 	 */
209 	return ib_enum_all_devs(_nldev_get_dumpit, skb, cb);
210 }
211 
212 static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
213 			       struct netlink_ext_ack *extack)
214 {
215 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
216 	struct ib_device *device;
217 	struct sk_buff *msg;
218 	u32 index;
219 	u32 port;
220 	int err;
221 
222 	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
223 			  nldev_policy, extack);
224 	if (err ||
225 	    !tb[RDMA_NLDEV_ATTR_DEV_INDEX] ||
226 	    !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
227 		return -EINVAL;
228 
229 	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
230 	device = ib_device_get_by_index(index);
231 	if (!device)
232 		return -EINVAL;
233 
234 	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
235 	if (!rdma_is_port_valid(device, port)) {
236 		err = -EINVAL;
237 		goto err;
238 	}
239 
240 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
241 	if (!msg) {
242 		err = -ENOMEM;
243 		goto err;
244 	}
245 
246 	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
247 			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
248 			0, 0);
249 
250 	err = fill_port_info(msg, device, port);
251 	if (err)
252 		goto err_free;
253 
254 	nlmsg_end(msg, nlh);
255 	put_device(&device->dev);
256 
257 	return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
258 
259 err_free:
260 	nlmsg_free(msg);
261 err:
262 	put_device(&device->dev);
263 	return err;
264 }
265 
266 static int nldev_port_get_dumpit(struct sk_buff *skb,
267 				 struct netlink_callback *cb)
268 {
269 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
270 	struct ib_device *device;
271 	int start = cb->args[0];
272 	struct nlmsghdr *nlh;
273 	u32 idx = 0;
274 	u32 ifindex;
275 	int err;
276 	u32 p;
277 
278 	err = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
279 			  nldev_policy, NULL);
280 	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
281 		return -EINVAL;
282 
283 	ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
284 	device = ib_device_get_by_index(ifindex);
285 	if (!device)
286 		return -EINVAL;
287 
288 	for (p = rdma_start_port(device); p <= rdma_end_port(device); ++p) {
289 		/*
290 		 * The dumpit function returns all information from specific
291 		 * index. This specific index is taken from the netlink
292 		 * messages request sent by user and it is available
293 		 * in cb->args[0].
294 		 *
295 		 * Usually, the user doesn't fill this field and it causes
296 		 * to return everything.
297 		 *
298 		 */
299 		if (idx < start) {
300 			idx++;
301 			continue;
302 		}
303 
304 		nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
305 				cb->nlh->nlmsg_seq,
306 				RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
307 						 RDMA_NLDEV_CMD_PORT_GET),
308 				0, NLM_F_MULTI);
309 
310 		if (fill_port_info(skb, device, p)) {
311 			nlmsg_cancel(skb, nlh);
312 			goto out;
313 		}
314 		idx++;
315 		nlmsg_end(skb, nlh);
316 	}
317 
318 out:
319 	put_device(&device->dev);
320 	cb->args[0] = idx;
321 	return skb->len;
322 }
323 
324 static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
325 	[RDMA_NLDEV_CMD_GET] = {
326 		.doit = nldev_get_doit,
327 		.dump = nldev_get_dumpit,
328 	},
329 	[RDMA_NLDEV_CMD_PORT_GET] = {
330 		.doit = nldev_port_get_doit,
331 		.dump = nldev_port_get_dumpit,
332 	},
333 };
334 
335 void __init nldev_init(void)
336 {
337 	rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table);
338 }
339 
340 void __exit nldev_exit(void)
341 {
342 	rdma_nl_unregister(RDMA_NL_NLDEV);
343 }
344 
345 MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_NLDEV, 5);
346