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