[PATCH] IB/sa_query: avoid unnecessary list scan
[linux-2.6-block.git] / drivers / infiniband / core / ucm.c
CommitLineData
a5b74540
HR
1/*
2 * Copyright (c) 2005 Topspin Communications. All rights reserved.
b9ef520f 3 * Copyright (c) 2005 Intel Corporation. All rights reserved.
a5b74540
HR
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: ucm.c 2594 2005-06-13 19:46:02Z libor $
34 */
35#include <linux/init.h>
36#include <linux/fs.h>
37#include <linux/module.h>
38#include <linux/device.h>
39#include <linux/err.h>
40#include <linux/poll.h>
41#include <linux/file.h>
42#include <linux/mount.h>
43#include <linux/cdev.h>
44
45#include <asm/uaccess.h>
46
47#include "ucm.h"
48
49MODULE_AUTHOR("Libor Michalek");
50MODULE_DESCRIPTION("InfiniBand userspace Connection Manager access");
51MODULE_LICENSE("Dual BSD/GPL");
52
79d81907
HR
53static int ucm_debug_level;
54
55module_param_named(debug_level, ucm_debug_level, int, 0644);
56MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0");
57
a5b74540
HR
58enum {
59 IB_UCM_MAJOR = 231,
60 IB_UCM_MINOR = 255
61};
62
63#define IB_UCM_DEV MKDEV(IB_UCM_MAJOR, IB_UCM_MINOR)
64
79d81907
HR
65#define PFX "UCM: "
66
67#define ucm_dbg(format, arg...) \
68 do { \
69 if (ucm_debug_level > 0) \
70 printk(KERN_DEBUG PFX format, ## arg); \
71 } while (0)
72
a5b74540
HR
73static struct semaphore ctx_id_mutex;
74static struct idr ctx_id_table;
75static int ctx_id_rover = 0;
76
b9ef520f 77static struct ib_ucm_context *ib_ucm_ctx_get(struct ib_ucm_file *file, int id)
a5b74540
HR
78{
79 struct ib_ucm_context *ctx;
80
81 down(&ctx_id_mutex);
82 ctx = idr_find(&ctx_id_table, id);
b9ef520f
SH
83 if (!ctx)
84 ctx = ERR_PTR(-ENOENT);
85 else if (ctx->file != file)
86 ctx = ERR_PTR(-EINVAL);
87 else
88 atomic_inc(&ctx->ref);
a5b74540
HR
89 up(&ctx_id_mutex);
90
91 return ctx;
92}
93
94static void ib_ucm_ctx_put(struct ib_ucm_context *ctx)
95{
b9ef520f
SH
96 if (atomic_dec_and_test(&ctx->ref))
97 wake_up(&ctx->wait);
98}
99
100static ssize_t ib_ucm_destroy_ctx(struct ib_ucm_file *file, int id)
101{
102 struct ib_ucm_context *ctx;
a5b74540
HR
103 struct ib_ucm_event *uevent;
104
105 down(&ctx_id_mutex);
b9ef520f
SH
106 ctx = idr_find(&ctx_id_table, id);
107 if (!ctx)
108 ctx = ERR_PTR(-ENOENT);
109 else if (ctx->file != file)
110 ctx = ERR_PTR(-EINVAL);
111 else
a5b74540 112 idr_remove(&ctx_id_table, ctx->id);
a5b74540
HR
113 up(&ctx_id_mutex);
114
b9ef520f
SH
115 if (IS_ERR(ctx))
116 return PTR_ERR(ctx);
a5b74540 117
b9ef520f
SH
118 atomic_dec(&ctx->ref);
119 wait_event(ctx->wait, !atomic_read(&ctx->ref));
120
121 /* No new events will be generated after destroying the cm_id. */
122 if (!IS_ERR(ctx->cm_id))
123 ib_destroy_cm_id(ctx->cm_id);
a5b74540 124
b9ef520f
SH
125 /* Cleanup events not yet reported to the user. */
126 down(&file->mutex);
a5b74540
HR
127 list_del(&ctx->file_list);
128 while (!list_empty(&ctx->events)) {
129
130 uevent = list_entry(ctx->events.next,
131 struct ib_ucm_event, ctx_list);
132 list_del(&uevent->file_list);
133 list_del(&uevent->ctx_list);
134
135 /* clear incoming connections. */
136 if (uevent->cm_id)
137 ib_destroy_cm_id(uevent->cm_id);
138
139 kfree(uevent);
140 }
b9ef520f 141 up(&file->mutex);
a5b74540 142
a5b74540 143 kfree(ctx);
b9ef520f 144 return 0;
a5b74540
HR
145}
146
147static struct ib_ucm_context *ib_ucm_ctx_alloc(struct ib_ucm_file *file)
148{
149 struct ib_ucm_context *ctx;
150 int result;
151
152 ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
153 if (!ctx)
154 return NULL;
155
b9ef520f
SH
156 atomic_set(&ctx->ref, 1);
157 init_waitqueue_head(&ctx->wait);
a5b74540
HR
158 ctx->file = file;
159
160 INIT_LIST_HEAD(&ctx->events);
a5b74540
HR
161
162 list_add_tail(&ctx->file_list, &file->ctxs);
163
164 ctx_id_rover = (ctx_id_rover + 1) & INT_MAX;
165retry:
166 result = idr_pre_get(&ctx_id_table, GFP_KERNEL);
167 if (!result)
168 goto error;
169
170 down(&ctx_id_mutex);
171 result = idr_get_new_above(&ctx_id_table, ctx, ctx_id_rover, &ctx->id);
172 up(&ctx_id_mutex);
173
174 if (result == -EAGAIN)
175 goto retry;
176 if (result)
177 goto error;
178
79d81907 179 ucm_dbg("Allocated CM ID <%d>\n", ctx->id);
a5b74540
HR
180
181 return ctx;
182error:
183 list_del(&ctx->file_list);
184 kfree(ctx);
185
186 return NULL;
187}
188/*
189 * Event portion of the API, handle CM events
190 * and allow event polling.
191 */
192static void ib_ucm_event_path_get(struct ib_ucm_path_rec *upath,
193 struct ib_sa_path_rec *kpath)
194{
195 if (!kpath || !upath)
196 return;
197
b9ef520f
SH
198 memcpy(upath->dgid, kpath->dgid.raw, sizeof *upath->dgid);
199 memcpy(upath->sgid, kpath->sgid.raw, sizeof *upath->sgid);
a5b74540
HR
200
201 upath->dlid = kpath->dlid;
202 upath->slid = kpath->slid;
203 upath->raw_traffic = kpath->raw_traffic;
204 upath->flow_label = kpath->flow_label;
205 upath->hop_limit = kpath->hop_limit;
206 upath->traffic_class = kpath->traffic_class;
207 upath->reversible = kpath->reversible;
208 upath->numb_path = kpath->numb_path;
209 upath->pkey = kpath->pkey;
210 upath->sl = kpath->sl;
211 upath->mtu_selector = kpath->mtu_selector;
212 upath->mtu = kpath->mtu;
213 upath->rate_selector = kpath->rate_selector;
214 upath->rate = kpath->rate;
215 upath->packet_life_time = kpath->packet_life_time;
216 upath->preference = kpath->preference;
217
218 upath->packet_life_time_selector =
219 kpath->packet_life_time_selector;
220}
221
b9ef520f
SH
222static void ib_ucm_event_req_get(struct ib_ucm_context *ctx,
223 struct ib_ucm_req_event_resp *ureq,
a5b74540
HR
224 struct ib_cm_req_event_param *kreq)
225{
b9ef520f 226 ureq->listen_id = ctx->id;
a5b74540
HR
227
228 ureq->remote_ca_guid = kreq->remote_ca_guid;
229 ureq->remote_qkey = kreq->remote_qkey;
230 ureq->remote_qpn = kreq->remote_qpn;
231 ureq->qp_type = kreq->qp_type;
232 ureq->starting_psn = kreq->starting_psn;
233 ureq->responder_resources = kreq->responder_resources;
234 ureq->initiator_depth = kreq->initiator_depth;
235 ureq->local_cm_response_timeout = kreq->local_cm_response_timeout;
236 ureq->flow_control = kreq->flow_control;
237 ureq->remote_cm_response_timeout = kreq->remote_cm_response_timeout;
238 ureq->retry_count = kreq->retry_count;
239 ureq->rnr_retry_count = kreq->rnr_retry_count;
240 ureq->srq = kreq->srq;
241
242 ib_ucm_event_path_get(&ureq->primary_path, kreq->primary_path);
243 ib_ucm_event_path_get(&ureq->alternate_path, kreq->alternate_path);
244}
245
246static void ib_ucm_event_rep_get(struct ib_ucm_rep_event_resp *urep,
247 struct ib_cm_rep_event_param *krep)
248{
249 urep->remote_ca_guid = krep->remote_ca_guid;
250 urep->remote_qkey = krep->remote_qkey;
251 urep->remote_qpn = krep->remote_qpn;
252 urep->starting_psn = krep->starting_psn;
253 urep->responder_resources = krep->responder_resources;
254 urep->initiator_depth = krep->initiator_depth;
255 urep->target_ack_delay = krep->target_ack_delay;
256 urep->failover_accepted = krep->failover_accepted;
257 urep->flow_control = krep->flow_control;
258 urep->rnr_retry_count = krep->rnr_retry_count;
259 urep->srq = krep->srq;
260}
261
b9ef520f
SH
262static void ib_ucm_event_sidr_req_get(struct ib_ucm_context *ctx,
263 struct ib_ucm_sidr_req_event_resp *ureq,
a5b74540
HR
264 struct ib_cm_sidr_req_event_param *kreq)
265{
b9ef520f 266 ureq->listen_id = ctx->id;
a5b74540
HR
267 ureq->pkey = kreq->pkey;
268}
269
270static void ib_ucm_event_sidr_rep_get(struct ib_ucm_sidr_rep_event_resp *urep,
271 struct ib_cm_sidr_rep_event_param *krep)
272{
273 urep->status = krep->status;
274 urep->qkey = krep->qkey;
275 urep->qpn = krep->qpn;
276};
277
b9ef520f
SH
278static int ib_ucm_event_process(struct ib_ucm_context *ctx,
279 struct ib_cm_event *evt,
a5b74540
HR
280 struct ib_ucm_event *uvt)
281{
282 void *info = NULL;
a5b74540
HR
283
284 switch (evt->event) {
285 case IB_CM_REQ_RECEIVED:
b9ef520f 286 ib_ucm_event_req_get(ctx, &uvt->resp.u.req_resp,
a5b74540
HR
287 &evt->param.req_rcvd);
288 uvt->data_len = IB_CM_REQ_PRIVATE_DATA_SIZE;
b9ef520f 289 uvt->resp.present = IB_UCM_PRES_PRIMARY;
a5b74540
HR
290 uvt->resp.present |= (evt->param.req_rcvd.alternate_path ?
291 IB_UCM_PRES_ALTERNATE : 0);
292 break;
293 case IB_CM_REP_RECEIVED:
294 ib_ucm_event_rep_get(&uvt->resp.u.rep_resp,
295 &evt->param.rep_rcvd);
296 uvt->data_len = IB_CM_REP_PRIVATE_DATA_SIZE;
a5b74540
HR
297 break;
298 case IB_CM_RTU_RECEIVED:
299 uvt->data_len = IB_CM_RTU_PRIVATE_DATA_SIZE;
300 uvt->resp.u.send_status = evt->param.send_status;
a5b74540
HR
301 break;
302 case IB_CM_DREQ_RECEIVED:
303 uvt->data_len = IB_CM_DREQ_PRIVATE_DATA_SIZE;
304 uvt->resp.u.send_status = evt->param.send_status;
a5b74540
HR
305 break;
306 case IB_CM_DREP_RECEIVED:
307 uvt->data_len = IB_CM_DREP_PRIVATE_DATA_SIZE;
308 uvt->resp.u.send_status = evt->param.send_status;
a5b74540
HR
309 break;
310 case IB_CM_MRA_RECEIVED:
b9ef520f
SH
311 uvt->resp.u.mra_resp.timeout =
312 evt->param.mra_rcvd.service_timeout;
a5b74540 313 uvt->data_len = IB_CM_MRA_PRIVATE_DATA_SIZE;
a5b74540
HR
314 break;
315 case IB_CM_REJ_RECEIVED:
b9ef520f 316 uvt->resp.u.rej_resp.reason = evt->param.rej_rcvd.reason;
a5b74540
HR
317 uvt->data_len = IB_CM_REJ_PRIVATE_DATA_SIZE;
318 uvt->info_len = evt->param.rej_rcvd.ari_length;
319 info = evt->param.rej_rcvd.ari;
a5b74540
HR
320 break;
321 case IB_CM_LAP_RECEIVED:
b9ef520f
SH
322 ib_ucm_event_path_get(&uvt->resp.u.lap_resp.path,
323 evt->param.lap_rcvd.alternate_path);
a5b74540 324 uvt->data_len = IB_CM_LAP_PRIVATE_DATA_SIZE;
b9ef520f 325 uvt->resp.present = IB_UCM_PRES_ALTERNATE;
a5b74540
HR
326 break;
327 case IB_CM_APR_RECEIVED:
b9ef520f 328 uvt->resp.u.apr_resp.status = evt->param.apr_rcvd.ap_status;
a5b74540
HR
329 uvt->data_len = IB_CM_APR_PRIVATE_DATA_SIZE;
330 uvt->info_len = evt->param.apr_rcvd.info_len;
331 info = evt->param.apr_rcvd.apr_info;
a5b74540
HR
332 break;
333 case IB_CM_SIDR_REQ_RECEIVED:
b9ef520f 334 ib_ucm_event_sidr_req_get(ctx, &uvt->resp.u.sidr_req_resp,
a5b74540
HR
335 &evt->param.sidr_req_rcvd);
336 uvt->data_len = IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE;
a5b74540
HR
337 break;
338 case IB_CM_SIDR_REP_RECEIVED:
339 ib_ucm_event_sidr_rep_get(&uvt->resp.u.sidr_rep_resp,
340 &evt->param.sidr_rep_rcvd);
341 uvt->data_len = IB_CM_SIDR_REP_PRIVATE_DATA_SIZE;
342 uvt->info_len = evt->param.sidr_rep_rcvd.info_len;
343 info = evt->param.sidr_rep_rcvd.info;
a5b74540
HR
344 break;
345 default:
346 uvt->resp.u.send_status = evt->param.send_status;
a5b74540
HR
347 break;
348 }
349
b9ef520f 350 if (uvt->data_len) {
a5b74540 351 uvt->data = kmalloc(uvt->data_len, GFP_KERNEL);
b9ef520f
SH
352 if (!uvt->data)
353 goto err1;
a5b74540
HR
354
355 memcpy(uvt->data, evt->private_data, uvt->data_len);
356 uvt->resp.present |= IB_UCM_PRES_DATA;
357 }
358
b9ef520f 359 if (uvt->info_len) {
a5b74540 360 uvt->info = kmalloc(uvt->info_len, GFP_KERNEL);
b9ef520f
SH
361 if (!uvt->info)
362 goto err2;
a5b74540
HR
363
364 memcpy(uvt->info, info, uvt->info_len);
365 uvt->resp.present |= IB_UCM_PRES_INFO;
366 }
a5b74540 367 return 0;
b9ef520f
SH
368
369err2:
79d81907 370 kfree(uvt->data);
b9ef520f
SH
371err1:
372 return -ENOMEM;
a5b74540
HR
373}
374
375static int ib_ucm_event_handler(struct ib_cm_id *cm_id,
376 struct ib_cm_event *event)
377{
378 struct ib_ucm_event *uevent;
379 struct ib_ucm_context *ctx;
380 int result = 0;
381 int id;
a5b74540 382
b9ef520f 383 ctx = cm_id->context;
a5b74540
HR
384
385 if (event->event == IB_CM_REQ_RECEIVED ||
386 event->event == IB_CM_SIDR_REQ_RECEIVED)
387 id = IB_UCM_CM_ID_INVALID;
b9ef520f
SH
388 else
389 id = ctx->id;
a5b74540
HR
390
391 uevent = kmalloc(sizeof(*uevent), GFP_KERNEL);
b9ef520f
SH
392 if (!uevent)
393 goto err1;
a5b74540
HR
394
395 memset(uevent, 0, sizeof(*uevent));
a5b74540
HR
396 uevent->resp.id = id;
397 uevent->resp.event = event->event;
398
b9ef520f 399 result = ib_ucm_event_process(ctx, event, uevent);
a5b74540 400 if (result)
b9ef520f 401 goto err2;
a5b74540
HR
402
403 uevent->ctx = ctx;
b9ef520f 404 uevent->cm_id = (id == IB_UCM_CM_ID_INVALID) ? cm_id : NULL;
a5b74540
HR
405
406 down(&ctx->file->mutex);
a5b74540
HR
407 list_add_tail(&uevent->file_list, &ctx->file->events);
408 list_add_tail(&uevent->ctx_list, &ctx->events);
a5b74540 409 wake_up_interruptible(&ctx->file->poll_wait);
a5b74540 410 up(&ctx->file->mutex);
b9ef520f
SH
411 return 0;
412
413err2:
414 kfree(uevent);
415err1:
416 /* Destroy new cm_id's */
417 return (id == IB_UCM_CM_ID_INVALID);
a5b74540
HR
418}
419
420static ssize_t ib_ucm_event(struct ib_ucm_file *file,
421 const char __user *inbuf,
422 int in_len, int out_len)
423{
424 struct ib_ucm_context *ctx;
425 struct ib_ucm_event_get cmd;
426 struct ib_ucm_event *uevent = NULL;
427 int result = 0;
428 DEFINE_WAIT(wait);
429
430 if (out_len < sizeof(struct ib_ucm_event_resp))
431 return -ENOSPC;
432
433 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
434 return -EFAULT;
435 /*
436 * wait
437 */
438 down(&file->mutex);
439
440 while (list_empty(&file->events)) {
441
442 if (file->filp->f_flags & O_NONBLOCK) {
443 result = -EAGAIN;
444 break;
445 }
446
447 if (signal_pending(current)) {
448 result = -ERESTARTSYS;
449 break;
450 }
451
452 prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE);
453
454 up(&file->mutex);
455 schedule();
456 down(&file->mutex);
457
458 finish_wait(&file->poll_wait, &wait);
459 }
460
461 if (result)
462 goto done;
463
464 uevent = list_entry(file->events.next, struct ib_ucm_event, file_list);
465
466 if (!uevent->cm_id)
467 goto user;
468
469 ctx = ib_ucm_ctx_alloc(file);
470 if (!ctx) {
471 result = -ENOMEM;
472 goto done;
473 }
474
b9ef520f
SH
475 ctx->cm_id = uevent->cm_id;
476 ctx->cm_id->context = ctx;
a5b74540
HR
477
478 uevent->resp.id = ctx->id;
479
480user:
481 if (copy_to_user((void __user *)(unsigned long)cmd.response,
482 &uevent->resp, sizeof(uevent->resp))) {
483 result = -EFAULT;
484 goto done;
485 }
486
487 if (uevent->data) {
488
489 if (cmd.data_len < uevent->data_len) {
490 result = -ENOMEM;
491 goto done;
492 }
493
494 if (copy_to_user((void __user *)(unsigned long)cmd.data,
495 uevent->data, uevent->data_len)) {
496 result = -EFAULT;
497 goto done;
498 }
499 }
500
501 if (uevent->info) {
502
503 if (cmd.info_len < uevent->info_len) {
504 result = -ENOMEM;
505 goto done;
506 }
507
508 if (copy_to_user((void __user *)(unsigned long)cmd.info,
509 uevent->info, uevent->info_len)) {
510 result = -EFAULT;
511 goto done;
512 }
513 }
514
515 list_del(&uevent->file_list);
516 list_del(&uevent->ctx_list);
517
79d81907
HR
518 kfree(uevent->data);
519 kfree(uevent->info);
a5b74540
HR
520 kfree(uevent);
521done:
522 up(&file->mutex);
523 return result;
524}
525
526
527static ssize_t ib_ucm_create_id(struct ib_ucm_file *file,
528 const char __user *inbuf,
529 int in_len, int out_len)
530{
531 struct ib_ucm_create_id cmd;
532 struct ib_ucm_create_id_resp resp;
533 struct ib_ucm_context *ctx;
534 int result;
535
536 if (out_len < sizeof(resp))
537 return -ENOSPC;
538
539 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
540 return -EFAULT;
541
b9ef520f 542 down(&file->mutex);
a5b74540 543 ctx = ib_ucm_ctx_alloc(file);
b9ef520f 544 up(&file->mutex);
a5b74540
HR
545 if (!ctx)
546 return -ENOMEM;
547
b9ef520f
SH
548 ctx->cm_id = ib_create_cm_id(ib_ucm_event_handler, ctx);
549 if (IS_ERR(ctx->cm_id)) {
550 result = PTR_ERR(ctx->cm_id);
551 goto err;
a5b74540
HR
552 }
553
554 resp.id = ctx->id;
555 if (copy_to_user((void __user *)(unsigned long)cmd.response,
556 &resp, sizeof(resp))) {
557 result = -EFAULT;
b9ef520f 558 goto err;
a5b74540
HR
559 }
560
561 return 0;
a5b74540 562
b9ef520f
SH
563err:
564 ib_ucm_destroy_ctx(file, ctx->id);
a5b74540
HR
565 return result;
566}
567
568static ssize_t ib_ucm_destroy_id(struct ib_ucm_file *file,
569 const char __user *inbuf,
570 int in_len, int out_len)
571{
572 struct ib_ucm_destroy_id cmd;
a5b74540
HR
573
574 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
575 return -EFAULT;
576
b9ef520f 577 return ib_ucm_destroy_ctx(file, cmd.id);
a5b74540
HR
578}
579
580static ssize_t ib_ucm_attr_id(struct ib_ucm_file *file,
581 const char __user *inbuf,
582 int in_len, int out_len)
583{
584 struct ib_ucm_attr_id_resp resp;
585 struct ib_ucm_attr_id cmd;
586 struct ib_ucm_context *ctx;
587 int result = 0;
588
589 if (out_len < sizeof(resp))
590 return -ENOSPC;
591
592 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
593 return -EFAULT;
594
b9ef520f
SH
595 ctx = ib_ucm_ctx_get(file, cmd.id);
596 if (IS_ERR(ctx))
597 return PTR_ERR(ctx);
a5b74540
HR
598
599 resp.service_id = ctx->cm_id->service_id;
600 resp.service_mask = ctx->cm_id->service_mask;
601 resp.local_id = ctx->cm_id->local_id;
602 resp.remote_id = ctx->cm_id->remote_id;
603
604 if (copy_to_user((void __user *)(unsigned long)cmd.response,
605 &resp, sizeof(resp)))
606 result = -EFAULT;
607
b9ef520f 608 ib_ucm_ctx_put(ctx);
a5b74540
HR
609 return result;
610}
611
612static ssize_t ib_ucm_listen(struct ib_ucm_file *file,
613 const char __user *inbuf,
614 int in_len, int out_len)
615{
616 struct ib_ucm_listen cmd;
617 struct ib_ucm_context *ctx;
618 int result;
619
620 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
621 return -EFAULT;
622
b9ef520f
SH
623 ctx = ib_ucm_ctx_get(file, cmd.id);
624 if (IS_ERR(ctx))
625 return PTR_ERR(ctx);
a5b74540 626
b9ef520f
SH
627 result = ib_cm_listen(ctx->cm_id, cmd.service_id, cmd.service_mask);
628 ib_ucm_ctx_put(ctx);
a5b74540
HR
629 return result;
630}
631
632static ssize_t ib_ucm_establish(struct ib_ucm_file *file,
633 const char __user *inbuf,
634 int in_len, int out_len)
635{
636 struct ib_ucm_establish cmd;
637 struct ib_ucm_context *ctx;
638 int result;
639
640 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
641 return -EFAULT;
642
b9ef520f
SH
643 ctx = ib_ucm_ctx_get(file, cmd.id);
644 if (IS_ERR(ctx))
645 return PTR_ERR(ctx);
a5b74540 646
b9ef520f
SH
647 result = ib_cm_establish(ctx->cm_id);
648 ib_ucm_ctx_put(ctx);
a5b74540
HR
649 return result;
650}
651
652static int ib_ucm_alloc_data(const void **dest, u64 src, u32 len)
653{
654 void *data;
655
656 *dest = NULL;
657
658 if (!len)
659 return 0;
660
661 data = kmalloc(len, GFP_KERNEL);
662 if (!data)
663 return -ENOMEM;
664
665 if (copy_from_user(data, (void __user *)(unsigned long)src, len)) {
666 kfree(data);
667 return -EFAULT;
668 }
669
670 *dest = data;
671 return 0;
672}
673
674static int ib_ucm_path_get(struct ib_sa_path_rec **path, u64 src)
675{
676 struct ib_ucm_path_rec ucm_path;
677 struct ib_sa_path_rec *sa_path;
678
679 *path = NULL;
680
681 if (!src)
682 return 0;
683
684 sa_path = kmalloc(sizeof(*sa_path), GFP_KERNEL);
685 if (!sa_path)
686 return -ENOMEM;
687
688 if (copy_from_user(&ucm_path, (void __user *)(unsigned long)src,
689 sizeof(ucm_path))) {
690
691 kfree(sa_path);
692 return -EFAULT;
693 }
694
b9ef520f
SH
695 memcpy(sa_path->dgid.raw, ucm_path.dgid, sizeof sa_path->dgid);
696 memcpy(sa_path->sgid.raw, ucm_path.sgid, sizeof sa_path->sgid);
a5b74540
HR
697
698 sa_path->dlid = ucm_path.dlid;
699 sa_path->slid = ucm_path.slid;
700 sa_path->raw_traffic = ucm_path.raw_traffic;
701 sa_path->flow_label = ucm_path.flow_label;
702 sa_path->hop_limit = ucm_path.hop_limit;
703 sa_path->traffic_class = ucm_path.traffic_class;
704 sa_path->reversible = ucm_path.reversible;
705 sa_path->numb_path = ucm_path.numb_path;
706 sa_path->pkey = ucm_path.pkey;
707 sa_path->sl = ucm_path.sl;
708 sa_path->mtu_selector = ucm_path.mtu_selector;
709 sa_path->mtu = ucm_path.mtu;
710 sa_path->rate_selector = ucm_path.rate_selector;
711 sa_path->rate = ucm_path.rate;
712 sa_path->packet_life_time = ucm_path.packet_life_time;
713 sa_path->preference = ucm_path.preference;
714
715 sa_path->packet_life_time_selector =
716 ucm_path.packet_life_time_selector;
717
718 *path = sa_path;
719 return 0;
720}
721
722static ssize_t ib_ucm_send_req(struct ib_ucm_file *file,
723 const char __user *inbuf,
724 int in_len, int out_len)
725{
726 struct ib_cm_req_param param;
727 struct ib_ucm_context *ctx;
728 struct ib_ucm_req cmd;
729 int result;
730
731 param.private_data = NULL;
732 param.primary_path = NULL;
733 param.alternate_path = NULL;
734
735 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
736 return -EFAULT;
737
738 result = ib_ucm_alloc_data(&param.private_data, cmd.data, cmd.len);
739 if (result)
740 goto done;
741
742 result = ib_ucm_path_get(&param.primary_path, cmd.primary_path);
743 if (result)
744 goto done;
745
746 result = ib_ucm_path_get(&param.alternate_path, cmd.alternate_path);
747 if (result)
748 goto done;
749
750 param.private_data_len = cmd.len;
751 param.service_id = cmd.sid;
752 param.qp_num = cmd.qpn;
753 param.qp_type = cmd.qp_type;
754 param.starting_psn = cmd.psn;
755 param.peer_to_peer = cmd.peer_to_peer;
756 param.responder_resources = cmd.responder_resources;
757 param.initiator_depth = cmd.initiator_depth;
758 param.remote_cm_response_timeout = cmd.remote_cm_response_timeout;
759 param.flow_control = cmd.flow_control;
760 param.local_cm_response_timeout = cmd.local_cm_response_timeout;
761 param.retry_count = cmd.retry_count;
762 param.rnr_retry_count = cmd.rnr_retry_count;
763 param.max_cm_retries = cmd.max_cm_retries;
764 param.srq = cmd.srq;
765
b9ef520f
SH
766 ctx = ib_ucm_ctx_get(file, cmd.id);
767 if (!IS_ERR(ctx)) {
a5b74540 768 result = ib_send_cm_req(ctx->cm_id, &param);
b9ef520f
SH
769 ib_ucm_ctx_put(ctx);
770 } else
771 result = PTR_ERR(ctx);
a5b74540 772
a5b74540 773done:
79d81907
HR
774 kfree(param.private_data);
775 kfree(param.primary_path);
776 kfree(param.alternate_path);
a5b74540
HR
777 return result;
778}
779
780static ssize_t ib_ucm_send_rep(struct ib_ucm_file *file,
781 const char __user *inbuf,
782 int in_len, int out_len)
783{
784 struct ib_cm_rep_param param;
785 struct ib_ucm_context *ctx;
786 struct ib_ucm_rep cmd;
787 int result;
788
789 param.private_data = NULL;
790
791 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
792 return -EFAULT;
793
794 result = ib_ucm_alloc_data(&param.private_data, cmd.data, cmd.len);
795 if (result)
796 return result;
797
798 param.qp_num = cmd.qpn;
799 param.starting_psn = cmd.psn;
800 param.private_data_len = cmd.len;
801 param.responder_resources = cmd.responder_resources;
802 param.initiator_depth = cmd.initiator_depth;
803 param.target_ack_delay = cmd.target_ack_delay;
804 param.failover_accepted = cmd.failover_accepted;
805 param.flow_control = cmd.flow_control;
806 param.rnr_retry_count = cmd.rnr_retry_count;
807 param.srq = cmd.srq;
808
b9ef520f
SH
809 ctx = ib_ucm_ctx_get(file, cmd.id);
810 if (!IS_ERR(ctx)) {
a5b74540 811 result = ib_send_cm_rep(ctx->cm_id, &param);
b9ef520f
SH
812 ib_ucm_ctx_put(ctx);
813 } else
814 result = PTR_ERR(ctx);
a5b74540 815
79d81907 816 kfree(param.private_data);
a5b74540
HR
817 return result;
818}
819
820static ssize_t ib_ucm_send_private_data(struct ib_ucm_file *file,
821 const char __user *inbuf, int in_len,
822 int (*func)(struct ib_cm_id *cm_id,
823 const void *private_data,
824 u8 private_data_len))
825{
826 struct ib_ucm_private_data cmd;
827 struct ib_ucm_context *ctx;
828 const void *private_data = NULL;
829 int result;
830
831 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
832 return -EFAULT;
833
834 result = ib_ucm_alloc_data(&private_data, cmd.data, cmd.len);
835 if (result)
836 return result;
837
b9ef520f
SH
838 ctx = ib_ucm_ctx_get(file, cmd.id);
839 if (!IS_ERR(ctx)) {
a5b74540 840 result = func(ctx->cm_id, private_data, cmd.len);
b9ef520f
SH
841 ib_ucm_ctx_put(ctx);
842 } else
843 result = PTR_ERR(ctx);
a5b74540 844
79d81907 845 kfree(private_data);
a5b74540
HR
846 return result;
847}
848
849static ssize_t ib_ucm_send_rtu(struct ib_ucm_file *file,
850 const char __user *inbuf,
851 int in_len, int out_len)
852{
853 return ib_ucm_send_private_data(file, inbuf, in_len, ib_send_cm_rtu);
854}
855
856static ssize_t ib_ucm_send_dreq(struct ib_ucm_file *file,
857 const char __user *inbuf,
858 int in_len, int out_len)
859{
860 return ib_ucm_send_private_data(file, inbuf, in_len, ib_send_cm_dreq);
861}
862
863static ssize_t ib_ucm_send_drep(struct ib_ucm_file *file,
864 const char __user *inbuf,
865 int in_len, int out_len)
866{
867 return ib_ucm_send_private_data(file, inbuf, in_len, ib_send_cm_drep);
868}
869
870static ssize_t ib_ucm_send_info(struct ib_ucm_file *file,
871 const char __user *inbuf, int in_len,
872 int (*func)(struct ib_cm_id *cm_id,
873 int status,
874 const void *info,
875 u8 info_len,
876 const void *data,
877 u8 data_len))
878{
879 struct ib_ucm_context *ctx;
880 struct ib_ucm_info cmd;
881 const void *data = NULL;
882 const void *info = NULL;
883 int result;
884
885 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
886 return -EFAULT;
887
888 result = ib_ucm_alloc_data(&data, cmd.data, cmd.data_len);
889 if (result)
890 goto done;
891
892 result = ib_ucm_alloc_data(&info, cmd.info, cmd.info_len);
893 if (result)
894 goto done;
895
b9ef520f
SH
896 ctx = ib_ucm_ctx_get(file, cmd.id);
897 if (!IS_ERR(ctx)) {
898 result = func(ctx->cm_id, cmd.status, info, cmd.info_len,
a5b74540 899 data, cmd.data_len);
b9ef520f
SH
900 ib_ucm_ctx_put(ctx);
901 } else
902 result = PTR_ERR(ctx);
a5b74540 903
a5b74540 904done:
79d81907
HR
905 kfree(data);
906 kfree(info);
a5b74540
HR
907 return result;
908}
909
910static ssize_t ib_ucm_send_rej(struct ib_ucm_file *file,
911 const char __user *inbuf,
912 int in_len, int out_len)
913{
914 return ib_ucm_send_info(file, inbuf, in_len, (void *)ib_send_cm_rej);
915}
916
917static ssize_t ib_ucm_send_apr(struct ib_ucm_file *file,
918 const char __user *inbuf,
919 int in_len, int out_len)
920{
921 return ib_ucm_send_info(file, inbuf, in_len, (void *)ib_send_cm_apr);
922}
923
924static ssize_t ib_ucm_send_mra(struct ib_ucm_file *file,
925 const char __user *inbuf,
926 int in_len, int out_len)
927{
928 struct ib_ucm_context *ctx;
929 struct ib_ucm_mra cmd;
930 const void *data = NULL;
931 int result;
932
933 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
934 return -EFAULT;
935
936 result = ib_ucm_alloc_data(&data, cmd.data, cmd.len);
937 if (result)
938 return result;
939
b9ef520f
SH
940 ctx = ib_ucm_ctx_get(file, cmd.id);
941 if (!IS_ERR(ctx)) {
942 result = ib_send_cm_mra(ctx->cm_id, cmd.timeout, data, cmd.len);
943 ib_ucm_ctx_put(ctx);
944 } else
945 result = PTR_ERR(ctx);
a5b74540 946
79d81907 947 kfree(data);
a5b74540
HR
948 return result;
949}
950
951static ssize_t ib_ucm_send_lap(struct ib_ucm_file *file,
952 const char __user *inbuf,
953 int in_len, int out_len)
954{
955 struct ib_ucm_context *ctx;
956 struct ib_sa_path_rec *path = NULL;
957 struct ib_ucm_lap cmd;
958 const void *data = NULL;
959 int result;
960
961 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
962 return -EFAULT;
963
964 result = ib_ucm_alloc_data(&data, cmd.data, cmd.len);
965 if (result)
966 goto done;
967
968 result = ib_ucm_path_get(&path, cmd.path);
969 if (result)
970 goto done;
971
b9ef520f
SH
972 ctx = ib_ucm_ctx_get(file, cmd.id);
973 if (!IS_ERR(ctx)) {
a5b74540 974 result = ib_send_cm_lap(ctx->cm_id, path, data, cmd.len);
b9ef520f
SH
975 ib_ucm_ctx_put(ctx);
976 } else
977 result = PTR_ERR(ctx);
a5b74540 978
a5b74540 979done:
79d81907
HR
980 kfree(data);
981 kfree(path);
a5b74540
HR
982 return result;
983}
984
985static ssize_t ib_ucm_send_sidr_req(struct ib_ucm_file *file,
986 const char __user *inbuf,
987 int in_len, int out_len)
988{
989 struct ib_cm_sidr_req_param param;
990 struct ib_ucm_context *ctx;
991 struct ib_ucm_sidr_req cmd;
992 int result;
993
994 param.private_data = NULL;
995 param.path = NULL;
996
997 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
998 return -EFAULT;
999
1000 result = ib_ucm_alloc_data(&param.private_data, cmd.data, cmd.len);
1001 if (result)
1002 goto done;
1003
1004 result = ib_ucm_path_get(&param.path, cmd.path);
1005 if (result)
1006 goto done;
1007
1008 param.private_data_len = cmd.len;
1009 param.service_id = cmd.sid;
1010 param.timeout_ms = cmd.timeout;
1011 param.max_cm_retries = cmd.max_cm_retries;
1012 param.pkey = cmd.pkey;
1013
b9ef520f
SH
1014 ctx = ib_ucm_ctx_get(file, cmd.id);
1015 if (!IS_ERR(ctx)) {
a5b74540 1016 result = ib_send_cm_sidr_req(ctx->cm_id, &param);
b9ef520f
SH
1017 ib_ucm_ctx_put(ctx);
1018 } else
1019 result = PTR_ERR(ctx);
a5b74540 1020
a5b74540 1021done:
79d81907
HR
1022 kfree(param.private_data);
1023 kfree(param.path);
a5b74540
HR
1024 return result;
1025}
1026
1027static ssize_t ib_ucm_send_sidr_rep(struct ib_ucm_file *file,
1028 const char __user *inbuf,
1029 int in_len, int out_len)
1030{
1031 struct ib_cm_sidr_rep_param param;
1032 struct ib_ucm_sidr_rep cmd;
1033 struct ib_ucm_context *ctx;
1034 int result;
1035
1036 param.info = NULL;
1037
1038 if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
1039 return -EFAULT;
1040
1041 result = ib_ucm_alloc_data(&param.private_data,
1042 cmd.data, cmd.data_len);
1043 if (result)
1044 goto done;
1045
1046 result = ib_ucm_alloc_data(&param.info, cmd.info, cmd.info_len);
1047 if (result)
1048 goto done;
1049
b9ef520f
SH
1050 param.qp_num = cmd.qpn;
1051 param.qkey = cmd.qkey;
1052 param.status = cmd.status;
1053 param.info_length = cmd.info_len;
1054 param.private_data_len = cmd.data_len;
a5b74540 1055
b9ef520f
SH
1056 ctx = ib_ucm_ctx_get(file, cmd.id);
1057 if (!IS_ERR(ctx)) {
a5b74540 1058 result = ib_send_cm_sidr_rep(ctx->cm_id, &param);
b9ef520f
SH
1059 ib_ucm_ctx_put(ctx);
1060 } else
1061 result = PTR_ERR(ctx);
a5b74540 1062
a5b74540 1063done:
79d81907
HR
1064 kfree(param.private_data);
1065 kfree(param.info);
a5b74540
HR
1066 return result;
1067}
1068
1069static ssize_t (*ucm_cmd_table[])(struct ib_ucm_file *file,
1070 const char __user *inbuf,
1071 int in_len, int out_len) = {
1072 [IB_USER_CM_CMD_CREATE_ID] = ib_ucm_create_id,
1073 [IB_USER_CM_CMD_DESTROY_ID] = ib_ucm_destroy_id,
1074 [IB_USER_CM_CMD_ATTR_ID] = ib_ucm_attr_id,
1075 [IB_USER_CM_CMD_LISTEN] = ib_ucm_listen,
1076 [IB_USER_CM_CMD_ESTABLISH] = ib_ucm_establish,
1077 [IB_USER_CM_CMD_SEND_REQ] = ib_ucm_send_req,
1078 [IB_USER_CM_CMD_SEND_REP] = ib_ucm_send_rep,
1079 [IB_USER_CM_CMD_SEND_RTU] = ib_ucm_send_rtu,
1080 [IB_USER_CM_CMD_SEND_DREQ] = ib_ucm_send_dreq,
1081 [IB_USER_CM_CMD_SEND_DREP] = ib_ucm_send_drep,
1082 [IB_USER_CM_CMD_SEND_REJ] = ib_ucm_send_rej,
1083 [IB_USER_CM_CMD_SEND_MRA] = ib_ucm_send_mra,
1084 [IB_USER_CM_CMD_SEND_LAP] = ib_ucm_send_lap,
1085 [IB_USER_CM_CMD_SEND_APR] = ib_ucm_send_apr,
1086 [IB_USER_CM_CMD_SEND_SIDR_REQ] = ib_ucm_send_sidr_req,
1087 [IB_USER_CM_CMD_SEND_SIDR_REP] = ib_ucm_send_sidr_rep,
1088 [IB_USER_CM_CMD_EVENT] = ib_ucm_event,
1089};
1090
1091static ssize_t ib_ucm_write(struct file *filp, const char __user *buf,
1092 size_t len, loff_t *pos)
1093{
1094 struct ib_ucm_file *file = filp->private_data;
1095 struct ib_ucm_cmd_hdr hdr;
1096 ssize_t result;
1097
1098 if (len < sizeof(hdr))
1099 return -EINVAL;
1100
1101 if (copy_from_user(&hdr, buf, sizeof(hdr)))
1102 return -EFAULT;
1103
79d81907
HR
1104 ucm_dbg("Write. cmd <%d> in <%d> out <%d> len <%Zu>\n",
1105 hdr.cmd, hdr.in, hdr.out, len);
a5b74540
HR
1106
1107 if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucm_cmd_table))
1108 return -EINVAL;
1109
1110 if (hdr.in + sizeof(hdr) > len)
1111 return -EINVAL;
1112
1113 result = ucm_cmd_table[hdr.cmd](file, buf + sizeof(hdr),
1114 hdr.in, hdr.out);
1115 if (!result)
1116 result = len;
1117
1118 return result;
1119}
1120
1121static unsigned int ib_ucm_poll(struct file *filp,
1122 struct poll_table_struct *wait)
1123{
1124 struct ib_ucm_file *file = filp->private_data;
1125 unsigned int mask = 0;
1126
1127 poll_wait(filp, &file->poll_wait, wait);
1128
1129 if (!list_empty(&file->events))
1130 mask = POLLIN | POLLRDNORM;
1131
1132 return mask;
1133}
1134
1135static int ib_ucm_open(struct inode *inode, struct file *filp)
1136{
1137 struct ib_ucm_file *file;
1138
1139 file = kmalloc(sizeof(*file), GFP_KERNEL);
1140 if (!file)
1141 return -ENOMEM;
1142
1143 INIT_LIST_HEAD(&file->events);
1144 INIT_LIST_HEAD(&file->ctxs);
1145 init_waitqueue_head(&file->poll_wait);
1146
1147 init_MUTEX(&file->mutex);
1148
1149 filp->private_data = file;
1150 file->filp = filp;
1151
79d81907 1152 ucm_dbg("Created struct\n");
a5b74540
HR
1153
1154 return 0;
1155}
1156
1157static int ib_ucm_close(struct inode *inode, struct file *filp)
1158{
1159 struct ib_ucm_file *file = filp->private_data;
1160 struct ib_ucm_context *ctx;
1161
1162 down(&file->mutex);
a5b74540
HR
1163 while (!list_empty(&file->ctxs)) {
1164
1165 ctx = list_entry(file->ctxs.next,
1166 struct ib_ucm_context, file_list);
1167
b9ef520f
SH
1168 up(&file->mutex);
1169 ib_ucm_destroy_ctx(file, ctx->id);
a5b74540
HR
1170 down(&file->mutex);
1171 }
a5b74540 1172 up(&file->mutex);
a5b74540 1173 kfree(file);
a5b74540
HR
1174 return 0;
1175}
1176
1177static struct file_operations ib_ucm_fops = {
1178 .owner = THIS_MODULE,
1179 .open = ib_ucm_open,
1180 .release = ib_ucm_close,
1181 .write = ib_ucm_write,
1182 .poll = ib_ucm_poll,
1183};
1184
1185
2d0d099f 1186static struct class *ib_ucm_class;
a5b74540
HR
1187static struct cdev ib_ucm_cdev;
1188
1189static int __init ib_ucm_init(void)
1190{
1191 int result;
1192
1193 result = register_chrdev_region(IB_UCM_DEV, 1, "infiniband_cm");
1194 if (result) {
79d81907 1195 ucm_dbg("Error <%d> registering dev\n", result);
a5b74540
HR
1196 goto err_chr;
1197 }
1198
1199 cdev_init(&ib_ucm_cdev, &ib_ucm_fops);
1200
1201 result = cdev_add(&ib_ucm_cdev, IB_UCM_DEV, 1);
1202 if (result) {
79d81907 1203 ucm_dbg("Error <%d> adding cdev\n", result);
a5b74540
HR
1204 goto err_cdev;
1205 }
1206
2d0d099f 1207 ib_ucm_class = class_create(THIS_MODULE, "infiniband_cm");
a5b74540
HR
1208 if (IS_ERR(ib_ucm_class)) {
1209 result = PTR_ERR(ib_ucm_class);
79d81907 1210 ucm_dbg("Error <%d> creating class\n", result);
a5b74540
HR
1211 goto err_class;
1212 }
1213
2d0d099f 1214 class_device_create(ib_ucm_class, IB_UCM_DEV, NULL, "ucm");
a5b74540
HR
1215
1216 idr_init(&ctx_id_table);
1217 init_MUTEX(&ctx_id_mutex);
1218
1219 return 0;
1220err_class:
1221 cdev_del(&ib_ucm_cdev);
1222err_cdev:
1223 unregister_chrdev_region(IB_UCM_DEV, 1);
1224err_chr:
1225 return result;
1226}
1227
1228static void __exit ib_ucm_cleanup(void)
1229{
2d0d099f
TD
1230 class_device_destroy(ib_ucm_class, IB_UCM_DEV);
1231 class_destroy(ib_ucm_class);
a5b74540
HR
1232 cdev_del(&ib_ucm_cdev);
1233 unregister_chrdev_region(IB_UCM_DEV, 1);
1234}
1235
1236module_init(ib_ucm_init);
1237module_exit(ib_ucm_cleanup);