Commit | Line | Data |
---|---|---|
0194621b | 1 | /* |
fe314195 | 2 | * Copyright(c) 2016 Intel Corporation. |
0194621b DD |
3 | * |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
5 | * redistributing this file, you may do so under either license. | |
6 | * | |
7 | * GPL LICENSE SUMMARY | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of version 2 of the GNU General Public License as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * BSD LICENSE | |
19 | * | |
20 | * Redistribution and use in source and binary forms, with or without | |
21 | * modification, are permitted provided that the following conditions | |
22 | * are met: | |
23 | * | |
24 | * - Redistributions of source code must retain the above copyright | |
25 | * notice, this list of conditions and the following disclaimer. | |
26 | * - Redistributions in binary form must reproduce the above copyright | |
27 | * notice, this list of conditions and the following disclaimer in | |
28 | * the documentation and/or other materials provided with the | |
29 | * distribution. | |
30 | * - Neither the name of Intel Corporation nor the names of its | |
31 | * contributors may be used to endorse or promote products derived | |
32 | * from this software without specific prior written permission. | |
33 | * | |
34 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
35 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
36 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
37 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
38 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
39 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
40 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
41 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
42 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
43 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
44 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
45 | * | |
46 | */ | |
47 | ||
48 | #include <linux/module.h> | |
49 | #include <linux/kernel.h> | |
50 | #include "vt.h" | |
81ba39a8 | 51 | #include "trace.h" |
0194621b | 52 | |
182285d0 DD |
53 | #define RVT_UVERBS_ABI_VERSION 2 |
54 | ||
0194621b DD |
55 | MODULE_LICENSE("Dual BSD/GPL"); |
56 | MODULE_DESCRIPTION("RDMA Verbs Transport Library"); | |
57 | ||
58 | static int rvt_init(void) | |
59 | { | |
60 | /* Do any work needed prior to drivers calling for registration*/ | |
61 | return 0; | |
62 | } | |
63 | module_init(rvt_init); | |
64 | ||
65 | static void rvt_cleanup(void) | |
66 | { | |
67 | } | |
68 | module_exit(rvt_cleanup); | |
69 | ||
ff6acd69 DD |
70 | struct rvt_dev_info *rvt_alloc_device(size_t size, int nports) |
71 | { | |
72 | struct rvt_dev_info *rdi = ERR_PTR(-ENOMEM); | |
73 | ||
74 | rdi = (struct rvt_dev_info *)ib_alloc_device(size); | |
75 | if (!rdi) | |
76 | return rdi; | |
77 | ||
78 | rdi->ports = kcalloc(nports, | |
79 | sizeof(struct rvt_ibport **), | |
80 | GFP_KERNEL); | |
81 | if (!rdi->ports) | |
82 | ib_dealloc_device(&rdi->ibdev); | |
83 | ||
84 | return rdi; | |
85 | } | |
86 | EXPORT_SYMBOL(rvt_alloc_device); | |
87 | ||
19ef1edd DD |
88 | static int rvt_query_device(struct ib_device *ibdev, |
89 | struct ib_device_attr *props, | |
90 | struct ib_udata *uhw) | |
91 | { | |
feaeb6e2 HC |
92 | struct rvt_dev_info *rdi = ib_to_rvt(ibdev); |
93 | ||
94 | if (uhw->inlen || uhw->outlen) | |
95 | return -EINVAL; | |
19ef1edd | 96 | /* |
feaeb6e2 | 97 | * Return rvt_dev_info.dparms.props contents |
19ef1edd | 98 | */ |
feaeb6e2 HC |
99 | *props = rdi->dparms.props; |
100 | return 0; | |
19ef1edd DD |
101 | } |
102 | ||
103 | static int rvt_modify_device(struct ib_device *device, | |
104 | int device_modify_mask, | |
105 | struct ib_device_modify *device_modify) | |
106 | { | |
107 | /* | |
108 | * Change dev props. Planned support is for node desc change and sys | |
109 | * guid change only. This matches hfi1 and qib behavior. Other drivers | |
110 | * that support existing modifications will need to add their support. | |
111 | */ | |
112 | ||
113 | /* | |
114 | * VT-DRIVER-API: node_desc_change() | |
115 | * VT-DRIVER-API: sys_guid_change() | |
116 | */ | |
117 | return -EOPNOTSUPP; | |
118 | } | |
119 | ||
765525c1 DD |
120 | /** |
121 | * rvt_query_port: Passes the query port call to the driver | |
122 | * @ibdev: Verbs IB dev | |
123 | * @port: port number | |
124 | * @props: structure to hold returned properties | |
125 | * | |
126 | * Returns 0 on success | |
127 | */ | |
128 | static int rvt_query_port(struct ib_device *ibdev, u8 port, | |
129 | struct ib_port_attr *props) | |
130 | { | |
131 | /* | |
132 | * VT-DRIVER-API: query_port_state() | |
133 | * driver returns pretty much everything in ib_port_attr | |
134 | */ | |
135 | return -EOPNOTSUPP; | |
136 | } | |
137 | ||
138 | /** | |
139 | * rvt_modify_port | |
140 | * @ibdev: Verbs IB dev | |
141 | * @port: Port number | |
142 | * @port_modify_mask: How to change the port | |
143 | * @props: Structure to fill in | |
144 | * | |
145 | * Returns 0 on success | |
146 | */ | |
147 | static int rvt_modify_port(struct ib_device *ibdev, u8 port, | |
148 | int port_modify_mask, struct ib_port_modify *props) | |
149 | { | |
150 | /* | |
151 | * VT-DRIVER-API: set_link_state() | |
152 | * driver will set the link state using the IB enumeration | |
153 | * | |
154 | * VT-DRIVER-API: clear_qkey_violations() | |
155 | * clears driver private qkey counter | |
156 | * | |
157 | * VT-DRIVER-API: get_lid() | |
158 | * driver needs to return the LID | |
159 | * | |
160 | * TBD: send_trap() and post_mad_send() need examined to see where they | |
161 | * fit in. | |
162 | */ | |
163 | return -EOPNOTSUPP; | |
164 | } | |
165 | ||
30588643 DD |
166 | /** |
167 | * rvt_query_pkey - Return a pkey from the table at a given index | |
168 | * @ibdev: Verbs IB dev | |
169 | * @port: Port number | |
170 | * @intex: Index into pkey table | |
171 | * | |
172 | * Returns 0 on failure pkey otherwise | |
173 | */ | |
174 | static int rvt_query_pkey(struct ib_device *ibdev, u8 port, u16 index, | |
175 | u16 *pkey) | |
176 | { | |
177 | /* | |
178 | * Driver will be responsible for keeping rvt_dev_info.pkey_table up to | |
179 | * date. This function will just return that value. There is no need to | |
180 | * lock, if a stale value is read and sent to the user so be it there is | |
181 | * no way to protect against that anyway. | |
182 | */ | |
38ce2c6f DD |
183 | struct rvt_dev_info *rdi = ib_to_rvt(ibdev); |
184 | int port_index; | |
185 | ||
186 | if (index >= rvt_get_npkeys(rdi)) | |
187 | return -EINVAL; | |
188 | ||
189 | port_index = port - 1; /* IB ports start at 1 our array at 0 */ | |
190 | if ((port_index < 0) || (port_index >= rdi->dparms.nports)) | |
191 | return -EINVAL; | |
192 | ||
193 | *pkey = rvt_get_pkey(rdi, port_index, index); | |
30588643 DD |
194 | return 0; |
195 | } | |
196 | ||
2d092e11 DD |
197 | /** |
198 | * rvt_query_gid - Return a gid from the table | |
199 | * @ibdev: Verbs IB dev | |
200 | * @port: Port number | |
201 | * @index: = Index in table | |
202 | * @gid: Gid to return | |
203 | * | |
204 | * Returns 0 on success | |
205 | */ | |
206 | static int rvt_query_gid(struct ib_device *ibdev, u8 port, | |
207 | int index, union ib_gid *gid) | |
208 | { | |
209 | /* | |
210 | * Driver is responsible for updating the guid table. Which will be used | |
211 | * to craft the return value. This will work similar to how query_pkey() | |
212 | * is being done. | |
213 | */ | |
214 | ||
215 | return -EOPNOTSUPP; | |
216 | } | |
217 | ||
6c43cf4b HC |
218 | struct rvt_ucontext { |
219 | struct ib_ucontext ibucontext; | |
220 | }; | |
221 | ||
222 | static inline struct rvt_ucontext *to_iucontext(struct ib_ucontext | |
223 | *ibucontext) | |
224 | { | |
225 | return container_of(ibucontext, struct rvt_ucontext, ibucontext); | |
226 | } | |
227 | ||
c4ed7d8b DD |
228 | /** |
229 | * rvt_alloc_ucontext - Allocate a user context | |
230 | * @ibdev: Vers IB dev | |
231 | * @data: User data allocated | |
232 | */ | |
233 | static struct ib_ucontext *rvt_alloc_ucontext(struct ib_device *ibdev, | |
234 | struct ib_udata *udata) | |
235 | { | |
6c43cf4b HC |
236 | struct rvt_ucontext *context; |
237 | ||
238 | context = kmalloc(sizeof(*context), GFP_KERNEL); | |
239 | if (!context) | |
240 | return ERR_PTR(-ENOMEM); | |
241 | return &context->ibucontext; | |
c4ed7d8b DD |
242 | } |
243 | ||
244 | /** | |
245 | *rvt_dealloc_ucontext - Free a user context | |
246 | *@context - Free this | |
247 | */ | |
248 | static int rvt_dealloc_ucontext(struct ib_ucontext *context) | |
249 | { | |
6c43cf4b HC |
250 | kfree(to_iucontext(context)); |
251 | return 0; | |
c4ed7d8b DD |
252 | } |
253 | ||
e6a8818a DD |
254 | static int rvt_get_port_immutable(struct ib_device *ibdev, u8 port_num, |
255 | struct ib_port_immutable *immutable) | |
256 | { | |
257 | return -EOPNOTSUPP; | |
258 | } | |
259 | ||
4997870a DD |
260 | /* |
261 | * Check driver override. If driver passes a value use it, otherwise we use our | |
262 | * own value. | |
263 | */ | |
264 | #define CHECK_DRIVER_OVERRIDE(rdi, x) \ | |
265 | rdi->ibdev.x = rdi->ibdev.x ? : rvt_ ##x | |
266 | ||
0194621b DD |
267 | int rvt_register_device(struct rvt_dev_info *rdi) |
268 | { | |
b534875d | 269 | /* Validate that drivers have provided the right information */ |
7b1e2099 DD |
270 | int ret = 0; |
271 | ||
0194621b DD |
272 | if (!rdi) |
273 | return -EINVAL; | |
274 | ||
b534875d DD |
275 | if ((!rdi->driver_f.port_callback) || |
276 | (!rdi->driver_f.get_card_name) || | |
119a8e70 KH |
277 | (!rdi->driver_f.get_pci_dev) || |
278 | (!rdi->driver_f.check_ah)) { | |
0acb0cc7 | 279 | pr_err("Driver not supporting req func\n"); |
b534875d DD |
280 | return -EINVAL; |
281 | } | |
282 | ||
81ba39a8 DD |
283 | /* Once we get past here we can use rvt_pr macros and tracepoints */ |
284 | trace_rvt_dbg(rdi, "Driver attempting registration"); | |
822514d7 | 285 | rvt_mmap_init(rdi); |
b534875d | 286 | |
19ef1edd DD |
287 | /* Dev Ops */ |
288 | CHECK_DRIVER_OVERRIDE(rdi, query_device); | |
289 | CHECK_DRIVER_OVERRIDE(rdi, modify_device); | |
765525c1 DD |
290 | CHECK_DRIVER_OVERRIDE(rdi, query_port); |
291 | CHECK_DRIVER_OVERRIDE(rdi, modify_port); | |
30588643 | 292 | CHECK_DRIVER_OVERRIDE(rdi, query_pkey); |
2d092e11 | 293 | CHECK_DRIVER_OVERRIDE(rdi, query_gid); |
c4ed7d8b DD |
294 | CHECK_DRIVER_OVERRIDE(rdi, alloc_ucontext); |
295 | CHECK_DRIVER_OVERRIDE(rdi, dealloc_ucontext); | |
e6a8818a | 296 | CHECK_DRIVER_OVERRIDE(rdi, get_port_immutable); |
19ef1edd | 297 | |
b518d3e6 | 298 | /* Queue Pairs */ |
0acb0cc7 DD |
299 | ret = rvt_driver_qp_init(rdi); |
300 | if (ret) { | |
301 | pr_err("Error in driver QP init.\n"); | |
302 | return -EINVAL; | |
303 | } | |
304 | ||
b518d3e6 DD |
305 | CHECK_DRIVER_OVERRIDE(rdi, create_qp); |
306 | CHECK_DRIVER_OVERRIDE(rdi, modify_qp); | |
307 | CHECK_DRIVER_OVERRIDE(rdi, destroy_qp); | |
308 | CHECK_DRIVER_OVERRIDE(rdi, query_qp); | |
8cf4020b DD |
309 | CHECK_DRIVER_OVERRIDE(rdi, post_send); |
310 | CHECK_DRIVER_OVERRIDE(rdi, post_recv); | |
311 | CHECK_DRIVER_OVERRIDE(rdi, post_srq_recv); | |
b518d3e6 | 312 | |
4c1e4972 DD |
313 | /* Address Handle */ |
314 | CHECK_DRIVER_OVERRIDE(rdi, create_ah); | |
315 | CHECK_DRIVER_OVERRIDE(rdi, destroy_ah); | |
316 | CHECK_DRIVER_OVERRIDE(rdi, modify_ah); | |
317 | CHECK_DRIVER_OVERRIDE(rdi, query_ah); | |
119a8e70 KH |
318 | spin_lock_init(&rdi->n_ahs_lock); |
319 | rdi->n_ahs_allocated = 0; | |
4c1e4972 | 320 | |
aad9158b DD |
321 | /* Shared Receive Queue */ |
322 | CHECK_DRIVER_OVERRIDE(rdi, create_srq); | |
323 | CHECK_DRIVER_OVERRIDE(rdi, modify_srq); | |
324 | CHECK_DRIVER_OVERRIDE(rdi, destroy_srq); | |
325 | CHECK_DRIVER_OVERRIDE(rdi, query_srq); | |
b8f881b9 | 326 | rvt_driver_srq_init(rdi); |
aad9158b | 327 | |
9fa25171 | 328 | /* Multicast */ |
4e74080b | 329 | rvt_driver_mcast_init(rdi); |
9fa25171 DD |
330 | CHECK_DRIVER_OVERRIDE(rdi, attach_mcast); |
331 | CHECK_DRIVER_OVERRIDE(rdi, detach_mcast); | |
332 | ||
2a055eb7 | 333 | /* Mem Region */ |
7b1e2099 DD |
334 | ret = rvt_driver_mr_init(rdi); |
335 | if (ret) { | |
36055a06 | 336 | pr_err("Error in driver MR init.\n"); |
7b1e2099 DD |
337 | goto bail_no_mr; |
338 | } | |
339 | ||
2a055eb7 DD |
340 | CHECK_DRIVER_OVERRIDE(rdi, get_dma_mr); |
341 | CHECK_DRIVER_OVERRIDE(rdi, reg_user_mr); | |
342 | CHECK_DRIVER_OVERRIDE(rdi, dereg_mr); | |
343 | CHECK_DRIVER_OVERRIDE(rdi, alloc_mr); | |
344 | CHECK_DRIVER_OVERRIDE(rdi, alloc_fmr); | |
345 | CHECK_DRIVER_OVERRIDE(rdi, map_phys_fmr); | |
346 | CHECK_DRIVER_OVERRIDE(rdi, unmap_fmr); | |
347 | CHECK_DRIVER_OVERRIDE(rdi, dealloc_fmr); | |
dc21752e | 348 | CHECK_DRIVER_OVERRIDE(rdi, mmap); |
2a055eb7 | 349 | |
cf16335a | 350 | /* Completion queues */ |
6f6387ae DD |
351 | ret = rvt_driver_cq_init(rdi); |
352 | if (ret) { | |
353 | pr_err("Error in driver CQ init.\n"); | |
354 | goto bail_mr; | |
355 | } | |
cf16335a DD |
356 | CHECK_DRIVER_OVERRIDE(rdi, create_cq); |
357 | CHECK_DRIVER_OVERRIDE(rdi, destroy_cq); | |
358 | CHECK_DRIVER_OVERRIDE(rdi, poll_cq); | |
359 | CHECK_DRIVER_OVERRIDE(rdi, req_notify_cq); | |
360 | CHECK_DRIVER_OVERRIDE(rdi, resize_cq); | |
361 | ||
8afd32eb | 362 | /* DMA Operations */ |
c1b332bc DD |
363 | rdi->ibdev.dma_ops = |
364 | rdi->ibdev.dma_ops ? : &rvt_default_dma_mapping_ops; | |
365 | ||
8afd32eb | 366 | /* Protection Domain */ |
4997870a DD |
367 | CHECK_DRIVER_OVERRIDE(rdi, alloc_pd); |
368 | CHECK_DRIVER_OVERRIDE(rdi, dealloc_pd); | |
8afd32eb DD |
369 | spin_lock_init(&rdi->n_pds_lock); |
370 | rdi->n_pds_allocated = 0; | |
371 | ||
182285d0 DD |
372 | /* |
373 | * There are some things which could be set by underlying drivers but | |
374 | * really should be up to rdmavt to set. For instance drivers can't know | |
375 | * exactly which functions rdmavt supports, nor do they know the ABI | |
376 | * version, so we do all of this sort of stuff here. | |
377 | */ | |
378 | rdi->ibdev.uverbs_abi_ver = RVT_UVERBS_ABI_VERSION; | |
379 | rdi->ibdev.uverbs_cmd_mask = | |
380 | (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) | | |
381 | (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) | | |
382 | (1ull << IB_USER_VERBS_CMD_QUERY_PORT) | | |
383 | (1ull << IB_USER_VERBS_CMD_ALLOC_PD) | | |
384 | (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) | | |
385 | (1ull << IB_USER_VERBS_CMD_CREATE_AH) | | |
386 | (1ull << IB_USER_VERBS_CMD_MODIFY_AH) | | |
387 | (1ull << IB_USER_VERBS_CMD_QUERY_AH) | | |
388 | (1ull << IB_USER_VERBS_CMD_DESTROY_AH) | | |
389 | (1ull << IB_USER_VERBS_CMD_REG_MR) | | |
390 | (1ull << IB_USER_VERBS_CMD_DEREG_MR) | | |
391 | (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) | | |
392 | (1ull << IB_USER_VERBS_CMD_CREATE_CQ) | | |
393 | (1ull << IB_USER_VERBS_CMD_RESIZE_CQ) | | |
394 | (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) | | |
395 | (1ull << IB_USER_VERBS_CMD_POLL_CQ) | | |
396 | (1ull << IB_USER_VERBS_CMD_REQ_NOTIFY_CQ) | | |
397 | (1ull << IB_USER_VERBS_CMD_CREATE_QP) | | |
398 | (1ull << IB_USER_VERBS_CMD_QUERY_QP) | | |
399 | (1ull << IB_USER_VERBS_CMD_MODIFY_QP) | | |
400 | (1ull << IB_USER_VERBS_CMD_DESTROY_QP) | | |
401 | (1ull << IB_USER_VERBS_CMD_POST_SEND) | | |
402 | (1ull << IB_USER_VERBS_CMD_POST_RECV) | | |
403 | (1ull << IB_USER_VERBS_CMD_ATTACH_MCAST) | | |
404 | (1ull << IB_USER_VERBS_CMD_DETACH_MCAST) | | |
405 | (1ull << IB_USER_VERBS_CMD_CREATE_SRQ) | | |
406 | (1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) | | |
407 | (1ull << IB_USER_VERBS_CMD_QUERY_SRQ) | | |
408 | (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) | | |
409 | (1ull << IB_USER_VERBS_CMD_POST_SRQ_RECV); | |
410 | rdi->ibdev.node_type = RDMA_NODE_IB_CA; | |
411 | rdi->ibdev.num_comp_vectors = 1; | |
412 | ||
7b1e2099 DD |
413 | /* We are now good to announce we exist */ |
414 | ret = ib_register_device(&rdi->ibdev, rdi->driver_f.port_callback); | |
415 | if (ret) { | |
416 | rvt_pr_err(rdi, "Failed to register driver with ib core.\n"); | |
6f6387ae | 417 | goto bail_cq; |
7b1e2099 DD |
418 | } |
419 | ||
3711baf2 DD |
420 | rvt_create_mad_agents(rdi); |
421 | ||
b534875d | 422 | rvt_pr_info(rdi, "Registration with rdmavt done.\n"); |
7b1e2099 | 423 | return ret; |
aec57787 | 424 | |
6f6387ae DD |
425 | bail_cq: |
426 | rvt_cq_exit(rdi); | |
427 | ||
7b1e2099 DD |
428 | bail_mr: |
429 | rvt_mr_exit(rdi); | |
430 | ||
431 | bail_no_mr: | |
0acb0cc7 DD |
432 | rvt_qp_exit(rdi); |
433 | ||
7b1e2099 | 434 | return ret; |
0194621b DD |
435 | } |
436 | EXPORT_SYMBOL(rvt_register_device); | |
437 | ||
438 | void rvt_unregister_device(struct rvt_dev_info *rdi) | |
439 | { | |
81ba39a8 | 440 | trace_rvt_dbg(rdi, "Driver is unregistering."); |
0194621b DD |
441 | if (!rdi) |
442 | return; | |
443 | ||
3711baf2 DD |
444 | rvt_free_mad_agents(rdi); |
445 | ||
0194621b | 446 | ib_unregister_device(&rdi->ibdev); |
6f6387ae | 447 | rvt_cq_exit(rdi); |
7b1e2099 | 448 | rvt_mr_exit(rdi); |
515667f8 | 449 | rvt_qp_exit(rdi); |
0194621b DD |
450 | } |
451 | EXPORT_SYMBOL(rvt_unregister_device); | |
f3d01bbc DD |
452 | |
453 | /* | |
454 | * Keep track of a list of ports. No need to have a detach port. | |
455 | * They persist until the driver goes away. | |
456 | */ | |
38ce2c6f DD |
457 | int rvt_init_port(struct rvt_dev_info *rdi, struct rvt_ibport *port, |
458 | int portnum, u16 *pkey_table) | |
f3d01bbc | 459 | { |
38ce2c6f | 460 | |
f3d01bbc | 461 | rdi->ports[portnum] = port; |
38ce2c6f DD |
462 | rdi->ports[portnum]->pkey_table = pkey_table; |
463 | ||
464 | return 0; | |
f3d01bbc | 465 | } |
38ce2c6f | 466 | EXPORT_SYMBOL(rvt_init_port); |