1922a8e9fSTom Tucker /*
2922a8e9fSTom Tucker * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved.
3922a8e9fSTom Tucker * Copyright (c) 2004 Topspin Corporation. All rights reserved.
4922a8e9fSTom Tucker * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved.
5922a8e9fSTom Tucker * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
6922a8e9fSTom Tucker * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
7922a8e9fSTom Tucker * Copyright (c) 2005 Network Appliance, Inc. All rights reserved.
8922a8e9fSTom Tucker *
9922a8e9fSTom Tucker * This software is available to you under a choice of one of two
10922a8e9fSTom Tucker * licenses. You may choose to be licensed under the terms of the GNU
11922a8e9fSTom Tucker * General Public License (GPL) Version 2, available from the file
12922a8e9fSTom Tucker * COPYING in the main directory of this source tree, or the
13922a8e9fSTom Tucker * OpenIB.org BSD license below:
14922a8e9fSTom Tucker *
15922a8e9fSTom Tucker * Redistribution and use in source and binary forms, with or
16922a8e9fSTom Tucker * without modification, are permitted provided that the following
17922a8e9fSTom Tucker * conditions are met:
18922a8e9fSTom Tucker *
19922a8e9fSTom Tucker * - Redistributions of source code must retain the above
20922a8e9fSTom Tucker * copyright notice, this list of conditions and the following
21922a8e9fSTom Tucker * disclaimer.
22922a8e9fSTom Tucker *
23922a8e9fSTom Tucker * - Redistributions in binary form must reproduce the above
24922a8e9fSTom Tucker * copyright notice, this list of conditions and the following
25922a8e9fSTom Tucker * disclaimer in the documentation and/or other materials
26922a8e9fSTom Tucker * provided with the distribution.
27922a8e9fSTom Tucker *
28922a8e9fSTom Tucker * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29922a8e9fSTom Tucker * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30922a8e9fSTom Tucker * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31922a8e9fSTom Tucker * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
32922a8e9fSTom Tucker * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33922a8e9fSTom Tucker * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34922a8e9fSTom Tucker * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35922a8e9fSTom Tucker * SOFTWARE.
36922a8e9fSTom Tucker *
37922a8e9fSTom Tucker */
38922a8e9fSTom Tucker #include <linux/dma-mapping.h>
39922a8e9fSTom Tucker #include <linux/err.h>
40922a8e9fSTom Tucker #include <linux/idr.h>
41922a8e9fSTom Tucker #include <linux/interrupt.h>
42922a8e9fSTom Tucker #include <linux/rbtree.h>
43d43c36dcSAlexey Dobriyan #include <linux/sched.h>
44922a8e9fSTom Tucker #include <linux/spinlock.h>
45922a8e9fSTom Tucker #include <linux/workqueue.h>
46922a8e9fSTom Tucker #include <linux/completion.h>
475a0e3ad6STejun Heo #include <linux/slab.h>
48e4dd23d7SPaul Gortmaker #include <linux/module.h>
492f0304d2SSteve Wise #include <linux/sysctl.h>
50922a8e9fSTom Tucker
51922a8e9fSTom Tucker #include <rdma/iw_cm.h>
52922a8e9fSTom Tucker #include <rdma/ib_addr.h>
53b493d91dSFaisal Latif #include <rdma/iw_portmap.h>
54b493d91dSFaisal Latif #include <rdma/rdma_netlink.h>
55922a8e9fSTom Tucker
56922a8e9fSTom Tucker #include "iwcm.h"
57922a8e9fSTom Tucker
58922a8e9fSTom Tucker MODULE_AUTHOR("Tom Tucker");
59922a8e9fSTom Tucker MODULE_DESCRIPTION("iWARP CM");
60922a8e9fSTom Tucker MODULE_LICENSE("Dual BSD/GPL");
61922a8e9fSTom Tucker
6277a5db13SSteve Wise static const char * const iwcm_rej_reason_strs[] = {
6377a5db13SSteve Wise [ECONNRESET] = "reset by remote host",
6477a5db13SSteve Wise [ECONNREFUSED] = "refused by remote application",
6577a5db13SSteve Wise [ETIMEDOUT] = "setup timeout",
6677a5db13SSteve Wise };
6777a5db13SSteve Wise
iwcm_reject_msg(int reason)6877a5db13SSteve Wise const char *__attribute_const__ iwcm_reject_msg(int reason)
6977a5db13SSteve Wise {
7077a5db13SSteve Wise size_t index;
7177a5db13SSteve Wise
7277a5db13SSteve Wise /* iWARP uses negative errnos */
7377a5db13SSteve Wise index = -reason;
7477a5db13SSteve Wise
7577a5db13SSteve Wise if (index < ARRAY_SIZE(iwcm_rej_reason_strs) &&
7677a5db13SSteve Wise iwcm_rej_reason_strs[index])
7777a5db13SSteve Wise return iwcm_rej_reason_strs[index];
7877a5db13SSteve Wise else
7977a5db13SSteve Wise return "unrecognized reason";
8077a5db13SSteve Wise }
8177a5db13SSteve Wise EXPORT_SYMBOL(iwcm_reject_msg);
8277a5db13SSteve Wise
83d0e312feSLeon Romanovsky static struct rdma_nl_cbs iwcm_nl_cb_table[RDMA_NL_IWPM_NUM_OPS] = {
84b493d91dSFaisal Latif [RDMA_NL_IWPM_REG_PID] = {.dump = iwpm_register_pid_cb},
85b493d91dSFaisal Latif [RDMA_NL_IWPM_ADD_MAPPING] = {.dump = iwpm_add_mapping_cb},
86b493d91dSFaisal Latif [RDMA_NL_IWPM_QUERY_MAPPING] = {.dump = iwpm_add_and_query_mapping_cb},
87b493d91dSFaisal Latif [RDMA_NL_IWPM_REMOTE_INFO] = {.dump = iwpm_remote_info_cb},
88b493d91dSFaisal Latif [RDMA_NL_IWPM_HANDLE_ERR] = {.dump = iwpm_mapping_error_cb},
89b493d91dSFaisal Latif [RDMA_NL_IWPM_MAPINFO] = {.dump = iwpm_mapping_info_cb},
90b0bad9adSSteve Wise [RDMA_NL_IWPM_MAPINFO_NUM] = {.dump = iwpm_ack_mapping_info_cb},
91b0bad9adSSteve Wise [RDMA_NL_IWPM_HELLO] = {.dump = iwpm_hello_cb}
92b493d91dSFaisal Latif };
93b493d91dSFaisal Latif
94922a8e9fSTom Tucker static struct workqueue_struct *iwcm_wq;
95922a8e9fSTom Tucker struct iwcm_work {
96922a8e9fSTom Tucker struct work_struct work;
97922a8e9fSTom Tucker struct iwcm_id_private *cm_id;
98922a8e9fSTom Tucker struct list_head list;
99922a8e9fSTom Tucker struct iw_cm_event event;
100922a8e9fSTom Tucker struct list_head free_list;
101922a8e9fSTom Tucker };
102922a8e9fSTom Tucker
1032f0304d2SSteve Wise static unsigned int default_backlog = 256;
1042f0304d2SSteve Wise
1052f0304d2SSteve Wise static struct ctl_table_header *iwcm_ctl_table_hdr;
1062f0304d2SSteve Wise static struct ctl_table iwcm_ctl_table[] = {
1072f0304d2SSteve Wise {
1082f0304d2SSteve Wise .procname = "default_backlog",
1092f0304d2SSteve Wise .data = &default_backlog,
1102f0304d2SSteve Wise .maxlen = sizeof(default_backlog),
1112f0304d2SSteve Wise .mode = 0644,
1122f0304d2SSteve Wise .proc_handler = proc_dointvec,
1132f0304d2SSteve Wise },
1142f0304d2SSteve Wise { }
1152f0304d2SSteve Wise };
1162f0304d2SSteve Wise
117922a8e9fSTom Tucker /*
118922a8e9fSTom Tucker * The following services provide a mechanism for pre-allocating iwcm_work
119922a8e9fSTom Tucker * elements. The design pre-allocates them based on the cm_id type:
120922a8e9fSTom Tucker * LISTENING IDS: Get enough elements preallocated to handle the
121922a8e9fSTom Tucker * listen backlog.
122922a8e9fSTom Tucker * ACTIVE IDS: 4: CONNECT_REPLY, ESTABLISHED, DISCONNECT, CLOSE
123922a8e9fSTom Tucker * PASSIVE IDS: 3: ESTABLISHED, DISCONNECT, CLOSE
124922a8e9fSTom Tucker *
125922a8e9fSTom Tucker * Allocating them in connect and listen avoids having to deal
126922a8e9fSTom Tucker * with allocation failures on the event upcall from the provider (which
127922a8e9fSTom Tucker * is called in the interrupt context).
128922a8e9fSTom Tucker *
129922a8e9fSTom Tucker * One exception is when creating the cm_id for incoming connection requests.
130922a8e9fSTom Tucker * There are two cases:
131922a8e9fSTom Tucker * 1) in the event upcall, cm_event_handler(), for a listening cm_id. If
132922a8e9fSTom Tucker * the backlog is exceeded, then no more connection request events will
133922a8e9fSTom Tucker * be processed. cm_event_handler() returns -ENOMEM in this case. Its up
134715a588fSKrishna Kumar * to the provider to reject the connection request.
135922a8e9fSTom Tucker * 2) in the connection request workqueue handler, cm_conn_req_handler().
136922a8e9fSTom Tucker * If work elements cannot be allocated for the new connect request cm_id,
137922a8e9fSTom Tucker * then IWCM will call the provider reject method. This is ok since
138922a8e9fSTom Tucker * cm_conn_req_handler() runs in the workqueue thread context.
139922a8e9fSTom Tucker */
140922a8e9fSTom Tucker
get_work(struct iwcm_id_private * cm_id_priv)141922a8e9fSTom Tucker static struct iwcm_work *get_work(struct iwcm_id_private *cm_id_priv)
142922a8e9fSTom Tucker {
143922a8e9fSTom Tucker struct iwcm_work *work;
144922a8e9fSTom Tucker
145922a8e9fSTom Tucker if (list_empty(&cm_id_priv->work_free_list))
146922a8e9fSTom Tucker return NULL;
147922a8e9fSTom Tucker work = list_entry(cm_id_priv->work_free_list.next, struct iwcm_work,
148922a8e9fSTom Tucker free_list);
149922a8e9fSTom Tucker list_del_init(&work->free_list);
150922a8e9fSTom Tucker return work;
151922a8e9fSTom Tucker }
152922a8e9fSTom Tucker
put_work(struct iwcm_work * work)153922a8e9fSTom Tucker static void put_work(struct iwcm_work *work)
154922a8e9fSTom Tucker {
155922a8e9fSTom Tucker list_add(&work->free_list, &work->cm_id->work_free_list);
156922a8e9fSTom Tucker }
157922a8e9fSTom Tucker
dealloc_work_entries(struct iwcm_id_private * cm_id_priv)158922a8e9fSTom Tucker static void dealloc_work_entries(struct iwcm_id_private *cm_id_priv)
159922a8e9fSTom Tucker {
160922a8e9fSTom Tucker struct list_head *e, *tmp;
161922a8e9fSTom Tucker
162810dbc69SBernard Metzler list_for_each_safe(e, tmp, &cm_id_priv->work_free_list) {
163810dbc69SBernard Metzler list_del(e);
164922a8e9fSTom Tucker kfree(list_entry(e, struct iwcm_work, free_list));
165922a8e9fSTom Tucker }
166810dbc69SBernard Metzler }
167922a8e9fSTom Tucker
alloc_work_entries(struct iwcm_id_private * cm_id_priv,int count)168922a8e9fSTom Tucker static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)
169922a8e9fSTom Tucker {
170922a8e9fSTom Tucker struct iwcm_work *work;
171922a8e9fSTom Tucker
172922a8e9fSTom Tucker BUG_ON(!list_empty(&cm_id_priv->work_free_list));
173922a8e9fSTom Tucker while (count--) {
174922a8e9fSTom Tucker work = kmalloc(sizeof(struct iwcm_work), GFP_KERNEL);
175922a8e9fSTom Tucker if (!work) {
176922a8e9fSTom Tucker dealloc_work_entries(cm_id_priv);
177922a8e9fSTom Tucker return -ENOMEM;
178922a8e9fSTom Tucker }
179922a8e9fSTom Tucker work->cm_id = cm_id_priv;
180922a8e9fSTom Tucker INIT_LIST_HEAD(&work->list);
181922a8e9fSTom Tucker put_work(work);
182922a8e9fSTom Tucker }
183922a8e9fSTom Tucker return 0;
184922a8e9fSTom Tucker }
185922a8e9fSTom Tucker
186922a8e9fSTom Tucker /*
187715a588fSKrishna Kumar * Save private data from incoming connection requests to
188715a588fSKrishna Kumar * iw_cm_event, so the low level driver doesn't have to. Adjust
189922a8e9fSTom Tucker * the event ptr to point to the local copy.
190922a8e9fSTom Tucker */
copy_private_data(struct iw_cm_event * event)191715a588fSKrishna Kumar static int copy_private_data(struct iw_cm_event *event)
192922a8e9fSTom Tucker {
193922a8e9fSTom Tucker void *p;
194922a8e9fSTom Tucker
195bed8bdfdSEric Sesterhenn p = kmemdup(event->private_data, event->private_data_len, GFP_ATOMIC);
196922a8e9fSTom Tucker if (!p)
197922a8e9fSTom Tucker return -ENOMEM;
198922a8e9fSTom Tucker event->private_data = p;
199922a8e9fSTom Tucker return 0;
200922a8e9fSTom Tucker }
201922a8e9fSTom Tucker
free_cm_id(struct iwcm_id_private * cm_id_priv)202ebb90986SSteve Wise static void free_cm_id(struct iwcm_id_private *cm_id_priv)
203ebb90986SSteve Wise {
204ebb90986SSteve Wise dealloc_work_entries(cm_id_priv);
205ebb90986SSteve Wise kfree(cm_id_priv);
206ebb90986SSteve Wise }
207ebb90986SSteve Wise
208922a8e9fSTom Tucker /*
2099ab1ffa8SKrishna Kumar * Release a reference on cm_id. If the last reference is being
21059c68ac3SSteve Wise * released, free the cm_id and return 1.
211922a8e9fSTom Tucker */
iwcm_deref_id(struct iwcm_id_private * cm_id_priv)212922a8e9fSTom Tucker static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv)
213922a8e9fSTom Tucker {
21460dff56dSWeihang Li if (refcount_dec_and_test(&cm_id_priv->refcount)) {
215922a8e9fSTom Tucker BUG_ON(!list_empty(&cm_id_priv->work_list));
21659c68ac3SSteve Wise free_cm_id(cm_id_priv);
217ebb90986SSteve Wise return 1;
218922a8e9fSTom Tucker }
219922a8e9fSTom Tucker
220ebb90986SSteve Wise return 0;
221922a8e9fSTom Tucker }
222922a8e9fSTom Tucker
add_ref(struct iw_cm_id * cm_id)223922a8e9fSTom Tucker static void add_ref(struct iw_cm_id *cm_id)
224922a8e9fSTom Tucker {
225922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
226922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
22760dff56dSWeihang Li refcount_inc(&cm_id_priv->refcount);
228922a8e9fSTom Tucker }
229922a8e9fSTom Tucker
rem_ref(struct iw_cm_id * cm_id)230922a8e9fSTom Tucker static void rem_ref(struct iw_cm_id *cm_id)
231922a8e9fSTom Tucker {
232922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
2336b59ba60SSteve Wise
234922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
2356b59ba60SSteve Wise
23659c68ac3SSteve Wise (void)iwcm_deref_id(cm_id_priv);
237922a8e9fSTom Tucker }
238922a8e9fSTom Tucker
239922a8e9fSTom Tucker static int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event);
240922a8e9fSTom Tucker
iw_create_cm_id(struct ib_device * device,iw_cm_handler cm_handler,void * context)241922a8e9fSTom Tucker struct iw_cm_id *iw_create_cm_id(struct ib_device *device,
242922a8e9fSTom Tucker iw_cm_handler cm_handler,
243922a8e9fSTom Tucker void *context)
244922a8e9fSTom Tucker {
245922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
246922a8e9fSTom Tucker
247922a8e9fSTom Tucker cm_id_priv = kzalloc(sizeof(*cm_id_priv), GFP_KERNEL);
248922a8e9fSTom Tucker if (!cm_id_priv)
249922a8e9fSTom Tucker return ERR_PTR(-ENOMEM);
250922a8e9fSTom Tucker
251922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
252922a8e9fSTom Tucker cm_id_priv->id.device = device;
253922a8e9fSTom Tucker cm_id_priv->id.cm_handler = cm_handler;
254922a8e9fSTom Tucker cm_id_priv->id.context = context;
255922a8e9fSTom Tucker cm_id_priv->id.event_handler = cm_event_handler;
256922a8e9fSTom Tucker cm_id_priv->id.add_ref = add_ref;
257922a8e9fSTom Tucker cm_id_priv->id.rem_ref = rem_ref;
258922a8e9fSTom Tucker spin_lock_init(&cm_id_priv->lock);
25960dff56dSWeihang Li refcount_set(&cm_id_priv->refcount, 1);
260922a8e9fSTom Tucker init_waitqueue_head(&cm_id_priv->connect_wait);
261922a8e9fSTom Tucker init_completion(&cm_id_priv->destroy_comp);
262922a8e9fSTom Tucker INIT_LIST_HEAD(&cm_id_priv->work_list);
263922a8e9fSTom Tucker INIT_LIST_HEAD(&cm_id_priv->work_free_list);
264922a8e9fSTom Tucker
265922a8e9fSTom Tucker return &cm_id_priv->id;
266922a8e9fSTom Tucker }
267922a8e9fSTom Tucker EXPORT_SYMBOL(iw_create_cm_id);
268922a8e9fSTom Tucker
269922a8e9fSTom Tucker
iwcm_modify_qp_err(struct ib_qp * qp)270922a8e9fSTom Tucker static int iwcm_modify_qp_err(struct ib_qp *qp)
271922a8e9fSTom Tucker {
272922a8e9fSTom Tucker struct ib_qp_attr qp_attr;
273922a8e9fSTom Tucker
274922a8e9fSTom Tucker if (!qp)
275922a8e9fSTom Tucker return -EINVAL;
276922a8e9fSTom Tucker
277922a8e9fSTom Tucker qp_attr.qp_state = IB_QPS_ERR;
278922a8e9fSTom Tucker return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
279922a8e9fSTom Tucker }
280922a8e9fSTom Tucker
281922a8e9fSTom Tucker /*
282922a8e9fSTom Tucker * This is really the RDMAC CLOSING state. It is most similar to the
283922a8e9fSTom Tucker * IB SQD QP state.
284922a8e9fSTom Tucker */
iwcm_modify_qp_sqd(struct ib_qp * qp)285922a8e9fSTom Tucker static int iwcm_modify_qp_sqd(struct ib_qp *qp)
286922a8e9fSTom Tucker {
287922a8e9fSTom Tucker struct ib_qp_attr qp_attr;
288922a8e9fSTom Tucker
289922a8e9fSTom Tucker BUG_ON(qp == NULL);
290922a8e9fSTom Tucker qp_attr.qp_state = IB_QPS_SQD;
291922a8e9fSTom Tucker return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
292922a8e9fSTom Tucker }
293922a8e9fSTom Tucker
294922a8e9fSTom Tucker /*
295922a8e9fSTom Tucker * CM_ID <-- CLOSING
296922a8e9fSTom Tucker *
297715a588fSKrishna Kumar * Block if a passive or active connection is currently being processed. Then
298922a8e9fSTom Tucker * process the event as follows:
299922a8e9fSTom Tucker * - If we are ESTABLISHED, move to CLOSING and modify the QP state
300922a8e9fSTom Tucker * based on the abrupt flag
301922a8e9fSTom Tucker * - If the connection is already in the CLOSING or IDLE state, the peer is
302922a8e9fSTom Tucker * disconnecting concurrently with us and we've already seen the
303922a8e9fSTom Tucker * DISCONNECT event -- ignore the request and return 0
304922a8e9fSTom Tucker * - Disconnect on a listening endpoint returns -EINVAL
305922a8e9fSTom Tucker */
iw_cm_disconnect(struct iw_cm_id * cm_id,int abrupt)306922a8e9fSTom Tucker int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt)
307922a8e9fSTom Tucker {
308922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
309922a8e9fSTom Tucker unsigned long flags;
310922a8e9fSTom Tucker int ret = 0;
311922a8e9fSTom Tucker struct ib_qp *qp = NULL;
312922a8e9fSTom Tucker
313922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
314922a8e9fSTom Tucker /* Wait if we're currently in a connect or accept downcall */
315922a8e9fSTom Tucker wait_event(cm_id_priv->connect_wait,
316922a8e9fSTom Tucker !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags));
317922a8e9fSTom Tucker
318922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
319922a8e9fSTom Tucker switch (cm_id_priv->state) {
320922a8e9fSTom Tucker case IW_CM_STATE_ESTABLISHED:
321922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_CLOSING;
322922a8e9fSTom Tucker
323922a8e9fSTom Tucker /* QP could be <nul> for user-mode client */
324922a8e9fSTom Tucker if (cm_id_priv->qp)
325922a8e9fSTom Tucker qp = cm_id_priv->qp;
326922a8e9fSTom Tucker else
327922a8e9fSTom Tucker ret = -EINVAL;
328922a8e9fSTom Tucker break;
329922a8e9fSTom Tucker case IW_CM_STATE_LISTEN:
330922a8e9fSTom Tucker ret = -EINVAL;
331922a8e9fSTom Tucker break;
332922a8e9fSTom Tucker case IW_CM_STATE_CLOSING:
333922a8e9fSTom Tucker /* remote peer closed first */
334922a8e9fSTom Tucker case IW_CM_STATE_IDLE:
335922a8e9fSTom Tucker /* accept or connect returned !0 */
336922a8e9fSTom Tucker break;
337922a8e9fSTom Tucker case IW_CM_STATE_CONN_RECV:
338922a8e9fSTom Tucker /*
339922a8e9fSTom Tucker * App called disconnect before/without calling accept after
340922a8e9fSTom Tucker * connect_request event delivered.
341922a8e9fSTom Tucker */
342922a8e9fSTom Tucker break;
343922a8e9fSTom Tucker case IW_CM_STATE_CONN_SENT:
344922a8e9fSTom Tucker /* Can only get here if wait above fails */
345922a8e9fSTom Tucker default:
346922a8e9fSTom Tucker BUG();
347922a8e9fSTom Tucker }
348922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
349922a8e9fSTom Tucker
350922a8e9fSTom Tucker if (qp) {
351922a8e9fSTom Tucker if (abrupt)
352922a8e9fSTom Tucker ret = iwcm_modify_qp_err(qp);
353922a8e9fSTom Tucker else
354922a8e9fSTom Tucker ret = iwcm_modify_qp_sqd(qp);
355922a8e9fSTom Tucker
356922a8e9fSTom Tucker /*
357922a8e9fSTom Tucker * If both sides are disconnecting the QP could
358922a8e9fSTom Tucker * already be in ERR or SQD states
359922a8e9fSTom Tucker */
360922a8e9fSTom Tucker ret = 0;
361922a8e9fSTom Tucker }
362922a8e9fSTom Tucker
363922a8e9fSTom Tucker return ret;
364922a8e9fSTom Tucker }
365922a8e9fSTom Tucker EXPORT_SYMBOL(iw_cm_disconnect);
366922a8e9fSTom Tucker
367922a8e9fSTom Tucker /*
368922a8e9fSTom Tucker * CM_ID <-- DESTROYING
369922a8e9fSTom Tucker *
370922a8e9fSTom Tucker * Clean up all resources associated with the connection and release
371922a8e9fSTom Tucker * the initial reference taken by iw_create_cm_id.
372ff5bbbdeSBart Van Assche *
373ff5bbbdeSBart Van Assche * Returns true if and only if the last cm_id_priv reference has been dropped.
374922a8e9fSTom Tucker */
destroy_cm_id(struct iw_cm_id * cm_id)375ff5bbbdeSBart Van Assche static bool destroy_cm_id(struct iw_cm_id *cm_id)
376922a8e9fSTom Tucker {
377922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
37854102dd4SKrishnamraju Eraparaju struct ib_qp *qp;
379922a8e9fSTom Tucker unsigned long flags;
380922a8e9fSTom Tucker
381922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
382922a8e9fSTom Tucker /*
383922a8e9fSTom Tucker * Wait if we're currently in a connect or accept downcall. A
384922a8e9fSTom Tucker * listening endpoint should never block here.
385922a8e9fSTom Tucker */
386922a8e9fSTom Tucker wait_event(cm_id_priv->connect_wait,
387922a8e9fSTom Tucker !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags));
388922a8e9fSTom Tucker
38959c68ac3SSteve Wise /*
39059c68ac3SSteve Wise * Since we're deleting the cm_id, drop any events that
39159c68ac3SSteve Wise * might arrive before the last dereference.
39259c68ac3SSteve Wise */
39359c68ac3SSteve Wise set_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags);
39459c68ac3SSteve Wise
395922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
39654102dd4SKrishnamraju Eraparaju qp = cm_id_priv->qp;
39754102dd4SKrishnamraju Eraparaju cm_id_priv->qp = NULL;
39854102dd4SKrishnamraju Eraparaju
399922a8e9fSTom Tucker switch (cm_id_priv->state) {
400922a8e9fSTom Tucker case IW_CM_STATE_LISTEN:
401922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_DESTROYING;
402922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
403922a8e9fSTom Tucker /* destroy the listening endpoint */
404dd05cb82SKamal Heib cm_id->device->ops.iw_destroy_listen(cm_id);
405922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
406922a8e9fSTom Tucker break;
407922a8e9fSTom Tucker case IW_CM_STATE_ESTABLISHED:
408922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_DESTROYING;
409922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
410922a8e9fSTom Tucker /* Abrupt close of the connection */
41154102dd4SKrishnamraju Eraparaju (void)iwcm_modify_qp_err(qp);
412922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
413922a8e9fSTom Tucker break;
414922a8e9fSTom Tucker case IW_CM_STATE_IDLE:
415922a8e9fSTom Tucker case IW_CM_STATE_CLOSING:
416922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_DESTROYING;
417922a8e9fSTom Tucker break;
418922a8e9fSTom Tucker case IW_CM_STATE_CONN_RECV:
419922a8e9fSTom Tucker /*
420922a8e9fSTom Tucker * App called destroy before/without calling accept after
421ebb90986SSteve Wise * receiving connection request event notification or
422ebb90986SSteve Wise * returned non zero from the event callback function.
423ebb90986SSteve Wise * In either case, must tell the provider to reject.
424922a8e9fSTom Tucker */
425922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_DESTROYING;
42654e05f15SSteve Wise spin_unlock_irqrestore(&cm_id_priv->lock, flags);
427dd05cb82SKamal Heib cm_id->device->ops.iw_reject(cm_id, NULL, 0);
42854e05f15SSteve Wise spin_lock_irqsave(&cm_id_priv->lock, flags);
429922a8e9fSTom Tucker break;
430922a8e9fSTom Tucker case IW_CM_STATE_CONN_SENT:
431922a8e9fSTom Tucker case IW_CM_STATE_DESTROYING:
432922a8e9fSTom Tucker default:
433922a8e9fSTom Tucker BUG();
434922a8e9fSTom Tucker break;
435922a8e9fSTom Tucker }
436922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
43754102dd4SKrishnamraju Eraparaju if (qp)
43854102dd4SKrishnamraju Eraparaju cm_id_priv->id.device->ops.iw_rem_ref(qp);
439922a8e9fSTom Tucker
440b493d91dSFaisal Latif if (cm_id->mapped) {
441b493d91dSFaisal Latif iwpm_remove_mapinfo(&cm_id->local_addr, &cm_id->m_local_addr);
442b493d91dSFaisal Latif iwpm_remove_mapping(&cm_id->local_addr, RDMA_NL_IWCM);
443b493d91dSFaisal Latif }
444b493d91dSFaisal Latif
445ff5bbbdeSBart Van Assche return iwcm_deref_id(cm_id_priv);
446922a8e9fSTom Tucker }
447922a8e9fSTom Tucker
448922a8e9fSTom Tucker /*
449922a8e9fSTom Tucker * This function is only called by the application thread and cannot
450922a8e9fSTom Tucker * be called by the event thread. The function will wait for all
451922a8e9fSTom Tucker * references to be released on the cm_id and then kfree the cm_id
452922a8e9fSTom Tucker * object.
453922a8e9fSTom Tucker */
iw_destroy_cm_id(struct iw_cm_id * cm_id)454922a8e9fSTom Tucker void iw_destroy_cm_id(struct iw_cm_id *cm_id)
455922a8e9fSTom Tucker {
456ff5bbbdeSBart Van Assche if (!destroy_cm_id(cm_id))
457ff5bbbdeSBart Van Assche flush_workqueue(iwcm_wq);
458922a8e9fSTom Tucker }
459922a8e9fSTom Tucker EXPORT_SYMBOL(iw_destroy_cm_id);
460922a8e9fSTom Tucker
461b493d91dSFaisal Latif /**
462b493d91dSFaisal Latif * iw_cm_check_wildcard - If IP address is 0 then use original
463b493d91dSFaisal Latif * @pm_addr: sockaddr containing the ip to check for wildcard
464b493d91dSFaisal Latif * @cm_addr: sockaddr containing the actual IP address
465b493d91dSFaisal Latif * @cm_outaddr: sockaddr to set IP addr which leaving port
466b493d91dSFaisal Latif *
467b493d91dSFaisal Latif * Checks the pm_addr for wildcard and then sets cm_outaddr's
468b493d91dSFaisal Latif * IP to the actual (cm_addr).
469b493d91dSFaisal Latif */
iw_cm_check_wildcard(struct sockaddr_storage * pm_addr,struct sockaddr_storage * cm_addr,struct sockaddr_storage * cm_outaddr)470b493d91dSFaisal Latif static void iw_cm_check_wildcard(struct sockaddr_storage *pm_addr,
471b493d91dSFaisal Latif struct sockaddr_storage *cm_addr,
472b493d91dSFaisal Latif struct sockaddr_storage *cm_outaddr)
473b493d91dSFaisal Latif {
474b493d91dSFaisal Latif if (pm_addr->ss_family == AF_INET) {
475b493d91dSFaisal Latif struct sockaddr_in *pm4_addr = (struct sockaddr_in *)pm_addr;
476b493d91dSFaisal Latif
477825107a2SBart Van Assche if (pm4_addr->sin_addr.s_addr == htonl(INADDR_ANY)) {
478b493d91dSFaisal Latif struct sockaddr_in *cm4_addr =
479b493d91dSFaisal Latif (struct sockaddr_in *)cm_addr;
480b493d91dSFaisal Latif struct sockaddr_in *cm4_outaddr =
481b493d91dSFaisal Latif (struct sockaddr_in *)cm_outaddr;
482b493d91dSFaisal Latif
483b493d91dSFaisal Latif cm4_outaddr->sin_addr = cm4_addr->sin_addr;
484b493d91dSFaisal Latif }
485b493d91dSFaisal Latif } else {
486b493d91dSFaisal Latif struct sockaddr_in6 *pm6_addr = (struct sockaddr_in6 *)pm_addr;
487b493d91dSFaisal Latif
488b493d91dSFaisal Latif if (ipv6_addr_type(&pm6_addr->sin6_addr) == IPV6_ADDR_ANY) {
489b493d91dSFaisal Latif struct sockaddr_in6 *cm6_addr =
490b493d91dSFaisal Latif (struct sockaddr_in6 *)cm_addr;
491b493d91dSFaisal Latif struct sockaddr_in6 *cm6_outaddr =
492b493d91dSFaisal Latif (struct sockaddr_in6 *)cm_outaddr;
493b493d91dSFaisal Latif
494b493d91dSFaisal Latif cm6_outaddr->sin6_addr = cm6_addr->sin6_addr;
495b493d91dSFaisal Latif }
496b493d91dSFaisal Latif }
497b493d91dSFaisal Latif }
498b493d91dSFaisal Latif
499b493d91dSFaisal Latif /**
500b493d91dSFaisal Latif * iw_cm_map - Use portmapper to map the ports
501b493d91dSFaisal Latif * @cm_id: connection manager pointer
502b493d91dSFaisal Latif * @active: Indicates the active side when true
503b493d91dSFaisal Latif * returns nonzero for error only if iwpm_create_mapinfo() fails
504b493d91dSFaisal Latif *
505b493d91dSFaisal Latif * Tries to add a mapping for a port using the Portmapper. If
506b493d91dSFaisal Latif * successful in mapping the IP/Port it will check the remote
507b493d91dSFaisal Latif * mapped IP address for a wildcard IP address and replace the
508b493d91dSFaisal Latif * zero IP address with the remote_addr.
509b493d91dSFaisal Latif */
iw_cm_map(struct iw_cm_id * cm_id,bool active)510b493d91dSFaisal Latif static int iw_cm_map(struct iw_cm_id *cm_id, bool active)
511b493d91dSFaisal Latif {
512d53ec8afSSteve Wise const char *devname = dev_name(&cm_id->device->dev);
513dd05cb82SKamal Heib const char *ifname = cm_id->device->iw_ifname;
5141882ab86SLeon Romanovsky struct iwpm_dev_data pm_reg_msg = {};
515b493d91dSFaisal Latif struct iwpm_sa_data pm_msg;
516b493d91dSFaisal Latif int status;
517b493d91dSFaisal Latif
518d53ec8afSSteve Wise if (strlen(devname) >= sizeof(pm_reg_msg.dev_name) ||
519d53ec8afSSteve Wise strlen(ifname) >= sizeof(pm_reg_msg.if_name))
520d53ec8afSSteve Wise return -EINVAL;
521d53ec8afSSteve Wise
522b493d91dSFaisal Latif cm_id->m_local_addr = cm_id->local_addr;
523b493d91dSFaisal Latif cm_id->m_remote_addr = cm_id->remote_addr;
524b493d91dSFaisal Latif
5251882ab86SLeon Romanovsky strcpy(pm_reg_msg.dev_name, devname);
5261882ab86SLeon Romanovsky strcpy(pm_reg_msg.if_name, ifname);
527b493d91dSFaisal Latif
528b493d91dSFaisal Latif if (iwpm_register_pid(&pm_reg_msg, RDMA_NL_IWCM) ||
529b493d91dSFaisal Latif !iwpm_valid_pid())
530b493d91dSFaisal Latif return 0;
531b493d91dSFaisal Latif
532b493d91dSFaisal Latif cm_id->mapped = true;
533b493d91dSFaisal Latif pm_msg.loc_addr = cm_id->local_addr;
534b493d91dSFaisal Latif pm_msg.rem_addr = cm_id->remote_addr;
535dd05cb82SKamal Heib pm_msg.flags = (cm_id->device->iw_driver_flags & IW_F_NO_PORT_MAP) ?
536b0bad9adSSteve Wise IWPM_FLAGS_NO_PORT_MAP : 0;
537b493d91dSFaisal Latif if (active)
538b493d91dSFaisal Latif status = iwpm_add_and_query_mapping(&pm_msg,
539b493d91dSFaisal Latif RDMA_NL_IWCM);
540b493d91dSFaisal Latif else
541b493d91dSFaisal Latif status = iwpm_add_mapping(&pm_msg, RDMA_NL_IWCM);
542b493d91dSFaisal Latif
543b493d91dSFaisal Latif if (!status) {
544b493d91dSFaisal Latif cm_id->m_local_addr = pm_msg.mapped_loc_addr;
545b493d91dSFaisal Latif if (active) {
546b493d91dSFaisal Latif cm_id->m_remote_addr = pm_msg.mapped_rem_addr;
547b493d91dSFaisal Latif iw_cm_check_wildcard(&pm_msg.mapped_rem_addr,
548b493d91dSFaisal Latif &cm_id->remote_addr,
549b493d91dSFaisal Latif &cm_id->m_remote_addr);
550b493d91dSFaisal Latif }
551b493d91dSFaisal Latif }
552b493d91dSFaisal Latif
553b493d91dSFaisal Latif return iwpm_create_mapinfo(&cm_id->local_addr,
554b493d91dSFaisal Latif &cm_id->m_local_addr,
555b0bad9adSSteve Wise RDMA_NL_IWCM, pm_msg.flags);
556b493d91dSFaisal Latif }
557b493d91dSFaisal Latif
558922a8e9fSTom Tucker /*
559922a8e9fSTom Tucker * CM_ID <-- LISTEN
560922a8e9fSTom Tucker *
561922a8e9fSTom Tucker * Start listening for connect requests. Generates one CONNECT_REQUEST
562922a8e9fSTom Tucker * event for each inbound connect request.
563922a8e9fSTom Tucker */
iw_cm_listen(struct iw_cm_id * cm_id,int backlog)564922a8e9fSTom Tucker int iw_cm_listen(struct iw_cm_id *cm_id, int backlog)
565922a8e9fSTom Tucker {
566922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
567922a8e9fSTom Tucker unsigned long flags;
56813fccdb3SKrishna Kumar int ret;
569922a8e9fSTom Tucker
570922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
571922a8e9fSTom Tucker
5722f0304d2SSteve Wise if (!backlog)
5732f0304d2SSteve Wise backlog = default_backlog;
5742f0304d2SSteve Wise
575922a8e9fSTom Tucker ret = alloc_work_entries(cm_id_priv, backlog);
576922a8e9fSTom Tucker if (ret)
577922a8e9fSTom Tucker return ret;
578922a8e9fSTom Tucker
579922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
580922a8e9fSTom Tucker switch (cm_id_priv->state) {
581922a8e9fSTom Tucker case IW_CM_STATE_IDLE:
582922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_LISTEN;
583922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
584b493d91dSFaisal Latif ret = iw_cm_map(cm_id, false);
585b493d91dSFaisal Latif if (!ret)
586dd05cb82SKamal Heib ret = cm_id->device->ops.iw_create_listen(cm_id,
587dd05cb82SKamal Heib backlog);
588922a8e9fSTom Tucker if (ret)
589922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
590922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
591922a8e9fSTom Tucker break;
592922a8e9fSTom Tucker default:
593922a8e9fSTom Tucker ret = -EINVAL;
594922a8e9fSTom Tucker }
595922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
596922a8e9fSTom Tucker
597922a8e9fSTom Tucker return ret;
598922a8e9fSTom Tucker }
599922a8e9fSTom Tucker EXPORT_SYMBOL(iw_cm_listen);
600922a8e9fSTom Tucker
601922a8e9fSTom Tucker /*
602922a8e9fSTom Tucker * CM_ID <-- IDLE
603922a8e9fSTom Tucker *
604922a8e9fSTom Tucker * Rejects an inbound connection request. No events are generated.
605922a8e9fSTom Tucker */
iw_cm_reject(struct iw_cm_id * cm_id,const void * private_data,u8 private_data_len)606922a8e9fSTom Tucker int iw_cm_reject(struct iw_cm_id *cm_id,
607922a8e9fSTom Tucker const void *private_data,
608922a8e9fSTom Tucker u8 private_data_len)
609922a8e9fSTom Tucker {
610922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
611922a8e9fSTom Tucker unsigned long flags;
612922a8e9fSTom Tucker int ret;
613922a8e9fSTom Tucker
614922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
615922a8e9fSTom Tucker set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
616922a8e9fSTom Tucker
617922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
618922a8e9fSTom Tucker if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
619922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
620922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
621922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
622922a8e9fSTom Tucker return -EINVAL;
623922a8e9fSTom Tucker }
624922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
625922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
626922a8e9fSTom Tucker
627dd05cb82SKamal Heib ret = cm_id->device->ops.iw_reject(cm_id, private_data,
628922a8e9fSTom Tucker private_data_len);
629922a8e9fSTom Tucker
630922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
631922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
632922a8e9fSTom Tucker
633922a8e9fSTom Tucker return ret;
634922a8e9fSTom Tucker }
635922a8e9fSTom Tucker EXPORT_SYMBOL(iw_cm_reject);
636922a8e9fSTom Tucker
637922a8e9fSTom Tucker /*
638922a8e9fSTom Tucker * CM_ID <-- ESTABLISHED
639922a8e9fSTom Tucker *
640922a8e9fSTom Tucker * Accepts an inbound connection request and generates an ESTABLISHED
641922a8e9fSTom Tucker * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block
642922a8e9fSTom Tucker * until the ESTABLISHED event is received from the provider.
643922a8e9fSTom Tucker */
iw_cm_accept(struct iw_cm_id * cm_id,struct iw_cm_conn_param * iw_param)644922a8e9fSTom Tucker int iw_cm_accept(struct iw_cm_id *cm_id,
645922a8e9fSTom Tucker struct iw_cm_conn_param *iw_param)
646922a8e9fSTom Tucker {
647922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
648922a8e9fSTom Tucker struct ib_qp *qp;
649922a8e9fSTom Tucker unsigned long flags;
650922a8e9fSTom Tucker int ret;
651922a8e9fSTom Tucker
652922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
653922a8e9fSTom Tucker set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
654922a8e9fSTom Tucker
655922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
656922a8e9fSTom Tucker if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
657922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
658922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
659922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
660922a8e9fSTom Tucker return -EINVAL;
661922a8e9fSTom Tucker }
662922a8e9fSTom Tucker /* Get the ib_qp given the QPN */
663dd05cb82SKamal Heib qp = cm_id->device->ops.iw_get_qp(cm_id->device, iw_param->qpn);
664922a8e9fSTom Tucker if (!qp) {
665922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
66626012f07SAnimesh K Trivedi clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
66726012f07SAnimesh K Trivedi wake_up_all(&cm_id_priv->connect_wait);
668922a8e9fSTom Tucker return -EINVAL;
669922a8e9fSTom Tucker }
670dd05cb82SKamal Heib cm_id->device->ops.iw_add_ref(qp);
671922a8e9fSTom Tucker cm_id_priv->qp = qp;
672922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
673922a8e9fSTom Tucker
674dd05cb82SKamal Heib ret = cm_id->device->ops.iw_accept(cm_id, iw_param);
675922a8e9fSTom Tucker if (ret) {
676922a8e9fSTom Tucker /* An error on accept precludes provider events */
677922a8e9fSTom Tucker BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
678922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
679922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
68054102dd4SKrishnamraju Eraparaju qp = cm_id_priv->qp;
681922a8e9fSTom Tucker cm_id_priv->qp = NULL;
682922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
68354102dd4SKrishnamraju Eraparaju if (qp)
68454102dd4SKrishnamraju Eraparaju cm_id->device->ops.iw_rem_ref(qp);
685922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
686922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
687922a8e9fSTom Tucker }
688922a8e9fSTom Tucker
689922a8e9fSTom Tucker return ret;
690922a8e9fSTom Tucker }
691922a8e9fSTom Tucker EXPORT_SYMBOL(iw_cm_accept);
692922a8e9fSTom Tucker
693922a8e9fSTom Tucker /*
694922a8e9fSTom Tucker * Active Side: CM_ID <-- CONN_SENT
695922a8e9fSTom Tucker *
696922a8e9fSTom Tucker * If successful, results in the generation of a CONNECT_REPLY
697922a8e9fSTom Tucker * event. iw_cm_disconnect and iw_cm_destroy will block until the
698922a8e9fSTom Tucker * CONNECT_REPLY event is received from the provider.
699922a8e9fSTom Tucker */
iw_cm_connect(struct iw_cm_id * cm_id,struct iw_cm_conn_param * iw_param)700922a8e9fSTom Tucker int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
701922a8e9fSTom Tucker {
702922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
70313fccdb3SKrishna Kumar int ret;
704922a8e9fSTom Tucker unsigned long flags;
70554102dd4SKrishnamraju Eraparaju struct ib_qp *qp = NULL;
706922a8e9fSTom Tucker
707922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
708922a8e9fSTom Tucker
709922a8e9fSTom Tucker ret = alloc_work_entries(cm_id_priv, 4);
710922a8e9fSTom Tucker if (ret)
711922a8e9fSTom Tucker return ret;
712922a8e9fSTom Tucker
713922a8e9fSTom Tucker set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
714922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
715922a8e9fSTom Tucker
716922a8e9fSTom Tucker if (cm_id_priv->state != IW_CM_STATE_IDLE) {
717b493d91dSFaisal Latif ret = -EINVAL;
718b493d91dSFaisal Latif goto err;
719922a8e9fSTom Tucker }
720922a8e9fSTom Tucker
721922a8e9fSTom Tucker /* Get the ib_qp given the QPN */
722dd05cb82SKamal Heib qp = cm_id->device->ops.iw_get_qp(cm_id->device, iw_param->qpn);
723922a8e9fSTom Tucker if (!qp) {
724b493d91dSFaisal Latif ret = -EINVAL;
725b493d91dSFaisal Latif goto err;
726922a8e9fSTom Tucker }
727dd05cb82SKamal Heib cm_id->device->ops.iw_add_ref(qp);
728922a8e9fSTom Tucker cm_id_priv->qp = qp;
729922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_CONN_SENT;
730922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
731922a8e9fSTom Tucker
732b493d91dSFaisal Latif ret = iw_cm_map(cm_id, true);
733b493d91dSFaisal Latif if (!ret)
734dd05cb82SKamal Heib ret = cm_id->device->ops.iw_connect(cm_id, iw_param);
735b493d91dSFaisal Latif if (!ret)
736b493d91dSFaisal Latif return 0; /* success */
737b493d91dSFaisal Latif
738922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
73954102dd4SKrishnamraju Eraparaju qp = cm_id_priv->qp;
740922a8e9fSTom Tucker cm_id_priv->qp = NULL;
741922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
742b493d91dSFaisal Latif err:
743b493d91dSFaisal Latif spin_unlock_irqrestore(&cm_id_priv->lock, flags);
74454102dd4SKrishnamraju Eraparaju if (qp)
74554102dd4SKrishnamraju Eraparaju cm_id->device->ops.iw_rem_ref(qp);
746922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
747922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
748922a8e9fSTom Tucker return ret;
749922a8e9fSTom Tucker }
750922a8e9fSTom Tucker EXPORT_SYMBOL(iw_cm_connect);
751922a8e9fSTom Tucker
752922a8e9fSTom Tucker /*
753922a8e9fSTom Tucker * Passive Side: new CM_ID <-- CONN_RECV
754922a8e9fSTom Tucker *
755922a8e9fSTom Tucker * Handles an inbound connect request. The function creates a new
756922a8e9fSTom Tucker * iw_cm_id to represent the new connection and inherits the client
757922a8e9fSTom Tucker * callback function and other attributes from the listening parent.
758922a8e9fSTom Tucker *
759922a8e9fSTom Tucker * The work item contains a pointer to the listen_cm_id and the event. The
760922a8e9fSTom Tucker * listen_cm_id contains the client cm_handler, context and
761922a8e9fSTom Tucker * device. These are copied when the device is cloned. The event
762922a8e9fSTom Tucker * contains the new four tuple.
763922a8e9fSTom Tucker *
764922a8e9fSTom Tucker * An error on the child should not affect the parent, so this
765922a8e9fSTom Tucker * function does not return a value.
766922a8e9fSTom Tucker */
cm_conn_req_handler(struct iwcm_id_private * listen_id_priv,struct iw_cm_event * iw_event)767922a8e9fSTom Tucker static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,
768922a8e9fSTom Tucker struct iw_cm_event *iw_event)
769922a8e9fSTom Tucker {
770922a8e9fSTom Tucker unsigned long flags;
771922a8e9fSTom Tucker struct iw_cm_id *cm_id;
772922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
773922a8e9fSTom Tucker int ret;
774922a8e9fSTom Tucker
775922a8e9fSTom Tucker /*
776922a8e9fSTom Tucker * The provider should never generate a connection request
777922a8e9fSTom Tucker * event with a bad status.
778922a8e9fSTom Tucker */
779922a8e9fSTom Tucker BUG_ON(iw_event->status);
780922a8e9fSTom Tucker
781922a8e9fSTom Tucker cm_id = iw_create_cm_id(listen_id_priv->id.device,
782922a8e9fSTom Tucker listen_id_priv->id.cm_handler,
783922a8e9fSTom Tucker listen_id_priv->id.context);
784922a8e9fSTom Tucker /* If the cm_id could not be created, ignore the request */
785922a8e9fSTom Tucker if (IS_ERR(cm_id))
78683b96586SKrishna Kumar goto out;
787922a8e9fSTom Tucker
788922a8e9fSTom Tucker cm_id->provider_data = iw_event->provider_data;
789b493d91dSFaisal Latif cm_id->m_local_addr = iw_event->local_addr;
790b493d91dSFaisal Latif cm_id->m_remote_addr = iw_event->remote_addr;
791b493d91dSFaisal Latif cm_id->local_addr = listen_id_priv->id.local_addr;
792b493d91dSFaisal Latif
793b493d91dSFaisal Latif ret = iwpm_get_remote_info(&listen_id_priv->id.m_local_addr,
794b493d91dSFaisal Latif &iw_event->remote_addr,
795b493d91dSFaisal Latif &cm_id->remote_addr,
796b493d91dSFaisal Latif RDMA_NL_IWCM);
797b493d91dSFaisal Latif if (ret) {
798922a8e9fSTom Tucker cm_id->remote_addr = iw_event->remote_addr;
799b493d91dSFaisal Latif } else {
800b493d91dSFaisal Latif iw_cm_check_wildcard(&listen_id_priv->id.m_local_addr,
801b493d91dSFaisal Latif &iw_event->local_addr,
802b493d91dSFaisal Latif &cm_id->local_addr);
803b493d91dSFaisal Latif iw_event->local_addr = cm_id->local_addr;
804b493d91dSFaisal Latif iw_event->remote_addr = cm_id->remote_addr;
805b493d91dSFaisal Latif }
806922a8e9fSTom Tucker
807922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
808922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_CONN_RECV;
809922a8e9fSTom Tucker
8103eae7c9fSSteve Wise /*
8113eae7c9fSSteve Wise * We could be destroying the listening id. If so, ignore this
8123eae7c9fSSteve Wise * upcall.
8133eae7c9fSSteve Wise */
8143eae7c9fSSteve Wise spin_lock_irqsave(&listen_id_priv->lock, flags);
8153eae7c9fSSteve Wise if (listen_id_priv->state != IW_CM_STATE_LISTEN) {
8163eae7c9fSSteve Wise spin_unlock_irqrestore(&listen_id_priv->lock, flags);
8173eae7c9fSSteve Wise iw_cm_reject(cm_id, NULL, 0);
8183eae7c9fSSteve Wise iw_destroy_cm_id(cm_id);
8193eae7c9fSSteve Wise goto out;
8203eae7c9fSSteve Wise }
8213eae7c9fSSteve Wise spin_unlock_irqrestore(&listen_id_priv->lock, flags);
8223eae7c9fSSteve Wise
823922a8e9fSTom Tucker ret = alloc_work_entries(cm_id_priv, 3);
824922a8e9fSTom Tucker if (ret) {
825922a8e9fSTom Tucker iw_cm_reject(cm_id, NULL, 0);
826922a8e9fSTom Tucker iw_destroy_cm_id(cm_id);
82783b96586SKrishna Kumar goto out;
828922a8e9fSTom Tucker }
829922a8e9fSTom Tucker
830922a8e9fSTom Tucker /* Call the client CM handler */
831922a8e9fSTom Tucker ret = cm_id->cm_handler(cm_id, iw_event);
832922a8e9fSTom Tucker if (ret) {
833ebb90986SSteve Wise iw_cm_reject(cm_id, NULL, 0);
83459c68ac3SSteve Wise iw_destroy_cm_id(cm_id);
835922a8e9fSTom Tucker }
836922a8e9fSTom Tucker
83783b96586SKrishna Kumar out:
838922a8e9fSTom Tucker if (iw_event->private_data_len)
839922a8e9fSTom Tucker kfree(iw_event->private_data);
840922a8e9fSTom Tucker }
841922a8e9fSTom Tucker
842922a8e9fSTom Tucker /*
843922a8e9fSTom Tucker * Passive Side: CM_ID <-- ESTABLISHED
844922a8e9fSTom Tucker *
845922a8e9fSTom Tucker * The provider generated an ESTABLISHED event which means that
846922a8e9fSTom Tucker * the MPA negotion has completed successfully and we are now in MPA
847922a8e9fSTom Tucker * FPDU mode.
848922a8e9fSTom Tucker *
849922a8e9fSTom Tucker * This event can only be received in the CONN_RECV state. If the
850922a8e9fSTom Tucker * remote peer closed, the ESTABLISHED event would be received followed
851922a8e9fSTom Tucker * by the CLOSE event. If the app closes, it will block until we wake
852922a8e9fSTom Tucker * it up after processing this event.
853922a8e9fSTom Tucker */
cm_conn_est_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)854922a8e9fSTom Tucker static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv,
855922a8e9fSTom Tucker struct iw_cm_event *iw_event)
856922a8e9fSTom Tucker {
857922a8e9fSTom Tucker unsigned long flags;
85813fccdb3SKrishna Kumar int ret;
859922a8e9fSTom Tucker
860922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
861922a8e9fSTom Tucker
862922a8e9fSTom Tucker /*
863922a8e9fSTom Tucker * We clear the CONNECT_WAIT bit here to allow the callback
864922a8e9fSTom Tucker * function to call iw_cm_disconnect. Calling iw_destroy_cm_id
865922a8e9fSTom Tucker * from a callback handler is not allowed.
866922a8e9fSTom Tucker */
867922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
868922a8e9fSTom Tucker BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
869922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
870922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
871922a8e9fSTom Tucker ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
872922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
873922a8e9fSTom Tucker
874922a8e9fSTom Tucker return ret;
875922a8e9fSTom Tucker }
876922a8e9fSTom Tucker
877922a8e9fSTom Tucker /*
878922a8e9fSTom Tucker * Active Side: CM_ID <-- ESTABLISHED
879922a8e9fSTom Tucker *
880922a8e9fSTom Tucker * The app has called connect and is waiting for the established event to
881922a8e9fSTom Tucker * post it's requests to the server. This event will wake up anyone
882922a8e9fSTom Tucker * blocked in iw_cm_disconnect or iw_destroy_id.
883922a8e9fSTom Tucker */
cm_conn_rep_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)884922a8e9fSTom Tucker static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
885922a8e9fSTom Tucker struct iw_cm_event *iw_event)
886922a8e9fSTom Tucker {
88754102dd4SKrishnamraju Eraparaju struct ib_qp *qp = NULL;
888922a8e9fSTom Tucker unsigned long flags;
88913fccdb3SKrishna Kumar int ret;
890922a8e9fSTom Tucker
891922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
892922a8e9fSTom Tucker /*
893922a8e9fSTom Tucker * Clear the connect wait bit so a callback function calling
894922a8e9fSTom Tucker * iw_cm_disconnect will not wait and deadlock this thread
895922a8e9fSTom Tucker */
896922a8e9fSTom Tucker clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags);
897922a8e9fSTom Tucker BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT);
898d0c49bf3SRoland Dreier if (iw_event->status == 0) {
899b493d91dSFaisal Latif cm_id_priv->id.m_local_addr = iw_event->local_addr;
900b493d91dSFaisal Latif cm_id_priv->id.m_remote_addr = iw_event->remote_addr;
901b493d91dSFaisal Latif iw_event->local_addr = cm_id_priv->id.local_addr;
902b493d91dSFaisal Latif iw_event->remote_addr = cm_id_priv->id.remote_addr;
903922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
904922a8e9fSTom Tucker } else {
905922a8e9fSTom Tucker /* REJECTED or RESET */
90654102dd4SKrishnamraju Eraparaju qp = cm_id_priv->qp;
907922a8e9fSTom Tucker cm_id_priv->qp = NULL;
908922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
909922a8e9fSTom Tucker }
910922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
91154102dd4SKrishnamraju Eraparaju if (qp)
91254102dd4SKrishnamraju Eraparaju cm_id_priv->id.device->ops.iw_rem_ref(qp);
913922a8e9fSTom Tucker ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
914922a8e9fSTom Tucker
915922a8e9fSTom Tucker if (iw_event->private_data_len)
916922a8e9fSTom Tucker kfree(iw_event->private_data);
917922a8e9fSTom Tucker
918922a8e9fSTom Tucker /* Wake up waiters on connect complete */
919922a8e9fSTom Tucker wake_up_all(&cm_id_priv->connect_wait);
920922a8e9fSTom Tucker
921922a8e9fSTom Tucker return ret;
922922a8e9fSTom Tucker }
923922a8e9fSTom Tucker
924922a8e9fSTom Tucker /*
925922a8e9fSTom Tucker * CM_ID <-- CLOSING
926922a8e9fSTom Tucker *
927922a8e9fSTom Tucker * If in the ESTABLISHED state, move to CLOSING.
928922a8e9fSTom Tucker */
cm_disconnect_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)929922a8e9fSTom Tucker static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv,
930922a8e9fSTom Tucker struct iw_cm_event *iw_event)
931922a8e9fSTom Tucker {
932922a8e9fSTom Tucker unsigned long flags;
933922a8e9fSTom Tucker
934922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
935922a8e9fSTom Tucker if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED)
936922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_CLOSING;
937922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
938922a8e9fSTom Tucker }
939922a8e9fSTom Tucker
940922a8e9fSTom Tucker /*
941922a8e9fSTom Tucker * CM_ID <-- IDLE
942922a8e9fSTom Tucker *
943922a8e9fSTom Tucker * If in the ESTBLISHED or CLOSING states, the QP will have have been
944922a8e9fSTom Tucker * moved by the provider to the ERR state. Disassociate the CM_ID from
945922a8e9fSTom Tucker * the QP, move to IDLE, and remove the 'connected' reference.
946922a8e9fSTom Tucker *
947922a8e9fSTom Tucker * If in some other state, the cm_id was destroyed asynchronously.
948922a8e9fSTom Tucker * This is the last reference that will result in waking up
949922a8e9fSTom Tucker * the app thread blocked in iw_destroy_cm_id.
950922a8e9fSTom Tucker */
cm_close_handler(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)951922a8e9fSTom Tucker static int cm_close_handler(struct iwcm_id_private *cm_id_priv,
952922a8e9fSTom Tucker struct iw_cm_event *iw_event)
953922a8e9fSTom Tucker {
95454102dd4SKrishnamraju Eraparaju struct ib_qp *qp;
955922a8e9fSTom Tucker unsigned long flags;
95654102dd4SKrishnamraju Eraparaju int ret = 0, notify_event = 0;
957922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
95854102dd4SKrishnamraju Eraparaju qp = cm_id_priv->qp;
959922a8e9fSTom Tucker cm_id_priv->qp = NULL;
96054102dd4SKrishnamraju Eraparaju
961922a8e9fSTom Tucker switch (cm_id_priv->state) {
962922a8e9fSTom Tucker case IW_CM_STATE_ESTABLISHED:
963922a8e9fSTom Tucker case IW_CM_STATE_CLOSING:
964922a8e9fSTom Tucker cm_id_priv->state = IW_CM_STATE_IDLE;
96554102dd4SKrishnamraju Eraparaju notify_event = 1;
966922a8e9fSTom Tucker break;
967922a8e9fSTom Tucker case IW_CM_STATE_DESTROYING:
968922a8e9fSTom Tucker break;
969922a8e9fSTom Tucker default:
970922a8e9fSTom Tucker BUG();
971922a8e9fSTom Tucker }
972922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
973922a8e9fSTom Tucker
97454102dd4SKrishnamraju Eraparaju if (qp)
97554102dd4SKrishnamraju Eraparaju cm_id_priv->id.device->ops.iw_rem_ref(qp);
97654102dd4SKrishnamraju Eraparaju if (notify_event)
97754102dd4SKrishnamraju Eraparaju ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
978922a8e9fSTom Tucker return ret;
979922a8e9fSTom Tucker }
980922a8e9fSTom Tucker
process_event(struct iwcm_id_private * cm_id_priv,struct iw_cm_event * iw_event)981922a8e9fSTom Tucker static int process_event(struct iwcm_id_private *cm_id_priv,
982922a8e9fSTom Tucker struct iw_cm_event *iw_event)
983922a8e9fSTom Tucker {
984922a8e9fSTom Tucker int ret = 0;
985922a8e9fSTom Tucker
986922a8e9fSTom Tucker switch (iw_event->event) {
987922a8e9fSTom Tucker case IW_CM_EVENT_CONNECT_REQUEST:
988922a8e9fSTom Tucker cm_conn_req_handler(cm_id_priv, iw_event);
989922a8e9fSTom Tucker break;
990922a8e9fSTom Tucker case IW_CM_EVENT_CONNECT_REPLY:
991922a8e9fSTom Tucker ret = cm_conn_rep_handler(cm_id_priv, iw_event);
992922a8e9fSTom Tucker break;
993922a8e9fSTom Tucker case IW_CM_EVENT_ESTABLISHED:
994922a8e9fSTom Tucker ret = cm_conn_est_handler(cm_id_priv, iw_event);
995922a8e9fSTom Tucker break;
996922a8e9fSTom Tucker case IW_CM_EVENT_DISCONNECT:
997922a8e9fSTom Tucker cm_disconnect_handler(cm_id_priv, iw_event);
998922a8e9fSTom Tucker break;
999922a8e9fSTom Tucker case IW_CM_EVENT_CLOSE:
1000922a8e9fSTom Tucker ret = cm_close_handler(cm_id_priv, iw_event);
1001922a8e9fSTom Tucker break;
1002922a8e9fSTom Tucker default:
1003922a8e9fSTom Tucker BUG();
1004922a8e9fSTom Tucker }
1005922a8e9fSTom Tucker
1006922a8e9fSTom Tucker return ret;
1007922a8e9fSTom Tucker }
1008922a8e9fSTom Tucker
1009922a8e9fSTom Tucker /*
1010922a8e9fSTom Tucker * Process events on the work_list for the cm_id. If the callback
1011922a8e9fSTom Tucker * function requests that the cm_id be deleted, a flag is set in the
1012922a8e9fSTom Tucker * cm_id flags to indicate that when the last reference is
1013922a8e9fSTom Tucker * removed, the cm_id is to be destroyed. This is necessary to
1014922a8e9fSTom Tucker * distinguish between an object that will be destroyed by the app
1015922a8e9fSTom Tucker * thread asleep on the destroy_comp list vs. an object destroyed
1016922a8e9fSTom Tucker * here synchronously when the last reference is removed.
1017922a8e9fSTom Tucker */
cm_work_handler(struct work_struct * _work)1018c4028958SDavid Howells static void cm_work_handler(struct work_struct *_work)
1019922a8e9fSTom Tucker {
10204c1ac1b4SDavid Howells struct iwcm_work *work = container_of(_work, struct iwcm_work, work);
102133ba0fa9SKrishna Kumar struct iw_cm_event levent;
1022922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv = work->cm_id;
1023922a8e9fSTom Tucker unsigned long flags;
1024922a8e9fSTom Tucker int empty;
1025922a8e9fSTom Tucker int ret = 0;
1026922a8e9fSTom Tucker
1027922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
1028922a8e9fSTom Tucker empty = list_empty(&cm_id_priv->work_list);
1029922a8e9fSTom Tucker while (!empty) {
1030922a8e9fSTom Tucker work = list_entry(cm_id_priv->work_list.next,
1031922a8e9fSTom Tucker struct iwcm_work, list);
1032922a8e9fSTom Tucker list_del_init(&work->list);
1033922a8e9fSTom Tucker empty = list_empty(&cm_id_priv->work_list);
103433ba0fa9SKrishna Kumar levent = work->event;
1035922a8e9fSTom Tucker put_work(work);
1036922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1037922a8e9fSTom Tucker
103859c68ac3SSteve Wise if (!test_bit(IWCM_F_DROP_EVENTS, &cm_id_priv->flags)) {
103933ba0fa9SKrishna Kumar ret = process_event(cm_id_priv, &levent);
104059c68ac3SSteve Wise if (ret)
1041ff5bbbdeSBart Van Assche WARN_ON_ONCE(destroy_cm_id(&cm_id_priv->id));
104259c68ac3SSteve Wise } else
104359c68ac3SSteve Wise pr_debug("dropping event %d\n", levent.event);
104459c68ac3SSteve Wise if (iwcm_deref_id(cm_id_priv))
1045922a8e9fSTom Tucker return;
1046e413a823SSteve Wise if (empty)
1047e413a823SSteve Wise return;
1048922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
1049922a8e9fSTom Tucker }
1050922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1051922a8e9fSTom Tucker }
1052922a8e9fSTom Tucker
1053922a8e9fSTom Tucker /*
1054922a8e9fSTom Tucker * This function is called on interrupt context. Schedule events on
1055922a8e9fSTom Tucker * the iwcm_wq thread to allow callback functions to downcall into
1056922a8e9fSTom Tucker * the CM and/or block. Events are queued to a per-CM_ID
1057922a8e9fSTom Tucker * work_list. If this is the first event on the work_list, the work
1058922a8e9fSTom Tucker * element is also queued on the iwcm_wq thread.
1059922a8e9fSTom Tucker *
1060922a8e9fSTom Tucker * Each event holds a reference on the cm_id. Until the last posted
1061922a8e9fSTom Tucker * event has been delivered and processed, the cm_id cannot be
1062922a8e9fSTom Tucker * deleted.
1063922a8e9fSTom Tucker *
1064922a8e9fSTom Tucker * Returns:
1065922a8e9fSTom Tucker * 0 - the event was handled.
1066922a8e9fSTom Tucker * -ENOMEM - the event was not handled due to lack of resources.
1067922a8e9fSTom Tucker */
cm_event_handler(struct iw_cm_id * cm_id,struct iw_cm_event * iw_event)1068922a8e9fSTom Tucker static int cm_event_handler(struct iw_cm_id *cm_id,
1069922a8e9fSTom Tucker struct iw_cm_event *iw_event)
1070922a8e9fSTom Tucker {
1071922a8e9fSTom Tucker struct iwcm_work *work;
1072922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
1073922a8e9fSTom Tucker unsigned long flags;
1074922a8e9fSTom Tucker int ret = 0;
1075922a8e9fSTom Tucker
1076922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
1077922a8e9fSTom Tucker
1078922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
1079922a8e9fSTom Tucker work = get_work(cm_id_priv);
1080922a8e9fSTom Tucker if (!work) {
1081922a8e9fSTom Tucker ret = -ENOMEM;
1082922a8e9fSTom Tucker goto out;
1083922a8e9fSTom Tucker }
1084922a8e9fSTom Tucker
1085c4028958SDavid Howells INIT_WORK(&work->work, cm_work_handler);
1086922a8e9fSTom Tucker work->cm_id = cm_id_priv;
1087922a8e9fSTom Tucker work->event = *iw_event;
1088922a8e9fSTom Tucker
1089922a8e9fSTom Tucker if ((work->event.event == IW_CM_EVENT_CONNECT_REQUEST ||
1090922a8e9fSTom Tucker work->event.event == IW_CM_EVENT_CONNECT_REPLY) &&
1091922a8e9fSTom Tucker work->event.private_data_len) {
1092715a588fSKrishna Kumar ret = copy_private_data(&work->event);
1093922a8e9fSTom Tucker if (ret) {
1094922a8e9fSTom Tucker put_work(work);
1095922a8e9fSTom Tucker goto out;
1096922a8e9fSTom Tucker }
1097922a8e9fSTom Tucker }
1098922a8e9fSTom Tucker
109960dff56dSWeihang Li refcount_inc(&cm_id_priv->refcount);
1100922a8e9fSTom Tucker if (list_empty(&cm_id_priv->work_list)) {
1101922a8e9fSTom Tucker list_add_tail(&work->list, &cm_id_priv->work_list);
1102922a8e9fSTom Tucker queue_work(iwcm_wq, &work->work);
1103922a8e9fSTom Tucker } else
1104922a8e9fSTom Tucker list_add_tail(&work->list, &cm_id_priv->work_list);
1105922a8e9fSTom Tucker out:
1106922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1107922a8e9fSTom Tucker return ret;
1108922a8e9fSTom Tucker }
1109922a8e9fSTom Tucker
iwcm_init_qp_init_attr(struct iwcm_id_private * cm_id_priv,struct ib_qp_attr * qp_attr,int * qp_attr_mask)1110922a8e9fSTom Tucker static int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv,
1111922a8e9fSTom Tucker struct ib_qp_attr *qp_attr,
1112922a8e9fSTom Tucker int *qp_attr_mask)
1113922a8e9fSTom Tucker {
1114922a8e9fSTom Tucker unsigned long flags;
1115922a8e9fSTom Tucker int ret;
1116922a8e9fSTom Tucker
1117922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
1118922a8e9fSTom Tucker switch (cm_id_priv->state) {
1119922a8e9fSTom Tucker case IW_CM_STATE_IDLE:
1120922a8e9fSTom Tucker case IW_CM_STATE_CONN_SENT:
1121922a8e9fSTom Tucker case IW_CM_STATE_CONN_RECV:
1122922a8e9fSTom Tucker case IW_CM_STATE_ESTABLISHED:
1123922a8e9fSTom Tucker *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
11241ca8d156SDotan Barak qp_attr->qp_access_flags = IB_ACCESS_REMOTE_WRITE|
1125922a8e9fSTom Tucker IB_ACCESS_REMOTE_READ;
1126922a8e9fSTom Tucker ret = 0;
1127922a8e9fSTom Tucker break;
1128922a8e9fSTom Tucker default:
1129922a8e9fSTom Tucker ret = -EINVAL;
1130922a8e9fSTom Tucker break;
1131922a8e9fSTom Tucker }
1132922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1133922a8e9fSTom Tucker return ret;
1134922a8e9fSTom Tucker }
1135922a8e9fSTom Tucker
iwcm_init_qp_rts_attr(struct iwcm_id_private * cm_id_priv,struct ib_qp_attr * qp_attr,int * qp_attr_mask)1136922a8e9fSTom Tucker static int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv,
1137922a8e9fSTom Tucker struct ib_qp_attr *qp_attr,
1138922a8e9fSTom Tucker int *qp_attr_mask)
1139922a8e9fSTom Tucker {
1140922a8e9fSTom Tucker unsigned long flags;
1141922a8e9fSTom Tucker int ret;
1142922a8e9fSTom Tucker
1143922a8e9fSTom Tucker spin_lock_irqsave(&cm_id_priv->lock, flags);
1144922a8e9fSTom Tucker switch (cm_id_priv->state) {
1145922a8e9fSTom Tucker case IW_CM_STATE_IDLE:
1146922a8e9fSTom Tucker case IW_CM_STATE_CONN_SENT:
1147922a8e9fSTom Tucker case IW_CM_STATE_CONN_RECV:
1148922a8e9fSTom Tucker case IW_CM_STATE_ESTABLISHED:
1149922a8e9fSTom Tucker *qp_attr_mask = 0;
1150922a8e9fSTom Tucker ret = 0;
1151922a8e9fSTom Tucker break;
1152922a8e9fSTom Tucker default:
1153922a8e9fSTom Tucker ret = -EINVAL;
1154922a8e9fSTom Tucker break;
1155922a8e9fSTom Tucker }
1156922a8e9fSTom Tucker spin_unlock_irqrestore(&cm_id_priv->lock, flags);
1157922a8e9fSTom Tucker return ret;
1158922a8e9fSTom Tucker }
1159922a8e9fSTom Tucker
iw_cm_init_qp_attr(struct iw_cm_id * cm_id,struct ib_qp_attr * qp_attr,int * qp_attr_mask)1160922a8e9fSTom Tucker int iw_cm_init_qp_attr(struct iw_cm_id *cm_id,
1161922a8e9fSTom Tucker struct ib_qp_attr *qp_attr,
1162922a8e9fSTom Tucker int *qp_attr_mask)
1163922a8e9fSTom Tucker {
1164922a8e9fSTom Tucker struct iwcm_id_private *cm_id_priv;
1165922a8e9fSTom Tucker int ret;
1166922a8e9fSTom Tucker
1167922a8e9fSTom Tucker cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
1168922a8e9fSTom Tucker switch (qp_attr->qp_state) {
1169922a8e9fSTom Tucker case IB_QPS_INIT:
1170922a8e9fSTom Tucker case IB_QPS_RTR:
1171922a8e9fSTom Tucker ret = iwcm_init_qp_init_attr(cm_id_priv,
1172922a8e9fSTom Tucker qp_attr, qp_attr_mask);
1173922a8e9fSTom Tucker break;
1174922a8e9fSTom Tucker case IB_QPS_RTS:
1175922a8e9fSTom Tucker ret = iwcm_init_qp_rts_attr(cm_id_priv,
1176922a8e9fSTom Tucker qp_attr, qp_attr_mask);
1177922a8e9fSTom Tucker break;
1178922a8e9fSTom Tucker default:
1179922a8e9fSTom Tucker ret = -EINVAL;
1180922a8e9fSTom Tucker break;
1181922a8e9fSTom Tucker }
1182922a8e9fSTom Tucker return ret;
1183922a8e9fSTom Tucker }
1184922a8e9fSTom Tucker EXPORT_SYMBOL(iw_cm_init_qp_attr);
1185922a8e9fSTom Tucker
iw_cm_init(void)1186922a8e9fSTom Tucker static int __init iw_cm_init(void)
1187922a8e9fSTom Tucker {
1188b493d91dSFaisal Latif int ret;
1189b493d91dSFaisal Latif
1190b493d91dSFaisal Latif ret = iwpm_init(RDMA_NL_IWCM);
1191b493d91dSFaisal Latif if (ret)
1192e677b72aSLeon Romanovsky return ret;
1193e677b72aSLeon Romanovsky
1194*8b7df763SZhu Yanjun iwcm_wq = alloc_ordered_workqueue("iw_cm_wq", WQ_MEM_RECLAIM);
1195922a8e9fSTom Tucker if (!iwcm_wq)
1196e677b72aSLeon Romanovsky goto err_alloc;
1197922a8e9fSTom Tucker
11982f0304d2SSteve Wise iwcm_ctl_table_hdr = register_net_sysctl(&init_net, "net/iw_cm",
11992f0304d2SSteve Wise iwcm_ctl_table);
12002f0304d2SSteve Wise if (!iwcm_ctl_table_hdr) {
12012f0304d2SSteve Wise pr_err("iw_cm: couldn't register sysctl paths\n");
1202e677b72aSLeon Romanovsky goto err_sysctl;
12032f0304d2SSteve Wise }
12042f0304d2SSteve Wise
1205e677b72aSLeon Romanovsky rdma_nl_register(RDMA_NL_IWCM, iwcm_nl_cb_table);
1206922a8e9fSTom Tucker return 0;
1207e677b72aSLeon Romanovsky
1208e677b72aSLeon Romanovsky err_sysctl:
1209e677b72aSLeon Romanovsky destroy_workqueue(iwcm_wq);
1210e677b72aSLeon Romanovsky err_alloc:
1211e677b72aSLeon Romanovsky iwpm_exit(RDMA_NL_IWCM);
1212e677b72aSLeon Romanovsky return -ENOMEM;
1213922a8e9fSTom Tucker }
1214922a8e9fSTom Tucker
iw_cm_cleanup(void)1215922a8e9fSTom Tucker static void __exit iw_cm_cleanup(void)
1216922a8e9fSTom Tucker {
1217e677b72aSLeon Romanovsky rdma_nl_unregister(RDMA_NL_IWCM);
12182f0304d2SSteve Wise unregister_net_sysctl_table(iwcm_ctl_table_hdr);
1219922a8e9fSTom Tucker destroy_workqueue(iwcm_wq);
1220b493d91dSFaisal Latif iwpm_exit(RDMA_NL_IWCM);
1221922a8e9fSTom Tucker }
1222922a8e9fSTom Tucker
1223e3bf14bdSJason Gunthorpe MODULE_ALIAS_RDMA_NETLINK(RDMA_NL_IWCM, 2);
1224e3bf14bdSJason Gunthorpe
1225922a8e9fSTom Tucker module_init(iw_cm_init);
1226922a8e9fSTom Tucker module_exit(iw_cm_cleanup);
1227