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