1219820Sjeff/*
2219820Sjeff * Copyright (c) 2005 Topspin Communications.  All rights reserved.
3219820Sjeff * Copyright (c) 2005 Intel Corporation.  All rights reserved.
4219820Sjeff *
5219820Sjeff * This software is available to you under a choice of one of two
6219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
7219820Sjeff * General Public License (GPL) Version 2, available from the file
8219820Sjeff * COPYING in the main directory of this source tree, or the
9219820Sjeff * OpenIB.org BSD license below:
10219820Sjeff *
11219820Sjeff *     Redistribution and use in source and binary forms, with or
12219820Sjeff *     without modification, are permitted provided that the following
13219820Sjeff *     conditions are met:
14219820Sjeff *
15219820Sjeff *      - Redistributions of source code must retain the above
16219820Sjeff *	copyright notice, this list of conditions and the following
17219820Sjeff *	disclaimer.
18219820Sjeff *
19219820Sjeff *      - Redistributions in binary form must reproduce the above
20219820Sjeff *	copyright notice, this list of conditions and the following
21219820Sjeff *	disclaimer in the documentation and/or other materials
22219820Sjeff *	provided with the distribution.
23219820Sjeff *
24219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31219820Sjeff * SOFTWARE.
32219820Sjeff */
33219820Sjeff
34219820Sjeff#include <linux/completion.h>
35219820Sjeff#include <linux/init.h>
36219820Sjeff#include <linux/fs.h>
37219820Sjeff#include <linux/module.h>
38219820Sjeff#include <linux/device.h>
39219820Sjeff#include <linux/err.h>
40219820Sjeff#include <linux/poll.h>
41219820Sjeff#include <linux/file.h>
42219820Sjeff#include <linux/mount.h>
43219820Sjeff#include <linux/cdev.h>
44219820Sjeff#include <linux/idr.h>
45219820Sjeff#include <linux/mutex.h>
46219820Sjeff
47219820Sjeff#include <asm/uaccess.h>
48219820Sjeff
49219820Sjeff#include <rdma/ib_cm.h>
50219820Sjeff#include <rdma/ib_user_cm.h>
51219820Sjeff#include <rdma/ib_marshall.h>
52219820Sjeff
53219820SjeffMODULE_AUTHOR("Libor Michalek");
54219820SjeffMODULE_DESCRIPTION("InfiniBand userspace Connection Manager access");
55219820SjeffMODULE_LICENSE("Dual BSD/GPL");
56219820Sjeff
57219820Sjeffstruct ib_ucm_device {
58219820Sjeff	int			devnum;
59219820Sjeff	struct cdev		cdev;
60219820Sjeff	struct device		dev;
61219820Sjeff	struct ib_device	*ib_dev;
62219820Sjeff};
63219820Sjeff
64219820Sjeffstruct ib_ucm_file {
65219820Sjeff	struct mutex file_mutex;
66219820Sjeff	struct file *filp;
67219820Sjeff	struct ib_ucm_device *device;
68219820Sjeff
69219820Sjeff	struct list_head  ctxs;
70219820Sjeff	struct list_head  events;
71219820Sjeff	wait_queue_head_t poll_wait;
72219820Sjeff};
73219820Sjeff
74219820Sjeffstruct ib_ucm_context {
75219820Sjeff	int                 id;
76219820Sjeff	struct completion   comp;
77219820Sjeff	atomic_t            ref;
78219820Sjeff	int		    events_reported;
79219820Sjeff
80219820Sjeff	struct ib_ucm_file *file;
81219820Sjeff	struct ib_cm_id    *cm_id;
82219820Sjeff	__u64		   uid;
83219820Sjeff
84219820Sjeff	struct list_head    events;    /* list of pending events. */
85219820Sjeff	struct list_head    file_list; /* member in file ctx list */
86219820Sjeff};
87219820Sjeff
88219820Sjeffstruct ib_ucm_event {
89219820Sjeff	struct ib_ucm_context *ctx;
90219820Sjeff	struct list_head file_list; /* member in file event list */
91219820Sjeff	struct list_head ctx_list;  /* member in ctx event list */
92219820Sjeff
93219820Sjeff	struct ib_cm_id *cm_id;
94219820Sjeff	struct ib_ucm_event_resp resp;
95219820Sjeff	void *data;
96219820Sjeff	void *info;
97219820Sjeff	int data_len;
98219820Sjeff	int info_len;
99219820Sjeff};
100219820Sjeff
101219820Sjeffenum {
102219820Sjeff	IB_UCM_MAJOR = 231,
103219820Sjeff	IB_UCM_BASE_MINOR = 224,
104219820Sjeff	IB_UCM_MAX_DEVICES = 32
105219820Sjeff};
106219820Sjeff
107219820Sjeff/* ib_cm and ib_user_cm modules share /sys/class/infiniband_cm */
108219820Sjeffextern struct class cm_class;
109219820Sjeff
110219820Sjeff#define IB_UCM_BASE_DEV MKDEV(IB_UCM_MAJOR, IB_UCM_BASE_MINOR)
111219820Sjeff
112219820Sjeffstatic void ib_ucm_add_one(struct ib_device *device);
113219820Sjeffstatic void ib_ucm_remove_one(struct ib_device *device);
114219820Sjeff
115219820Sjeffstatic struct ib_client ucm_client = {
116219820Sjeff	.name   = "ucm",
117219820Sjeff	.add    = ib_ucm_add_one,
118219820Sjeff	.remove = ib_ucm_remove_one
119219820Sjeff};
120219820Sjeff
121219820Sjeffstatic DEFINE_MUTEX(ctx_id_mutex);
122219820Sjeffstatic DEFINE_IDR(ctx_id_table);
123219820Sjeffstatic DECLARE_BITMAP(dev_map, IB_UCM_MAX_DEVICES);
124219820Sjeff
125219820Sjeffstatic struct ib_ucm_context *ib_ucm_ctx_get(struct ib_ucm_file *file, int id)
126219820Sjeff{
127219820Sjeff	struct ib_ucm_context *ctx;
128219820Sjeff
129219820Sjeff	mutex_lock(&ctx_id_mutex);
130219820Sjeff	ctx = idr_find(&ctx_id_table, id);
131219820Sjeff	if (!ctx)
132219820Sjeff		ctx = ERR_PTR(-ENOENT);
133219820Sjeff	else if (ctx->file != file)
134219820Sjeff		ctx = ERR_PTR(-EINVAL);
135219820Sjeff	else
136219820Sjeff		atomic_inc(&ctx->ref);
137219820Sjeff	mutex_unlock(&ctx_id_mutex);
138219820Sjeff
139219820Sjeff	return ctx;
140219820Sjeff}
141219820Sjeff
142219820Sjeffstatic void ib_ucm_ctx_put(struct ib_ucm_context *ctx)
143219820Sjeff{
144219820Sjeff	if (atomic_dec_and_test(&ctx->ref))
145219820Sjeff		complete(&ctx->comp);
146219820Sjeff}
147219820Sjeff
148219820Sjeffstatic inline int ib_ucm_new_cm_id(int event)
149219820Sjeff{
150219820Sjeff	return event == IB_CM_REQ_RECEIVED || event == IB_CM_SIDR_REQ_RECEIVED;
151219820Sjeff}
152219820Sjeff
153219820Sjeffstatic void ib_ucm_cleanup_events(struct ib_ucm_context *ctx)
154219820Sjeff{
155219820Sjeff	struct ib_ucm_event *uevent;
156219820Sjeff
157219820Sjeff	mutex_lock(&ctx->file->file_mutex);
158219820Sjeff	list_del(&ctx->file_list);
159219820Sjeff	while (!list_empty(&ctx->events)) {
160219820Sjeff
161219820Sjeff		uevent = list_entry(ctx->events.next,
162219820Sjeff				    struct ib_ucm_event, ctx_list);
163219820Sjeff		list_del(&uevent->file_list);
164219820Sjeff		list_del(&uevent->ctx_list);
165219820Sjeff		mutex_unlock(&ctx->file->file_mutex);
166219820Sjeff
167219820Sjeff		/* clear incoming connections. */
168219820Sjeff		if (ib_ucm_new_cm_id(uevent->resp.event))
169219820Sjeff			ib_destroy_cm_id(uevent->cm_id);
170219820Sjeff
171219820Sjeff		kfree(uevent);
172219820Sjeff		mutex_lock(&ctx->file->file_mutex);
173219820Sjeff	}
174219820Sjeff	mutex_unlock(&ctx->file->file_mutex);
175219820Sjeff}
176219820Sjeff
177219820Sjeffstatic struct ib_ucm_context *ib_ucm_ctx_alloc(struct ib_ucm_file *file)
178219820Sjeff{
179219820Sjeff	struct ib_ucm_context *ctx;
180219820Sjeff	int result;
181219820Sjeff
182219820Sjeff	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
183219820Sjeff	if (!ctx)
184219820Sjeff		return NULL;
185219820Sjeff
186219820Sjeff	atomic_set(&ctx->ref, 1);
187219820Sjeff	init_completion(&ctx->comp);
188219820Sjeff	ctx->file = file;
189219820Sjeff	INIT_LIST_HEAD(&ctx->events);
190219820Sjeff
191219820Sjeff	do {
192219820Sjeff		result = idr_pre_get(&ctx_id_table, GFP_KERNEL);
193219820Sjeff		if (!result)
194219820Sjeff			goto error;
195219820Sjeff
196219820Sjeff		mutex_lock(&ctx_id_mutex);
197219820Sjeff		result = idr_get_new(&ctx_id_table, ctx, &ctx->id);
198219820Sjeff		mutex_unlock(&ctx_id_mutex);
199219820Sjeff	} while (result == -EAGAIN);
200219820Sjeff
201219820Sjeff	if (result)
202219820Sjeff		goto error;
203219820Sjeff
204219820Sjeff	list_add_tail(&ctx->file_list, &file->ctxs);
205219820Sjeff	return ctx;
206219820Sjeff
207219820Sjefferror:
208219820Sjeff	kfree(ctx);
209219820Sjeff	return NULL;
210219820Sjeff}
211219820Sjeff
212219820Sjeffstatic void ib_ucm_event_req_get(struct ib_ucm_req_event_resp *ureq,
213219820Sjeff				 struct ib_cm_req_event_param *kreq)
214219820Sjeff{
215219820Sjeff	ureq->remote_ca_guid             = kreq->remote_ca_guid;
216219820Sjeff	ureq->remote_qkey                = kreq->remote_qkey;
217219820Sjeff	ureq->remote_qpn                 = kreq->remote_qpn;
218219820Sjeff	ureq->qp_type                    = kreq->qp_type;
219219820Sjeff	ureq->starting_psn               = kreq->starting_psn;
220219820Sjeff	ureq->responder_resources        = kreq->responder_resources;
221219820Sjeff	ureq->initiator_depth            = kreq->initiator_depth;
222219820Sjeff	ureq->local_cm_response_timeout  = kreq->local_cm_response_timeout;
223219820Sjeff	ureq->flow_control               = kreq->flow_control;
224219820Sjeff	ureq->remote_cm_response_timeout = kreq->remote_cm_response_timeout;
225219820Sjeff	ureq->retry_count                = kreq->retry_count;
226219820Sjeff	ureq->rnr_retry_count            = kreq->rnr_retry_count;
227219820Sjeff	ureq->srq                        = kreq->srq;
228219820Sjeff	ureq->port			 = kreq->port;
229219820Sjeff
230219820Sjeff	ib_copy_path_rec_to_user(&ureq->primary_path, kreq->primary_path);
231219820Sjeff	if (kreq->alternate_path)
232219820Sjeff		ib_copy_path_rec_to_user(&ureq->alternate_path,
233219820Sjeff					 kreq->alternate_path);
234219820Sjeff}
235219820Sjeff
236219820Sjeffstatic void ib_ucm_event_rep_get(struct ib_ucm_rep_event_resp *urep,
237219820Sjeff				 struct ib_cm_rep_event_param *krep)
238219820Sjeff{
239219820Sjeff	urep->remote_ca_guid      = krep->remote_ca_guid;
240219820Sjeff	urep->remote_qkey         = krep->remote_qkey;
241219820Sjeff	urep->remote_qpn          = krep->remote_qpn;
242219820Sjeff	urep->starting_psn        = krep->starting_psn;
243219820Sjeff	urep->responder_resources = krep->responder_resources;
244219820Sjeff	urep->initiator_depth     = krep->initiator_depth;
245219820Sjeff	urep->target_ack_delay    = krep->target_ack_delay;
246219820Sjeff	urep->failover_accepted   = krep->failover_accepted;
247219820Sjeff	urep->flow_control        = krep->flow_control;
248219820Sjeff	urep->rnr_retry_count     = krep->rnr_retry_count;
249219820Sjeff	urep->srq                 = krep->srq;
250219820Sjeff}
251219820Sjeff
252219820Sjeffstatic void ib_ucm_event_sidr_rep_get(struct ib_ucm_sidr_rep_event_resp *urep,
253219820Sjeff				      struct ib_cm_sidr_rep_event_param *krep)
254219820Sjeff{
255219820Sjeff	urep->status = krep->status;
256219820Sjeff	urep->qkey   = krep->qkey;
257219820Sjeff	urep->qpn    = krep->qpn;
258219820Sjeff};
259219820Sjeff
260219820Sjeffstatic int ib_ucm_event_process(struct ib_cm_event *evt,
261219820Sjeff				struct ib_ucm_event *uvt)
262219820Sjeff{
263219820Sjeff	void *info = NULL;
264219820Sjeff
265219820Sjeff	switch (evt->event) {
266219820Sjeff	case IB_CM_REQ_RECEIVED:
267219820Sjeff		ib_ucm_event_req_get(&uvt->resp.u.req_resp,
268219820Sjeff				     &evt->param.req_rcvd);
269219820Sjeff		uvt->data_len      = IB_CM_REQ_PRIVATE_DATA_SIZE;
270219820Sjeff		uvt->resp.present  = IB_UCM_PRES_PRIMARY;
271219820Sjeff		uvt->resp.present |= (evt->param.req_rcvd.alternate_path ?
272219820Sjeff				      IB_UCM_PRES_ALTERNATE : 0);
273219820Sjeff		break;
274219820Sjeff	case IB_CM_REP_RECEIVED:
275219820Sjeff		ib_ucm_event_rep_get(&uvt->resp.u.rep_resp,
276219820Sjeff				     &evt->param.rep_rcvd);
277219820Sjeff		uvt->data_len = IB_CM_REP_PRIVATE_DATA_SIZE;
278219820Sjeff		break;
279219820Sjeff	case IB_CM_RTU_RECEIVED:
280219820Sjeff		uvt->data_len = IB_CM_RTU_PRIVATE_DATA_SIZE;
281219820Sjeff		uvt->resp.u.send_status = evt->param.send_status;
282219820Sjeff		break;
283219820Sjeff	case IB_CM_DREQ_RECEIVED:
284219820Sjeff		uvt->data_len = IB_CM_DREQ_PRIVATE_DATA_SIZE;
285219820Sjeff		uvt->resp.u.send_status = evt->param.send_status;
286219820Sjeff		break;
287219820Sjeff	case IB_CM_DREP_RECEIVED:
288219820Sjeff		uvt->data_len = IB_CM_DREP_PRIVATE_DATA_SIZE;
289219820Sjeff		uvt->resp.u.send_status = evt->param.send_status;
290219820Sjeff		break;
291219820Sjeff	case IB_CM_MRA_RECEIVED:
292219820Sjeff		uvt->resp.u.mra_resp.timeout =
293219820Sjeff					evt->param.mra_rcvd.service_timeout;
294219820Sjeff		uvt->data_len = IB_CM_MRA_PRIVATE_DATA_SIZE;
295219820Sjeff		break;
296219820Sjeff	case IB_CM_REJ_RECEIVED:
297219820Sjeff		uvt->resp.u.rej_resp.reason = evt->param.rej_rcvd.reason;
298219820Sjeff		uvt->data_len = IB_CM_REJ_PRIVATE_DATA_SIZE;
299219820Sjeff		uvt->info_len = evt->param.rej_rcvd.ari_length;
300219820Sjeff		info	      = evt->param.rej_rcvd.ari;
301219820Sjeff		break;
302219820Sjeff	case IB_CM_LAP_RECEIVED:
303219820Sjeff		ib_copy_path_rec_to_user(&uvt->resp.u.lap_resp.path,
304219820Sjeff					 evt->param.lap_rcvd.alternate_path);
305219820Sjeff		uvt->data_len = IB_CM_LAP_PRIVATE_DATA_SIZE;
306219820Sjeff		uvt->resp.present = IB_UCM_PRES_ALTERNATE;
307219820Sjeff		break;
308219820Sjeff	case IB_CM_APR_RECEIVED:
309219820Sjeff		uvt->resp.u.apr_resp.status = evt->param.apr_rcvd.ap_status;
310219820Sjeff		uvt->data_len = IB_CM_APR_PRIVATE_DATA_SIZE;
311219820Sjeff		uvt->info_len = evt->param.apr_rcvd.info_len;
312219820Sjeff		info	      = evt->param.apr_rcvd.apr_info;
313219820Sjeff		break;
314219820Sjeff	case IB_CM_SIDR_REQ_RECEIVED:
315219820Sjeff		uvt->resp.u.sidr_req_resp.pkey =
316219820Sjeff					evt->param.sidr_req_rcvd.pkey;
317219820Sjeff		uvt->resp.u.sidr_req_resp.port =
318219820Sjeff					evt->param.sidr_req_rcvd.port;
319219820Sjeff		uvt->data_len = IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE;
320219820Sjeff		break;
321219820Sjeff	case IB_CM_SIDR_REP_RECEIVED:
322219820Sjeff		ib_ucm_event_sidr_rep_get(&uvt->resp.u.sidr_rep_resp,
323219820Sjeff					  &evt->param.sidr_rep_rcvd);
324219820Sjeff		uvt->data_len = IB_CM_SIDR_REP_PRIVATE_DATA_SIZE;
325219820Sjeff		uvt->info_len = evt->param.sidr_rep_rcvd.info_len;
326219820Sjeff		info	      = evt->param.sidr_rep_rcvd.info;
327219820Sjeff		break;
328219820Sjeff	default:
329219820Sjeff		uvt->resp.u.send_status = evt->param.send_status;
330219820Sjeff		break;
331219820Sjeff	}
332219820Sjeff
333219820Sjeff	if (uvt->data_len) {
334219820Sjeff		uvt->data = kmemdup(evt->private_data, uvt->data_len, GFP_KERNEL);
335219820Sjeff		if (!uvt->data)
336219820Sjeff			goto err1;
337219820Sjeff
338219820Sjeff		uvt->resp.present |= IB_UCM_PRES_DATA;
339219820Sjeff	}
340219820Sjeff
341219820Sjeff	if (uvt->info_len) {
342219820Sjeff		uvt->info = kmemdup(info, uvt->info_len, GFP_KERNEL);
343219820Sjeff		if (!uvt->info)
344219820Sjeff			goto err2;
345219820Sjeff
346219820Sjeff		uvt->resp.present |= IB_UCM_PRES_INFO;
347219820Sjeff	}
348219820Sjeff	return 0;
349219820Sjeff
350219820Sjefferr2:
351219820Sjeff	kfree(uvt->data);
352219820Sjefferr1:
353219820Sjeff	return -ENOMEM;
354219820Sjeff}
355219820Sjeff
356219820Sjeffstatic int ib_ucm_event_handler(struct ib_cm_id *cm_id,
357219820Sjeff				struct ib_cm_event *event)
358219820Sjeff{
359219820Sjeff	struct ib_ucm_event *uevent;
360219820Sjeff	struct ib_ucm_context *ctx;
361219820Sjeff	int result = 0;
362219820Sjeff
363219820Sjeff	ctx = cm_id->context;
364219820Sjeff
365219820Sjeff	uevent = kzalloc(sizeof *uevent, GFP_KERNEL);
366219820Sjeff	if (!uevent)
367219820Sjeff		goto err1;
368219820Sjeff
369219820Sjeff	uevent->ctx = ctx;
370219820Sjeff	uevent->cm_id = cm_id;
371219820Sjeff	uevent->resp.uid = ctx->uid;
372219820Sjeff	uevent->resp.id = ctx->id;
373219820Sjeff	uevent->resp.event = event->event;
374219820Sjeff
375219820Sjeff	result = ib_ucm_event_process(event, uevent);
376219820Sjeff	if (result)
377219820Sjeff		goto err2;
378219820Sjeff
379219820Sjeff	mutex_lock(&ctx->file->file_mutex);
380219820Sjeff	list_add_tail(&uevent->file_list, &ctx->file->events);
381219820Sjeff	list_add_tail(&uevent->ctx_list, &ctx->events);
382219820Sjeff	wake_up_interruptible(&ctx->file->poll_wait);
383219820Sjeff	if (ctx->file->filp)
384219820Sjeff		selwakeup(&ctx->file->filp->f_selinfo);
385219820Sjeff	mutex_unlock(&ctx->file->file_mutex);
386219820Sjeff	return 0;
387219820Sjeff
388219820Sjefferr2:
389219820Sjeff	kfree(uevent);
390219820Sjefferr1:
391219820Sjeff	/* Destroy new cm_id's */
392219820Sjeff	return ib_ucm_new_cm_id(event->event);
393219820Sjeff}
394219820Sjeff
395219820Sjeffstatic ssize_t ib_ucm_event(struct ib_ucm_file *file,
396219820Sjeff			    const char __user *inbuf,
397219820Sjeff			    int in_len, int out_len)
398219820Sjeff{
399219820Sjeff	struct ib_ucm_context *ctx;
400219820Sjeff	struct ib_ucm_event_get cmd;
401219820Sjeff	struct ib_ucm_event *uevent;
402219820Sjeff	int result = 0;
403219820Sjeff	DEFINE_WAIT(wait);
404219820Sjeff
405219820Sjeff	if (out_len < sizeof(struct ib_ucm_event_resp))
406219820Sjeff		return -ENOSPC;
407219820Sjeff
408219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
409219820Sjeff		return -EFAULT;
410219820Sjeff
411219820Sjeff	mutex_lock(&file->file_mutex);
412219820Sjeff	while (list_empty(&file->events)) {
413219820Sjeff		mutex_unlock(&file->file_mutex);
414219820Sjeff
415219820Sjeff		if (file->filp->f_flags & O_NONBLOCK)
416219820Sjeff			return -EAGAIN;
417219820Sjeff
418219820Sjeff		if (wait_event_interruptible(file->poll_wait,
419219820Sjeff					     !list_empty(&file->events)))
420219820Sjeff			return -ERESTARTSYS;
421219820Sjeff
422219820Sjeff		mutex_lock(&file->file_mutex);
423219820Sjeff	}
424219820Sjeff
425219820Sjeff	uevent = list_entry(file->events.next, struct ib_ucm_event, file_list);
426219820Sjeff
427219820Sjeff	if (ib_ucm_new_cm_id(uevent->resp.event)) {
428219820Sjeff		ctx = ib_ucm_ctx_alloc(file);
429219820Sjeff		if (!ctx) {
430219820Sjeff			result = -ENOMEM;
431219820Sjeff			goto done;
432219820Sjeff		}
433219820Sjeff
434219820Sjeff		ctx->cm_id = uevent->cm_id;
435219820Sjeff		ctx->cm_id->context = ctx;
436219820Sjeff		uevent->resp.id = ctx->id;
437219820Sjeff	}
438219820Sjeff
439219820Sjeff	if (copy_to_user((void __user *)(unsigned long)cmd.response,
440219820Sjeff			 &uevent->resp, sizeof(uevent->resp))) {
441219820Sjeff		result = -EFAULT;
442219820Sjeff		goto done;
443219820Sjeff	}
444219820Sjeff
445219820Sjeff	if (uevent->data) {
446219820Sjeff		if (cmd.data_len < uevent->data_len) {
447219820Sjeff			result = -ENOMEM;
448219820Sjeff			goto done;
449219820Sjeff		}
450219820Sjeff		if (copy_to_user((void __user *)(unsigned long)cmd.data,
451219820Sjeff				 uevent->data, uevent->data_len)) {
452219820Sjeff			result = -EFAULT;
453219820Sjeff			goto done;
454219820Sjeff		}
455219820Sjeff	}
456219820Sjeff
457219820Sjeff	if (uevent->info) {
458219820Sjeff		if (cmd.info_len < uevent->info_len) {
459219820Sjeff			result = -ENOMEM;
460219820Sjeff			goto done;
461219820Sjeff		}
462219820Sjeff		if (copy_to_user((void __user *)(unsigned long)cmd.info,
463219820Sjeff				 uevent->info, uevent->info_len)) {
464219820Sjeff			result = -EFAULT;
465219820Sjeff			goto done;
466219820Sjeff		}
467219820Sjeff	}
468219820Sjeff
469219820Sjeff	list_del(&uevent->file_list);
470219820Sjeff	list_del(&uevent->ctx_list);
471219820Sjeff	uevent->ctx->events_reported++;
472219820Sjeff
473219820Sjeff	kfree(uevent->data);
474219820Sjeff	kfree(uevent->info);
475219820Sjeff	kfree(uevent);
476219820Sjeffdone:
477219820Sjeff	mutex_unlock(&file->file_mutex);
478219820Sjeff	return result;
479219820Sjeff}
480219820Sjeff
481219820Sjeffstatic ssize_t ib_ucm_create_id(struct ib_ucm_file *file,
482219820Sjeff				const char __user *inbuf,
483219820Sjeff				int in_len, int out_len)
484219820Sjeff{
485219820Sjeff	struct ib_ucm_create_id cmd;
486219820Sjeff	struct ib_ucm_create_id_resp resp;
487219820Sjeff	struct ib_ucm_context *ctx;
488219820Sjeff	int result;
489219820Sjeff
490219820Sjeff	if (out_len < sizeof(resp))
491219820Sjeff		return -ENOSPC;
492219820Sjeff
493219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
494219820Sjeff		return -EFAULT;
495219820Sjeff
496219820Sjeff	mutex_lock(&file->file_mutex);
497219820Sjeff	ctx = ib_ucm_ctx_alloc(file);
498219820Sjeff	mutex_unlock(&file->file_mutex);
499219820Sjeff	if (!ctx)
500219820Sjeff		return -ENOMEM;
501219820Sjeff
502219820Sjeff	ctx->uid = cmd.uid;
503219820Sjeff	ctx->cm_id = ib_create_cm_id(file->device->ib_dev,
504219820Sjeff				     ib_ucm_event_handler, ctx);
505219820Sjeff	if (IS_ERR(ctx->cm_id)) {
506219820Sjeff		result = PTR_ERR(ctx->cm_id);
507219820Sjeff		goto err1;
508219820Sjeff	}
509219820Sjeff
510219820Sjeff	resp.id = ctx->id;
511219820Sjeff	if (copy_to_user((void __user *)(unsigned long)cmd.response,
512219820Sjeff			 &resp, sizeof(resp))) {
513219820Sjeff		result = -EFAULT;
514219820Sjeff		goto err2;
515219820Sjeff	}
516219820Sjeff	return 0;
517219820Sjeff
518219820Sjefferr2:
519219820Sjeff	ib_destroy_cm_id(ctx->cm_id);
520219820Sjefferr1:
521219820Sjeff	mutex_lock(&ctx_id_mutex);
522219820Sjeff	idr_remove(&ctx_id_table, ctx->id);
523219820Sjeff	mutex_unlock(&ctx_id_mutex);
524219820Sjeff	kfree(ctx);
525219820Sjeff	return result;
526219820Sjeff}
527219820Sjeff
528219820Sjeffstatic ssize_t ib_ucm_destroy_id(struct ib_ucm_file *file,
529219820Sjeff				 const char __user *inbuf,
530219820Sjeff				 int in_len, int out_len)
531219820Sjeff{
532219820Sjeff	struct ib_ucm_destroy_id cmd;
533219820Sjeff	struct ib_ucm_destroy_id_resp resp;
534219820Sjeff	struct ib_ucm_context *ctx;
535219820Sjeff	int result = 0;
536219820Sjeff
537219820Sjeff	if (out_len < sizeof(resp))
538219820Sjeff		return -ENOSPC;
539219820Sjeff
540219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
541219820Sjeff		return -EFAULT;
542219820Sjeff
543219820Sjeff	mutex_lock(&ctx_id_mutex);
544219820Sjeff	ctx = idr_find(&ctx_id_table, cmd.id);
545219820Sjeff	if (!ctx)
546219820Sjeff		ctx = ERR_PTR(-ENOENT);
547219820Sjeff	else if (ctx->file != file)
548219820Sjeff		ctx = ERR_PTR(-EINVAL);
549219820Sjeff	else
550219820Sjeff		idr_remove(&ctx_id_table, ctx->id);
551219820Sjeff	mutex_unlock(&ctx_id_mutex);
552219820Sjeff
553219820Sjeff	if (IS_ERR(ctx))
554219820Sjeff		return PTR_ERR(ctx);
555219820Sjeff
556219820Sjeff	ib_ucm_ctx_put(ctx);
557219820Sjeff	wait_for_completion(&ctx->comp);
558219820Sjeff
559219820Sjeff	/* No new events will be generated after destroying the cm_id. */
560219820Sjeff	ib_destroy_cm_id(ctx->cm_id);
561219820Sjeff	/* Cleanup events not yet reported to the user. */
562219820Sjeff	ib_ucm_cleanup_events(ctx);
563219820Sjeff
564219820Sjeff	resp.events_reported = ctx->events_reported;
565219820Sjeff	if (copy_to_user((void __user *)(unsigned long)cmd.response,
566219820Sjeff			 &resp, sizeof(resp)))
567219820Sjeff		result = -EFAULT;
568219820Sjeff
569219820Sjeff	kfree(ctx);
570219820Sjeff	return result;
571219820Sjeff}
572219820Sjeff
573219820Sjeffstatic ssize_t ib_ucm_attr_id(struct ib_ucm_file *file,
574219820Sjeff			      const char __user *inbuf,
575219820Sjeff			      int in_len, int out_len)
576219820Sjeff{
577219820Sjeff	struct ib_ucm_attr_id_resp resp;
578219820Sjeff	struct ib_ucm_attr_id cmd;
579219820Sjeff	struct ib_ucm_context *ctx;
580219820Sjeff	int result = 0;
581219820Sjeff
582219820Sjeff	if (out_len < sizeof(resp))
583219820Sjeff		return -ENOSPC;
584219820Sjeff
585219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
586219820Sjeff		return -EFAULT;
587219820Sjeff
588219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
589219820Sjeff	if (IS_ERR(ctx))
590219820Sjeff		return PTR_ERR(ctx);
591219820Sjeff
592219820Sjeff	resp.service_id   = ctx->cm_id->service_id;
593219820Sjeff	resp.service_mask = ctx->cm_id->service_mask;
594219820Sjeff	resp.local_id     = ctx->cm_id->local_id;
595219820Sjeff	resp.remote_id    = ctx->cm_id->remote_id;
596219820Sjeff
597219820Sjeff	if (copy_to_user((void __user *)(unsigned long)cmd.response,
598219820Sjeff			 &resp, sizeof(resp)))
599219820Sjeff		result = -EFAULT;
600219820Sjeff
601219820Sjeff	ib_ucm_ctx_put(ctx);
602219820Sjeff	return result;
603219820Sjeff}
604219820Sjeff
605219820Sjeffstatic ssize_t ib_ucm_init_qp_attr(struct ib_ucm_file *file,
606219820Sjeff				   const char __user *inbuf,
607219820Sjeff				   int in_len, int out_len)
608219820Sjeff{
609219820Sjeff	struct ib_uverbs_qp_attr resp;
610219820Sjeff	struct ib_ucm_init_qp_attr cmd;
611219820Sjeff	struct ib_ucm_context *ctx;
612219820Sjeff	struct ib_qp_attr qp_attr;
613219820Sjeff	int result = 0;
614219820Sjeff
615219820Sjeff	if (out_len < sizeof(resp))
616219820Sjeff		return -ENOSPC;
617219820Sjeff
618219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
619219820Sjeff		return -EFAULT;
620219820Sjeff
621219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
622219820Sjeff	if (IS_ERR(ctx))
623219820Sjeff		return PTR_ERR(ctx);
624219820Sjeff
625219820Sjeff	resp.qp_attr_mask = 0;
626219820Sjeff	memset(&qp_attr, 0, sizeof qp_attr);
627219820Sjeff	qp_attr.qp_state = cmd.qp_state;
628219820Sjeff	result = ib_cm_init_qp_attr(ctx->cm_id, &qp_attr, &resp.qp_attr_mask);
629219820Sjeff	if (result)
630219820Sjeff		goto out;
631219820Sjeff
632219820Sjeff	ib_copy_qp_attr_to_user(&resp, &qp_attr);
633219820Sjeff
634219820Sjeff	if (copy_to_user((void __user *)(unsigned long)cmd.response,
635219820Sjeff			 &resp, sizeof(resp)))
636219820Sjeff		result = -EFAULT;
637219820Sjeff
638219820Sjeffout:
639219820Sjeff	ib_ucm_ctx_put(ctx);
640219820Sjeff	return result;
641219820Sjeff}
642219820Sjeff
643219820Sjeffstatic int ucm_validate_listen(__be64 service_id, __be64 service_mask)
644219820Sjeff{
645219820Sjeff	service_id &= service_mask;
646219820Sjeff
647219820Sjeff	if (((service_id & IB_CMA_SERVICE_ID_MASK) == IB_CMA_SERVICE_ID) ||
648219820Sjeff	    ((service_id & IB_SDP_SERVICE_ID_MASK) == IB_SDP_SERVICE_ID))
649219820Sjeff		return -EINVAL;
650219820Sjeff
651219820Sjeff	return 0;
652219820Sjeff}
653219820Sjeff
654219820Sjeffstatic ssize_t ib_ucm_listen(struct ib_ucm_file *file,
655219820Sjeff			     const char __user *inbuf,
656219820Sjeff			     int in_len, int out_len)
657219820Sjeff{
658219820Sjeff	struct ib_ucm_listen cmd;
659219820Sjeff	struct ib_ucm_context *ctx;
660219820Sjeff	int result;
661219820Sjeff
662219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
663219820Sjeff		return -EFAULT;
664219820Sjeff
665219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
666219820Sjeff	if (IS_ERR(ctx))
667219820Sjeff		return PTR_ERR(ctx);
668219820Sjeff
669219820Sjeff	result = ucm_validate_listen(cmd.service_id, cmd.service_mask);
670219820Sjeff	if (result)
671219820Sjeff		goto out;
672219820Sjeff
673219820Sjeff	result = ib_cm_listen(ctx->cm_id, cmd.service_id, cmd.service_mask,
674219820Sjeff			      NULL);
675219820Sjeffout:
676219820Sjeff	ib_ucm_ctx_put(ctx);
677219820Sjeff	return result;
678219820Sjeff}
679219820Sjeff
680219820Sjeffstatic ssize_t ib_ucm_notify(struct ib_ucm_file *file,
681219820Sjeff			     const char __user *inbuf,
682219820Sjeff			     int in_len, int out_len)
683219820Sjeff{
684219820Sjeff	struct ib_ucm_notify cmd;
685219820Sjeff	struct ib_ucm_context *ctx;
686219820Sjeff	int result;
687219820Sjeff
688219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
689219820Sjeff		return -EFAULT;
690219820Sjeff
691219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
692219820Sjeff	if (IS_ERR(ctx))
693219820Sjeff		return PTR_ERR(ctx);
694219820Sjeff
695219820Sjeff	result = ib_cm_notify(ctx->cm_id, (enum ib_event_type) cmd.event);
696219820Sjeff	ib_ucm_ctx_put(ctx);
697219820Sjeff	return result;
698219820Sjeff}
699219820Sjeff
700219820Sjeffstatic int ib_ucm_alloc_data(const void **dest, u64 src, u32 len)
701219820Sjeff{
702219820Sjeff	void *data;
703219820Sjeff
704219820Sjeff	*dest = NULL;
705219820Sjeff
706219820Sjeff	if (!len)
707219820Sjeff		return 0;
708219820Sjeff
709219820Sjeff	data = kmalloc(len, GFP_KERNEL);
710219820Sjeff	if (!data)
711219820Sjeff		return -ENOMEM;
712219820Sjeff
713219820Sjeff	if (copy_from_user(data, (void __user *)(unsigned long)src, len)) {
714219820Sjeff		kfree(data);
715219820Sjeff		return -EFAULT;
716219820Sjeff	}
717219820Sjeff
718219820Sjeff	*dest = data;
719219820Sjeff	return 0;
720219820Sjeff}
721219820Sjeff
722219820Sjeffstatic int ib_ucm_path_get(struct ib_sa_path_rec **path, u64 src)
723219820Sjeff{
724219820Sjeff	struct ib_user_path_rec upath;
725219820Sjeff	struct ib_sa_path_rec  *sa_path;
726219820Sjeff
727219820Sjeff	*path = NULL;
728219820Sjeff
729219820Sjeff	if (!src)
730219820Sjeff		return 0;
731219820Sjeff
732219820Sjeff	sa_path = kmalloc(sizeof(*sa_path), GFP_KERNEL);
733219820Sjeff	if (!sa_path)
734219820Sjeff		return -ENOMEM;
735219820Sjeff
736219820Sjeff	if (copy_from_user(&upath, (void __user *)(unsigned long)src,
737219820Sjeff			   sizeof(upath))) {
738219820Sjeff
739219820Sjeff		kfree(sa_path);
740219820Sjeff		return -EFAULT;
741219820Sjeff	}
742219820Sjeff
743219820Sjeff	ib_copy_path_rec_from_user(sa_path, &upath);
744219820Sjeff	*path = sa_path;
745219820Sjeff	return 0;
746219820Sjeff}
747219820Sjeff
748219820Sjeffstatic ssize_t ib_ucm_send_req(struct ib_ucm_file *file,
749219820Sjeff			       const char __user *inbuf,
750219820Sjeff			       int in_len, int out_len)
751219820Sjeff{
752219820Sjeff	struct ib_cm_req_param param;
753219820Sjeff	struct ib_ucm_context *ctx;
754219820Sjeff	struct ib_ucm_req cmd;
755219820Sjeff	int result;
756219820Sjeff
757219820Sjeff	param.private_data   = NULL;
758219820Sjeff	param.primary_path   = NULL;
759219820Sjeff	param.alternate_path = NULL;
760219820Sjeff
761219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
762219820Sjeff		return -EFAULT;
763219820Sjeff
764219820Sjeff	result = ib_ucm_alloc_data(&param.private_data, cmd.data, cmd.len);
765219820Sjeff	if (result)
766219820Sjeff		goto done;
767219820Sjeff
768219820Sjeff	result = ib_ucm_path_get(&param.primary_path, cmd.primary_path);
769219820Sjeff	if (result)
770219820Sjeff		goto done;
771219820Sjeff
772219820Sjeff	result = ib_ucm_path_get(&param.alternate_path, cmd.alternate_path);
773219820Sjeff	if (result)
774219820Sjeff		goto done;
775219820Sjeff
776219820Sjeff	param.private_data_len           = cmd.len;
777219820Sjeff	param.service_id                 = cmd.sid;
778219820Sjeff	param.qp_num                     = cmd.qpn;
779219820Sjeff	param.qp_type                    = cmd.qp_type;
780219820Sjeff	param.starting_psn               = cmd.psn;
781219820Sjeff	param.peer_to_peer               = cmd.peer_to_peer;
782219820Sjeff	param.responder_resources        = cmd.responder_resources;
783219820Sjeff	param.initiator_depth            = cmd.initiator_depth;
784219820Sjeff	param.remote_cm_response_timeout = cmd.remote_cm_response_timeout;
785219820Sjeff	param.flow_control               = cmd.flow_control;
786219820Sjeff	param.local_cm_response_timeout  = cmd.local_cm_response_timeout;
787219820Sjeff	param.retry_count                = cmd.retry_count;
788219820Sjeff	param.rnr_retry_count            = cmd.rnr_retry_count;
789219820Sjeff	param.max_cm_retries             = cmd.max_cm_retries;
790219820Sjeff	param.srq                        = cmd.srq;
791219820Sjeff
792219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
793219820Sjeff	if (!IS_ERR(ctx)) {
794219820Sjeff		result = ib_send_cm_req(ctx->cm_id, &param);
795219820Sjeff		ib_ucm_ctx_put(ctx);
796219820Sjeff	} else
797219820Sjeff		result = PTR_ERR(ctx);
798219820Sjeff
799219820Sjeffdone:
800219820Sjeff	kfree(param.private_data);
801219820Sjeff	kfree(param.primary_path);
802219820Sjeff	kfree(param.alternate_path);
803219820Sjeff	return result;
804219820Sjeff}
805219820Sjeff
806219820Sjeffstatic ssize_t ib_ucm_send_rep(struct ib_ucm_file *file,
807219820Sjeff			       const char __user *inbuf,
808219820Sjeff			       int in_len, int out_len)
809219820Sjeff{
810219820Sjeff	struct ib_cm_rep_param param;
811219820Sjeff	struct ib_ucm_context *ctx;
812219820Sjeff	struct ib_ucm_rep cmd;
813219820Sjeff	int result;
814219820Sjeff
815219820Sjeff	param.private_data = NULL;
816219820Sjeff
817219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
818219820Sjeff		return -EFAULT;
819219820Sjeff
820219820Sjeff	result = ib_ucm_alloc_data(&param.private_data, cmd.data, cmd.len);
821219820Sjeff	if (result)
822219820Sjeff		return result;
823219820Sjeff
824219820Sjeff	param.qp_num              = cmd.qpn;
825219820Sjeff	param.starting_psn        = cmd.psn;
826219820Sjeff	param.private_data_len    = cmd.len;
827219820Sjeff	param.responder_resources = cmd.responder_resources;
828219820Sjeff	param.initiator_depth     = cmd.initiator_depth;
829219820Sjeff	param.failover_accepted   = cmd.failover_accepted;
830219820Sjeff	param.flow_control        = cmd.flow_control;
831219820Sjeff	param.rnr_retry_count     = cmd.rnr_retry_count;
832219820Sjeff	param.srq                 = cmd.srq;
833219820Sjeff
834219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
835219820Sjeff	if (!IS_ERR(ctx)) {
836219820Sjeff		ctx->uid = cmd.uid;
837219820Sjeff		result = ib_send_cm_rep(ctx->cm_id, &param);
838219820Sjeff		ib_ucm_ctx_put(ctx);
839219820Sjeff	} else
840219820Sjeff		result = PTR_ERR(ctx);
841219820Sjeff
842219820Sjeff	kfree(param.private_data);
843219820Sjeff	return result;
844219820Sjeff}
845219820Sjeff
846219820Sjeffstatic ssize_t ib_ucm_send_private_data(struct ib_ucm_file *file,
847219820Sjeff					const char __user *inbuf, int in_len,
848219820Sjeff					int (*func)(struct ib_cm_id *cm_id,
849219820Sjeff						    const void *private_data,
850219820Sjeff						    u8 private_data_len))
851219820Sjeff{
852219820Sjeff	struct ib_ucm_private_data cmd;
853219820Sjeff	struct ib_ucm_context *ctx;
854219820Sjeff	const void *private_data = NULL;
855219820Sjeff	int result;
856219820Sjeff
857219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
858219820Sjeff		return -EFAULT;
859219820Sjeff
860219820Sjeff	result = ib_ucm_alloc_data(&private_data, cmd.data, cmd.len);
861219820Sjeff	if (result)
862219820Sjeff		return result;
863219820Sjeff
864219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
865219820Sjeff	if (!IS_ERR(ctx)) {
866219820Sjeff		result = func(ctx->cm_id, private_data, cmd.len);
867219820Sjeff		ib_ucm_ctx_put(ctx);
868219820Sjeff	} else
869219820Sjeff		result = PTR_ERR(ctx);
870219820Sjeff
871219820Sjeff	kfree(private_data);
872219820Sjeff	return result;
873219820Sjeff}
874219820Sjeff
875219820Sjeffstatic ssize_t ib_ucm_send_rtu(struct ib_ucm_file *file,
876219820Sjeff			       const char __user *inbuf,
877219820Sjeff			       int in_len, int out_len)
878219820Sjeff{
879219820Sjeff	return ib_ucm_send_private_data(file, inbuf, in_len, ib_send_cm_rtu);
880219820Sjeff}
881219820Sjeff
882219820Sjeffstatic ssize_t ib_ucm_send_dreq(struct ib_ucm_file *file,
883219820Sjeff				const char __user *inbuf,
884219820Sjeff				int in_len, int out_len)
885219820Sjeff{
886219820Sjeff	return ib_ucm_send_private_data(file, inbuf, in_len, ib_send_cm_dreq);
887219820Sjeff}
888219820Sjeff
889219820Sjeffstatic ssize_t ib_ucm_send_drep(struct ib_ucm_file *file,
890219820Sjeff				const char __user *inbuf,
891219820Sjeff				int in_len, int out_len)
892219820Sjeff{
893219820Sjeff	return ib_ucm_send_private_data(file, inbuf, in_len, ib_send_cm_drep);
894219820Sjeff}
895219820Sjeff
896219820Sjeffstatic ssize_t ib_ucm_send_info(struct ib_ucm_file *file,
897219820Sjeff				const char __user *inbuf, int in_len,
898219820Sjeff				int (*func)(struct ib_cm_id *cm_id,
899219820Sjeff					    int status,
900219820Sjeff					    const void *info,
901219820Sjeff					    u8 info_len,
902219820Sjeff					    const void *data,
903219820Sjeff					    u8 data_len))
904219820Sjeff{
905219820Sjeff	struct ib_ucm_context *ctx;
906219820Sjeff	struct ib_ucm_info cmd;
907219820Sjeff	const void *data = NULL;
908219820Sjeff	const void *info = NULL;
909219820Sjeff	int result;
910219820Sjeff
911219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
912219820Sjeff		return -EFAULT;
913219820Sjeff
914219820Sjeff	result = ib_ucm_alloc_data(&data, cmd.data, cmd.data_len);
915219820Sjeff	if (result)
916219820Sjeff		goto done;
917219820Sjeff
918219820Sjeff	result = ib_ucm_alloc_data(&info, cmd.info, cmd.info_len);
919219820Sjeff	if (result)
920219820Sjeff		goto done;
921219820Sjeff
922219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
923219820Sjeff	if (!IS_ERR(ctx)) {
924219820Sjeff		result = func(ctx->cm_id, cmd.status, info, cmd.info_len,
925219820Sjeff			      data, cmd.data_len);
926219820Sjeff		ib_ucm_ctx_put(ctx);
927219820Sjeff	} else
928219820Sjeff		result = PTR_ERR(ctx);
929219820Sjeff
930219820Sjeffdone:
931219820Sjeff	kfree(data);
932219820Sjeff	kfree(info);
933219820Sjeff	return result;
934219820Sjeff}
935219820Sjeff
936219820Sjeffstatic ssize_t ib_ucm_send_rej(struct ib_ucm_file *file,
937219820Sjeff			       const char __user *inbuf,
938219820Sjeff			       int in_len, int out_len)
939219820Sjeff{
940219820Sjeff	return ib_ucm_send_info(file, inbuf, in_len, (void *)ib_send_cm_rej);
941219820Sjeff}
942219820Sjeff
943219820Sjeffstatic ssize_t ib_ucm_send_apr(struct ib_ucm_file *file,
944219820Sjeff			       const char __user *inbuf,
945219820Sjeff			       int in_len, int out_len)
946219820Sjeff{
947219820Sjeff	return ib_ucm_send_info(file, inbuf, in_len, (void *)ib_send_cm_apr);
948219820Sjeff}
949219820Sjeff
950219820Sjeffstatic ssize_t ib_ucm_send_mra(struct ib_ucm_file *file,
951219820Sjeff			       const char __user *inbuf,
952219820Sjeff			       int in_len, int out_len)
953219820Sjeff{
954219820Sjeff	struct ib_ucm_context *ctx;
955219820Sjeff	struct ib_ucm_mra cmd;
956219820Sjeff	const void *data = NULL;
957219820Sjeff	int result;
958219820Sjeff
959219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
960219820Sjeff		return -EFAULT;
961219820Sjeff
962219820Sjeff	result = ib_ucm_alloc_data(&data, cmd.data, cmd.len);
963219820Sjeff	if (result)
964219820Sjeff		return result;
965219820Sjeff
966219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
967219820Sjeff	if (!IS_ERR(ctx)) {
968219820Sjeff		result = ib_send_cm_mra(ctx->cm_id, cmd.timeout, data, cmd.len);
969219820Sjeff		ib_ucm_ctx_put(ctx);
970219820Sjeff	} else
971219820Sjeff		result = PTR_ERR(ctx);
972219820Sjeff
973219820Sjeff	kfree(data);
974219820Sjeff	return result;
975219820Sjeff}
976219820Sjeff
977219820Sjeffstatic ssize_t ib_ucm_send_lap(struct ib_ucm_file *file,
978219820Sjeff			       const char __user *inbuf,
979219820Sjeff			       int in_len, int out_len)
980219820Sjeff{
981219820Sjeff	struct ib_ucm_context *ctx;
982219820Sjeff	struct ib_sa_path_rec *path = NULL;
983219820Sjeff	struct ib_ucm_lap cmd;
984219820Sjeff	const void *data = NULL;
985219820Sjeff	int result;
986219820Sjeff
987219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
988219820Sjeff		return -EFAULT;
989219820Sjeff
990219820Sjeff	result = ib_ucm_alloc_data(&data, cmd.data, cmd.len);
991219820Sjeff	if (result)
992219820Sjeff		goto done;
993219820Sjeff
994219820Sjeff	result = ib_ucm_path_get(&path, cmd.path);
995219820Sjeff	if (result)
996219820Sjeff		goto done;
997219820Sjeff
998219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
999219820Sjeff	if (!IS_ERR(ctx)) {
1000219820Sjeff		result = ib_send_cm_lap(ctx->cm_id, path, data, cmd.len);
1001219820Sjeff		ib_ucm_ctx_put(ctx);
1002219820Sjeff	} else
1003219820Sjeff		result = PTR_ERR(ctx);
1004219820Sjeff
1005219820Sjeffdone:
1006219820Sjeff	kfree(data);
1007219820Sjeff	kfree(path);
1008219820Sjeff	return result;
1009219820Sjeff}
1010219820Sjeff
1011219820Sjeffstatic ssize_t ib_ucm_send_sidr_req(struct ib_ucm_file *file,
1012219820Sjeff				    const char __user *inbuf,
1013219820Sjeff				    int in_len, int out_len)
1014219820Sjeff{
1015219820Sjeff	struct ib_cm_sidr_req_param param;
1016219820Sjeff	struct ib_ucm_context *ctx;
1017219820Sjeff	struct ib_ucm_sidr_req cmd;
1018219820Sjeff	int result;
1019219820Sjeff
1020219820Sjeff	param.private_data = NULL;
1021219820Sjeff	param.path = NULL;
1022219820Sjeff
1023219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
1024219820Sjeff		return -EFAULT;
1025219820Sjeff
1026219820Sjeff	result = ib_ucm_alloc_data(&param.private_data, cmd.data, cmd.len);
1027219820Sjeff	if (result)
1028219820Sjeff		goto done;
1029219820Sjeff
1030219820Sjeff	result = ib_ucm_path_get(&param.path, cmd.path);
1031219820Sjeff	if (result)
1032219820Sjeff		goto done;
1033219820Sjeff
1034219820Sjeff	param.private_data_len = cmd.len;
1035219820Sjeff	param.service_id       = cmd.sid;
1036219820Sjeff	param.timeout_ms       = cmd.timeout;
1037219820Sjeff	param.max_cm_retries   = cmd.max_cm_retries;
1038219820Sjeff
1039219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
1040219820Sjeff	if (!IS_ERR(ctx)) {
1041219820Sjeff		result = ib_send_cm_sidr_req(ctx->cm_id, &param);
1042219820Sjeff		ib_ucm_ctx_put(ctx);
1043219820Sjeff	} else
1044219820Sjeff		result = PTR_ERR(ctx);
1045219820Sjeff
1046219820Sjeffdone:
1047219820Sjeff	kfree(param.private_data);
1048219820Sjeff	kfree(param.path);
1049219820Sjeff	return result;
1050219820Sjeff}
1051219820Sjeff
1052219820Sjeffstatic ssize_t ib_ucm_send_sidr_rep(struct ib_ucm_file *file,
1053219820Sjeff				    const char __user *inbuf,
1054219820Sjeff				    int in_len, int out_len)
1055219820Sjeff{
1056219820Sjeff	struct ib_cm_sidr_rep_param param;
1057219820Sjeff	struct ib_ucm_sidr_rep cmd;
1058219820Sjeff	struct ib_ucm_context *ctx;
1059219820Sjeff	int result;
1060219820Sjeff
1061219820Sjeff	param.info = NULL;
1062219820Sjeff
1063219820Sjeff	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))
1064219820Sjeff		return -EFAULT;
1065219820Sjeff
1066219820Sjeff	result = ib_ucm_alloc_data(&param.private_data,
1067219820Sjeff				   cmd.data, cmd.data_len);
1068219820Sjeff	if (result)
1069219820Sjeff		goto done;
1070219820Sjeff
1071219820Sjeff	result = ib_ucm_alloc_data(&param.info, cmd.info, cmd.info_len);
1072219820Sjeff	if (result)
1073219820Sjeff		goto done;
1074219820Sjeff
1075219820Sjeff	param.qp_num		= cmd.qpn;
1076219820Sjeff	param.qkey		= cmd.qkey;
1077219820Sjeff	param.status		= cmd.status;
1078219820Sjeff	param.info_length	= cmd.info_len;
1079219820Sjeff	param.private_data_len	= cmd.data_len;
1080219820Sjeff
1081219820Sjeff	ctx = ib_ucm_ctx_get(file, cmd.id);
1082219820Sjeff	if (!IS_ERR(ctx)) {
1083219820Sjeff		result = ib_send_cm_sidr_rep(ctx->cm_id, &param);
1084219820Sjeff		ib_ucm_ctx_put(ctx);
1085219820Sjeff	} else
1086219820Sjeff		result = PTR_ERR(ctx);
1087219820Sjeff
1088219820Sjeffdone:
1089219820Sjeff	kfree(param.private_data);
1090219820Sjeff	kfree(param.info);
1091219820Sjeff	return result;
1092219820Sjeff}
1093219820Sjeff
1094219820Sjeffstatic ssize_t (*ucm_cmd_table[])(struct ib_ucm_file *file,
1095219820Sjeff				  const char __user *inbuf,
1096219820Sjeff				  int in_len, int out_len) = {
1097219820Sjeff	[IB_USER_CM_CMD_CREATE_ID]     = ib_ucm_create_id,
1098219820Sjeff	[IB_USER_CM_CMD_DESTROY_ID]    = ib_ucm_destroy_id,
1099219820Sjeff	[IB_USER_CM_CMD_ATTR_ID]       = ib_ucm_attr_id,
1100219820Sjeff	[IB_USER_CM_CMD_LISTEN]        = ib_ucm_listen,
1101219820Sjeff	[IB_USER_CM_CMD_NOTIFY]        = ib_ucm_notify,
1102219820Sjeff	[IB_USER_CM_CMD_SEND_REQ]      = ib_ucm_send_req,
1103219820Sjeff	[IB_USER_CM_CMD_SEND_REP]      = ib_ucm_send_rep,
1104219820Sjeff	[IB_USER_CM_CMD_SEND_RTU]      = ib_ucm_send_rtu,
1105219820Sjeff	[IB_USER_CM_CMD_SEND_DREQ]     = ib_ucm_send_dreq,
1106219820Sjeff	[IB_USER_CM_CMD_SEND_DREP]     = ib_ucm_send_drep,
1107219820Sjeff	[IB_USER_CM_CMD_SEND_REJ]      = ib_ucm_send_rej,
1108219820Sjeff	[IB_USER_CM_CMD_SEND_MRA]      = ib_ucm_send_mra,
1109219820Sjeff	[IB_USER_CM_CMD_SEND_LAP]      = ib_ucm_send_lap,
1110219820Sjeff	[IB_USER_CM_CMD_SEND_APR]      = ib_ucm_send_apr,
1111219820Sjeff	[IB_USER_CM_CMD_SEND_SIDR_REQ] = ib_ucm_send_sidr_req,
1112219820Sjeff	[IB_USER_CM_CMD_SEND_SIDR_REP] = ib_ucm_send_sidr_rep,
1113219820Sjeff	[IB_USER_CM_CMD_EVENT]	       = ib_ucm_event,
1114219820Sjeff	[IB_USER_CM_CMD_INIT_QP_ATTR]  = ib_ucm_init_qp_attr,
1115219820Sjeff};
1116219820Sjeff
1117219820Sjeffstatic ssize_t ib_ucm_write(struct file *filp, const char __user *buf,
1118219820Sjeff			    size_t len, loff_t *pos)
1119219820Sjeff{
1120219820Sjeff	struct ib_ucm_file *file = filp->private_data;
1121219820Sjeff	struct ib_ucm_cmd_hdr hdr;
1122219820Sjeff	ssize_t result;
1123219820Sjeff
1124219820Sjeff	if (len < sizeof(hdr))
1125219820Sjeff		return -EINVAL;
1126219820Sjeff
1127219820Sjeff	if (copy_from_user(&hdr, buf, sizeof(hdr)))
1128219820Sjeff		return -EFAULT;
1129219820Sjeff
1130219820Sjeff	if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucm_cmd_table))
1131219820Sjeff		return -EINVAL;
1132219820Sjeff
1133219820Sjeff	if (hdr.in + sizeof(hdr) > len)
1134219820Sjeff		return -EINVAL;
1135219820Sjeff
1136219820Sjeff	result = ucm_cmd_table[hdr.cmd](file, buf + sizeof(hdr),
1137219820Sjeff					hdr.in, hdr.out);
1138219820Sjeff	if (!result)
1139219820Sjeff		result = len;
1140219820Sjeff
1141219820Sjeff	return result;
1142219820Sjeff}
1143219820Sjeff
1144219820Sjeffstatic unsigned int ib_ucm_poll(struct file *filp,
1145219820Sjeff				struct poll_table_struct *wait)
1146219820Sjeff{
1147219820Sjeff	struct ib_ucm_file *file = filp->private_data;
1148219820Sjeff	unsigned int mask = 0;
1149219820Sjeff
1150219820Sjeff	poll_wait(filp, &file->poll_wait, wait);
1151219820Sjeff
1152219820Sjeff	if (!list_empty(&file->events))
1153219820Sjeff		mask = POLLIN | POLLRDNORM;
1154219820Sjeff
1155219820Sjeff	return mask;
1156219820Sjeff}
1157219820Sjeff
1158219820Sjeff/*
1159219820Sjeff * ib_ucm_open() does not need the BKL:
1160219820Sjeff *
1161219820Sjeff *  - no global state is referred to;
1162219820Sjeff *  - there is no ioctl method to race against;
1163219820Sjeff *  - no further module initialization is required for open to work
1164219820Sjeff *    after the device is registered.
1165219820Sjeff */
1166219820Sjeffstatic int ib_ucm_open(struct inode *inode, struct file *filp)
1167219820Sjeff{
1168219820Sjeff	struct ib_ucm_file *file;
1169219820Sjeff
1170219820Sjeff	file = kzalloc(sizeof(*file), GFP_KERNEL);
1171219820Sjeff	if (!file)
1172219820Sjeff		return -ENOMEM;
1173219820Sjeff
1174219820Sjeff	INIT_LIST_HEAD(&file->events);
1175219820Sjeff	INIT_LIST_HEAD(&file->ctxs);
1176219820Sjeff	init_waitqueue_head(&file->poll_wait);
1177219820Sjeff
1178219820Sjeff	mutex_init(&file->file_mutex);
1179219820Sjeff
1180219820Sjeff	filp->private_data = file;
1181219820Sjeff	file->filp = filp;
1182219820Sjeff	file->device = container_of(inode->i_cdev->si_drv1, struct ib_ucm_device, cdev);
1183219820Sjeff
1184219820Sjeff	return 0;
1185219820Sjeff}
1186219820Sjeff
1187219820Sjeffstatic int ib_ucm_close(struct inode *inode, struct file *filp)
1188219820Sjeff{
1189219820Sjeff	struct ib_ucm_file *file = filp->private_data;
1190219820Sjeff	struct ib_ucm_context *ctx;
1191219820Sjeff
1192219820Sjeff	mutex_lock(&file->file_mutex);
1193219820Sjeff	while (!list_empty(&file->ctxs)) {
1194219820Sjeff		ctx = list_entry(file->ctxs.next,
1195219820Sjeff				 struct ib_ucm_context, file_list);
1196219820Sjeff		mutex_unlock(&file->file_mutex);
1197219820Sjeff
1198219820Sjeff		mutex_lock(&ctx_id_mutex);
1199219820Sjeff		idr_remove(&ctx_id_table, ctx->id);
1200219820Sjeff		mutex_unlock(&ctx_id_mutex);
1201219820Sjeff
1202219820Sjeff		ib_destroy_cm_id(ctx->cm_id);
1203219820Sjeff		ib_ucm_cleanup_events(ctx);
1204219820Sjeff		kfree(ctx);
1205219820Sjeff
1206219820Sjeff		mutex_lock(&file->file_mutex);
1207219820Sjeff	}
1208219820Sjeff	mutex_unlock(&file->file_mutex);
1209219820Sjeff	kfree(file);
1210219820Sjeff	return 0;
1211219820Sjeff}
1212219820Sjeff
1213219820Sjeffstatic void ib_ucm_release_dev(struct device *dev)
1214219820Sjeff{
1215219820Sjeff	struct ib_ucm_device *ucm_dev;
1216219820Sjeff
1217219820Sjeff	ucm_dev = container_of(dev, struct ib_ucm_device, dev);
1218219820Sjeff	cdev_del(&ucm_dev->cdev);
1219219820Sjeff	clear_bit(ucm_dev->devnum, dev_map);
1220219820Sjeff	kfree(ucm_dev);
1221219820Sjeff}
1222219820Sjeff
1223219820Sjeffstatic const struct file_operations ucm_fops = {
1224219820Sjeff	.owner 	 = THIS_MODULE,
1225219820Sjeff	.open 	 = ib_ucm_open,
1226219820Sjeff	.release = ib_ucm_close,
1227219820Sjeff	.write 	 = ib_ucm_write,
1228219820Sjeff	.poll    = ib_ucm_poll,
1229219820Sjeff};
1230219820Sjeff
1231219820Sjeffstatic ssize_t show_ibdev(struct device *dev, struct device_attribute *attr,
1232219820Sjeff			  char *buf)
1233219820Sjeff{
1234219820Sjeff	struct ib_ucm_device *ucm_dev;
1235219820Sjeff
1236219820Sjeff	ucm_dev = container_of(dev, struct ib_ucm_device, dev);
1237219820Sjeff	return sprintf(buf, "%s\n", ucm_dev->ib_dev->name);
1238219820Sjeff}
1239219820Sjeffstatic DEVICE_ATTR(ibdev, S_IRUGO, show_ibdev, NULL);
1240219820Sjeff
1241219820Sjeffstatic void ib_ucm_add_one(struct ib_device *device)
1242219820Sjeff{
1243219820Sjeff	struct ib_ucm_device *ucm_dev;
1244219820Sjeff
1245219820Sjeff	if (!device->alloc_ucontext ||
1246219820Sjeff	    rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
1247219820Sjeff		return;
1248219820Sjeff
1249219820Sjeff	ucm_dev = kzalloc(sizeof *ucm_dev, GFP_KERNEL);
1250219820Sjeff	if (!ucm_dev)
1251219820Sjeff		return;
1252219820Sjeff
1253219820Sjeff	ucm_dev->ib_dev = device;
1254219820Sjeff
1255219820Sjeff	ucm_dev->devnum = find_first_zero_bit(dev_map, IB_UCM_MAX_DEVICES);
1256219820Sjeff	if (ucm_dev->devnum >= IB_UCM_MAX_DEVICES)
1257219820Sjeff		goto err;
1258219820Sjeff
1259219820Sjeff	set_bit(ucm_dev->devnum, dev_map);
1260219820Sjeff
1261219820Sjeff	cdev_init(&ucm_dev->cdev, &ucm_fops);
1262219820Sjeff	ucm_dev->cdev.owner = THIS_MODULE;
1263219820Sjeff	kobject_set_name(&ucm_dev->cdev.kobj, "ucm%d", ucm_dev->devnum);
1264219820Sjeff	if (cdev_add(&ucm_dev->cdev, IB_UCM_BASE_DEV + ucm_dev->devnum, 1))
1265219820Sjeff		goto err;
1266219820Sjeff
1267219820Sjeff	ucm_dev->dev.class = &cm_class;
1268219820Sjeff	ucm_dev->dev.parent = device->dma_device;
1269219820Sjeff	ucm_dev->dev.devt = ucm_dev->cdev.dev;
1270219820Sjeff	ucm_dev->dev.release = ib_ucm_release_dev;
1271219820Sjeff	dev_set_name(&ucm_dev->dev, "ucm%d", ucm_dev->devnum);
1272219820Sjeff	if (device_register(&ucm_dev->dev))
1273219820Sjeff		goto err_cdev;
1274219820Sjeff
1275219820Sjeff	if (device_create_file(&ucm_dev->dev, &dev_attr_ibdev))
1276219820Sjeff		goto err_dev;
1277219820Sjeff
1278219820Sjeff	ib_set_client_data(device, &ucm_client, ucm_dev);
1279219820Sjeff	return;
1280219820Sjeff
1281219820Sjefferr_dev:
1282219820Sjeff	device_unregister(&ucm_dev->dev);
1283219820Sjefferr_cdev:
1284219820Sjeff	cdev_del(&ucm_dev->cdev);
1285219820Sjeff	clear_bit(ucm_dev->devnum, dev_map);
1286219820Sjefferr:
1287219820Sjeff	kfree(ucm_dev);
1288219820Sjeff	return;
1289219820Sjeff}
1290219820Sjeff
1291219820Sjeffstatic void ib_ucm_remove_one(struct ib_device *device)
1292219820Sjeff{
1293219820Sjeff	struct ib_ucm_device *ucm_dev = ib_get_client_data(device, &ucm_client);
1294219820Sjeff
1295219820Sjeff	if (!ucm_dev)
1296219820Sjeff		return;
1297219820Sjeff
1298219820Sjeff	device_unregister(&ucm_dev->dev);
1299219820Sjeff}
1300219820Sjeff
1301219820Sjeffstatic ssize_t show_abi_version(struct class *class, char *buf)
1302219820Sjeff{
1303219820Sjeff	return sprintf(buf, "%d\n", IB_USER_CM_ABI_VERSION);
1304219820Sjeff}
1305219820Sjeffstatic CLASS_ATTR(abi_version, S_IRUGO, show_abi_version, NULL);
1306219820Sjeff
1307219820Sjeffstatic int __init ib_ucm_init(void)
1308219820Sjeff{
1309219820Sjeff	int ret;
1310219820Sjeff
1311219820Sjeff	ret = register_chrdev_region(IB_UCM_BASE_DEV, IB_UCM_MAX_DEVICES,
1312219820Sjeff				     "infiniband_cm");
1313219820Sjeff	if (ret) {
1314219820Sjeff		printk(KERN_ERR "ucm: couldn't register device number\n");
1315219820Sjeff		goto error1;
1316219820Sjeff	}
1317219820Sjeff
1318219820Sjeff	ret = class_create_file(&cm_class, &class_attr_abi_version);
1319219820Sjeff	if (ret) {
1320219820Sjeff		printk(KERN_ERR "ucm: couldn't create abi_version attribute\n");
1321219820Sjeff		goto error2;
1322219820Sjeff	}
1323219820Sjeff
1324219820Sjeff	ret = ib_register_client(&ucm_client);
1325219820Sjeff	if (ret) {
1326219820Sjeff		printk(KERN_ERR "ucm: couldn't register client\n");
1327219820Sjeff		goto error3;
1328219820Sjeff	}
1329219820Sjeff	return 0;
1330219820Sjeff
1331219820Sjefferror3:
1332219820Sjeff	class_remove_file(&cm_class, &class_attr_abi_version);
1333219820Sjefferror2:
1334219820Sjeff	unregister_chrdev_region(IB_UCM_BASE_DEV, IB_UCM_MAX_DEVICES);
1335219820Sjefferror1:
1336219820Sjeff	return ret;
1337219820Sjeff}
1338219820Sjeff
1339219820Sjeffstatic void __exit ib_ucm_cleanup(void)
1340219820Sjeff{
1341219820Sjeff	ib_unregister_client(&ucm_client);
1342219820Sjeff	class_remove_file(&cm_class, &class_attr_abi_version);
1343219820Sjeff	unregister_chrdev_region(IB_UCM_BASE_DEV, IB_UCM_MAX_DEVICES);
1344219820Sjeff	idr_destroy(&ctx_id_table);
1345219820Sjeff}
1346219820Sjeff
1347219820Sjeffmodule_init_order(ib_ucm_init, SI_ORDER_THIRD);
1348219820Sjeffmodule_exit(ib_ucm_cleanup);
1349