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(¶m.private_data, cmd.data, cmd.len); 765219820Sjeff if (result) 766219820Sjeff goto done; 767219820Sjeff 768219820Sjeff result = ib_ucm_path_get(¶m.primary_path, cmd.primary_path); 769219820Sjeff if (result) 770219820Sjeff goto done; 771219820Sjeff 772219820Sjeff result = ib_ucm_path_get(¶m.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, ¶m); 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(¶m.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, ¶m); 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(¶m.private_data, cmd.data, cmd.len); 1027219820Sjeff if (result) 1028219820Sjeff goto done; 1029219820Sjeff 1030219820Sjeff result = ib_ucm_path_get(¶m.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, ¶m); 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(¶m.private_data, 1067219820Sjeff cmd.data, cmd.data_len); 1068219820Sjeff if (result) 1069219820Sjeff goto done; 1070219820Sjeff 1071219820Sjeff result = ib_ucm_alloc_data(¶m.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, ¶m); 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