diff options
-rw-r--r-- | drivers/infiniband/core/uverbs.h | 132 | ||||
-rw-r--r-- | drivers/infiniband/core/uverbs_cmd.c | 1006 | ||||
-rw-r--r-- | drivers/infiniband/core/uverbs_main.c | 698 |
3 files changed, 1836 insertions, 0 deletions
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h new file mode 100644 index 000000000000..57347f1e82c1 --- /dev/null +++ b/drivers/infiniband/core/uverbs.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Topspin Communications. All rights reserved. | ||
3 | * Copyright (c) 2005 Cisco Systems. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | * | ||
33 | * $Id: uverbs.h 2559 2005-06-06 19:43:16Z roland $ | ||
34 | */ | ||
35 | |||
36 | #ifndef UVERBS_H | ||
37 | #define UVERBS_H | ||
38 | |||
39 | /* Include device.h and fs.h until cdev.h is self-sufficient */ | ||
40 | #include <linux/fs.h> | ||
41 | #include <linux/device.h> | ||
42 | #include <linux/cdev.h> | ||
43 | #include <linux/kref.h> | ||
44 | #include <linux/idr.h> | ||
45 | |||
46 | #include <ib_verbs.h> | ||
47 | #include <ib_user_verbs.h> | ||
48 | |||
49 | struct ib_uverbs_device { | ||
50 | int devnum; | ||
51 | struct cdev dev; | ||
52 | struct class_device class_dev; | ||
53 | struct ib_device *ib_dev; | ||
54 | int num_comp; | ||
55 | }; | ||
56 | |||
57 | struct ib_uverbs_event_file { | ||
58 | struct kref ref; | ||
59 | struct ib_uverbs_file *uverbs_file; | ||
60 | spinlock_t lock; | ||
61 | int fd; | ||
62 | int is_async; | ||
63 | wait_queue_head_t poll_wait; | ||
64 | struct list_head event_list; | ||
65 | }; | ||
66 | |||
67 | struct ib_uverbs_file { | ||
68 | struct kref ref; | ||
69 | struct ib_uverbs_device *device; | ||
70 | struct ib_ucontext *ucontext; | ||
71 | struct ib_event_handler event_handler; | ||
72 | struct ib_uverbs_event_file async_file; | ||
73 | struct ib_uverbs_event_file comp_file[1]; | ||
74 | }; | ||
75 | |||
76 | struct ib_uverbs_async_event { | ||
77 | struct ib_uverbs_async_event_desc desc; | ||
78 | struct list_head list; | ||
79 | }; | ||
80 | |||
81 | struct ib_uverbs_comp_event { | ||
82 | struct ib_uverbs_comp_event_desc desc; | ||
83 | struct list_head list; | ||
84 | }; | ||
85 | |||
86 | struct ib_uobject_mr { | ||
87 | struct ib_uobject uobj; | ||
88 | struct page *page_list; | ||
89 | struct scatterlist *sg_list; | ||
90 | }; | ||
91 | |||
92 | extern struct semaphore ib_uverbs_idr_mutex; | ||
93 | extern struct idr ib_uverbs_pd_idr; | ||
94 | extern struct idr ib_uverbs_mr_idr; | ||
95 | extern struct idr ib_uverbs_mw_idr; | ||
96 | extern struct idr ib_uverbs_ah_idr; | ||
97 | extern struct idr ib_uverbs_cq_idr; | ||
98 | extern struct idr ib_uverbs_qp_idr; | ||
99 | |||
100 | void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context); | ||
101 | void ib_uverbs_cq_event_handler(struct ib_event *event, void *context_ptr); | ||
102 | void ib_uverbs_qp_event_handler(struct ib_event *event, void *context_ptr); | ||
103 | |||
104 | int ib_umem_get(struct ib_device *dev, struct ib_umem *mem, | ||
105 | void *addr, size_t size, int write); | ||
106 | void ib_umem_release(struct ib_device *dev, struct ib_umem *umem); | ||
107 | void ib_umem_release_on_close(struct ib_device *dev, struct ib_umem *umem); | ||
108 | |||
109 | #define IB_UVERBS_DECLARE_CMD(name) \ | ||
110 | ssize_t ib_uverbs_##name(struct ib_uverbs_file *file, \ | ||
111 | const char __user *buf, int in_len, \ | ||
112 | int out_len) | ||
113 | |||
114 | IB_UVERBS_DECLARE_CMD(query_params); | ||
115 | IB_UVERBS_DECLARE_CMD(get_context); | ||
116 | IB_UVERBS_DECLARE_CMD(query_device); | ||
117 | IB_UVERBS_DECLARE_CMD(query_port); | ||
118 | IB_UVERBS_DECLARE_CMD(query_gid); | ||
119 | IB_UVERBS_DECLARE_CMD(query_pkey); | ||
120 | IB_UVERBS_DECLARE_CMD(alloc_pd); | ||
121 | IB_UVERBS_DECLARE_CMD(dealloc_pd); | ||
122 | IB_UVERBS_DECLARE_CMD(reg_mr); | ||
123 | IB_UVERBS_DECLARE_CMD(dereg_mr); | ||
124 | IB_UVERBS_DECLARE_CMD(create_cq); | ||
125 | IB_UVERBS_DECLARE_CMD(destroy_cq); | ||
126 | IB_UVERBS_DECLARE_CMD(create_qp); | ||
127 | IB_UVERBS_DECLARE_CMD(modify_qp); | ||
128 | IB_UVERBS_DECLARE_CMD(destroy_qp); | ||
129 | IB_UVERBS_DECLARE_CMD(attach_mcast); | ||
130 | IB_UVERBS_DECLARE_CMD(detach_mcast); | ||
131 | |||
132 | #endif /* UVERBS_H */ | ||
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c new file mode 100644 index 000000000000..5f2bbcda4c73 --- /dev/null +++ b/drivers/infiniband/core/uverbs_cmd.c | |||
@@ -0,0 +1,1006 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Topspin Communications. All rights reserved. | ||
3 | * Copyright (c) 2005 Cisco Systems. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | * | ||
33 | * $Id: uverbs_cmd.c 2708 2005-06-24 17:27:21Z roland $ | ||
34 | */ | ||
35 | |||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #include "uverbs.h" | ||
39 | |||
40 | #define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \ | ||
41 | do { \ | ||
42 | (udata)->inbuf = (void __user *) (ibuf); \ | ||
43 | (udata)->outbuf = (void __user *) (obuf); \ | ||
44 | (udata)->inlen = (ilen); \ | ||
45 | (udata)->outlen = (olen); \ | ||
46 | } while (0) | ||
47 | |||
48 | ssize_t ib_uverbs_query_params(struct ib_uverbs_file *file, | ||
49 | const char __user *buf, | ||
50 | int in_len, int out_len) | ||
51 | { | ||
52 | struct ib_uverbs_query_params cmd; | ||
53 | struct ib_uverbs_query_params_resp resp; | ||
54 | |||
55 | if (out_len < sizeof resp) | ||
56 | return -ENOSPC; | ||
57 | |||
58 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
59 | return -EFAULT; | ||
60 | |||
61 | memset(&resp, 0, sizeof resp); | ||
62 | |||
63 | resp.num_cq_events = file->device->num_comp; | ||
64 | |||
65 | if (copy_to_user((void __user *) (unsigned long) cmd.response, &resp, sizeof resp)) | ||
66 | return -EFAULT; | ||
67 | |||
68 | return in_len; | ||
69 | } | ||
70 | |||
71 | ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, | ||
72 | const char __user *buf, | ||
73 | int in_len, int out_len) | ||
74 | { | ||
75 | struct ib_uverbs_get_context cmd; | ||
76 | struct ib_uverbs_get_context_resp resp; | ||
77 | struct ib_udata udata; | ||
78 | struct ib_device *ibdev = file->device->ib_dev; | ||
79 | int i; | ||
80 | int ret = in_len; | ||
81 | |||
82 | if (out_len < sizeof resp) | ||
83 | return -ENOSPC; | ||
84 | |||
85 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
86 | return -EFAULT; | ||
87 | |||
88 | INIT_UDATA(&udata, buf + sizeof cmd, | ||
89 | (unsigned long) cmd.response + sizeof resp, | ||
90 | in_len - sizeof cmd, out_len - sizeof resp); | ||
91 | |||
92 | file->ucontext = ibdev->alloc_ucontext(ibdev, &udata); | ||
93 | if (IS_ERR(file->ucontext)) { | ||
94 | ret = PTR_ERR(file->ucontext); | ||
95 | file->ucontext = NULL; | ||
96 | return ret; | ||
97 | } | ||
98 | |||
99 | file->ucontext->device = ibdev; | ||
100 | INIT_LIST_HEAD(&file->ucontext->pd_list); | ||
101 | INIT_LIST_HEAD(&file->ucontext->mr_list); | ||
102 | INIT_LIST_HEAD(&file->ucontext->mw_list); | ||
103 | INIT_LIST_HEAD(&file->ucontext->cq_list); | ||
104 | INIT_LIST_HEAD(&file->ucontext->qp_list); | ||
105 | INIT_LIST_HEAD(&file->ucontext->srq_list); | ||
106 | INIT_LIST_HEAD(&file->ucontext->ah_list); | ||
107 | spin_lock_init(&file->ucontext->lock); | ||
108 | |||
109 | resp.async_fd = file->async_file.fd; | ||
110 | for (i = 0; i < file->device->num_comp; ++i) | ||
111 | if (copy_to_user((void __user *) (unsigned long) cmd.cq_fd_tab + | ||
112 | i * sizeof (__u32), | ||
113 | &file->comp_file[i].fd, sizeof (__u32))) | ||
114 | goto err; | ||
115 | |||
116 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
117 | &resp, sizeof resp)) | ||
118 | goto err; | ||
119 | |||
120 | return in_len; | ||
121 | |||
122 | err: | ||
123 | ibdev->dealloc_ucontext(file->ucontext); | ||
124 | file->ucontext = NULL; | ||
125 | |||
126 | return -EFAULT; | ||
127 | } | ||
128 | |||
129 | ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file, | ||
130 | const char __user *buf, | ||
131 | int in_len, int out_len) | ||
132 | { | ||
133 | struct ib_uverbs_query_device cmd; | ||
134 | struct ib_uverbs_query_device_resp resp; | ||
135 | struct ib_device_attr attr; | ||
136 | int ret; | ||
137 | |||
138 | if (out_len < sizeof resp) | ||
139 | return -ENOSPC; | ||
140 | |||
141 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
142 | return -EFAULT; | ||
143 | |||
144 | ret = ib_query_device(file->device->ib_dev, &attr); | ||
145 | if (ret) | ||
146 | return ret; | ||
147 | |||
148 | memset(&resp, 0, sizeof resp); | ||
149 | |||
150 | resp.fw_ver = attr.fw_ver; | ||
151 | resp.node_guid = attr.node_guid; | ||
152 | resp.sys_image_guid = attr.sys_image_guid; | ||
153 | resp.max_mr_size = attr.max_mr_size; | ||
154 | resp.page_size_cap = attr.page_size_cap; | ||
155 | resp.vendor_id = attr.vendor_id; | ||
156 | resp.vendor_part_id = attr.vendor_part_id; | ||
157 | resp.hw_ver = attr.hw_ver; | ||
158 | resp.max_qp = attr.max_qp; | ||
159 | resp.max_qp_wr = attr.max_qp_wr; | ||
160 | resp.device_cap_flags = attr.device_cap_flags; | ||
161 | resp.max_sge = attr.max_sge; | ||
162 | resp.max_sge_rd = attr.max_sge_rd; | ||
163 | resp.max_cq = attr.max_cq; | ||
164 | resp.max_cqe = attr.max_cqe; | ||
165 | resp.max_mr = attr.max_mr; | ||
166 | resp.max_pd = attr.max_pd; | ||
167 | resp.max_qp_rd_atom = attr.max_qp_rd_atom; | ||
168 | resp.max_ee_rd_atom = attr.max_ee_rd_atom; | ||
169 | resp.max_res_rd_atom = attr.max_res_rd_atom; | ||
170 | resp.max_qp_init_rd_atom = attr.max_qp_init_rd_atom; | ||
171 | resp.max_ee_init_rd_atom = attr.max_ee_init_rd_atom; | ||
172 | resp.atomic_cap = attr.atomic_cap; | ||
173 | resp.max_ee = attr.max_ee; | ||
174 | resp.max_rdd = attr.max_rdd; | ||
175 | resp.max_mw = attr.max_mw; | ||
176 | resp.max_raw_ipv6_qp = attr.max_raw_ipv6_qp; | ||
177 | resp.max_raw_ethy_qp = attr.max_raw_ethy_qp; | ||
178 | resp.max_mcast_grp = attr.max_mcast_grp; | ||
179 | resp.max_mcast_qp_attach = attr.max_mcast_qp_attach; | ||
180 | resp.max_total_mcast_qp_attach = attr.max_total_mcast_qp_attach; | ||
181 | resp.max_ah = attr.max_ah; | ||
182 | resp.max_fmr = attr.max_fmr; | ||
183 | resp.max_map_per_fmr = attr.max_map_per_fmr; | ||
184 | resp.max_srq = attr.max_srq; | ||
185 | resp.max_srq_wr = attr.max_srq_wr; | ||
186 | resp.max_srq_sge = attr.max_srq_sge; | ||
187 | resp.max_pkeys = attr.max_pkeys; | ||
188 | resp.local_ca_ack_delay = attr.local_ca_ack_delay; | ||
189 | resp.phys_port_cnt = file->device->ib_dev->phys_port_cnt; | ||
190 | |||
191 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
192 | &resp, sizeof resp)) | ||
193 | return -EFAULT; | ||
194 | |||
195 | return in_len; | ||
196 | } | ||
197 | |||
198 | ssize_t ib_uverbs_query_port(struct ib_uverbs_file *file, | ||
199 | const char __user *buf, | ||
200 | int in_len, int out_len) | ||
201 | { | ||
202 | struct ib_uverbs_query_port cmd; | ||
203 | struct ib_uverbs_query_port_resp resp; | ||
204 | struct ib_port_attr attr; | ||
205 | int ret; | ||
206 | |||
207 | if (out_len < sizeof resp) | ||
208 | return -ENOSPC; | ||
209 | |||
210 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
211 | return -EFAULT; | ||
212 | |||
213 | ret = ib_query_port(file->device->ib_dev, cmd.port_num, &attr); | ||
214 | if (ret) | ||
215 | return ret; | ||
216 | |||
217 | memset(&resp, 0, sizeof resp); | ||
218 | |||
219 | resp.state = attr.state; | ||
220 | resp.max_mtu = attr.max_mtu; | ||
221 | resp.active_mtu = attr.active_mtu; | ||
222 | resp.gid_tbl_len = attr.gid_tbl_len; | ||
223 | resp.port_cap_flags = attr.port_cap_flags; | ||
224 | resp.max_msg_sz = attr.max_msg_sz; | ||
225 | resp.bad_pkey_cntr = attr.bad_pkey_cntr; | ||
226 | resp.qkey_viol_cntr = attr.qkey_viol_cntr; | ||
227 | resp.pkey_tbl_len = attr.pkey_tbl_len; | ||
228 | resp.lid = attr.lid; | ||
229 | resp.sm_lid = attr.sm_lid; | ||
230 | resp.lmc = attr.lmc; | ||
231 | resp.max_vl_num = attr.max_vl_num; | ||
232 | resp.sm_sl = attr.sm_sl; | ||
233 | resp.subnet_timeout = attr.subnet_timeout; | ||
234 | resp.init_type_reply = attr.init_type_reply; | ||
235 | resp.active_width = attr.active_width; | ||
236 | resp.active_speed = attr.active_speed; | ||
237 | resp.phys_state = attr.phys_state; | ||
238 | |||
239 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
240 | &resp, sizeof resp)) | ||
241 | return -EFAULT; | ||
242 | |||
243 | return in_len; | ||
244 | } | ||
245 | |||
246 | ssize_t ib_uverbs_query_gid(struct ib_uverbs_file *file, | ||
247 | const char __user *buf, | ||
248 | int in_len, int out_len) | ||
249 | { | ||
250 | struct ib_uverbs_query_gid cmd; | ||
251 | struct ib_uverbs_query_gid_resp resp; | ||
252 | int ret; | ||
253 | |||
254 | if (out_len < sizeof resp) | ||
255 | return -ENOSPC; | ||
256 | |||
257 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
258 | return -EFAULT; | ||
259 | |||
260 | memset(&resp, 0, sizeof resp); | ||
261 | |||
262 | ret = ib_query_gid(file->device->ib_dev, cmd.port_num, cmd.index, | ||
263 | (union ib_gid *) resp.gid); | ||
264 | if (ret) | ||
265 | return ret; | ||
266 | |||
267 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
268 | &resp, sizeof resp)) | ||
269 | return -EFAULT; | ||
270 | |||
271 | return in_len; | ||
272 | } | ||
273 | |||
274 | ssize_t ib_uverbs_query_pkey(struct ib_uverbs_file *file, | ||
275 | const char __user *buf, | ||
276 | int in_len, int out_len) | ||
277 | { | ||
278 | struct ib_uverbs_query_pkey cmd; | ||
279 | struct ib_uverbs_query_pkey_resp resp; | ||
280 | int ret; | ||
281 | |||
282 | if (out_len < sizeof resp) | ||
283 | return -ENOSPC; | ||
284 | |||
285 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
286 | return -EFAULT; | ||
287 | |||
288 | memset(&resp, 0, sizeof resp); | ||
289 | |||
290 | ret = ib_query_pkey(file->device->ib_dev, cmd.port_num, cmd.index, | ||
291 | &resp.pkey); | ||
292 | if (ret) | ||
293 | return ret; | ||
294 | |||
295 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
296 | &resp, sizeof resp)) | ||
297 | return -EFAULT; | ||
298 | |||
299 | return in_len; | ||
300 | } | ||
301 | |||
302 | ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file, | ||
303 | const char __user *buf, | ||
304 | int in_len, int out_len) | ||
305 | { | ||
306 | struct ib_uverbs_alloc_pd cmd; | ||
307 | struct ib_uverbs_alloc_pd_resp resp; | ||
308 | struct ib_udata udata; | ||
309 | struct ib_uobject *uobj; | ||
310 | struct ib_pd *pd; | ||
311 | int ret; | ||
312 | |||
313 | if (out_len < sizeof resp) | ||
314 | return -ENOSPC; | ||
315 | |||
316 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
317 | return -EFAULT; | ||
318 | |||
319 | INIT_UDATA(&udata, buf + sizeof cmd, | ||
320 | (unsigned long) cmd.response + sizeof resp, | ||
321 | in_len - sizeof cmd, out_len - sizeof resp); | ||
322 | |||
323 | uobj = kmalloc(sizeof *uobj, GFP_KERNEL); | ||
324 | if (!uobj) | ||
325 | return -ENOMEM; | ||
326 | |||
327 | uobj->context = file->ucontext; | ||
328 | |||
329 | pd = file->device->ib_dev->alloc_pd(file->device->ib_dev, | ||
330 | file->ucontext, &udata); | ||
331 | if (IS_ERR(pd)) { | ||
332 | ret = PTR_ERR(pd); | ||
333 | goto err; | ||
334 | } | ||
335 | |||
336 | pd->device = file->device->ib_dev; | ||
337 | pd->uobject = uobj; | ||
338 | atomic_set(&pd->usecnt, 0); | ||
339 | |||
340 | retry: | ||
341 | if (!idr_pre_get(&ib_uverbs_pd_idr, GFP_KERNEL)) { | ||
342 | ret = -ENOMEM; | ||
343 | goto err_pd; | ||
344 | } | ||
345 | |||
346 | down(&ib_uverbs_idr_mutex); | ||
347 | ret = idr_get_new(&ib_uverbs_pd_idr, pd, &uobj->id); | ||
348 | up(&ib_uverbs_idr_mutex); | ||
349 | |||
350 | if (ret == -EAGAIN) | ||
351 | goto retry; | ||
352 | if (ret) | ||
353 | goto err_pd; | ||
354 | |||
355 | spin_lock_irq(&file->ucontext->lock); | ||
356 | list_add_tail(&uobj->list, &file->ucontext->pd_list); | ||
357 | spin_unlock_irq(&file->ucontext->lock); | ||
358 | |||
359 | memset(&resp, 0, sizeof resp); | ||
360 | resp.pd_handle = uobj->id; | ||
361 | |||
362 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
363 | &resp, sizeof resp)) { | ||
364 | ret = -EFAULT; | ||
365 | goto err_list; | ||
366 | } | ||
367 | |||
368 | return in_len; | ||
369 | |||
370 | err_list: | ||
371 | spin_lock_irq(&file->ucontext->lock); | ||
372 | list_del(&uobj->list); | ||
373 | spin_unlock_irq(&file->ucontext->lock); | ||
374 | |||
375 | down(&ib_uverbs_idr_mutex); | ||
376 | idr_remove(&ib_uverbs_pd_idr, uobj->id); | ||
377 | up(&ib_uverbs_idr_mutex); | ||
378 | |||
379 | err_pd: | ||
380 | ib_dealloc_pd(pd); | ||
381 | |||
382 | err: | ||
383 | kfree(uobj); | ||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file, | ||
388 | const char __user *buf, | ||
389 | int in_len, int out_len) | ||
390 | { | ||
391 | struct ib_uverbs_dealloc_pd cmd; | ||
392 | struct ib_pd *pd; | ||
393 | struct ib_uobject *uobj; | ||
394 | int ret = -EINVAL; | ||
395 | |||
396 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
397 | return -EFAULT; | ||
398 | |||
399 | down(&ib_uverbs_idr_mutex); | ||
400 | |||
401 | pd = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle); | ||
402 | if (!pd || pd->uobject->context != file->ucontext) | ||
403 | goto out; | ||
404 | |||
405 | uobj = pd->uobject; | ||
406 | |||
407 | ret = ib_dealloc_pd(pd); | ||
408 | if (ret) | ||
409 | goto out; | ||
410 | |||
411 | idr_remove(&ib_uverbs_pd_idr, cmd.pd_handle); | ||
412 | |||
413 | spin_lock_irq(&file->ucontext->lock); | ||
414 | list_del(&uobj->list); | ||
415 | spin_unlock_irq(&file->ucontext->lock); | ||
416 | |||
417 | kfree(uobj); | ||
418 | |||
419 | out: | ||
420 | up(&ib_uverbs_idr_mutex); | ||
421 | |||
422 | return ret ? ret : in_len; | ||
423 | } | ||
424 | |||
425 | ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, | ||
426 | const char __user *buf, int in_len, | ||
427 | int out_len) | ||
428 | { | ||
429 | struct ib_uverbs_reg_mr cmd; | ||
430 | struct ib_uverbs_reg_mr_resp resp; | ||
431 | struct ib_udata udata; | ||
432 | struct ib_umem_object *obj; | ||
433 | struct ib_pd *pd; | ||
434 | struct ib_mr *mr; | ||
435 | int ret; | ||
436 | |||
437 | if (out_len < sizeof resp) | ||
438 | return -ENOSPC; | ||
439 | |||
440 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
441 | return -EFAULT; | ||
442 | |||
443 | INIT_UDATA(&udata, buf + sizeof cmd, | ||
444 | (unsigned long) cmd.response + sizeof resp, | ||
445 | in_len - sizeof cmd, out_len - sizeof resp); | ||
446 | |||
447 | if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK)) | ||
448 | return -EINVAL; | ||
449 | |||
450 | obj = kmalloc(sizeof *obj, GFP_KERNEL); | ||
451 | if (!obj) | ||
452 | return -ENOMEM; | ||
453 | |||
454 | obj->uobject.context = file->ucontext; | ||
455 | |||
456 | /* | ||
457 | * We ask for writable memory if any access flags other than | ||
458 | * "remote read" are set. "Local write" and "remote write" | ||
459 | * obviously require write access. "Remote atomic" can do | ||
460 | * things like fetch and add, which will modify memory, and | ||
461 | * "MW bind" can change permissions by binding a window. | ||
462 | */ | ||
463 | ret = ib_umem_get(file->device->ib_dev, &obj->umem, | ||
464 | (void *) (unsigned long) cmd.start, cmd.length, | ||
465 | !!(cmd.access_flags & ~IB_ACCESS_REMOTE_READ)); | ||
466 | if (ret) | ||
467 | goto err_free; | ||
468 | |||
469 | obj->umem.virt_base = cmd.hca_va; | ||
470 | |||
471 | down(&ib_uverbs_idr_mutex); | ||
472 | |||
473 | pd = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle); | ||
474 | if (!pd || pd->uobject->context != file->ucontext) { | ||
475 | ret = -EINVAL; | ||
476 | goto err_up; | ||
477 | } | ||
478 | |||
479 | if (!pd->device->reg_user_mr) { | ||
480 | ret = -ENOSYS; | ||
481 | goto err_up; | ||
482 | } | ||
483 | |||
484 | mr = pd->device->reg_user_mr(pd, &obj->umem, cmd.access_flags, &udata); | ||
485 | if (IS_ERR(mr)) { | ||
486 | ret = PTR_ERR(mr); | ||
487 | goto err_up; | ||
488 | } | ||
489 | |||
490 | mr->device = pd->device; | ||
491 | mr->pd = pd; | ||
492 | mr->uobject = &obj->uobject; | ||
493 | atomic_inc(&pd->usecnt); | ||
494 | atomic_set(&mr->usecnt, 0); | ||
495 | |||
496 | memset(&resp, 0, sizeof resp); | ||
497 | resp.lkey = mr->lkey; | ||
498 | resp.rkey = mr->rkey; | ||
499 | |||
500 | retry: | ||
501 | if (!idr_pre_get(&ib_uverbs_mr_idr, GFP_KERNEL)) { | ||
502 | ret = -ENOMEM; | ||
503 | goto err_unreg; | ||
504 | } | ||
505 | |||
506 | ret = idr_get_new(&ib_uverbs_mr_idr, mr, &obj->uobject.id); | ||
507 | |||
508 | if (ret == -EAGAIN) | ||
509 | goto retry; | ||
510 | if (ret) | ||
511 | goto err_unreg; | ||
512 | |||
513 | resp.mr_handle = obj->uobject.id; | ||
514 | |||
515 | spin_lock_irq(&file->ucontext->lock); | ||
516 | list_add_tail(&obj->uobject.list, &file->ucontext->mr_list); | ||
517 | spin_unlock_irq(&file->ucontext->lock); | ||
518 | |||
519 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
520 | &resp, sizeof resp)) { | ||
521 | ret = -EFAULT; | ||
522 | goto err_list; | ||
523 | } | ||
524 | |||
525 | up(&ib_uverbs_idr_mutex); | ||
526 | |||
527 | return in_len; | ||
528 | |||
529 | err_list: | ||
530 | spin_lock_irq(&file->ucontext->lock); | ||
531 | list_del(&obj->uobject.list); | ||
532 | spin_unlock_irq(&file->ucontext->lock); | ||
533 | |||
534 | err_unreg: | ||
535 | ib_dereg_mr(mr); | ||
536 | |||
537 | err_up: | ||
538 | up(&ib_uverbs_idr_mutex); | ||
539 | |||
540 | ib_umem_release(file->device->ib_dev, &obj->umem); | ||
541 | |||
542 | err_free: | ||
543 | kfree(obj); | ||
544 | return ret; | ||
545 | } | ||
546 | |||
547 | ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file, | ||
548 | const char __user *buf, int in_len, | ||
549 | int out_len) | ||
550 | { | ||
551 | struct ib_uverbs_dereg_mr cmd; | ||
552 | struct ib_mr *mr; | ||
553 | struct ib_umem_object *memobj; | ||
554 | int ret = -EINVAL; | ||
555 | |||
556 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
557 | return -EFAULT; | ||
558 | |||
559 | down(&ib_uverbs_idr_mutex); | ||
560 | |||
561 | mr = idr_find(&ib_uverbs_mr_idr, cmd.mr_handle); | ||
562 | if (!mr || mr->uobject->context != file->ucontext) | ||
563 | goto out; | ||
564 | |||
565 | memobj = container_of(mr->uobject, struct ib_umem_object, uobject); | ||
566 | |||
567 | ret = ib_dereg_mr(mr); | ||
568 | if (ret) | ||
569 | goto out; | ||
570 | |||
571 | idr_remove(&ib_uverbs_mr_idr, cmd.mr_handle); | ||
572 | |||
573 | spin_lock_irq(&file->ucontext->lock); | ||
574 | list_del(&memobj->uobject.list); | ||
575 | spin_unlock_irq(&file->ucontext->lock); | ||
576 | |||
577 | ib_umem_release(file->device->ib_dev, &memobj->umem); | ||
578 | kfree(memobj); | ||
579 | |||
580 | out: | ||
581 | up(&ib_uverbs_idr_mutex); | ||
582 | |||
583 | return ret ? ret : in_len; | ||
584 | } | ||
585 | |||
586 | ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file, | ||
587 | const char __user *buf, int in_len, | ||
588 | int out_len) | ||
589 | { | ||
590 | struct ib_uverbs_create_cq cmd; | ||
591 | struct ib_uverbs_create_cq_resp resp; | ||
592 | struct ib_udata udata; | ||
593 | struct ib_uobject *uobj; | ||
594 | struct ib_cq *cq; | ||
595 | int ret; | ||
596 | |||
597 | if (out_len < sizeof resp) | ||
598 | return -ENOSPC; | ||
599 | |||
600 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
601 | return -EFAULT; | ||
602 | |||
603 | INIT_UDATA(&udata, buf + sizeof cmd, | ||
604 | (unsigned long) cmd.response + sizeof resp, | ||
605 | in_len - sizeof cmd, out_len - sizeof resp); | ||
606 | |||
607 | if (cmd.event_handler >= file->device->num_comp) | ||
608 | return -EINVAL; | ||
609 | |||
610 | uobj = kmalloc(sizeof *uobj, GFP_KERNEL); | ||
611 | if (!uobj) | ||
612 | return -ENOMEM; | ||
613 | |||
614 | uobj->user_handle = cmd.user_handle; | ||
615 | uobj->context = file->ucontext; | ||
616 | |||
617 | cq = file->device->ib_dev->create_cq(file->device->ib_dev, cmd.cqe, | ||
618 | file->ucontext, &udata); | ||
619 | if (IS_ERR(cq)) { | ||
620 | ret = PTR_ERR(cq); | ||
621 | goto err; | ||
622 | } | ||
623 | |||
624 | cq->device = file->device->ib_dev; | ||
625 | cq->uobject = uobj; | ||
626 | cq->comp_handler = ib_uverbs_comp_handler; | ||
627 | cq->event_handler = ib_uverbs_cq_event_handler; | ||
628 | cq->cq_context = file; | ||
629 | atomic_set(&cq->usecnt, 0); | ||
630 | |||
631 | retry: | ||
632 | if (!idr_pre_get(&ib_uverbs_cq_idr, GFP_KERNEL)) { | ||
633 | ret = -ENOMEM; | ||
634 | goto err_cq; | ||
635 | } | ||
636 | |||
637 | down(&ib_uverbs_idr_mutex); | ||
638 | ret = idr_get_new(&ib_uverbs_cq_idr, cq, &uobj->id); | ||
639 | up(&ib_uverbs_idr_mutex); | ||
640 | |||
641 | if (ret == -EAGAIN) | ||
642 | goto retry; | ||
643 | if (ret) | ||
644 | goto err_cq; | ||
645 | |||
646 | spin_lock_irq(&file->ucontext->lock); | ||
647 | list_add_tail(&uobj->list, &file->ucontext->cq_list); | ||
648 | spin_unlock_irq(&file->ucontext->lock); | ||
649 | |||
650 | memset(&resp, 0, sizeof resp); | ||
651 | resp.cq_handle = uobj->id; | ||
652 | resp.cqe = cq->cqe; | ||
653 | |||
654 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
655 | &resp, sizeof resp)) { | ||
656 | ret = -EFAULT; | ||
657 | goto err_list; | ||
658 | } | ||
659 | |||
660 | return in_len; | ||
661 | |||
662 | err_list: | ||
663 | spin_lock_irq(&file->ucontext->lock); | ||
664 | list_del(&uobj->list); | ||
665 | spin_unlock_irq(&file->ucontext->lock); | ||
666 | |||
667 | down(&ib_uverbs_idr_mutex); | ||
668 | idr_remove(&ib_uverbs_cq_idr, uobj->id); | ||
669 | up(&ib_uverbs_idr_mutex); | ||
670 | |||
671 | err_cq: | ||
672 | ib_destroy_cq(cq); | ||
673 | |||
674 | err: | ||
675 | kfree(uobj); | ||
676 | return ret; | ||
677 | } | ||
678 | |||
679 | ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file, | ||
680 | const char __user *buf, int in_len, | ||
681 | int out_len) | ||
682 | { | ||
683 | struct ib_uverbs_destroy_cq cmd; | ||
684 | struct ib_cq *cq; | ||
685 | struct ib_uobject *uobj; | ||
686 | int ret = -EINVAL; | ||
687 | |||
688 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
689 | return -EFAULT; | ||
690 | |||
691 | down(&ib_uverbs_idr_mutex); | ||
692 | |||
693 | cq = idr_find(&ib_uverbs_cq_idr, cmd.cq_handle); | ||
694 | if (!cq || cq->uobject->context != file->ucontext) | ||
695 | goto out; | ||
696 | |||
697 | uobj = cq->uobject; | ||
698 | |||
699 | ret = ib_destroy_cq(cq); | ||
700 | if (ret) | ||
701 | goto out; | ||
702 | |||
703 | idr_remove(&ib_uverbs_cq_idr, cmd.cq_handle); | ||
704 | |||
705 | spin_lock_irq(&file->ucontext->lock); | ||
706 | list_del(&uobj->list); | ||
707 | spin_unlock_irq(&file->ucontext->lock); | ||
708 | |||
709 | kfree(uobj); | ||
710 | |||
711 | out: | ||
712 | up(&ib_uverbs_idr_mutex); | ||
713 | |||
714 | return ret ? ret : in_len; | ||
715 | } | ||
716 | |||
717 | ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file, | ||
718 | const char __user *buf, int in_len, | ||
719 | int out_len) | ||
720 | { | ||
721 | struct ib_uverbs_create_qp cmd; | ||
722 | struct ib_uverbs_create_qp_resp resp; | ||
723 | struct ib_udata udata; | ||
724 | struct ib_uobject *uobj; | ||
725 | struct ib_pd *pd; | ||
726 | struct ib_cq *scq, *rcq; | ||
727 | struct ib_qp *qp; | ||
728 | struct ib_qp_init_attr attr; | ||
729 | int ret; | ||
730 | |||
731 | if (out_len < sizeof resp) | ||
732 | return -ENOSPC; | ||
733 | |||
734 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
735 | return -EFAULT; | ||
736 | |||
737 | INIT_UDATA(&udata, buf + sizeof cmd, | ||
738 | (unsigned long) cmd.response + sizeof resp, | ||
739 | in_len - sizeof cmd, out_len - sizeof resp); | ||
740 | |||
741 | uobj = kmalloc(sizeof *uobj, GFP_KERNEL); | ||
742 | if (!uobj) | ||
743 | return -ENOMEM; | ||
744 | |||
745 | down(&ib_uverbs_idr_mutex); | ||
746 | |||
747 | pd = idr_find(&ib_uverbs_pd_idr, cmd.pd_handle); | ||
748 | scq = idr_find(&ib_uverbs_cq_idr, cmd.send_cq_handle); | ||
749 | rcq = idr_find(&ib_uverbs_cq_idr, cmd.recv_cq_handle); | ||
750 | |||
751 | if (!pd || pd->uobject->context != file->ucontext || | ||
752 | !scq || scq->uobject->context != file->ucontext || | ||
753 | !rcq || rcq->uobject->context != file->ucontext) { | ||
754 | ret = -EINVAL; | ||
755 | goto err_up; | ||
756 | } | ||
757 | |||
758 | attr.event_handler = ib_uverbs_qp_event_handler; | ||
759 | attr.qp_context = file; | ||
760 | attr.send_cq = scq; | ||
761 | attr.recv_cq = rcq; | ||
762 | attr.srq = NULL; | ||
763 | attr.sq_sig_type = cmd.sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR; | ||
764 | attr.qp_type = cmd.qp_type; | ||
765 | |||
766 | attr.cap.max_send_wr = cmd.max_send_wr; | ||
767 | attr.cap.max_recv_wr = cmd.max_recv_wr; | ||
768 | attr.cap.max_send_sge = cmd.max_send_sge; | ||
769 | attr.cap.max_recv_sge = cmd.max_recv_sge; | ||
770 | attr.cap.max_inline_data = cmd.max_inline_data; | ||
771 | |||
772 | uobj->user_handle = cmd.user_handle; | ||
773 | uobj->context = file->ucontext; | ||
774 | |||
775 | qp = pd->device->create_qp(pd, &attr, &udata); | ||
776 | if (IS_ERR(qp)) { | ||
777 | ret = PTR_ERR(qp); | ||
778 | goto err_up; | ||
779 | } | ||
780 | |||
781 | qp->device = pd->device; | ||
782 | qp->pd = pd; | ||
783 | qp->send_cq = attr.send_cq; | ||
784 | qp->recv_cq = attr.recv_cq; | ||
785 | qp->srq = attr.srq; | ||
786 | qp->uobject = uobj; | ||
787 | qp->event_handler = attr.event_handler; | ||
788 | qp->qp_context = attr.qp_context; | ||
789 | qp->qp_type = attr.qp_type; | ||
790 | atomic_inc(&pd->usecnt); | ||
791 | atomic_inc(&attr.send_cq->usecnt); | ||
792 | atomic_inc(&attr.recv_cq->usecnt); | ||
793 | if (attr.srq) | ||
794 | atomic_inc(&attr.srq->usecnt); | ||
795 | |||
796 | memset(&resp, 0, sizeof resp); | ||
797 | resp.qpn = qp->qp_num; | ||
798 | |||
799 | retry: | ||
800 | if (!idr_pre_get(&ib_uverbs_qp_idr, GFP_KERNEL)) { | ||
801 | ret = -ENOMEM; | ||
802 | goto err_destroy; | ||
803 | } | ||
804 | |||
805 | ret = idr_get_new(&ib_uverbs_qp_idr, qp, &uobj->id); | ||
806 | |||
807 | if (ret == -EAGAIN) | ||
808 | goto retry; | ||
809 | if (ret) | ||
810 | goto err_destroy; | ||
811 | |||
812 | resp.qp_handle = uobj->id; | ||
813 | |||
814 | spin_lock_irq(&file->ucontext->lock); | ||
815 | list_add_tail(&uobj->list, &file->ucontext->qp_list); | ||
816 | spin_unlock_irq(&file->ucontext->lock); | ||
817 | |||
818 | if (copy_to_user((void __user *) (unsigned long) cmd.response, | ||
819 | &resp, sizeof resp)) { | ||
820 | ret = -EFAULT; | ||
821 | goto err_list; | ||
822 | } | ||
823 | |||
824 | up(&ib_uverbs_idr_mutex); | ||
825 | |||
826 | return in_len; | ||
827 | |||
828 | err_list: | ||
829 | spin_lock_irq(&file->ucontext->lock); | ||
830 | list_del(&uobj->list); | ||
831 | spin_unlock_irq(&file->ucontext->lock); | ||
832 | |||
833 | err_destroy: | ||
834 | ib_destroy_qp(qp); | ||
835 | |||
836 | err_up: | ||
837 | up(&ib_uverbs_idr_mutex); | ||
838 | |||
839 | kfree(uobj); | ||
840 | return ret; | ||
841 | } | ||
842 | |||
843 | ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file, | ||
844 | const char __user *buf, int in_len, | ||
845 | int out_len) | ||
846 | { | ||
847 | struct ib_uverbs_modify_qp cmd; | ||
848 | struct ib_qp *qp; | ||
849 | struct ib_qp_attr *attr; | ||
850 | int ret; | ||
851 | |||
852 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
853 | return -EFAULT; | ||
854 | |||
855 | attr = kmalloc(sizeof *attr, GFP_KERNEL); | ||
856 | if (!attr) | ||
857 | return -ENOMEM; | ||
858 | |||
859 | down(&ib_uverbs_idr_mutex); | ||
860 | |||
861 | qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle); | ||
862 | if (!qp || qp->uobject->context != file->ucontext) { | ||
863 | ret = -EINVAL; | ||
864 | goto out; | ||
865 | } | ||
866 | |||
867 | attr->qp_state = cmd.qp_state; | ||
868 | attr->cur_qp_state = cmd.cur_qp_state; | ||
869 | attr->path_mtu = cmd.path_mtu; | ||
870 | attr->path_mig_state = cmd.path_mig_state; | ||
871 | attr->qkey = cmd.qkey; | ||
872 | attr->rq_psn = cmd.rq_psn; | ||
873 | attr->sq_psn = cmd.sq_psn; | ||
874 | attr->dest_qp_num = cmd.dest_qp_num; | ||
875 | attr->qp_access_flags = cmd.qp_access_flags; | ||
876 | attr->pkey_index = cmd.pkey_index; | ||
877 | attr->alt_pkey_index = cmd.pkey_index; | ||
878 | attr->en_sqd_async_notify = cmd.en_sqd_async_notify; | ||
879 | attr->max_rd_atomic = cmd.max_rd_atomic; | ||
880 | attr->max_dest_rd_atomic = cmd.max_dest_rd_atomic; | ||
881 | attr->min_rnr_timer = cmd.min_rnr_timer; | ||
882 | attr->port_num = cmd.port_num; | ||
883 | attr->timeout = cmd.timeout; | ||
884 | attr->retry_cnt = cmd.retry_cnt; | ||
885 | attr->rnr_retry = cmd.rnr_retry; | ||
886 | attr->alt_port_num = cmd.alt_port_num; | ||
887 | attr->alt_timeout = cmd.alt_timeout; | ||
888 | |||
889 | memcpy(attr->ah_attr.grh.dgid.raw, cmd.dest.dgid, 16); | ||
890 | attr->ah_attr.grh.flow_label = cmd.dest.flow_label; | ||
891 | attr->ah_attr.grh.sgid_index = cmd.dest.sgid_index; | ||
892 | attr->ah_attr.grh.hop_limit = cmd.dest.hop_limit; | ||
893 | attr->ah_attr.grh.traffic_class = cmd.dest.traffic_class; | ||
894 | attr->ah_attr.dlid = cmd.dest.dlid; | ||
895 | attr->ah_attr.sl = cmd.dest.sl; | ||
896 | attr->ah_attr.src_path_bits = cmd.dest.src_path_bits; | ||
897 | attr->ah_attr.static_rate = cmd.dest.static_rate; | ||
898 | attr->ah_attr.ah_flags = cmd.dest.is_global ? IB_AH_GRH : 0; | ||
899 | attr->ah_attr.port_num = cmd.dest.port_num; | ||
900 | |||
901 | memcpy(attr->alt_ah_attr.grh.dgid.raw, cmd.alt_dest.dgid, 16); | ||
902 | attr->alt_ah_attr.grh.flow_label = cmd.alt_dest.flow_label; | ||
903 | attr->alt_ah_attr.grh.sgid_index = cmd.alt_dest.sgid_index; | ||
904 | attr->alt_ah_attr.grh.hop_limit = cmd.alt_dest.hop_limit; | ||
905 | attr->alt_ah_attr.grh.traffic_class = cmd.alt_dest.traffic_class; | ||
906 | attr->alt_ah_attr.dlid = cmd.alt_dest.dlid; | ||
907 | attr->alt_ah_attr.sl = cmd.alt_dest.sl; | ||
908 | attr->alt_ah_attr.src_path_bits = cmd.alt_dest.src_path_bits; | ||
909 | attr->alt_ah_attr.static_rate = cmd.alt_dest.static_rate; | ||
910 | attr->alt_ah_attr.ah_flags = cmd.alt_dest.is_global ? IB_AH_GRH : 0; | ||
911 | attr->alt_ah_attr.port_num = cmd.alt_dest.port_num; | ||
912 | |||
913 | ret = ib_modify_qp(qp, attr, cmd.attr_mask); | ||
914 | if (ret) | ||
915 | goto out; | ||
916 | |||
917 | ret = in_len; | ||
918 | |||
919 | out: | ||
920 | up(&ib_uverbs_idr_mutex); | ||
921 | kfree(attr); | ||
922 | |||
923 | return ret; | ||
924 | } | ||
925 | |||
926 | ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, | ||
927 | const char __user *buf, int in_len, | ||
928 | int out_len) | ||
929 | { | ||
930 | struct ib_uverbs_destroy_qp cmd; | ||
931 | struct ib_qp *qp; | ||
932 | struct ib_uobject *uobj; | ||
933 | int ret = -EINVAL; | ||
934 | |||
935 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
936 | return -EFAULT; | ||
937 | |||
938 | down(&ib_uverbs_idr_mutex); | ||
939 | |||
940 | qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle); | ||
941 | if (!qp || qp->uobject->context != file->ucontext) | ||
942 | goto out; | ||
943 | |||
944 | uobj = qp->uobject; | ||
945 | |||
946 | ret = ib_destroy_qp(qp); | ||
947 | if (ret) | ||
948 | goto out; | ||
949 | |||
950 | idr_remove(&ib_uverbs_qp_idr, cmd.qp_handle); | ||
951 | |||
952 | spin_lock_irq(&file->ucontext->lock); | ||
953 | list_del(&uobj->list); | ||
954 | spin_unlock_irq(&file->ucontext->lock); | ||
955 | |||
956 | kfree(uobj); | ||
957 | |||
958 | out: | ||
959 | up(&ib_uverbs_idr_mutex); | ||
960 | |||
961 | return ret ? ret : in_len; | ||
962 | } | ||
963 | |||
964 | ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file, | ||
965 | const char __user *buf, int in_len, | ||
966 | int out_len) | ||
967 | { | ||
968 | struct ib_uverbs_attach_mcast cmd; | ||
969 | struct ib_qp *qp; | ||
970 | int ret = -EINVAL; | ||
971 | |||
972 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
973 | return -EFAULT; | ||
974 | |||
975 | down(&ib_uverbs_idr_mutex); | ||
976 | |||
977 | qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle); | ||
978 | if (qp && qp->uobject->context == file->ucontext) | ||
979 | ret = ib_attach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid); | ||
980 | |||
981 | up(&ib_uverbs_idr_mutex); | ||
982 | |||
983 | return ret ? ret : in_len; | ||
984 | } | ||
985 | |||
986 | ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file, | ||
987 | const char __user *buf, int in_len, | ||
988 | int out_len) | ||
989 | { | ||
990 | struct ib_uverbs_detach_mcast cmd; | ||
991 | struct ib_qp *qp; | ||
992 | int ret = -EINVAL; | ||
993 | |||
994 | if (copy_from_user(&cmd, buf, sizeof cmd)) | ||
995 | return -EFAULT; | ||
996 | |||
997 | down(&ib_uverbs_idr_mutex); | ||
998 | |||
999 | qp = idr_find(&ib_uverbs_qp_idr, cmd.qp_handle); | ||
1000 | if (qp && qp->uobject->context == file->ucontext) | ||
1001 | ret = ib_detach_mcast(qp, (union ib_gid *) cmd.gid, cmd.mlid); | ||
1002 | |||
1003 | up(&ib_uverbs_idr_mutex); | ||
1004 | |||
1005 | return ret ? ret : in_len; | ||
1006 | } | ||
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c new file mode 100644 index 000000000000..fbbe03d8c901 --- /dev/null +++ b/drivers/infiniband/core/uverbs_main.c | |||
@@ -0,0 +1,698 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Topspin Communications. All rights reserved. | ||
3 | * Copyright (c) 2005 Cisco Systems. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | * | ||
33 | * $Id: uverbs_main.c 2733 2005-06-28 19:14:34Z roland $ | ||
34 | */ | ||
35 | |||
36 | #include <linux/module.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/device.h> | ||
39 | #include <linux/err.h> | ||
40 | #include <linux/fs.h> | ||
41 | #include <linux/poll.h> | ||
42 | #include <linux/file.h> | ||
43 | #include <linux/mount.h> | ||
44 | |||
45 | #include <asm/uaccess.h> | ||
46 | |||
47 | #include "uverbs.h" | ||
48 | |||
49 | MODULE_AUTHOR("Roland Dreier"); | ||
50 | MODULE_DESCRIPTION("InfiniBand userspace verbs access"); | ||
51 | MODULE_LICENSE("Dual BSD/GPL"); | ||
52 | |||
53 | #define INFINIBANDEVENTFS_MAGIC 0x49426576 /* "IBev" */ | ||
54 | |||
55 | enum { | ||
56 | IB_UVERBS_MAJOR = 231, | ||
57 | IB_UVERBS_BASE_MINOR = 192, | ||
58 | IB_UVERBS_MAX_DEVICES = 32 | ||
59 | }; | ||
60 | |||
61 | #define IB_UVERBS_BASE_DEV MKDEV(IB_UVERBS_MAJOR, IB_UVERBS_BASE_MINOR) | ||
62 | |||
63 | DECLARE_MUTEX(ib_uverbs_idr_mutex); | ||
64 | DEFINE_IDR(ib_uverbs_pd_idr); | ||
65 | DEFINE_IDR(ib_uverbs_mr_idr); | ||
66 | DEFINE_IDR(ib_uverbs_mw_idr); | ||
67 | DEFINE_IDR(ib_uverbs_ah_idr); | ||
68 | DEFINE_IDR(ib_uverbs_cq_idr); | ||
69 | DEFINE_IDR(ib_uverbs_qp_idr); | ||
70 | |||
71 | static spinlock_t map_lock; | ||
72 | static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES); | ||
73 | |||
74 | static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file, | ||
75 | const char __user *buf, int in_len, | ||
76 | int out_len) = { | ||
77 | [IB_USER_VERBS_CMD_QUERY_PARAMS] = ib_uverbs_query_params, | ||
78 | [IB_USER_VERBS_CMD_GET_CONTEXT] = ib_uverbs_get_context, | ||
79 | [IB_USER_VERBS_CMD_QUERY_DEVICE] = ib_uverbs_query_device, | ||
80 | [IB_USER_VERBS_CMD_QUERY_PORT] = ib_uverbs_query_port, | ||
81 | [IB_USER_VERBS_CMD_QUERY_GID] = ib_uverbs_query_gid, | ||
82 | [IB_USER_VERBS_CMD_QUERY_PKEY] = ib_uverbs_query_pkey, | ||
83 | [IB_USER_VERBS_CMD_ALLOC_PD] = ib_uverbs_alloc_pd, | ||
84 | [IB_USER_VERBS_CMD_DEALLOC_PD] = ib_uverbs_dealloc_pd, | ||
85 | [IB_USER_VERBS_CMD_REG_MR] = ib_uverbs_reg_mr, | ||
86 | [IB_USER_VERBS_CMD_DEREG_MR] = ib_uverbs_dereg_mr, | ||
87 | [IB_USER_VERBS_CMD_CREATE_CQ] = ib_uverbs_create_cq, | ||
88 | [IB_USER_VERBS_CMD_DESTROY_CQ] = ib_uverbs_destroy_cq, | ||
89 | [IB_USER_VERBS_CMD_CREATE_QP] = ib_uverbs_create_qp, | ||
90 | [IB_USER_VERBS_CMD_MODIFY_QP] = ib_uverbs_modify_qp, | ||
91 | [IB_USER_VERBS_CMD_DESTROY_QP] = ib_uverbs_destroy_qp, | ||
92 | [IB_USER_VERBS_CMD_ATTACH_MCAST] = ib_uverbs_attach_mcast, | ||
93 | [IB_USER_VERBS_CMD_DETACH_MCAST] = ib_uverbs_detach_mcast, | ||
94 | }; | ||
95 | |||
96 | static struct vfsmount *uverbs_event_mnt; | ||
97 | |||
98 | static void ib_uverbs_add_one(struct ib_device *device); | ||
99 | static void ib_uverbs_remove_one(struct ib_device *device); | ||
100 | |||
101 | static int ib_dealloc_ucontext(struct ib_ucontext *context) | ||
102 | { | ||
103 | struct ib_uobject *uobj, *tmp; | ||
104 | |||
105 | if (!context) | ||
106 | return 0; | ||
107 | |||
108 | down(&ib_uverbs_idr_mutex); | ||
109 | |||
110 | /* XXX Free AHs */ | ||
111 | |||
112 | list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) { | ||
113 | struct ib_qp *qp = idr_find(&ib_uverbs_qp_idr, uobj->id); | ||
114 | idr_remove(&ib_uverbs_qp_idr, uobj->id); | ||
115 | ib_destroy_qp(qp); | ||
116 | list_del(&uobj->list); | ||
117 | kfree(uobj); | ||
118 | } | ||
119 | |||
120 | list_for_each_entry_safe(uobj, tmp, &context->cq_list, list) { | ||
121 | struct ib_cq *cq = idr_find(&ib_uverbs_cq_idr, uobj->id); | ||
122 | idr_remove(&ib_uverbs_cq_idr, uobj->id); | ||
123 | ib_destroy_cq(cq); | ||
124 | list_del(&uobj->list); | ||
125 | kfree(uobj); | ||
126 | } | ||
127 | |||
128 | /* XXX Free SRQs */ | ||
129 | /* XXX Free MWs */ | ||
130 | |||
131 | list_for_each_entry_safe(uobj, tmp, &context->mr_list, list) { | ||
132 | struct ib_mr *mr = idr_find(&ib_uverbs_mr_idr, uobj->id); | ||
133 | struct ib_umem_object *memobj; | ||
134 | |||
135 | idr_remove(&ib_uverbs_mr_idr, uobj->id); | ||
136 | ib_dereg_mr(mr); | ||
137 | |||
138 | memobj = container_of(uobj, struct ib_umem_object, uobject); | ||
139 | ib_umem_release_on_close(mr->device, &memobj->umem); | ||
140 | |||
141 | list_del(&uobj->list); | ||
142 | kfree(memobj); | ||
143 | } | ||
144 | |||
145 | list_for_each_entry_safe(uobj, tmp, &context->pd_list, list) { | ||
146 | struct ib_pd *pd = idr_find(&ib_uverbs_pd_idr, uobj->id); | ||
147 | idr_remove(&ib_uverbs_pd_idr, uobj->id); | ||
148 | ib_dealloc_pd(pd); | ||
149 | list_del(&uobj->list); | ||
150 | kfree(uobj); | ||
151 | } | ||
152 | |||
153 | up(&ib_uverbs_idr_mutex); | ||
154 | |||
155 | return context->device->dealloc_ucontext(context); | ||
156 | } | ||
157 | |||
158 | static void ib_uverbs_release_file(struct kref *ref) | ||
159 | { | ||
160 | struct ib_uverbs_file *file = | ||
161 | container_of(ref, struct ib_uverbs_file, ref); | ||
162 | |||
163 | module_put(file->device->ib_dev->owner); | ||
164 | kfree(file); | ||
165 | } | ||
166 | |||
167 | static ssize_t ib_uverbs_event_read(struct file *filp, char __user *buf, | ||
168 | size_t count, loff_t *pos) | ||
169 | { | ||
170 | struct ib_uverbs_event_file *file = filp->private_data; | ||
171 | void *event; | ||
172 | int eventsz; | ||
173 | int ret = 0; | ||
174 | |||
175 | spin_lock_irq(&file->lock); | ||
176 | |||
177 | while (list_empty(&file->event_list) && file->fd >= 0) { | ||
178 | spin_unlock_irq(&file->lock); | ||
179 | |||
180 | if (filp->f_flags & O_NONBLOCK) | ||
181 | return -EAGAIN; | ||
182 | |||
183 | if (wait_event_interruptible(file->poll_wait, | ||
184 | !list_empty(&file->event_list) || | ||
185 | file->fd < 0)) | ||
186 | return -ERESTARTSYS; | ||
187 | |||
188 | spin_lock_irq(&file->lock); | ||
189 | } | ||
190 | |||
191 | if (file->fd < 0) { | ||
192 | spin_unlock_irq(&file->lock); | ||
193 | return -ENODEV; | ||
194 | } | ||
195 | |||
196 | if (file->is_async) { | ||
197 | event = list_entry(file->event_list.next, | ||
198 | struct ib_uverbs_async_event, list); | ||
199 | eventsz = sizeof (struct ib_uverbs_async_event_desc); | ||
200 | } else { | ||
201 | event = list_entry(file->event_list.next, | ||
202 | struct ib_uverbs_comp_event, list); | ||
203 | eventsz = sizeof (struct ib_uverbs_comp_event_desc); | ||
204 | } | ||
205 | |||
206 | if (eventsz > count) { | ||
207 | ret = -EINVAL; | ||
208 | event = NULL; | ||
209 | } else | ||
210 | list_del(file->event_list.next); | ||
211 | |||
212 | spin_unlock_irq(&file->lock); | ||
213 | |||
214 | if (event) { | ||
215 | if (copy_to_user(buf, event, eventsz)) | ||
216 | ret = -EFAULT; | ||
217 | else | ||
218 | ret = eventsz; | ||
219 | } | ||
220 | |||
221 | kfree(event); | ||
222 | |||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | static unsigned int ib_uverbs_event_poll(struct file *filp, | ||
227 | struct poll_table_struct *wait) | ||
228 | { | ||
229 | unsigned int pollflags = 0; | ||
230 | struct ib_uverbs_event_file *file = filp->private_data; | ||
231 | |||
232 | poll_wait(filp, &file->poll_wait, wait); | ||
233 | |||
234 | spin_lock_irq(&file->lock); | ||
235 | if (file->fd < 0) | ||
236 | pollflags = POLLERR; | ||
237 | else if (!list_empty(&file->event_list)) | ||
238 | pollflags = POLLIN | POLLRDNORM; | ||
239 | spin_unlock_irq(&file->lock); | ||
240 | |||
241 | return pollflags; | ||
242 | } | ||
243 | |||
244 | static void ib_uverbs_event_release(struct ib_uverbs_event_file *file) | ||
245 | { | ||
246 | struct list_head *entry, *tmp; | ||
247 | |||
248 | spin_lock_irq(&file->lock); | ||
249 | if (file->fd != -1) { | ||
250 | file->fd = -1; | ||
251 | list_for_each_safe(entry, tmp, &file->event_list) | ||
252 | if (file->is_async) | ||
253 | kfree(list_entry(entry, struct ib_uverbs_async_event, list)); | ||
254 | else | ||
255 | kfree(list_entry(entry, struct ib_uverbs_comp_event, list)); | ||
256 | } | ||
257 | spin_unlock_irq(&file->lock); | ||
258 | } | ||
259 | |||
260 | static int ib_uverbs_event_close(struct inode *inode, struct file *filp) | ||
261 | { | ||
262 | struct ib_uverbs_event_file *file = filp->private_data; | ||
263 | |||
264 | ib_uverbs_event_release(file); | ||
265 | kref_put(&file->uverbs_file->ref, ib_uverbs_release_file); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static struct file_operations uverbs_event_fops = { | ||
271 | /* | ||
272 | * No .owner field since we artificially create event files, | ||
273 | * so there is no increment to the module reference count in | ||
274 | * the open path. All event files come from a uverbs command | ||
275 | * file, which already takes a module reference, so this is OK. | ||
276 | */ | ||
277 | .read = ib_uverbs_event_read, | ||
278 | .poll = ib_uverbs_event_poll, | ||
279 | .release = ib_uverbs_event_close | ||
280 | }; | ||
281 | |||
282 | void ib_uverbs_comp_handler(struct ib_cq *cq, void *cq_context) | ||
283 | { | ||
284 | struct ib_uverbs_file *file = cq_context; | ||
285 | struct ib_uverbs_comp_event *entry; | ||
286 | unsigned long flags; | ||
287 | |||
288 | entry = kmalloc(sizeof *entry, GFP_ATOMIC); | ||
289 | if (!entry) | ||
290 | return; | ||
291 | |||
292 | entry->desc.cq_handle = cq->uobject->user_handle; | ||
293 | |||
294 | spin_lock_irqsave(&file->comp_file[0].lock, flags); | ||
295 | list_add_tail(&entry->list, &file->comp_file[0].event_list); | ||
296 | spin_unlock_irqrestore(&file->comp_file[0].lock, flags); | ||
297 | |||
298 | wake_up_interruptible(&file->comp_file[0].poll_wait); | ||
299 | } | ||
300 | |||
301 | static void ib_uverbs_async_handler(struct ib_uverbs_file *file, | ||
302 | __u64 element, __u64 event) | ||
303 | { | ||
304 | struct ib_uverbs_async_event *entry; | ||
305 | unsigned long flags; | ||
306 | |||
307 | entry = kmalloc(sizeof *entry, GFP_ATOMIC); | ||
308 | if (!entry) | ||
309 | return; | ||
310 | |||
311 | entry->desc.element = element; | ||
312 | entry->desc.event_type = event; | ||
313 | |||
314 | spin_lock_irqsave(&file->async_file.lock, flags); | ||
315 | list_add_tail(&entry->list, &file->async_file.event_list); | ||
316 | spin_unlock_irqrestore(&file->async_file.lock, flags); | ||
317 | |||
318 | wake_up_interruptible(&file->async_file.poll_wait); | ||
319 | } | ||
320 | |||
321 | void ib_uverbs_cq_event_handler(struct ib_event *event, void *context_ptr) | ||
322 | { | ||
323 | ib_uverbs_async_handler(context_ptr, | ||
324 | event->element.cq->uobject->user_handle, | ||
325 | event->event); | ||
326 | } | ||
327 | |||
328 | void ib_uverbs_qp_event_handler(struct ib_event *event, void *context_ptr) | ||
329 | { | ||
330 | ib_uverbs_async_handler(context_ptr, | ||
331 | event->element.qp->uobject->user_handle, | ||
332 | event->event); | ||
333 | } | ||
334 | |||
335 | static void ib_uverbs_event_handler(struct ib_event_handler *handler, | ||
336 | struct ib_event *event) | ||
337 | { | ||
338 | struct ib_uverbs_file *file = | ||
339 | container_of(handler, struct ib_uverbs_file, event_handler); | ||
340 | |||
341 | ib_uverbs_async_handler(file, event->element.port_num, event->event); | ||
342 | } | ||
343 | |||
344 | static int ib_uverbs_event_init(struct ib_uverbs_event_file *file, | ||
345 | struct ib_uverbs_file *uverbs_file) | ||
346 | { | ||
347 | struct file *filp; | ||
348 | |||
349 | spin_lock_init(&file->lock); | ||
350 | INIT_LIST_HEAD(&file->event_list); | ||
351 | init_waitqueue_head(&file->poll_wait); | ||
352 | file->uverbs_file = uverbs_file; | ||
353 | |||
354 | file->fd = get_unused_fd(); | ||
355 | if (file->fd < 0) | ||
356 | return file->fd; | ||
357 | |||
358 | filp = get_empty_filp(); | ||
359 | if (!filp) { | ||
360 | put_unused_fd(file->fd); | ||
361 | return -ENFILE; | ||
362 | } | ||
363 | |||
364 | filp->f_op = &uverbs_event_fops; | ||
365 | filp->f_vfsmnt = mntget(uverbs_event_mnt); | ||
366 | filp->f_dentry = dget(uverbs_event_mnt->mnt_root); | ||
367 | filp->f_mapping = filp->f_dentry->d_inode->i_mapping; | ||
368 | filp->f_flags = O_RDONLY; | ||
369 | filp->f_mode = FMODE_READ; | ||
370 | filp->private_data = file; | ||
371 | |||
372 | fd_install(file->fd, filp); | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, | ||
378 | size_t count, loff_t *pos) | ||
379 | { | ||
380 | struct ib_uverbs_file *file = filp->private_data; | ||
381 | struct ib_uverbs_cmd_hdr hdr; | ||
382 | |||
383 | if (count < sizeof hdr) | ||
384 | return -EINVAL; | ||
385 | |||
386 | if (copy_from_user(&hdr, buf, sizeof hdr)) | ||
387 | return -EFAULT; | ||
388 | |||
389 | if (hdr.in_words * 4 != count) | ||
390 | return -EINVAL; | ||
391 | |||
392 | if (hdr.command < 0 || hdr.command >= ARRAY_SIZE(uverbs_cmd_table)) | ||
393 | return -EINVAL; | ||
394 | |||
395 | if (!file->ucontext && | ||
396 | hdr.command != IB_USER_VERBS_CMD_QUERY_PARAMS && | ||
397 | hdr.command != IB_USER_VERBS_CMD_GET_CONTEXT) | ||
398 | return -EINVAL; | ||
399 | |||
400 | return uverbs_cmd_table[hdr.command](file, buf + sizeof hdr, | ||
401 | hdr.in_words * 4, hdr.out_words * 4); | ||
402 | } | ||
403 | |||
404 | static int ib_uverbs_mmap(struct file *filp, struct vm_area_struct *vma) | ||
405 | { | ||
406 | struct ib_uverbs_file *file = filp->private_data; | ||
407 | |||
408 | if (!file->ucontext) | ||
409 | return -ENODEV; | ||
410 | else | ||
411 | return file->device->ib_dev->mmap(file->ucontext, vma); | ||
412 | } | ||
413 | |||
414 | static int ib_uverbs_open(struct inode *inode, struct file *filp) | ||
415 | { | ||
416 | struct ib_uverbs_device *dev = | ||
417 | container_of(inode->i_cdev, struct ib_uverbs_device, dev); | ||
418 | struct ib_uverbs_file *file; | ||
419 | int i = 0; | ||
420 | int ret; | ||
421 | |||
422 | if (!try_module_get(dev->ib_dev->owner)) | ||
423 | return -ENODEV; | ||
424 | |||
425 | file = kmalloc(sizeof *file + | ||
426 | (dev->num_comp - 1) * sizeof (struct ib_uverbs_event_file), | ||
427 | GFP_KERNEL); | ||
428 | if (!file) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | file->device = dev; | ||
432 | kref_init(&file->ref); | ||
433 | |||
434 | file->ucontext = NULL; | ||
435 | |||
436 | ret = ib_uverbs_event_init(&file->async_file, file); | ||
437 | if (ret) | ||
438 | goto err; | ||
439 | |||
440 | file->async_file.is_async = 1; | ||
441 | |||
442 | kref_get(&file->ref); | ||
443 | |||
444 | for (i = 0; i < dev->num_comp; ++i) { | ||
445 | ret = ib_uverbs_event_init(&file->comp_file[i], file); | ||
446 | if (ret) | ||
447 | goto err_async; | ||
448 | kref_get(&file->ref); | ||
449 | file->comp_file[i].is_async = 0; | ||
450 | } | ||
451 | |||
452 | |||
453 | filp->private_data = file; | ||
454 | |||
455 | INIT_IB_EVENT_HANDLER(&file->event_handler, dev->ib_dev, | ||
456 | ib_uverbs_event_handler); | ||
457 | if (ib_register_event_handler(&file->event_handler)) | ||
458 | goto err_async; | ||
459 | |||
460 | return 0; | ||
461 | |||
462 | err_async: | ||
463 | while (i--) | ||
464 | ib_uverbs_event_release(&file->comp_file[i]); | ||
465 | |||
466 | ib_uverbs_event_release(&file->async_file); | ||
467 | |||
468 | err: | ||
469 | kref_put(&file->ref, ib_uverbs_release_file); | ||
470 | |||
471 | return ret; | ||
472 | } | ||
473 | |||
474 | static int ib_uverbs_close(struct inode *inode, struct file *filp) | ||
475 | { | ||
476 | struct ib_uverbs_file *file = filp->private_data; | ||
477 | int i; | ||
478 | |||
479 | ib_unregister_event_handler(&file->event_handler); | ||
480 | ib_uverbs_event_release(&file->async_file); | ||
481 | ib_dealloc_ucontext(file->ucontext); | ||
482 | |||
483 | for (i = 0; i < file->device->num_comp; ++i) | ||
484 | ib_uverbs_event_release(&file->comp_file[i]); | ||
485 | |||
486 | kref_put(&file->ref, ib_uverbs_release_file); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static struct file_operations uverbs_fops = { | ||
492 | .owner = THIS_MODULE, | ||
493 | .write = ib_uverbs_write, | ||
494 | .open = ib_uverbs_open, | ||
495 | .release = ib_uverbs_close | ||
496 | }; | ||
497 | |||
498 | static struct file_operations uverbs_mmap_fops = { | ||
499 | .owner = THIS_MODULE, | ||
500 | .write = ib_uverbs_write, | ||
501 | .mmap = ib_uverbs_mmap, | ||
502 | .open = ib_uverbs_open, | ||
503 | .release = ib_uverbs_close | ||
504 | }; | ||
505 | |||
506 | static struct ib_client uverbs_client = { | ||
507 | .name = "uverbs", | ||
508 | .add = ib_uverbs_add_one, | ||
509 | .remove = ib_uverbs_remove_one | ||
510 | }; | ||
511 | |||
512 | static ssize_t show_ibdev(struct class_device *class_dev, char *buf) | ||
513 | { | ||
514 | struct ib_uverbs_device *dev = | ||
515 | container_of(class_dev, struct ib_uverbs_device, class_dev); | ||
516 | |||
517 | return sprintf(buf, "%s\n", dev->ib_dev->name); | ||
518 | } | ||
519 | static CLASS_DEVICE_ATTR(ibdev, S_IRUGO, show_ibdev, NULL); | ||
520 | |||
521 | static void ib_uverbs_release_class_dev(struct class_device *class_dev) | ||
522 | { | ||
523 | struct ib_uverbs_device *dev = | ||
524 | container_of(class_dev, struct ib_uverbs_device, class_dev); | ||
525 | |||
526 | cdev_del(&dev->dev); | ||
527 | clear_bit(dev->devnum, dev_map); | ||
528 | kfree(dev); | ||
529 | } | ||
530 | |||
531 | static struct class uverbs_class = { | ||
532 | .name = "infiniband_verbs", | ||
533 | .release = ib_uverbs_release_class_dev | ||
534 | }; | ||
535 | |||
536 | static ssize_t show_abi_version(struct class *class, char *buf) | ||
537 | { | ||
538 | return sprintf(buf, "%d\n", IB_USER_VERBS_ABI_VERSION); | ||
539 | } | ||
540 | static CLASS_ATTR(abi_version, S_IRUGO, show_abi_version, NULL); | ||
541 | |||
542 | static void ib_uverbs_add_one(struct ib_device *device) | ||
543 | { | ||
544 | struct ib_uverbs_device *uverbs_dev; | ||
545 | |||
546 | if (!device->alloc_ucontext) | ||
547 | return; | ||
548 | |||
549 | uverbs_dev = kmalloc(sizeof *uverbs_dev, GFP_KERNEL); | ||
550 | if (!uverbs_dev) | ||
551 | return; | ||
552 | |||
553 | memset(uverbs_dev, 0, sizeof *uverbs_dev); | ||
554 | |||
555 | spin_lock(&map_lock); | ||
556 | uverbs_dev->devnum = find_first_zero_bit(dev_map, IB_UVERBS_MAX_DEVICES); | ||
557 | if (uverbs_dev->devnum >= IB_UVERBS_MAX_DEVICES) { | ||
558 | spin_unlock(&map_lock); | ||
559 | goto err; | ||
560 | } | ||
561 | set_bit(uverbs_dev->devnum, dev_map); | ||
562 | spin_unlock(&map_lock); | ||
563 | |||
564 | uverbs_dev->ib_dev = device; | ||
565 | uverbs_dev->num_comp = 1; | ||
566 | |||
567 | if (device->mmap) | ||
568 | cdev_init(&uverbs_dev->dev, &uverbs_mmap_fops); | ||
569 | else | ||
570 | cdev_init(&uverbs_dev->dev, &uverbs_fops); | ||
571 | uverbs_dev->dev.owner = THIS_MODULE; | ||
572 | kobject_set_name(&uverbs_dev->dev.kobj, "uverbs%d", uverbs_dev->devnum); | ||
573 | if (cdev_add(&uverbs_dev->dev, IB_UVERBS_BASE_DEV + uverbs_dev->devnum, 1)) | ||
574 | goto err; | ||
575 | |||
576 | uverbs_dev->class_dev.class = &uverbs_class; | ||
577 | uverbs_dev->class_dev.dev = device->dma_device; | ||
578 | uverbs_dev->class_dev.devt = uverbs_dev->dev.dev; | ||
579 | snprintf(uverbs_dev->class_dev.class_id, BUS_ID_SIZE, "uverbs%d", uverbs_dev->devnum); | ||
580 | if (class_device_register(&uverbs_dev->class_dev)) | ||
581 | goto err_cdev; | ||
582 | |||
583 | if (class_device_create_file(&uverbs_dev->class_dev, &class_device_attr_ibdev)) | ||
584 | goto err_class; | ||
585 | |||
586 | ib_set_client_data(device, &uverbs_client, uverbs_dev); | ||
587 | |||
588 | return; | ||
589 | |||
590 | err_class: | ||
591 | class_device_unregister(&uverbs_dev->class_dev); | ||
592 | |||
593 | err_cdev: | ||
594 | cdev_del(&uverbs_dev->dev); | ||
595 | clear_bit(uverbs_dev->devnum, dev_map); | ||
596 | |||
597 | err: | ||
598 | kfree(uverbs_dev); | ||
599 | return; | ||
600 | } | ||
601 | |||
602 | static void ib_uverbs_remove_one(struct ib_device *device) | ||
603 | { | ||
604 | struct ib_uverbs_device *uverbs_dev = ib_get_client_data(device, &uverbs_client); | ||
605 | |||
606 | if (!uverbs_dev) | ||
607 | return; | ||
608 | |||
609 | class_device_unregister(&uverbs_dev->class_dev); | ||
610 | } | ||
611 | |||
612 | static struct super_block *uverbs_event_get_sb(struct file_system_type *fs_type, int flags, | ||
613 | const char *dev_name, void *data) | ||
614 | { | ||
615 | return get_sb_pseudo(fs_type, "infinibandevent:", NULL, | ||
616 | INFINIBANDEVENTFS_MAGIC); | ||
617 | } | ||
618 | |||
619 | static struct file_system_type uverbs_event_fs = { | ||
620 | /* No owner field so module can be unloaded */ | ||
621 | .name = "infinibandeventfs", | ||
622 | .get_sb = uverbs_event_get_sb, | ||
623 | .kill_sb = kill_litter_super | ||
624 | }; | ||
625 | |||
626 | static int __init ib_uverbs_init(void) | ||
627 | { | ||
628 | int ret; | ||
629 | |||
630 | spin_lock_init(&map_lock); | ||
631 | |||
632 | ret = register_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_MAX_DEVICES, | ||
633 | "infiniband_verbs"); | ||
634 | if (ret) { | ||
635 | printk(KERN_ERR "user_verbs: couldn't register device number\n"); | ||
636 | goto out; | ||
637 | } | ||
638 | |||
639 | ret = class_register(&uverbs_class); | ||
640 | if (ret) { | ||
641 | printk(KERN_ERR "user_verbs: couldn't create class infiniband_verbs\n"); | ||
642 | goto out_chrdev; | ||
643 | } | ||
644 | |||
645 | ret = class_create_file(&uverbs_class, &class_attr_abi_version); | ||
646 | if (ret) { | ||
647 | printk(KERN_ERR "user_verbs: couldn't create abi_version attribute\n"); | ||
648 | goto out_class; | ||
649 | } | ||
650 | |||
651 | ret = register_filesystem(&uverbs_event_fs); | ||
652 | if (ret) { | ||
653 | printk(KERN_ERR "user_verbs: couldn't register infinibandeventfs\n"); | ||
654 | goto out_class; | ||
655 | } | ||
656 | |||
657 | uverbs_event_mnt = kern_mount(&uverbs_event_fs); | ||
658 | if (IS_ERR(uverbs_event_mnt)) { | ||
659 | ret = PTR_ERR(uverbs_event_mnt); | ||
660 | printk(KERN_ERR "user_verbs: couldn't mount infinibandeventfs\n"); | ||
661 | goto out_fs; | ||
662 | } | ||
663 | |||
664 | ret = ib_register_client(&uverbs_client); | ||
665 | if (ret) { | ||
666 | printk(KERN_ERR "user_verbs: couldn't register client\n"); | ||
667 | goto out_mnt; | ||
668 | } | ||
669 | |||
670 | return 0; | ||
671 | |||
672 | out_mnt: | ||
673 | mntput(uverbs_event_mnt); | ||
674 | |||
675 | out_fs: | ||
676 | unregister_filesystem(&uverbs_event_fs); | ||
677 | |||
678 | out_class: | ||
679 | class_unregister(&uverbs_class); | ||
680 | |||
681 | out_chrdev: | ||
682 | unregister_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_MAX_DEVICES); | ||
683 | |||
684 | out: | ||
685 | return ret; | ||
686 | } | ||
687 | |||
688 | static void __exit ib_uverbs_cleanup(void) | ||
689 | { | ||
690 | ib_unregister_client(&uverbs_client); | ||
691 | mntput(uverbs_event_mnt); | ||
692 | unregister_filesystem(&uverbs_event_fs); | ||
693 | class_unregister(&uverbs_class); | ||
694 | unregister_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_MAX_DEVICES); | ||
695 | } | ||
696 | |||
697 | module_init(ib_uverbs_init); | ||
698 | module_exit(ib_uverbs_cleanup); | ||