162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2005-2006 Intel Corporation. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/completion.h> 3462306a36Sopenharmony_ci#include <linux/file.h> 3562306a36Sopenharmony_ci#include <linux/mutex.h> 3662306a36Sopenharmony_ci#include <linux/poll.h> 3762306a36Sopenharmony_ci#include <linux/sched.h> 3862306a36Sopenharmony_ci#include <linux/idr.h> 3962306a36Sopenharmony_ci#include <linux/in.h> 4062306a36Sopenharmony_ci#include <linux/in6.h> 4162306a36Sopenharmony_ci#include <linux/miscdevice.h> 4262306a36Sopenharmony_ci#include <linux/slab.h> 4362306a36Sopenharmony_ci#include <linux/sysctl.h> 4462306a36Sopenharmony_ci#include <linux/module.h> 4562306a36Sopenharmony_ci#include <linux/nsproxy.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <linux/nospec.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include <rdma/rdma_user_cm.h> 5062306a36Sopenharmony_ci#include <rdma/ib_marshall.h> 5162306a36Sopenharmony_ci#include <rdma/rdma_cm.h> 5262306a36Sopenharmony_ci#include <rdma/rdma_cm_ib.h> 5362306a36Sopenharmony_ci#include <rdma/ib_addr.h> 5462306a36Sopenharmony_ci#include <rdma/ib.h> 5562306a36Sopenharmony_ci#include <rdma/ib_cm.h> 5662306a36Sopenharmony_ci#include <rdma/rdma_netlink.h> 5762306a36Sopenharmony_ci#include "core_priv.h" 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciMODULE_AUTHOR("Sean Hefty"); 6062306a36Sopenharmony_ciMODULE_DESCRIPTION("RDMA Userspace Connection Manager Access"); 6162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic unsigned int max_backlog = 1024; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct ctl_table_header *ucma_ctl_table_hdr; 6662306a36Sopenharmony_cistatic struct ctl_table ucma_ctl_table[] = { 6762306a36Sopenharmony_ci { 6862306a36Sopenharmony_ci .procname = "max_backlog", 6962306a36Sopenharmony_ci .data = &max_backlog, 7062306a36Sopenharmony_ci .maxlen = sizeof max_backlog, 7162306a36Sopenharmony_ci .mode = 0644, 7262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 7362306a36Sopenharmony_ci }, 7462306a36Sopenharmony_ci { } 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct ucma_file { 7862306a36Sopenharmony_ci struct mutex mut; 7962306a36Sopenharmony_ci struct file *filp; 8062306a36Sopenharmony_ci struct list_head ctx_list; 8162306a36Sopenharmony_ci struct list_head event_list; 8262306a36Sopenharmony_ci wait_queue_head_t poll_wait; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct ucma_context { 8662306a36Sopenharmony_ci u32 id; 8762306a36Sopenharmony_ci struct completion comp; 8862306a36Sopenharmony_ci refcount_t ref; 8962306a36Sopenharmony_ci int events_reported; 9062306a36Sopenharmony_ci atomic_t backlog; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci struct ucma_file *file; 9362306a36Sopenharmony_ci struct rdma_cm_id *cm_id; 9462306a36Sopenharmony_ci struct mutex mutex; 9562306a36Sopenharmony_ci u64 uid; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci struct list_head list; 9862306a36Sopenharmony_ci struct list_head mc_list; 9962306a36Sopenharmony_ci struct work_struct close_work; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct ucma_multicast { 10362306a36Sopenharmony_ci struct ucma_context *ctx; 10462306a36Sopenharmony_ci u32 id; 10562306a36Sopenharmony_ci int events_reported; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci u64 uid; 10862306a36Sopenharmony_ci u8 join_state; 10962306a36Sopenharmony_ci struct list_head list; 11062306a36Sopenharmony_ci struct sockaddr_storage addr; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct ucma_event { 11462306a36Sopenharmony_ci struct ucma_context *ctx; 11562306a36Sopenharmony_ci struct ucma_context *conn_req_ctx; 11662306a36Sopenharmony_ci struct ucma_multicast *mc; 11762306a36Sopenharmony_ci struct list_head list; 11862306a36Sopenharmony_ci struct rdma_ucm_event_resp resp; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic DEFINE_XARRAY_ALLOC(ctx_table); 12262306a36Sopenharmony_cistatic DEFINE_XARRAY_ALLOC(multicast_table); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const struct file_operations ucma_fops; 12562306a36Sopenharmony_cistatic int ucma_destroy_private_ctx(struct ucma_context *ctx); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic inline struct ucma_context *_ucma_find_context(int id, 12862306a36Sopenharmony_ci struct ucma_file *file) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct ucma_context *ctx; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ctx = xa_load(&ctx_table, id); 13362306a36Sopenharmony_ci if (!ctx) 13462306a36Sopenharmony_ci ctx = ERR_PTR(-ENOENT); 13562306a36Sopenharmony_ci else if (ctx->file != file) 13662306a36Sopenharmony_ci ctx = ERR_PTR(-EINVAL); 13762306a36Sopenharmony_ci return ctx; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic struct ucma_context *ucma_get_ctx(struct ucma_file *file, int id) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct ucma_context *ctx; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci xa_lock(&ctx_table); 14562306a36Sopenharmony_ci ctx = _ucma_find_context(id, file); 14662306a36Sopenharmony_ci if (!IS_ERR(ctx)) 14762306a36Sopenharmony_ci if (!refcount_inc_not_zero(&ctx->ref)) 14862306a36Sopenharmony_ci ctx = ERR_PTR(-ENXIO); 14962306a36Sopenharmony_ci xa_unlock(&ctx_table); 15062306a36Sopenharmony_ci return ctx; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void ucma_put_ctx(struct ucma_context *ctx) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci if (refcount_dec_and_test(&ctx->ref)) 15662306a36Sopenharmony_ci complete(&ctx->comp); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Same as ucm_get_ctx but requires that ->cm_id->device is valid, eg that the 16162306a36Sopenharmony_ci * CM_ID is bound. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistatic struct ucma_context *ucma_get_ctx_dev(struct ucma_file *file, int id) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct ucma_context *ctx = ucma_get_ctx(file, id); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (IS_ERR(ctx)) 16862306a36Sopenharmony_ci return ctx; 16962306a36Sopenharmony_ci if (!ctx->cm_id->device) { 17062306a36Sopenharmony_ci ucma_put_ctx(ctx); 17162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci return ctx; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void ucma_close_id(struct work_struct *work) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct ucma_context *ctx = container_of(work, struct ucma_context, close_work); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* once all inflight tasks are finished, we close all underlying 18162306a36Sopenharmony_ci * resources. The context is still alive till its explicit destryoing 18262306a36Sopenharmony_ci * by its creator. This puts back the xarray's reference. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci ucma_put_ctx(ctx); 18562306a36Sopenharmony_ci wait_for_completion(&ctx->comp); 18662306a36Sopenharmony_ci /* No new events will be generated after destroying the id. */ 18762306a36Sopenharmony_ci rdma_destroy_id(ctx->cm_id); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Reading the cm_id without holding a positive ref is not allowed */ 19062306a36Sopenharmony_ci ctx->cm_id = NULL; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct ucma_context *ucma_alloc_ctx(struct ucma_file *file) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct ucma_context *ctx; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 19862306a36Sopenharmony_ci if (!ctx) 19962306a36Sopenharmony_ci return NULL; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci INIT_WORK(&ctx->close_work, ucma_close_id); 20262306a36Sopenharmony_ci init_completion(&ctx->comp); 20362306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->mc_list); 20462306a36Sopenharmony_ci /* So list_del() will work if we don't do ucma_finish_ctx() */ 20562306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->list); 20662306a36Sopenharmony_ci ctx->file = file; 20762306a36Sopenharmony_ci mutex_init(&ctx->mutex); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (xa_alloc(&ctx_table, &ctx->id, NULL, xa_limit_32b, GFP_KERNEL)) { 21062306a36Sopenharmony_ci kfree(ctx); 21162306a36Sopenharmony_ci return NULL; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci return ctx; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void ucma_set_ctx_cm_id(struct ucma_context *ctx, 21762306a36Sopenharmony_ci struct rdma_cm_id *cm_id) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci refcount_set(&ctx->ref, 1); 22062306a36Sopenharmony_ci ctx->cm_id = cm_id; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void ucma_finish_ctx(struct ucma_context *ctx) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci lockdep_assert_held(&ctx->file->mut); 22662306a36Sopenharmony_ci list_add_tail(&ctx->list, &ctx->file->ctx_list); 22762306a36Sopenharmony_ci xa_store(&ctx_table, ctx->id, ctx, GFP_KERNEL); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst, 23162306a36Sopenharmony_ci struct rdma_conn_param *src) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci if (src->private_data_len) 23462306a36Sopenharmony_ci memcpy(dst->private_data, src->private_data, 23562306a36Sopenharmony_ci src->private_data_len); 23662306a36Sopenharmony_ci dst->private_data_len = src->private_data_len; 23762306a36Sopenharmony_ci dst->responder_resources = src->responder_resources; 23862306a36Sopenharmony_ci dst->initiator_depth = src->initiator_depth; 23962306a36Sopenharmony_ci dst->flow_control = src->flow_control; 24062306a36Sopenharmony_ci dst->retry_count = src->retry_count; 24162306a36Sopenharmony_ci dst->rnr_retry_count = src->rnr_retry_count; 24262306a36Sopenharmony_ci dst->srq = src->srq; 24362306a36Sopenharmony_ci dst->qp_num = src->qp_num; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void ucma_copy_ud_event(struct ib_device *device, 24762306a36Sopenharmony_ci struct rdma_ucm_ud_param *dst, 24862306a36Sopenharmony_ci struct rdma_ud_param *src) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci if (src->private_data_len) 25162306a36Sopenharmony_ci memcpy(dst->private_data, src->private_data, 25262306a36Sopenharmony_ci src->private_data_len); 25362306a36Sopenharmony_ci dst->private_data_len = src->private_data_len; 25462306a36Sopenharmony_ci ib_copy_ah_attr_to_user(device, &dst->ah_attr, &src->ah_attr); 25562306a36Sopenharmony_ci dst->qp_num = src->qp_num; 25662306a36Sopenharmony_ci dst->qkey = src->qkey; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic struct ucma_event *ucma_create_uevent(struct ucma_context *ctx, 26062306a36Sopenharmony_ci struct rdma_cm_event *event) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct ucma_event *uevent; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci uevent = kzalloc(sizeof(*uevent), GFP_KERNEL); 26562306a36Sopenharmony_ci if (!uevent) 26662306a36Sopenharmony_ci return NULL; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci uevent->ctx = ctx; 26962306a36Sopenharmony_ci switch (event->event) { 27062306a36Sopenharmony_ci case RDMA_CM_EVENT_MULTICAST_JOIN: 27162306a36Sopenharmony_ci case RDMA_CM_EVENT_MULTICAST_ERROR: 27262306a36Sopenharmony_ci uevent->mc = (struct ucma_multicast *) 27362306a36Sopenharmony_ci event->param.ud.private_data; 27462306a36Sopenharmony_ci uevent->resp.uid = uevent->mc->uid; 27562306a36Sopenharmony_ci uevent->resp.id = uevent->mc->id; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci uevent->resp.uid = ctx->uid; 27962306a36Sopenharmony_ci uevent->resp.id = ctx->id; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci uevent->resp.event = event->event; 28362306a36Sopenharmony_ci uevent->resp.status = event->status; 28462306a36Sopenharmony_ci if (ctx->cm_id->qp_type == IB_QPT_UD) 28562306a36Sopenharmony_ci ucma_copy_ud_event(ctx->cm_id->device, &uevent->resp.param.ud, 28662306a36Sopenharmony_ci &event->param.ud); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci ucma_copy_conn_event(&uevent->resp.param.conn, 28962306a36Sopenharmony_ci &event->param.conn); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci uevent->resp.ece.vendor_id = event->ece.vendor_id; 29262306a36Sopenharmony_ci uevent->resp.ece.attr_mod = event->ece.attr_mod; 29362306a36Sopenharmony_ci return uevent; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int ucma_connect_event_handler(struct rdma_cm_id *cm_id, 29762306a36Sopenharmony_ci struct rdma_cm_event *event) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct ucma_context *listen_ctx = cm_id->context; 30062306a36Sopenharmony_ci struct ucma_context *ctx; 30162306a36Sopenharmony_ci struct ucma_event *uevent; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!atomic_add_unless(&listen_ctx->backlog, -1, 0)) 30462306a36Sopenharmony_ci return -ENOMEM; 30562306a36Sopenharmony_ci ctx = ucma_alloc_ctx(listen_ctx->file); 30662306a36Sopenharmony_ci if (!ctx) 30762306a36Sopenharmony_ci goto err_backlog; 30862306a36Sopenharmony_ci ucma_set_ctx_cm_id(ctx, cm_id); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci uevent = ucma_create_uevent(listen_ctx, event); 31162306a36Sopenharmony_ci if (!uevent) 31262306a36Sopenharmony_ci goto err_alloc; 31362306a36Sopenharmony_ci uevent->conn_req_ctx = ctx; 31462306a36Sopenharmony_ci uevent->resp.id = ctx->id; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ctx->cm_id->context = ctx; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci mutex_lock(&ctx->file->mut); 31962306a36Sopenharmony_ci ucma_finish_ctx(ctx); 32062306a36Sopenharmony_ci list_add_tail(&uevent->list, &ctx->file->event_list); 32162306a36Sopenharmony_ci mutex_unlock(&ctx->file->mut); 32262306a36Sopenharmony_ci wake_up_interruptible(&ctx->file->poll_wait); 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cierr_alloc: 32662306a36Sopenharmony_ci ucma_destroy_private_ctx(ctx); 32762306a36Sopenharmony_cierr_backlog: 32862306a36Sopenharmony_ci atomic_inc(&listen_ctx->backlog); 32962306a36Sopenharmony_ci /* Returning error causes the new ID to be destroyed */ 33062306a36Sopenharmony_ci return -ENOMEM; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic int ucma_event_handler(struct rdma_cm_id *cm_id, 33462306a36Sopenharmony_ci struct rdma_cm_event *event) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct ucma_event *uevent; 33762306a36Sopenharmony_ci struct ucma_context *ctx = cm_id->context; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) 34062306a36Sopenharmony_ci return ucma_connect_event_handler(cm_id, event); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * We ignore events for new connections until userspace has set their 34462306a36Sopenharmony_ci * context. This can only happen if an error occurs on a new connection 34562306a36Sopenharmony_ci * before the user accepts it. This is okay, since the accept will just 34662306a36Sopenharmony_ci * fail later. However, we do need to release the underlying HW 34762306a36Sopenharmony_ci * resources in case of a device removal event. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci if (ctx->uid) { 35062306a36Sopenharmony_ci uevent = ucma_create_uevent(ctx, event); 35162306a36Sopenharmony_ci if (!uevent) 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci mutex_lock(&ctx->file->mut); 35562306a36Sopenharmony_ci list_add_tail(&uevent->list, &ctx->file->event_list); 35662306a36Sopenharmony_ci mutex_unlock(&ctx->file->mut); 35762306a36Sopenharmony_ci wake_up_interruptible(&ctx->file->poll_wait); 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { 36162306a36Sopenharmony_ci xa_lock(&ctx_table); 36262306a36Sopenharmony_ci if (xa_load(&ctx_table, ctx->id) == ctx) 36362306a36Sopenharmony_ci queue_work(system_unbound_wq, &ctx->close_work); 36462306a36Sopenharmony_ci xa_unlock(&ctx_table); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf, 37062306a36Sopenharmony_ci int in_len, int out_len) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct rdma_ucm_get_event cmd; 37362306a36Sopenharmony_ci struct ucma_event *uevent; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Old 32 bit user space does not send the 4 byte padding in the 37762306a36Sopenharmony_ci * reserved field. We don't care, allow it to keep working. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci if (out_len < sizeof(uevent->resp) - sizeof(uevent->resp.reserved) - 38062306a36Sopenharmony_ci sizeof(uevent->resp.ece)) 38162306a36Sopenharmony_ci return -ENOSPC; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 38462306a36Sopenharmony_ci return -EFAULT; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci mutex_lock(&file->mut); 38762306a36Sopenharmony_ci while (list_empty(&file->event_list)) { 38862306a36Sopenharmony_ci mutex_unlock(&file->mut); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (file->filp->f_flags & O_NONBLOCK) 39162306a36Sopenharmony_ci return -EAGAIN; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (wait_event_interruptible(file->poll_wait, 39462306a36Sopenharmony_ci !list_empty(&file->event_list))) 39562306a36Sopenharmony_ci return -ERESTARTSYS; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci mutex_lock(&file->mut); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci uevent = list_first_entry(&file->event_list, struct ucma_event, list); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), 40362306a36Sopenharmony_ci &uevent->resp, 40462306a36Sopenharmony_ci min_t(size_t, out_len, sizeof(uevent->resp)))) { 40562306a36Sopenharmony_ci mutex_unlock(&file->mut); 40662306a36Sopenharmony_ci return -EFAULT; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci list_del(&uevent->list); 41062306a36Sopenharmony_ci uevent->ctx->events_reported++; 41162306a36Sopenharmony_ci if (uevent->mc) 41262306a36Sopenharmony_ci uevent->mc->events_reported++; 41362306a36Sopenharmony_ci if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) 41462306a36Sopenharmony_ci atomic_inc(&uevent->ctx->backlog); 41562306a36Sopenharmony_ci mutex_unlock(&file->mut); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci kfree(uevent); 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int ucma_get_qp_type(struct rdma_ucm_create_id *cmd, enum ib_qp_type *qp_type) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci switch (cmd->ps) { 42462306a36Sopenharmony_ci case RDMA_PS_TCP: 42562306a36Sopenharmony_ci *qp_type = IB_QPT_RC; 42662306a36Sopenharmony_ci return 0; 42762306a36Sopenharmony_ci case RDMA_PS_UDP: 42862306a36Sopenharmony_ci case RDMA_PS_IPOIB: 42962306a36Sopenharmony_ci *qp_type = IB_QPT_UD; 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci case RDMA_PS_IB: 43262306a36Sopenharmony_ci *qp_type = cmd->qp_type; 43362306a36Sopenharmony_ci return 0; 43462306a36Sopenharmony_ci default: 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf, 44062306a36Sopenharmony_ci int in_len, int out_len) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct rdma_ucm_create_id cmd; 44362306a36Sopenharmony_ci struct rdma_ucm_create_id_resp resp; 44462306a36Sopenharmony_ci struct ucma_context *ctx; 44562306a36Sopenharmony_ci struct rdma_cm_id *cm_id; 44662306a36Sopenharmony_ci enum ib_qp_type qp_type; 44762306a36Sopenharmony_ci int ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (out_len < sizeof(resp)) 45062306a36Sopenharmony_ci return -ENOSPC; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 45362306a36Sopenharmony_ci return -EFAULT; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ret = ucma_get_qp_type(&cmd, &qp_type); 45662306a36Sopenharmony_ci if (ret) 45762306a36Sopenharmony_ci return ret; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ctx = ucma_alloc_ctx(file); 46062306a36Sopenharmony_ci if (!ctx) 46162306a36Sopenharmony_ci return -ENOMEM; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ctx->uid = cmd.uid; 46462306a36Sopenharmony_ci cm_id = rdma_create_user_id(ucma_event_handler, ctx, cmd.ps, qp_type); 46562306a36Sopenharmony_ci if (IS_ERR(cm_id)) { 46662306a36Sopenharmony_ci ret = PTR_ERR(cm_id); 46762306a36Sopenharmony_ci goto err1; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci ucma_set_ctx_cm_id(ctx, cm_id); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci resp.id = ctx->id; 47262306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), 47362306a36Sopenharmony_ci &resp, sizeof(resp))) { 47462306a36Sopenharmony_ci ret = -EFAULT; 47562306a36Sopenharmony_ci goto err1; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci mutex_lock(&file->mut); 47962306a36Sopenharmony_ci ucma_finish_ctx(ctx); 48062306a36Sopenharmony_ci mutex_unlock(&file->mut); 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cierr1: 48462306a36Sopenharmony_ci ucma_destroy_private_ctx(ctx); 48562306a36Sopenharmony_ci return ret; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void ucma_cleanup_multicast(struct ucma_context *ctx) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct ucma_multicast *mc, *tmp; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci xa_lock(&multicast_table); 49362306a36Sopenharmony_ci list_for_each_entry_safe(mc, tmp, &ctx->mc_list, list) { 49462306a36Sopenharmony_ci list_del(&mc->list); 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * At this point mc->ctx->ref is 0 so the mc cannot leave the 49762306a36Sopenharmony_ci * lock on the reader and this is enough serialization 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci __xa_erase(&multicast_table, mc->id); 50062306a36Sopenharmony_ci kfree(mc); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci xa_unlock(&multicast_table); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void ucma_cleanup_mc_events(struct ucma_multicast *mc) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct ucma_event *uevent, *tmp; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci rdma_lock_handler(mc->ctx->cm_id); 51062306a36Sopenharmony_ci mutex_lock(&mc->ctx->file->mut); 51162306a36Sopenharmony_ci list_for_each_entry_safe(uevent, tmp, &mc->ctx->file->event_list, list) { 51262306a36Sopenharmony_ci if (uevent->mc != mc) 51362306a36Sopenharmony_ci continue; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci list_del(&uevent->list); 51662306a36Sopenharmony_ci kfree(uevent); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci mutex_unlock(&mc->ctx->file->mut); 51962306a36Sopenharmony_ci rdma_unlock_handler(mc->ctx->cm_id); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int ucma_cleanup_ctx_events(struct ucma_context *ctx) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci int events_reported; 52562306a36Sopenharmony_ci struct ucma_event *uevent, *tmp; 52662306a36Sopenharmony_ci LIST_HEAD(list); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* Cleanup events not yet reported to the user.*/ 52962306a36Sopenharmony_ci mutex_lock(&ctx->file->mut); 53062306a36Sopenharmony_ci list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) { 53162306a36Sopenharmony_ci if (uevent->ctx != ctx) 53262306a36Sopenharmony_ci continue; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST && 53562306a36Sopenharmony_ci xa_cmpxchg(&ctx_table, uevent->conn_req_ctx->id, 53662306a36Sopenharmony_ci uevent->conn_req_ctx, XA_ZERO_ENTRY, 53762306a36Sopenharmony_ci GFP_KERNEL) == uevent->conn_req_ctx) { 53862306a36Sopenharmony_ci list_move_tail(&uevent->list, &list); 53962306a36Sopenharmony_ci continue; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci list_del(&uevent->list); 54262306a36Sopenharmony_ci kfree(uevent); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci list_del(&ctx->list); 54562306a36Sopenharmony_ci events_reported = ctx->events_reported; 54662306a36Sopenharmony_ci mutex_unlock(&ctx->file->mut); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * If this was a listening ID then any connections spawned from it that 55062306a36Sopenharmony_ci * have not been delivered to userspace are cleaned up too. Must be done 55162306a36Sopenharmony_ci * outside any locks. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci list_for_each_entry_safe(uevent, tmp, &list, list) { 55462306a36Sopenharmony_ci ucma_destroy_private_ctx(uevent->conn_req_ctx); 55562306a36Sopenharmony_ci kfree(uevent); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci return events_reported; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/* 56162306a36Sopenharmony_ci * When this is called the xarray must have a XA_ZERO_ENTRY in the ctx->id (ie 56262306a36Sopenharmony_ci * the ctx is not public to the user). This either because: 56362306a36Sopenharmony_ci * - ucma_finish_ctx() hasn't been called 56462306a36Sopenharmony_ci * - xa_cmpxchg() succeed to remove the entry (only one thread can succeed) 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_cistatic int ucma_destroy_private_ctx(struct ucma_context *ctx) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci int events_reported; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* 57162306a36Sopenharmony_ci * Destroy the underlying cm_id. New work queuing is prevented now by 57262306a36Sopenharmony_ci * the removal from the xarray. Once the work is cancled ref will either 57362306a36Sopenharmony_ci * be 0 because the work ran to completion and consumed the ref from the 57462306a36Sopenharmony_ci * xarray, or it will be positive because we still have the ref from the 57562306a36Sopenharmony_ci * xarray. This can also be 0 in cases where cm_id was never set 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci cancel_work_sync(&ctx->close_work); 57862306a36Sopenharmony_ci if (refcount_read(&ctx->ref)) 57962306a36Sopenharmony_ci ucma_close_id(&ctx->close_work); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci events_reported = ucma_cleanup_ctx_events(ctx); 58262306a36Sopenharmony_ci ucma_cleanup_multicast(ctx); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci WARN_ON(xa_cmpxchg(&ctx_table, ctx->id, XA_ZERO_ENTRY, NULL, 58562306a36Sopenharmony_ci GFP_KERNEL) != NULL); 58662306a36Sopenharmony_ci mutex_destroy(&ctx->mutex); 58762306a36Sopenharmony_ci kfree(ctx); 58862306a36Sopenharmony_ci return events_reported; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf, 59262306a36Sopenharmony_ci int in_len, int out_len) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct rdma_ucm_destroy_id cmd; 59562306a36Sopenharmony_ci struct rdma_ucm_destroy_id_resp resp; 59662306a36Sopenharmony_ci struct ucma_context *ctx; 59762306a36Sopenharmony_ci int ret = 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (out_len < sizeof(resp)) 60062306a36Sopenharmony_ci return -ENOSPC; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 60362306a36Sopenharmony_ci return -EFAULT; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci xa_lock(&ctx_table); 60662306a36Sopenharmony_ci ctx = _ucma_find_context(cmd.id, file); 60762306a36Sopenharmony_ci if (!IS_ERR(ctx)) { 60862306a36Sopenharmony_ci if (__xa_cmpxchg(&ctx_table, ctx->id, ctx, XA_ZERO_ENTRY, 60962306a36Sopenharmony_ci GFP_KERNEL) != ctx) 61062306a36Sopenharmony_ci ctx = ERR_PTR(-ENOENT); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci xa_unlock(&ctx_table); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (IS_ERR(ctx)) 61562306a36Sopenharmony_ci return PTR_ERR(ctx); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci resp.events_reported = ucma_destroy_private_ctx(ctx); 61862306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), 61962306a36Sopenharmony_ci &resp, sizeof(resp))) 62062306a36Sopenharmony_ci ret = -EFAULT; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return ret; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic ssize_t ucma_bind_ip(struct ucma_file *file, const char __user *inbuf, 62662306a36Sopenharmony_ci int in_len, int out_len) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct rdma_ucm_bind_ip cmd; 62962306a36Sopenharmony_ci struct ucma_context *ctx; 63062306a36Sopenharmony_ci int ret; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 63362306a36Sopenharmony_ci return -EFAULT; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!rdma_addr_size_in6(&cmd.addr)) 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 63962306a36Sopenharmony_ci if (IS_ERR(ctx)) 64062306a36Sopenharmony_ci return PTR_ERR(ctx); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 64362306a36Sopenharmony_ci ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr); 64462306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ucma_put_ctx(ctx); 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic ssize_t ucma_bind(struct ucma_file *file, const char __user *inbuf, 65162306a36Sopenharmony_ci int in_len, int out_len) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct rdma_ucm_bind cmd; 65462306a36Sopenharmony_ci struct ucma_context *ctx; 65562306a36Sopenharmony_ci int ret; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 65862306a36Sopenharmony_ci return -EFAULT; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (cmd.reserved || !cmd.addr_size || 66162306a36Sopenharmony_ci cmd.addr_size != rdma_addr_size_kss(&cmd.addr)) 66262306a36Sopenharmony_ci return -EINVAL; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 66562306a36Sopenharmony_ci if (IS_ERR(ctx)) 66662306a36Sopenharmony_ci return PTR_ERR(ctx); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 66962306a36Sopenharmony_ci ret = rdma_bind_addr(ctx->cm_id, (struct sockaddr *) &cmd.addr); 67062306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 67162306a36Sopenharmony_ci ucma_put_ctx(ctx); 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic ssize_t ucma_resolve_ip(struct ucma_file *file, 67662306a36Sopenharmony_ci const char __user *inbuf, 67762306a36Sopenharmony_ci int in_len, int out_len) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct rdma_ucm_resolve_ip cmd; 68062306a36Sopenharmony_ci struct ucma_context *ctx; 68162306a36Sopenharmony_ci int ret; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 68462306a36Sopenharmony_ci return -EFAULT; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if ((cmd.src_addr.sin6_family && !rdma_addr_size_in6(&cmd.src_addr)) || 68762306a36Sopenharmony_ci !rdma_addr_size_in6(&cmd.dst_addr)) 68862306a36Sopenharmony_ci return -EINVAL; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 69162306a36Sopenharmony_ci if (IS_ERR(ctx)) 69262306a36Sopenharmony_ci return PTR_ERR(ctx); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 69562306a36Sopenharmony_ci ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, 69662306a36Sopenharmony_ci (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms); 69762306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 69862306a36Sopenharmony_ci ucma_put_ctx(ctx); 69962306a36Sopenharmony_ci return ret; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic ssize_t ucma_resolve_addr(struct ucma_file *file, 70362306a36Sopenharmony_ci const char __user *inbuf, 70462306a36Sopenharmony_ci int in_len, int out_len) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct rdma_ucm_resolve_addr cmd; 70762306a36Sopenharmony_ci struct ucma_context *ctx; 70862306a36Sopenharmony_ci int ret; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 71162306a36Sopenharmony_ci return -EFAULT; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (cmd.reserved || 71462306a36Sopenharmony_ci (cmd.src_size && (cmd.src_size != rdma_addr_size_kss(&cmd.src_addr))) || 71562306a36Sopenharmony_ci !cmd.dst_size || (cmd.dst_size != rdma_addr_size_kss(&cmd.dst_addr))) 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 71962306a36Sopenharmony_ci if (IS_ERR(ctx)) 72062306a36Sopenharmony_ci return PTR_ERR(ctx); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 72362306a36Sopenharmony_ci ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, 72462306a36Sopenharmony_ci (struct sockaddr *) &cmd.dst_addr, cmd.timeout_ms); 72562306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 72662306a36Sopenharmony_ci ucma_put_ctx(ctx); 72762306a36Sopenharmony_ci return ret; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic ssize_t ucma_resolve_route(struct ucma_file *file, 73162306a36Sopenharmony_ci const char __user *inbuf, 73262306a36Sopenharmony_ci int in_len, int out_len) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct rdma_ucm_resolve_route cmd; 73562306a36Sopenharmony_ci struct ucma_context *ctx; 73662306a36Sopenharmony_ci int ret; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 73962306a36Sopenharmony_ci return -EFAULT; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd.id); 74262306a36Sopenharmony_ci if (IS_ERR(ctx)) 74362306a36Sopenharmony_ci return PTR_ERR(ctx); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 74662306a36Sopenharmony_ci ret = rdma_resolve_route(ctx->cm_id, cmd.timeout_ms); 74762306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 74862306a36Sopenharmony_ci ucma_put_ctx(ctx); 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void ucma_copy_ib_route(struct rdma_ucm_query_route_resp *resp, 75362306a36Sopenharmony_ci struct rdma_route *route) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct rdma_dev_addr *dev_addr; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci resp->num_paths = route->num_pri_alt_paths; 75862306a36Sopenharmony_ci switch (route->num_pri_alt_paths) { 75962306a36Sopenharmony_ci case 0: 76062306a36Sopenharmony_ci dev_addr = &route->addr.dev_addr; 76162306a36Sopenharmony_ci rdma_addr_get_dgid(dev_addr, 76262306a36Sopenharmony_ci (union ib_gid *) &resp->ib_route[0].dgid); 76362306a36Sopenharmony_ci rdma_addr_get_sgid(dev_addr, 76462306a36Sopenharmony_ci (union ib_gid *) &resp->ib_route[0].sgid); 76562306a36Sopenharmony_ci resp->ib_route[0].pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr)); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case 2: 76862306a36Sopenharmony_ci ib_copy_path_rec_to_user(&resp->ib_route[1], 76962306a36Sopenharmony_ci &route->path_rec[1]); 77062306a36Sopenharmony_ci fallthrough; 77162306a36Sopenharmony_ci case 1: 77262306a36Sopenharmony_ci ib_copy_path_rec_to_user(&resp->ib_route[0], 77362306a36Sopenharmony_ci &route->path_rec[0]); 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci default: 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic void ucma_copy_iboe_route(struct rdma_ucm_query_route_resp *resp, 78162306a36Sopenharmony_ci struct rdma_route *route) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci resp->num_paths = route->num_pri_alt_paths; 78562306a36Sopenharmony_ci switch (route->num_pri_alt_paths) { 78662306a36Sopenharmony_ci case 0: 78762306a36Sopenharmony_ci rdma_ip2gid((struct sockaddr *)&route->addr.dst_addr, 78862306a36Sopenharmony_ci (union ib_gid *)&resp->ib_route[0].dgid); 78962306a36Sopenharmony_ci rdma_ip2gid((struct sockaddr *)&route->addr.src_addr, 79062306a36Sopenharmony_ci (union ib_gid *)&resp->ib_route[0].sgid); 79162306a36Sopenharmony_ci resp->ib_route[0].pkey = cpu_to_be16(0xffff); 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci case 2: 79462306a36Sopenharmony_ci ib_copy_path_rec_to_user(&resp->ib_route[1], 79562306a36Sopenharmony_ci &route->path_rec[1]); 79662306a36Sopenharmony_ci fallthrough; 79762306a36Sopenharmony_ci case 1: 79862306a36Sopenharmony_ci ib_copy_path_rec_to_user(&resp->ib_route[0], 79962306a36Sopenharmony_ci &route->path_rec[0]); 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci default: 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic void ucma_copy_iw_route(struct rdma_ucm_query_route_resp *resp, 80762306a36Sopenharmony_ci struct rdma_route *route) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct rdma_dev_addr *dev_addr; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci dev_addr = &route->addr.dev_addr; 81262306a36Sopenharmony_ci rdma_addr_get_dgid(dev_addr, (union ib_gid *) &resp->ib_route[0].dgid); 81362306a36Sopenharmony_ci rdma_addr_get_sgid(dev_addr, (union ib_gid *) &resp->ib_route[0].sgid); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic ssize_t ucma_query_route(struct ucma_file *file, 81762306a36Sopenharmony_ci const char __user *inbuf, 81862306a36Sopenharmony_ci int in_len, int out_len) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct rdma_ucm_query cmd; 82162306a36Sopenharmony_ci struct rdma_ucm_query_route_resp resp; 82262306a36Sopenharmony_ci struct ucma_context *ctx; 82362306a36Sopenharmony_ci struct sockaddr *addr; 82462306a36Sopenharmony_ci int ret = 0; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (out_len < offsetof(struct rdma_ucm_query_route_resp, ibdev_index)) 82762306a36Sopenharmony_ci return -ENOSPC; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 83062306a36Sopenharmony_ci return -EFAULT; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 83362306a36Sopenharmony_ci if (IS_ERR(ctx)) 83462306a36Sopenharmony_ci return PTR_ERR(ctx); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 83762306a36Sopenharmony_ci memset(&resp, 0, sizeof resp); 83862306a36Sopenharmony_ci addr = (struct sockaddr *) &ctx->cm_id->route.addr.src_addr; 83962306a36Sopenharmony_ci memcpy(&resp.src_addr, addr, addr->sa_family == AF_INET ? 84062306a36Sopenharmony_ci sizeof(struct sockaddr_in) : 84162306a36Sopenharmony_ci sizeof(struct sockaddr_in6)); 84262306a36Sopenharmony_ci addr = (struct sockaddr *) &ctx->cm_id->route.addr.dst_addr; 84362306a36Sopenharmony_ci memcpy(&resp.dst_addr, addr, addr->sa_family == AF_INET ? 84462306a36Sopenharmony_ci sizeof(struct sockaddr_in) : 84562306a36Sopenharmony_ci sizeof(struct sockaddr_in6)); 84662306a36Sopenharmony_ci if (!ctx->cm_id->device) 84762306a36Sopenharmony_ci goto out; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid; 85062306a36Sopenharmony_ci resp.ibdev_index = ctx->cm_id->device->index; 85162306a36Sopenharmony_ci resp.port_num = ctx->cm_id->port_num; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (rdma_cap_ib_sa(ctx->cm_id->device, ctx->cm_id->port_num)) 85462306a36Sopenharmony_ci ucma_copy_ib_route(&resp, &ctx->cm_id->route); 85562306a36Sopenharmony_ci else if (rdma_protocol_roce(ctx->cm_id->device, ctx->cm_id->port_num)) 85662306a36Sopenharmony_ci ucma_copy_iboe_route(&resp, &ctx->cm_id->route); 85762306a36Sopenharmony_ci else if (rdma_protocol_iwarp(ctx->cm_id->device, ctx->cm_id->port_num)) 85862306a36Sopenharmony_ci ucma_copy_iw_route(&resp, &ctx->cm_id->route); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ciout: 86162306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 86262306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), &resp, 86362306a36Sopenharmony_ci min_t(size_t, out_len, sizeof(resp)))) 86462306a36Sopenharmony_ci ret = -EFAULT; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci ucma_put_ctx(ctx); 86762306a36Sopenharmony_ci return ret; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic void ucma_query_device_addr(struct rdma_cm_id *cm_id, 87162306a36Sopenharmony_ci struct rdma_ucm_query_addr_resp *resp) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci if (!cm_id->device) 87462306a36Sopenharmony_ci return; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci resp->node_guid = (__force __u64) cm_id->device->node_guid; 87762306a36Sopenharmony_ci resp->ibdev_index = cm_id->device->index; 87862306a36Sopenharmony_ci resp->port_num = cm_id->port_num; 87962306a36Sopenharmony_ci resp->pkey = (__force __u16) cpu_to_be16( 88062306a36Sopenharmony_ci ib_addr_get_pkey(&cm_id->route.addr.dev_addr)); 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic ssize_t ucma_query_addr(struct ucma_context *ctx, 88462306a36Sopenharmony_ci void __user *response, int out_len) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct rdma_ucm_query_addr_resp resp; 88762306a36Sopenharmony_ci struct sockaddr *addr; 88862306a36Sopenharmony_ci int ret = 0; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (out_len < offsetof(struct rdma_ucm_query_addr_resp, ibdev_index)) 89162306a36Sopenharmony_ci return -ENOSPC; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci memset(&resp, 0, sizeof resp); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci addr = (struct sockaddr *) &ctx->cm_id->route.addr.src_addr; 89662306a36Sopenharmony_ci resp.src_size = rdma_addr_size(addr); 89762306a36Sopenharmony_ci memcpy(&resp.src_addr, addr, resp.src_size); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci addr = (struct sockaddr *) &ctx->cm_id->route.addr.dst_addr; 90062306a36Sopenharmony_ci resp.dst_size = rdma_addr_size(addr); 90162306a36Sopenharmony_ci memcpy(&resp.dst_addr, addr, resp.dst_size); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ucma_query_device_addr(ctx->cm_id, &resp); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (copy_to_user(response, &resp, min_t(size_t, out_len, sizeof(resp)))) 90662306a36Sopenharmony_ci ret = -EFAULT; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci return ret; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic ssize_t ucma_query_path(struct ucma_context *ctx, 91262306a36Sopenharmony_ci void __user *response, int out_len) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct rdma_ucm_query_path_resp *resp; 91562306a36Sopenharmony_ci int i, ret = 0; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (out_len < sizeof(*resp)) 91862306a36Sopenharmony_ci return -ENOSPC; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci resp = kzalloc(out_len, GFP_KERNEL); 92162306a36Sopenharmony_ci if (!resp) 92262306a36Sopenharmony_ci return -ENOMEM; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci resp->num_paths = ctx->cm_id->route.num_pri_alt_paths; 92562306a36Sopenharmony_ci for (i = 0, out_len -= sizeof(*resp); 92662306a36Sopenharmony_ci i < resp->num_paths && out_len > sizeof(struct ib_path_rec_data); 92762306a36Sopenharmony_ci i++, out_len -= sizeof(struct ib_path_rec_data)) { 92862306a36Sopenharmony_ci struct sa_path_rec *rec = &ctx->cm_id->route.path_rec[i]; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci resp->path_data[i].flags = IB_PATH_GMP | IB_PATH_PRIMARY | 93162306a36Sopenharmony_ci IB_PATH_BIDIRECTIONAL; 93262306a36Sopenharmony_ci if (rec->rec_type == SA_PATH_REC_TYPE_OPA) { 93362306a36Sopenharmony_ci struct sa_path_rec ib; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci sa_convert_path_opa_to_ib(&ib, rec); 93662306a36Sopenharmony_ci ib_sa_pack_path(&ib, &resp->path_data[i].path_rec); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci } else { 93962306a36Sopenharmony_ci ib_sa_pack_path(rec, &resp->path_data[i].path_rec); 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (copy_to_user(response, resp, struct_size(resp, path_data, i))) 94462306a36Sopenharmony_ci ret = -EFAULT; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci kfree(resp); 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic ssize_t ucma_query_gid(struct ucma_context *ctx, 95162306a36Sopenharmony_ci void __user *response, int out_len) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct rdma_ucm_query_addr_resp resp; 95462306a36Sopenharmony_ci struct sockaddr_ib *addr; 95562306a36Sopenharmony_ci int ret = 0; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (out_len < offsetof(struct rdma_ucm_query_addr_resp, ibdev_index)) 95862306a36Sopenharmony_ci return -ENOSPC; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci memset(&resp, 0, sizeof resp); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ucma_query_device_addr(ctx->cm_id, &resp); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci addr = (struct sockaddr_ib *) &resp.src_addr; 96562306a36Sopenharmony_ci resp.src_size = sizeof(*addr); 96662306a36Sopenharmony_ci if (ctx->cm_id->route.addr.src_addr.ss_family == AF_IB) { 96762306a36Sopenharmony_ci memcpy(addr, &ctx->cm_id->route.addr.src_addr, resp.src_size); 96862306a36Sopenharmony_ci } else { 96962306a36Sopenharmony_ci addr->sib_family = AF_IB; 97062306a36Sopenharmony_ci addr->sib_pkey = (__force __be16) resp.pkey; 97162306a36Sopenharmony_ci rdma_read_gids(ctx->cm_id, (union ib_gid *)&addr->sib_addr, 97262306a36Sopenharmony_ci NULL); 97362306a36Sopenharmony_ci addr->sib_sid = rdma_get_service_id(ctx->cm_id, (struct sockaddr *) 97462306a36Sopenharmony_ci &ctx->cm_id->route.addr.src_addr); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci addr = (struct sockaddr_ib *) &resp.dst_addr; 97862306a36Sopenharmony_ci resp.dst_size = sizeof(*addr); 97962306a36Sopenharmony_ci if (ctx->cm_id->route.addr.dst_addr.ss_family == AF_IB) { 98062306a36Sopenharmony_ci memcpy(addr, &ctx->cm_id->route.addr.dst_addr, resp.dst_size); 98162306a36Sopenharmony_ci } else { 98262306a36Sopenharmony_ci addr->sib_family = AF_IB; 98362306a36Sopenharmony_ci addr->sib_pkey = (__force __be16) resp.pkey; 98462306a36Sopenharmony_ci rdma_read_gids(ctx->cm_id, NULL, 98562306a36Sopenharmony_ci (union ib_gid *)&addr->sib_addr); 98662306a36Sopenharmony_ci addr->sib_sid = rdma_get_service_id(ctx->cm_id, (struct sockaddr *) 98762306a36Sopenharmony_ci &ctx->cm_id->route.addr.dst_addr); 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (copy_to_user(response, &resp, min_t(size_t, out_len, sizeof(resp)))) 99162306a36Sopenharmony_ci ret = -EFAULT; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci return ret; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic ssize_t ucma_query(struct ucma_file *file, 99762306a36Sopenharmony_ci const char __user *inbuf, 99862306a36Sopenharmony_ci int in_len, int out_len) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct rdma_ucm_query cmd; 100162306a36Sopenharmony_ci struct ucma_context *ctx; 100262306a36Sopenharmony_ci void __user *response; 100362306a36Sopenharmony_ci int ret; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 100662306a36Sopenharmony_ci return -EFAULT; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci response = u64_to_user_ptr(cmd.response); 100962306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 101062306a36Sopenharmony_ci if (IS_ERR(ctx)) 101162306a36Sopenharmony_ci return PTR_ERR(ctx); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 101462306a36Sopenharmony_ci switch (cmd.option) { 101562306a36Sopenharmony_ci case RDMA_USER_CM_QUERY_ADDR: 101662306a36Sopenharmony_ci ret = ucma_query_addr(ctx, response, out_len); 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case RDMA_USER_CM_QUERY_PATH: 101962306a36Sopenharmony_ci ret = ucma_query_path(ctx, response, out_len); 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case RDMA_USER_CM_QUERY_GID: 102262306a36Sopenharmony_ci ret = ucma_query_gid(ctx, response, out_len); 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci default: 102562306a36Sopenharmony_ci ret = -ENOSYS; 102662306a36Sopenharmony_ci break; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci ucma_put_ctx(ctx); 103162306a36Sopenharmony_ci return ret; 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic void ucma_copy_conn_param(struct rdma_cm_id *id, 103562306a36Sopenharmony_ci struct rdma_conn_param *dst, 103662306a36Sopenharmony_ci struct rdma_ucm_conn_param *src) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci dst->private_data = src->private_data; 103962306a36Sopenharmony_ci dst->private_data_len = src->private_data_len; 104062306a36Sopenharmony_ci dst->responder_resources = src->responder_resources; 104162306a36Sopenharmony_ci dst->initiator_depth = src->initiator_depth; 104262306a36Sopenharmony_ci dst->flow_control = src->flow_control; 104362306a36Sopenharmony_ci dst->retry_count = src->retry_count; 104462306a36Sopenharmony_ci dst->rnr_retry_count = src->rnr_retry_count; 104562306a36Sopenharmony_ci dst->srq = src->srq; 104662306a36Sopenharmony_ci dst->qp_num = src->qp_num & 0xFFFFFF; 104762306a36Sopenharmony_ci dst->qkey = (id->route.addr.src_addr.ss_family == AF_IB) ? src->qkey : 0; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic ssize_t ucma_connect(struct ucma_file *file, const char __user *inbuf, 105162306a36Sopenharmony_ci int in_len, int out_len) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct rdma_conn_param conn_param; 105462306a36Sopenharmony_ci struct rdma_ucm_ece ece = {}; 105562306a36Sopenharmony_ci struct rdma_ucm_connect cmd; 105662306a36Sopenharmony_ci struct ucma_context *ctx; 105762306a36Sopenharmony_ci size_t in_size; 105862306a36Sopenharmony_ci int ret; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (in_len < offsetofend(typeof(cmd), reserved)) 106162306a36Sopenharmony_ci return -EINVAL; 106262306a36Sopenharmony_ci in_size = min_t(size_t, in_len, sizeof(cmd)); 106362306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, in_size)) 106462306a36Sopenharmony_ci return -EFAULT; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (!cmd.conn_param.valid) 106762306a36Sopenharmony_ci return -EINVAL; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd.id); 107062306a36Sopenharmony_ci if (IS_ERR(ctx)) 107162306a36Sopenharmony_ci return PTR_ERR(ctx); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci ucma_copy_conn_param(ctx->cm_id, &conn_param, &cmd.conn_param); 107462306a36Sopenharmony_ci if (offsetofend(typeof(cmd), ece) <= in_size) { 107562306a36Sopenharmony_ci ece.vendor_id = cmd.ece.vendor_id; 107662306a36Sopenharmony_ci ece.attr_mod = cmd.ece.attr_mod; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 108062306a36Sopenharmony_ci ret = rdma_connect_ece(ctx->cm_id, &conn_param, &ece); 108162306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 108262306a36Sopenharmony_ci ucma_put_ctx(ctx); 108362306a36Sopenharmony_ci return ret; 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic ssize_t ucma_listen(struct ucma_file *file, const char __user *inbuf, 108762306a36Sopenharmony_ci int in_len, int out_len) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct rdma_ucm_listen cmd; 109062306a36Sopenharmony_ci struct ucma_context *ctx; 109162306a36Sopenharmony_ci int ret; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 109462306a36Sopenharmony_ci return -EFAULT; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 109762306a36Sopenharmony_ci if (IS_ERR(ctx)) 109862306a36Sopenharmony_ci return PTR_ERR(ctx); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (cmd.backlog <= 0 || cmd.backlog > max_backlog) 110162306a36Sopenharmony_ci cmd.backlog = max_backlog; 110262306a36Sopenharmony_ci atomic_set(&ctx->backlog, cmd.backlog); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 110562306a36Sopenharmony_ci ret = rdma_listen(ctx->cm_id, cmd.backlog); 110662306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 110762306a36Sopenharmony_ci ucma_put_ctx(ctx); 110862306a36Sopenharmony_ci return ret; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic ssize_t ucma_accept(struct ucma_file *file, const char __user *inbuf, 111262306a36Sopenharmony_ci int in_len, int out_len) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci struct rdma_ucm_accept cmd; 111562306a36Sopenharmony_ci struct rdma_conn_param conn_param; 111662306a36Sopenharmony_ci struct rdma_ucm_ece ece = {}; 111762306a36Sopenharmony_ci struct ucma_context *ctx; 111862306a36Sopenharmony_ci size_t in_size; 111962306a36Sopenharmony_ci int ret; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (in_len < offsetofend(typeof(cmd), reserved)) 112262306a36Sopenharmony_ci return -EINVAL; 112362306a36Sopenharmony_ci in_size = min_t(size_t, in_len, sizeof(cmd)); 112462306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, in_size)) 112562306a36Sopenharmony_ci return -EFAULT; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd.id); 112862306a36Sopenharmony_ci if (IS_ERR(ctx)) 112962306a36Sopenharmony_ci return PTR_ERR(ctx); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (offsetofend(typeof(cmd), ece) <= in_size) { 113262306a36Sopenharmony_ci ece.vendor_id = cmd.ece.vendor_id; 113362306a36Sopenharmony_ci ece.attr_mod = cmd.ece.attr_mod; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (cmd.conn_param.valid) { 113762306a36Sopenharmony_ci ucma_copy_conn_param(ctx->cm_id, &conn_param, &cmd.conn_param); 113862306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 113962306a36Sopenharmony_ci rdma_lock_handler(ctx->cm_id); 114062306a36Sopenharmony_ci ret = rdma_accept_ece(ctx->cm_id, &conn_param, &ece); 114162306a36Sopenharmony_ci if (!ret) { 114262306a36Sopenharmony_ci /* The uid must be set atomically with the handler */ 114362306a36Sopenharmony_ci ctx->uid = cmd.uid; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci rdma_unlock_handler(ctx->cm_id); 114662306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 114762306a36Sopenharmony_ci } else { 114862306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 114962306a36Sopenharmony_ci rdma_lock_handler(ctx->cm_id); 115062306a36Sopenharmony_ci ret = rdma_accept_ece(ctx->cm_id, NULL, &ece); 115162306a36Sopenharmony_ci rdma_unlock_handler(ctx->cm_id); 115262306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci ucma_put_ctx(ctx); 115562306a36Sopenharmony_ci return ret; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic ssize_t ucma_reject(struct ucma_file *file, const char __user *inbuf, 115962306a36Sopenharmony_ci int in_len, int out_len) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct rdma_ucm_reject cmd; 116262306a36Sopenharmony_ci struct ucma_context *ctx; 116362306a36Sopenharmony_ci int ret; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 116662306a36Sopenharmony_ci return -EFAULT; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (!cmd.reason) 116962306a36Sopenharmony_ci cmd.reason = IB_CM_REJ_CONSUMER_DEFINED; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci switch (cmd.reason) { 117262306a36Sopenharmony_ci case IB_CM_REJ_CONSUMER_DEFINED: 117362306a36Sopenharmony_ci case IB_CM_REJ_VENDOR_OPTION_NOT_SUPPORTED: 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci default: 117662306a36Sopenharmony_ci return -EINVAL; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd.id); 118062306a36Sopenharmony_ci if (IS_ERR(ctx)) 118162306a36Sopenharmony_ci return PTR_ERR(ctx); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 118462306a36Sopenharmony_ci ret = rdma_reject(ctx->cm_id, cmd.private_data, cmd.private_data_len, 118562306a36Sopenharmony_ci cmd.reason); 118662306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 118762306a36Sopenharmony_ci ucma_put_ctx(ctx); 118862306a36Sopenharmony_ci return ret; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic ssize_t ucma_disconnect(struct ucma_file *file, const char __user *inbuf, 119262306a36Sopenharmony_ci int in_len, int out_len) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci struct rdma_ucm_disconnect cmd; 119562306a36Sopenharmony_ci struct ucma_context *ctx; 119662306a36Sopenharmony_ci int ret; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 119962306a36Sopenharmony_ci return -EFAULT; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd.id); 120262306a36Sopenharmony_ci if (IS_ERR(ctx)) 120362306a36Sopenharmony_ci return PTR_ERR(ctx); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 120662306a36Sopenharmony_ci ret = rdma_disconnect(ctx->cm_id); 120762306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 120862306a36Sopenharmony_ci ucma_put_ctx(ctx); 120962306a36Sopenharmony_ci return ret; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic ssize_t ucma_init_qp_attr(struct ucma_file *file, 121362306a36Sopenharmony_ci const char __user *inbuf, 121462306a36Sopenharmony_ci int in_len, int out_len) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct rdma_ucm_init_qp_attr cmd; 121762306a36Sopenharmony_ci struct ib_uverbs_qp_attr resp; 121862306a36Sopenharmony_ci struct ucma_context *ctx; 121962306a36Sopenharmony_ci struct ib_qp_attr qp_attr; 122062306a36Sopenharmony_ci int ret; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (out_len < sizeof(resp)) 122362306a36Sopenharmony_ci return -ENOSPC; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 122662306a36Sopenharmony_ci return -EFAULT; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (cmd.qp_state > IB_QPS_ERR) 122962306a36Sopenharmony_ci return -EINVAL; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd.id); 123262306a36Sopenharmony_ci if (IS_ERR(ctx)) 123362306a36Sopenharmony_ci return PTR_ERR(ctx); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci resp.qp_attr_mask = 0; 123662306a36Sopenharmony_ci memset(&qp_attr, 0, sizeof qp_attr); 123762306a36Sopenharmony_ci qp_attr.qp_state = cmd.qp_state; 123862306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 123962306a36Sopenharmony_ci ret = rdma_init_qp_attr(ctx->cm_id, &qp_attr, &resp.qp_attr_mask); 124062306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 124162306a36Sopenharmony_ci if (ret) 124262306a36Sopenharmony_ci goto out; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci ib_copy_qp_attr_to_user(ctx->cm_id->device, &resp, &qp_attr); 124562306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), 124662306a36Sopenharmony_ci &resp, sizeof(resp))) 124762306a36Sopenharmony_ci ret = -EFAULT; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ciout: 125062306a36Sopenharmony_ci ucma_put_ctx(ctx); 125162306a36Sopenharmony_ci return ret; 125262306a36Sopenharmony_ci} 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cistatic int ucma_set_option_id(struct ucma_context *ctx, int optname, 125562306a36Sopenharmony_ci void *optval, size_t optlen) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci int ret = 0; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci switch (optname) { 126062306a36Sopenharmony_ci case RDMA_OPTION_ID_TOS: 126162306a36Sopenharmony_ci if (optlen != sizeof(u8)) { 126262306a36Sopenharmony_ci ret = -EINVAL; 126362306a36Sopenharmony_ci break; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci rdma_set_service_type(ctx->cm_id, *((u8 *) optval)); 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci case RDMA_OPTION_ID_REUSEADDR: 126862306a36Sopenharmony_ci if (optlen != sizeof(int)) { 126962306a36Sopenharmony_ci ret = -EINVAL; 127062306a36Sopenharmony_ci break; 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci ret = rdma_set_reuseaddr(ctx->cm_id, *((int *) optval) ? 1 : 0); 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci case RDMA_OPTION_ID_AFONLY: 127562306a36Sopenharmony_ci if (optlen != sizeof(int)) { 127662306a36Sopenharmony_ci ret = -EINVAL; 127762306a36Sopenharmony_ci break; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci ret = rdma_set_afonly(ctx->cm_id, *((int *) optval) ? 1 : 0); 128062306a36Sopenharmony_ci break; 128162306a36Sopenharmony_ci case RDMA_OPTION_ID_ACK_TIMEOUT: 128262306a36Sopenharmony_ci if (optlen != sizeof(u8)) { 128362306a36Sopenharmony_ci ret = -EINVAL; 128462306a36Sopenharmony_ci break; 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci ret = rdma_set_ack_timeout(ctx->cm_id, *((u8 *)optval)); 128762306a36Sopenharmony_ci break; 128862306a36Sopenharmony_ci default: 128962306a36Sopenharmony_ci ret = -ENOSYS; 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci return ret; 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int ucma_set_ib_path(struct ucma_context *ctx, 129662306a36Sopenharmony_ci struct ib_path_rec_data *path_data, size_t optlen) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct sa_path_rec sa_path; 129962306a36Sopenharmony_ci struct rdma_cm_event event; 130062306a36Sopenharmony_ci int ret; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (optlen % sizeof(*path_data)) 130362306a36Sopenharmony_ci return -EINVAL; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci for (; optlen; optlen -= sizeof(*path_data), path_data++) { 130662306a36Sopenharmony_ci if (path_data->flags == (IB_PATH_GMP | IB_PATH_PRIMARY | 130762306a36Sopenharmony_ci IB_PATH_BIDIRECTIONAL)) 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (!optlen) 131262306a36Sopenharmony_ci return -EINVAL; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (!ctx->cm_id->device) 131562306a36Sopenharmony_ci return -EINVAL; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci memset(&sa_path, 0, sizeof(sa_path)); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci sa_path.rec_type = SA_PATH_REC_TYPE_IB; 132062306a36Sopenharmony_ci ib_sa_unpack_path(path_data->path_rec, &sa_path); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (rdma_cap_opa_ah(ctx->cm_id->device, ctx->cm_id->port_num)) { 132362306a36Sopenharmony_ci struct sa_path_rec opa; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci sa_convert_path_ib_to_opa(&opa, &sa_path); 132662306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 132762306a36Sopenharmony_ci ret = rdma_set_ib_path(ctx->cm_id, &opa); 132862306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 132962306a36Sopenharmony_ci } else { 133062306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 133162306a36Sopenharmony_ci ret = rdma_set_ib_path(ctx->cm_id, &sa_path); 133262306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci if (ret) 133562306a36Sopenharmony_ci return ret; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci memset(&event, 0, sizeof event); 133862306a36Sopenharmony_ci event.event = RDMA_CM_EVENT_ROUTE_RESOLVED; 133962306a36Sopenharmony_ci return ucma_event_handler(ctx->cm_id, &event); 134062306a36Sopenharmony_ci} 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cistatic int ucma_set_option_ib(struct ucma_context *ctx, int optname, 134362306a36Sopenharmony_ci void *optval, size_t optlen) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci int ret; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci switch (optname) { 134862306a36Sopenharmony_ci case RDMA_OPTION_IB_PATH: 134962306a36Sopenharmony_ci ret = ucma_set_ib_path(ctx, optval, optlen); 135062306a36Sopenharmony_ci break; 135162306a36Sopenharmony_ci default: 135262306a36Sopenharmony_ci ret = -ENOSYS; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return ret; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic int ucma_set_option_level(struct ucma_context *ctx, int level, 135962306a36Sopenharmony_ci int optname, void *optval, size_t optlen) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci int ret; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci switch (level) { 136462306a36Sopenharmony_ci case RDMA_OPTION_ID: 136562306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 136662306a36Sopenharmony_ci ret = ucma_set_option_id(ctx, optname, optval, optlen); 136762306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci case RDMA_OPTION_IB: 137062306a36Sopenharmony_ci ret = ucma_set_option_ib(ctx, optname, optval, optlen); 137162306a36Sopenharmony_ci break; 137262306a36Sopenharmony_ci default: 137362306a36Sopenharmony_ci ret = -ENOSYS; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci return ret; 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf, 138062306a36Sopenharmony_ci int in_len, int out_len) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci struct rdma_ucm_set_option cmd; 138362306a36Sopenharmony_ci struct ucma_context *ctx; 138462306a36Sopenharmony_ci void *optval; 138562306a36Sopenharmony_ci int ret; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 138862306a36Sopenharmony_ci return -EFAULT; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci if (unlikely(cmd.optlen > KMALLOC_MAX_SIZE)) 139162306a36Sopenharmony_ci return -EINVAL; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 139462306a36Sopenharmony_ci if (IS_ERR(ctx)) 139562306a36Sopenharmony_ci return PTR_ERR(ctx); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci optval = memdup_user(u64_to_user_ptr(cmd.optval), 139862306a36Sopenharmony_ci cmd.optlen); 139962306a36Sopenharmony_ci if (IS_ERR(optval)) { 140062306a36Sopenharmony_ci ret = PTR_ERR(optval); 140162306a36Sopenharmony_ci goto out; 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci ret = ucma_set_option_level(ctx, cmd.level, cmd.optname, optval, 140562306a36Sopenharmony_ci cmd.optlen); 140662306a36Sopenharmony_ci kfree(optval); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ciout: 140962306a36Sopenharmony_ci ucma_put_ctx(ctx); 141062306a36Sopenharmony_ci return ret; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, 141462306a36Sopenharmony_ci int in_len, int out_len) 141562306a36Sopenharmony_ci{ 141662306a36Sopenharmony_ci struct rdma_ucm_notify cmd; 141762306a36Sopenharmony_ci struct ucma_context *ctx; 141862306a36Sopenharmony_ci int ret = -EINVAL; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 142162306a36Sopenharmony_ci return -EFAULT; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci ctx = ucma_get_ctx(file, cmd.id); 142462306a36Sopenharmony_ci if (IS_ERR(ctx)) 142562306a36Sopenharmony_ci return PTR_ERR(ctx); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 142862306a36Sopenharmony_ci if (ctx->cm_id->device) 142962306a36Sopenharmony_ci ret = rdma_notify(ctx->cm_id, (enum ib_event_type)cmd.event); 143062306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci ucma_put_ctx(ctx); 143362306a36Sopenharmony_ci return ret; 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic ssize_t ucma_process_join(struct ucma_file *file, 143762306a36Sopenharmony_ci struct rdma_ucm_join_mcast *cmd, int out_len) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct rdma_ucm_create_id_resp resp; 144062306a36Sopenharmony_ci struct ucma_context *ctx; 144162306a36Sopenharmony_ci struct ucma_multicast *mc; 144262306a36Sopenharmony_ci struct sockaddr *addr; 144362306a36Sopenharmony_ci int ret; 144462306a36Sopenharmony_ci u8 join_state; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci if (out_len < sizeof(resp)) 144762306a36Sopenharmony_ci return -ENOSPC; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci addr = (struct sockaddr *) &cmd->addr; 145062306a36Sopenharmony_ci if (cmd->addr_size != rdma_addr_size(addr)) 145162306a36Sopenharmony_ci return -EINVAL; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (cmd->join_flags == RDMA_MC_JOIN_FLAG_FULLMEMBER) 145462306a36Sopenharmony_ci join_state = BIT(FULLMEMBER_JOIN); 145562306a36Sopenharmony_ci else if (cmd->join_flags == RDMA_MC_JOIN_FLAG_SENDONLY_FULLMEMBER) 145662306a36Sopenharmony_ci join_state = BIT(SENDONLY_FULLMEMBER_JOIN); 145762306a36Sopenharmony_ci else 145862306a36Sopenharmony_ci return -EINVAL; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci ctx = ucma_get_ctx_dev(file, cmd->id); 146162306a36Sopenharmony_ci if (IS_ERR(ctx)) 146262306a36Sopenharmony_ci return PTR_ERR(ctx); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci mc = kzalloc(sizeof(*mc), GFP_KERNEL); 146562306a36Sopenharmony_ci if (!mc) { 146662306a36Sopenharmony_ci ret = -ENOMEM; 146762306a36Sopenharmony_ci goto err_put_ctx; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci mc->ctx = ctx; 147162306a36Sopenharmony_ci mc->join_state = join_state; 147262306a36Sopenharmony_ci mc->uid = cmd->uid; 147362306a36Sopenharmony_ci memcpy(&mc->addr, addr, cmd->addr_size); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci xa_lock(&multicast_table); 147662306a36Sopenharmony_ci if (__xa_alloc(&multicast_table, &mc->id, NULL, xa_limit_32b, 147762306a36Sopenharmony_ci GFP_KERNEL)) { 147862306a36Sopenharmony_ci ret = -ENOMEM; 147962306a36Sopenharmony_ci goto err_free_mc; 148062306a36Sopenharmony_ci } 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci list_add_tail(&mc->list, &ctx->mc_list); 148362306a36Sopenharmony_ci xa_unlock(&multicast_table); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 148662306a36Sopenharmony_ci ret = rdma_join_multicast(ctx->cm_id, (struct sockaddr *)&mc->addr, 148762306a36Sopenharmony_ci join_state, mc); 148862306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 148962306a36Sopenharmony_ci if (ret) 149062306a36Sopenharmony_ci goto err_xa_erase; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci resp.id = mc->id; 149362306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd->response), 149462306a36Sopenharmony_ci &resp, sizeof(resp))) { 149562306a36Sopenharmony_ci ret = -EFAULT; 149662306a36Sopenharmony_ci goto err_leave_multicast; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci xa_store(&multicast_table, mc->id, mc, 0); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci ucma_put_ctx(ctx); 150262306a36Sopenharmony_ci return 0; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cierr_leave_multicast: 150562306a36Sopenharmony_ci mutex_lock(&ctx->mutex); 150662306a36Sopenharmony_ci rdma_leave_multicast(ctx->cm_id, (struct sockaddr *) &mc->addr); 150762306a36Sopenharmony_ci mutex_unlock(&ctx->mutex); 150862306a36Sopenharmony_ci ucma_cleanup_mc_events(mc); 150962306a36Sopenharmony_cierr_xa_erase: 151062306a36Sopenharmony_ci xa_lock(&multicast_table); 151162306a36Sopenharmony_ci list_del(&mc->list); 151262306a36Sopenharmony_ci __xa_erase(&multicast_table, mc->id); 151362306a36Sopenharmony_cierr_free_mc: 151462306a36Sopenharmony_ci xa_unlock(&multicast_table); 151562306a36Sopenharmony_ci kfree(mc); 151662306a36Sopenharmony_cierr_put_ctx: 151762306a36Sopenharmony_ci ucma_put_ctx(ctx); 151862306a36Sopenharmony_ci return ret; 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_cistatic ssize_t ucma_join_ip_multicast(struct ucma_file *file, 152262306a36Sopenharmony_ci const char __user *inbuf, 152362306a36Sopenharmony_ci int in_len, int out_len) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci struct rdma_ucm_join_ip_mcast cmd; 152662306a36Sopenharmony_ci struct rdma_ucm_join_mcast join_cmd; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 152962306a36Sopenharmony_ci return -EFAULT; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci join_cmd.response = cmd.response; 153262306a36Sopenharmony_ci join_cmd.uid = cmd.uid; 153362306a36Sopenharmony_ci join_cmd.id = cmd.id; 153462306a36Sopenharmony_ci join_cmd.addr_size = rdma_addr_size_in6(&cmd.addr); 153562306a36Sopenharmony_ci if (!join_cmd.addr_size) 153662306a36Sopenharmony_ci return -EINVAL; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci join_cmd.join_flags = RDMA_MC_JOIN_FLAG_FULLMEMBER; 153962306a36Sopenharmony_ci memcpy(&join_cmd.addr, &cmd.addr, join_cmd.addr_size); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci return ucma_process_join(file, &join_cmd, out_len); 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic ssize_t ucma_join_multicast(struct ucma_file *file, 154562306a36Sopenharmony_ci const char __user *inbuf, 154662306a36Sopenharmony_ci int in_len, int out_len) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct rdma_ucm_join_mcast cmd; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 155162306a36Sopenharmony_ci return -EFAULT; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (!rdma_addr_size_kss(&cmd.addr)) 155462306a36Sopenharmony_ci return -EINVAL; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return ucma_process_join(file, &cmd, out_len); 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic ssize_t ucma_leave_multicast(struct ucma_file *file, 156062306a36Sopenharmony_ci const char __user *inbuf, 156162306a36Sopenharmony_ci int in_len, int out_len) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct rdma_ucm_destroy_id cmd; 156462306a36Sopenharmony_ci struct rdma_ucm_destroy_id_resp resp; 156562306a36Sopenharmony_ci struct ucma_multicast *mc; 156662306a36Sopenharmony_ci int ret = 0; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (out_len < sizeof(resp)) 156962306a36Sopenharmony_ci return -ENOSPC; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 157262306a36Sopenharmony_ci return -EFAULT; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci xa_lock(&multicast_table); 157562306a36Sopenharmony_ci mc = xa_load(&multicast_table, cmd.id); 157662306a36Sopenharmony_ci if (!mc) 157762306a36Sopenharmony_ci mc = ERR_PTR(-ENOENT); 157862306a36Sopenharmony_ci else if (READ_ONCE(mc->ctx->file) != file) 157962306a36Sopenharmony_ci mc = ERR_PTR(-EINVAL); 158062306a36Sopenharmony_ci else if (!refcount_inc_not_zero(&mc->ctx->ref)) 158162306a36Sopenharmony_ci mc = ERR_PTR(-ENXIO); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci if (IS_ERR(mc)) { 158462306a36Sopenharmony_ci xa_unlock(&multicast_table); 158562306a36Sopenharmony_ci ret = PTR_ERR(mc); 158662306a36Sopenharmony_ci goto out; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci list_del(&mc->list); 159062306a36Sopenharmony_ci __xa_erase(&multicast_table, mc->id); 159162306a36Sopenharmony_ci xa_unlock(&multicast_table); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci mutex_lock(&mc->ctx->mutex); 159462306a36Sopenharmony_ci rdma_leave_multicast(mc->ctx->cm_id, (struct sockaddr *) &mc->addr); 159562306a36Sopenharmony_ci mutex_unlock(&mc->ctx->mutex); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci ucma_cleanup_mc_events(mc); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci ucma_put_ctx(mc->ctx); 160062306a36Sopenharmony_ci resp.events_reported = mc->events_reported; 160162306a36Sopenharmony_ci kfree(mc); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), 160462306a36Sopenharmony_ci &resp, sizeof(resp))) 160562306a36Sopenharmony_ci ret = -EFAULT; 160662306a36Sopenharmony_ciout: 160762306a36Sopenharmony_ci return ret; 160862306a36Sopenharmony_ci} 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_cistatic ssize_t ucma_migrate_id(struct ucma_file *new_file, 161162306a36Sopenharmony_ci const char __user *inbuf, 161262306a36Sopenharmony_ci int in_len, int out_len) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci struct rdma_ucm_migrate_id cmd; 161562306a36Sopenharmony_ci struct rdma_ucm_migrate_resp resp; 161662306a36Sopenharmony_ci struct ucma_event *uevent, *tmp; 161762306a36Sopenharmony_ci struct ucma_context *ctx; 161862306a36Sopenharmony_ci LIST_HEAD(event_list); 161962306a36Sopenharmony_ci struct fd f; 162062306a36Sopenharmony_ci struct ucma_file *cur_file; 162162306a36Sopenharmony_ci int ret = 0; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci if (copy_from_user(&cmd, inbuf, sizeof(cmd))) 162462306a36Sopenharmony_ci return -EFAULT; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci /* Get current fd to protect against it being closed */ 162762306a36Sopenharmony_ci f = fdget(cmd.fd); 162862306a36Sopenharmony_ci if (!f.file) 162962306a36Sopenharmony_ci return -ENOENT; 163062306a36Sopenharmony_ci if (f.file->f_op != &ucma_fops) { 163162306a36Sopenharmony_ci ret = -EINVAL; 163262306a36Sopenharmony_ci goto file_put; 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci cur_file = f.file->private_data; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci /* Validate current fd and prevent destruction of id. */ 163762306a36Sopenharmony_ci ctx = ucma_get_ctx(cur_file, cmd.id); 163862306a36Sopenharmony_ci if (IS_ERR(ctx)) { 163962306a36Sopenharmony_ci ret = PTR_ERR(ctx); 164062306a36Sopenharmony_ci goto file_put; 164162306a36Sopenharmony_ci } 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci rdma_lock_handler(ctx->cm_id); 164462306a36Sopenharmony_ci /* 164562306a36Sopenharmony_ci * ctx->file can only be changed under the handler & xa_lock. xa_load() 164662306a36Sopenharmony_ci * must be checked again to ensure the ctx hasn't begun destruction 164762306a36Sopenharmony_ci * since the ucma_get_ctx(). 164862306a36Sopenharmony_ci */ 164962306a36Sopenharmony_ci xa_lock(&ctx_table); 165062306a36Sopenharmony_ci if (_ucma_find_context(cmd.id, cur_file) != ctx) { 165162306a36Sopenharmony_ci xa_unlock(&ctx_table); 165262306a36Sopenharmony_ci ret = -ENOENT; 165362306a36Sopenharmony_ci goto err_unlock; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci ctx->file = new_file; 165662306a36Sopenharmony_ci xa_unlock(&ctx_table); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci mutex_lock(&cur_file->mut); 165962306a36Sopenharmony_ci list_del(&ctx->list); 166062306a36Sopenharmony_ci /* 166162306a36Sopenharmony_ci * At this point lock_handler() prevents addition of new uevents for 166262306a36Sopenharmony_ci * this ctx. 166362306a36Sopenharmony_ci */ 166462306a36Sopenharmony_ci list_for_each_entry_safe(uevent, tmp, &cur_file->event_list, list) 166562306a36Sopenharmony_ci if (uevent->ctx == ctx) 166662306a36Sopenharmony_ci list_move_tail(&uevent->list, &event_list); 166762306a36Sopenharmony_ci resp.events_reported = ctx->events_reported; 166862306a36Sopenharmony_ci mutex_unlock(&cur_file->mut); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci mutex_lock(&new_file->mut); 167162306a36Sopenharmony_ci list_add_tail(&ctx->list, &new_file->ctx_list); 167262306a36Sopenharmony_ci list_splice_tail(&event_list, &new_file->event_list); 167362306a36Sopenharmony_ci mutex_unlock(&new_file->mut); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(cmd.response), 167662306a36Sopenharmony_ci &resp, sizeof(resp))) 167762306a36Sopenharmony_ci ret = -EFAULT; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cierr_unlock: 168062306a36Sopenharmony_ci rdma_unlock_handler(ctx->cm_id); 168162306a36Sopenharmony_ci ucma_put_ctx(ctx); 168262306a36Sopenharmony_cifile_put: 168362306a36Sopenharmony_ci fdput(f); 168462306a36Sopenharmony_ci return ret; 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_cistatic ssize_t (*ucma_cmd_table[])(struct ucma_file *file, 168862306a36Sopenharmony_ci const char __user *inbuf, 168962306a36Sopenharmony_ci int in_len, int out_len) = { 169062306a36Sopenharmony_ci [RDMA_USER_CM_CMD_CREATE_ID] = ucma_create_id, 169162306a36Sopenharmony_ci [RDMA_USER_CM_CMD_DESTROY_ID] = ucma_destroy_id, 169262306a36Sopenharmony_ci [RDMA_USER_CM_CMD_BIND_IP] = ucma_bind_ip, 169362306a36Sopenharmony_ci [RDMA_USER_CM_CMD_RESOLVE_IP] = ucma_resolve_ip, 169462306a36Sopenharmony_ci [RDMA_USER_CM_CMD_RESOLVE_ROUTE] = ucma_resolve_route, 169562306a36Sopenharmony_ci [RDMA_USER_CM_CMD_QUERY_ROUTE] = ucma_query_route, 169662306a36Sopenharmony_ci [RDMA_USER_CM_CMD_CONNECT] = ucma_connect, 169762306a36Sopenharmony_ci [RDMA_USER_CM_CMD_LISTEN] = ucma_listen, 169862306a36Sopenharmony_ci [RDMA_USER_CM_CMD_ACCEPT] = ucma_accept, 169962306a36Sopenharmony_ci [RDMA_USER_CM_CMD_REJECT] = ucma_reject, 170062306a36Sopenharmony_ci [RDMA_USER_CM_CMD_DISCONNECT] = ucma_disconnect, 170162306a36Sopenharmony_ci [RDMA_USER_CM_CMD_INIT_QP_ATTR] = ucma_init_qp_attr, 170262306a36Sopenharmony_ci [RDMA_USER_CM_CMD_GET_EVENT] = ucma_get_event, 170362306a36Sopenharmony_ci [RDMA_USER_CM_CMD_GET_OPTION] = NULL, 170462306a36Sopenharmony_ci [RDMA_USER_CM_CMD_SET_OPTION] = ucma_set_option, 170562306a36Sopenharmony_ci [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, 170662306a36Sopenharmony_ci [RDMA_USER_CM_CMD_JOIN_IP_MCAST] = ucma_join_ip_multicast, 170762306a36Sopenharmony_ci [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast, 170862306a36Sopenharmony_ci [RDMA_USER_CM_CMD_MIGRATE_ID] = ucma_migrate_id, 170962306a36Sopenharmony_ci [RDMA_USER_CM_CMD_QUERY] = ucma_query, 171062306a36Sopenharmony_ci [RDMA_USER_CM_CMD_BIND] = ucma_bind, 171162306a36Sopenharmony_ci [RDMA_USER_CM_CMD_RESOLVE_ADDR] = ucma_resolve_addr, 171262306a36Sopenharmony_ci [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast 171362306a36Sopenharmony_ci}; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_cistatic ssize_t ucma_write(struct file *filp, const char __user *buf, 171662306a36Sopenharmony_ci size_t len, loff_t *pos) 171762306a36Sopenharmony_ci{ 171862306a36Sopenharmony_ci struct ucma_file *file = filp->private_data; 171962306a36Sopenharmony_ci struct rdma_ucm_cmd_hdr hdr; 172062306a36Sopenharmony_ci ssize_t ret; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (!ib_safe_file_access(filp)) { 172362306a36Sopenharmony_ci pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n", 172462306a36Sopenharmony_ci __func__, task_tgid_vnr(current), current->comm); 172562306a36Sopenharmony_ci return -EACCES; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (len < sizeof(hdr)) 172962306a36Sopenharmony_ci return -EINVAL; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (copy_from_user(&hdr, buf, sizeof(hdr))) 173262306a36Sopenharmony_ci return -EFAULT; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci if (hdr.cmd >= ARRAY_SIZE(ucma_cmd_table)) 173562306a36Sopenharmony_ci return -EINVAL; 173662306a36Sopenharmony_ci hdr.cmd = array_index_nospec(hdr.cmd, ARRAY_SIZE(ucma_cmd_table)); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (hdr.in + sizeof(hdr) > len) 173962306a36Sopenharmony_ci return -EINVAL; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (!ucma_cmd_table[hdr.cmd]) 174262306a36Sopenharmony_ci return -ENOSYS; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci ret = ucma_cmd_table[hdr.cmd](file, buf + sizeof(hdr), hdr.in, hdr.out); 174562306a36Sopenharmony_ci if (!ret) 174662306a36Sopenharmony_ci ret = len; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci return ret; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic __poll_t ucma_poll(struct file *filp, struct poll_table_struct *wait) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci struct ucma_file *file = filp->private_data; 175462306a36Sopenharmony_ci __poll_t mask = 0; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci poll_wait(filp, &file->poll_wait, wait); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (!list_empty(&file->event_list)) 175962306a36Sopenharmony_ci mask = EPOLLIN | EPOLLRDNORM; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci return mask; 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci/* 176562306a36Sopenharmony_ci * ucma_open() does not need the BKL: 176662306a36Sopenharmony_ci * 176762306a36Sopenharmony_ci * - no global state is referred to; 176862306a36Sopenharmony_ci * - there is no ioctl method to race against; 176962306a36Sopenharmony_ci * - no further module initialization is required for open to work 177062306a36Sopenharmony_ci * after the device is registered. 177162306a36Sopenharmony_ci */ 177262306a36Sopenharmony_cistatic int ucma_open(struct inode *inode, struct file *filp) 177362306a36Sopenharmony_ci{ 177462306a36Sopenharmony_ci struct ucma_file *file; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci file = kmalloc(sizeof *file, GFP_KERNEL); 177762306a36Sopenharmony_ci if (!file) 177862306a36Sopenharmony_ci return -ENOMEM; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci INIT_LIST_HEAD(&file->event_list); 178162306a36Sopenharmony_ci INIT_LIST_HEAD(&file->ctx_list); 178262306a36Sopenharmony_ci init_waitqueue_head(&file->poll_wait); 178362306a36Sopenharmony_ci mutex_init(&file->mut); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci filp->private_data = file; 178662306a36Sopenharmony_ci file->filp = filp; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci return stream_open(inode, filp); 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cistatic int ucma_close(struct inode *inode, struct file *filp) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci struct ucma_file *file = filp->private_data; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci /* 179662306a36Sopenharmony_ci * All paths that touch ctx_list or ctx_list starting from write() are 179762306a36Sopenharmony_ci * prevented by this being a FD release function. The list_add_tail() in 179862306a36Sopenharmony_ci * ucma_connect_event_handler() can run concurrently, however it only 179962306a36Sopenharmony_ci * adds to the list *after* a listening ID. By only reading the first of 180062306a36Sopenharmony_ci * the list, and relying on ucma_destroy_private_ctx() to block 180162306a36Sopenharmony_ci * ucma_connect_event_handler(), no additional locking is needed. 180262306a36Sopenharmony_ci */ 180362306a36Sopenharmony_ci while (!list_empty(&file->ctx_list)) { 180462306a36Sopenharmony_ci struct ucma_context *ctx = list_first_entry( 180562306a36Sopenharmony_ci &file->ctx_list, struct ucma_context, list); 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci WARN_ON(xa_cmpxchg(&ctx_table, ctx->id, ctx, XA_ZERO_ENTRY, 180862306a36Sopenharmony_ci GFP_KERNEL) != ctx); 180962306a36Sopenharmony_ci ucma_destroy_private_ctx(ctx); 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci kfree(file); 181262306a36Sopenharmony_ci return 0; 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic const struct file_operations ucma_fops = { 181662306a36Sopenharmony_ci .owner = THIS_MODULE, 181762306a36Sopenharmony_ci .open = ucma_open, 181862306a36Sopenharmony_ci .release = ucma_close, 181962306a36Sopenharmony_ci .write = ucma_write, 182062306a36Sopenharmony_ci .poll = ucma_poll, 182162306a36Sopenharmony_ci .llseek = no_llseek, 182262306a36Sopenharmony_ci}; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cistatic struct miscdevice ucma_misc = { 182562306a36Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 182662306a36Sopenharmony_ci .name = "rdma_cm", 182762306a36Sopenharmony_ci .nodename = "infiniband/rdma_cm", 182862306a36Sopenharmony_ci .mode = 0666, 182962306a36Sopenharmony_ci .fops = &ucma_fops, 183062306a36Sopenharmony_ci}; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_cistatic int ucma_get_global_nl_info(struct ib_client_nl_info *res) 183362306a36Sopenharmony_ci{ 183462306a36Sopenharmony_ci res->abi = RDMA_USER_CM_ABI_VERSION; 183562306a36Sopenharmony_ci res->cdev = ucma_misc.this_device; 183662306a36Sopenharmony_ci return 0; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_cistatic struct ib_client rdma_cma_client = { 184062306a36Sopenharmony_ci .name = "rdma_cm", 184162306a36Sopenharmony_ci .get_global_nl_info = ucma_get_global_nl_info, 184262306a36Sopenharmony_ci}; 184362306a36Sopenharmony_ciMODULE_ALIAS_RDMA_CLIENT("rdma_cm"); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_cistatic ssize_t abi_version_show(struct device *dev, 184662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 184762306a36Sopenharmony_ci{ 184862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", RDMA_USER_CM_ABI_VERSION); 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(abi_version); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_cistatic int __init ucma_init(void) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci int ret; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci ret = misc_register(&ucma_misc); 185762306a36Sopenharmony_ci if (ret) 185862306a36Sopenharmony_ci return ret; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci ret = device_create_file(ucma_misc.this_device, &dev_attr_abi_version); 186162306a36Sopenharmony_ci if (ret) { 186262306a36Sopenharmony_ci pr_err("rdma_ucm: couldn't create abi_version attr\n"); 186362306a36Sopenharmony_ci goto err1; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci ucma_ctl_table_hdr = register_net_sysctl(&init_net, "net/rdma_ucm", ucma_ctl_table); 186762306a36Sopenharmony_ci if (!ucma_ctl_table_hdr) { 186862306a36Sopenharmony_ci pr_err("rdma_ucm: couldn't register sysctl paths\n"); 186962306a36Sopenharmony_ci ret = -ENOMEM; 187062306a36Sopenharmony_ci goto err2; 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci ret = ib_register_client(&rdma_cma_client); 187462306a36Sopenharmony_ci if (ret) 187562306a36Sopenharmony_ci goto err3; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci return 0; 187862306a36Sopenharmony_cierr3: 187962306a36Sopenharmony_ci unregister_net_sysctl_table(ucma_ctl_table_hdr); 188062306a36Sopenharmony_cierr2: 188162306a36Sopenharmony_ci device_remove_file(ucma_misc.this_device, &dev_attr_abi_version); 188262306a36Sopenharmony_cierr1: 188362306a36Sopenharmony_ci misc_deregister(&ucma_misc); 188462306a36Sopenharmony_ci return ret; 188562306a36Sopenharmony_ci} 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cistatic void __exit ucma_cleanup(void) 188862306a36Sopenharmony_ci{ 188962306a36Sopenharmony_ci ib_unregister_client(&rdma_cma_client); 189062306a36Sopenharmony_ci unregister_net_sysctl_table(ucma_ctl_table_hdr); 189162306a36Sopenharmony_ci device_remove_file(ucma_misc.this_device, &dev_attr_abi_version); 189262306a36Sopenharmony_ci misc_deregister(&ucma_misc); 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_cimodule_init(ucma_init); 189662306a36Sopenharmony_cimodule_exit(ucma_cleanup); 1897