]>
Commit | Line | Data |
---|---|---|
6c80b41a LR |
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 | ||
e3bf14bd | 33 | #include <linux/module.h> |
bf3c5a93 LR |
34 | #include <linux/pid.h> |
35 | #include <linux/pid_namespace.h> | |
3856ec4b | 36 | #include <linux/mutex.h> |
b4c598a6 | 37 | #include <net/netlink.h> |
00313983 | 38 | #include <rdma/rdma_cm.h> |
6c80b41a LR |
39 | #include <rdma/rdma_netlink.h> |
40 | ||
41 | #include "core_priv.h" | |
00313983 | 42 | #include "cma_priv.h" |
41eda65c | 43 | #include "restrack.h" |
6c80b41a | 44 | |
b4c598a6 LR |
45 | static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { |
46 | [RDMA_NLDEV_ATTR_DEV_INDEX] = { .type = NLA_U32 }, | |
47 | [RDMA_NLDEV_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, | |
48 | .len = IB_DEVICE_NAME_MAX - 1}, | |
49 | [RDMA_NLDEV_ATTR_PORT_INDEX] = { .type = NLA_U32 }, | |
8621a7e3 LR |
50 | [RDMA_NLDEV_ATTR_FW_VERSION] = { .type = NLA_NUL_STRING, |
51 | .len = IB_FW_VERSION_NAME_MAX - 1}, | |
1aaff896 LR |
52 | [RDMA_NLDEV_ATTR_NODE_GUID] = { .type = NLA_U64 }, |
53 | [RDMA_NLDEV_ATTR_SYS_IMAGE_GUID] = { .type = NLA_U64 }, | |
12026fbb | 54 | [RDMA_NLDEV_ATTR_SUBNET_PREFIX] = { .type = NLA_U64 }, |
80a06dd3 LR |
55 | [RDMA_NLDEV_ATTR_LID] = { .type = NLA_U32 }, |
56 | [RDMA_NLDEV_ATTR_SM_LID] = { .type = NLA_U32 }, | |
34840fea | 57 | [RDMA_NLDEV_ATTR_LMC] = { .type = NLA_U8 }, |
5654e49d LR |
58 | [RDMA_NLDEV_ATTR_PORT_STATE] = { .type = NLA_U8 }, |
59 | [RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = { .type = NLA_U8 }, | |
1bb77b8c | 60 | [RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = { .type = NLA_U8 }, |
bf3c5a93 LR |
61 | [RDMA_NLDEV_ATTR_RES_SUMMARY] = { .type = NLA_NESTED }, |
62 | [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY] = { .type = NLA_NESTED }, | |
63 | [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME] = { .type = NLA_NUL_STRING, | |
64 | .len = 16 }, | |
65 | [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR] = { .type = NLA_U64 }, | |
b5fa635a LR |
66 | [RDMA_NLDEV_ATTR_RES_QP] = { .type = NLA_NESTED }, |
67 | [RDMA_NLDEV_ATTR_RES_QP_ENTRY] = { .type = NLA_NESTED }, | |
68 | [RDMA_NLDEV_ATTR_RES_LQPN] = { .type = NLA_U32 }, | |
69 | [RDMA_NLDEV_ATTR_RES_RQPN] = { .type = NLA_U32 }, | |
70 | [RDMA_NLDEV_ATTR_RES_RQ_PSN] = { .type = NLA_U32 }, | |
71 | [RDMA_NLDEV_ATTR_RES_SQ_PSN] = { .type = NLA_U32 }, | |
72 | [RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE] = { .type = NLA_U8 }, | |
73 | [RDMA_NLDEV_ATTR_RES_TYPE] = { .type = NLA_U8 }, | |
74 | [RDMA_NLDEV_ATTR_RES_STATE] = { .type = NLA_U8 }, | |
75 | [RDMA_NLDEV_ATTR_RES_PID] = { .type = NLA_U32 }, | |
76 | [RDMA_NLDEV_ATTR_RES_KERN_NAME] = { .type = NLA_NUL_STRING, | |
77 | .len = TASK_COMM_LEN }, | |
00313983 SW |
78 | [RDMA_NLDEV_ATTR_RES_CM_ID] = { .type = NLA_NESTED }, |
79 | [RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY] = { .type = NLA_NESTED }, | |
80 | [RDMA_NLDEV_ATTR_RES_PS] = { .type = NLA_U32 }, | |
81 | [RDMA_NLDEV_ATTR_RES_SRC_ADDR] = { | |
82 | .len = sizeof(struct __kernel_sockaddr_storage) }, | |
83 | [RDMA_NLDEV_ATTR_RES_DST_ADDR] = { | |
84 | .len = sizeof(struct __kernel_sockaddr_storage) }, | |
a34fc089 SW |
85 | [RDMA_NLDEV_ATTR_RES_CQ] = { .type = NLA_NESTED }, |
86 | [RDMA_NLDEV_ATTR_RES_CQ_ENTRY] = { .type = NLA_NESTED }, | |
87 | [RDMA_NLDEV_ATTR_RES_CQE] = { .type = NLA_U32 }, | |
88 | [RDMA_NLDEV_ATTR_RES_USECNT] = { .type = NLA_U64 }, | |
89 | [RDMA_NLDEV_ATTR_RES_POLL_CTX] = { .type = NLA_U8 }, | |
fccec5b8 SW |
90 | [RDMA_NLDEV_ATTR_RES_MR] = { .type = NLA_NESTED }, |
91 | [RDMA_NLDEV_ATTR_RES_MR_ENTRY] = { .type = NLA_NESTED }, | |
92 | [RDMA_NLDEV_ATTR_RES_RKEY] = { .type = NLA_U32 }, | |
93 | [RDMA_NLDEV_ATTR_RES_LKEY] = { .type = NLA_U32 }, | |
94 | [RDMA_NLDEV_ATTR_RES_IOVA] = { .type = NLA_U64 }, | |
95 | [RDMA_NLDEV_ATTR_RES_MRLEN] = { .type = NLA_U64 }, | |
29cf1351 SW |
96 | [RDMA_NLDEV_ATTR_RES_PD] = { .type = NLA_NESTED }, |
97 | [RDMA_NLDEV_ATTR_RES_PD_ENTRY] = { .type = NLA_NESTED }, | |
98 | [RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY] = { .type = NLA_U32 }, | |
99 | [RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY] = { .type = NLA_U32 }, | |
5b2cc79d LR |
100 | [RDMA_NLDEV_ATTR_NDEV_INDEX] = { .type = NLA_U32 }, |
101 | [RDMA_NLDEV_ATTR_NDEV_NAME] = { .type = NLA_NUL_STRING, | |
102 | .len = IFNAMSIZ }, | |
da5c8507 SW |
103 | [RDMA_NLDEV_ATTR_DRIVER] = { .type = NLA_NESTED }, |
104 | [RDMA_NLDEV_ATTR_DRIVER_ENTRY] = { .type = NLA_NESTED }, | |
105 | [RDMA_NLDEV_ATTR_DRIVER_STRING] = { .type = NLA_NUL_STRING, | |
106 | .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN }, | |
107 | [RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE] = { .type = NLA_U8 }, | |
108 | [RDMA_NLDEV_ATTR_DRIVER_S32] = { .type = NLA_S32 }, | |
109 | [RDMA_NLDEV_ATTR_DRIVER_U32] = { .type = NLA_U32 }, | |
110 | [RDMA_NLDEV_ATTR_DRIVER_S64] = { .type = NLA_S64 }, | |
111 | [RDMA_NLDEV_ATTR_DRIVER_U64] = { .type = NLA_U64 }, | |
517b773e LR |
112 | [RDMA_NLDEV_ATTR_RES_PDN] = { .type = NLA_U32 }, |
113 | [RDMA_NLDEV_ATTR_RES_CQN] = { .type = NLA_U32 }, | |
114 | [RDMA_NLDEV_ATTR_RES_MRN] = { .type = NLA_U32 }, | |
115 | [RDMA_NLDEV_ATTR_RES_CM_IDN] = { .type = NLA_U32 }, | |
c3d02788 | 116 | [RDMA_NLDEV_ATTR_RES_CTXN] = { .type = NLA_U32 }, |
3856ec4b SW |
117 | [RDMA_NLDEV_ATTR_LINK_TYPE] = { .type = NLA_NUL_STRING, |
118 | .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN }, | |
cb7e0e13 | 119 | [RDMA_NLDEV_SYS_ATTR_NETNS_MODE] = { .type = NLA_U8 }, |
9e886b39 LR |
120 | [RDMA_NLDEV_ATTR_DEV_PROTOCOL] = { .type = NLA_NUL_STRING, |
121 | .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN }, | |
2e5b8a01 | 122 | [RDMA_NLDEV_NET_NS_FD] = { .type = NLA_U32 }, |
0e2d00eb JG |
123 | [RDMA_NLDEV_ATTR_CHARDEV] = { .type = NLA_U64 }, |
124 | [RDMA_NLDEV_ATTR_CHARDEV_ABI] = { .type = NLA_U64 }, | |
125 | [RDMA_NLDEV_ATTR_CHARDEV_TYPE] = { .type = NLA_NUL_STRING, | |
126 | .len = 128 }, | |
127 | [RDMA_NLDEV_ATTR_CHARDEV_NAME] = { .type = NLA_NUL_STRING, | |
128 | .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN }, | |
b4c598a6 LR |
129 | }; |
130 | ||
73937e8a SW |
131 | static int put_driver_name_print_type(struct sk_buff *msg, const char *name, |
132 | enum rdma_nldev_print_type print_type) | |
133 | { | |
134 | if (nla_put_string(msg, RDMA_NLDEV_ATTR_DRIVER_STRING, name)) | |
135 | return -EMSGSIZE; | |
136 | if (print_type != RDMA_NLDEV_PRINT_TYPE_UNSPEC && | |
137 | nla_put_u8(msg, RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE, print_type)) | |
138 | return -EMSGSIZE; | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static int _rdma_nl_put_driver_u32(struct sk_buff *msg, const char *name, | |
144 | enum rdma_nldev_print_type print_type, | |
145 | u32 value) | |
146 | { | |
147 | if (put_driver_name_print_type(msg, name, print_type)) | |
148 | return -EMSGSIZE; | |
149 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DRIVER_U32, value)) | |
150 | return -EMSGSIZE; | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static int _rdma_nl_put_driver_u64(struct sk_buff *msg, const char *name, | |
156 | enum rdma_nldev_print_type print_type, | |
157 | u64 value) | |
158 | { | |
159 | if (put_driver_name_print_type(msg, name, print_type)) | |
160 | return -EMSGSIZE; | |
161 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_DRIVER_U64, value, | |
162 | RDMA_NLDEV_ATTR_PAD)) | |
163 | return -EMSGSIZE; | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | int rdma_nl_put_driver_u32(struct sk_buff *msg, const char *name, u32 value) | |
169 | { | |
170 | return _rdma_nl_put_driver_u32(msg, name, RDMA_NLDEV_PRINT_TYPE_UNSPEC, | |
171 | value); | |
172 | } | |
173 | EXPORT_SYMBOL(rdma_nl_put_driver_u32); | |
174 | ||
175 | int rdma_nl_put_driver_u32_hex(struct sk_buff *msg, const char *name, | |
176 | u32 value) | |
177 | { | |
178 | return _rdma_nl_put_driver_u32(msg, name, RDMA_NLDEV_PRINT_TYPE_HEX, | |
179 | value); | |
180 | } | |
181 | EXPORT_SYMBOL(rdma_nl_put_driver_u32_hex); | |
182 | ||
183 | int rdma_nl_put_driver_u64(struct sk_buff *msg, const char *name, u64 value) | |
184 | { | |
185 | return _rdma_nl_put_driver_u64(msg, name, RDMA_NLDEV_PRINT_TYPE_UNSPEC, | |
186 | value); | |
187 | } | |
188 | EXPORT_SYMBOL(rdma_nl_put_driver_u64); | |
189 | ||
190 | int rdma_nl_put_driver_u64_hex(struct sk_buff *msg, const char *name, u64 value) | |
191 | { | |
192 | return _rdma_nl_put_driver_u64(msg, name, RDMA_NLDEV_PRINT_TYPE_HEX, | |
193 | value); | |
194 | } | |
195 | EXPORT_SYMBOL(rdma_nl_put_driver_u64_hex); | |
196 | ||
c2409810 | 197 | static int fill_nldev_handle(struct sk_buff *msg, struct ib_device *device) |
b4c598a6 LR |
198 | { |
199 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_DEV_INDEX, device->index)) | |
200 | return -EMSGSIZE; | |
896de009 JG |
201 | if (nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_NAME, |
202 | dev_name(&device->dev))) | |
b4c598a6 | 203 | return -EMSGSIZE; |
c2409810 LR |
204 | |
205 | return 0; | |
206 | } | |
207 | ||
208 | static int fill_dev_info(struct sk_buff *msg, struct ib_device *device) | |
209 | { | |
210 | char fw[IB_FW_VERSION_NAME_MAX]; | |
9e886b39 LR |
211 | int ret = 0; |
212 | u8 port; | |
c2409810 LR |
213 | |
214 | if (fill_nldev_handle(msg, device)) | |
215 | return -EMSGSIZE; | |
216 | ||
b4c598a6 LR |
217 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, rdma_end_port(device))) |
218 | return -EMSGSIZE; | |
ac505253 LR |
219 | |
220 | BUILD_BUG_ON(sizeof(device->attrs.device_cap_flags) != sizeof(u64)); | |
221 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS, | |
25a0ad85 SW |
222 | device->attrs.device_cap_flags, |
223 | RDMA_NLDEV_ATTR_PAD)) | |
ac505253 LR |
224 | return -EMSGSIZE; |
225 | ||
8621a7e3 | 226 | ib_get_device_fw_str(device, fw); |
5b2cc79d | 227 | /* Device without FW has strlen(fw) = 0 */ |
8621a7e3 LR |
228 | if (strlen(fw) && nla_put_string(msg, RDMA_NLDEV_ATTR_FW_VERSION, fw)) |
229 | return -EMSGSIZE; | |
230 | ||
1aaff896 | 231 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_NODE_GUID, |
25a0ad85 SW |
232 | be64_to_cpu(device->node_guid), |
233 | RDMA_NLDEV_ATTR_PAD)) | |
1aaff896 LR |
234 | return -EMSGSIZE; |
235 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SYS_IMAGE_GUID, | |
25a0ad85 SW |
236 | be64_to_cpu(device->attrs.sys_image_guid), |
237 | RDMA_NLDEV_ATTR_PAD)) | |
1aaff896 | 238 | return -EMSGSIZE; |
1bb77b8c LR |
239 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_DEV_NODE_TYPE, device->node_type)) |
240 | return -EMSGSIZE; | |
9e886b39 LR |
241 | |
242 | /* | |
243 | * Link type is determined on first port and mlx4 device | |
244 | * which can potentially have two different link type for the same | |
245 | * IB device is considered as better to be avoided in the future, | |
246 | */ | |
247 | port = rdma_start_port(device); | |
248 | if (rdma_cap_opa_mad(device, port)) | |
249 | ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "opa"); | |
250 | else if (rdma_protocol_ib(device, port)) | |
251 | ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "ib"); | |
252 | else if (rdma_protocol_iwarp(device, port)) | |
253 | ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "iw"); | |
254 | else if (rdma_protocol_roce(device, port)) | |
255 | ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, "roce"); | |
256 | else if (rdma_protocol_usnic(device, port)) | |
257 | ret = nla_put_string(msg, RDMA_NLDEV_ATTR_DEV_PROTOCOL, | |
258 | "usnic"); | |
259 | return ret; | |
b4c598a6 LR |
260 | } |
261 | ||
7d02f605 | 262 | static int fill_port_info(struct sk_buff *msg, |
5b2cc79d LR |
263 | struct ib_device *device, u32 port, |
264 | const struct net *net) | |
7d02f605 | 265 | { |
5b2cc79d | 266 | struct net_device *netdev = NULL; |
ac505253 LR |
267 | struct ib_port_attr attr; |
268 | int ret; | |
4fa2813d | 269 | u64 cap_flags = 0; |
ac505253 | 270 | |
c2409810 | 271 | if (fill_nldev_handle(msg, device)) |
7d02f605 | 272 | return -EMSGSIZE; |
c2409810 | 273 | |
7d02f605 LR |
274 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, port)) |
275 | return -EMSGSIZE; | |
ac505253 LR |
276 | |
277 | ret = ib_query_port(device, port, &attr); | |
278 | if (ret) | |
279 | return ret; | |
280 | ||
80a06dd3 | 281 | if (rdma_protocol_ib(device, port)) { |
4fa2813d MG |
282 | BUILD_BUG_ON((sizeof(attr.port_cap_flags) + |
283 | sizeof(attr.port_cap_flags2)) > sizeof(u64)); | |
284 | cap_flags = attr.port_cap_flags | | |
285 | ((u64)attr.port_cap_flags2 << 32); | |
dd8028f1 | 286 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CAP_FLAGS, |
4fa2813d | 287 | cap_flags, RDMA_NLDEV_ATTR_PAD)) |
dd8028f1 LR |
288 | return -EMSGSIZE; |
289 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_SUBNET_PREFIX, | |
290 | attr.subnet_prefix, RDMA_NLDEV_ATTR_PAD)) | |
291 | return -EMSGSIZE; | |
80a06dd3 LR |
292 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_LID, attr.lid)) |
293 | return -EMSGSIZE; | |
294 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_SM_LID, attr.sm_lid)) | |
295 | return -EMSGSIZE; | |
34840fea LR |
296 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_LMC, attr.lmc)) |
297 | return -EMSGSIZE; | |
80a06dd3 | 298 | } |
5654e49d LR |
299 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_STATE, attr.state)) |
300 | return -EMSGSIZE; | |
301 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_PORT_PHYS_STATE, attr.phys_state)) | |
302 | return -EMSGSIZE; | |
5b2cc79d | 303 | |
c2261dd7 | 304 | netdev = ib_device_get_netdev(device, port); |
5b2cc79d LR |
305 | if (netdev && net_eq(dev_net(netdev), net)) { |
306 | ret = nla_put_u32(msg, | |
307 | RDMA_NLDEV_ATTR_NDEV_INDEX, netdev->ifindex); | |
308 | if (ret) | |
309 | goto out; | |
310 | ret = nla_put_string(msg, | |
311 | RDMA_NLDEV_ATTR_NDEV_NAME, netdev->name); | |
312 | } | |
313 | ||
314 | out: | |
315 | if (netdev) | |
316 | dev_put(netdev); | |
317 | return ret; | |
7d02f605 LR |
318 | } |
319 | ||
bf3c5a93 LR |
320 | static int fill_res_info_entry(struct sk_buff *msg, |
321 | const char *name, u64 curr) | |
322 | { | |
323 | struct nlattr *entry_attr; | |
324 | ||
ae0be8de MK |
325 | entry_attr = nla_nest_start_noflag(msg, |
326 | RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY); | |
bf3c5a93 LR |
327 | if (!entry_attr) |
328 | return -EMSGSIZE; | |
329 | ||
330 | if (nla_put_string(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME, name)) | |
331 | goto err; | |
25a0ad85 SW |
332 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR, curr, |
333 | RDMA_NLDEV_ATTR_PAD)) | |
bf3c5a93 LR |
334 | goto err; |
335 | ||
336 | nla_nest_end(msg, entry_attr); | |
337 | return 0; | |
338 | ||
339 | err: | |
340 | nla_nest_cancel(msg, entry_attr); | |
341 | return -EMSGSIZE; | |
342 | } | |
343 | ||
344 | static int fill_res_info(struct sk_buff *msg, struct ib_device *device) | |
345 | { | |
346 | static const char * const names[RDMA_RESTRACK_MAX] = { | |
347 | [RDMA_RESTRACK_PD] = "pd", | |
348 | [RDMA_RESTRACK_CQ] = "cq", | |
349 | [RDMA_RESTRACK_QP] = "qp", | |
00313983 | 350 | [RDMA_RESTRACK_CM_ID] = "cm_id", |
fccec5b8 | 351 | [RDMA_RESTRACK_MR] = "mr", |
ffd321e4 | 352 | [RDMA_RESTRACK_CTX] = "ctx", |
bf3c5a93 LR |
353 | }; |
354 | ||
bf3c5a93 LR |
355 | struct nlattr *table_attr; |
356 | int ret, i, curr; | |
357 | ||
358 | if (fill_nldev_handle(msg, device)) | |
359 | return -EMSGSIZE; | |
360 | ||
ae0be8de | 361 | table_attr = nla_nest_start_noflag(msg, RDMA_NLDEV_ATTR_RES_SUMMARY); |
bf3c5a93 LR |
362 | if (!table_attr) |
363 | return -EMSGSIZE; | |
364 | ||
365 | for (i = 0; i < RDMA_RESTRACK_MAX; i++) { | |
366 | if (!names[i]) | |
367 | continue; | |
0ad699c0 LR |
368 | curr = rdma_restrack_count(device, i, |
369 | task_active_pid_ns(current)); | |
bf3c5a93 LR |
370 | ret = fill_res_info_entry(msg, names[i], curr); |
371 | if (ret) | |
372 | goto err; | |
373 | } | |
374 | ||
375 | nla_nest_end(msg, table_attr); | |
376 | return 0; | |
377 | ||
378 | err: | |
379 | nla_nest_cancel(msg, table_attr); | |
380 | return ret; | |
381 | } | |
382 | ||
00313983 SW |
383 | static int fill_res_name_pid(struct sk_buff *msg, |
384 | struct rdma_restrack_entry *res) | |
385 | { | |
386 | /* | |
387 | * For user resources, user is should read /proc/PID/comm to get the | |
388 | * name of the task file. | |
389 | */ | |
390 | if (rdma_is_kernel_res(res)) { | |
391 | if (nla_put_string(msg, RDMA_NLDEV_ATTR_RES_KERN_NAME, | |
392 | res->kern_name)) | |
393 | return -EMSGSIZE; | |
394 | } else { | |
395 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PID, | |
396 | task_pid_vnr(res->task))) | |
397 | return -EMSGSIZE; | |
398 | } | |
399 | return 0; | |
400 | } | |
401 | ||
02da3750 LR |
402 | static bool fill_res_entry(struct ib_device *dev, struct sk_buff *msg, |
403 | struct rdma_restrack_entry *res) | |
404 | { | |
405 | if (!dev->ops.fill_res_entry) | |
406 | return false; | |
407 | return dev->ops.fill_res_entry(msg, res); | |
408 | } | |
409 | ||
659067b0 | 410 | static int fill_res_qp_entry(struct sk_buff *msg, bool has_cap_net_admin, |
d12ff624 | 411 | struct rdma_restrack_entry *res, uint32_t port) |
b5fa635a | 412 | { |
d12ff624 | 413 | struct ib_qp *qp = container_of(res, struct ib_qp, res); |
02da3750 | 414 | struct ib_device *dev = qp->device; |
b5fa635a | 415 | struct ib_qp_init_attr qp_init_attr; |
b5fa635a LR |
416 | struct ib_qp_attr qp_attr; |
417 | int ret; | |
418 | ||
419 | ret = ib_query_qp(qp, &qp_attr, 0, &qp_init_attr); | |
420 | if (ret) | |
421 | return ret; | |
422 | ||
423 | if (port && port != qp_attr.port_num) | |
c5dfe0ea | 424 | return -EAGAIN; |
b5fa635a LR |
425 | |
426 | /* In create_qp() port is not set yet */ | |
427 | if (qp_attr.port_num && | |
428 | nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, qp_attr.port_num)) | |
429 | goto err; | |
430 | ||
431 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, qp->qp_num)) | |
432 | goto err; | |
433 | if (qp->qp_type == IB_QPT_RC || qp->qp_type == IB_QPT_UC) { | |
434 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RQPN, | |
435 | qp_attr.dest_qp_num)) | |
436 | goto err; | |
437 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RQ_PSN, | |
438 | qp_attr.rq_psn)) | |
439 | goto err; | |
440 | } | |
441 | ||
442 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_SQ_PSN, qp_attr.sq_psn)) | |
443 | goto err; | |
444 | ||
445 | if (qp->qp_type == IB_QPT_RC || qp->qp_type == IB_QPT_UC || | |
446 | qp->qp_type == IB_QPT_XRC_INI || qp->qp_type == IB_QPT_XRC_TGT) { | |
447 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE, | |
448 | qp_attr.path_mig_state)) | |
449 | goto err; | |
450 | } | |
451 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, qp->qp_type)) | |
452 | goto err; | |
453 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, qp_attr.qp_state)) | |
454 | goto err; | |
455 | ||
c3d02788 LR |
456 | if (!rdma_is_kernel_res(res) && |
457 | nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, qp->pd->res.id)) | |
458 | goto err; | |
459 | ||
00313983 SW |
460 | if (fill_res_name_pid(msg, res)) |
461 | goto err; | |
462 | ||
02da3750 | 463 | if (fill_res_entry(dev, msg, res)) |
da5c8507 SW |
464 | goto err; |
465 | ||
00313983 SW |
466 | return 0; |
467 | ||
c5dfe0ea | 468 | err: return -EMSGSIZE; |
00313983 SW |
469 | } |
470 | ||
659067b0 | 471 | static int fill_res_cm_id_entry(struct sk_buff *msg, bool has_cap_net_admin, |
00313983 SW |
472 | struct rdma_restrack_entry *res, uint32_t port) |
473 | { | |
474 | struct rdma_id_private *id_priv = | |
475 | container_of(res, struct rdma_id_private, res); | |
02da3750 | 476 | struct ib_device *dev = id_priv->id.device; |
00313983 | 477 | struct rdma_cm_id *cm_id = &id_priv->id; |
00313983 SW |
478 | |
479 | if (port && port != cm_id->port_num) | |
480 | return 0; | |
481 | ||
00313983 SW |
482 | if (cm_id->port_num && |
483 | nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, cm_id->port_num)) | |
484 | goto err; | |
485 | ||
486 | if (id_priv->qp_num) { | |
487 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, id_priv->qp_num)) | |
b5fa635a | 488 | goto err; |
00313983 | 489 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, cm_id->qp_type)) |
b5fa635a LR |
490 | goto err; |
491 | } | |
492 | ||
00313983 SW |
493 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PS, cm_id->ps)) |
494 | goto err; | |
495 | ||
496 | if (nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_STATE, id_priv->state)) | |
497 | goto err; | |
498 | ||
499 | if (cm_id->route.addr.src_addr.ss_family && | |
500 | nla_put(msg, RDMA_NLDEV_ATTR_RES_SRC_ADDR, | |
501 | sizeof(cm_id->route.addr.src_addr), | |
502 | &cm_id->route.addr.src_addr)) | |
503 | goto err; | |
504 | if (cm_id->route.addr.dst_addr.ss_family && | |
505 | nla_put(msg, RDMA_NLDEV_ATTR_RES_DST_ADDR, | |
506 | sizeof(cm_id->route.addr.dst_addr), | |
507 | &cm_id->route.addr.dst_addr)) | |
508 | goto err; | |
509 | ||
517b773e LR |
510 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CM_IDN, res->id)) |
511 | goto err; | |
512 | ||
00313983 SW |
513 | if (fill_res_name_pid(msg, res)) |
514 | goto err; | |
515 | ||
02da3750 | 516 | if (fill_res_entry(dev, msg, res)) |
da5c8507 SW |
517 | goto err; |
518 | ||
b5fa635a LR |
519 | return 0; |
520 | ||
c5dfe0ea | 521 | err: return -EMSGSIZE; |
b5fa635a LR |
522 | } |
523 | ||
659067b0 | 524 | static int fill_res_cq_entry(struct sk_buff *msg, bool has_cap_net_admin, |
a34fc089 SW |
525 | struct rdma_restrack_entry *res, uint32_t port) |
526 | { | |
527 | struct ib_cq *cq = container_of(res, struct ib_cq, res); | |
02da3750 | 528 | struct ib_device *dev = cq->device; |
a34fc089 SW |
529 | |
530 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQE, cq->cqe)) | |
531 | goto err; | |
532 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_USECNT, | |
25a0ad85 | 533 | atomic_read(&cq->usecnt), RDMA_NLDEV_ATTR_PAD)) |
a34fc089 SW |
534 | goto err; |
535 | ||
536 | /* Poll context is only valid for kernel CQs */ | |
537 | if (rdma_is_kernel_res(res) && | |
538 | nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_POLL_CTX, cq->poll_ctx)) | |
539 | goto err; | |
540 | ||
517b773e LR |
541 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CQN, res->id)) |
542 | goto err; | |
c3d02788 LR |
543 | if (!rdma_is_kernel_res(res) && |
544 | nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN, | |
545 | cq->uobject->context->res.id)) | |
546 | goto err; | |
517b773e | 547 | |
a34fc089 SW |
548 | if (fill_res_name_pid(msg, res)) |
549 | goto err; | |
550 | ||
02da3750 | 551 | if (fill_res_entry(dev, msg, res)) |
da5c8507 SW |
552 | goto err; |
553 | ||
a34fc089 SW |
554 | return 0; |
555 | ||
c5dfe0ea | 556 | err: return -EMSGSIZE; |
a34fc089 SW |
557 | } |
558 | ||
659067b0 | 559 | static int fill_res_mr_entry(struct sk_buff *msg, bool has_cap_net_admin, |
fccec5b8 SW |
560 | struct rdma_restrack_entry *res, uint32_t port) |
561 | { | |
562 | struct ib_mr *mr = container_of(res, struct ib_mr, res); | |
02da3750 | 563 | struct ib_device *dev = mr->pd->device; |
fccec5b8 | 564 | |
659067b0 | 565 | if (has_cap_net_admin) { |
fccec5b8 SW |
566 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_RKEY, mr->rkey)) |
567 | goto err; | |
568 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LKEY, mr->lkey)) | |
569 | goto err; | |
fccec5b8 SW |
570 | } |
571 | ||
25a0ad85 SW |
572 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_MRLEN, mr->length, |
573 | RDMA_NLDEV_ATTR_PAD)) | |
fccec5b8 SW |
574 | goto err; |
575 | ||
517b773e LR |
576 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_MRN, res->id)) |
577 | goto err; | |
578 | ||
c3d02788 LR |
579 | if (!rdma_is_kernel_res(res) && |
580 | nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, mr->pd->res.id)) | |
581 | goto err; | |
582 | ||
fccec5b8 SW |
583 | if (fill_res_name_pid(msg, res)) |
584 | goto err; | |
585 | ||
02da3750 | 586 | if (fill_res_entry(dev, msg, res)) |
da5c8507 SW |
587 | goto err; |
588 | ||
fccec5b8 SW |
589 | return 0; |
590 | ||
c5dfe0ea | 591 | err: return -EMSGSIZE; |
fccec5b8 SW |
592 | } |
593 | ||
659067b0 | 594 | static int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin, |
29cf1351 SW |
595 | struct rdma_restrack_entry *res, uint32_t port) |
596 | { | |
597 | struct ib_pd *pd = container_of(res, struct ib_pd, res); | |
02da3750 | 598 | struct ib_device *dev = pd->device; |
29cf1351 | 599 | |
659067b0 | 600 | if (has_cap_net_admin) { |
29cf1351 SW |
601 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY, |
602 | pd->local_dma_lkey)) | |
603 | goto err; | |
604 | if ((pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY) && | |
605 | nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY, | |
606 | pd->unsafe_global_rkey)) | |
607 | goto err; | |
608 | } | |
609 | if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_RES_USECNT, | |
25a0ad85 | 610 | atomic_read(&pd->usecnt), RDMA_NLDEV_ATTR_PAD)) |
29cf1351 | 611 | goto err; |
29cf1351 | 612 | |
517b773e LR |
613 | if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_PDN, res->id)) |
614 | goto err; | |
615 | ||
c3d02788 LR |
616 | if (!rdma_is_kernel_res(res) && |
617 | nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_CTXN, | |
618 | pd->uobject->context->res.id)) | |
619 | goto err; | |
620 | ||
29cf1351 SW |
621 | if (fill_res_name_pid(msg, res)) |
622 | goto err; | |
623 | ||
02da3750 | 624 | if (fill_res_entry(dev, msg, res)) |
da5c8507 SW |
625 | goto err; |
626 | ||
29cf1351 SW |
627 | return 0; |
628 | ||
c5dfe0ea | 629 | err: return -EMSGSIZE; |
29cf1351 SW |
630 | } |
631 | ||
e5c9469e LR |
632 | static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
633 | struct netlink_ext_ack *extack) | |
634 | { | |
635 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
636 | struct ib_device *device; | |
637 | struct sk_buff *msg; | |
638 | u32 index; | |
639 | int err; | |
640 | ||
8cb08174 JB |
641 | err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
642 | nldev_policy, extack); | |
e5c9469e LR |
643 | if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) |
644 | return -EINVAL; | |
645 | ||
646 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
647 | ||
37eeab55 | 648 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
e5c9469e LR |
649 | if (!device) |
650 | return -EINVAL; | |
651 | ||
652 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
f8978bd9 LR |
653 | if (!msg) { |
654 | err = -ENOMEM; | |
655 | goto err; | |
656 | } | |
e5c9469e LR |
657 | |
658 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | |
659 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), | |
660 | 0, 0); | |
661 | ||
662 | err = fill_dev_info(msg, device); | |
f8978bd9 LR |
663 | if (err) |
664 | goto err_free; | |
e5c9469e LR |
665 | |
666 | nlmsg_end(msg, nlh); | |
667 | ||
01b67117 | 668 | ib_device_put(device); |
e5c9469e | 669 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); |
f8978bd9 LR |
670 | |
671 | err_free: | |
672 | nlmsg_free(msg); | |
673 | err: | |
01b67117 | 674 | ib_device_put(device); |
f8978bd9 | 675 | return err; |
e5c9469e LR |
676 | } |
677 | ||
05d940d3 LR |
678 | static int nldev_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
679 | struct netlink_ext_ack *extack) | |
680 | { | |
681 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
682 | struct ib_device *device; | |
683 | u32 index; | |
684 | int err; | |
685 | ||
8cb08174 JB |
686 | err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
687 | nldev_policy, extack); | |
05d940d3 LR |
688 | if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) |
689 | return -EINVAL; | |
690 | ||
691 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 692 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
05d940d3 LR |
693 | if (!device) |
694 | return -EINVAL; | |
695 | ||
696 | if (tb[RDMA_NLDEV_ATTR_DEV_NAME]) { | |
697 | char name[IB_DEVICE_NAME_MAX] = {}; | |
698 | ||
699 | nla_strlcpy(name, tb[RDMA_NLDEV_ATTR_DEV_NAME], | |
700 | IB_DEVICE_NAME_MAX); | |
701 | err = ib_device_rename(device, name); | |
2e5b8a01 | 702 | goto done; |
05d940d3 LR |
703 | } |
704 | ||
2e5b8a01 PP |
705 | if (tb[RDMA_NLDEV_NET_NS_FD]) { |
706 | u32 ns_fd; | |
707 | ||
708 | ns_fd = nla_get_u32(tb[RDMA_NLDEV_NET_NS_FD]); | |
709 | err = ib_device_set_netns_put(skb, device, ns_fd); | |
710 | goto put_done; | |
711 | } | |
712 | ||
713 | done: | |
01b67117 | 714 | ib_device_put(device); |
2e5b8a01 | 715 | put_done: |
05d940d3 LR |
716 | return err; |
717 | } | |
718 | ||
b4c598a6 LR |
719 | static int _nldev_get_dumpit(struct ib_device *device, |
720 | struct sk_buff *skb, | |
721 | struct netlink_callback *cb, | |
722 | unsigned int idx) | |
723 | { | |
724 | int start = cb->args[0]; | |
725 | struct nlmsghdr *nlh; | |
726 | ||
727 | if (idx < start) | |
728 | return 0; | |
729 | ||
730 | nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | |
731 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), | |
732 | 0, NLM_F_MULTI); | |
733 | ||
734 | if (fill_dev_info(skb, device)) { | |
735 | nlmsg_cancel(skb, nlh); | |
736 | goto out; | |
737 | } | |
738 | ||
739 | nlmsg_end(skb, nlh); | |
740 | ||
741 | idx++; | |
742 | ||
743 | out: cb->args[0] = idx; | |
744 | return skb->len; | |
745 | } | |
746 | ||
747 | static int nldev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb) | |
748 | { | |
749 | /* | |
750 | * There is no need to take lock, because | |
37eeab55 | 751 | * we are relying on ib_core's locking. |
b4c598a6 LR |
752 | */ |
753 | return ib_enum_all_devs(_nldev_get_dumpit, skb, cb); | |
754 | } | |
755 | ||
c3f66f7b LR |
756 | static int nldev_port_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
757 | struct netlink_ext_ack *extack) | |
758 | { | |
759 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
760 | struct ib_device *device; | |
761 | struct sk_buff *msg; | |
762 | u32 index; | |
763 | u32 port; | |
764 | int err; | |
765 | ||
8cb08174 JB |
766 | err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
767 | nldev_policy, extack); | |
287683d0 LR |
768 | if (err || |
769 | !tb[RDMA_NLDEV_ATTR_DEV_INDEX] || | |
770 | !tb[RDMA_NLDEV_ATTR_PORT_INDEX]) | |
c3f66f7b LR |
771 | return -EINVAL; |
772 | ||
773 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 774 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
c3f66f7b LR |
775 | if (!device) |
776 | return -EINVAL; | |
777 | ||
778 | port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
f8978bd9 LR |
779 | if (!rdma_is_port_valid(device, port)) { |
780 | err = -EINVAL; | |
781 | goto err; | |
782 | } | |
c3f66f7b LR |
783 | |
784 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
f8978bd9 LR |
785 | if (!msg) { |
786 | err = -ENOMEM; | |
787 | goto err; | |
788 | } | |
c3f66f7b LR |
789 | |
790 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | |
791 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_GET), | |
792 | 0, 0); | |
793 | ||
5b2cc79d | 794 | err = fill_port_info(msg, device, port, sock_net(skb->sk)); |
f8978bd9 LR |
795 | if (err) |
796 | goto err_free; | |
c3f66f7b LR |
797 | |
798 | nlmsg_end(msg, nlh); | |
01b67117 | 799 | ib_device_put(device); |
c3f66f7b LR |
800 | |
801 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); | |
f8978bd9 LR |
802 | |
803 | err_free: | |
804 | nlmsg_free(msg); | |
805 | err: | |
01b67117 | 806 | ib_device_put(device); |
f8978bd9 | 807 | return err; |
c3f66f7b LR |
808 | } |
809 | ||
7d02f605 LR |
810 | static int nldev_port_get_dumpit(struct sk_buff *skb, |
811 | struct netlink_callback *cb) | |
812 | { | |
813 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
814 | struct ib_device *device; | |
815 | int start = cb->args[0]; | |
816 | struct nlmsghdr *nlh; | |
817 | u32 idx = 0; | |
818 | u32 ifindex; | |
819 | int err; | |
ea1075ed | 820 | unsigned int p; |
7d02f605 | 821 | |
8cb08174 JB |
822 | err = nlmsg_parse_deprecated(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
823 | nldev_policy, NULL); | |
7d02f605 LR |
824 | if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) |
825 | return -EINVAL; | |
826 | ||
827 | ifindex = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 828 | device = ib_device_get_by_index(sock_net(skb->sk), ifindex); |
7d02f605 LR |
829 | if (!device) |
830 | return -EINVAL; | |
831 | ||
ea1075ed | 832 | rdma_for_each_port (device, p) { |
7d02f605 LR |
833 | /* |
834 | * The dumpit function returns all information from specific | |
835 | * index. This specific index is taken from the netlink | |
836 | * messages request sent by user and it is available | |
837 | * in cb->args[0]. | |
838 | * | |
839 | * Usually, the user doesn't fill this field and it causes | |
840 | * to return everything. | |
841 | * | |
842 | */ | |
843 | if (idx < start) { | |
844 | idx++; | |
845 | continue; | |
846 | } | |
847 | ||
848 | nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, | |
849 | cb->nlh->nlmsg_seq, | |
850 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, | |
851 | RDMA_NLDEV_CMD_PORT_GET), | |
852 | 0, NLM_F_MULTI); | |
853 | ||
5b2cc79d | 854 | if (fill_port_info(skb, device, p, sock_net(skb->sk))) { |
7d02f605 LR |
855 | nlmsg_cancel(skb, nlh); |
856 | goto out; | |
857 | } | |
858 | idx++; | |
859 | nlmsg_end(skb, nlh); | |
860 | } | |
861 | ||
f8978bd9 | 862 | out: |
01b67117 | 863 | ib_device_put(device); |
f8978bd9 | 864 | cb->args[0] = idx; |
7d02f605 LR |
865 | return skb->len; |
866 | } | |
867 | ||
bf3c5a93 LR |
868 | static int nldev_res_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
869 | struct netlink_ext_ack *extack) | |
870 | { | |
871 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
872 | struct ib_device *device; | |
873 | struct sk_buff *msg; | |
874 | u32 index; | |
875 | int ret; | |
876 | ||
8cb08174 JB |
877 | ret = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
878 | nldev_policy, extack); | |
bf3c5a93 LR |
879 | if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) |
880 | return -EINVAL; | |
881 | ||
882 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 883 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
bf3c5a93 LR |
884 | if (!device) |
885 | return -EINVAL; | |
886 | ||
887 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
f34727a1 DC |
888 | if (!msg) { |
889 | ret = -ENOMEM; | |
bf3c5a93 | 890 | goto err; |
f34727a1 | 891 | } |
bf3c5a93 LR |
892 | |
893 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | |
894 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET), | |
895 | 0, 0); | |
896 | ||
897 | ret = fill_res_info(msg, device); | |
898 | if (ret) | |
899 | goto err_free; | |
900 | ||
901 | nlmsg_end(msg, nlh); | |
01b67117 | 902 | ib_device_put(device); |
bf3c5a93 LR |
903 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); |
904 | ||
905 | err_free: | |
906 | nlmsg_free(msg); | |
907 | err: | |
01b67117 | 908 | ib_device_put(device); |
bf3c5a93 LR |
909 | return ret; |
910 | } | |
911 | ||
912 | static int _nldev_res_get_dumpit(struct ib_device *device, | |
913 | struct sk_buff *skb, | |
914 | struct netlink_callback *cb, | |
915 | unsigned int idx) | |
916 | { | |
917 | int start = cb->args[0]; | |
918 | struct nlmsghdr *nlh; | |
919 | ||
920 | if (idx < start) | |
921 | return 0; | |
922 | ||
923 | nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | |
924 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, RDMA_NLDEV_CMD_RES_GET), | |
925 | 0, NLM_F_MULTI); | |
926 | ||
927 | if (fill_res_info(skb, device)) { | |
928 | nlmsg_cancel(skb, nlh); | |
929 | goto out; | |
930 | } | |
bf3c5a93 LR |
931 | nlmsg_end(skb, nlh); |
932 | ||
933 | idx++; | |
934 | ||
935 | out: | |
936 | cb->args[0] = idx; | |
937 | return skb->len; | |
938 | } | |
939 | ||
940 | static int nldev_res_get_dumpit(struct sk_buff *skb, | |
941 | struct netlink_callback *cb) | |
942 | { | |
943 | return ib_enum_all_devs(_nldev_res_get_dumpit, skb, cb); | |
944 | } | |
945 | ||
d12ff624 | 946 | struct nldev_fill_res_entry { |
659067b0 | 947 | int (*fill_res_func)(struct sk_buff *msg, bool has_cap_net_admin, |
d12ff624 SW |
948 | struct rdma_restrack_entry *res, u32 port); |
949 | enum rdma_nldev_attr nldev_attr; | |
950 | enum rdma_nldev_command nldev_cmd; | |
c5dfe0ea LR |
951 | u8 flags; |
952 | u32 entry; | |
953 | u32 id; | |
954 | }; | |
955 | ||
956 | enum nldev_res_flags { | |
957 | NLDEV_PER_DEV = 1 << 0, | |
d12ff624 SW |
958 | }; |
959 | ||
960 | static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = { | |
961 | [RDMA_RESTRACK_QP] = { | |
962 | .fill_res_func = fill_res_qp_entry, | |
963 | .nldev_cmd = RDMA_NLDEV_CMD_RES_QP_GET, | |
964 | .nldev_attr = RDMA_NLDEV_ATTR_RES_QP, | |
c5dfe0ea | 965 | .entry = RDMA_NLDEV_ATTR_RES_QP_ENTRY, |
1b8b7788 | 966 | .id = RDMA_NLDEV_ATTR_RES_LQPN, |
d12ff624 | 967 | }, |
00313983 SW |
968 | [RDMA_RESTRACK_CM_ID] = { |
969 | .fill_res_func = fill_res_cm_id_entry, | |
970 | .nldev_cmd = RDMA_NLDEV_CMD_RES_CM_ID_GET, | |
971 | .nldev_attr = RDMA_NLDEV_ATTR_RES_CM_ID, | |
c5dfe0ea | 972 | .entry = RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY, |
517b773e | 973 | .id = RDMA_NLDEV_ATTR_RES_CM_IDN, |
00313983 | 974 | }, |
a34fc089 SW |
975 | [RDMA_RESTRACK_CQ] = { |
976 | .fill_res_func = fill_res_cq_entry, | |
977 | .nldev_cmd = RDMA_NLDEV_CMD_RES_CQ_GET, | |
978 | .nldev_attr = RDMA_NLDEV_ATTR_RES_CQ, | |
c5dfe0ea LR |
979 | .flags = NLDEV_PER_DEV, |
980 | .entry = RDMA_NLDEV_ATTR_RES_CQ_ENTRY, | |
517b773e | 981 | .id = RDMA_NLDEV_ATTR_RES_CQN, |
a34fc089 | 982 | }, |
fccec5b8 SW |
983 | [RDMA_RESTRACK_MR] = { |
984 | .fill_res_func = fill_res_mr_entry, | |
985 | .nldev_cmd = RDMA_NLDEV_CMD_RES_MR_GET, | |
986 | .nldev_attr = RDMA_NLDEV_ATTR_RES_MR, | |
c5dfe0ea LR |
987 | .flags = NLDEV_PER_DEV, |
988 | .entry = RDMA_NLDEV_ATTR_RES_MR_ENTRY, | |
517b773e | 989 | .id = RDMA_NLDEV_ATTR_RES_MRN, |
fccec5b8 | 990 | }, |
29cf1351 SW |
991 | [RDMA_RESTRACK_PD] = { |
992 | .fill_res_func = fill_res_pd_entry, | |
993 | .nldev_cmd = RDMA_NLDEV_CMD_RES_PD_GET, | |
994 | .nldev_attr = RDMA_NLDEV_ATTR_RES_PD, | |
c5dfe0ea LR |
995 | .flags = NLDEV_PER_DEV, |
996 | .entry = RDMA_NLDEV_ATTR_RES_PD_ENTRY, | |
517b773e | 997 | .id = RDMA_NLDEV_ATTR_RES_PDN, |
29cf1351 | 998 | }, |
d12ff624 SW |
999 | }; |
1000 | ||
8be565e6 LR |
1001 | static bool is_visible_in_pid_ns(struct rdma_restrack_entry *res) |
1002 | { | |
1003 | /* | |
1004 | * 1. Kern resources should be visible in init name space only | |
1005 | * 2. Present only resources visible in the current namespace | |
1006 | */ | |
1007 | if (rdma_is_kernel_res(res)) | |
1008 | return task_active_pid_ns(current) == &init_pid_ns; | |
1009 | return task_active_pid_ns(current) == task_active_pid_ns(res->task); | |
1010 | } | |
1011 | ||
c5dfe0ea LR |
1012 | static int res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
1013 | struct netlink_ext_ack *extack, | |
1014 | enum rdma_restrack_type res_type) | |
1015 | { | |
1016 | const struct nldev_fill_res_entry *fe = &fill_entries[res_type]; | |
1017 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
1018 | struct rdma_restrack_entry *res; | |
1019 | struct ib_device *device; | |
1020 | u32 index, id, port = 0; | |
1021 | bool has_cap_net_admin; | |
1022 | struct sk_buff *msg; | |
1023 | int ret; | |
1024 | ||
8cb08174 JB |
1025 | ret = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
1026 | nldev_policy, extack); | |
c5dfe0ea LR |
1027 | if (ret || !tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !fe->id || !tb[fe->id]) |
1028 | return -EINVAL; | |
1029 | ||
1030 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 1031 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
c5dfe0ea LR |
1032 | if (!device) |
1033 | return -EINVAL; | |
1034 | ||
1035 | if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
1036 | port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
1037 | if (!rdma_is_port_valid(device, port)) { | |
1038 | ret = -EINVAL; | |
1039 | goto err; | |
1040 | } | |
1041 | } | |
1042 | ||
1043 | if ((port && fe->flags & NLDEV_PER_DEV) || | |
1044 | (!port && ~fe->flags & NLDEV_PER_DEV)) { | |
1045 | ret = -EINVAL; | |
1046 | goto err; | |
1047 | } | |
1048 | ||
1049 | id = nla_get_u32(tb[fe->id]); | |
1050 | res = rdma_restrack_get_byid(device, res_type, id); | |
1051 | if (IS_ERR(res)) { | |
1052 | ret = PTR_ERR(res); | |
1053 | goto err; | |
1054 | } | |
1055 | ||
1056 | if (!is_visible_in_pid_ns(res)) { | |
1057 | ret = -ENOENT; | |
1058 | goto err_get; | |
1059 | } | |
1060 | ||
1061 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1062 | if (!msg) { | |
1063 | ret = -ENOMEM; | |
1064 | goto err; | |
1065 | } | |
1066 | ||
1067 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | |
1068 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, fe->nldev_cmd), | |
1069 | 0, 0); | |
1070 | ||
1071 | if (fill_nldev_handle(msg, device)) { | |
1072 | ret = -EMSGSIZE; | |
1073 | goto err_free; | |
1074 | } | |
1075 | ||
1076 | has_cap_net_admin = netlink_capable(skb, CAP_NET_ADMIN); | |
1077 | ret = fe->fill_res_func(msg, has_cap_net_admin, res, port); | |
1078 | rdma_restrack_put(res); | |
1079 | if (ret) | |
1080 | goto err_free; | |
1081 | ||
1082 | nlmsg_end(msg, nlh); | |
1083 | ib_device_put(device); | |
1084 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); | |
1085 | ||
1086 | err_free: | |
1087 | nlmsg_free(msg); | |
1088 | err_get: | |
1089 | rdma_restrack_put(res); | |
1090 | err: | |
1091 | ib_device_put(device); | |
1092 | return ret; | |
1093 | } | |
1094 | ||
d12ff624 SW |
1095 | static int res_get_common_dumpit(struct sk_buff *skb, |
1096 | struct netlink_callback *cb, | |
1097 | enum rdma_restrack_type res_type) | |
b5fa635a | 1098 | { |
d12ff624 | 1099 | const struct nldev_fill_res_entry *fe = &fill_entries[res_type]; |
b5fa635a LR |
1100 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; |
1101 | struct rdma_restrack_entry *res; | |
7c77c6a9 | 1102 | struct rdma_restrack_root *rt; |
b5fa635a LR |
1103 | int err, ret = 0, idx = 0; |
1104 | struct nlattr *table_attr; | |
c5dfe0ea | 1105 | struct nlattr *entry_attr; |
b5fa635a LR |
1106 | struct ib_device *device; |
1107 | int start = cb->args[0]; | |
659067b0 | 1108 | bool has_cap_net_admin; |
b5fa635a | 1109 | struct nlmsghdr *nlh; |
fd47c2f9 | 1110 | unsigned long id; |
b5fa635a | 1111 | u32 index, port = 0; |
d12ff624 | 1112 | bool filled = false; |
b5fa635a | 1113 | |
8cb08174 JB |
1114 | err = nlmsg_parse_deprecated(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
1115 | nldev_policy, NULL); | |
b5fa635a | 1116 | /* |
d12ff624 | 1117 | * Right now, we are expecting the device index to get res information, |
b5fa635a LR |
1118 | * but it is possible to extend this code to return all devices in |
1119 | * one shot by checking the existence of RDMA_NLDEV_ATTR_DEV_INDEX. | |
1120 | * if it doesn't exist, we will iterate over all devices. | |
1121 | * | |
1122 | * But it is not needed for now. | |
1123 | */ | |
1124 | if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) | |
1125 | return -EINVAL; | |
1126 | ||
1127 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 1128 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
b5fa635a LR |
1129 | if (!device) |
1130 | return -EINVAL; | |
1131 | ||
1132 | /* | |
1133 | * If no PORT_INDEX is supplied, we will return all QPs from that device | |
1134 | */ | |
1135 | if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
1136 | port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
1137 | if (!rdma_is_port_valid(device, port)) { | |
1138 | ret = -EINVAL; | |
1139 | goto err_index; | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | |
d12ff624 | 1144 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, fe->nldev_cmd), |
b5fa635a LR |
1145 | 0, NLM_F_MULTI); |
1146 | ||
1147 | if (fill_nldev_handle(skb, device)) { | |
1148 | ret = -EMSGSIZE; | |
1149 | goto err; | |
1150 | } | |
1151 | ||
ae0be8de | 1152 | table_attr = nla_nest_start_noflag(skb, fe->nldev_attr); |
b5fa635a LR |
1153 | if (!table_attr) { |
1154 | ret = -EMSGSIZE; | |
1155 | goto err; | |
1156 | } | |
1157 | ||
659067b0 LR |
1158 | has_cap_net_admin = netlink_capable(cb->skb, CAP_NET_ADMIN); |
1159 | ||
7c77c6a9 LR |
1160 | rt = &device->res[res_type]; |
1161 | xa_lock(&rt->xa); | |
fd47c2f9 LR |
1162 | /* |
1163 | * FIXME: if the skip ahead is something common this loop should | |
1164 | * use xas_for_each & xas_pause to optimize, we can have a lot of | |
1165 | * objects. | |
1166 | */ | |
7c77c6a9 | 1167 | xa_for_each(&rt->xa, id, res) { |
8be565e6 | 1168 | if (!is_visible_in_pid_ns(res)) |
f2a0e45f | 1169 | continue; |
b5fa635a | 1170 | |
f2a0e45f | 1171 | if (idx < start || !rdma_restrack_get(res)) |
b5fa635a LR |
1172 | goto next; |
1173 | ||
7c77c6a9 LR |
1174 | xa_unlock(&rt->xa); |
1175 | ||
d12ff624 | 1176 | filled = true; |
b5fa635a | 1177 | |
ae0be8de | 1178 | entry_attr = nla_nest_start_noflag(skb, fe->entry); |
c5dfe0ea LR |
1179 | if (!entry_attr) { |
1180 | ret = -EMSGSIZE; | |
1181 | rdma_restrack_put(res); | |
7c77c6a9 | 1182 | goto msg_full; |
c5dfe0ea | 1183 | } |
c5dfe0ea | 1184 | |
659067b0 | 1185 | ret = fe->fill_res_func(skb, has_cap_net_admin, res, port); |
b5fa635a LR |
1186 | rdma_restrack_put(res); |
1187 | ||
7c77c6a9 | 1188 | if (ret) { |
c5dfe0ea | 1189 | nla_nest_cancel(skb, entry_attr); |
7c77c6a9 LR |
1190 | if (ret == -EMSGSIZE) |
1191 | goto msg_full; | |
1192 | if (ret == -EAGAIN) | |
1193 | goto again; | |
b5fa635a | 1194 | goto res_err; |
7c77c6a9 | 1195 | } |
c5dfe0ea | 1196 | nla_nest_end(skb, entry_attr); |
7c77c6a9 | 1197 | again: xa_lock(&rt->xa); |
b5fa635a LR |
1198 | next: idx++; |
1199 | } | |
7c77c6a9 | 1200 | xa_unlock(&rt->xa); |
b5fa635a | 1201 | |
7c77c6a9 | 1202 | msg_full: |
b5fa635a LR |
1203 | nla_nest_end(skb, table_attr); |
1204 | nlmsg_end(skb, nlh); | |
1205 | cb->args[0] = idx; | |
1206 | ||
1207 | /* | |
d12ff624 | 1208 | * No more entries to fill, cancel the message and |
b5fa635a LR |
1209 | * return 0 to mark end of dumpit. |
1210 | */ | |
d12ff624 | 1211 | if (!filled) |
b5fa635a LR |
1212 | goto err; |
1213 | ||
01b67117 | 1214 | ib_device_put(device); |
b5fa635a LR |
1215 | return skb->len; |
1216 | ||
1217 | res_err: | |
1218 | nla_nest_cancel(skb, table_attr); | |
b5fa635a LR |
1219 | |
1220 | err: | |
1221 | nlmsg_cancel(skb, nlh); | |
1222 | ||
1223 | err_index: | |
01b67117 | 1224 | ib_device_put(device); |
b5fa635a LR |
1225 | return ret; |
1226 | } | |
1227 | ||
f732e713 LR |
1228 | #define RES_GET_FUNCS(name, type) \ |
1229 | static int nldev_res_get_##name##_dumpit(struct sk_buff *skb, \ | |
1230 | struct netlink_callback *cb) \ | |
1231 | { \ | |
1232 | return res_get_common_dumpit(skb, cb, type); \ | |
c5dfe0ea LR |
1233 | } \ |
1234 | static int nldev_res_get_##name##_doit(struct sk_buff *skb, \ | |
1235 | struct nlmsghdr *nlh, \ | |
1236 | struct netlink_ext_ack *extack) \ | |
1237 | { \ | |
1238 | return res_get_common_doit(skb, nlh, extack, type); \ | |
f732e713 | 1239 | } |
fccec5b8 | 1240 | |
f732e713 LR |
1241 | RES_GET_FUNCS(qp, RDMA_RESTRACK_QP); |
1242 | RES_GET_FUNCS(cm_id, RDMA_RESTRACK_CM_ID); | |
1243 | RES_GET_FUNCS(cq, RDMA_RESTRACK_CQ); | |
1244 | RES_GET_FUNCS(pd, RDMA_RESTRACK_PD); | |
1245 | RES_GET_FUNCS(mr, RDMA_RESTRACK_MR); | |
29cf1351 | 1246 | |
3856ec4b SW |
1247 | static LIST_HEAD(link_ops); |
1248 | static DECLARE_RWSEM(link_ops_rwsem); | |
1249 | ||
1250 | static const struct rdma_link_ops *link_ops_get(const char *type) | |
1251 | { | |
1252 | const struct rdma_link_ops *ops; | |
1253 | ||
1254 | list_for_each_entry(ops, &link_ops, list) { | |
1255 | if (!strcmp(ops->type, type)) | |
1256 | goto out; | |
1257 | } | |
1258 | ops = NULL; | |
1259 | out: | |
1260 | return ops; | |
1261 | } | |
1262 | ||
1263 | void rdma_link_register(struct rdma_link_ops *ops) | |
1264 | { | |
1265 | down_write(&link_ops_rwsem); | |
afc1990e | 1266 | if (WARN_ON_ONCE(link_ops_get(ops->type))) |
3856ec4b | 1267 | goto out; |
3856ec4b SW |
1268 | list_add(&ops->list, &link_ops); |
1269 | out: | |
1270 | up_write(&link_ops_rwsem); | |
1271 | } | |
1272 | EXPORT_SYMBOL(rdma_link_register); | |
1273 | ||
1274 | void rdma_link_unregister(struct rdma_link_ops *ops) | |
1275 | { | |
1276 | down_write(&link_ops_rwsem); | |
1277 | list_del(&ops->list); | |
1278 | up_write(&link_ops_rwsem); | |
1279 | } | |
1280 | EXPORT_SYMBOL(rdma_link_unregister); | |
1281 | ||
1282 | static int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, | |
1283 | struct netlink_ext_ack *extack) | |
1284 | { | |
1285 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
1286 | char ibdev_name[IB_DEVICE_NAME_MAX]; | |
1287 | const struct rdma_link_ops *ops; | |
1288 | char ndev_name[IFNAMSIZ]; | |
1289 | struct net_device *ndev; | |
1290 | char type[IFNAMSIZ]; | |
1291 | int err; | |
1292 | ||
8cb08174 JB |
1293 | err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
1294 | nldev_policy, extack); | |
3856ec4b SW |
1295 | if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] || |
1296 | !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME]) | |
1297 | return -EINVAL; | |
1298 | ||
1299 | nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME], | |
1300 | sizeof(ibdev_name)); | |
1301 | if (strchr(ibdev_name, '%')) | |
1302 | return -EINVAL; | |
1303 | ||
1304 | nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type)); | |
1305 | nla_strlcpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME], | |
1306 | sizeof(ndev_name)); | |
1307 | ||
1308 | ndev = dev_get_by_name(&init_net, ndev_name); | |
1309 | if (!ndev) | |
1310 | return -ENODEV; | |
1311 | ||
1312 | down_read(&link_ops_rwsem); | |
1313 | ops = link_ops_get(type); | |
1314 | #ifdef CONFIG_MODULES | |
1315 | if (!ops) { | |
1316 | up_read(&link_ops_rwsem); | |
1317 | request_module("rdma-link-%s", type); | |
1318 | down_read(&link_ops_rwsem); | |
1319 | ops = link_ops_get(type); | |
1320 | } | |
1321 | #endif | |
1322 | err = ops ? ops->newlink(ibdev_name, ndev) : -EINVAL; | |
1323 | up_read(&link_ops_rwsem); | |
1324 | dev_put(ndev); | |
1325 | ||
1326 | return err; | |
1327 | } | |
1328 | ||
1329 | static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, | |
1330 | struct netlink_ext_ack *extack) | |
1331 | { | |
1332 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
1333 | struct ib_device *device; | |
1334 | u32 index; | |
1335 | int err; | |
1336 | ||
8cb08174 JB |
1337 | err = nlmsg_parse_deprecated(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
1338 | nldev_policy, extack); | |
3856ec4b SW |
1339 | if (err || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) |
1340 | return -EINVAL; | |
1341 | ||
1342 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
37eeab55 | 1343 | device = ib_device_get_by_index(sock_net(skb->sk), index); |
3856ec4b SW |
1344 | if (!device) |
1345 | return -EINVAL; | |
1346 | ||
1347 | if (!(device->attrs.device_cap_flags & IB_DEVICE_ALLOW_USER_UNREG)) { | |
1348 | ib_device_put(device); | |
1349 | return -EINVAL; | |
1350 | } | |
1351 | ||
1352 | ib_unregister_device_and_put(device); | |
1353 | return 0; | |
1354 | } | |
1355 | ||
0e2d00eb JG |
1356 | static int nldev_get_chardev(struct sk_buff *skb, struct nlmsghdr *nlh, |
1357 | struct netlink_ext_ack *extack) | |
1358 | { | |
1359 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
1360 | char client_name[IB_DEVICE_NAME_MAX]; | |
1361 | struct ib_client_nl_info data = {}; | |
1362 | struct ib_device *ibdev = NULL; | |
1363 | struct sk_buff *msg; | |
1364 | u32 index; | |
1365 | int err; | |
1366 | ||
1367 | err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy, | |
1368 | extack); | |
1369 | if (err || !tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE]) | |
1370 | return -EINVAL; | |
1371 | ||
1372 | if (nla_strlcpy(client_name, tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE], | |
1373 | sizeof(client_name)) >= sizeof(client_name)) | |
1374 | return -EINVAL; | |
1375 | ||
1376 | if (tb[RDMA_NLDEV_ATTR_DEV_INDEX]) { | |
1377 | index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); | |
1378 | ibdev = ib_device_get_by_index(sock_net(skb->sk), index); | |
1379 | if (!ibdev) | |
1380 | return -EINVAL; | |
1381 | ||
1382 | if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
1383 | data.port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); | |
1384 | if (!rdma_is_port_valid(ibdev, data.port)) { | |
1385 | err = -EINVAL; | |
1386 | goto out_put; | |
1387 | } | |
1388 | } else { | |
1389 | data.port = -1; | |
1390 | } | |
1391 | } else if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { | |
1392 | return -EINVAL; | |
1393 | } | |
1394 | ||
1395 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1396 | if (!msg) { | |
1397 | err = -ENOMEM; | |
1398 | goto out_put; | |
1399 | } | |
1400 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | |
1401 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, | |
1402 | RDMA_NLDEV_CMD_GET_CHARDEV), | |
1403 | 0, 0); | |
1404 | ||
1405 | data.nl_msg = msg; | |
1406 | err = ib_get_client_nl_info(ibdev, client_name, &data); | |
1407 | if (err) | |
1408 | goto out_nlmsg; | |
1409 | ||
1410 | err = nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CHARDEV, | |
1411 | huge_encode_dev(data.cdev->devt), | |
1412 | RDMA_NLDEV_ATTR_PAD); | |
1413 | if (err) | |
1414 | goto out_data; | |
1415 | err = nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CHARDEV_ABI, data.abi, | |
1416 | RDMA_NLDEV_ATTR_PAD); | |
1417 | if (err) | |
1418 | goto out_data; | |
1419 | if (nla_put_string(msg, RDMA_NLDEV_ATTR_CHARDEV_NAME, | |
1420 | dev_name(data.cdev))) { | |
1421 | err = -EMSGSIZE; | |
1422 | goto out_data; | |
1423 | } | |
1424 | ||
1425 | nlmsg_end(msg, nlh); | |
1426 | put_device(data.cdev); | |
1427 | if (ibdev) | |
1428 | ib_device_put(ibdev); | |
1429 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); | |
1430 | ||
1431 | out_data: | |
1432 | put_device(data.cdev); | |
1433 | out_nlmsg: | |
1434 | nlmsg_free(msg); | |
1435 | out_put: | |
1436 | if (ibdev) | |
1437 | ib_device_put(ibdev); | |
1438 | return err; | |
1439 | } | |
1440 | ||
4d7ba8ce PP |
1441 | static int nldev_sys_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
1442 | struct netlink_ext_ack *extack) | |
cb7e0e13 PP |
1443 | { |
1444 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
4d7ba8ce | 1445 | struct sk_buff *msg; |
cb7e0e13 PP |
1446 | int err; |
1447 | ||
4d7ba8ce PP |
1448 | err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, |
1449 | nldev_policy, extack); | |
cb7e0e13 PP |
1450 | if (err) |
1451 | return err; | |
1452 | ||
4d7ba8ce PP |
1453 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
1454 | if (!msg) | |
1455 | return -ENOMEM; | |
1456 | ||
1457 | nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, | |
cb7e0e13 PP |
1458 | RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, |
1459 | RDMA_NLDEV_CMD_SYS_GET), | |
1460 | 0, 0); | |
1461 | ||
4d7ba8ce | 1462 | err = nla_put_u8(msg, RDMA_NLDEV_SYS_ATTR_NETNS_MODE, |
cb7e0e13 PP |
1463 | (u8)ib_devices_shared_netns); |
1464 | if (err) { | |
4d7ba8ce | 1465 | nlmsg_free(msg); |
cb7e0e13 PP |
1466 | return err; |
1467 | } | |
4d7ba8ce PP |
1468 | nlmsg_end(msg, nlh); |
1469 | return rdma_nl_unicast(msg, NETLINK_CB(skb).portid); | |
cb7e0e13 PP |
1470 | } |
1471 | ||
2b34c558 PP |
1472 | static int nldev_set_sys_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, |
1473 | struct netlink_ext_ack *extack) | |
1474 | { | |
1475 | struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | |
1476 | u8 enable; | |
1477 | int err; | |
1478 | ||
1479 | err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, | |
1480 | nldev_policy, extack); | |
1481 | if (err || !tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]) | |
1482 | return -EINVAL; | |
1483 | ||
1484 | enable = nla_get_u8(tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]); | |
1485 | /* Only 0 and 1 are supported */ | |
1486 | if (enable > 1) | |
1487 | return -EINVAL; | |
1488 | ||
1489 | err = rdma_compatdev_set(enable); | |
1490 | return err; | |
1491 | } | |
1492 | ||
d0e312fe | 1493 | static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { |
b4c598a6 | 1494 | [RDMA_NLDEV_CMD_GET] = { |
e5c9469e | 1495 | .doit = nldev_get_doit, |
b4c598a6 LR |
1496 | .dump = nldev_get_dumpit, |
1497 | }, | |
0e2d00eb JG |
1498 | [RDMA_NLDEV_CMD_GET_CHARDEV] = { |
1499 | .doit = nldev_get_chardev, | |
1500 | }, | |
05d940d3 LR |
1501 | [RDMA_NLDEV_CMD_SET] = { |
1502 | .doit = nldev_set_doit, | |
1503 | .flags = RDMA_NL_ADMIN_PERM, | |
1504 | }, | |
3856ec4b SW |
1505 | [RDMA_NLDEV_CMD_NEWLINK] = { |
1506 | .doit = nldev_newlink, | |
1507 | .flags = RDMA_NL_ADMIN_PERM, | |
1508 | }, | |
1509 | [RDMA_NLDEV_CMD_DELLINK] = { | |
1510 | .doit = nldev_dellink, | |
1511 | .flags = RDMA_NL_ADMIN_PERM, | |
1512 | }, | |
7d02f605 | 1513 | [RDMA_NLDEV_CMD_PORT_GET] = { |
c3f66f7b | 1514 | .doit = nldev_port_get_doit, |
7d02f605 LR |
1515 | .dump = nldev_port_get_dumpit, |
1516 | }, | |
bf3c5a93 LR |
1517 | [RDMA_NLDEV_CMD_RES_GET] = { |
1518 | .doit = nldev_res_get_doit, | |
1519 | .dump = nldev_res_get_dumpit, | |
1520 | }, | |
b5fa635a | 1521 | [RDMA_NLDEV_CMD_RES_QP_GET] = { |
c5dfe0ea | 1522 | .doit = nldev_res_get_qp_doit, |
b5fa635a | 1523 | .dump = nldev_res_get_qp_dumpit, |
b5fa635a | 1524 | }, |
00313983 | 1525 | [RDMA_NLDEV_CMD_RES_CM_ID_GET] = { |
c5dfe0ea | 1526 | .doit = nldev_res_get_cm_id_doit, |
00313983 SW |
1527 | .dump = nldev_res_get_cm_id_dumpit, |
1528 | }, | |
a34fc089 | 1529 | [RDMA_NLDEV_CMD_RES_CQ_GET] = { |
c5dfe0ea | 1530 | .doit = nldev_res_get_cq_doit, |
a34fc089 SW |
1531 | .dump = nldev_res_get_cq_dumpit, |
1532 | }, | |
fccec5b8 | 1533 | [RDMA_NLDEV_CMD_RES_MR_GET] = { |
c5dfe0ea | 1534 | .doit = nldev_res_get_mr_doit, |
fccec5b8 SW |
1535 | .dump = nldev_res_get_mr_dumpit, |
1536 | }, | |
29cf1351 | 1537 | [RDMA_NLDEV_CMD_RES_PD_GET] = { |
c5dfe0ea | 1538 | .doit = nldev_res_get_pd_doit, |
29cf1351 SW |
1539 | .dump = nldev_res_get_pd_dumpit, |
1540 | }, | |
cb7e0e13 | 1541 | [RDMA_NLDEV_CMD_SYS_GET] = { |
4d7ba8ce | 1542 | .doit = nldev_sys_get_doit, |
cb7e0e13 | 1543 | }, |
2b34c558 PP |
1544 | [RDMA_NLDEV_CMD_SYS_SET] = { |
1545 | .doit = nldev_set_sys_set_doit, | |
1546 | .flags = RDMA_NL_ADMIN_PERM, | |
1547 | }, | |
b4c598a6 LR |
1548 | }; |
1549 | ||
6c80b41a LR |
1550 | void __init nldev_init(void) |
1551 | { | |
b4c598a6 | 1552 | rdma_nl_register(RDMA_NL_NLDEV, nldev_cb_table); |
6c80b41a LR |
1553 | } |
1554 | ||
1555 | void __exit nldev_exit(void) | |
1556 | { | |
1557 | rdma_nl_unregister(RDMA_NL_NLDEV); | |
1558 | } | |
e3bf14bd JG |
1559 | |
1560 | MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_NLDEV, 5); |