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(¶m.private_data, cmd.data, cmd.len); 761219820Sjeff if (result) 762219820Sjeff goto done; 763219820Sjeff 764219820Sjeff result = ib_ucm_path_get(¶m.primary_path, cmd.primary_path); 765219820Sjeff if (result) 766219820Sjeff goto done; 767219820Sjeff 768219820Sjeff result = ib_ucm_path_get(¶m.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, ¶m); 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(¶m.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, ¶m); 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(¶m.private_data, cmd.data, cmd.len); 1023219820Sjeff if (result) 1024219820Sjeff goto done; 1025219820Sjeff 1026219820Sjeff result = ib_ucm_path_get(¶m.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, ¶m); 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(¶m.private_data, 1063219820Sjeff cmd.data, cmd.data_len); 1064219820Sjeff if (result) 1065219820Sjeff goto done; 1066219820Sjeff 1067219820Sjeff result = ib_ucm_alloc_data(¶m.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, ¶m); 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