162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Voltaire Corporation.  All rights reserved.
562306a36Sopenharmony_ci * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
662306a36Sopenharmony_ci * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
762306a36Sopenharmony_ci * Copyright (c) 2005 Network Appliance, Inc. All rights reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This software is available to you under a choice of one of two
1062306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
1162306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
1262306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
1362306a36Sopenharmony_ci * OpenIB.org BSD license below:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1662306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1762306a36Sopenharmony_ci *     conditions are met:
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
2062306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2162306a36Sopenharmony_ci *        disclaimer.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2462306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2562306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2662306a36Sopenharmony_ci *        provided with the distribution.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2962306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
3062306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
3162306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
3262306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3362306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3462306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3562306a36Sopenharmony_ci * SOFTWARE.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3962306a36Sopenharmony_ci#include <linux/err.h>
4062306a36Sopenharmony_ci#include <linux/idr.h>
4162306a36Sopenharmony_ci#include <linux/interrupt.h>
4262306a36Sopenharmony_ci#include <linux/rbtree.h>
4362306a36Sopenharmony_ci#include <linux/sched.h>
4462306a36Sopenharmony_ci#include <linux/spinlock.h>
4562306a36Sopenharmony_ci#include <linux/workqueue.h>
4662306a36Sopenharmony_ci#include <linux/completion.h>
4762306a36Sopenharmony_ci#include <linux/slab.h>
4862306a36Sopenharmony_ci#include <linux/module.h>
4962306a36Sopenharmony_ci#include <linux/sysctl.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <rdma/iw_cm.h>
5262306a36Sopenharmony_ci#include <rdma/ib_addr.h>
5362306a36Sopenharmony_ci#include <rdma/iw_portmap.h>
5462306a36Sopenharmony_ci#include <rdma/rdma_netlink.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include "iwcm.h"
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ciMODULE_AUTHOR("Tom Tucker");
5962306a36Sopenharmony_ciMODULE_DESCRIPTION("iWARP CM");
6062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const char * const iwcm_rej_reason_strs[] = {
6362306a36Sopenharmony_ci	[ECONNRESET]			= "reset by remote host",
6462306a36Sopenharmony_ci	[ECONNREFUSED]			= "refused by remote application",
6562306a36Sopenharmony_ci	[ETIMEDOUT]			= "setup timeout",
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciconst char *__attribute_const__ iwcm_reject_msg(int reason)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	size_t index;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* iWARP uses negative errnos */
7362306a36Sopenharmony_ci	index = -reason;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (index < ARRAY_SIZE(iwcm_rej_reason_strs) &&
7662306a36Sopenharmony_ci	    iwcm_rej_reason_strs[index])
7762306a36Sopenharmony_ci		return iwcm_rej_reason_strs[index];
7862306a36Sopenharmony_ci	else
7962306a36Sopenharmony_ci		return "unrecognized reason";
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ciEXPORT_SYMBOL(iwcm_reject_msg);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct rdma_nl_cbs iwcm_nl_cb_table[RDMA_NL_IWPM_NUM_OPS] = {
8462306a36Sopenharmony_ci	[RDMA_NL_IWPM_REG_PID] = {.dump = iwpm_register_pid_cb},
8562306a36Sopenharmony_ci	[RDMA_NL_IWPM_ADD_MAPPING] = {.dump = iwpm_add_mapping_cb},
8662306a36Sopenharmony_ci	[RDMA_NL_IWPM_QUERY_MAPPING] = {.dump = iwpm_add_and_query_mapping_cb},
8762306a36Sopenharmony_ci	[RDMA_NL_IWPM_REMOTE_INFO] = {.dump = iwpm_remote_info_cb},
8862306a36Sopenharmony_ci	[RDMA_NL_IWPM_HANDLE_ERR] = {.dump = iwpm_mapping_error_cb},
8962306a36Sopenharmony_ci	[RDMA_NL_IWPM_MAPINFO] = {.dump = iwpm_mapping_info_cb},
9062306a36Sopenharmony_ci	[RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb},
9162306a36Sopenharmony_ci	[RDMA_NL_IWPM_HELLO] = {.dump = iwpm_hello_cb}
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic struct workqueue_struct *iwcm_wq;
9562306a36Sopenharmony_cistruct iwcm_work {
9662306a36Sopenharmony_ci	struct work_struct work;
9762306a36Sopenharmony_ci	struct iwcm_id_private *cm_id;
9862306a36Sopenharmony_ci	struct list_head list;
9962306a36Sopenharmony_ci	struct iw_cm_event event;
10062306a36Sopenharmony_ci	struct list_head free_list;
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic unsigned int default_backlog = 256;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic struct ctl_table_header *iwcm_ctl_table_hdr;
10662306a36Sopenharmony_cistatic struct ctl_table iwcm_ctl_table[] = {
10762306a36Sopenharmony_ci	{
10862306a36Sopenharmony_ci		.procname	= "default_backlog",
10962306a36Sopenharmony_ci		.data		= &default_backlog,
11062306a36Sopenharmony_ci		.maxlen		= sizeof(default_backlog),
11162306a36Sopenharmony_ci		.mode		= 0644,
11262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
11362306a36Sopenharmony_ci	},
11462306a36Sopenharmony_ci	{ }
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * The following services provide a mechanism for pre-allocating iwcm_work
11962306a36Sopenharmony_ci * elements.  The design pre-allocates them  based on the cm_id type:
12062306a36Sopenharmony_ci *	LISTENING IDS: 	Get enough elements preallocated to handle the
12162306a36Sopenharmony_ci *			listen backlog.
12262306a36Sopenharmony_ci *	ACTIVE IDS:	4: CONNECT_REPLY, ESTABLISHED, DISCONNECT, CLOSE
12362306a36Sopenharmony_ci *	PASSIVE IDS:	3: ESTABLISHED, DISCONNECT, CLOSE
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * Allocating them in connect and listen avoids having to deal
12662306a36Sopenharmony_ci * with allocation failures on the event upcall from the provider (which
12762306a36Sopenharmony_ci * is called in the interrupt context).
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * One exception is when creating the cm_id for incoming connection requests.
13062306a36Sopenharmony_ci * There are two cases:
13162306a36Sopenharmony_ci * 1) in the event upcall, cm_event_handler(), for a listening cm_id.  If
13262306a36Sopenharmony_ci *    the backlog is exceeded, then no more connection request events will
13362306a36Sopenharmony_ci *    be processed.  cm_event_handler() returns -ENOMEM in this case.  Its up
13462306a36Sopenharmony_ci *    to the provider to reject the connection request.
13562306a36Sopenharmony_ci * 2) in the connection request workqueue handler, cm_conn_req_handler().
13662306a36Sopenharmony_ci *    If work elements cannot be allocated for the new connect request cm_id,
13762306a36Sopenharmony_ci *    then IWCM will call the provider reject method.  This is ok since
13862306a36Sopenharmony_ci *    cm_conn_req_handler() runs in the workqueue thread context.
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct iwcm_work *get_work(struct iwcm_id_private *cm_id_priv)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct iwcm_work *work;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (list_empty(&cm_id_priv->work_free_list))
14662306a36Sopenharmony_ci		return NULL;
14762306a36Sopenharmony_ci	work = list_entry(cm_id_priv->work_free_list.next, struct iwcm_work,
14862306a36Sopenharmony_ci			  free_list);
14962306a36Sopenharmony_ci	list_del_init(&work->free_list);
15062306a36Sopenharmony_ci	return work;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void put_work(struct iwcm_work *work)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	list_add(&work->free_list, &work->cm_id->work_free_list);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void dealloc_work_entries(struct iwcm_id_private *cm_id_priv)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct list_head *e, *tmp;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	list_for_each_safe(e, tmp, &cm_id_priv->work_free_list) {
16362306a36Sopenharmony_ci		list_del(e);
16462306a36Sopenharmony_ci		kfree(list_entry(e, struct iwcm_work, free_list));
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct iwcm_work *work;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	BUG_ON(!list_empty(&cm_id_priv->work_free_list));
17362306a36Sopenharmony_ci	while (count--) {
17462306a36Sopenharmony_ci		work = kmalloc(sizeof(struct iwcm_work), GFP_KERNEL);
17562306a36Sopenharmony_ci		if (!work) {
17662306a36Sopenharmony_ci			dealloc_work_entries(cm_id_priv);
17762306a36Sopenharmony_ci			return -ENOMEM;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		work->cm_id = cm_id_priv;
18062306a36Sopenharmony_ci		INIT_LIST_HEAD(&work->list);
18162306a36Sopenharmony_ci		put_work(work);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci	return 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*
18762306a36Sopenharmony_ci * Save private data from incoming connection requests to
18862306a36Sopenharmony_ci * iw_cm_event, so the low level driver doesn't have to. Adjust
18962306a36Sopenharmony_ci * the event ptr to point to the local copy.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_cistatic int copy_private_data(struct iw_cm_event *event)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	void *p;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	p = kmemdup(event->private_data, event->private_data_len, GFP_ATOMIC);
19662306a36Sopenharmony_ci	if (!p)
19762306a36Sopenharmony_ci		return -ENOMEM;
19862306a36Sopenharmony_ci	event->private_data = p;
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void free_cm_id(struct iwcm_id_private *cm_id_priv)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	dealloc_work_entries(cm_id_priv);
20562306a36Sopenharmony_ci	kfree(cm_id_priv);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*
20962306a36Sopenharmony_ci * Release a reference on cm_id. If the last reference is being
21062306a36Sopenharmony_ci * released, free the cm_id and return 1.
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_cistatic int iwcm_deref_id(struct iwcm_id_private *cm_id_priv)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	if (refcount_dec_and_test(&cm_id_priv->refcount)) {
21562306a36Sopenharmony_ci		BUG_ON(!list_empty(&cm_id_priv->work_list));
21662306a36Sopenharmony_ci		free_cm_id(cm_id_priv);
21762306a36Sopenharmony_ci		return 1;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void add_ref(struct iw_cm_id *cm_id)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
22662306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
22762306a36Sopenharmony_ci	refcount_inc(&cm_id_priv->refcount);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void rem_ref(struct iw_cm_id *cm_id)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	(void)iwcm_deref_id(cm_id_priv);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistruct iw_cm_id *iw_create_cm_id(struct ib_device *device,
24262306a36Sopenharmony_ci				 iw_cm_handler cm_handler,
24362306a36Sopenharmony_ci				 void *context)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	cm_id_priv = kzalloc(sizeof(*cm_id_priv), GFP_KERNEL);
24862306a36Sopenharmony_ci	if (!cm_id_priv)
24962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	cm_id_priv->state = IW_CM_STATE_IDLE;
25262306a36Sopenharmony_ci	cm_id_priv->id.device = device;
25362306a36Sopenharmony_ci	cm_id_priv->id.cm_handler = cm_handler;
25462306a36Sopenharmony_ci	cm_id_priv->id.context = context;
25562306a36Sopenharmony_ci	cm_id_priv->id.event_handler = cm_event_handler;
25662306a36Sopenharmony_ci	cm_id_priv->id.add_ref = add_ref;
25762306a36Sopenharmony_ci	cm_id_priv->id.rem_ref = rem_ref;
25862306a36Sopenharmony_ci	spin_lock_init(&cm_id_priv->lock);
25962306a36Sopenharmony_ci	refcount_set(&cm_id_priv->refcount, 1);
26062306a36Sopenharmony_ci	init_waitqueue_head(&cm_id_priv->connect_wait);
26162306a36Sopenharmony_ci	init_completion(&cm_id_priv->destroy_comp);
26262306a36Sopenharmony_ci	INIT_LIST_HEAD(&cm_id_priv->work_list);
26362306a36Sopenharmony_ci	INIT_LIST_HEAD(&cm_id_priv->work_free_list);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return &cm_id_priv->id;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ciEXPORT_SYMBOL(iw_create_cm_id);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int iwcm_modify_qp_err(struct ib_qp *qp)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct ib_qp_attr qp_attr;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!qp)
27562306a36Sopenharmony_ci		return -EINVAL;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	qp_attr.qp_state = IB_QPS_ERR;
27862306a36Sopenharmony_ci	return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/*
28262306a36Sopenharmony_ci * This is really the RDMAC CLOSING state. It is most similar to the
28362306a36Sopenharmony_ci * IB SQD QP state.
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic int iwcm_modify_qp_sqd(struct ib_qp *qp)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct ib_qp_attr qp_attr;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	BUG_ON(qp == NULL);
29062306a36Sopenharmony_ci	qp_attr.qp_state = IB_QPS_SQD;
29162306a36Sopenharmony_ci	return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/*
29562306a36Sopenharmony_ci * CM_ID <-- CLOSING
29662306a36Sopenharmony_ci *
29762306a36Sopenharmony_ci * Block if a passive or active connection is currently being processed. Then
29862306a36Sopenharmony_ci * process the event as follows:
29962306a36Sopenharmony_ci * - If we are ESTABLISHED, move to CLOSING and modify the QP state
30062306a36Sopenharmony_ci *   based on the abrupt flag
30162306a36Sopenharmony_ci * - If the connection is already in the CLOSING or IDLE state, the peer is
30262306a36Sopenharmony_ci *   disconnecting concurrently with us and we've already seen the
30362306a36Sopenharmony_ci *   DISCONNECT event -- ignore the request and return 0
30462306a36Sopenharmony_ci * - Disconnect on a listening endpoint returns -EINVAL
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_ciint iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
30962306a36Sopenharmony_ci	unsigned long flags;
31062306a36Sopenharmony_ci	int ret = 0;
31162306a36Sopenharmony_ci	struct ib_qp *qp = NULL;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
31462306a36Sopenharmony_ci	/* Wait if we're currently in a connect or accept downcall */
31562306a36Sopenharmony_ci	wait_event(cm_id_priv->connect_wait,
31662306a36Sopenharmony_ci		   !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
31962306a36Sopenharmony_ci	switch (cm_id_priv->state) {
32062306a36Sopenharmony_ci	case IW_CM_STATE_ESTABLISHED:
32162306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_CLOSING;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		/* QP could be <nul> for user-mode client */
32462306a36Sopenharmony_ci		if (cm_id_priv->qp)
32562306a36Sopenharmony_ci			qp = cm_id_priv->qp;
32662306a36Sopenharmony_ci		else
32762306a36Sopenharmony_ci			ret = -EINVAL;
32862306a36Sopenharmony_ci		break;
32962306a36Sopenharmony_ci	case IW_CM_STATE_LISTEN:
33062306a36Sopenharmony_ci		ret = -EINVAL;
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case IW_CM_STATE_CLOSING:
33362306a36Sopenharmony_ci		/* remote peer closed first */
33462306a36Sopenharmony_ci	case IW_CM_STATE_IDLE:
33562306a36Sopenharmony_ci		/* accept or connect returned !0 */
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	case IW_CM_STATE_CONN_RECV:
33862306a36Sopenharmony_ci		/*
33962306a36Sopenharmony_ci		 * App called disconnect before/without calling accept after
34062306a36Sopenharmony_ci		 * connect_request event delivered.
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	case IW_CM_STATE_CONN_SENT:
34462306a36Sopenharmony_ci		/* Can only get here if wait above fails */
34562306a36Sopenharmony_ci	default:
34662306a36Sopenharmony_ci		BUG();
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (qp) {
35162306a36Sopenharmony_ci		if (abrupt)
35262306a36Sopenharmony_ci			ret = iwcm_modify_qp_err(qp);
35362306a36Sopenharmony_ci		else
35462306a36Sopenharmony_ci			ret = iwcm_modify_qp_sqd(qp);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/*
35762306a36Sopenharmony_ci		 * If both sides are disconnecting the QP could
35862306a36Sopenharmony_ci		 * already be in ERR or SQD states
35962306a36Sopenharmony_ci		 */
36062306a36Sopenharmony_ci		ret = 0;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return ret;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ciEXPORT_SYMBOL(iw_cm_disconnect);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/*
36862306a36Sopenharmony_ci * CM_ID <-- DESTROYING
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * Clean up all resources associated with the connection and release
37162306a36Sopenharmony_ci * the initial reference taken by iw_create_cm_id.
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_cistatic void destroy_cm_id(struct iw_cm_id *cm_id)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
37662306a36Sopenharmony_ci	struct ib_qp *qp;
37762306a36Sopenharmony_ci	unsigned long flags;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
38062306a36Sopenharmony_ci	/*
38162306a36Sopenharmony_ci	 * Wait if we're currently in a connect or accept downcall. A
38262306a36Sopenharmony_ci	 * listening endpoint should never block here.
38362306a36Sopenharmony_ci	 */
38462306a36Sopenharmony_ci	wait_event(cm_id_priv->connect_wait,
38562306a36Sopenharmony_ci		   !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags));
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/*
38862306a36Sopenharmony_ci	 * Since we're deleting the cm_id, drop any events that
38962306a36Sopenharmony_ci	 * might arrive before the last dereference.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	set_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
39462306a36Sopenharmony_ci	qp = cm_id_priv->qp;
39562306a36Sopenharmony_ci	cm_id_priv->qp = NULL;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	switch (cm_id_priv->state) {
39862306a36Sopenharmony_ci	case IW_CM_STATE_LISTEN:
39962306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_DESTROYING;
40062306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
40162306a36Sopenharmony_ci		/* destroy the listening endpoint */
40262306a36Sopenharmony_ci		cm_id->device->ops.iw_destroy_listen(cm_id);
40362306a36Sopenharmony_ci		spin_lock_irqsave(&cm_id_priv->lock, flags);
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci	case IW_CM_STATE_ESTABLISHED:
40662306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_DESTROYING;
40762306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
40862306a36Sopenharmony_ci		/* Abrupt close of the connection */
40962306a36Sopenharmony_ci		(void)iwcm_modify_qp_err(qp);
41062306a36Sopenharmony_ci		spin_lock_irqsave(&cm_id_priv->lock, flags);
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci	case IW_CM_STATE_IDLE:
41362306a36Sopenharmony_ci	case IW_CM_STATE_CLOSING:
41462306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_DESTROYING;
41562306a36Sopenharmony_ci		break;
41662306a36Sopenharmony_ci	case IW_CM_STATE_CONN_RECV:
41762306a36Sopenharmony_ci		/*
41862306a36Sopenharmony_ci		 * App called destroy before/without calling accept after
41962306a36Sopenharmony_ci		 * receiving connection request event notification or
42062306a36Sopenharmony_ci		 * returned non zero from the event callback function.
42162306a36Sopenharmony_ci		 * In either case, must tell the provider to reject.
42262306a36Sopenharmony_ci		 */
42362306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_DESTROYING;
42462306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
42562306a36Sopenharmony_ci		cm_id->device->ops.iw_reject(cm_id, NULL, 0);
42662306a36Sopenharmony_ci		spin_lock_irqsave(&cm_id_priv->lock, flags);
42762306a36Sopenharmony_ci		break;
42862306a36Sopenharmony_ci	case IW_CM_STATE_CONN_SENT:
42962306a36Sopenharmony_ci	case IW_CM_STATE_DESTROYING:
43062306a36Sopenharmony_ci	default:
43162306a36Sopenharmony_ci		BUG();
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
43562306a36Sopenharmony_ci	if (qp)
43662306a36Sopenharmony_ci		cm_id_priv->id.device->ops.iw_rem_ref(qp);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (cm_id->mapped) {
43962306a36Sopenharmony_ci		iwpm_remove_mapinfo(&cm_id->local_addr, &cm_id->m_local_addr);
44062306a36Sopenharmony_ci		iwpm_remove_mapping(&cm_id->local_addr, RDMA_NL_IWCM);
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	(void)iwcm_deref_id(cm_id_priv);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/*
44762306a36Sopenharmony_ci * This function is only called by the application thread and cannot
44862306a36Sopenharmony_ci * be called by the event thread. The function will wait for all
44962306a36Sopenharmony_ci * references to be released on the cm_id and then kfree the cm_id
45062306a36Sopenharmony_ci * object.
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_civoid iw_destroy_cm_id(struct iw_cm_id *cm_id)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	destroy_cm_id(cm_id);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ciEXPORT_SYMBOL(iw_destroy_cm_id);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci/**
45962306a36Sopenharmony_ci * iw_cm_check_wildcard - If IP address is 0 then use original
46062306a36Sopenharmony_ci * @pm_addr: sockaddr containing the ip to check for wildcard
46162306a36Sopenharmony_ci * @cm_addr: sockaddr containing the actual IP address
46262306a36Sopenharmony_ci * @cm_outaddr: sockaddr to set IP addr which leaving port
46362306a36Sopenharmony_ci *
46462306a36Sopenharmony_ci *  Checks the pm_addr for wildcard and then sets cm_outaddr's
46562306a36Sopenharmony_ci *  IP to the actual (cm_addr).
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cistatic void iw_cm_check_wildcard(struct sockaddr_storage *pm_addr,
46862306a36Sopenharmony_ci				 struct sockaddr_storage *cm_addr,
46962306a36Sopenharmony_ci				 struct sockaddr_storage *cm_outaddr)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	if (pm_addr->ss_family == AF_INET) {
47262306a36Sopenharmony_ci		struct sockaddr_in *pm4_addr = (struct sockaddr_in *)pm_addr;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		if (pm4_addr->sin_addr.s_addr == htonl(INADDR_ANY)) {
47562306a36Sopenharmony_ci			struct sockaddr_in *cm4_addr =
47662306a36Sopenharmony_ci				(struct sockaddr_in *)cm_addr;
47762306a36Sopenharmony_ci			struct sockaddr_in *cm4_outaddr =
47862306a36Sopenharmony_ci				(struct sockaddr_in *)cm_outaddr;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci			cm4_outaddr->sin_addr = cm4_addr->sin_addr;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci	} else {
48362306a36Sopenharmony_ci		struct sockaddr_in6 *pm6_addr = (struct sockaddr_in6 *)pm_addr;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		if (ipv6_addr_type(&pm6_addr->sin6_addr) == IPV6_ADDR_ANY) {
48662306a36Sopenharmony_ci			struct sockaddr_in6 *cm6_addr =
48762306a36Sopenharmony_ci				(struct sockaddr_in6 *)cm_addr;
48862306a36Sopenharmony_ci			struct sockaddr_in6 *cm6_outaddr =
48962306a36Sopenharmony_ci				(struct sockaddr_in6 *)cm_outaddr;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci			cm6_outaddr->sin6_addr = cm6_addr->sin6_addr;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/**
49762306a36Sopenharmony_ci * iw_cm_map - Use portmapper to map the ports
49862306a36Sopenharmony_ci * @cm_id: connection manager pointer
49962306a36Sopenharmony_ci * @active: Indicates the active side when true
50062306a36Sopenharmony_ci * returns nonzero for error only if iwpm_create_mapinfo() fails
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * Tries to add a mapping for a port using the Portmapper. If
50362306a36Sopenharmony_ci * successful in mapping the IP/Port it will check the remote
50462306a36Sopenharmony_ci * mapped IP address for a wildcard IP address and replace the
50562306a36Sopenharmony_ci * zero IP address with the remote_addr.
50662306a36Sopenharmony_ci */
50762306a36Sopenharmony_cistatic int iw_cm_map(struct iw_cm_id *cm_id, bool active)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	const char *devname = dev_name(&cm_id->device->dev);
51062306a36Sopenharmony_ci	const char *ifname = cm_id->device->iw_ifname;
51162306a36Sopenharmony_ci	struct iwpm_dev_data pm_reg_msg = {};
51262306a36Sopenharmony_ci	struct iwpm_sa_data pm_msg;
51362306a36Sopenharmony_ci	int status;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (strlen(devname) >= sizeof(pm_reg_msg.dev_name) ||
51662306a36Sopenharmony_ci	    strlen(ifname) >= sizeof(pm_reg_msg.if_name))
51762306a36Sopenharmony_ci		return -EINVAL;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	cm_id->m_local_addr = cm_id->local_addr;
52062306a36Sopenharmony_ci	cm_id->m_remote_addr = cm_id->remote_addr;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	strcpy(pm_reg_msg.dev_name, devname);
52362306a36Sopenharmony_ci	strcpy(pm_reg_msg.if_name, ifname);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (iwpm_register_pid(&pm_reg_msg, RDMA_NL_IWCM) ||
52662306a36Sopenharmony_ci	    !iwpm_valid_pid())
52762306a36Sopenharmony_ci		return 0;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	cm_id->mapped = true;
53062306a36Sopenharmony_ci	pm_msg.loc_addr = cm_id->local_addr;
53162306a36Sopenharmony_ci	pm_msg.rem_addr = cm_id->remote_addr;
53262306a36Sopenharmony_ci	pm_msg.flags = (cm_id->device->iw_driver_flags & IW_F_NO_PORT_MAP) ?
53362306a36Sopenharmony_ci		       IWPM_FLAGS_NO_PORT_MAP : 0;
53462306a36Sopenharmony_ci	if (active)
53562306a36Sopenharmony_ci		status = iwpm_add_and_query_mapping(&pm_msg,
53662306a36Sopenharmony_ci						    RDMA_NL_IWCM);
53762306a36Sopenharmony_ci	else
53862306a36Sopenharmony_ci		status = iwpm_add_mapping(&pm_msg, RDMA_NL_IWCM);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (!status) {
54162306a36Sopenharmony_ci		cm_id->m_local_addr = pm_msg.mapped_loc_addr;
54262306a36Sopenharmony_ci		if (active) {
54362306a36Sopenharmony_ci			cm_id->m_remote_addr = pm_msg.mapped_rem_addr;
54462306a36Sopenharmony_ci			iw_cm_check_wildcard(&pm_msg.mapped_rem_addr,
54562306a36Sopenharmony_ci					     &cm_id->remote_addr,
54662306a36Sopenharmony_ci					     &cm_id->m_remote_addr);
54762306a36Sopenharmony_ci		}
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return iwpm_create_mapinfo(&cm_id->local_addr,
55162306a36Sopenharmony_ci				   &cm_id->m_local_addr,
55262306a36Sopenharmony_ci				   RDMA_NL_IWCM, pm_msg.flags);
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci/*
55662306a36Sopenharmony_ci * CM_ID <-- LISTEN
55762306a36Sopenharmony_ci *
55862306a36Sopenharmony_ci * Start listening for connect requests. Generates one CONNECT_REQUEST
55962306a36Sopenharmony_ci * event for each inbound connect request.
56062306a36Sopenharmony_ci */
56162306a36Sopenharmony_ciint iw_cm_listen(struct iw_cm_id *cm_id, int backlog)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
56462306a36Sopenharmony_ci	unsigned long flags;
56562306a36Sopenharmony_ci	int ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (!backlog)
57062306a36Sopenharmony_ci		backlog = default_backlog;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ret = alloc_work_entries(cm_id_priv, backlog);
57362306a36Sopenharmony_ci	if (ret)
57462306a36Sopenharmony_ci		return ret;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
57762306a36Sopenharmony_ci	switch (cm_id_priv->state) {
57862306a36Sopenharmony_ci	case IW_CM_STATE_IDLE:
57962306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_LISTEN;
58062306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
58162306a36Sopenharmony_ci		ret = iw_cm_map(cm_id, false);
58262306a36Sopenharmony_ci		if (!ret)
58362306a36Sopenharmony_ci			ret = cm_id->device->ops.iw_create_listen(cm_id,
58462306a36Sopenharmony_ci								  backlog);
58562306a36Sopenharmony_ci		if (ret)
58662306a36Sopenharmony_ci			cm_id_priv->state = IW_CM_STATE_IDLE;
58762306a36Sopenharmony_ci		spin_lock_irqsave(&cm_id_priv->lock, flags);
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci	default:
59062306a36Sopenharmony_ci		ret = -EINVAL;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return ret;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ciEXPORT_SYMBOL(iw_cm_listen);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/*
59962306a36Sopenharmony_ci * CM_ID <-- IDLE
60062306a36Sopenharmony_ci *
60162306a36Sopenharmony_ci * Rejects an inbound connection request. No events are generated.
60262306a36Sopenharmony_ci */
60362306a36Sopenharmony_ciint iw_cm_reject(struct iw_cm_id *cm_id,
60462306a36Sopenharmony_ci		 const void *private_data,
60562306a36Sopenharmony_ci		 u8 private_data_len)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
60862306a36Sopenharmony_ci	unsigned long flags;
60962306a36Sopenharmony_ci	int ret;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
61262306a36Sopenharmony_ci	set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
61562306a36Sopenharmony_ci	if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
61662306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
61762306a36Sopenharmony_ci		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
61862306a36Sopenharmony_ci		wake_up_all(&cm_id_priv->connect_wait);
61962306a36Sopenharmony_ci		return -EINVAL;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	cm_id_priv->state = IW_CM_STATE_IDLE;
62262306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	ret = cm_id->device->ops.iw_reject(cm_id, private_data,
62562306a36Sopenharmony_ci					  private_data_len);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
62862306a36Sopenharmony_ci	wake_up_all(&cm_id_priv->connect_wait);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return ret;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ciEXPORT_SYMBOL(iw_cm_reject);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci/*
63562306a36Sopenharmony_ci * CM_ID <-- ESTABLISHED
63662306a36Sopenharmony_ci *
63762306a36Sopenharmony_ci * Accepts an inbound connection request and generates an ESTABLISHED
63862306a36Sopenharmony_ci * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block
63962306a36Sopenharmony_ci * until the ESTABLISHED event is received from the provider.
64062306a36Sopenharmony_ci */
64162306a36Sopenharmony_ciint iw_cm_accept(struct iw_cm_id *cm_id,
64262306a36Sopenharmony_ci		 struct iw_cm_conn_param *iw_param)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
64562306a36Sopenharmony_ci	struct ib_qp *qp;
64662306a36Sopenharmony_ci	unsigned long flags;
64762306a36Sopenharmony_ci	int ret;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
65062306a36Sopenharmony_ci	set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
65362306a36Sopenharmony_ci	if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
65462306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
65562306a36Sopenharmony_ci		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
65662306a36Sopenharmony_ci		wake_up_all(&cm_id_priv->connect_wait);
65762306a36Sopenharmony_ci		return -EINVAL;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci	/* Get the ib_qp given the QPN */
66062306a36Sopenharmony_ci	qp = cm_id->device->ops.iw_get_qp(cm_id->device, iw_param->qpn);
66162306a36Sopenharmony_ci	if (!qp) {
66262306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
66362306a36Sopenharmony_ci		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
66462306a36Sopenharmony_ci		wake_up_all(&cm_id_priv->connect_wait);
66562306a36Sopenharmony_ci		return -EINVAL;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci	cm_id->device->ops.iw_add_ref(qp);
66862306a36Sopenharmony_ci	cm_id_priv->qp = qp;
66962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	ret = cm_id->device->ops.iw_accept(cm_id, iw_param);
67262306a36Sopenharmony_ci	if (ret) {
67362306a36Sopenharmony_ci		/* An error on accept precludes provider events */
67462306a36Sopenharmony_ci		BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
67562306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_IDLE;
67662306a36Sopenharmony_ci		spin_lock_irqsave(&cm_id_priv->lock, flags);
67762306a36Sopenharmony_ci		qp = cm_id_priv->qp;
67862306a36Sopenharmony_ci		cm_id_priv->qp = NULL;
67962306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
68062306a36Sopenharmony_ci		if (qp)
68162306a36Sopenharmony_ci			cm_id->device->ops.iw_rem_ref(qp);
68262306a36Sopenharmony_ci		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
68362306a36Sopenharmony_ci		wake_up_all(&cm_id_priv->connect_wait);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	return ret;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ciEXPORT_SYMBOL(iw_cm_accept);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci/*
69162306a36Sopenharmony_ci * Active Side: CM_ID <-- CONN_SENT
69262306a36Sopenharmony_ci *
69362306a36Sopenharmony_ci * If successful, results in the generation of a CONNECT_REPLY
69462306a36Sopenharmony_ci * event. iw_cm_disconnect and iw_cm_destroy will block until the
69562306a36Sopenharmony_ci * CONNECT_REPLY event is received from the provider.
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_ciint iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
70062306a36Sopenharmony_ci	int ret;
70162306a36Sopenharmony_ci	unsigned long flags;
70262306a36Sopenharmony_ci	struct ib_qp *qp = NULL;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	ret = alloc_work_entries(cm_id_priv, 4);
70762306a36Sopenharmony_ci	if (ret)
70862306a36Sopenharmony_ci		return ret;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
71162306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (cm_id_priv->state != IW_CM_STATE_IDLE) {
71462306a36Sopenharmony_ci		ret = -EINVAL;
71562306a36Sopenharmony_ci		goto err;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Get the ib_qp given the QPN */
71962306a36Sopenharmony_ci	qp = cm_id->device->ops.iw_get_qp(cm_id->device, iw_param->qpn);
72062306a36Sopenharmony_ci	if (!qp) {
72162306a36Sopenharmony_ci		ret = -EINVAL;
72262306a36Sopenharmony_ci		goto err;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci	cm_id->device->ops.iw_add_ref(qp);
72562306a36Sopenharmony_ci	cm_id_priv->qp = qp;
72662306a36Sopenharmony_ci	cm_id_priv->state = IW_CM_STATE_CONN_SENT;
72762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	ret = iw_cm_map(cm_id, true);
73062306a36Sopenharmony_ci	if (!ret)
73162306a36Sopenharmony_ci		ret = cm_id->device->ops.iw_connect(cm_id, iw_param);
73262306a36Sopenharmony_ci	if (!ret)
73362306a36Sopenharmony_ci		return 0;	/* success */
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
73662306a36Sopenharmony_ci	qp = cm_id_priv->qp;
73762306a36Sopenharmony_ci	cm_id_priv->qp = NULL;
73862306a36Sopenharmony_ci	cm_id_priv->state = IW_CM_STATE_IDLE;
73962306a36Sopenharmony_cierr:
74062306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
74162306a36Sopenharmony_ci	if (qp)
74262306a36Sopenharmony_ci		cm_id->device->ops.iw_rem_ref(qp);
74362306a36Sopenharmony_ci	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
74462306a36Sopenharmony_ci	wake_up_all(&cm_id_priv->connect_wait);
74562306a36Sopenharmony_ci	return ret;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ciEXPORT_SYMBOL(iw_cm_connect);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci/*
75062306a36Sopenharmony_ci * Passive Side: new CM_ID <-- CONN_RECV
75162306a36Sopenharmony_ci *
75262306a36Sopenharmony_ci * Handles an inbound connect request. The function creates a new
75362306a36Sopenharmony_ci * iw_cm_id to represent the new connection and inherits the client
75462306a36Sopenharmony_ci * callback function and other attributes from the listening parent.
75562306a36Sopenharmony_ci *
75662306a36Sopenharmony_ci * The work item contains a pointer to the listen_cm_id and the event. The
75762306a36Sopenharmony_ci * listen_cm_id contains the client cm_handler, context and
75862306a36Sopenharmony_ci * device. These are copied when the device is cloned. The event
75962306a36Sopenharmony_ci * contains the new four tuple.
76062306a36Sopenharmony_ci *
76162306a36Sopenharmony_ci * An error on the child should not affect the parent, so this
76262306a36Sopenharmony_ci * function does not return a value.
76362306a36Sopenharmony_ci */
76462306a36Sopenharmony_cistatic void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,
76562306a36Sopenharmony_ci				struct iw_cm_event *iw_event)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	unsigned long flags;
76862306a36Sopenharmony_ci	struct iw_cm_id *cm_id;
76962306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
77062306a36Sopenharmony_ci	int ret;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/*
77362306a36Sopenharmony_ci	 * The provider should never generate a connection request
77462306a36Sopenharmony_ci	 * event with a bad status.
77562306a36Sopenharmony_ci	 */
77662306a36Sopenharmony_ci	BUG_ON(iw_event->status);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	cm_id = iw_create_cm_id(listen_id_priv->id.device,
77962306a36Sopenharmony_ci				listen_id_priv->id.cm_handler,
78062306a36Sopenharmony_ci				listen_id_priv->id.context);
78162306a36Sopenharmony_ci	/* If the cm_id could not be created, ignore the request */
78262306a36Sopenharmony_ci	if (IS_ERR(cm_id))
78362306a36Sopenharmony_ci		goto out;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	cm_id->provider_data = iw_event->provider_data;
78662306a36Sopenharmony_ci	cm_id->m_local_addr = iw_event->local_addr;
78762306a36Sopenharmony_ci	cm_id->m_remote_addr = iw_event->remote_addr;
78862306a36Sopenharmony_ci	cm_id->local_addr = listen_id_priv->id.local_addr;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	ret = iwpm_get_remote_info(&listen_id_priv->id.m_local_addr,
79162306a36Sopenharmony_ci				   &iw_event->remote_addr,
79262306a36Sopenharmony_ci				   &cm_id->remote_addr,
79362306a36Sopenharmony_ci				   RDMA_NL_IWCM);
79462306a36Sopenharmony_ci	if (ret) {
79562306a36Sopenharmony_ci		cm_id->remote_addr = iw_event->remote_addr;
79662306a36Sopenharmony_ci	} else {
79762306a36Sopenharmony_ci		iw_cm_check_wildcard(&listen_id_priv->id.m_local_addr,
79862306a36Sopenharmony_ci				     &iw_event->local_addr,
79962306a36Sopenharmony_ci				     &cm_id->local_addr);
80062306a36Sopenharmony_ci		iw_event->local_addr = cm_id->local_addr;
80162306a36Sopenharmony_ci		iw_event->remote_addr = cm_id->remote_addr;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
80562306a36Sopenharmony_ci	cm_id_priv->state = IW_CM_STATE_CONN_RECV;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/*
80862306a36Sopenharmony_ci	 * We could be destroying the listening id. If so, ignore this
80962306a36Sopenharmony_ci	 * upcall.
81062306a36Sopenharmony_ci	 */
81162306a36Sopenharmony_ci	spin_lock_irqsave(&listen_id_priv->lock, flags);
81262306a36Sopenharmony_ci	if (listen_id_priv->state != IW_CM_STATE_LISTEN) {
81362306a36Sopenharmony_ci		spin_unlock_irqrestore(&listen_id_priv->lock, flags);
81462306a36Sopenharmony_ci		iw_cm_reject(cm_id, NULL, 0);
81562306a36Sopenharmony_ci		iw_destroy_cm_id(cm_id);
81662306a36Sopenharmony_ci		goto out;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	spin_unlock_irqrestore(&listen_id_priv->lock, flags);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	ret = alloc_work_entries(cm_id_priv, 3);
82162306a36Sopenharmony_ci	if (ret) {
82262306a36Sopenharmony_ci		iw_cm_reject(cm_id, NULL, 0);
82362306a36Sopenharmony_ci		iw_destroy_cm_id(cm_id);
82462306a36Sopenharmony_ci		goto out;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* Call the client CM handler */
82862306a36Sopenharmony_ci	ret = cm_id->cm_handler(cm_id, iw_event);
82962306a36Sopenharmony_ci	if (ret) {
83062306a36Sopenharmony_ci		iw_cm_reject(cm_id, NULL, 0);
83162306a36Sopenharmony_ci		iw_destroy_cm_id(cm_id);
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ciout:
83562306a36Sopenharmony_ci	if (iw_event->private_data_len)
83662306a36Sopenharmony_ci		kfree(iw_event->private_data);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci/*
84062306a36Sopenharmony_ci * Passive Side: CM_ID <-- ESTABLISHED
84162306a36Sopenharmony_ci *
84262306a36Sopenharmony_ci * The provider generated an ESTABLISHED event which means that
84362306a36Sopenharmony_ci * the MPA negotion has completed successfully and we are now in MPA
84462306a36Sopenharmony_ci * FPDU mode.
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * This event can only be received in the CONN_RECV state. If the
84762306a36Sopenharmony_ci * remote peer closed, the ESTABLISHED event would be received followed
84862306a36Sopenharmony_ci * by the CLOSE event. If the app closes, it will block until we wake
84962306a36Sopenharmony_ci * it up after processing this event.
85062306a36Sopenharmony_ci */
85162306a36Sopenharmony_cistatic int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv,
85262306a36Sopenharmony_ci			       struct iw_cm_event *iw_event)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	unsigned long flags;
85562306a36Sopenharmony_ci	int ret;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/*
86062306a36Sopenharmony_ci	 * We clear the CONNECT_WAIT bit here to allow the callback
86162306a36Sopenharmony_ci	 * function to call iw_cm_disconnect. Calling iw_destroy_cm_id
86262306a36Sopenharmony_ci	 * from a callback handler is not allowed.
86362306a36Sopenharmony_ci	 */
86462306a36Sopenharmony_ci	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
86562306a36Sopenharmony_ci	BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
86662306a36Sopenharmony_ci	cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
86762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
86862306a36Sopenharmony_ci	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
86962306a36Sopenharmony_ci	wake_up_all(&cm_id_priv->connect_wait);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return ret;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci/*
87562306a36Sopenharmony_ci * Active Side: CM_ID <-- ESTABLISHED
87662306a36Sopenharmony_ci *
87762306a36Sopenharmony_ci * The app has called connect and is waiting for the established event to
87862306a36Sopenharmony_ci * post it's requests to the server. This event will wake up anyone
87962306a36Sopenharmony_ci * blocked in iw_cm_disconnect or iw_destroy_id.
88062306a36Sopenharmony_ci */
88162306a36Sopenharmony_cistatic int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
88262306a36Sopenharmony_ci			       struct iw_cm_event *iw_event)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct ib_qp *qp = NULL;
88562306a36Sopenharmony_ci	unsigned long flags;
88662306a36Sopenharmony_ci	int ret;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
88962306a36Sopenharmony_ci	/*
89062306a36Sopenharmony_ci	 * Clear the connect wait bit so a callback function calling
89162306a36Sopenharmony_ci	 * iw_cm_disconnect will not wait and deadlock this thread
89262306a36Sopenharmony_ci	 */
89362306a36Sopenharmony_ci	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
89462306a36Sopenharmony_ci	BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT);
89562306a36Sopenharmony_ci	if (iw_event->status == 0) {
89662306a36Sopenharmony_ci		cm_id_priv->id.m_local_addr = iw_event->local_addr;
89762306a36Sopenharmony_ci		cm_id_priv->id.m_remote_addr = iw_event->remote_addr;
89862306a36Sopenharmony_ci		iw_event->local_addr = cm_id_priv->id.local_addr;
89962306a36Sopenharmony_ci		iw_event->remote_addr = cm_id_priv->id.remote_addr;
90062306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
90162306a36Sopenharmony_ci	} else {
90262306a36Sopenharmony_ci		/* REJECTED or RESET */
90362306a36Sopenharmony_ci		qp = cm_id_priv->qp;
90462306a36Sopenharmony_ci		cm_id_priv->qp = NULL;
90562306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_IDLE;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
90862306a36Sopenharmony_ci	if (qp)
90962306a36Sopenharmony_ci		cm_id_priv->id.device->ops.iw_rem_ref(qp);
91062306a36Sopenharmony_ci	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (iw_event->private_data_len)
91362306a36Sopenharmony_ci		kfree(iw_event->private_data);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* Wake up waiters on connect complete */
91662306a36Sopenharmony_ci	wake_up_all(&cm_id_priv->connect_wait);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	return ret;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci/*
92262306a36Sopenharmony_ci * CM_ID <-- CLOSING
92362306a36Sopenharmony_ci *
92462306a36Sopenharmony_ci * If in the ESTABLISHED state, move to CLOSING.
92562306a36Sopenharmony_ci */
92662306a36Sopenharmony_cistatic void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv,
92762306a36Sopenharmony_ci				  struct iw_cm_event *iw_event)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	unsigned long flags;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
93262306a36Sopenharmony_ci	if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED)
93362306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_CLOSING;
93462306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/*
93862306a36Sopenharmony_ci * CM_ID <-- IDLE
93962306a36Sopenharmony_ci *
94062306a36Sopenharmony_ci * If in the ESTBLISHED or CLOSING states, the QP will have have been
94162306a36Sopenharmony_ci * moved by the provider to the ERR state. Disassociate the CM_ID from
94262306a36Sopenharmony_ci * the QP,  move to IDLE, and remove the 'connected' reference.
94362306a36Sopenharmony_ci *
94462306a36Sopenharmony_ci * If in some other state, the cm_id was destroyed asynchronously.
94562306a36Sopenharmony_ci * This is the last reference that will result in waking up
94662306a36Sopenharmony_ci * the app thread blocked in iw_destroy_cm_id.
94762306a36Sopenharmony_ci */
94862306a36Sopenharmony_cistatic int cm_close_handler(struct iwcm_id_private *cm_id_priv,
94962306a36Sopenharmony_ci				  struct iw_cm_event *iw_event)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct ib_qp *qp;
95262306a36Sopenharmony_ci	unsigned long flags;
95362306a36Sopenharmony_ci	int ret = 0, notify_event = 0;
95462306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
95562306a36Sopenharmony_ci	qp = cm_id_priv->qp;
95662306a36Sopenharmony_ci	cm_id_priv->qp = NULL;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	switch (cm_id_priv->state) {
95962306a36Sopenharmony_ci	case IW_CM_STATE_ESTABLISHED:
96062306a36Sopenharmony_ci	case IW_CM_STATE_CLOSING:
96162306a36Sopenharmony_ci		cm_id_priv->state = IW_CM_STATE_IDLE;
96262306a36Sopenharmony_ci		notify_event = 1;
96362306a36Sopenharmony_ci		break;
96462306a36Sopenharmony_ci	case IW_CM_STATE_DESTROYING:
96562306a36Sopenharmony_ci		break;
96662306a36Sopenharmony_ci	default:
96762306a36Sopenharmony_ci		BUG();
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (qp)
97262306a36Sopenharmony_ci		cm_id_priv->id.device->ops.iw_rem_ref(qp);
97362306a36Sopenharmony_ci	if (notify_event)
97462306a36Sopenharmony_ci		ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
97562306a36Sopenharmony_ci	return ret;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int process_event(struct iwcm_id_private *cm_id_priv,
97962306a36Sopenharmony_ci			 struct iw_cm_event *iw_event)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	int ret = 0;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	switch (iw_event->event) {
98462306a36Sopenharmony_ci	case IW_CM_EVENT_CONNECT_REQUEST:
98562306a36Sopenharmony_ci		cm_conn_req_handler(cm_id_priv, iw_event);
98662306a36Sopenharmony_ci		break;
98762306a36Sopenharmony_ci	case IW_CM_EVENT_CONNECT_REPLY:
98862306a36Sopenharmony_ci		ret = cm_conn_rep_handler(cm_id_priv, iw_event);
98962306a36Sopenharmony_ci		break;
99062306a36Sopenharmony_ci	case IW_CM_EVENT_ESTABLISHED:
99162306a36Sopenharmony_ci		ret = cm_conn_est_handler(cm_id_priv, iw_event);
99262306a36Sopenharmony_ci		break;
99362306a36Sopenharmony_ci	case IW_CM_EVENT_DISCONNECT:
99462306a36Sopenharmony_ci		cm_disconnect_handler(cm_id_priv, iw_event);
99562306a36Sopenharmony_ci		break;
99662306a36Sopenharmony_ci	case IW_CM_EVENT_CLOSE:
99762306a36Sopenharmony_ci		ret = cm_close_handler(cm_id_priv, iw_event);
99862306a36Sopenharmony_ci		break;
99962306a36Sopenharmony_ci	default:
100062306a36Sopenharmony_ci		BUG();
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return ret;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci/*
100762306a36Sopenharmony_ci * Process events on the work_list for the cm_id. If the callback
100862306a36Sopenharmony_ci * function requests that the cm_id be deleted, a flag is set in the
100962306a36Sopenharmony_ci * cm_id flags to indicate that when the last reference is
101062306a36Sopenharmony_ci * removed, the cm_id is to be destroyed. This is necessary to
101162306a36Sopenharmony_ci * distinguish between an object that will be destroyed by the app
101262306a36Sopenharmony_ci * thread asleep on the destroy_comp list vs. an object destroyed
101362306a36Sopenharmony_ci * here synchronously when the last reference is removed.
101462306a36Sopenharmony_ci */
101562306a36Sopenharmony_cistatic void cm_work_handler(struct work_struct *_work)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct iwcm_work *work = container_of(_work, struct iwcm_work, work);
101862306a36Sopenharmony_ci	struct iw_cm_event levent;
101962306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv = work->cm_id;
102062306a36Sopenharmony_ci	unsigned long flags;
102162306a36Sopenharmony_ci	int empty;
102262306a36Sopenharmony_ci	int ret = 0;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
102562306a36Sopenharmony_ci	empty = list_empty(&cm_id_priv->work_list);
102662306a36Sopenharmony_ci	while (!empty) {
102762306a36Sopenharmony_ci		work = list_entry(cm_id_priv->work_list.next,
102862306a36Sopenharmony_ci				  struct iwcm_work, list);
102962306a36Sopenharmony_ci		list_del_init(&work->list);
103062306a36Sopenharmony_ci		empty = list_empty(&cm_id_priv->work_list);
103162306a36Sopenharmony_ci		levent = work->event;
103262306a36Sopenharmony_ci		put_work(work);
103362306a36Sopenharmony_ci		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		if (!test_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags)) {
103662306a36Sopenharmony_ci			ret = process_event(cm_id_priv, &levent);
103762306a36Sopenharmony_ci			if (ret)
103862306a36Sopenharmony_ci				destroy_cm_id(&cm_id_priv->id);
103962306a36Sopenharmony_ci		} else
104062306a36Sopenharmony_ci			pr_debug("dropping event %d\n", levent.event);
104162306a36Sopenharmony_ci		if (iwcm_deref_id(cm_id_priv))
104262306a36Sopenharmony_ci			return;
104362306a36Sopenharmony_ci		if (empty)
104462306a36Sopenharmony_ci			return;
104562306a36Sopenharmony_ci		spin_lock_irqsave(&cm_id_priv->lock, flags);
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci/*
105162306a36Sopenharmony_ci * This function is called on interrupt context. Schedule events on
105262306a36Sopenharmony_ci * the iwcm_wq thread to allow callback functions to downcall into
105362306a36Sopenharmony_ci * the CM and/or block.  Events are queued to a per-CM_ID
105462306a36Sopenharmony_ci * work_list. If this is the first event on the work_list, the work
105562306a36Sopenharmony_ci * element is also queued on the iwcm_wq thread.
105662306a36Sopenharmony_ci *
105762306a36Sopenharmony_ci * Each event holds a reference on the cm_id. Until the last posted
105862306a36Sopenharmony_ci * event has been delivered and processed, the cm_id cannot be
105962306a36Sopenharmony_ci * deleted.
106062306a36Sopenharmony_ci *
106162306a36Sopenharmony_ci * Returns:
106262306a36Sopenharmony_ci * 	      0	- the event was handled.
106362306a36Sopenharmony_ci *	-ENOMEM	- the event was not handled due to lack of resources.
106462306a36Sopenharmony_ci */
106562306a36Sopenharmony_cistatic int cm_event_handler(struct iw_cm_id *cm_id,
106662306a36Sopenharmony_ci			     struct iw_cm_event *iw_event)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct iwcm_work *work;
106962306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
107062306a36Sopenharmony_ci	unsigned long flags;
107162306a36Sopenharmony_ci	int ret = 0;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
107662306a36Sopenharmony_ci	work = get_work(cm_id_priv);
107762306a36Sopenharmony_ci	if (!work) {
107862306a36Sopenharmony_ci		ret = -ENOMEM;
107962306a36Sopenharmony_ci		goto out;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	INIT_WORK(&work->work, cm_work_handler);
108362306a36Sopenharmony_ci	work->cm_id = cm_id_priv;
108462306a36Sopenharmony_ci	work->event = *iw_event;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if ((work->event.event == IW_CM_EVENT_CONNECT_REQUEST ||
108762306a36Sopenharmony_ci	     work->event.event == IW_CM_EVENT_CONNECT_REPLY) &&
108862306a36Sopenharmony_ci	    work->event.private_data_len) {
108962306a36Sopenharmony_ci		ret = copy_private_data(&work->event);
109062306a36Sopenharmony_ci		if (ret) {
109162306a36Sopenharmony_ci			put_work(work);
109262306a36Sopenharmony_ci			goto out;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	refcount_inc(&cm_id_priv->refcount);
109762306a36Sopenharmony_ci	if (list_empty(&cm_id_priv->work_list)) {
109862306a36Sopenharmony_ci		list_add_tail(&work->list, &cm_id_priv->work_list);
109962306a36Sopenharmony_ci		queue_work(iwcm_wq, &work->work);
110062306a36Sopenharmony_ci	} else
110162306a36Sopenharmony_ci		list_add_tail(&work->list, &cm_id_priv->work_list);
110262306a36Sopenharmony_ciout:
110362306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
110462306a36Sopenharmony_ci	return ret;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv,
110862306a36Sopenharmony_ci				  struct ib_qp_attr *qp_attr,
110962306a36Sopenharmony_ci				  int *qp_attr_mask)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	unsigned long flags;
111262306a36Sopenharmony_ci	int ret;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
111562306a36Sopenharmony_ci	switch (cm_id_priv->state) {
111662306a36Sopenharmony_ci	case IW_CM_STATE_IDLE:
111762306a36Sopenharmony_ci	case IW_CM_STATE_CONN_SENT:
111862306a36Sopenharmony_ci	case IW_CM_STATE_CONN_RECV:
111962306a36Sopenharmony_ci	case IW_CM_STATE_ESTABLISHED:
112062306a36Sopenharmony_ci		*qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
112162306a36Sopenharmony_ci		qp_attr->qp_access_flags = IB_ACCESS_REMOTE_WRITE|
112262306a36Sopenharmony_ci					   IB_ACCESS_REMOTE_READ;
112362306a36Sopenharmony_ci		ret = 0;
112462306a36Sopenharmony_ci		break;
112562306a36Sopenharmony_ci	default:
112662306a36Sopenharmony_ci		ret = -EINVAL;
112762306a36Sopenharmony_ci		break;
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
113062306a36Sopenharmony_ci	return ret;
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cistatic int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv,
113462306a36Sopenharmony_ci				  struct ib_qp_attr *qp_attr,
113562306a36Sopenharmony_ci				  int *qp_attr_mask)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	unsigned long flags;
113862306a36Sopenharmony_ci	int ret;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	spin_lock_irqsave(&cm_id_priv->lock, flags);
114162306a36Sopenharmony_ci	switch (cm_id_priv->state) {
114262306a36Sopenharmony_ci	case IW_CM_STATE_IDLE:
114362306a36Sopenharmony_ci	case IW_CM_STATE_CONN_SENT:
114462306a36Sopenharmony_ci	case IW_CM_STATE_CONN_RECV:
114562306a36Sopenharmony_ci	case IW_CM_STATE_ESTABLISHED:
114662306a36Sopenharmony_ci		*qp_attr_mask = 0;
114762306a36Sopenharmony_ci		ret = 0;
114862306a36Sopenharmony_ci		break;
114962306a36Sopenharmony_ci	default:
115062306a36Sopenharmony_ci		ret = -EINVAL;
115162306a36Sopenharmony_ci		break;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
115462306a36Sopenharmony_ci	return ret;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ciint iw_cm_init_qp_attr(struct iw_cm_id *cm_id,
115862306a36Sopenharmony_ci		       struct ib_qp_attr *qp_attr,
115962306a36Sopenharmony_ci		       int *qp_attr_mask)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct iwcm_id_private *cm_id_priv;
116262306a36Sopenharmony_ci	int ret;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
116562306a36Sopenharmony_ci	switch (qp_attr->qp_state) {
116662306a36Sopenharmony_ci	case IB_QPS_INIT:
116762306a36Sopenharmony_ci	case IB_QPS_RTR:
116862306a36Sopenharmony_ci		ret = iwcm_init_qp_init_attr(cm_id_priv,
116962306a36Sopenharmony_ci					     qp_attr, qp_attr_mask);
117062306a36Sopenharmony_ci		break;
117162306a36Sopenharmony_ci	case IB_QPS_RTS:
117262306a36Sopenharmony_ci		ret = iwcm_init_qp_rts_attr(cm_id_priv,
117362306a36Sopenharmony_ci					    qp_attr, qp_attr_mask);
117462306a36Sopenharmony_ci		break;
117562306a36Sopenharmony_ci	default:
117662306a36Sopenharmony_ci		ret = -EINVAL;
117762306a36Sopenharmony_ci		break;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci	return ret;
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ciEXPORT_SYMBOL(iw_cm_init_qp_attr);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int __init iw_cm_init(void)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	int ret;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	ret = iwpm_init(RDMA_NL_IWCM);
118862306a36Sopenharmony_ci	if (ret)
118962306a36Sopenharmony_ci		return ret;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	iwcm_wq = alloc_ordered_workqueue("iw_cm_wq", 0);
119262306a36Sopenharmony_ci	if (!iwcm_wq)
119362306a36Sopenharmony_ci		goto err_alloc;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	iwcm_ctl_table_hdr = register_net_sysctl(&init_net, "net/iw_cm",
119662306a36Sopenharmony_ci						 iwcm_ctl_table);
119762306a36Sopenharmony_ci	if (!iwcm_ctl_table_hdr) {
119862306a36Sopenharmony_ci		pr_err("iw_cm: couldn't register sysctl paths\n");
119962306a36Sopenharmony_ci		goto err_sysctl;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	rdma_nl_register(RDMA_NL_IWCM, iwcm_nl_cb_table);
120362306a36Sopenharmony_ci	return 0;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cierr_sysctl:
120662306a36Sopenharmony_ci	destroy_workqueue(iwcm_wq);
120762306a36Sopenharmony_cierr_alloc:
120862306a36Sopenharmony_ci	iwpm_exit(RDMA_NL_IWCM);
120962306a36Sopenharmony_ci	return -ENOMEM;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic void __exit iw_cm_cleanup(void)
121362306a36Sopenharmony_ci{
121462306a36Sopenharmony_ci	rdma_nl_unregister(RDMA_NL_IWCM);
121562306a36Sopenharmony_ci	unregister_net_sysctl_table(iwcm_ctl_table_hdr);
121662306a36Sopenharmony_ci	destroy_workqueue(iwcm_wq);
121762306a36Sopenharmony_ci	iwpm_exit(RDMA_NL_IWCM);
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ciMODULE_ALIAS_RDMA_NETLINK(RDMA_NL_IWCM, 2);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_cimodule_init(iw_cm_init);
122362306a36Sopenharmony_cimodule_exit(iw_cm_cleanup);
1224