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