xref: /openbmc/linux/drivers/infiniband/core/nldev.c (revision ac505253)
16c80b41aSLeon Romanovsky /*
26c80b41aSLeon Romanovsky  * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
36c80b41aSLeon Romanovsky  *
46c80b41aSLeon Romanovsky  * Redistribution and use in source and binary forms, with or without
56c80b41aSLeon Romanovsky  * modification, are permitted provided that the following conditions are met:
66c80b41aSLeon Romanovsky  *
76c80b41aSLeon Romanovsky  * 1. Redistributions of source code must retain the above copyright
86c80b41aSLeon Romanovsky  *    notice, this list of conditions and the following disclaimer.
96c80b41aSLeon Romanovsky  * 2. Redistributions in binary form must reproduce the above copyright
106c80b41aSLeon Romanovsky  *    notice, this list of conditions and the following disclaimer in the
116c80b41aSLeon Romanovsky  *    documentation and/or other materials provided with the distribution.
126c80b41aSLeon Romanovsky  * 3. Neither the names of the copyright holders nor the names of its
136c80b41aSLeon Romanovsky  *    contributors may be used to endorse or promote products derived from
146c80b41aSLeon Romanovsky  *    this software without specific prior written permission.
156c80b41aSLeon Romanovsky  *
166c80b41aSLeon Romanovsky  * Alternatively, this software may be distributed under the terms of the
176c80b41aSLeon Romanovsky  * GNU General Public License ("GPL") version 2 as published by the Free
186c80b41aSLeon Romanovsky  * Software Foundation.
196c80b41aSLeon Romanovsky  *
206c80b41aSLeon Romanovsky  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
216c80b41aSLeon Romanovsky  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
226c80b41aSLeon Romanovsky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
236c80b41aSLeon Romanovsky  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
246c80b41aSLeon Romanovsky  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
256c80b41aSLeon Romanovsky  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
266c80b41aSLeon Romanovsky  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
276c80b41aSLeon Romanovsky  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
286c80b41aSLeon Romanovsky  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
296c80b41aSLeon Romanovsky  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
306c80b41aSLeon Romanovsky  * POSSIBILITY OF SUCH DAMAGE.
316c80b41aSLeon Romanovsky  */
326c80b41aSLeon Romanovsky 
33b4c598a6SLeon Romanovsky #include <net/netlink.h>
346c80b41aSLeon Romanovsky #include <rdma/rdma_netlink.h>
356c80b41aSLeon Romanovsky 
366c80b41aSLeon Romanovsky #include "core_priv.h"
376c80b41aSLeon Romanovsky 
38b4c598a6SLeon Romanovsky static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
39b4c598a6SLeon Romanovsky 	[RDMA_NLDEV_ATTR_DEV_INDEX]     = { .type = NLA_U32 },
40b4c598a6SLeon Romanovsky 	[RDMA_NLDEV_ATTR_DEV_NAME]	= { .type = NLA_NUL_STRING,
41b4c598a6SLeon Romanovsky 					    .len = IB_DEVICE_NAME_MAX - 1},
42b4c598a6SLeon Romanovsky 	[RDMA_NLDEV_ATTR_PORT_INDEX]	= { .type = NLA_U32 },
43b4c598a6SLeon Romanovsky };
44b4c598a6SLeon Romanovsky 
45b4c598a6SLeon Romanovsky static int fill_dev_info(struct sk_buff *msg, struct ib_device *device)
46b4c598a6SLeon Romanovsky {
47b4c598a6SLeon Romanovsky 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
48b4c598a6SLeon Romanovsky 		return -EMSGSIZE;
49b4c598a6SLeon Romanovsky 	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, device->name))
50b4c598a6SLeon Romanovsky 		return -EMSGSIZE;
51b4c598a6SLeon Romanovsky 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, rdma_end_port(device)))
52b4c598a6SLeon Romanovsky 		return -EMSGSIZE;
53ac505253SLeon Romanovsky 
54ac505253SLeon Romanovsky 	BUILD_BUG_ON(sizeof(device->attrs.device_cap_flags) != sizeof(u64));
55ac505253SLeon Romanovsky 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
56ac505253SLeon Romanovsky 			      device->attrs.device_cap_flags, 0))
57ac505253SLeon Romanovsky 		return -EMSGSIZE;
58ac505253SLeon Romanovsky 
59b4c598a6SLeon Romanovsky 	return 0;
60b4c598a6SLeon Romanovsky }
61b4c598a6SLeon Romanovsky 
627d02f605SLeon Romanovsky static int fill_port_info(struct sk_buff *msg,
637d02f605SLeon Romanovsky 			  struct ib_device *device, u32 port)
647d02f605SLeon Romanovsky {
65ac505253SLeon Romanovsky 	struct ib_port_attr attr;
66ac505253SLeon Romanovsky 	int ret;
67ac505253SLeon Romanovsky 
687d02f605SLeon Romanovsky 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index))
697d02f605SLeon Romanovsky 		return -EMSGSIZE;
707d02f605SLeon Romanovsky 	if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, device->name))
717d02f605SLeon Romanovsky 		return -EMSGSIZE;
727d02f605SLeon Romanovsky 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port))
737d02f605SLeon Romanovsky 		return -EMSGSIZE;
74ac505253SLeon Romanovsky 
75ac505253SLeon Romanovsky 	ret = ib_query_port(device, port, &attr);
76ac505253SLeon Romanovsky 	if (ret)
77ac505253SLeon Romanovsky 		return ret;
78ac505253SLeon Romanovsky 
79ac505253SLeon Romanovsky 	BUILD_BUG_ON(sizeof(attr.port_cap_flags) > sizeof(u64));
80ac505253SLeon Romanovsky 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS,
81ac505253SLeon Romanovsky 			      (u64)attr.port_cap_flags, 0))
82ac505253SLeon Romanovsky 		return -EMSGSIZE;
83ac505253SLeon Romanovsky 
847d02f605SLeon Romanovsky 	return 0;
857d02f605SLeon Romanovsky }
867d02f605SLeon Romanovsky 
87e5c9469eSLeon Romanovsky static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
88e5c9469eSLeon Romanovsky 			  struct netlink_ext_ack *extack)
89e5c9469eSLeon Romanovsky {
90e5c9469eSLeon Romanovsky 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
91e5c9469eSLeon Romanovsky 	struct ib_device *device;
92e5c9469eSLeon Romanovsky 	struct sk_buff *msg;
93e5c9469eSLeon Romanovsky 	u32 index;
94e5c9469eSLeon Romanovsky 	int err;
95e5c9469eSLeon Romanovsky 
96e5c9469eSLeon Romanovsky 	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
97e5c9469eSLeon Romanovsky 			  nldev_policy, extack);
98e5c9469eSLeon Romanovsky 	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
99e5c9469eSLeon Romanovsky 		return -EINVAL;
100e5c9469eSLeon Romanovsky 
101e5c9469eSLeon Romanovsky 	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
102e5c9469eSLeon Romanovsky 
103e5c9469eSLeon Romanovsky 	device = __ib_device_get_by_index(index);
104e5c9469eSLeon Romanovsky 	if (!device)
105e5c9469eSLeon Romanovsky 		return -EINVAL;
106e5c9469eSLeon Romanovsky 
107e5c9469eSLeon Romanovsky 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
108e5c9469eSLeon Romanovsky 	if (!msg)
109e5c9469eSLeon Romanovsky 		return -ENOMEM;
110e5c9469eSLeon Romanovsky 
111e5c9469eSLeon Romanovsky 	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
112e5c9469eSLeon Romanovsky 			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
113e5c9469eSLeon Romanovsky 			0, 0);
114e5c9469eSLeon Romanovsky 
115e5c9469eSLeon Romanovsky 	err = fill_dev_info(msg, device);
116e5c9469eSLeon Romanovsky 	if (err) {
117e5c9469eSLeon Romanovsky 		nlmsg_free(msg);
118e5c9469eSLeon Romanovsky 		return err;
119e5c9469eSLeon Romanovsky 	}
120e5c9469eSLeon Romanovsky 
121e5c9469eSLeon Romanovsky 	nlmsg_end(msg, nlh);
122e5c9469eSLeon Romanovsky 
123e5c9469eSLeon Romanovsky 	return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
124e5c9469eSLeon Romanovsky }
125e5c9469eSLeon Romanovsky 
126b4c598a6SLeon Romanovsky static int _nldev_get_dumpit(struct ib_device *device,
127b4c598a6SLeon Romanovsky 			     struct sk_buff *skb,
128b4c598a6SLeon Romanovsky 			     struct netlink_callback *cb,
129b4c598a6SLeon Romanovsky 			     unsigned int idx)
130b4c598a6SLeon Romanovsky {
131b4c598a6SLeon Romanovsky 	int start = cb->args[0];
132b4c598a6SLeon Romanovsky 	struct nlmsghdr *nlh;
133b4c598a6SLeon Romanovsky 
134b4c598a6SLeon Romanovsky 	if (idx < start)
135b4c598a6SLeon Romanovsky 		return 0;
136b4c598a6SLeon Romanovsky 
137b4c598a6SLeon Romanovsky 	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
138b4c598a6SLeon Romanovsky 			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
139b4c598a6SLeon Romanovsky 			0, NLM_F_MULTI);
140b4c598a6SLeon Romanovsky 
141b4c598a6SLeon Romanovsky 	if (fill_dev_info(skb, device)) {
142b4c598a6SLeon Romanovsky 		nlmsg_cancel(skb, nlh);
143b4c598a6SLeon Romanovsky 		goto out;
144b4c598a6SLeon Romanovsky 	}
145b4c598a6SLeon Romanovsky 
146b4c598a6SLeon Romanovsky 	nlmsg_end(skb, nlh);
147b4c598a6SLeon Romanovsky 
148b4c598a6SLeon Romanovsky 	idx++;
149b4c598a6SLeon Romanovsky 
150b4c598a6SLeon Romanovsky out:	cb->args[0] = idx;
151b4c598a6SLeon Romanovsky 	return skb->len;
152b4c598a6SLeon Romanovsky }
153b4c598a6SLeon Romanovsky 
154b4c598a6SLeon Romanovsky static int nldev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
155b4c598a6SLeon Romanovsky {
156b4c598a6SLeon Romanovsky 	/*
157b4c598a6SLeon Romanovsky 	 * There is no need to take lock, because
158b4c598a6SLeon Romanovsky 	 * we are relying on ib_core's lists_rwsem
159b4c598a6SLeon Romanovsky 	 */
160b4c598a6SLeon Romanovsky 	return ib_enum_all_devs(_nldev_get_dumpit, skb, cb);
161b4c598a6SLeon Romanovsky }
162b4c598a6SLeon Romanovsky 
163c3f66f7bSLeon Romanovsky static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
164c3f66f7bSLeon Romanovsky 			       struct netlink_ext_ack *extack)
165c3f66f7bSLeon Romanovsky {
166c3f66f7bSLeon Romanovsky 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
167c3f66f7bSLeon Romanovsky 	struct ib_device *device;
168c3f66f7bSLeon Romanovsky 	struct sk_buff *msg;
169c3f66f7bSLeon Romanovsky 	u32 index;
170c3f66f7bSLeon Romanovsky 	u32 port;
171c3f66f7bSLeon Romanovsky 	int err;
172c3f66f7bSLeon Romanovsky 
173c3f66f7bSLeon Romanovsky 	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
174c3f66f7bSLeon Romanovsky 			  nldev_policy, extack);
175c3f66f7bSLeon Romanovsky 	if (err || !tb[RDMA_NLDEV_ATTR_PORT_INDEX])
176c3f66f7bSLeon Romanovsky 		return -EINVAL;
177c3f66f7bSLeon Romanovsky 
178c3f66f7bSLeon Romanovsky 	index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
179c3f66f7bSLeon Romanovsky 	device = __ib_device_get_by_index(index);
180c3f66f7bSLeon Romanovsky 	if (!device)
181c3f66f7bSLeon Romanovsky 		return -EINVAL;
182c3f66f7bSLeon Romanovsky 
183c3f66f7bSLeon Romanovsky 	port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
184c3f66f7bSLeon Romanovsky 	if (!rdma_is_port_valid(device, port))
185c3f66f7bSLeon Romanovsky 		return -EINVAL;
186c3f66f7bSLeon Romanovsky 
187c3f66f7bSLeon Romanovsky 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
188c3f66f7bSLeon Romanovsky 	if (!msg)
189c3f66f7bSLeon Romanovsky 		return -ENOMEM;
190c3f66f7bSLeon Romanovsky 
191c3f66f7bSLeon Romanovsky 	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
192c3f66f7bSLeon Romanovsky 			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET),
193c3f66f7bSLeon Romanovsky 			0, 0);
194c3f66f7bSLeon Romanovsky 
195c3f66f7bSLeon Romanovsky 	err = fill_port_info(msg, device, port);
196c3f66f7bSLeon Romanovsky 	if (err) {
197c3f66f7bSLeon Romanovsky 		nlmsg_free(msg);
198c3f66f7bSLeon Romanovsky 		return err;
199c3f66f7bSLeon Romanovsky 	}
200c3f66f7bSLeon Romanovsky 
201c3f66f7bSLeon Romanovsky 	nlmsg_end(msg, nlh);
202c3f66f7bSLeon Romanovsky 
203c3f66f7bSLeon Romanovsky 	return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);
204c3f66f7bSLeon Romanovsky }
205c3f66f7bSLeon Romanovsky 
2067d02f605SLeon Romanovsky static int nldev_port_get_dumpit(struct sk_buff *skb,
2077d02f605SLeon Romanovsky 				 struct netlink_callback *cb)
2087d02f605SLeon Romanovsky {
2097d02f605SLeon Romanovsky 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
2107d02f605SLeon Romanovsky 	struct ib_device *device;
2117d02f605SLeon Romanovsky 	int start = cb->args[0];
2127d02f605SLeon Romanovsky 	struct nlmsghdr *nlh;
2137d02f605SLeon Romanovsky 	u32 idx = 0;
2147d02f605SLeon Romanovsky 	u32 ifindex;
2157d02f605SLeon Romanovsky 	int err;
2167d02f605SLeon Romanovsky 	u32 p;
2177d02f605SLeon Romanovsky 
2187d02f605SLeon Romanovsky 	err = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
2197d02f605SLeon Romanovsky 			  nldev_policy, NULL);
2207d02f605SLeon Romanovsky 	if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX])
2217d02f605SLeon Romanovsky 		return -EINVAL;
2227d02f605SLeon Romanovsky 
2237d02f605SLeon Romanovsky 	ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
2247d02f605SLeon Romanovsky 	device = __ib_device_get_by_index(ifindex);
2257d02f605SLeon Romanovsky 	if (!device)
2267d02f605SLeon Romanovsky 		return -EINVAL;
2277d02f605SLeon Romanovsky 
2287d02f605SLeon Romanovsky 	for (p = rdma_start_port(device); p <= rdma_end_port(device); ++p) {
2297d02f605SLeon Romanovsky 		/*
2307d02f605SLeon Romanovsky 		 * The dumpit function returns all information from specific
2317d02f605SLeon Romanovsky 		 * index. This specific index is taken from the netlink
2327d02f605SLeon Romanovsky 		 * messages request sent by user and it is available
2337d02f605SLeon Romanovsky 		 * in cb->args[0].
2347d02f605SLeon Romanovsky 		 *
2357d02f605SLeon Romanovsky 		 * Usually, the user doesn't fill this field and it causes
2367d02f605SLeon Romanovsky 		 * to return everything.
2377d02f605SLeon Romanovsky 		 *
2387d02f605SLeon Romanovsky 		 */
2397d02f605SLeon Romanovsky 		if (idx < start) {
2407d02f605SLeon Romanovsky 			idx++;
2417d02f605SLeon Romanovsky 			continue;
2427d02f605SLeon Romanovsky 		}
2437d02f605SLeon Romanovsky 
2447d02f605SLeon Romanovsky 		nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
2457d02f605SLeon Romanovsky 				cb->nlh->nlmsg_seq,
2467d02f605SLeon Romanovsky 				RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
2477d02f605SLeon Romanovsky 						 RDMA_NLDEV_CMD_PORT_GET),
2487d02f605SLeon Romanovsky 				0, NLM_F_MULTI);
2497d02f605SLeon Romanovsky 
2507d02f605SLeon Romanovsky 		if (fill_port_info(skb, device, p)) {
2517d02f605SLeon Romanovsky 			nlmsg_cancel(skb, nlh);
2527d02f605SLeon Romanovsky 			goto out;
2537d02f605SLeon Romanovsky 		}
2547d02f605SLeon Romanovsky 		idx++;
2557d02f605SLeon Romanovsky 		nlmsg_end(skb, nlh);
2567d02f605SLeon Romanovsky 	}
2577d02f605SLeon Romanovsky 
2587d02f605SLeon Romanovsky out:	cb->args[0] = idx;
2597d02f605SLeon Romanovsky 	return skb->len;
2607d02f605SLeon Romanovsky }
2617d02f605SLeon Romanovsky 
262b4c598a6SLeon Romanovsky static const struct rdma_nl_cbs nldev_cb_table[] = {
263b4c598a6SLeon Romanovsky 	[RDMA_NLDEV_CMD_GET] = {
264e5c9469eSLeon Romanovsky 		.doit = nldev_get_doit,
265b4c598a6SLeon Romanovsky 		.dump = nldev_get_dumpit,
266b4c598a6SLeon Romanovsky 	},
2677d02f605SLeon Romanovsky 	[RDMA_NLDEV_CMD_PORT_GET] = {
268c3f66f7bSLeon Romanovsky 		.doit = nldev_port_get_doit,
2697d02f605SLeon Romanovsky 		.dump = nldev_port_get_dumpit,
2707d02f605SLeon Romanovsky 	},
271b4c598a6SLeon Romanovsky };
272b4c598a6SLeon Romanovsky 
2736c80b41aSLeon Romanovsky void __init nldev_init(void)
2746c80b41aSLeon Romanovsky {
275b4c598a6SLeon Romanovsky 	rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table);
2766c80b41aSLeon Romanovsky }
2776c80b41aSLeon Romanovsky 
2786c80b41aSLeon Romanovsky void __exit nldev_exit(void)
2796c80b41aSLeon Romanovsky {
2806c80b41aSLeon Romanovsky 	rdma_nl_unregister(RDMA_NL_NLDEV);
2816c80b41aSLeon Romanovsky }
282