1219820Sjeff/* 2219820Sjeff * Copyright (c) 2005-2006 Intel Corporation. All rights reserved. 3219820Sjeff * 4219820Sjeff * This software is available to you under a choice of one of two 5219820Sjeff * licenses. You may choose to be licensed under the terms of the GNU 6219820Sjeff * General Public License (GPL) Version 2, available from the file 7219820Sjeff * COPYING in the main directory of this source tree, or the 8219820Sjeff * OpenIB.org BSD license below: 9219820Sjeff * 10219820Sjeff * Redistribution and use in source and binary forms, with or 11219820Sjeff * without modification, are permitted provided that the following 12219820Sjeff * conditions are met: 13219820Sjeff * 14219820Sjeff * - Redistributions of source code must retain the above 15219820Sjeff * copyright notice, this list of conditions and the following 16219820Sjeff * disclaimer. 17219820Sjeff * 18219820Sjeff * - Redistributions in binary form must reproduce the above 19219820Sjeff * copyright notice, this list of conditions and the following 20219820Sjeff * disclaimer in the documentation and/or other materials 21219820Sjeff * provided with the distribution. 22219820Sjeff * 23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30219820Sjeff * SOFTWARE. 31219820Sjeff */ 32219820Sjeff 33219820Sjeff#include <linux/completion.h> 34219820Sjeff#include <linux/file.h> 35219820Sjeff#include <linux/mutex.h> 36219820Sjeff#include <linux/poll.h> 37219820Sjeff#include <linux/idr.h> 38219820Sjeff#include <linux/in.h> 39219820Sjeff#include <linux/in6.h> 40219820Sjeff#include <linux/miscdevice.h> 41219820Sjeff 42219820Sjeff#include <rdma/rdma_user_cm.h> 43219820Sjeff#include <rdma/ib_marshall.h> 44219820Sjeff#include <rdma/rdma_cm.h> 45219820Sjeff#include <rdma/rdma_cm_ib.h> 46219820Sjeff 47219820SjeffMODULE_AUTHOR("Sean Hefty"); 48219820SjeffMODULE_DESCRIPTION("RDMA Userspace Connection Manager Access"); 49219820SjeffMODULE_LICENSE("Dual BSD/GPL"); 50219820Sjeff 51219820Sjeffenum { 52219820Sjeff UCMA_MAX_BACKLOG = 1024 53219820Sjeff}; 54219820Sjeff 55219820Sjeffstruct ucma_file { 56219820Sjeff struct mutex mut; 57219820Sjeff struct file *filp; 58219820Sjeff struct list_head ctx_list; 59219820Sjeff struct list_head event_list; 60219820Sjeff wait_queue_head_t poll_wait; 61219820Sjeff}; 62219820Sjeff 63219820Sjeffstruct ucma_context { 64219820Sjeff int id; 65219820Sjeff struct completion comp; 66219820Sjeff atomic_t ref; 67219820Sjeff int events_reported; 68219820Sjeff int backlog; 69219820Sjeff 70219820Sjeff struct ucma_file *file; 71219820Sjeff struct rdma_cm_id *cm_id; 72219820Sjeff u64 uid; 73219820Sjeff 74219820Sjeff struct list_head list; 75219820Sjeff struct list_head mc_list; 76219820Sjeff}; 77219820Sjeff 78219820Sjeffstruct ucma_multicast { 79219820Sjeff struct ucma_context *ctx; 80219820Sjeff int id; 81219820Sjeff int events_reported; 82219820Sjeff 83219820Sjeff u64 uid; 84219820Sjeff struct list_head list; 85219820Sjeff struct sockaddr_storage addr; 86219820Sjeff}; 87219820Sjeff 88219820Sjeffstruct ucma_event { 89219820Sjeff struct ucma_context *ctx; 90219820Sjeff struct ucma_multicast *mc; 91219820Sjeff struct list_head list; 92219820Sjeff struct rdma_cm_id *cm_id; 93219820Sjeff struct rdma_ucm_event_resp resp; 94219820Sjeff}; 95219820Sjeff 96219820Sjeffstatic DEFINE_MUTEX(mut); 97219820Sjeffstatic DEFINE_IDR(ctx_idr); 98219820Sjeffstatic DEFINE_IDR(multicast_idr); 99219820Sjeff 100219820Sjeffstatic inline struct ucma_context *_ucma_find_context(int id, 101219820Sjeff struct ucma_file *file) 102219820Sjeff{ 103219820Sjeff struct ucma_context *ctx; 104219820Sjeff 105219820Sjeff ctx = idr_find(&ctx_idr, id); 106219820Sjeff if (!ctx) 107219820Sjeff ctx = ERR_PTR(-ENOENT); 108219820Sjeff else if (ctx->file != file) 109219820Sjeff ctx = ERR_PTR(-EINVAL); 110219820Sjeff return ctx; 111219820Sjeff} 112219820Sjeff 113219820Sjeffstatic struct ucma_context *ucma_get_ctx(struct ucma_file *file, int id) 114219820Sjeff{ 115219820Sjeff struct ucma_context *ctx; 116219820Sjeff 117219820Sjeff mutex_lock(&mut); 118219820Sjeff ctx = _ucma_find_context(id, file); 119219820Sjeff if (!IS_ERR(ctx)) 120219820Sjeff atomic_inc(&ctx->ref); 121219820Sjeff mutex_unlock(&mut); 122219820Sjeff return ctx; 123219820Sjeff} 124219820Sjeff 125219820Sjeffstatic void ucma_put_ctx(struct ucma_context *ctx) 126219820Sjeff{ 127219820Sjeff if (atomic_dec_and_test(&ctx->ref)) 128219820Sjeff complete(&ctx->comp); 129219820Sjeff} 130219820Sjeff 131219820Sjeffstatic struct ucma_context *ucma_alloc_ctx(struct ucma_file *file) 132219820Sjeff{ 133219820Sjeff struct ucma_context *ctx; 134219820Sjeff int ret; 135219820Sjeff 136219820Sjeff ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 137219820Sjeff if (!ctx) 138219820Sjeff return NULL; 139219820Sjeff 140219820Sjeff atomic_set(&ctx->ref, 1); 141219820Sjeff init_completion(&ctx->comp); 142219820Sjeff INIT_LIST_HEAD(&ctx->mc_list); 143219820Sjeff ctx->file = file; 144219820Sjeff 145219820Sjeff do { 146219820Sjeff ret = idr_pre_get(&ctx_idr, GFP_KERNEL); 147219820Sjeff if (!ret) 148219820Sjeff goto error; 149219820Sjeff 150219820Sjeff mutex_lock(&mut); 151219820Sjeff ret = idr_get_new(&ctx_idr, ctx, &ctx->id); 152219820Sjeff mutex_unlock(&mut); 153219820Sjeff } while (ret == -EAGAIN); 154219820Sjeff 155219820Sjeff if (ret) 156219820Sjeff goto error; 157219820Sjeff 158219820Sjeff list_add_tail(&ctx->list, &file->ctx_list); 159219820Sjeff return ctx; 160219820Sjeff 161219820Sjefferror: 162219820Sjeff kfree(ctx); 163219820Sjeff return NULL; 164219820Sjeff} 165219820Sjeff 166219820Sjeffstatic struct ucma_multicast* ucma_alloc_multicast(struct ucma_context *ctx) 167219820Sjeff{ 168219820Sjeff struct ucma_multicast *mc; 169219820Sjeff int ret; 170219820Sjeff 171219820Sjeff mc = kzalloc(sizeof(*mc), GFP_KERNEL); 172219820Sjeff if (!mc) 173219820Sjeff return NULL; 174219820Sjeff 175219820Sjeff do { 176219820Sjeff ret = idr_pre_get(&multicast_idr, GFP_KERNEL); 177219820Sjeff if (!ret) 178219820Sjeff goto error; 179219820Sjeff 180219820Sjeff mutex_lock(&mut); 181219820Sjeff ret = idr_get_new(&multicast_idr, mc, &mc->id); 182219820Sjeff mutex_unlock(&mut); 183219820Sjeff } while (ret == -EAGAIN); 184219820Sjeff 185219820Sjeff if (ret) 186219820Sjeff goto error; 187219820Sjeff 188219820Sjeff mc->ctx = ctx; 189219820Sjeff list_add_tail(&mc->list, &ctx->mc_list); 190219820Sjeff return mc; 191219820Sjeff 192219820Sjefferror: 193219820Sjeff kfree(mc); 194219820Sjeff return NULL; 195219820Sjeff} 196219820Sjeff 197219820Sjeffstatic void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst, 198219820Sjeff struct rdma_conn_param *src) 199219820Sjeff{ 200219820Sjeff if (src->private_data_len) 201219820Sjeff memcpy(dst->private_data, src->private_data, 202219820Sjeff src->private_data_len); 203219820Sjeff dst->private_data_len = src->private_data_len; 204219820Sjeff dst->responder_resources =src->responder_resources; 205219820Sjeff dst->initiator_depth = src->initiator_depth; 206219820Sjeff dst->flow_control = src->flow_control; 207219820Sjeff dst->retry_count = src->retry_count; 208219820Sjeff dst->rnr_retry_count = src->rnr_retry_count; 209219820Sjeff dst->srq = src->srq; 210219820Sjeff dst->qp_num = src->qp_num; 211219820Sjeff} 212219820Sjeff 213219820Sjeffstatic void ucma_copy_ud_event(struct rdma_ucm_ud_param *dst, 214219820Sjeff struct rdma_ud_param *src) 215219820Sjeff{ 216219820Sjeff if (src->private_data_len) 217219820Sjeff memcpy(dst->private_data, src->private_data, 218219820Sjeff src->private_data_len); 219219820Sjeff dst->private_data_len = src->private_data_len; 220219820Sjeff ib_copy_ah_attr_to_user(&dst->ah_attr, &src->ah_attr); 221219820Sjeff dst->qp_num = src->qp_num; 222219820Sjeff dst->qkey = src->qkey; 223219820Sjeff} 224219820Sjeff 225219820Sjeffstatic void ucma_set_event_context(struct ucma_context *ctx, 226219820Sjeff struct rdma_cm_event *event, 227219820Sjeff struct ucma_event *uevent) 228219820Sjeff{ 229219820Sjeff uevent->ctx = ctx; 230219820Sjeff switch (event->event) { 231219820Sjeff case RDMA_CM_EVENT_MULTICAST_JOIN: 232219820Sjeff case RDMA_CM_EVENT_MULTICAST_ERROR: 233219820Sjeff uevent->mc = (struct ucma_multicast *) 234219820Sjeff event->param.ud.private_data; 235219820Sjeff uevent->resp.uid = uevent->mc->uid; 236219820Sjeff uevent->resp.id = uevent->mc->id; 237219820Sjeff break; 238219820Sjeff default: 239219820Sjeff uevent->resp.uid = ctx->uid; 240219820Sjeff uevent->resp.id = ctx->id; 241219820Sjeff break; 242219820Sjeff } 243219820Sjeff} 244219820Sjeff 245219820Sjeffstatic int ucma_event_handler(struct rdma_cm_id *cm_id, 246219820Sjeff struct rdma_cm_event *event) 247219820Sjeff{ 248219820Sjeff struct ucma_event *uevent; 249219820Sjeff struct ucma_context *ctx = cm_id->context; 250219820Sjeff int ret = 0; 251219820Sjeff 252219820Sjeff uevent = kzalloc(sizeof(*uevent), GFP_KERNEL); 253219820Sjeff if (!uevent) 254219820Sjeff return event->event == RDMA_CM_EVENT_CONNECT_REQUEST; 255219820Sjeff 256219820Sjeff uevent->cm_id = cm_id; 257219820Sjeff ucma_set_event_context(ctx, event, uevent); 258219820Sjeff uevent->resp.event = event->event; 259219820Sjeff uevent->resp.status = event->status; 260219820Sjeff if (cm_id->ps == RDMA_PS_UDP || cm_id->ps == RDMA_PS_IPOIB) 261219820Sjeff ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud); 262219820Sjeff else 263219820Sjeff ucma_copy_conn_event(&uevent->resp.param.conn, 264219820Sjeff &event->param.conn); 265219820Sjeff 266219820Sjeff mutex_lock(&ctx->file->mut); 267219820Sjeff if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) { 268219820Sjeff if (!ctx->backlog) { 269219820Sjeff ret = -ENOMEM; 270219820Sjeff kfree(uevent); 271219820Sjeff goto out; 272219820Sjeff } 273219820Sjeff ctx->backlog--; 274219820Sjeff } else if (!ctx->uid) { 275219820Sjeff /* 276219820Sjeff * We ignore events for new connections until userspace has set 277219820Sjeff * their context. This can only happen if an error occurs on a 278219820Sjeff * new connection before the user accepts it. This is okay, 279219820Sjeff * since the accept will just fail later. 280219820Sjeff */ 281219820Sjeff kfree(uevent); 282219820Sjeff goto out; 283219820Sjeff } 284219820Sjeff 285219820Sjeff list_add_tail(&uevent->list, &ctx->file->event_list); 286219820Sjeff wake_up_interruptible(&ctx->file->poll_wait); 287219820Sjeff if (ctx->file->filp) 288219820Sjeff selwakeup(&ctx->file->filp->f_selinfo); 289219820Sjeffout: 290219820Sjeff mutex_unlock(&ctx->file->mut); 291219820Sjeff return ret; 292219820Sjeff} 293219820Sjeff 294219820Sjeffstatic ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf, 295219820Sjeff int in_len, int out_len) 296219820Sjeff{ 297219820Sjeff struct ucma_context *ctx; 298219820Sjeff struct rdma_ucm_get_event cmd; 299219820Sjeff struct ucma_event *uevent; 300219820Sjeff int ret = 0; 301219820Sjeff DEFINE_WAIT(wait); 302219820Sjeff 303219820Sjeff if (out_len < sizeof uevent->resp) 304219820Sjeff return -ENOSPC; 305219820Sjeff 306219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 307219820Sjeff return -EFAULT; 308219820Sjeff 309219820Sjeff mutex_lock(&file->mut); 310219820Sjeff while (list_empty(&file->event_list)) { 311219820Sjeff mutex_unlock(&file->mut); 312219820Sjeff 313219820Sjeff if (file->filp->f_flags & O_NONBLOCK) 314219820Sjeff return -EAGAIN; 315219820Sjeff 316219820Sjeff if (wait_event_interruptible(file->poll_wait, 317219820Sjeff !list_empty(&file->event_list))) 318219820Sjeff return -ERESTARTSYS; 319219820Sjeff 320219820Sjeff mutex_lock(&file->mut); 321219820Sjeff } 322219820Sjeff 323219820Sjeff uevent = list_entry(file->event_list.next, struct ucma_event, list); 324219820Sjeff 325219820Sjeff if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) { 326219820Sjeff ctx = ucma_alloc_ctx(file); 327219820Sjeff if (!ctx) { 328219820Sjeff ret = -ENOMEM; 329219820Sjeff goto done; 330219820Sjeff } 331219820Sjeff uevent->ctx->backlog++; 332219820Sjeff ctx->cm_id = uevent->cm_id; 333219820Sjeff ctx->cm_id->context = ctx; 334219820Sjeff uevent->resp.id = ctx->id; 335219820Sjeff } 336219820Sjeff 337219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 338219820Sjeff &uevent->resp, sizeof uevent->resp)) { 339219820Sjeff ret = -EFAULT; 340219820Sjeff goto done; 341219820Sjeff } 342219820Sjeff 343219820Sjeff list_del(&uevent->list); 344219820Sjeff uevent->ctx->events_reported++; 345219820Sjeff if (uevent->mc) 346219820Sjeff uevent->mc->events_reported++; 347219820Sjeff kfree(uevent); 348219820Sjeffdone: 349219820Sjeff mutex_unlock(&file->mut); 350219820Sjeff return ret; 351219820Sjeff} 352219820Sjeff 353219820Sjeffstatic ssize_t ucma_create_id(struct ucma_file *file, 354219820Sjeff const char __user *inbuf, 355219820Sjeff int in_len, int out_len) 356219820Sjeff{ 357219820Sjeff struct rdma_ucm_create_id cmd; 358219820Sjeff struct rdma_ucm_create_id_resp resp; 359219820Sjeff struct ucma_context *ctx; 360219820Sjeff int ret; 361219820Sjeff 362219820Sjeff if (out_len < sizeof(resp)) 363219820Sjeff return -ENOSPC; 364219820Sjeff 365219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 366219820Sjeff return -EFAULT; 367219820Sjeff 368219820Sjeff mutex_lock(&file->mut); 369219820Sjeff ctx = ucma_alloc_ctx(file); 370219820Sjeff mutex_unlock(&file->mut); 371219820Sjeff if (!ctx) 372219820Sjeff return -ENOMEM; 373219820Sjeff 374219820Sjeff ctx->uid = cmd.uid; 375219820Sjeff ctx->cm_id = rdma_create_id(ucma_event_handler, ctx, cmd.ps); 376219820Sjeff if (IS_ERR(ctx->cm_id)) { 377219820Sjeff ret = PTR_ERR(ctx->cm_id); 378219820Sjeff goto err1; 379219820Sjeff } 380219820Sjeff 381219820Sjeff resp.id = ctx->id; 382219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 383219820Sjeff &resp, sizeof(resp))) { 384219820Sjeff ret = -EFAULT; 385219820Sjeff goto err2; 386219820Sjeff } 387219820Sjeff return 0; 388219820Sjeff 389219820Sjefferr2: 390219820Sjeff rdma_destroy_id(ctx->cm_id); 391219820Sjefferr1: 392219820Sjeff mutex_lock(&mut); 393219820Sjeff idr_remove(&ctx_idr, ctx->id); 394219820Sjeff mutex_unlock(&mut); 395219820Sjeff kfree(ctx); 396219820Sjeff return ret; 397219820Sjeff} 398219820Sjeff 399219820Sjeffstatic void ucma_cleanup_multicast(struct ucma_context *ctx) 400219820Sjeff{ 401219820Sjeff struct ucma_multicast *mc, *tmp; 402219820Sjeff 403219820Sjeff mutex_lock(&mut); 404219820Sjeff list_for_each_entry_safe(mc, tmp, &ctx->mc_list, list) { 405219820Sjeff list_del(&mc->list); 406219820Sjeff idr_remove(&multicast_idr, mc->id); 407219820Sjeff kfree(mc); 408219820Sjeff } 409219820Sjeff mutex_unlock(&mut); 410219820Sjeff} 411219820Sjeff 412219820Sjeffstatic void ucma_cleanup_events(struct ucma_context *ctx) 413219820Sjeff{ 414219820Sjeff struct ucma_event *uevent, *tmp; 415219820Sjeff 416219820Sjeff list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) { 417219820Sjeff if (uevent->ctx != ctx) 418219820Sjeff continue; 419219820Sjeff 420219820Sjeff list_del(&uevent->list); 421219820Sjeff 422219820Sjeff /* clear incoming connections. */ 423219820Sjeff if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) 424219820Sjeff rdma_destroy_id(uevent->cm_id); 425219820Sjeff 426219820Sjeff kfree(uevent); 427219820Sjeff } 428219820Sjeff} 429219820Sjeff 430219820Sjeffstatic void ucma_cleanup_mc_events(struct ucma_multicast *mc) 431219820Sjeff{ 432219820Sjeff struct ucma_event *uevent, *tmp; 433219820Sjeff 434219820Sjeff list_for_each_entry_safe(uevent, tmp, &mc->ctx->file->event_list, list) { 435219820Sjeff if (uevent->mc != mc) 436219820Sjeff continue; 437219820Sjeff 438219820Sjeff list_del(&uevent->list); 439219820Sjeff kfree(uevent); 440219820Sjeff } 441219820Sjeff} 442219820Sjeff 443219820Sjeffstatic int ucma_free_ctx(struct ucma_context *ctx) 444219820Sjeff{ 445219820Sjeff int events_reported; 446219820Sjeff 447219820Sjeff /* No new events will be generated after destroying the id. */ 448219820Sjeff rdma_destroy_id(ctx->cm_id); 449219820Sjeff 450219820Sjeff ucma_cleanup_multicast(ctx); 451219820Sjeff 452219820Sjeff /* Cleanup events not yet reported to the user. */ 453219820Sjeff mutex_lock(&ctx->file->mut); 454219820Sjeff ucma_cleanup_events(ctx); 455219820Sjeff list_del(&ctx->list); 456219820Sjeff mutex_unlock(&ctx->file->mut); 457219820Sjeff 458219820Sjeff events_reported = ctx->events_reported; 459219820Sjeff kfree(ctx); 460219820Sjeff return events_reported; 461219820Sjeff} 462219820Sjeff 463219820Sjeffstatic ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf, 464219820Sjeff int in_len, int out_len) 465219820Sjeff{ 466219820Sjeff struct rdma_ucm_destroy_id cmd; 467219820Sjeff struct rdma_ucm_destroy_id_resp resp; 468219820Sjeff struct ucma_context *ctx; 469219820Sjeff int ret = 0; 470219820Sjeff 471219820Sjeff if (out_len < sizeof(resp)) 472219820Sjeff return -ENOSPC; 473219820Sjeff 474219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 475219820Sjeff return -EFAULT; 476219820Sjeff 477219820Sjeff mutex_lock(&mut); 478219820Sjeff ctx = _ucma_find_context(cmd.id, file); 479219820Sjeff if (!IS_ERR(ctx)) 480219820Sjeff idr_remove(&ctx_idr, ctx->id); 481219820Sjeff mutex_unlock(&mut); 482219820Sjeff 483219820Sjeff if (IS_ERR(ctx)) 484219820Sjeff return PTR_ERR(ctx); 485219820Sjeff 486219820Sjeff ucma_put_ctx(ctx); 487219820Sjeff wait_for_completion(&ctx->comp); 488219820Sjeff resp.events_reported = ucma_free_ctx(ctx); 489219820Sjeff 490219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 491219820Sjeff &resp, sizeof(resp))) 492219820Sjeff ret = -EFAULT; 493219820Sjeff 494219820Sjeff return ret; 495219820Sjeff} 496219820Sjeff 497219820Sjeffstatic ssize_t ucma_bind_addr(struct ucma_file *file, const char __user *inbuf, 498219820Sjeff int in_len, int out_len) 499219820Sjeff{ 500219820Sjeff struct rdma_ucm_bind_addr cmd; 501219820Sjeff struct ucma_context *ctx; 502219820Sjeff int ret; 503219820Sjeff 504219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 505219820Sjeff return -EFAULT; 506219820Sjeff 507219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 508219820Sjeff if (IS_ERR(ctx)) 509219820Sjeff return PTR_ERR(ctx); 510219820Sjeff 511219820Sjeff ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr); 512219820Sjeff ucma_put_ctx(ctx); 513219820Sjeff return ret; 514219820Sjeff} 515219820Sjeff 516219820Sjeffstatic ssize_t ucma_resolve_addr(struct ucma_file *file, 517219820Sjeff const char __user *inbuf, 518219820Sjeff int in_len, int out_len) 519219820Sjeff{ 520219820Sjeff struct rdma_ucm_resolve_addr cmd; 521219820Sjeff struct ucma_context *ctx; 522219820Sjeff int ret; 523219820Sjeff 524219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 525219820Sjeff return -EFAULT; 526219820Sjeff 527219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 528219820Sjeff if (IS_ERR(ctx)) 529219820Sjeff return PTR_ERR(ctx); 530219820Sjeff 531219820Sjeff ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, 532219820Sjeff (struct sockaddr *) &cmd.dst_addr, 533219820Sjeff cmd.timeout_ms); 534219820Sjeff ucma_put_ctx(ctx); 535219820Sjeff return ret; 536219820Sjeff} 537219820Sjeff 538219820Sjeffstatic ssize_t ucma_resolve_route(struct ucma_file *file, 539219820Sjeff const char __user *inbuf, 540219820Sjeff int in_len, int out_len) 541219820Sjeff{ 542219820Sjeff struct rdma_ucm_resolve_route cmd; 543219820Sjeff struct ucma_context *ctx; 544219820Sjeff int ret; 545219820Sjeff 546219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 547219820Sjeff return -EFAULT; 548219820Sjeff 549219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 550219820Sjeff if (IS_ERR(ctx)) 551219820Sjeff return PTR_ERR(ctx); 552219820Sjeff 553219820Sjeff ret = rdma_resolve_route(ctx->cm_id, cmd.timeout_ms); 554219820Sjeff ucma_put_ctx(ctx); 555219820Sjeff return ret; 556219820Sjeff} 557219820Sjeff 558219820Sjeffstatic void ucma_copy_ib_route(struct rdma_ucm_query_route_resp *resp, 559219820Sjeff struct rdma_route *route) 560219820Sjeff{ 561219820Sjeff struct rdma_dev_addr *dev_addr; 562219820Sjeff 563219820Sjeff resp->num_paths = route->num_paths; 564219820Sjeff switch (route->num_paths) { 565219820Sjeff case 0: 566219820Sjeff dev_addr = &route->addr.dev_addr; 567219820Sjeff rdma_addr_get_dgid(dev_addr, 568219820Sjeff (union ib_gid *) &resp->ib_route[0].dgid); 569219820Sjeff rdma_addr_get_sgid(dev_addr, 570219820Sjeff (union ib_gid *) &resp->ib_route[0].sgid); 571219820Sjeff resp->ib_route[0].pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr)); 572219820Sjeff break; 573219820Sjeff case 2: 574219820Sjeff ib_copy_path_rec_to_user(&resp->ib_route[1], 575219820Sjeff &route->path_rec[1]); 576219820Sjeff /* fall through */ 577219820Sjeff case 1: 578219820Sjeff ib_copy_path_rec_to_user(&resp->ib_route[0], 579219820Sjeff &route->path_rec[0]); 580219820Sjeff break; 581219820Sjeff default: 582219820Sjeff break; 583219820Sjeff } 584219820Sjeff} 585219820Sjeff 586219820Sjeffstatic void ucma_copy_iboe_route(struct rdma_ucm_query_route_resp *resp, 587219820Sjeff struct rdma_route *route) 588219820Sjeff{ 589219820Sjeff struct rdma_dev_addr *dev_addr; 590219820Sjeff struct net_device *dev; 591219820Sjeff u16 vid = 0; 592219820Sjeff 593219820Sjeff resp->num_paths = route->num_paths; 594219820Sjeff switch (route->num_paths) { 595219820Sjeff case 0: 596219820Sjeff dev_addr = &route->addr.dev_addr; 597219820Sjeff dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if); 598219820Sjeff if (dev) { 599219820Sjeff vid = rdma_vlan_dev_vlan_id(dev); 600219820Sjeff dev_put(dev); 601219820Sjeff } 602219820Sjeff 603219820Sjeff iboe_mac_vlan_to_ll((union ib_gid *) &resp->ib_route[0].dgid, 604219820Sjeff dev_addr->dst_dev_addr, vid); 605219820Sjeff iboe_addr_get_sgid(dev_addr, 606219820Sjeff (union ib_gid *) &resp->ib_route[0].sgid); 607219820Sjeff resp->ib_route[0].pkey = cpu_to_be16(0xffff); 608219820Sjeff break; 609219820Sjeff case 2: 610219820Sjeff ib_copy_path_rec_to_user(&resp->ib_route[1], 611219820Sjeff &route->path_rec[1]); 612219820Sjeff /* fall through */ 613219820Sjeff case 1: 614219820Sjeff ib_copy_path_rec_to_user(&resp->ib_route[0], 615219820Sjeff &route->path_rec[0]); 616219820Sjeff break; 617219820Sjeff default: 618219820Sjeff break; 619219820Sjeff } 620219820Sjeff} 621219820Sjeff 622219820Sjeffstatic ssize_t ucma_query_route(struct ucma_file *file, 623219820Sjeff const char __user *inbuf, 624219820Sjeff int in_len, int out_len) 625219820Sjeff{ 626219820Sjeff struct rdma_ucm_query_route cmd; 627219820Sjeff struct rdma_ucm_query_route_resp resp; 628219820Sjeff struct ucma_context *ctx; 629219820Sjeff struct sockaddr *addr; 630219820Sjeff int ret = 0; 631219820Sjeff 632219820Sjeff if (out_len < sizeof(resp)) 633219820Sjeff return -ENOSPC; 634219820Sjeff 635219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 636219820Sjeff return -EFAULT; 637219820Sjeff 638219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 639219820Sjeff if (IS_ERR(ctx)) 640219820Sjeff return PTR_ERR(ctx); 641219820Sjeff 642219820Sjeff memset(&resp, 0, sizeof resp); 643219820Sjeff addr = (struct sockaddr *) &ctx->cm_id->route.addr.src_addr; 644219820Sjeff memcpy(&resp.src_addr, addr, addr->sa_family == AF_INET ? 645219820Sjeff sizeof(struct sockaddr_in) : 646219820Sjeff sizeof(struct sockaddr_in6)); 647219820Sjeff addr = (struct sockaddr *) &ctx->cm_id->route.addr.dst_addr; 648219820Sjeff memcpy(&resp.dst_addr, addr, addr->sa_family == AF_INET ? 649219820Sjeff sizeof(struct sockaddr_in) : 650219820Sjeff sizeof(struct sockaddr_in6)); 651219820Sjeff if (!ctx->cm_id->device) 652219820Sjeff goto out; 653219820Sjeff 654219820Sjeff resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid; 655219820Sjeff resp.port_num = ctx->cm_id->port_num; 656219820Sjeff if (rdma_node_get_transport(ctx->cm_id->device->node_type) == RDMA_TRANSPORT_IB) { 657219820Sjeff switch (rdma_port_get_link_layer(ctx->cm_id->device, ctx->cm_id->port_num)) { 658219820Sjeff case IB_LINK_LAYER_INFINIBAND: 659219820Sjeff ucma_copy_ib_route(&resp, &ctx->cm_id->route); 660219820Sjeff break; 661219820Sjeff case IB_LINK_LAYER_ETHERNET: 662219820Sjeff ucma_copy_iboe_route(&resp, &ctx->cm_id->route); 663219820Sjeff break; 664219820Sjeff default: 665219820Sjeff break; 666219820Sjeff } 667219820Sjeff } 668219820Sjeff 669219820Sjeffout: 670219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 671219820Sjeff &resp, sizeof(resp))) 672219820Sjeff ret = -EFAULT; 673219820Sjeff 674219820Sjeff ucma_put_ctx(ctx); 675219820Sjeff return ret; 676219820Sjeff} 677219820Sjeff 678219820Sjeffstatic void ucma_copy_conn_param(struct rdma_conn_param *dst, 679219820Sjeff struct rdma_ucm_conn_param *src) 680219820Sjeff{ 681219820Sjeff dst->private_data = src->private_data; 682219820Sjeff dst->private_data_len = src->private_data_len; 683219820Sjeff dst->responder_resources =src->responder_resources; 684219820Sjeff dst->initiator_depth = src->initiator_depth; 685219820Sjeff dst->flow_control = src->flow_control; 686219820Sjeff dst->retry_count = src->retry_count; 687219820Sjeff dst->rnr_retry_count = src->rnr_retry_count; 688219820Sjeff dst->srq = src->srq; 689219820Sjeff dst->qp_num = src->qp_num; 690219820Sjeff} 691219820Sjeff 692219820Sjeffstatic ssize_t ucma_connect(struct ucma_file *file, const char __user *inbuf, 693219820Sjeff int in_len, int out_len) 694219820Sjeff{ 695219820Sjeff struct rdma_ucm_connect cmd; 696219820Sjeff struct rdma_conn_param conn_param; 697219820Sjeff struct ucma_context *ctx; 698219820Sjeff int ret; 699219820Sjeff 700219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 701219820Sjeff return -EFAULT; 702219820Sjeff 703219820Sjeff if (!cmd.conn_param.valid) 704219820Sjeff return -EINVAL; 705219820Sjeff 706219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 707219820Sjeff if (IS_ERR(ctx)) 708219820Sjeff return PTR_ERR(ctx); 709219820Sjeff 710219820Sjeff ucma_copy_conn_param(&conn_param, &cmd.conn_param); 711219820Sjeff ret = rdma_connect(ctx->cm_id, &conn_param); 712219820Sjeff ucma_put_ctx(ctx); 713219820Sjeff return ret; 714219820Sjeff} 715219820Sjeff 716219820Sjeffstatic ssize_t ucma_listen(struct ucma_file *file, const char __user *inbuf, 717219820Sjeff int in_len, int out_len) 718219820Sjeff{ 719219820Sjeff struct rdma_ucm_listen cmd; 720219820Sjeff struct ucma_context *ctx; 721219820Sjeff int ret; 722219820Sjeff 723219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 724219820Sjeff return -EFAULT; 725219820Sjeff 726219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 727219820Sjeff if (IS_ERR(ctx)) 728219820Sjeff return PTR_ERR(ctx); 729219820Sjeff 730219820Sjeff ctx->backlog = cmd.backlog > 0 && cmd.backlog < UCMA_MAX_BACKLOG ? 731219820Sjeff cmd.backlog : UCMA_MAX_BACKLOG; 732219820Sjeff ret = rdma_listen(ctx->cm_id, ctx->backlog); 733219820Sjeff ucma_put_ctx(ctx); 734219820Sjeff return ret; 735219820Sjeff} 736219820Sjeff 737219820Sjeffstatic ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf, 738219820Sjeff int in_len, int out_len) 739219820Sjeff{ 740219820Sjeff struct rdma_ucm_accept cmd; 741219820Sjeff struct rdma_conn_param conn_param; 742219820Sjeff struct ucma_context *ctx; 743219820Sjeff int ret; 744219820Sjeff 745219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 746219820Sjeff return -EFAULT; 747219820Sjeff 748219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 749219820Sjeff if (IS_ERR(ctx)) 750219820Sjeff return PTR_ERR(ctx); 751219820Sjeff 752219820Sjeff if (cmd.conn_param.valid) { 753219820Sjeff ctx->uid = cmd.uid; 754219820Sjeff ucma_copy_conn_param(&conn_param, &cmd.conn_param); 755219820Sjeff ret = rdma_accept(ctx->cm_id, &conn_param); 756219820Sjeff } else 757219820Sjeff ret = rdma_accept(ctx->cm_id, NULL); 758219820Sjeff 759219820Sjeff ucma_put_ctx(ctx); 760219820Sjeff return ret; 761219820Sjeff} 762219820Sjeff 763219820Sjeffstatic ssize_t ucma_reject(struct ucma_file *file, const char __user *inbuf, 764219820Sjeff int in_len, int out_len) 765219820Sjeff{ 766219820Sjeff struct rdma_ucm_reject cmd; 767219820Sjeff struct ucma_context *ctx; 768219820Sjeff int ret; 769219820Sjeff 770219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 771219820Sjeff return -EFAULT; 772219820Sjeff 773219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 774219820Sjeff if (IS_ERR(ctx)) 775219820Sjeff return PTR_ERR(ctx); 776219820Sjeff 777219820Sjeff ret = rdma_reject(ctx->cm_id, cmd.private_data, cmd.private_data_len); 778219820Sjeff ucma_put_ctx(ctx); 779219820Sjeff return ret; 780219820Sjeff} 781219820Sjeff 782219820Sjeffstatic ssize_t ucma_disconnect(struct ucma_file *file, const char __user *inbuf, 783219820Sjeff int in_len, int out_len) 784219820Sjeff{ 785219820Sjeff struct rdma_ucm_disconnect cmd; 786219820Sjeff struct ucma_context *ctx; 787219820Sjeff int ret; 788219820Sjeff 789219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 790219820Sjeff return -EFAULT; 791219820Sjeff 792219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 793219820Sjeff if (IS_ERR(ctx)) 794219820Sjeff return PTR_ERR(ctx); 795219820Sjeff 796219820Sjeff ret = rdma_disconnect(ctx->cm_id); 797219820Sjeff ucma_put_ctx(ctx); 798219820Sjeff return ret; 799219820Sjeff} 800219820Sjeff 801219820Sjeffstatic ssize_t ucma_init_qp_attr(struct ucma_file *file, 802219820Sjeff const char __user *inbuf, 803219820Sjeff int in_len, int out_len) 804219820Sjeff{ 805219820Sjeff struct rdma_ucm_init_qp_attr cmd; 806219820Sjeff struct ib_uverbs_qp_attr resp; 807219820Sjeff struct ucma_context *ctx; 808219820Sjeff struct ib_qp_attr qp_attr; 809219820Sjeff int ret; 810219820Sjeff 811219820Sjeff if (out_len < sizeof(resp)) 812219820Sjeff return -ENOSPC; 813219820Sjeff 814219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 815219820Sjeff return -EFAULT; 816219820Sjeff 817219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 818219820Sjeff if (IS_ERR(ctx)) 819219820Sjeff return PTR_ERR(ctx); 820219820Sjeff 821219820Sjeff resp.qp_attr_mask = 0; 822219820Sjeff memset(&qp_attr, 0, sizeof qp_attr); 823219820Sjeff qp_attr.qp_state = cmd.qp_state; 824219820Sjeff ret = rdma_init_qp_attr(ctx->cm_id, &qp_attr, &resp.qp_attr_mask); 825219820Sjeff if (ret) 826219820Sjeff goto out; 827219820Sjeff 828219820Sjeff ib_copy_qp_attr_to_user(&resp, &qp_attr); 829219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 830219820Sjeff &resp, sizeof(resp))) 831219820Sjeff ret = -EFAULT; 832219820Sjeff 833219820Sjeffout: 834219820Sjeff ucma_put_ctx(ctx); 835219820Sjeff return ret; 836219820Sjeff} 837219820Sjeff 838219820Sjeffstatic int ucma_set_option_id(struct ucma_context *ctx, int optname, 839219820Sjeff void *optval, size_t optlen) 840219820Sjeff{ 841219820Sjeff int ret = 0; 842219820Sjeff 843219820Sjeff switch (optname) { 844219820Sjeff case RDMA_OPTION_ID_TOS: 845219820Sjeff if (optlen != sizeof(u8)) { 846219820Sjeff ret = -EINVAL; 847219820Sjeff break; 848219820Sjeff } 849219820Sjeff rdma_set_service_type(ctx->cm_id, *((u8 *) optval)); 850219820Sjeff break; 851219820Sjeff default: 852219820Sjeff ret = -ENOSYS; 853219820Sjeff } 854219820Sjeff 855219820Sjeff return ret; 856219820Sjeff} 857219820Sjeff 858219820Sjeffstatic int ucma_set_ib_path(struct ucma_context *ctx, 859219820Sjeff struct ib_path_rec_data *path_data, size_t optlen) 860219820Sjeff{ 861219820Sjeff struct ib_sa_path_rec sa_path; 862219820Sjeff struct rdma_cm_event event; 863219820Sjeff int ret; 864219820Sjeff 865219820Sjeff if (optlen % sizeof(*path_data)) 866219820Sjeff return -EINVAL; 867219820Sjeff 868219820Sjeff for (; optlen; optlen -= sizeof(*path_data), path_data++) { 869219820Sjeff if (path_data->flags == (IB_PATH_GMP | IB_PATH_PRIMARY | 870219820Sjeff IB_PATH_BIDIRECTIONAL)) 871219820Sjeff break; 872219820Sjeff } 873219820Sjeff 874219820Sjeff if (!optlen) 875219820Sjeff return -EINVAL; 876219820Sjeff 877219820Sjeff ib_sa_unpack_path(path_data->path_rec, &sa_path); 878219820Sjeff ret = rdma_set_ib_paths(ctx->cm_id, &sa_path, 1); 879219820Sjeff if (ret) 880219820Sjeff return ret; 881219820Sjeff 882219820Sjeff memset(&event, 0, sizeof event); 883219820Sjeff event.event = RDMA_CM_EVENT_ROUTE_RESOLVED; 884219820Sjeff return ucma_event_handler(ctx->cm_id, &event); 885219820Sjeff} 886219820Sjeff 887219820Sjeffstatic int ucma_set_option_ib(struct ucma_context *ctx, int optname, 888219820Sjeff void *optval, size_t optlen) 889219820Sjeff{ 890219820Sjeff int ret; 891219820Sjeff 892219820Sjeff switch (optname) { 893219820Sjeff case RDMA_OPTION_IB_PATH: 894219820Sjeff ret = ucma_set_ib_path(ctx, optval, optlen); 895219820Sjeff break; 896219820Sjeff default: 897219820Sjeff ret = -ENOSYS; 898219820Sjeff } 899219820Sjeff 900219820Sjeff return ret; 901219820Sjeff} 902219820Sjeff 903219820Sjeffstatic int ucma_set_option_level(struct ucma_context *ctx, int level, 904219820Sjeff int optname, void *optval, size_t optlen) 905219820Sjeff{ 906219820Sjeff int ret; 907219820Sjeff 908219820Sjeff switch (level) { 909219820Sjeff case RDMA_OPTION_ID: 910219820Sjeff ret = ucma_set_option_id(ctx, optname, optval, optlen); 911219820Sjeff break; 912219820Sjeff case RDMA_OPTION_IB: 913219820Sjeff ret = ucma_set_option_ib(ctx, optname, optval, optlen); 914219820Sjeff break; 915219820Sjeff default: 916219820Sjeff ret = -ENOSYS; 917219820Sjeff } 918219820Sjeff 919219820Sjeff return ret; 920219820Sjeff} 921219820Sjeff 922219820Sjeffstatic ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf, 923219820Sjeff int in_len, int out_len) 924219820Sjeff{ 925219820Sjeff struct rdma_ucm_set_option cmd; 926219820Sjeff struct ucma_context *ctx; 927219820Sjeff void *optval; 928219820Sjeff int ret; 929219820Sjeff 930219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 931219820Sjeff return -EFAULT; 932219820Sjeff 933219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 934219820Sjeff if (IS_ERR(ctx)) 935219820Sjeff return PTR_ERR(ctx); 936219820Sjeff 937219820Sjeff optval = kmalloc(cmd.optlen, GFP_KERNEL); 938219820Sjeff if (!optval) { 939219820Sjeff ret = -ENOMEM; 940219820Sjeff goto out1; 941219820Sjeff } 942219820Sjeff 943219820Sjeff if (copy_from_user(optval, (void __user *) (unsigned long) cmd.optval, 944219820Sjeff cmd.optlen)) { 945219820Sjeff ret = -EFAULT; 946219820Sjeff goto out2; 947219820Sjeff } 948219820Sjeff 949219820Sjeff ret = ucma_set_option_level(ctx, cmd.level, cmd.optname, optval, 950219820Sjeff cmd.optlen); 951219820Sjeffout2: 952219820Sjeff kfree(optval); 953219820Sjeffout1: 954219820Sjeff ucma_put_ctx(ctx); 955219820Sjeff return ret; 956219820Sjeff} 957219820Sjeff 958219820Sjeffstatic ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, 959219820Sjeff int in_len, int out_len) 960219820Sjeff{ 961219820Sjeff struct rdma_ucm_notify cmd; 962219820Sjeff struct ucma_context *ctx; 963219820Sjeff int ret; 964219820Sjeff 965219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 966219820Sjeff return -EFAULT; 967219820Sjeff 968219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 969219820Sjeff if (IS_ERR(ctx)) 970219820Sjeff return PTR_ERR(ctx); 971219820Sjeff 972219820Sjeff ret = rdma_notify(ctx->cm_id, (enum ib_event_type) cmd.event); 973219820Sjeff ucma_put_ctx(ctx); 974219820Sjeff return ret; 975219820Sjeff} 976219820Sjeff 977219820Sjeffstatic ssize_t ucma_join_multicast(struct ucma_file *file, 978219820Sjeff const char __user *inbuf, 979219820Sjeff int in_len, int out_len) 980219820Sjeff{ 981219820Sjeff struct rdma_ucm_join_mcast cmd; 982219820Sjeff struct rdma_ucm_create_id_resp resp; 983219820Sjeff struct ucma_context *ctx; 984219820Sjeff struct ucma_multicast *mc; 985219820Sjeff int ret; 986219820Sjeff 987219820Sjeff if (out_len < sizeof(resp)) 988219820Sjeff return -ENOSPC; 989219820Sjeff 990219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 991219820Sjeff return -EFAULT; 992219820Sjeff 993219820Sjeff ctx = ucma_get_ctx(file, cmd.id); 994219820Sjeff if (IS_ERR(ctx)) 995219820Sjeff return PTR_ERR(ctx); 996219820Sjeff 997219820Sjeff mutex_lock(&file->mut); 998219820Sjeff mc = ucma_alloc_multicast(ctx); 999219820Sjeff if (!mc) { 1000219820Sjeff ret = -ENOMEM; 1001219820Sjeff goto err1; 1002219820Sjeff } 1003219820Sjeff 1004219820Sjeff mc->uid = cmd.uid; 1005219820Sjeff memcpy(&mc->addr, &cmd.addr, sizeof cmd.addr); 1006219820Sjeff ret = rdma_join_multicast(ctx->cm_id, (struct sockaddr *) &mc->addr, mc); 1007219820Sjeff if (ret) 1008219820Sjeff goto err2; 1009219820Sjeff 1010219820Sjeff resp.id = mc->id; 1011219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 1012219820Sjeff &resp, sizeof(resp))) { 1013219820Sjeff ret = -EFAULT; 1014219820Sjeff goto err3; 1015219820Sjeff } 1016219820Sjeff 1017219820Sjeff mutex_unlock(&file->mut); 1018219820Sjeff ucma_put_ctx(ctx); 1019219820Sjeff return 0; 1020219820Sjeff 1021219820Sjefferr3: 1022219820Sjeff rdma_leave_multicast(ctx->cm_id, (struct sockaddr *) &mc->addr); 1023219820Sjeff ucma_cleanup_mc_events(mc); 1024219820Sjefferr2: 1025219820Sjeff mutex_lock(&mut); 1026219820Sjeff idr_remove(&multicast_idr, mc->id); 1027219820Sjeff mutex_unlock(&mut); 1028219820Sjeff list_del(&mc->list); 1029219820Sjeff kfree(mc); 1030219820Sjefferr1: 1031219820Sjeff mutex_unlock(&file->mut); 1032219820Sjeff ucma_put_ctx(ctx); 1033219820Sjeff return ret; 1034219820Sjeff} 1035219820Sjeff 1036219820Sjeffstatic ssize_t ucma_leave_multicast(struct ucma_file *file, 1037219820Sjeff const char __user *inbuf, 1038219820Sjeff int in_len, int out_len) 1039219820Sjeff{ 1040219820Sjeff struct rdma_ucm_destroy_id cmd; 1041219820Sjeff struct rdma_ucm_destroy_id_resp resp; 1042219820Sjeff struct ucma_multicast *mc; 1043219820Sjeff int ret = 0; 1044219820Sjeff 1045219820Sjeff if (out_len < sizeof(resp)) 1046219820Sjeff return -ENOSPC; 1047219820Sjeff 1048219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 1049219820Sjeff return -EFAULT; 1050219820Sjeff 1051219820Sjeff mutex_lock(&mut); 1052219820Sjeff mc = idr_find(&multicast_idr, cmd.id); 1053219820Sjeff if (!mc) 1054219820Sjeff mc = ERR_PTR(-ENOENT); 1055219820Sjeff else if (mc->ctx->file != file) 1056219820Sjeff mc = ERR_PTR(-EINVAL); 1057219820Sjeff else { 1058219820Sjeff idr_remove(&multicast_idr, mc->id); 1059219820Sjeff atomic_inc(&mc->ctx->ref); 1060219820Sjeff } 1061219820Sjeff mutex_unlock(&mut); 1062219820Sjeff 1063219820Sjeff if (IS_ERR(mc)) { 1064219820Sjeff ret = PTR_ERR(mc); 1065219820Sjeff goto out; 1066219820Sjeff } 1067219820Sjeff 1068219820Sjeff rdma_leave_multicast(mc->ctx->cm_id, (struct sockaddr *) &mc->addr); 1069219820Sjeff mutex_lock(&mc->ctx->file->mut); 1070219820Sjeff ucma_cleanup_mc_events(mc); 1071219820Sjeff list_del(&mc->list); 1072219820Sjeff mutex_unlock(&mc->ctx->file->mut); 1073219820Sjeff 1074219820Sjeff ucma_put_ctx(mc->ctx); 1075219820Sjeff resp.events_reported = mc->events_reported; 1076219820Sjeff kfree(mc); 1077219820Sjeff 1078219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 1079219820Sjeff &resp, sizeof(resp))) 1080219820Sjeff ret = -EFAULT; 1081219820Sjeffout: 1082219820Sjeff return ret; 1083219820Sjeff} 1084219820Sjeff 1085219820Sjeffstatic void ucma_lock_files(struct ucma_file *file1, struct ucma_file *file2) 1086219820Sjeff{ 1087219820Sjeff /* Acquire mutex's based on pointer comparison to prevent deadlock. */ 1088219820Sjeff if (file1 < file2) { 1089219820Sjeff mutex_lock(&file1->mut); 1090219820Sjeff mutex_lock(&file2->mut); 1091219820Sjeff } else { 1092219820Sjeff mutex_lock(&file2->mut); 1093219820Sjeff mutex_lock(&file1->mut); 1094219820Sjeff } 1095219820Sjeff} 1096219820Sjeff 1097219820Sjeffstatic void ucma_unlock_files(struct ucma_file *file1, struct ucma_file *file2) 1098219820Sjeff{ 1099219820Sjeff if (file1 < file2) { 1100219820Sjeff mutex_unlock(&file2->mut); 1101219820Sjeff mutex_unlock(&file1->mut); 1102219820Sjeff } else { 1103219820Sjeff mutex_unlock(&file1->mut); 1104219820Sjeff mutex_unlock(&file2->mut); 1105219820Sjeff } 1106219820Sjeff} 1107219820Sjeff 1108219820Sjeffstatic void ucma_move_events(struct ucma_context *ctx, struct ucma_file *file) 1109219820Sjeff{ 1110219820Sjeff struct ucma_event *uevent, *tmp; 1111219820Sjeff 1112219820Sjeff list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) 1113219820Sjeff if (uevent->ctx == ctx) 1114219820Sjeff list_move_tail(&uevent->list, &file->event_list); 1115219820Sjeff} 1116219820Sjeff 1117219820Sjeffstatic ssize_t ucma_migrate_id(struct ucma_file *new_file, 1118219820Sjeff const char __user *inbuf, 1119219820Sjeff int in_len, int out_len) 1120219820Sjeff{ 1121219820Sjeff struct rdma_ucm_migrate_id cmd; 1122219820Sjeff struct rdma_ucm_migrate_resp resp; 1123219820Sjeff struct ucma_context *ctx; 1124219820Sjeff struct file *filp; 1125219820Sjeff struct ucma_file *cur_file; 1126219820Sjeff int ret = 0; 1127219820Sjeff 1128219820Sjeff if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 1129219820Sjeff return -EFAULT; 1130219820Sjeff 1131219820Sjeff /* Get current fd to protect against it being closed */ 1132219820Sjeff filp = fget(cmd.fd); 1133219820Sjeff if (!filp) 1134219820Sjeff return -ENOENT; 1135219820Sjeff 1136219820Sjeff /* Validate current fd and prevent destruction of id. */ 1137219820Sjeff ctx = ucma_get_ctx(filp->private_data, cmd.id); 1138219820Sjeff if (IS_ERR(ctx)) { 1139219820Sjeff ret = PTR_ERR(ctx); 1140219820Sjeff goto file_put; 1141219820Sjeff } 1142219820Sjeff 1143219820Sjeff cur_file = ctx->file; 1144219820Sjeff if (cur_file == new_file) { 1145219820Sjeff resp.events_reported = ctx->events_reported; 1146219820Sjeff goto response; 1147219820Sjeff } 1148219820Sjeff 1149219820Sjeff /* 1150219820Sjeff * Migrate events between fd's, maintaining order, and avoiding new 1151219820Sjeff * events being added before existing events. 1152219820Sjeff */ 1153219820Sjeff ucma_lock_files(cur_file, new_file); 1154219820Sjeff mutex_lock(&mut); 1155219820Sjeff 1156219820Sjeff list_move_tail(&ctx->list, &new_file->ctx_list); 1157219820Sjeff ucma_move_events(ctx, new_file); 1158219820Sjeff ctx->file = new_file; 1159219820Sjeff resp.events_reported = ctx->events_reported; 1160219820Sjeff 1161219820Sjeff mutex_unlock(&mut); 1162219820Sjeff ucma_unlock_files(cur_file, new_file); 1163219820Sjeff 1164219820Sjeffresponse: 1165219820Sjeff if (copy_to_user((void __user *)(unsigned long)cmd.response, 1166219820Sjeff &resp, sizeof(resp))) 1167219820Sjeff ret = -EFAULT; 1168219820Sjeff 1169219820Sjeff ucma_put_ctx(ctx); 1170219820Sjefffile_put: 1171219820Sjeff fput(filp); 1172219820Sjeff return ret; 1173219820Sjeff} 1174219820Sjeff 1175219820Sjeffstatic ssize_t (*ucma_cmd_table[])(struct ucma_file *file, 1176219820Sjeff const char __user *inbuf, 1177219820Sjeff int in_len, int out_len) = { 1178219820Sjeff [RDMA_USER_CM_CMD_CREATE_ID] = ucma_create_id, 1179219820Sjeff [RDMA_USER_CM_CMD_DESTROY_ID] = ucma_destroy_id, 1180219820Sjeff [RDMA_USER_CM_CMD_BIND_ADDR] = ucma_bind_addr, 1181219820Sjeff [RDMA_USER_CM_CMD_RESOLVE_ADDR] = ucma_resolve_addr, 1182219820Sjeff [RDMA_USER_CM_CMD_RESOLVE_ROUTE]= ucma_resolve_route, 1183219820Sjeff [RDMA_USER_CM_CMD_QUERY_ROUTE] = ucma_query_route, 1184219820Sjeff [RDMA_USER_CM_CMD_CONNECT] = ucma_connect, 1185219820Sjeff [RDMA_USER_CM_CMD_LISTEN] = ucma_listen, 1186219820Sjeff [RDMA_USER_CM_CMD_ACCEPT] = ucma_accept, 1187219820Sjeff [RDMA_USER_CM_CMD_REJECT] = ucma_reject, 1188219820Sjeff [RDMA_USER_CM_CMD_DISCONNECT] = ucma_disconnect, 1189219820Sjeff [RDMA_USER_CM_CMD_INIT_QP_ATTR] = ucma_init_qp_attr, 1190219820Sjeff [RDMA_USER_CM_CMD_GET_EVENT] = ucma_get_event, 1191219820Sjeff [RDMA_USER_CM_CMD_GET_OPTION] = NULL, 1192219820Sjeff [RDMA_USER_CM_CMD_SET_OPTION] = ucma_set_option, 1193219820Sjeff [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, 1194219820Sjeff [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast, 1195219820Sjeff [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast, 1196219820Sjeff [RDMA_USER_CM_CMD_MIGRATE_ID] = ucma_migrate_id 1197219820Sjeff}; 1198219820Sjeff 1199219820Sjeffstatic ssize_t ucma_write(struct file *filp, const char __user *buf, 1200219820Sjeff size_t len, loff_t *pos) 1201219820Sjeff{ 1202219820Sjeff struct ucma_file *file = filp->private_data; 1203219820Sjeff struct rdma_ucm_cmd_hdr hdr; 1204219820Sjeff ssize_t ret; 1205219820Sjeff 1206219820Sjeff if (len < sizeof(hdr)) 1207219820Sjeff return -EINVAL; 1208219820Sjeff 1209219820Sjeff if (copy_from_user(&hdr, buf, sizeof(hdr))) 1210219820Sjeff return -EFAULT; 1211219820Sjeff 1212219820Sjeff if (hdr.cmd < 0 || hdr.cmd >= ARRAY_SIZE(ucma_cmd_table)) 1213219820Sjeff return -EINVAL; 1214219820Sjeff 1215219820Sjeff if (hdr.in + sizeof(hdr) > len) 1216219820Sjeff return -EINVAL; 1217219820Sjeff 1218219820Sjeff if (!ucma_cmd_table[hdr.cmd]) 1219219820Sjeff return -ENOSYS; 1220219820Sjeff 1221219820Sjeff ret = ucma_cmd_table[hdr.cmd](file, buf + sizeof(hdr), hdr.in, hdr.out); 1222219820Sjeff if (!ret) 1223219820Sjeff ret = len; 1224219820Sjeff 1225219820Sjeff return ret; 1226219820Sjeff} 1227219820Sjeff 1228219820Sjeffstatic unsigned int ucma_poll(struct file *filp, struct poll_table_struct *wait) 1229219820Sjeff{ 1230219820Sjeff struct ucma_file *file = filp->private_data; 1231219820Sjeff unsigned int mask = 0; 1232219820Sjeff 1233219820Sjeff poll_wait(filp, &file->poll_wait, wait); 1234219820Sjeff 1235219820Sjeff if (!list_empty(&file->event_list)) 1236219820Sjeff mask = POLLIN | POLLRDNORM; 1237219820Sjeff 1238219820Sjeff return mask; 1239219820Sjeff} 1240219820Sjeff 1241219820Sjeff/* 1242219820Sjeff * ucma_open() does not need the BKL: 1243219820Sjeff * 1244219820Sjeff * - no global state is referred to; 1245219820Sjeff * - there is no ioctl method to race against; 1246219820Sjeff * - no further module initialization is required for open to work 1247219820Sjeff * after the device is registered. 1248219820Sjeff */ 1249219820Sjeffstatic int ucma_open(struct inode *inode, struct file *filp) 1250219820Sjeff{ 1251219820Sjeff struct ucma_file *file; 1252219820Sjeff 1253219820Sjeff file = kmalloc(sizeof *file, GFP_KERNEL); 1254219820Sjeff if (!file) 1255219820Sjeff return -ENOMEM; 1256219820Sjeff 1257219820Sjeff INIT_LIST_HEAD(&file->event_list); 1258219820Sjeff INIT_LIST_HEAD(&file->ctx_list); 1259219820Sjeff init_waitqueue_head(&file->poll_wait); 1260219820Sjeff mutex_init(&file->mut); 1261219820Sjeff 1262219820Sjeff filp->private_data = file; 1263219820Sjeff file->filp = filp; 1264219820Sjeff return 0; 1265219820Sjeff} 1266219820Sjeff 1267219820Sjeffstatic int ucma_close(struct inode *inode, struct file *filp) 1268219820Sjeff{ 1269219820Sjeff struct ucma_file *file = filp->private_data; 1270219820Sjeff struct ucma_context *ctx, *tmp; 1271219820Sjeff 1272219820Sjeff mutex_lock(&file->mut); 1273219820Sjeff list_for_each_entry_safe(ctx, tmp, &file->ctx_list, list) { 1274219820Sjeff mutex_unlock(&file->mut); 1275219820Sjeff 1276219820Sjeff mutex_lock(&mut); 1277219820Sjeff idr_remove(&ctx_idr, ctx->id); 1278219820Sjeff mutex_unlock(&mut); 1279219820Sjeff 1280219820Sjeff ucma_free_ctx(ctx); 1281219820Sjeff mutex_lock(&file->mut); 1282219820Sjeff } 1283219820Sjeff mutex_unlock(&file->mut); 1284219820Sjeff kfree(file); 1285219820Sjeff return 0; 1286219820Sjeff} 1287219820Sjeff 1288219820Sjeffstatic const struct file_operations ucma_fops = { 1289219820Sjeff .owner = THIS_MODULE, 1290219820Sjeff .open = ucma_open, 1291219820Sjeff .release = ucma_close, 1292219820Sjeff .write = ucma_write, 1293219820Sjeff .poll = ucma_poll, 1294219820Sjeff}; 1295219820Sjeff 1296219820Sjeffstatic struct miscdevice ucma_misc = { 1297219820Sjeff .minor = MISC_DYNAMIC_MINOR, 1298219820Sjeff .name = "rdma_cm", 1299219820Sjeff .fops = &ucma_fops, 1300219820Sjeff}; 1301219820Sjeff 1302219820Sjeffstatic ssize_t show_abi_version(struct device *dev, 1303219820Sjeff struct device_attribute *attr, 1304219820Sjeff char *buf) 1305219820Sjeff{ 1306219820Sjeff return sprintf(buf, "%d\n", RDMA_USER_CM_ABI_VERSION); 1307219820Sjeff} 1308219820Sjeffstatic DEVICE_ATTR(abi_version, S_IRUGO, show_abi_version, NULL); 1309219820Sjeff 1310219820Sjeffstatic int __init ucma_init(void) 1311219820Sjeff{ 1312219820Sjeff int ret; 1313219820Sjeff 1314219820Sjeff ret = misc_register(&ucma_misc); 1315219820Sjeff if (ret) 1316219820Sjeff return ret; 1317219820Sjeff 1318219820Sjeff ret = device_create_file(ucma_misc.this_device, &dev_attr_abi_version); 1319219820Sjeff if (ret) { 1320219820Sjeff printk(KERN_ERR "rdma_ucm: couldn't create abi_version attr\n"); 1321219820Sjeff goto err; 1322219820Sjeff } 1323219820Sjeff return 0; 1324219820Sjefferr: 1325219820Sjeff misc_deregister(&ucma_misc); 1326219820Sjeff return ret; 1327219820Sjeff} 1328219820Sjeff 1329219820Sjeffstatic void __exit ucma_cleanup(void) 1330219820Sjeff{ 1331219820Sjeff device_remove_file(ucma_misc.this_device, &dev_attr_abi_version); 1332219820Sjeff misc_deregister(&ucma_misc); 1333219820Sjeff idr_destroy(&ctx_idr); 1334219820Sjeff} 1335219820Sjeff 1336219820Sjeffmodule_init(ucma_init); 1337219820Sjeffmodule_exit(ucma_cleanup); 1338