109938b21SHans Petter Selasky /*-
209938b21SHans Petter Selasky  * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
309938b21SHans Petter Selasky  *
49f715dc1SHans Petter Selasky  * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
59f715dc1SHans Petter Selasky  * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
69f715dc1SHans Petter Selasky  * Copyright (c) 2004, 2005 Voltaire Corporation.  All rights reserved.
79f715dc1SHans Petter Selasky  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
89f715dc1SHans Petter Selasky  * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
99f715dc1SHans Petter Selasky  * Copyright (c) 2005 Network Appliance, Inc. All rights reserved.
109f715dc1SHans Petter Selasky  *
119f715dc1SHans Petter Selasky  * This software is available to you under a choice of one of two
129f715dc1SHans Petter Selasky  * licenses.  You may choose to be licensed under the terms of the GNU
139f715dc1SHans Petter Selasky  * General Public License (GPL) Version 2, available from the file
149f715dc1SHans Petter Selasky  * COPYING in the main directory of this source tree, or the
159f715dc1SHans Petter Selasky  * OpenIB.org BSD license below:
169f715dc1SHans Petter Selasky  *
179f715dc1SHans Petter Selasky  *     Redistribution and use in source and binary forms, with or
189f715dc1SHans Petter Selasky  *     without modification, are permitted provided that the following
199f715dc1SHans Petter Selasky  *     conditions are met:
209f715dc1SHans Petter Selasky  *
219f715dc1SHans Petter Selasky  *      - Redistributions of source code must retain the above
229f715dc1SHans Petter Selasky  *        copyright notice, this list of conditions and the following
239f715dc1SHans Petter Selasky  *        disclaimer.
249f715dc1SHans Petter Selasky  *
259f715dc1SHans Petter Selasky  *      - Redistributions in binary form must reproduce the above
269f715dc1SHans Petter Selasky  *        copyright notice, this list of conditions and the following
279f715dc1SHans Petter Selasky  *        disclaimer in the documentation and/or other materials
289f715dc1SHans Petter Selasky  *        provided with the distribution.
299f715dc1SHans Petter Selasky  *
309f715dc1SHans Petter Selasky  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
319f715dc1SHans Petter Selasky  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
329f715dc1SHans Petter Selasky  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
339f715dc1SHans Petter Selasky  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
349f715dc1SHans Petter Selasky  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
359f715dc1SHans Petter Selasky  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
369f715dc1SHans Petter Selasky  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
379f715dc1SHans Petter Selasky  * SOFTWARE.
389f715dc1SHans Petter Selasky  */
3909938b21SHans Petter Selasky 
40cda1e10cSHans Petter Selasky #include <sys/cdefs.h>
419f715dc1SHans Petter Selasky #include <linux/dma-mapping.h>
429f715dc1SHans Petter Selasky #include <linux/err.h>
439f715dc1SHans Petter Selasky #include <linux/idr.h>
449f715dc1SHans Petter Selasky #include <linux/interrupt.h>
459f715dc1SHans Petter Selasky #include <linux/rbtree.h>
469f715dc1SHans Petter Selasky #include <linux/sched.h>
479f715dc1SHans Petter Selasky #include <linux/spinlock.h>
489f715dc1SHans Petter Selasky #include <linux/workqueue.h>
499f715dc1SHans Petter Selasky #include <linux/completion.h>
509f715dc1SHans Petter Selasky #include <linux/slab.h>
519f715dc1SHans Petter Selasky #include <linux/module.h>
52bca9d05fSHans Petter Selasky #include <linux/wait.h>
539f715dc1SHans Petter Selasky 
549f715dc1SHans Petter Selasky #include <rdma/iw_cm.h>
559f715dc1SHans Petter Selasky #include <rdma/ib_addr.h>
569f715dc1SHans Petter Selasky #include <rdma/iw_portmap.h>
579f715dc1SHans Petter Selasky 
589f715dc1SHans Petter Selasky #include "iwcm.h"
599f715dc1SHans Petter Selasky 
609f715dc1SHans Petter Selasky MODULE_AUTHOR("Tom Tucker");
619f715dc1SHans Petter Selasky MODULE_DESCRIPTION("iWARP CM");
629f715dc1SHans Petter Selasky MODULE_LICENSE("Dual BSD/GPL");
639f715dc1SHans Petter Selasky 
64e25bcf8dSHans Petter Selasky static const char * const iwcm_rej_reason_strs[] = {
65e25bcf8dSHans Petter Selasky 	[ECONNRESET]			= "reset by remote host",
66e25bcf8dSHans Petter Selasky 	[ECONNREFUSED]			= "refused by remote application",
67e25bcf8dSHans Petter Selasky 	[ETIMEDOUT]			= "setup timeout",
68e25bcf8dSHans Petter Selasky };
69e25bcf8dSHans Petter Selasky 
iwcm_reject_msg(int reason)70e25bcf8dSHans Petter Selasky const char *__attribute_const__ iwcm_reject_msg(int reason)
71e25bcf8dSHans Petter Selasky {
72e25bcf8dSHans Petter Selasky 	size_t index;
73e25bcf8dSHans Petter Selasky 
74e25bcf8dSHans Petter Selasky 	/* iWARP uses negative errnos */
75e25bcf8dSHans Petter Selasky 	index = -reason;
76e25bcf8dSHans Petter Selasky 
77e25bcf8dSHans Petter Selasky 	if (index < ARRAY_SIZE(iwcm_rej_reason_strs) &&
78e25bcf8dSHans Petter Selasky 	    iwcm_rej_reason_strs[index])
79e25bcf8dSHans Petter Selasky 		return iwcm_rej_reason_strs[index];
80e25bcf8dSHans Petter Selasky 	else
81e25bcf8dSHans Petter Selasky 		return "unrecognized reason";
82e25bcf8dSHans Petter Selasky }
83e25bcf8dSHans Petter Selasky EXPORT_SYMBOL(iwcm_reject_msg);
84e25bcf8dSHans Petter Selasky 
859f715dc1SHans Petter Selasky static struct workqueue_struct *iwcm_wq;
869f715dc1SHans Petter Selasky struct iwcm_work {
879f715dc1SHans Petter Selasky 	struct work_struct work;
889f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id;
899f715dc1SHans Petter Selasky 	struct list_head list;
909f715dc1SHans Petter Selasky 	struct iw_cm_event event;
919f715dc1SHans Petter Selasky 	struct list_head free_list;
929f715dc1SHans Petter Selasky };
939f715dc1SHans Petter Selasky 
949f715dc1SHans Petter Selasky static unsigned int default_backlog = 256;
959f715dc1SHans Petter Selasky 
969f715dc1SHans Petter Selasky /*
979f715dc1SHans Petter Selasky  * The following services provide a mechanism for pre-allocating iwcm_work
989f715dc1SHans Petter Selasky  * elements.  The design pre-allocates them  based on the cm_id type:
999f715dc1SHans Petter Selasky  *	LISTENING IDS: 	Get enough elements preallocated to handle the
1009f715dc1SHans Petter Selasky  *			listen backlog.
1019f715dc1SHans Petter Selasky  *	ACTIVE IDS:	4: CONNECT_REPLY, ESTABLISHED, DISCONNECT, CLOSE
1029f715dc1SHans Petter Selasky  *	PASSIVE IDS:	3: ESTABLISHED, DISCONNECT, CLOSE
1039f715dc1SHans Petter Selasky  *
1049f715dc1SHans Petter Selasky  * Allocating them in connect and listen avoids having to deal
1059f715dc1SHans Petter Selasky  * with allocation failures on the event upcall from the provider (which
1069f715dc1SHans Petter Selasky  * is called in the interrupt context).
1079f715dc1SHans Petter Selasky  *
1089f715dc1SHans Petter Selasky  * One exception is when creating the cm_id for incoming connection requests.
1099f715dc1SHans Petter Selasky  * There are two cases:
1109f715dc1SHans Petter Selasky  * 1) in the event upcall, cm_event_handler(), for a listening cm_id.  If
1119f715dc1SHans Petter Selasky  *    the backlog is exceeded, then no more connection request events will
1129f715dc1SHans Petter Selasky  *    be processed.  cm_event_handler() returns -ENOMEM in this case.  Its up
1139f715dc1SHans Petter Selasky  *    to the provider to reject the connection request.
1149f715dc1SHans Petter Selasky  * 2) in the connection request workqueue handler, cm_conn_req_handler().
1159f715dc1SHans Petter Selasky  *    If work elements cannot be allocated for the new connect request cm_id,
1169f715dc1SHans Petter Selasky  *    then IWCM will call the provider reject method.  This is ok since
1179f715dc1SHans Petter Selasky  *    cm_conn_req_handler() runs in the workqueue thread context.
1189f715dc1SHans Petter Selasky  */
1199f715dc1SHans Petter Selasky 
get_work(struct iwcm_id_private * cm_id_priv)1209f715dc1SHans Petter Selasky static struct iwcm_work *get_work(struct iwcm_id_private *cm_id_priv)
1219f715dc1SHans Petter Selasky {
1229f715dc1SHans Petter Selasky 	struct iwcm_work *work;
1239f715dc1SHans Petter Selasky 
1249f715dc1SHans Petter Selasky 	if (list_empty(&cm_id_priv->work_free_list))
1259f715dc1SHans Petter Selasky 		return NULL;
1269f715dc1SHans Petter Selasky 	work = list_entry(cm_id_priv->work_free_list.next, struct iwcm_work,
1279f715dc1SHans Petter Selasky 			  free_list);
1289f715dc1SHans Petter Selasky 	list_del_init(&work->free_list);
1299f715dc1SHans Petter Selasky 	return work;
1309f715dc1SHans Petter Selasky }
1319f715dc1SHans Petter Selasky 
put_work(struct iwcm_work * work)1329f715dc1SHans Petter Selasky static void put_work(struct iwcm_work *work)
1339f715dc1SHans Petter Selasky {
1349f715dc1SHans Petter Selasky 	list_add(&work->free_list, &work->cm_id->work_free_list);
1359f715dc1SHans Petter Selasky }
1369f715dc1SHans Petter Selasky 
dealloc_work_entries(struct iwcm_id_private * cm_id_priv)1379f715dc1SHans Petter Selasky static void dealloc_work_entries(struct iwcm_id_private *cm_id_priv)
1389f715dc1SHans Petter Selasky {
1399f715dc1SHans Petter Selasky 	struct list_head *e, *tmp;
1409f715dc1SHans Petter Selasky 
1419f715dc1SHans Petter Selasky 	list_for_each_safe(e, tmp, &cm_id_priv->work_free_list)
1429f715dc1SHans Petter Selasky 		kfree(list_entry(e, struct iwcm_work, free_list));
1439f715dc1SHans Petter Selasky }
1449f715dc1SHans Petter Selasky 
alloc_work_entries(struct iwcm_id_private * cm_id_priv,int count)1459f715dc1SHans Petter Selasky static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)
1469f715dc1SHans Petter Selasky {
1479f715dc1SHans Petter Selasky 	struct iwcm_work *work;
1489f715dc1SHans Petter Selasky 
1499f715dc1SHans Petter Selasky 	BUG_ON(!list_empty(&cm_id_priv->work_free_list));
1509f715dc1SHans Petter Selasky 	while (count--) {
1519f715dc1SHans Petter Selasky 		work = kmalloc(sizeof(struct iwcm_work), GFP_KERNEL);
1529f715dc1SHans Petter Selasky 		if (!work) {
1539f715dc1SHans Petter Selasky 			dealloc_work_entries(cm_id_priv);
1549f715dc1SHans Petter Selasky 			return -ENOMEM;
1559f715dc1SHans Petter Selasky 		}
1569f715dc1SHans Petter Selasky 		work->cm_id = cm_id_priv;
1579f715dc1SHans Petter Selasky 		INIT_LIST_HEAD(&work->list);
1589f715dc1SHans Petter Selasky 		put_work(work);
1599f715dc1SHans Petter Selasky 	}
1609f715dc1SHans Petter Selasky 	return 0;
1619f715dc1SHans Petter Selasky }
1629f715dc1SHans Petter Selasky 
1639f715dc1SHans Petter Selasky /*
1649f715dc1SHans Petter Selasky  * Save private data from incoming connection requests to
1659f715dc1SHans Petter Selasky  * iw_cm_event, so the low level driver doesn't have to. Adjust
1669f715dc1SHans Petter Selasky  * the event ptr to point to the local copy.
1679f715dc1SHans Petter Selasky  */
copy_private_data(struct iw_cm_event * event)1689f715dc1SHans Petter Selasky static int copy_private_data(struct iw_cm_event *event)
1699f715dc1SHans Petter Selasky {
1709f715dc1SHans Petter Selasky 	void *p;
1719f715dc1SHans Petter Selasky 
1729f715dc1SHans Petter Selasky 	p = kmemdup(event->private_data, event->private_data_len, GFP_ATOMIC);
1739f715dc1SHans Petter Selasky 	if (!p)
1749f715dc1SHans Petter Selasky 		return -ENOMEM;
1759f715dc1SHans Petter Selasky 	event->private_data = p;
1769f715dc1SHans Petter Selasky 	return 0;
1779f715dc1SHans Petter Selasky }
1789f715dc1SHans Petter Selasky 
free_cm_id(struct iwcm_id_private * cm_id_priv)1799f715dc1SHans Petter Selasky static void free_cm_id(struct iwcm_id_private *cm_id_priv)
1809f715dc1SHans Petter Selasky {
1819f715dc1SHans Petter Selasky 	dealloc_work_entries(cm_id_priv);
1829f715dc1SHans Petter Selasky 	kfree(cm_id_priv);
1839f715dc1SHans Petter Selasky }
1849f715dc1SHans Petter Selasky 
1859f715dc1SHans Petter Selasky /*
1869f715dc1SHans Petter Selasky  * Release a reference on cm_id. If the last reference is being
1879f715dc1SHans Petter Selasky  * released, free the cm_id and return 1.
1889f715dc1SHans Petter Selasky  */
iwcm_deref_id(struct iwcm_id_private * cm_id_priv)1899f715dc1SHans Petter Selasky static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv)
1909f715dc1SHans Petter Selasky {
1919f715dc1SHans Petter Selasky 	BUG_ON(atomic_read(&cm_id_priv->refcount)==0);
1929f715dc1SHans Petter Selasky 	if (atomic_dec_and_test(&cm_id_priv->refcount)) {
1939f715dc1SHans Petter Selasky 		BUG_ON(!list_empty(&cm_id_priv->work_list));
1949f715dc1SHans Petter Selasky 		free_cm_id(cm_id_priv);
1959f715dc1SHans Petter Selasky 		return 1;
1969f715dc1SHans Petter Selasky 	}
1979f715dc1SHans Petter Selasky 
1989f715dc1SHans Petter Selasky 	return 0;
1999f715dc1SHans Petter Selasky }
2009f715dc1SHans Petter Selasky 
add_ref(struct iw_cm_id * cm_id)2019f715dc1SHans Petter Selasky static void add_ref(struct iw_cm_id *cm_id)
2029f715dc1SHans Petter Selasky {
2039f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
2049f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
2059f715dc1SHans Petter Selasky 	atomic_inc(&cm_id_priv->refcount);
2069f715dc1SHans Petter Selasky }
2079f715dc1SHans Petter Selasky 
rem_ref(struct iw_cm_id * cm_id)2089f715dc1SHans Petter Selasky static void rem_ref(struct iw_cm_id *cm_id)
2099f715dc1SHans Petter Selasky {
2109f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
2119f715dc1SHans Petter Selasky 
2129f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
2139f715dc1SHans Petter Selasky 
2149f715dc1SHans Petter Selasky 	(void)iwcm_deref_id(cm_id_priv);
2159f715dc1SHans Petter Selasky }
2169f715dc1SHans Petter Selasky 
2179f715dc1SHans Petter Selasky static int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event);
2189f715dc1SHans Petter Selasky 
iw_create_cm_id(struct ib_device * device,iw_cm_handler cm_handler,void * context)2199f715dc1SHans Petter Selasky struct iw_cm_id *iw_create_cm_id(struct ib_device *device,
2209f715dc1SHans Petter Selasky 				 iw_cm_handler cm_handler,
2219f715dc1SHans Petter Selasky 				 void *context)
2229f715dc1SHans Petter Selasky {
2239f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
2249f715dc1SHans Petter Selasky 
2259f715dc1SHans Petter Selasky 	cm_id_priv = kzalloc(sizeof(*cm_id_priv), GFP_KERNEL);
2269f715dc1SHans Petter Selasky 	if (!cm_id_priv)
2279f715dc1SHans Petter Selasky 		return ERR_PTR(-ENOMEM);
2289f715dc1SHans Petter Selasky 
2299f715dc1SHans Petter Selasky 	cm_id_priv->state = IW_CM_STATE_IDLE;
2309f715dc1SHans Petter Selasky 	cm_id_priv->id.device = device;
2319f715dc1SHans Petter Selasky 	cm_id_priv->id.cm_handler = cm_handler;
2329f715dc1SHans Petter Selasky 	cm_id_priv->id.context = context;
2339f715dc1SHans Petter Selasky 	cm_id_priv->id.event_handler = cm_event_handler;
2349f715dc1SHans Petter Selasky 	cm_id_priv->id.add_ref = add_ref;
2359f715dc1SHans Petter Selasky 	cm_id_priv->id.rem_ref = rem_ref;
2369f715dc1SHans Petter Selasky 	spin_lock_init(&cm_id_priv->lock);
2379f715dc1SHans Petter Selasky 	atomic_set(&cm_id_priv->refcount, 1);
2389f715dc1SHans Petter Selasky 	init_waitqueue_head(&cm_id_priv->connect_wait);
2399f715dc1SHans Petter Selasky 	init_completion(&cm_id_priv->destroy_comp);
2409f715dc1SHans Petter Selasky 	INIT_LIST_HEAD(&cm_id_priv->work_list);
2419f715dc1SHans Petter Selasky 	INIT_LIST_HEAD(&cm_id_priv->work_free_list);
2429f715dc1SHans Petter Selasky 
2439f715dc1SHans Petter Selasky 	return &cm_id_priv->id;
2449f715dc1SHans Petter Selasky }
2459f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_create_cm_id);
2469f715dc1SHans Petter Selasky 
2479f715dc1SHans Petter Selasky 
iwcm_modify_qp_err(struct ib_qp * qp)2489f715dc1SHans Petter Selasky static int iwcm_modify_qp_err(struct ib_qp *qp)
2499f715dc1SHans Petter Selasky {
2509f715dc1SHans Petter Selasky 	struct ib_qp_attr qp_attr;
2519f715dc1SHans Petter Selasky 
2529f715dc1SHans Petter Selasky 	if (!qp)
2539f715dc1SHans Petter Selasky 		return -EINVAL;
2549f715dc1SHans Petter Selasky 
2559f715dc1SHans Petter Selasky 	qp_attr.qp_state = IB_QPS_ERR;
2569f715dc1SHans Petter Selasky 	return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
2579f715dc1SHans Petter Selasky }
2589f715dc1SHans Petter Selasky 
2599f715dc1SHans Petter Selasky /*
2609f715dc1SHans Petter Selasky  * This is really the RDMAC CLOSING state. It is most similar to the
2619f715dc1SHans Petter Selasky  * IB SQD QP state.
2629f715dc1SHans Petter Selasky  */
iwcm_modify_qp_sqd(struct ib_qp * qp)2639f715dc1SHans Petter Selasky static int iwcm_modify_qp_sqd(struct ib_qp *qp)
2649f715dc1SHans Petter Selasky {
2659f715dc1SHans Petter Selasky 	struct ib_qp_attr qp_attr;
2669f715dc1SHans Petter Selasky 
2679f715dc1SHans Petter Selasky 	BUG_ON(qp == NULL);
2689f715dc1SHans Petter Selasky 	qp_attr.qp_state = IB_QPS_SQD;
2699f715dc1SHans Petter Selasky 	return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
2709f715dc1SHans Petter Selasky }
2719f715dc1SHans Petter Selasky 
2729f715dc1SHans Petter Selasky /*
2739f715dc1SHans Petter Selasky  * CM_ID <-- CLOSING
2749f715dc1SHans Petter Selasky  *
2759f715dc1SHans Petter Selasky  * Block if a passive or active connection is currently being processed. Then
2769f715dc1SHans Petter Selasky  * process the event as follows:
2779f715dc1SHans Petter Selasky  * - If we are ESTABLISHED, move to CLOSING and modify the QP state
2789f715dc1SHans Petter Selasky  *   based on the abrupt flag
2799f715dc1SHans Petter Selasky  * - If the connection is already in the CLOSING or IDLE state, the peer is
2809f715dc1SHans Petter Selasky  *   disconnecting concurrently with us and we've already seen the
2819f715dc1SHans Petter Selasky  *   DISCONNECT event -- ignore the request and return 0
2829f715dc1SHans Petter Selasky  * - Disconnect on a listening endpoint returns -EINVAL
2839f715dc1SHans Petter Selasky  */
iw_cm_disconnect(struct iw_cm_id * cm_id,int abrupt)2849f715dc1SHans Petter Selasky int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt)
2859f715dc1SHans Petter Selasky {
2869f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
2879f715dc1SHans Petter Selasky 	unsigned long flags;
2889f715dc1SHans Petter Selasky 	int ret = 0;
2899f715dc1SHans Petter Selasky 	struct ib_qp *qp = NULL;
2909f715dc1SHans Petter Selasky 
2919f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
2929f715dc1SHans Petter Selasky 	/* Wait if we're currently in a connect or accept downcall */
2939f715dc1SHans Petter Selasky 	wait_event(cm_id_priv->connect_wait,
2949f715dc1SHans Petter Selasky 		   !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags));
2959f715dc1SHans Petter Selasky 
2969f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
2979f715dc1SHans Petter Selasky 	switch (cm_id_priv->state) {
2989f715dc1SHans Petter Selasky 	case IW_CM_STATE_ESTABLISHED:
2999f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_CLOSING;
3009f715dc1SHans Petter Selasky 
3019f715dc1SHans Petter Selasky 		/* QP could be <nul> for user-mode client */
3029f715dc1SHans Petter Selasky 		if (cm_id_priv->qp)
3039f715dc1SHans Petter Selasky 			qp = cm_id_priv->qp;
3049f715dc1SHans Petter Selasky 		else
3059f715dc1SHans Petter Selasky 			ret = -EINVAL;
3069f715dc1SHans Petter Selasky 		break;
3079f715dc1SHans Petter Selasky 	case IW_CM_STATE_LISTEN:
3089f715dc1SHans Petter Selasky 		ret = -EINVAL;
3099f715dc1SHans Petter Selasky 		break;
3109f715dc1SHans Petter Selasky 	case IW_CM_STATE_CLOSING:
3119f715dc1SHans Petter Selasky 		/* remote peer closed first */
3129f715dc1SHans Petter Selasky 	case IW_CM_STATE_IDLE:
3139f715dc1SHans Petter Selasky 		/* accept or connect returned !0 */
3149f715dc1SHans Petter Selasky 		break;
3159f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_RECV:
3169f715dc1SHans Petter Selasky 		/*
3179f715dc1SHans Petter Selasky 		 * App called disconnect before/without calling accept after
3189f715dc1SHans Petter Selasky 		 * connect_request event delivered.
3199f715dc1SHans Petter Selasky 		 */
3209f715dc1SHans Petter Selasky 		break;
3219f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_SENT:
3229f715dc1SHans Petter Selasky 		/* Can only get here if wait above fails */
3239f715dc1SHans Petter Selasky 	default:
3249f715dc1SHans Petter Selasky 		BUG();
3259f715dc1SHans Petter Selasky 	}
3269f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
3279f715dc1SHans Petter Selasky 
3289f715dc1SHans Petter Selasky 	if (qp) {
3299f715dc1SHans Petter Selasky 		if (abrupt)
3304b9b52a1SSlava Shwartsman 			(void) iwcm_modify_qp_err(qp);
3319f715dc1SHans Petter Selasky 		else
3324b9b52a1SSlava Shwartsman 			(void) iwcm_modify_qp_sqd(qp);
3339f715dc1SHans Petter Selasky 
3349f715dc1SHans Petter Selasky 		/*
3359f715dc1SHans Petter Selasky 		 * If both sides are disconnecting the QP could
3369f715dc1SHans Petter Selasky 		 * already be in ERR or SQD states
3379f715dc1SHans Petter Selasky 		 */
3389f715dc1SHans Petter Selasky 		ret = 0;
3399f715dc1SHans Petter Selasky 	}
3409f715dc1SHans Petter Selasky 
3419f715dc1SHans Petter Selasky 	return ret;
3429f715dc1SHans Petter Selasky }
3439f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_cm_disconnect);
3449f715dc1SHans Petter Selasky 
3459f715dc1SHans Petter Selasky /*
3469f715dc1SHans Petter Selasky  * CM_ID <-- DESTROYING
3479f715dc1SHans Petter Selasky  *
3489f715dc1SHans Petter Selasky  * Clean up all resources associated with the connection and release
3499f715dc1SHans Petter Selasky  * the initial reference taken by iw_create_cm_id.
3509f715dc1SHans Petter Selasky  */
destroy_cm_id(struct iw_cm_id * cm_id)3519f715dc1SHans Petter Selasky static void destroy_cm_id(struct iw_cm_id *cm_id)
3529f715dc1SHans Petter Selasky {
3539f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
3549f715dc1SHans Petter Selasky 	unsigned long flags;
3559f715dc1SHans Petter Selasky 
3569f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
3579f715dc1SHans Petter Selasky 	/*
3589f715dc1SHans Petter Selasky 	 * Wait if we're currently in a connect or accept downcall. A
3599f715dc1SHans Petter Selasky 	 * listening endpoint should never block here.
3609f715dc1SHans Petter Selasky 	 */
3619f715dc1SHans Petter Selasky 	wait_event(cm_id_priv->connect_wait,
3629f715dc1SHans Petter Selasky 		   !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags));
3639f715dc1SHans Petter Selasky 
3649f715dc1SHans Petter Selasky 	/*
3659f715dc1SHans Petter Selasky 	 * Since we're deleting the cm_id, drop any events that
3669f715dc1SHans Petter Selasky 	 * might arrive before the last dereference.
3679f715dc1SHans Petter Selasky 	 */
3689f715dc1SHans Petter Selasky 	set_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags);
3699f715dc1SHans Petter Selasky 
3709f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
3719f715dc1SHans Petter Selasky 	switch (cm_id_priv->state) {
3729f715dc1SHans Petter Selasky 	case IW_CM_STATE_LISTEN:
3739f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_DESTROYING;
3749f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
3759f715dc1SHans Petter Selasky 		/* destroy the listening endpoint */
3769f715dc1SHans Petter Selasky 		cm_id->device->iwcm->destroy_listen(cm_id);
3779f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
3789f715dc1SHans Petter Selasky 		break;
3799f715dc1SHans Petter Selasky 	case IW_CM_STATE_ESTABLISHED:
3809f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_DESTROYING;
3819f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
3829f715dc1SHans Petter Selasky 		/* Abrupt close of the connection */
3839f715dc1SHans Petter Selasky 		(void)iwcm_modify_qp_err(cm_id_priv->qp);
3849f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
3859f715dc1SHans Petter Selasky 		break;
3869f715dc1SHans Petter Selasky 	case IW_CM_STATE_IDLE:
3879f715dc1SHans Petter Selasky 	case IW_CM_STATE_CLOSING:
3889f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_DESTROYING;
3899f715dc1SHans Petter Selasky 		break;
3909f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_RECV:
3919f715dc1SHans Petter Selasky 		/*
3929f715dc1SHans Petter Selasky 		 * App called destroy before/without calling accept after
3939f715dc1SHans Petter Selasky 		 * receiving connection request event notification or
3949f715dc1SHans Petter Selasky 		 * returned non zero from the event callback function.
3959f715dc1SHans Petter Selasky 		 * In either case, must tell the provider to reject.
3969f715dc1SHans Petter Selasky 		 */
3979f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_DESTROYING;
3989f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
3999f715dc1SHans Petter Selasky 		cm_id->device->iwcm->reject(cm_id, NULL, 0);
4009f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
4019f715dc1SHans Petter Selasky 		break;
4029f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_SENT:
4039f715dc1SHans Petter Selasky 	case IW_CM_STATE_DESTROYING:
4049f715dc1SHans Petter Selasky 	default:
4059f715dc1SHans Petter Selasky 		BUG();
4069f715dc1SHans Petter Selasky 		break;
4079f715dc1SHans Petter Selasky 	}
4089f715dc1SHans Petter Selasky 	if (cm_id_priv->qp) {
4099f715dc1SHans Petter Selasky 		cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp);
4109f715dc1SHans Petter Selasky 		cm_id_priv->qp = NULL;
4119f715dc1SHans Petter Selasky 	}
4129f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
4139f715dc1SHans Petter Selasky 
4149f715dc1SHans Petter Selasky 	(void)iwcm_deref_id(cm_id_priv);
4159f715dc1SHans Petter Selasky }
4169f715dc1SHans Petter Selasky 
4179f715dc1SHans Petter Selasky /*
4189f715dc1SHans Petter Selasky  * This function is only called by the application thread and cannot
4199f715dc1SHans Petter Selasky  * be called by the event thread. The function will wait for all
4209f715dc1SHans Petter Selasky  * references to be released on the cm_id and then kfree the cm_id
4219f715dc1SHans Petter Selasky  * object.
4229f715dc1SHans Petter Selasky  */
iw_destroy_cm_id(struct iw_cm_id * cm_id)4239f715dc1SHans Petter Selasky void iw_destroy_cm_id(struct iw_cm_id *cm_id)
4249f715dc1SHans Petter Selasky {
4259f715dc1SHans Petter Selasky 	destroy_cm_id(cm_id);
4269f715dc1SHans Petter Selasky }
4279f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_destroy_cm_id);
4289f715dc1SHans Petter Selasky 
4299f715dc1SHans Petter Selasky /**
4309f715dc1SHans Petter Selasky  * iw_cm_map - Use portmapper to map the ports
4319f715dc1SHans Petter Selasky  * @cm_id: connection manager pointer
4329f715dc1SHans Petter Selasky  * @active: Indicates the active side when true
4339f715dc1SHans Petter Selasky  * returns nonzero for error only if iwpm_create_mapinfo() fails
4349f715dc1SHans Petter Selasky  *
4359f715dc1SHans Petter Selasky  * Tries to add a mapping for a port using the Portmapper. If
4369f715dc1SHans Petter Selasky  * successful in mapping the IP/Port it will check the remote
4379f715dc1SHans Petter Selasky  * mapped IP address for a wildcard IP address and replace the
4389f715dc1SHans Petter Selasky  * zero IP address with the remote_addr.
4399f715dc1SHans Petter Selasky  */
iw_cm_map(struct iw_cm_id * cm_id,bool active)4409f715dc1SHans Petter Selasky static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
4419f715dc1SHans Petter Selasky {
4429f715dc1SHans Petter Selasky 	cm_id->m_local_addr = cm_id->local_addr;
4439f715dc1SHans Petter Selasky 	cm_id->m_remote_addr = cm_id->remote_addr;
4449f715dc1SHans Petter Selasky 
4459f715dc1SHans Petter Selasky 	return 0;
4469f715dc1SHans Petter Selasky }
4479f715dc1SHans Petter Selasky 
4489f715dc1SHans Petter Selasky /*
4499f715dc1SHans Petter Selasky  * CM_ID <-- LISTEN
4509f715dc1SHans Petter Selasky  *
4519f715dc1SHans Petter Selasky  * Start listening for connect requests. Generates one CONNECT_REQUEST
4529f715dc1SHans Petter Selasky  * event for each inbound connect request.
4539f715dc1SHans Petter Selasky  */
iw_cm_listen(struct iw_cm_id * cm_id,int backlog)4549f715dc1SHans Petter Selasky int iw_cm_listen(struct iw_cm_id *cm_id, int backlog)
4559f715dc1SHans Petter Selasky {
4569f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
4579f715dc1SHans Petter Selasky 	unsigned long flags;
4589f715dc1SHans Petter Selasky 	int ret;
4599f715dc1SHans Petter Selasky 
4609f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
4619f715dc1SHans Petter Selasky 
4629f715dc1SHans Petter Selasky 	if (!backlog)
4639f715dc1SHans Petter Selasky 		backlog = default_backlog;
4649f715dc1SHans Petter Selasky 
4659f715dc1SHans Petter Selasky 	ret = alloc_work_entries(cm_id_priv, backlog);
4669f715dc1SHans Petter Selasky 	if (ret)
4679f715dc1SHans Petter Selasky 		return ret;
4689f715dc1SHans Petter Selasky 
4699f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
4709f715dc1SHans Petter Selasky 	switch (cm_id_priv->state) {
4719f715dc1SHans Petter Selasky 	case IW_CM_STATE_IDLE:
4729f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_LISTEN;
4739f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
4749f715dc1SHans Petter Selasky 		ret = iw_cm_map(cm_id, false);
4759f715dc1SHans Petter Selasky 		if (!ret)
4769f715dc1SHans Petter Selasky 			ret = cm_id->device->iwcm->create_listen(cm_id, backlog);
4779f715dc1SHans Petter Selasky 		if (ret)
4789f715dc1SHans Petter Selasky 			cm_id_priv->state = IW_CM_STATE_IDLE;
4799f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
4809f715dc1SHans Petter Selasky 		break;
4819f715dc1SHans Petter Selasky 	default:
4829f715dc1SHans Petter Selasky 		ret = -EINVAL;
4839f715dc1SHans Petter Selasky 	}
4849f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
4859f715dc1SHans Petter Selasky 
4869f715dc1SHans Petter Selasky 	return ret;
4879f715dc1SHans Petter Selasky }
4889f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_cm_listen);
4899f715dc1SHans Petter Selasky 
4909f715dc1SHans Petter Selasky /*
4919f715dc1SHans Petter Selasky  * CM_ID <-- IDLE
4929f715dc1SHans Petter Selasky  *
4939f715dc1SHans Petter Selasky  * Rejects an inbound connection request. No events are generated.
4949f715dc1SHans Petter Selasky  */
iw_cm_reject(struct iw_cm_id * cm_id,const void * private_data,u8 private_data_len)4959f715dc1SHans Petter Selasky int iw_cm_reject(struct iw_cm_id *cm_id,
4969f715dc1SHans Petter Selasky 		 const void *private_data,
4979f715dc1SHans Petter Selasky 		 u8 private_data_len)
4989f715dc1SHans Petter Selasky {
4999f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
5009f715dc1SHans Petter Selasky 	unsigned long flags;
5019f715dc1SHans Petter Selasky 	int ret;
5029f715dc1SHans Petter Selasky 
5039f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
5049f715dc1SHans Petter Selasky 	set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5059f715dc1SHans Petter Selasky 
5069f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
5079f715dc1SHans Petter Selasky 	if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
5089f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
5099f715dc1SHans Petter Selasky 		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5109f715dc1SHans Petter Selasky 		wake_up_all(&cm_id_priv->connect_wait);
5119f715dc1SHans Petter Selasky 		return -EINVAL;
5129f715dc1SHans Petter Selasky 	}
5139f715dc1SHans Petter Selasky 	cm_id_priv->state = IW_CM_STATE_IDLE;
5149f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
5159f715dc1SHans Petter Selasky 
5169f715dc1SHans Petter Selasky 	ret = cm_id->device->iwcm->reject(cm_id, private_data,
5179f715dc1SHans Petter Selasky 					  private_data_len);
5189f715dc1SHans Petter Selasky 
5199f715dc1SHans Petter Selasky 	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5209f715dc1SHans Petter Selasky 	wake_up_all(&cm_id_priv->connect_wait);
5219f715dc1SHans Petter Selasky 
5229f715dc1SHans Petter Selasky 	return ret;
5239f715dc1SHans Petter Selasky }
5249f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_cm_reject);
5259f715dc1SHans Petter Selasky 
5269f715dc1SHans Petter Selasky /*
5279f715dc1SHans Petter Selasky  * CM_ID <-- ESTABLISHED
5289f715dc1SHans Petter Selasky  *
5299f715dc1SHans Petter Selasky  * Accepts an inbound connection request and generates an ESTABLISHED
5309f715dc1SHans Petter Selasky  * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block
5319f715dc1SHans Petter Selasky  * until the ESTABLISHED event is received from the provider.
5329f715dc1SHans Petter Selasky  */
iw_cm_accept(struct iw_cm_id * cm_id,struct iw_cm_conn_param * iw_param)5339f715dc1SHans Petter Selasky int iw_cm_accept(struct iw_cm_id *cm_id,
5349f715dc1SHans Petter Selasky 		 struct iw_cm_conn_param *iw_param)
5359f715dc1SHans Petter Selasky {
5369f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
5379f715dc1SHans Petter Selasky 	struct ib_qp *qp;
5389f715dc1SHans Petter Selasky 	unsigned long flags;
5399f715dc1SHans Petter Selasky 	int ret;
5409f715dc1SHans Petter Selasky 
5419f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
5429f715dc1SHans Petter Selasky 	set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5439f715dc1SHans Petter Selasky 
5449f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
5459f715dc1SHans Petter Selasky 	if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
5469f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
5479f715dc1SHans Petter Selasky 		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5489f715dc1SHans Petter Selasky 		wake_up_all(&cm_id_priv->connect_wait);
5499f715dc1SHans Petter Selasky 		return -EINVAL;
5509f715dc1SHans Petter Selasky 	}
5519f715dc1SHans Petter Selasky 	/* Get the ib_qp given the QPN */
5529f715dc1SHans Petter Selasky 	qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn);
5539f715dc1SHans Petter Selasky 	if (!qp) {
5549f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
5559f715dc1SHans Petter Selasky 		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5569f715dc1SHans Petter Selasky 		wake_up_all(&cm_id_priv->connect_wait);
5579f715dc1SHans Petter Selasky 		return -EINVAL;
5589f715dc1SHans Petter Selasky 	}
5599f715dc1SHans Petter Selasky 	cm_id->device->iwcm->add_ref(qp);
5609f715dc1SHans Petter Selasky 	cm_id_priv->qp = qp;
5619f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
5629f715dc1SHans Petter Selasky 
5639f715dc1SHans Petter Selasky 	ret = cm_id->device->iwcm->accept(cm_id, iw_param);
5649f715dc1SHans Petter Selasky 	if (ret) {
5659f715dc1SHans Petter Selasky 		/* An error on accept precludes provider events */
5669f715dc1SHans Petter Selasky 		BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
5679f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_IDLE;
5689f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
5699f715dc1SHans Petter Selasky 		if (cm_id_priv->qp) {
5709f715dc1SHans Petter Selasky 			cm_id->device->iwcm->rem_ref(qp);
5719f715dc1SHans Petter Selasky 			cm_id_priv->qp = NULL;
5729f715dc1SHans Petter Selasky 		}
5739f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
5749f715dc1SHans Petter Selasky 		clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
5759f715dc1SHans Petter Selasky 		wake_up_all(&cm_id_priv->connect_wait);
5769f715dc1SHans Petter Selasky 	}
5779f715dc1SHans Petter Selasky 
5789f715dc1SHans Petter Selasky 	return ret;
5799f715dc1SHans Petter Selasky }
5809f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_cm_accept);
5819f715dc1SHans Petter Selasky 
5829f715dc1SHans Petter Selasky /*
5839f715dc1SHans Petter Selasky  * Active Side: CM_ID <-- CONN_SENT
5849f715dc1SHans Petter Selasky  *
5859f715dc1SHans Petter Selasky  * If successful, results in the generation of a CONNECT_REPLY
5869f715dc1SHans Petter Selasky  * event. iw_cm_disconnect and iw_cm_destroy will block until the
5879f715dc1SHans Petter Selasky  * CONNECT_REPLY event is received from the provider.
5889f715dc1SHans Petter Selasky  */
iw_cm_connect(struct iw_cm_id * cm_id,struct iw_cm_conn_param * iw_param)5899f715dc1SHans Petter Selasky int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
5909f715dc1SHans Petter Selasky {
5919f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
5929f715dc1SHans Petter Selasky 	int ret;
5939f715dc1SHans Petter Selasky 	unsigned long flags;
5949f715dc1SHans Petter Selasky 	struct ib_qp *qp;
5959f715dc1SHans Petter Selasky 
5969f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
5979f715dc1SHans Petter Selasky 
5989f715dc1SHans Petter Selasky 	ret = alloc_work_entries(cm_id_priv, 4);
5999f715dc1SHans Petter Selasky 	if (ret)
6009f715dc1SHans Petter Selasky 		return ret;
6019f715dc1SHans Petter Selasky 
6029f715dc1SHans Petter Selasky 	set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
6039f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
6049f715dc1SHans Petter Selasky 
6059f715dc1SHans Petter Selasky 	if (cm_id_priv->state != IW_CM_STATE_IDLE) {
6069f715dc1SHans Petter Selasky 		ret = -EINVAL;
6079f715dc1SHans Petter Selasky 		goto err;
6089f715dc1SHans Petter Selasky 	}
6099f715dc1SHans Petter Selasky 
6109f715dc1SHans Petter Selasky 	/* Get the ib_qp given the QPN */
6119f715dc1SHans Petter Selasky 	qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn);
6129f715dc1SHans Petter Selasky 	if (!qp) {
6139f715dc1SHans Petter Selasky 		ret = -EINVAL;
6149f715dc1SHans Petter Selasky 		goto err;
6159f715dc1SHans Petter Selasky 	}
6169f715dc1SHans Petter Selasky 	cm_id->device->iwcm->add_ref(qp);
6179f715dc1SHans Petter Selasky 	cm_id_priv->qp = qp;
6189f715dc1SHans Petter Selasky 	cm_id_priv->state = IW_CM_STATE_CONN_SENT;
6199f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
6209f715dc1SHans Petter Selasky 
6219f715dc1SHans Petter Selasky 	ret = iw_cm_map(cm_id, true);
6229f715dc1SHans Petter Selasky 	if (!ret)
6239f715dc1SHans Petter Selasky 		ret = cm_id->device->iwcm->connect(cm_id, iw_param);
6249f715dc1SHans Petter Selasky 	if (!ret)
6259f715dc1SHans Petter Selasky 		return 0;	/* success */
6269f715dc1SHans Petter Selasky 
6279f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
6289f715dc1SHans Petter Selasky 	if (cm_id_priv->qp) {
6299f715dc1SHans Petter Selasky 		cm_id->device->iwcm->rem_ref(qp);
6309f715dc1SHans Petter Selasky 		cm_id_priv->qp = NULL;
6319f715dc1SHans Petter Selasky 	}
6329f715dc1SHans Petter Selasky 	cm_id_priv->state = IW_CM_STATE_IDLE;
6339f715dc1SHans Petter Selasky err:
6349f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
6359f715dc1SHans Petter Selasky 	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
6369f715dc1SHans Petter Selasky 	wake_up_all(&cm_id_priv->connect_wait);
6379f715dc1SHans Petter Selasky 	return ret;
6389f715dc1SHans Petter Selasky }
6399f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_cm_connect);
6409f715dc1SHans Petter Selasky 
6419f715dc1SHans Petter Selasky /*
6429f715dc1SHans Petter Selasky  * Passive Side: new CM_ID <-- CONN_RECV
6439f715dc1SHans Petter Selasky  *
6449f715dc1SHans Petter Selasky  * Handles an inbound connect request. The function creates a new
6459f715dc1SHans Petter Selasky  * iw_cm_id to represent the new connection and inherits the client
6469f715dc1SHans Petter Selasky  * callback function and other attributes from the listening parent.
6479f715dc1SHans Petter Selasky  *
6489f715dc1SHans Petter Selasky  * The work item contains a pointer to the listen_cm_id and the event. The
6499f715dc1SHans Petter Selasky  * listen_cm_id contains the client cm_handler, context and
6509f715dc1SHans Petter Selasky  * device. These are copied when the device is cloned. The event
6519f715dc1SHans Petter Selasky  * contains the new four tuple.
6529f715dc1SHans Petter Selasky  *
6539f715dc1SHans Petter Selasky  * An error on the child should not affect the parent, so this
6549f715dc1SHans Petter Selasky  * function does not return a value.
6559f715dc1SHans Petter Selasky  */
cm_conn_req_handler(struct iwcm_id_private * listen_id_priv,struct iw_cm_event * iw_event)6569f715dc1SHans Petter Selasky static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,
6579f715dc1SHans Petter Selasky 				struct iw_cm_event *iw_event)
6589f715dc1SHans Petter Selasky {
6599f715dc1SHans Petter Selasky 	unsigned long flags;
6609f715dc1SHans Petter Selasky 	struct iw_cm_id *cm_id;
6619f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
6629f715dc1SHans Petter Selasky 	int ret;
6639f715dc1SHans Petter Selasky 
6649f715dc1SHans Petter Selasky 	/*
6659f715dc1SHans Petter Selasky 	 * The provider should never generate a connection request
6669f715dc1SHans Petter Selasky 	 * event with a bad status.
6679f715dc1SHans Petter Selasky 	 */
6689f715dc1SHans Petter Selasky 	BUG_ON(iw_event->status);
6699f715dc1SHans Petter Selasky 
6709f715dc1SHans Petter Selasky 	cm_id = iw_create_cm_id(listen_id_priv->id.device,
6719f715dc1SHans Petter Selasky 				listen_id_priv->id.cm_handler,
6729f715dc1SHans Petter Selasky 				listen_id_priv->id.context);
6739f715dc1SHans Petter Selasky 	/* If the cm_id could not be created, ignore the request */
6749f715dc1SHans Petter Selasky 	if (IS_ERR(cm_id))
6759f715dc1SHans Petter Selasky 		goto out;
6769f715dc1SHans Petter Selasky 
6779f715dc1SHans Petter Selasky 	cm_id->provider_data = iw_event->provider_data;
6789f715dc1SHans Petter Selasky 	cm_id->m_local_addr = iw_event->local_addr;
6799f715dc1SHans Petter Selasky 	cm_id->m_remote_addr = iw_event->remote_addr;
6809f715dc1SHans Petter Selasky 	cm_id->local_addr = listen_id_priv->id.local_addr;
6819f715dc1SHans Petter Selasky 	cm_id->remote_addr = iw_event->remote_addr;
6829f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
6839f715dc1SHans Petter Selasky 	cm_id_priv->state = IW_CM_STATE_CONN_RECV;
6849f715dc1SHans Petter Selasky 
6859f715dc1SHans Petter Selasky 	/*
6869f715dc1SHans Petter Selasky 	 * We could be destroying the listening id. If so, ignore this
6879f715dc1SHans Petter Selasky 	 * upcall.
6889f715dc1SHans Petter Selasky 	 */
6899f715dc1SHans Petter Selasky 	spin_lock_irqsave(&listen_id_priv->lock, flags);
6909f715dc1SHans Petter Selasky 	if (listen_id_priv->state != IW_CM_STATE_LISTEN) {
6919f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&listen_id_priv->lock, flags);
6929f715dc1SHans Petter Selasky 		iw_cm_reject(cm_id, NULL, 0);
6939f715dc1SHans Petter Selasky 		iw_destroy_cm_id(cm_id);
6949f715dc1SHans Petter Selasky 		goto out;
6959f715dc1SHans Petter Selasky 	}
6969f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&listen_id_priv->lock, flags);
6979f715dc1SHans Petter Selasky 
6989f715dc1SHans Petter Selasky 	ret = alloc_work_entries(cm_id_priv, 3);
6999f715dc1SHans Petter Selasky 	if (ret) {
7009f715dc1SHans Petter Selasky 		iw_cm_reject(cm_id, NULL, 0);
7019f715dc1SHans Petter Selasky 		iw_destroy_cm_id(cm_id);
7029f715dc1SHans Petter Selasky 		goto out;
7039f715dc1SHans Petter Selasky 	}
7049f715dc1SHans Petter Selasky 
7059f715dc1SHans Petter Selasky 	/* Call the client CM handler */
7069f715dc1SHans Petter Selasky 	ret = cm_id->cm_handler(cm_id, iw_event);
7079f715dc1SHans Petter Selasky 	if (ret) {
7089f715dc1SHans Petter Selasky 		iw_cm_reject(cm_id, NULL, 0);
7099f715dc1SHans Petter Selasky 		iw_destroy_cm_id(cm_id);
7109f715dc1SHans Petter Selasky 	}
7119f715dc1SHans Petter Selasky 
7129f715dc1SHans Petter Selasky out:
7139f715dc1SHans Petter Selasky 	if (iw_event->private_data_len)
7149f715dc1SHans Petter Selasky 		kfree(iw_event->private_data);
7159f715dc1SHans Petter Selasky }
7169f715dc1SHans Petter Selasky 
7179f715dc1SHans Petter Selasky /*
7189f715dc1SHans Petter Selasky  * Passive Side: CM_ID <-- ESTABLISHED
7199f715dc1SHans Petter Selasky  *
7209f715dc1SHans Petter Selasky  * The provider generated an ESTABLISHED event which means that
7219f715dc1SHans Petter Selasky  * the MPA negotion has completed successfully and we are now in MPA
7229f715dc1SHans Petter Selasky  * FPDU mode.
7239f715dc1SHans Petter Selasky  *
7249f715dc1SHans Petter Selasky  * This event can only be received in the CONN_RECV state. If the
7259f715dc1SHans Petter Selasky  * remote peer closed, the ESTABLISHED event would be received followed
7269f715dc1SHans Petter Selasky  * by the CLOSE event. If the app closes, it will block until we wake
7279f715dc1SHans Petter Selasky  * it up after processing this event.
7289f715dc1SHans Petter Selasky  */
cm_conn_est_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)7299f715dc1SHans Petter Selasky static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv,
7309f715dc1SHans Petter Selasky 			       struct iw_cm_event *iw_event)
7319f715dc1SHans Petter Selasky {
7329f715dc1SHans Petter Selasky 	unsigned long flags;
7339f715dc1SHans Petter Selasky 	int ret;
7349f715dc1SHans Petter Selasky 
7359f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
7369f715dc1SHans Petter Selasky 
7379f715dc1SHans Petter Selasky 	/*
7389f715dc1SHans Petter Selasky 	 * We clear the CONNECT_WAIT bit here to allow the callback
7399f715dc1SHans Petter Selasky 	 * function to call iw_cm_disconnect. Calling iw_destroy_cm_id
7409f715dc1SHans Petter Selasky 	 * from a callback handler is not allowed.
7419f715dc1SHans Petter Selasky 	 */
7429f715dc1SHans Petter Selasky 	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
7439f715dc1SHans Petter Selasky 	BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
7449f715dc1SHans Petter Selasky 	cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
7459f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
7469f715dc1SHans Petter Selasky 	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
7479f715dc1SHans Petter Selasky 	wake_up_all(&cm_id_priv->connect_wait);
7489f715dc1SHans Petter Selasky 
7499f715dc1SHans Petter Selasky 	return ret;
7509f715dc1SHans Petter Selasky }
7519f715dc1SHans Petter Selasky 
7529f715dc1SHans Petter Selasky /*
7539f715dc1SHans Petter Selasky  * Active Side: CM_ID <-- ESTABLISHED
7549f715dc1SHans Petter Selasky  *
7559f715dc1SHans Petter Selasky  * The app has called connect and is waiting for the established event to
7569f715dc1SHans Petter Selasky  * post it's requests to the server. This event will wake up anyone
7579f715dc1SHans Petter Selasky  * blocked in iw_cm_disconnect or iw_destroy_id.
7589f715dc1SHans Petter Selasky  */
cm_conn_rep_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)7599f715dc1SHans Petter Selasky static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
7609f715dc1SHans Petter Selasky 			       struct iw_cm_event *iw_event)
7619f715dc1SHans Petter Selasky {
7629f715dc1SHans Petter Selasky 	unsigned long flags;
7639f715dc1SHans Petter Selasky 	int ret;
7649f715dc1SHans Petter Selasky 
7659f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
7669f715dc1SHans Petter Selasky 	/*
7679f715dc1SHans Petter Selasky 	 * Clear the connect wait bit so a callback function calling
7689f715dc1SHans Petter Selasky 	 * iw_cm_disconnect will not wait and deadlock this thread
7699f715dc1SHans Petter Selasky 	 */
7709f715dc1SHans Petter Selasky 	clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
7719f715dc1SHans Petter Selasky 	BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT);
7729f715dc1SHans Petter Selasky 	if (iw_event->status == 0) {
7739f715dc1SHans Petter Selasky 		cm_id_priv->id.m_local_addr = iw_event->local_addr;
7749f715dc1SHans Petter Selasky 		cm_id_priv->id.m_remote_addr = iw_event->remote_addr;
7759f715dc1SHans Petter Selasky 		iw_event->local_addr = cm_id_priv->id.local_addr;
7769f715dc1SHans Petter Selasky 		iw_event->remote_addr = cm_id_priv->id.remote_addr;
7779f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
7789f715dc1SHans Petter Selasky 	} else {
7799f715dc1SHans Petter Selasky 		/* REJECTED or RESET */
7809f715dc1SHans Petter Selasky 		cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp);
7819f715dc1SHans Petter Selasky 		cm_id_priv->qp = NULL;
7829f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_IDLE;
7839f715dc1SHans Petter Selasky 	}
7849f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
7859f715dc1SHans Petter Selasky 	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
7869f715dc1SHans Petter Selasky 
7879f715dc1SHans Petter Selasky 	if (iw_event->private_data_len)
7889f715dc1SHans Petter Selasky 		kfree(iw_event->private_data);
7899f715dc1SHans Petter Selasky 
7909f715dc1SHans Petter Selasky 	/* Wake up waiters on connect complete */
7919f715dc1SHans Petter Selasky 	wake_up_all(&cm_id_priv->connect_wait);
7929f715dc1SHans Petter Selasky 
7939f715dc1SHans Petter Selasky 	return ret;
7949f715dc1SHans Petter Selasky }
7959f715dc1SHans Petter Selasky 
7969f715dc1SHans Petter Selasky /*
7979f715dc1SHans Petter Selasky  * CM_ID <-- CLOSING
7989f715dc1SHans Petter Selasky  *
7999f715dc1SHans Petter Selasky  * If in the ESTABLISHED state, move to CLOSING.
8009f715dc1SHans Petter Selasky  */
cm_disconnect_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)8019f715dc1SHans Petter Selasky static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv,
8029f715dc1SHans Petter Selasky 				  struct iw_cm_event *iw_event)
8039f715dc1SHans Petter Selasky {
8049f715dc1SHans Petter Selasky 	unsigned long flags;
8059f715dc1SHans Petter Selasky 
8069f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
8079f715dc1SHans Petter Selasky 	if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED)
8089f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_CLOSING;
8099f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
8109f715dc1SHans Petter Selasky }
8119f715dc1SHans Petter Selasky 
8129f715dc1SHans Petter Selasky /*
8139f715dc1SHans Petter Selasky  * CM_ID <-- IDLE
8149f715dc1SHans Petter Selasky  *
8159f715dc1SHans Petter Selasky  * If in the ESTBLISHED or CLOSING states, the QP will have have been
8169f715dc1SHans Petter Selasky  * moved by the provider to the ERR state. Disassociate the CM_ID from
8179f715dc1SHans Petter Selasky  * the QP,  move to IDLE, and remove the 'connected' reference.
8189f715dc1SHans Petter Selasky  *
8199f715dc1SHans Petter Selasky  * If in some other state, the cm_id was destroyed asynchronously.
8209f715dc1SHans Petter Selasky  * This is the last reference that will result in waking up
8219f715dc1SHans Petter Selasky  * the app thread blocked in iw_destroy_cm_id.
8229f715dc1SHans Petter Selasky  */
cm_close_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)8239f715dc1SHans Petter Selasky static int cm_close_handler(struct iwcm_id_private *cm_id_priv,
8249f715dc1SHans Petter Selasky 				  struct iw_cm_event *iw_event)
8259f715dc1SHans Petter Selasky {
8269f715dc1SHans Petter Selasky 	unsigned long flags;
8279f715dc1SHans Petter Selasky 	int ret = 0;
8289f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
8299f715dc1SHans Petter Selasky 
8309f715dc1SHans Petter Selasky 	if (cm_id_priv->qp) {
8319f715dc1SHans Petter Selasky 		cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp);
8329f715dc1SHans Petter Selasky 		cm_id_priv->qp = NULL;
8339f715dc1SHans Petter Selasky 	}
8349f715dc1SHans Petter Selasky 	switch (cm_id_priv->state) {
8359f715dc1SHans Petter Selasky 	case IW_CM_STATE_ESTABLISHED:
8369f715dc1SHans Petter Selasky 	case IW_CM_STATE_CLOSING:
8379f715dc1SHans Petter Selasky 		cm_id_priv->state = IW_CM_STATE_IDLE;
8389f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
8399f715dc1SHans Petter Selasky 		ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
8409f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
8419f715dc1SHans Petter Selasky 		break;
8429f715dc1SHans Petter Selasky 	case IW_CM_STATE_DESTROYING:
8439f715dc1SHans Petter Selasky 		break;
8449f715dc1SHans Petter Selasky 	default:
8459f715dc1SHans Petter Selasky 		BUG();
8469f715dc1SHans Petter Selasky 	}
8479f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
8489f715dc1SHans Petter Selasky 
8499f715dc1SHans Petter Selasky 	return ret;
8509f715dc1SHans Petter Selasky }
8519f715dc1SHans Petter Selasky 
process_event(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)8529f715dc1SHans Petter Selasky static int process_event(struct iwcm_id_private *cm_id_priv,
8539f715dc1SHans Petter Selasky 			 struct iw_cm_event *iw_event)
8549f715dc1SHans Petter Selasky {
8559f715dc1SHans Petter Selasky 	int ret = 0;
8569f715dc1SHans Petter Selasky 
8579f715dc1SHans Petter Selasky 	switch (iw_event->event) {
8589f715dc1SHans Petter Selasky 	case IW_CM_EVENT_CONNECT_REQUEST:
8599f715dc1SHans Petter Selasky 		cm_conn_req_handler(cm_id_priv, iw_event);
8609f715dc1SHans Petter Selasky 		break;
8619f715dc1SHans Petter Selasky 	case IW_CM_EVENT_CONNECT_REPLY:
8629f715dc1SHans Petter Selasky 		ret = cm_conn_rep_handler(cm_id_priv, iw_event);
8639f715dc1SHans Petter Selasky 		break;
8649f715dc1SHans Petter Selasky 	case IW_CM_EVENT_ESTABLISHED:
8659f715dc1SHans Petter Selasky 		ret = cm_conn_est_handler(cm_id_priv, iw_event);
8669f715dc1SHans Petter Selasky 		break;
8679f715dc1SHans Petter Selasky 	case IW_CM_EVENT_DISCONNECT:
8689f715dc1SHans Petter Selasky 		cm_disconnect_handler(cm_id_priv, iw_event);
8699f715dc1SHans Petter Selasky 		break;
8709f715dc1SHans Petter Selasky 	case IW_CM_EVENT_CLOSE:
8719f715dc1SHans Petter Selasky 		ret = cm_close_handler(cm_id_priv, iw_event);
8729f715dc1SHans Petter Selasky 		break;
8739f715dc1SHans Petter Selasky 	default:
8749f715dc1SHans Petter Selasky 		BUG();
8759f715dc1SHans Petter Selasky 	}
8769f715dc1SHans Petter Selasky 
8779f715dc1SHans Petter Selasky 	return ret;
8789f715dc1SHans Petter Selasky }
8799f715dc1SHans Petter Selasky 
8809f715dc1SHans Petter Selasky /*
8819f715dc1SHans Petter Selasky  * Process events on the work_list for the cm_id. If the callback
8829f715dc1SHans Petter Selasky  * function requests that the cm_id be deleted, a flag is set in the
8839f715dc1SHans Petter Selasky  * cm_id flags to indicate that when the last reference is
8849f715dc1SHans Petter Selasky  * removed, the cm_id is to be destroyed. This is necessary to
8859f715dc1SHans Petter Selasky  * distinguish between an object that will be destroyed by the app
8869f715dc1SHans Petter Selasky  * thread asleep on the destroy_comp list vs. an object destroyed
8879f715dc1SHans Petter Selasky  * here synchronously when the last reference is removed.
8889f715dc1SHans Petter Selasky  */
cm_work_handler(struct work_struct * _work)8899f715dc1SHans Petter Selasky static void cm_work_handler(struct work_struct *_work)
8909f715dc1SHans Petter Selasky {
8919f715dc1SHans Petter Selasky 	struct iwcm_work *work = container_of(_work, struct iwcm_work, work);
8929f715dc1SHans Petter Selasky 	struct iw_cm_event levent;
8939f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv = work->cm_id;
8949f715dc1SHans Petter Selasky 	unsigned long flags;
8959f715dc1SHans Petter Selasky 	int empty;
8969f715dc1SHans Petter Selasky 	int ret = 0;
8979f715dc1SHans Petter Selasky 
8989f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
8999f715dc1SHans Petter Selasky 	empty = list_empty(&cm_id_priv->work_list);
9009f715dc1SHans Petter Selasky 	while (!empty) {
9019f715dc1SHans Petter Selasky 		work = list_entry(cm_id_priv->work_list.next,
9029f715dc1SHans Petter Selasky 				  struct iwcm_work, list);
9039f715dc1SHans Petter Selasky 		list_del_init(&work->list);
9049f715dc1SHans Petter Selasky 		empty = list_empty(&cm_id_priv->work_list);
9059f715dc1SHans Petter Selasky 		levent = work->event;
9069f715dc1SHans Petter Selasky 		put_work(work);
9079f715dc1SHans Petter Selasky 		spin_unlock_irqrestore(&cm_id_priv->lock, flags);
9089f715dc1SHans Petter Selasky 
9099f715dc1SHans Petter Selasky 		if (!test_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags)) {
9109f715dc1SHans Petter Selasky 			ret = process_event(cm_id_priv, &levent);
9119f715dc1SHans Petter Selasky 			if (ret)
9129f715dc1SHans Petter Selasky 				destroy_cm_id(&cm_id_priv->id);
9139f715dc1SHans Petter Selasky 		} else
9149f715dc1SHans Petter Selasky 			pr_debug("dropping event %d\n", levent.event);
9159f715dc1SHans Petter Selasky 		if (iwcm_deref_id(cm_id_priv))
9169f715dc1SHans Petter Selasky 			return;
9179f715dc1SHans Petter Selasky 		if (empty)
9189f715dc1SHans Petter Selasky 			return;
9199f715dc1SHans Petter Selasky 		spin_lock_irqsave(&cm_id_priv->lock, flags);
9209f715dc1SHans Petter Selasky 	}
9219f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
9229f715dc1SHans Petter Selasky }
9239f715dc1SHans Petter Selasky 
9249f715dc1SHans Petter Selasky /*
9259f715dc1SHans Petter Selasky  * This function is called on interrupt context. Schedule events on
9269f715dc1SHans Petter Selasky  * the iwcm_wq thread to allow callback functions to downcall into
9279f715dc1SHans Petter Selasky  * the CM and/or block.  Events are queued to a per-CM_ID
9289f715dc1SHans Petter Selasky  * work_list. If this is the first event on the work_list, the work
9299f715dc1SHans Petter Selasky  * element is also queued on the iwcm_wq thread.
9309f715dc1SHans Petter Selasky  *
9319f715dc1SHans Petter Selasky  * Each event holds a reference on the cm_id. Until the last posted
9329f715dc1SHans Petter Selasky  * event has been delivered and processed, the cm_id cannot be
9339f715dc1SHans Petter Selasky  * deleted.
9349f715dc1SHans Petter Selasky  *
9359f715dc1SHans Petter Selasky  * Returns:
9369f715dc1SHans Petter Selasky  * 	      0	- the event was handled.
9379f715dc1SHans Petter Selasky  *	-ENOMEM	- the event was not handled due to lack of resources.
9389f715dc1SHans Petter Selasky  */
cm_event_handler(struct iw_cm_id * cm_id,struct iw_cm_event * iw_event)9399f715dc1SHans Petter Selasky static int cm_event_handler(struct iw_cm_id *cm_id,
9409f715dc1SHans Petter Selasky 			     struct iw_cm_event *iw_event)
9419f715dc1SHans Petter Selasky {
9429f715dc1SHans Petter Selasky 	struct iwcm_work *work;
9439f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
9449f715dc1SHans Petter Selasky 	unsigned long flags;
9459f715dc1SHans Petter Selasky 	int ret = 0;
9469f715dc1SHans Petter Selasky 
9479f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
9489f715dc1SHans Petter Selasky 
9499f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
9509f715dc1SHans Petter Selasky 	work = get_work(cm_id_priv);
9519f715dc1SHans Petter Selasky 	if (!work) {
9529f715dc1SHans Petter Selasky 		ret = -ENOMEM;
9539f715dc1SHans Petter Selasky 		goto out;
9549f715dc1SHans Petter Selasky 	}
9559f715dc1SHans Petter Selasky 
9569f715dc1SHans Petter Selasky 	INIT_WORK(&work->work, cm_work_handler);
9579f715dc1SHans Petter Selasky 	work->cm_id = cm_id_priv;
9589f715dc1SHans Petter Selasky 	work->event = *iw_event;
9599f715dc1SHans Petter Selasky 
9609f715dc1SHans Petter Selasky 	if ((work->event.event == IW_CM_EVENT_CONNECT_REQUEST ||
9619f715dc1SHans Petter Selasky 	     work->event.event == IW_CM_EVENT_CONNECT_REPLY) &&
9629f715dc1SHans Petter Selasky 	    work->event.private_data_len) {
9639f715dc1SHans Petter Selasky 		ret = copy_private_data(&work->event);
9649f715dc1SHans Petter Selasky 		if (ret) {
9659f715dc1SHans Petter Selasky 			put_work(work);
9669f715dc1SHans Petter Selasky 			goto out;
9679f715dc1SHans Petter Selasky 		}
9689f715dc1SHans Petter Selasky 	}
9699f715dc1SHans Petter Selasky 
9709f715dc1SHans Petter Selasky 	atomic_inc(&cm_id_priv->refcount);
9719f715dc1SHans Petter Selasky 	if (list_empty(&cm_id_priv->work_list)) {
9729f715dc1SHans Petter Selasky 		list_add_tail(&work->list, &cm_id_priv->work_list);
9739f715dc1SHans Petter Selasky 		queue_work(iwcm_wq, &work->work);
9749f715dc1SHans Petter Selasky 	} else
9759f715dc1SHans Petter Selasky 		list_add_tail(&work->list, &cm_id_priv->work_list);
9769f715dc1SHans Petter Selasky out:
9779f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
9789f715dc1SHans Petter Selasky 	return ret;
9799f715dc1SHans Petter Selasky }
9809f715dc1SHans Petter Selasky 
iwcm_init_qp_init_attr(struct iwcm_id_private * cm_id_priv,struct ib_qp_attr * qp_attr,int * qp_attr_mask)9819f715dc1SHans Petter Selasky static int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv,
9829f715dc1SHans Petter Selasky 				  struct ib_qp_attr *qp_attr,
9839f715dc1SHans Petter Selasky 				  int *qp_attr_mask)
9849f715dc1SHans Petter Selasky {
9859f715dc1SHans Petter Selasky 	unsigned long flags;
9869f715dc1SHans Petter Selasky 	int ret;
9879f715dc1SHans Petter Selasky 
9889f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
9899f715dc1SHans Petter Selasky 	switch (cm_id_priv->state) {
9909f715dc1SHans Petter Selasky 	case IW_CM_STATE_IDLE:
9919f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_SENT:
9929f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_RECV:
9939f715dc1SHans Petter Selasky 	case IW_CM_STATE_ESTABLISHED:
9949f715dc1SHans Petter Selasky 		*qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
9959f715dc1SHans Petter Selasky 		qp_attr->qp_access_flags = IB_ACCESS_REMOTE_WRITE|
9969f715dc1SHans Petter Selasky 					   IB_ACCESS_REMOTE_READ;
9979f715dc1SHans Petter Selasky 		ret = 0;
9989f715dc1SHans Petter Selasky 		break;
9999f715dc1SHans Petter Selasky 	default:
10009f715dc1SHans Petter Selasky 		ret = -EINVAL;
10019f715dc1SHans Petter Selasky 		break;
10029f715dc1SHans Petter Selasky 	}
10039f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
10049f715dc1SHans Petter Selasky 	return ret;
10059f715dc1SHans Petter Selasky }
10069f715dc1SHans Petter Selasky 
iwcm_init_qp_rts_attr(struct iwcm_id_private * cm_id_priv,struct ib_qp_attr * qp_attr,int * qp_attr_mask)10079f715dc1SHans Petter Selasky static int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv,
10089f715dc1SHans Petter Selasky 				  struct ib_qp_attr *qp_attr,
10099f715dc1SHans Petter Selasky 				  int *qp_attr_mask)
10109f715dc1SHans Petter Selasky {
10119f715dc1SHans Petter Selasky 	unsigned long flags;
10129f715dc1SHans Petter Selasky 	int ret;
10139f715dc1SHans Petter Selasky 
10149f715dc1SHans Petter Selasky 	spin_lock_irqsave(&cm_id_priv->lock, flags);
10159f715dc1SHans Petter Selasky 	switch (cm_id_priv->state) {
10169f715dc1SHans Petter Selasky 	case IW_CM_STATE_IDLE:
10179f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_SENT:
10189f715dc1SHans Petter Selasky 	case IW_CM_STATE_CONN_RECV:
10199f715dc1SHans Petter Selasky 	case IW_CM_STATE_ESTABLISHED:
10209f715dc1SHans Petter Selasky 		*qp_attr_mask = 0;
10219f715dc1SHans Petter Selasky 		ret = 0;
10229f715dc1SHans Petter Selasky 		break;
10239f715dc1SHans Petter Selasky 	default:
10249f715dc1SHans Petter Selasky 		ret = -EINVAL;
10259f715dc1SHans Petter Selasky 		break;
10269f715dc1SHans Petter Selasky 	}
10279f715dc1SHans Petter Selasky 	spin_unlock_irqrestore(&cm_id_priv->lock, flags);
10289f715dc1SHans Petter Selasky 	return ret;
10299f715dc1SHans Petter Selasky }
10309f715dc1SHans Petter Selasky 
iw_cm_init_qp_attr(struct iw_cm_id * cm_id,struct ib_qp_attr * qp_attr,int * qp_attr_mask)10319f715dc1SHans Petter Selasky int iw_cm_init_qp_attr(struct iw_cm_id *cm_id,
10329f715dc1SHans Petter Selasky 		       struct ib_qp_attr *qp_attr,
10339f715dc1SHans Petter Selasky 		       int *qp_attr_mask)
10349f715dc1SHans Petter Selasky {
10359f715dc1SHans Petter Selasky 	struct iwcm_id_private *cm_id_priv;
10369f715dc1SHans Petter Selasky 	int ret;
10379f715dc1SHans Petter Selasky 
10389f715dc1SHans Petter Selasky 	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
10399f715dc1SHans Petter Selasky 	switch (qp_attr->qp_state) {
10409f715dc1SHans Petter Selasky 	case IB_QPS_INIT:
10419f715dc1SHans Petter Selasky 	case IB_QPS_RTR:
10429f715dc1SHans Petter Selasky 		ret = iwcm_init_qp_init_attr(cm_id_priv,
10439f715dc1SHans Petter Selasky 					     qp_attr, qp_attr_mask);
10449f715dc1SHans Petter Selasky 		break;
10459f715dc1SHans Petter Selasky 	case IB_QPS_RTS:
10469f715dc1SHans Petter Selasky 		ret = iwcm_init_qp_rts_attr(cm_id_priv,
10479f715dc1SHans Petter Selasky 					    qp_attr, qp_attr_mask);
10489f715dc1SHans Petter Selasky 		break;
10499f715dc1SHans Petter Selasky 	default:
10509f715dc1SHans Petter Selasky 		ret = -EINVAL;
10519f715dc1SHans Petter Selasky 		break;
10529f715dc1SHans Petter Selasky 	}
10539f715dc1SHans Petter Selasky 	return ret;
10549f715dc1SHans Petter Selasky }
10559f715dc1SHans Petter Selasky EXPORT_SYMBOL(iw_cm_init_qp_attr);
10569f715dc1SHans Petter Selasky 
iw_cm_init(void)10579f715dc1SHans Petter Selasky static int __init iw_cm_init(void)
10589f715dc1SHans Petter Selasky {
10599f715dc1SHans Petter Selasky 	iwcm_wq = alloc_ordered_workqueue("iw_cm_wq", WQ_MEM_RECLAIM);
10609f715dc1SHans Petter Selasky 	if (!iwcm_wq)
10619f715dc1SHans Petter Selasky 		return -ENOMEM;
10629f715dc1SHans Petter Selasky 
10639f715dc1SHans Petter Selasky 	return 0;
10649f715dc1SHans Petter Selasky }
10659f715dc1SHans Petter Selasky 
iw_cm_cleanup(void)10669f715dc1SHans Petter Selasky static void __exit iw_cm_cleanup(void)
10679f715dc1SHans Petter Selasky {
10689f715dc1SHans Petter Selasky 	destroy_workqueue(iwcm_wq);
10699f715dc1SHans Petter Selasky }
10709f715dc1SHans Petter Selasky 
10711866c98eSHans Petter Selasky module_init_order(iw_cm_init, SI_ORDER_FIRST);
10721866c98eSHans Petter Selasky module_exit_order(iw_cm_cleanup, SI_ORDER_FIRST);
1073