1920d93eaSCheng Xu // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2920d93eaSCheng Xu 
3920d93eaSCheng Xu /* Authors: Cheng Xu <chengyou@linux.alibaba.com> */
4920d93eaSCheng Xu /*          Kai Shen <kaishen@linux.alibaba.com> */
5920d93eaSCheng Xu /* Copyright (c) 2020-2022, Alibaba Group. */
6920d93eaSCheng Xu 
7920d93eaSCheng Xu /* Authors: Bernard Metzler <bmt@zurich.ibm.com> */
8920d93eaSCheng Xu /*          Fredy Neeser */
9920d93eaSCheng Xu /*          Greg Joyce <greg@opengridcomputing.com> */
10920d93eaSCheng Xu /* Copyright (c) 2008-2019, IBM Corporation */
11920d93eaSCheng Xu /* Copyright (c) 2017, Open Grid Computing, Inc. */
12920d93eaSCheng Xu 
13920d93eaSCheng Xu #include <linux/workqueue.h>
14*40e0b090SPeilin Ye #include <trace/events/sock.h>
15920d93eaSCheng Xu 
16920d93eaSCheng Xu #include "erdma.h"
17920d93eaSCheng Xu #include "erdma_cm.h"
18920d93eaSCheng Xu #include "erdma_verbs.h"
19920d93eaSCheng Xu 
20920d93eaSCheng Xu static struct workqueue_struct *erdma_cm_wq;
21920d93eaSCheng Xu 
22920d93eaSCheng Xu static void erdma_cm_llp_state_change(struct sock *sk);
23920d93eaSCheng Xu static void erdma_cm_llp_data_ready(struct sock *sk);
24920d93eaSCheng Xu static void erdma_cm_llp_error_report(struct sock *sk);
25920d93eaSCheng Xu 
erdma_sk_assign_cm_upcalls(struct sock * sk)26920d93eaSCheng Xu static void erdma_sk_assign_cm_upcalls(struct sock *sk)
27920d93eaSCheng Xu {
28920d93eaSCheng Xu 	write_lock_bh(&sk->sk_callback_lock);
29920d93eaSCheng Xu 	sk->sk_state_change = erdma_cm_llp_state_change;
30920d93eaSCheng Xu 	sk->sk_data_ready = erdma_cm_llp_data_ready;
31920d93eaSCheng Xu 	sk->sk_error_report = erdma_cm_llp_error_report;
32920d93eaSCheng Xu 	write_unlock_bh(&sk->sk_callback_lock);
33920d93eaSCheng Xu }
34920d93eaSCheng Xu 
erdma_sk_save_upcalls(struct sock * sk)35920d93eaSCheng Xu static void erdma_sk_save_upcalls(struct sock *sk)
36920d93eaSCheng Xu {
37920d93eaSCheng Xu 	struct erdma_cep *cep = sk_to_cep(sk);
38920d93eaSCheng Xu 
39920d93eaSCheng Xu 	write_lock_bh(&sk->sk_callback_lock);
40920d93eaSCheng Xu 	cep->sk_state_change = sk->sk_state_change;
41920d93eaSCheng Xu 	cep->sk_data_ready = sk->sk_data_ready;
42920d93eaSCheng Xu 	cep->sk_error_report = sk->sk_error_report;
43920d93eaSCheng Xu 	write_unlock_bh(&sk->sk_callback_lock);
44920d93eaSCheng Xu }
45920d93eaSCheng Xu 
erdma_sk_restore_upcalls(struct sock * sk,struct erdma_cep * cep)46920d93eaSCheng Xu static void erdma_sk_restore_upcalls(struct sock *sk, struct erdma_cep *cep)
47920d93eaSCheng Xu {
48920d93eaSCheng Xu 	sk->sk_state_change = cep->sk_state_change;
49920d93eaSCheng Xu 	sk->sk_data_ready = cep->sk_data_ready;
50920d93eaSCheng Xu 	sk->sk_error_report = cep->sk_error_report;
51920d93eaSCheng Xu 	sk->sk_user_data = NULL;
52920d93eaSCheng Xu }
53920d93eaSCheng Xu 
erdma_socket_disassoc(struct socket * s)54920d93eaSCheng Xu static void erdma_socket_disassoc(struct socket *s)
55920d93eaSCheng Xu {
56920d93eaSCheng Xu 	struct sock *sk = s->sk;
57920d93eaSCheng Xu 	struct erdma_cep *cep;
58920d93eaSCheng Xu 
59920d93eaSCheng Xu 	if (sk) {
60920d93eaSCheng Xu 		write_lock_bh(&sk->sk_callback_lock);
61920d93eaSCheng Xu 		cep = sk_to_cep(sk);
62920d93eaSCheng Xu 		if (cep) {
63920d93eaSCheng Xu 			erdma_sk_restore_upcalls(sk, cep);
64920d93eaSCheng Xu 			erdma_cep_put(cep);
65920d93eaSCheng Xu 		} else {
66920d93eaSCheng Xu 			WARN_ON_ONCE(1);
67920d93eaSCheng Xu 		}
68920d93eaSCheng Xu 		write_unlock_bh(&sk->sk_callback_lock);
69920d93eaSCheng Xu 	} else {
70920d93eaSCheng Xu 		WARN_ON_ONCE(1);
71920d93eaSCheng Xu 	}
72920d93eaSCheng Xu }
73920d93eaSCheng Xu 
erdma_cep_socket_assoc(struct erdma_cep * cep,struct socket * s)74920d93eaSCheng Xu static void erdma_cep_socket_assoc(struct erdma_cep *cep, struct socket *s)
75920d93eaSCheng Xu {
76920d93eaSCheng Xu 	cep->sock = s;
77920d93eaSCheng Xu 	erdma_cep_get(cep);
78920d93eaSCheng Xu 	s->sk->sk_user_data = cep;
79920d93eaSCheng Xu 
80920d93eaSCheng Xu 	erdma_sk_save_upcalls(s->sk);
81920d93eaSCheng Xu 	erdma_sk_assign_cm_upcalls(s->sk);
82920d93eaSCheng Xu }
83920d93eaSCheng Xu 
erdma_disassoc_listen_cep(struct erdma_cep * cep)84920d93eaSCheng Xu static void erdma_disassoc_listen_cep(struct erdma_cep *cep)
85920d93eaSCheng Xu {
86920d93eaSCheng Xu 	if (cep->listen_cep) {
87920d93eaSCheng Xu 		erdma_cep_put(cep->listen_cep);
88920d93eaSCheng Xu 		cep->listen_cep = NULL;
89920d93eaSCheng Xu 	}
90920d93eaSCheng Xu }
91920d93eaSCheng Xu 
erdma_cep_alloc(struct erdma_dev * dev)92920d93eaSCheng Xu static struct erdma_cep *erdma_cep_alloc(struct erdma_dev *dev)
93920d93eaSCheng Xu {
94920d93eaSCheng Xu 	struct erdma_cep *cep = kzalloc(sizeof(*cep), GFP_KERNEL);
95920d93eaSCheng Xu 	unsigned long flags;
96920d93eaSCheng Xu 
97920d93eaSCheng Xu 	if (!cep)
98920d93eaSCheng Xu 		return NULL;
99920d93eaSCheng Xu 
100920d93eaSCheng Xu 	INIT_LIST_HEAD(&cep->listenq);
101920d93eaSCheng Xu 	INIT_LIST_HEAD(&cep->devq);
102920d93eaSCheng Xu 	INIT_LIST_HEAD(&cep->work_freelist);
103920d93eaSCheng Xu 
104920d93eaSCheng Xu 	kref_init(&cep->ref);
105920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_IDLE;
106920d93eaSCheng Xu 	init_waitqueue_head(&cep->waitq);
107920d93eaSCheng Xu 	spin_lock_init(&cep->lock);
108920d93eaSCheng Xu 	cep->dev = dev;
109920d93eaSCheng Xu 
110920d93eaSCheng Xu 	spin_lock_irqsave(&dev->lock, flags);
111920d93eaSCheng Xu 	list_add_tail(&cep->devq, &dev->cep_list);
112920d93eaSCheng Xu 	spin_unlock_irqrestore(&dev->lock, flags);
113920d93eaSCheng Xu 
114920d93eaSCheng Xu 	return cep;
115920d93eaSCheng Xu }
116920d93eaSCheng Xu 
erdma_cm_free_work(struct erdma_cep * cep)117920d93eaSCheng Xu static void erdma_cm_free_work(struct erdma_cep *cep)
118920d93eaSCheng Xu {
119920d93eaSCheng Xu 	struct list_head *w, *tmp;
120920d93eaSCheng Xu 	struct erdma_cm_work *work;
121920d93eaSCheng Xu 
122920d93eaSCheng Xu 	list_for_each_safe(w, tmp, &cep->work_freelist) {
123920d93eaSCheng Xu 		work = list_entry(w, struct erdma_cm_work, list);
124920d93eaSCheng Xu 		list_del(&work->list);
125920d93eaSCheng Xu 		kfree(work);
126920d93eaSCheng Xu 	}
127920d93eaSCheng Xu }
128920d93eaSCheng Xu 
erdma_cancel_mpatimer(struct erdma_cep * cep)129920d93eaSCheng Xu static void erdma_cancel_mpatimer(struct erdma_cep *cep)
130920d93eaSCheng Xu {
131920d93eaSCheng Xu 	spin_lock_bh(&cep->lock);
132920d93eaSCheng Xu 	if (cep->mpa_timer) {
133920d93eaSCheng Xu 		if (cancel_delayed_work(&cep->mpa_timer->work)) {
134920d93eaSCheng Xu 			erdma_cep_put(cep);
135920d93eaSCheng Xu 			kfree(cep->mpa_timer);
136920d93eaSCheng Xu 		}
137920d93eaSCheng Xu 		cep->mpa_timer = NULL;
138920d93eaSCheng Xu 	}
139920d93eaSCheng Xu 	spin_unlock_bh(&cep->lock);
140920d93eaSCheng Xu }
141920d93eaSCheng Xu 
erdma_put_work(struct erdma_cm_work * work)142920d93eaSCheng Xu static void erdma_put_work(struct erdma_cm_work *work)
143920d93eaSCheng Xu {
144920d93eaSCheng Xu 	INIT_LIST_HEAD(&work->list);
145920d93eaSCheng Xu 	spin_lock_bh(&work->cep->lock);
146920d93eaSCheng Xu 	list_add(&work->list, &work->cep->work_freelist);
147920d93eaSCheng Xu 	spin_unlock_bh(&work->cep->lock);
148920d93eaSCheng Xu }
149920d93eaSCheng Xu 
erdma_cep_set_inuse(struct erdma_cep * cep)150920d93eaSCheng Xu static void erdma_cep_set_inuse(struct erdma_cep *cep)
151920d93eaSCheng Xu {
152920d93eaSCheng Xu 	unsigned long flags;
153920d93eaSCheng Xu 
154920d93eaSCheng Xu 	spin_lock_irqsave(&cep->lock, flags);
155920d93eaSCheng Xu 	while (cep->in_use) {
156920d93eaSCheng Xu 		spin_unlock_irqrestore(&cep->lock, flags);
157920d93eaSCheng Xu 		wait_event_interruptible(cep->waitq, !cep->in_use);
158920d93eaSCheng Xu 		if (signal_pending(current))
159920d93eaSCheng Xu 			flush_signals(current);
160920d93eaSCheng Xu 
161920d93eaSCheng Xu 		spin_lock_irqsave(&cep->lock, flags);
162920d93eaSCheng Xu 	}
163920d93eaSCheng Xu 
164920d93eaSCheng Xu 	cep->in_use = 1;
165920d93eaSCheng Xu 	spin_unlock_irqrestore(&cep->lock, flags);
166920d93eaSCheng Xu }
167920d93eaSCheng Xu 
erdma_cep_set_free(struct erdma_cep * cep)168920d93eaSCheng Xu static void erdma_cep_set_free(struct erdma_cep *cep)
169920d93eaSCheng Xu {
170920d93eaSCheng Xu 	unsigned long flags;
171920d93eaSCheng Xu 
172920d93eaSCheng Xu 	spin_lock_irqsave(&cep->lock, flags);
173920d93eaSCheng Xu 	cep->in_use = 0;
174920d93eaSCheng Xu 	spin_unlock_irqrestore(&cep->lock, flags);
175920d93eaSCheng Xu 
176920d93eaSCheng Xu 	wake_up(&cep->waitq);
177920d93eaSCheng Xu }
178920d93eaSCheng Xu 
__erdma_cep_dealloc(struct kref * ref)179920d93eaSCheng Xu static void __erdma_cep_dealloc(struct kref *ref)
180920d93eaSCheng Xu {
181920d93eaSCheng Xu 	struct erdma_cep *cep = container_of(ref, struct erdma_cep, ref);
182920d93eaSCheng Xu 	struct erdma_dev *dev = cep->dev;
183920d93eaSCheng Xu 	unsigned long flags;
184920d93eaSCheng Xu 
185920d93eaSCheng Xu 	WARN_ON(cep->listen_cep);
186920d93eaSCheng Xu 
187920d93eaSCheng Xu 	kfree(cep->private_data);
188920d93eaSCheng Xu 	kfree(cep->mpa.pdata);
189920d93eaSCheng Xu 	spin_lock_bh(&cep->lock);
190920d93eaSCheng Xu 	if (!list_empty(&cep->work_freelist))
191920d93eaSCheng Xu 		erdma_cm_free_work(cep);
192920d93eaSCheng Xu 	spin_unlock_bh(&cep->lock);
193920d93eaSCheng Xu 
194920d93eaSCheng Xu 	spin_lock_irqsave(&dev->lock, flags);
195920d93eaSCheng Xu 	list_del(&cep->devq);
196920d93eaSCheng Xu 	spin_unlock_irqrestore(&dev->lock, flags);
197920d93eaSCheng Xu 	kfree(cep);
198920d93eaSCheng Xu }
199920d93eaSCheng Xu 
erdma_get_work(struct erdma_cep * cep)200920d93eaSCheng Xu static struct erdma_cm_work *erdma_get_work(struct erdma_cep *cep)
201920d93eaSCheng Xu {
202920d93eaSCheng Xu 	struct erdma_cm_work *work = NULL;
203920d93eaSCheng Xu 
204920d93eaSCheng Xu 	spin_lock_bh(&cep->lock);
205920d93eaSCheng Xu 	if (!list_empty(&cep->work_freelist)) {
206920d93eaSCheng Xu 		work = list_entry(cep->work_freelist.next, struct erdma_cm_work,
207920d93eaSCheng Xu 				  list);
208920d93eaSCheng Xu 		list_del_init(&work->list);
209920d93eaSCheng Xu 	}
210920d93eaSCheng Xu 
211920d93eaSCheng Xu 	spin_unlock_bh(&cep->lock);
212920d93eaSCheng Xu 	return work;
213920d93eaSCheng Xu }
214920d93eaSCheng Xu 
erdma_cm_alloc_work(struct erdma_cep * cep,int num)215920d93eaSCheng Xu static int erdma_cm_alloc_work(struct erdma_cep *cep, int num)
216920d93eaSCheng Xu {
217920d93eaSCheng Xu 	struct erdma_cm_work *work;
218920d93eaSCheng Xu 
219920d93eaSCheng Xu 	while (num--) {
220920d93eaSCheng Xu 		work = kmalloc(sizeof(*work), GFP_KERNEL);
221920d93eaSCheng Xu 		if (!work) {
222920d93eaSCheng Xu 			if (!(list_empty(&cep->work_freelist)))
223920d93eaSCheng Xu 				erdma_cm_free_work(cep);
224920d93eaSCheng Xu 			return -ENOMEM;
225920d93eaSCheng Xu 		}
226920d93eaSCheng Xu 		work->cep = cep;
227920d93eaSCheng Xu 		INIT_LIST_HEAD(&work->list);
228920d93eaSCheng Xu 		list_add(&work->list, &cep->work_freelist);
229920d93eaSCheng Xu 	}
230920d93eaSCheng Xu 
231920d93eaSCheng Xu 	return 0;
232920d93eaSCheng Xu }
233920d93eaSCheng Xu 
erdma_cm_upcall(struct erdma_cep * cep,enum iw_cm_event_type reason,int status)234920d93eaSCheng Xu static int erdma_cm_upcall(struct erdma_cep *cep, enum iw_cm_event_type reason,
235920d93eaSCheng Xu 			   int status)
236920d93eaSCheng Xu {
237920d93eaSCheng Xu 	struct iw_cm_event event;
238920d93eaSCheng Xu 	struct iw_cm_id *cm_id;
239920d93eaSCheng Xu 
240920d93eaSCheng Xu 	memset(&event, 0, sizeof(event));
241920d93eaSCheng Xu 	event.status = status;
242920d93eaSCheng Xu 	event.event = reason;
243920d93eaSCheng Xu 
244920d93eaSCheng Xu 	if (reason == IW_CM_EVENT_CONNECT_REQUEST) {
245920d93eaSCheng Xu 		event.provider_data = cep;
246920d93eaSCheng Xu 		cm_id = cep->listen_cep->cm_id;
247920d93eaSCheng Xu 
248920d93eaSCheng Xu 		event.ird = cep->dev->attrs.max_ird;
249920d93eaSCheng Xu 		event.ord = cep->dev->attrs.max_ord;
250920d93eaSCheng Xu 	} else {
251920d93eaSCheng Xu 		cm_id = cep->cm_id;
252920d93eaSCheng Xu 	}
253920d93eaSCheng Xu 
254920d93eaSCheng Xu 	if (reason == IW_CM_EVENT_CONNECT_REQUEST ||
255920d93eaSCheng Xu 	    reason == IW_CM_EVENT_CONNECT_REPLY) {
256920d93eaSCheng Xu 		u16 pd_len = be16_to_cpu(cep->mpa.hdr.params.pd_len);
257920d93eaSCheng Xu 
258920d93eaSCheng Xu 		if (pd_len && cep->mpa.pdata) {
259920d93eaSCheng Xu 			event.private_data_len = pd_len;
260920d93eaSCheng Xu 			event.private_data = cep->mpa.pdata;
261920d93eaSCheng Xu 		}
262920d93eaSCheng Xu 
263920d93eaSCheng Xu 		getname_local(cep->sock, &event.local_addr);
264920d93eaSCheng Xu 		getname_peer(cep->sock, &event.remote_addr);
265920d93eaSCheng Xu 	}
266920d93eaSCheng Xu 
267920d93eaSCheng Xu 	return cm_id->event_handler(cm_id, &event);
268920d93eaSCheng Xu }
269920d93eaSCheng Xu 
erdma_qp_cm_drop(struct erdma_qp * qp)270920d93eaSCheng Xu void erdma_qp_cm_drop(struct erdma_qp *qp)
271920d93eaSCheng Xu {
272920d93eaSCheng Xu 	struct erdma_cep *cep = qp->cep;
273920d93eaSCheng Xu 
274920d93eaSCheng Xu 	if (!qp->cep)
275920d93eaSCheng Xu 		return;
276920d93eaSCheng Xu 
277920d93eaSCheng Xu 	erdma_cep_set_inuse(cep);
278920d93eaSCheng Xu 
279920d93eaSCheng Xu 	/* already closed. */
280920d93eaSCheng Xu 	if (cep->state == ERDMA_EPSTATE_CLOSED)
281920d93eaSCheng Xu 		goto out;
282920d93eaSCheng Xu 
283920d93eaSCheng Xu 	if (cep->cm_id) {
284920d93eaSCheng Xu 		switch (cep->state) {
285920d93eaSCheng Xu 		case ERDMA_EPSTATE_AWAIT_MPAREP:
286920d93eaSCheng Xu 			erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY,
287920d93eaSCheng Xu 					-EINVAL);
288920d93eaSCheng Xu 			break;
289920d93eaSCheng Xu 		case ERDMA_EPSTATE_RDMA_MODE:
290920d93eaSCheng Xu 			erdma_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0);
291920d93eaSCheng Xu 			break;
292920d93eaSCheng Xu 		case ERDMA_EPSTATE_IDLE:
293920d93eaSCheng Xu 		case ERDMA_EPSTATE_LISTENING:
294920d93eaSCheng Xu 		case ERDMA_EPSTATE_CONNECTING:
295920d93eaSCheng Xu 		case ERDMA_EPSTATE_AWAIT_MPAREQ:
296920d93eaSCheng Xu 		case ERDMA_EPSTATE_RECVD_MPAREQ:
297920d93eaSCheng Xu 		case ERDMA_EPSTATE_CLOSED:
298920d93eaSCheng Xu 		default:
299920d93eaSCheng Xu 			break;
300920d93eaSCheng Xu 		}
301920d93eaSCheng Xu 		cep->cm_id->rem_ref(cep->cm_id);
302920d93eaSCheng Xu 		cep->cm_id = NULL;
303920d93eaSCheng Xu 		erdma_cep_put(cep);
304920d93eaSCheng Xu 	}
305920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_CLOSED;
306920d93eaSCheng Xu 
307920d93eaSCheng Xu 	if (cep->sock) {
308920d93eaSCheng Xu 		erdma_socket_disassoc(cep->sock);
309920d93eaSCheng Xu 		sock_release(cep->sock);
310920d93eaSCheng Xu 		cep->sock = NULL;
311920d93eaSCheng Xu 	}
312920d93eaSCheng Xu 
313920d93eaSCheng Xu 	if (cep->qp) {
314920d93eaSCheng Xu 		cep->qp = NULL;
315920d93eaSCheng Xu 		erdma_qp_put(qp);
316920d93eaSCheng Xu 	}
317920d93eaSCheng Xu out:
318920d93eaSCheng Xu 	erdma_cep_set_free(cep);
319920d93eaSCheng Xu }
320920d93eaSCheng Xu 
erdma_cep_put(struct erdma_cep * cep)321920d93eaSCheng Xu void erdma_cep_put(struct erdma_cep *cep)
322920d93eaSCheng Xu {
323920d93eaSCheng Xu 	WARN_ON(kref_read(&cep->ref) < 1);
324920d93eaSCheng Xu 	kref_put(&cep->ref, __erdma_cep_dealloc);
325920d93eaSCheng Xu }
326920d93eaSCheng Xu 
erdma_cep_get(struct erdma_cep * cep)327920d93eaSCheng Xu void erdma_cep_get(struct erdma_cep *cep)
328920d93eaSCheng Xu {
329920d93eaSCheng Xu 	kref_get(&cep->ref);
330920d93eaSCheng Xu }
331920d93eaSCheng Xu 
erdma_send_mpareqrep(struct erdma_cep * cep,const void * pdata,u8 pd_len)332920d93eaSCheng Xu static int erdma_send_mpareqrep(struct erdma_cep *cep, const void *pdata,
333920d93eaSCheng Xu 				u8 pd_len)
334920d93eaSCheng Xu {
335920d93eaSCheng Xu 	struct socket *s = cep->sock;
336920d93eaSCheng Xu 	struct mpa_rr *rr = &cep->mpa.hdr;
337920d93eaSCheng Xu 	struct kvec iov[3];
338920d93eaSCheng Xu 	struct msghdr msg;
339920d93eaSCheng Xu 	int iovec_num = 0;
340920d93eaSCheng Xu 	int ret;
341920d93eaSCheng Xu 	int mpa_len;
342920d93eaSCheng Xu 
343920d93eaSCheng Xu 	memset(&msg, 0, sizeof(msg));
344920d93eaSCheng Xu 
345920d93eaSCheng Xu 	rr->params.pd_len = cpu_to_be16(pd_len);
346920d93eaSCheng Xu 
347920d93eaSCheng Xu 	iov[iovec_num].iov_base = rr;
348920d93eaSCheng Xu 	iov[iovec_num].iov_len = sizeof(*rr);
349920d93eaSCheng Xu 	iovec_num++;
350920d93eaSCheng Xu 	mpa_len = sizeof(*rr);
351920d93eaSCheng Xu 
352920d93eaSCheng Xu 	iov[iovec_num].iov_base = &cep->mpa.ext_data;
353920d93eaSCheng Xu 	iov[iovec_num].iov_len = sizeof(cep->mpa.ext_data);
354920d93eaSCheng Xu 	iovec_num++;
355920d93eaSCheng Xu 	mpa_len += sizeof(cep->mpa.ext_data);
356920d93eaSCheng Xu 
357920d93eaSCheng Xu 	if (pd_len) {
358920d93eaSCheng Xu 		iov[iovec_num].iov_base = (char *)pdata;
359920d93eaSCheng Xu 		iov[iovec_num].iov_len = pd_len;
360920d93eaSCheng Xu 		mpa_len += pd_len;
361920d93eaSCheng Xu 		iovec_num++;
362920d93eaSCheng Xu 	}
363920d93eaSCheng Xu 
364920d93eaSCheng Xu 	ret = kernel_sendmsg(s, &msg, iov, iovec_num, mpa_len);
365920d93eaSCheng Xu 
366920d93eaSCheng Xu 	return ret < 0 ? ret : 0;
367920d93eaSCheng Xu }
368920d93eaSCheng Xu 
ksock_recv(struct socket * sock,char * buf,size_t size,int flags)369920d93eaSCheng Xu static inline int ksock_recv(struct socket *sock, char *buf, size_t size,
370920d93eaSCheng Xu 			     int flags)
371920d93eaSCheng Xu {
372920d93eaSCheng Xu 	struct kvec iov = { buf, size };
373920d93eaSCheng Xu 	struct msghdr msg = { .msg_name = NULL, .msg_flags = flags };
374920d93eaSCheng Xu 
375920d93eaSCheng Xu 	return kernel_recvmsg(sock, &msg, &iov, 1, size, flags);
376920d93eaSCheng Xu }
377920d93eaSCheng Xu 
__recv_mpa_hdr(struct erdma_cep * cep,int hdr_rcvd,char * hdr,int hdr_size,int * rcvd_out)378920d93eaSCheng Xu static int __recv_mpa_hdr(struct erdma_cep *cep, int hdr_rcvd, char *hdr,
379920d93eaSCheng Xu 			  int hdr_size, int *rcvd_out)
380920d93eaSCheng Xu {
381920d93eaSCheng Xu 	struct socket *s = cep->sock;
382920d93eaSCheng Xu 	int rcvd;
383920d93eaSCheng Xu 
384920d93eaSCheng Xu 	*rcvd_out = 0;
385920d93eaSCheng Xu 	if (hdr_rcvd < hdr_size) {
386920d93eaSCheng Xu 		rcvd = ksock_recv(s, hdr + hdr_rcvd, hdr_size - hdr_rcvd,
387920d93eaSCheng Xu 				  MSG_DONTWAIT);
388920d93eaSCheng Xu 		if (rcvd == -EAGAIN)
389920d93eaSCheng Xu 			return -EAGAIN;
390920d93eaSCheng Xu 
391920d93eaSCheng Xu 		if (rcvd <= 0)
392920d93eaSCheng Xu 			return -ECONNABORTED;
393920d93eaSCheng Xu 
394920d93eaSCheng Xu 		hdr_rcvd += rcvd;
395920d93eaSCheng Xu 		*rcvd_out = rcvd;
396920d93eaSCheng Xu 
397920d93eaSCheng Xu 		if (hdr_rcvd < hdr_size)
398920d93eaSCheng Xu 			return -EAGAIN;
399920d93eaSCheng Xu 	}
400920d93eaSCheng Xu 
401920d93eaSCheng Xu 	return 0;
402920d93eaSCheng Xu }
403920d93eaSCheng Xu 
__mpa_rr_set_revision(__be16 * bits,u8 rev)404920d93eaSCheng Xu static void __mpa_rr_set_revision(__be16 *bits, u8 rev)
405920d93eaSCheng Xu {
406920d93eaSCheng Xu 	*bits = (*bits & ~MPA_RR_MASK_REVISION) |
407920d93eaSCheng Xu 		(cpu_to_be16(rev) & MPA_RR_MASK_REVISION);
408920d93eaSCheng Xu }
409920d93eaSCheng Xu 
__mpa_rr_revision(__be16 mpa_rr_bits)410920d93eaSCheng Xu static u8 __mpa_rr_revision(__be16 mpa_rr_bits)
411920d93eaSCheng Xu {
412920d93eaSCheng Xu 	__be16 rev = mpa_rr_bits & MPA_RR_MASK_REVISION;
413920d93eaSCheng Xu 
414920d93eaSCheng Xu 	return (u8)be16_to_cpu(rev);
415920d93eaSCheng Xu }
416920d93eaSCheng Xu 
__mpa_ext_set_cc(__be32 * bits,u32 cc)417920d93eaSCheng Xu static void __mpa_ext_set_cc(__be32 *bits, u32 cc)
418920d93eaSCheng Xu {
419920d93eaSCheng Xu 	*bits = (*bits & ~MPA_EXT_FLAG_CC) |
420920d93eaSCheng Xu 		(cpu_to_be32(cc) & MPA_EXT_FLAG_CC);
421920d93eaSCheng Xu }
422920d93eaSCheng Xu 
__mpa_ext_cc(__be32 mpa_ext_bits)423920d93eaSCheng Xu static u8 __mpa_ext_cc(__be32 mpa_ext_bits)
424920d93eaSCheng Xu {
425920d93eaSCheng Xu 	__be32 cc = mpa_ext_bits & MPA_EXT_FLAG_CC;
426920d93eaSCheng Xu 
427920d93eaSCheng Xu 	return (u8)be32_to_cpu(cc);
428920d93eaSCheng Xu }
429920d93eaSCheng Xu 
430920d93eaSCheng Xu /*
431920d93eaSCheng Xu  * Receive MPA Request/Reply header.
432920d93eaSCheng Xu  *
433920d93eaSCheng Xu  * Returns 0 if complete MPA Request/Reply haeder including
434920d93eaSCheng Xu  * eventual private data was received. Returns -EAGAIN if
435920d93eaSCheng Xu  * header was partially received or negative error code otherwise.
436920d93eaSCheng Xu  *
437920d93eaSCheng Xu  * Context: May be called in process context only
438920d93eaSCheng Xu  */
erdma_recv_mpa_rr(struct erdma_cep * cep)439920d93eaSCheng Xu static int erdma_recv_mpa_rr(struct erdma_cep *cep)
440920d93eaSCheng Xu {
441920d93eaSCheng Xu 	struct mpa_rr *hdr = &cep->mpa.hdr;
442920d93eaSCheng Xu 	struct socket *s = cep->sock;
443920d93eaSCheng Xu 	u16 pd_len;
444920d93eaSCheng Xu 	int rcvd, to_rcv, ret, pd_rcvd;
445920d93eaSCheng Xu 
446920d93eaSCheng Xu 	if (cep->mpa.bytes_rcvd < sizeof(struct mpa_rr)) {
447920d93eaSCheng Xu 		ret = __recv_mpa_hdr(cep, cep->mpa.bytes_rcvd,
448920d93eaSCheng Xu 				     (char *)&cep->mpa.hdr,
449920d93eaSCheng Xu 				     sizeof(struct mpa_rr), &rcvd);
450920d93eaSCheng Xu 		cep->mpa.bytes_rcvd += rcvd;
451920d93eaSCheng Xu 		if (ret)
452920d93eaSCheng Xu 			return ret;
453920d93eaSCheng Xu 	}
454920d93eaSCheng Xu 
455920d93eaSCheng Xu 	if (be16_to_cpu(hdr->params.pd_len) > MPA_MAX_PRIVDATA ||
456920d93eaSCheng Xu 	    __mpa_rr_revision(hdr->params.bits) != MPA_REVISION_EXT_1)
457920d93eaSCheng Xu 		return -EPROTO;
458920d93eaSCheng Xu 
459920d93eaSCheng Xu 	if (cep->mpa.bytes_rcvd - sizeof(struct mpa_rr) <
460920d93eaSCheng Xu 	    sizeof(struct erdma_mpa_ext)) {
461920d93eaSCheng Xu 		ret = __recv_mpa_hdr(
462920d93eaSCheng Xu 			cep, cep->mpa.bytes_rcvd - sizeof(struct mpa_rr),
463920d93eaSCheng Xu 			(char *)&cep->mpa.ext_data,
464920d93eaSCheng Xu 			sizeof(struct erdma_mpa_ext), &rcvd);
465920d93eaSCheng Xu 		cep->mpa.bytes_rcvd += rcvd;
466920d93eaSCheng Xu 		if (ret)
467920d93eaSCheng Xu 			return ret;
468920d93eaSCheng Xu 	}
469920d93eaSCheng Xu 
470920d93eaSCheng Xu 	pd_len = be16_to_cpu(hdr->params.pd_len);
471920d93eaSCheng Xu 	pd_rcvd = cep->mpa.bytes_rcvd - sizeof(struct mpa_rr) -
472920d93eaSCheng Xu 		  sizeof(struct erdma_mpa_ext);
473920d93eaSCheng Xu 	to_rcv = pd_len - pd_rcvd;
474920d93eaSCheng Xu 
475920d93eaSCheng Xu 	if (!to_rcv) {
476920d93eaSCheng Xu 		/*
477920d93eaSCheng Xu 		 * We have received the whole MPA Request/Reply message.
478920d93eaSCheng Xu 		 * Check against peer protocol violation.
479920d93eaSCheng Xu 		 */
480920d93eaSCheng Xu 		u32 word;
481920d93eaSCheng Xu 
482920d93eaSCheng Xu 		ret = __recv_mpa_hdr(cep, 0, (char *)&word, sizeof(word),
483920d93eaSCheng Xu 				     &rcvd);
484920d93eaSCheng Xu 		if (ret == -EAGAIN && rcvd == 0)
485920d93eaSCheng Xu 			return 0;
486920d93eaSCheng Xu 
487920d93eaSCheng Xu 		if (ret)
488920d93eaSCheng Xu 			return ret;
489920d93eaSCheng Xu 
490920d93eaSCheng Xu 		return -EPROTO;
491920d93eaSCheng Xu 	}
492920d93eaSCheng Xu 
493920d93eaSCheng Xu 	/*
494920d93eaSCheng Xu 	 * At this point, MPA header has been fully received, and pd_len != 0.
495920d93eaSCheng Xu 	 * So, begin to receive private data.
496920d93eaSCheng Xu 	 */
497920d93eaSCheng Xu 	if (!cep->mpa.pdata) {
498920d93eaSCheng Xu 		cep->mpa.pdata = kmalloc(pd_len + 4, GFP_KERNEL);
499920d93eaSCheng Xu 		if (!cep->mpa.pdata)
500920d93eaSCheng Xu 			return -ENOMEM;
501920d93eaSCheng Xu 	}
502920d93eaSCheng Xu 
503920d93eaSCheng Xu 	rcvd = ksock_recv(s, cep->mpa.pdata + pd_rcvd, to_rcv + 4,
504920d93eaSCheng Xu 			  MSG_DONTWAIT);
505920d93eaSCheng Xu 	if (rcvd < 0)
506920d93eaSCheng Xu 		return rcvd;
507920d93eaSCheng Xu 
508920d93eaSCheng Xu 	if (rcvd > to_rcv)
509920d93eaSCheng Xu 		return -EPROTO;
510920d93eaSCheng Xu 
511920d93eaSCheng Xu 	cep->mpa.bytes_rcvd += rcvd;
512920d93eaSCheng Xu 
513920d93eaSCheng Xu 	if (to_rcv == rcvd)
514920d93eaSCheng Xu 		return 0;
515920d93eaSCheng Xu 
516920d93eaSCheng Xu 	return -EAGAIN;
517920d93eaSCheng Xu }
518920d93eaSCheng Xu 
519920d93eaSCheng Xu /*
520920d93eaSCheng Xu  * erdma_proc_mpareq()
521920d93eaSCheng Xu  *
522920d93eaSCheng Xu  * Read MPA Request from socket and signal new connection to IWCM
523920d93eaSCheng Xu  * if success. Caller must hold lock on corresponding listening CEP.
524920d93eaSCheng Xu  */
erdma_proc_mpareq(struct erdma_cep * cep)525920d93eaSCheng Xu static int erdma_proc_mpareq(struct erdma_cep *cep)
526920d93eaSCheng Xu {
527920d93eaSCheng Xu 	struct mpa_rr *req;
528920d93eaSCheng Xu 	int ret;
529920d93eaSCheng Xu 
530920d93eaSCheng Xu 	ret = erdma_recv_mpa_rr(cep);
531920d93eaSCheng Xu 	if (ret)
532920d93eaSCheng Xu 		return ret;
533920d93eaSCheng Xu 
534920d93eaSCheng Xu 	req = &cep->mpa.hdr;
535920d93eaSCheng Xu 
536920d93eaSCheng Xu 	if (memcmp(req->key, MPA_KEY_REQ, MPA_KEY_SIZE))
537920d93eaSCheng Xu 		return -EPROTO;
538920d93eaSCheng Xu 
539920d93eaSCheng Xu 	memcpy(req->key, MPA_KEY_REP, MPA_KEY_SIZE);
540920d93eaSCheng Xu 
541920d93eaSCheng Xu 	/* Currently does not support marker and crc. */
542920d93eaSCheng Xu 	if (req->params.bits & MPA_RR_FLAG_MARKERS ||
543920d93eaSCheng Xu 	    req->params.bits & MPA_RR_FLAG_CRC)
544920d93eaSCheng Xu 		goto reject_conn;
545920d93eaSCheng Xu 
546920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_RECVD_MPAREQ;
547920d93eaSCheng Xu 
548920d93eaSCheng Xu 	/* Keep reference until IWCM accepts/rejects */
549920d93eaSCheng Xu 	erdma_cep_get(cep);
550920d93eaSCheng Xu 	ret = erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REQUEST, 0);
551920d93eaSCheng Xu 	if (ret)
552920d93eaSCheng Xu 		erdma_cep_put(cep);
553920d93eaSCheng Xu 
554920d93eaSCheng Xu 	return ret;
555920d93eaSCheng Xu 
556920d93eaSCheng Xu reject_conn:
557920d93eaSCheng Xu 	req->params.bits &= ~MPA_RR_FLAG_MARKERS;
558920d93eaSCheng Xu 	req->params.bits |= MPA_RR_FLAG_REJECT;
559920d93eaSCheng Xu 	req->params.bits &= ~MPA_RR_FLAG_CRC;
560920d93eaSCheng Xu 
561920d93eaSCheng Xu 	kfree(cep->mpa.pdata);
562920d93eaSCheng Xu 	cep->mpa.pdata = NULL;
563920d93eaSCheng Xu 	erdma_send_mpareqrep(cep, NULL, 0);
564920d93eaSCheng Xu 
565920d93eaSCheng Xu 	return -EOPNOTSUPP;
566920d93eaSCheng Xu }
567920d93eaSCheng Xu 
erdma_proc_mpareply(struct erdma_cep * cep)568920d93eaSCheng Xu static int erdma_proc_mpareply(struct erdma_cep *cep)
569920d93eaSCheng Xu {
570920d93eaSCheng Xu 	struct erdma_qp_attrs qp_attrs;
571920d93eaSCheng Xu 	struct erdma_qp *qp = cep->qp;
572920d93eaSCheng Xu 	struct mpa_rr *rep;
573920d93eaSCheng Xu 	int ret;
574920d93eaSCheng Xu 
575920d93eaSCheng Xu 	ret = erdma_recv_mpa_rr(cep);
576920d93eaSCheng Xu 	if (ret)
577920d93eaSCheng Xu 		goto out_err;
578920d93eaSCheng Xu 
579920d93eaSCheng Xu 	erdma_cancel_mpatimer(cep);
580920d93eaSCheng Xu 
581920d93eaSCheng Xu 	rep = &cep->mpa.hdr;
582920d93eaSCheng Xu 
583920d93eaSCheng Xu 	if (memcmp(rep->key, MPA_KEY_REP, MPA_KEY_SIZE)) {
584920d93eaSCheng Xu 		ret = -EPROTO;
585920d93eaSCheng Xu 		goto out_err;
586920d93eaSCheng Xu 	}
587920d93eaSCheng Xu 
588920d93eaSCheng Xu 	if (rep->params.bits & MPA_RR_FLAG_REJECT) {
589920d93eaSCheng Xu 		erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -ECONNRESET);
590920d93eaSCheng Xu 		return -ECONNRESET;
591920d93eaSCheng Xu 	}
592920d93eaSCheng Xu 
593920d93eaSCheng Xu 	/* Currently does not support marker and crc. */
594920d93eaSCheng Xu 	if ((rep->params.bits & MPA_RR_FLAG_MARKERS) ||
595920d93eaSCheng Xu 	    (rep->params.bits & MPA_RR_FLAG_CRC)) {
596920d93eaSCheng Xu 		erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -ECONNREFUSED);
597920d93eaSCheng Xu 		return -EINVAL;
598920d93eaSCheng Xu 	}
599920d93eaSCheng Xu 
600920d93eaSCheng Xu 	memset(&qp_attrs, 0, sizeof(qp_attrs));
601920d93eaSCheng Xu 	qp_attrs.irq_size = cep->ird;
602920d93eaSCheng Xu 	qp_attrs.orq_size = cep->ord;
603920d93eaSCheng Xu 	qp_attrs.state = ERDMA_QP_STATE_RTS;
604920d93eaSCheng Xu 
605920d93eaSCheng Xu 	down_write(&qp->state_lock);
606920d93eaSCheng Xu 	if (qp->attrs.state > ERDMA_QP_STATE_RTR) {
607920d93eaSCheng Xu 		ret = -EINVAL;
608920d93eaSCheng Xu 		up_write(&qp->state_lock);
609920d93eaSCheng Xu 		goto out_err;
610920d93eaSCheng Xu 	}
611920d93eaSCheng Xu 
612920d93eaSCheng Xu 	qp->attrs.qp_type = ERDMA_QP_ACTIVE;
613920d93eaSCheng Xu 	if (__mpa_ext_cc(cep->mpa.ext_data.bits) != qp->attrs.cc)
614920d93eaSCheng Xu 		qp->attrs.cc = COMPROMISE_CC;
615920d93eaSCheng Xu 
616920d93eaSCheng Xu 	ret = erdma_modify_qp_internal(qp, &qp_attrs,
617920d93eaSCheng Xu 				       ERDMA_QP_ATTR_STATE |
618920d93eaSCheng Xu 				       ERDMA_QP_ATTR_LLP_HANDLE |
619920d93eaSCheng Xu 				       ERDMA_QP_ATTR_MPA);
620920d93eaSCheng Xu 
621920d93eaSCheng Xu 	up_write(&qp->state_lock);
622920d93eaSCheng Xu 
623920d93eaSCheng Xu 	if (!ret) {
624920d93eaSCheng Xu 		ret = erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 0);
625920d93eaSCheng Xu 		if (!ret)
626920d93eaSCheng Xu 			cep->state = ERDMA_EPSTATE_RDMA_MODE;
627920d93eaSCheng Xu 
628920d93eaSCheng Xu 		return 0;
629920d93eaSCheng Xu 	}
630920d93eaSCheng Xu 
631920d93eaSCheng Xu out_err:
632920d93eaSCheng Xu 	if (ret != -EAGAIN)
633920d93eaSCheng Xu 		erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -EINVAL);
634920d93eaSCheng Xu 
635920d93eaSCheng Xu 	return ret;
636920d93eaSCheng Xu }
637920d93eaSCheng Xu 
erdma_accept_newconn(struct erdma_cep * cep)638920d93eaSCheng Xu static void erdma_accept_newconn(struct erdma_cep *cep)
639920d93eaSCheng Xu {
640920d93eaSCheng Xu 	struct socket *s = cep->sock;
641920d93eaSCheng Xu 	struct socket *new_s = NULL;
642920d93eaSCheng Xu 	struct erdma_cep *new_cep = NULL;
643920d93eaSCheng Xu 	int ret = 0;
644920d93eaSCheng Xu 
645920d93eaSCheng Xu 	if (cep->state != ERDMA_EPSTATE_LISTENING)
646920d93eaSCheng Xu 		goto error;
647920d93eaSCheng Xu 
648920d93eaSCheng Xu 	new_cep = erdma_cep_alloc(cep->dev);
649920d93eaSCheng Xu 	if (!new_cep)
650920d93eaSCheng Xu 		goto error;
651920d93eaSCheng Xu 
652920d93eaSCheng Xu 	/*
653920d93eaSCheng Xu 	 * 4: Allocate a sufficient number of work elements
654920d93eaSCheng Xu 	 * to allow concurrent handling of local + peer close
655920d93eaSCheng Xu 	 * events, MPA header processing + MPA timeout.
656920d93eaSCheng Xu 	 */
657920d93eaSCheng Xu 	if (erdma_cm_alloc_work(new_cep, 4) != 0)
658920d93eaSCheng Xu 		goto error;
659920d93eaSCheng Xu 
660920d93eaSCheng Xu 	/*
661920d93eaSCheng Xu 	 * Copy saved socket callbacks from listening CEP
662920d93eaSCheng Xu 	 * and assign new socket with new CEP
663920d93eaSCheng Xu 	 */
664920d93eaSCheng Xu 	new_cep->sk_state_change = cep->sk_state_change;
665920d93eaSCheng Xu 	new_cep->sk_data_ready = cep->sk_data_ready;
666920d93eaSCheng Xu 	new_cep->sk_error_report = cep->sk_error_report;
667920d93eaSCheng Xu 
668920d93eaSCheng Xu 	ret = kernel_accept(s, &new_s, O_NONBLOCK);
669920d93eaSCheng Xu 	if (ret != 0)
670920d93eaSCheng Xu 		goto error;
671920d93eaSCheng Xu 
672920d93eaSCheng Xu 	new_cep->sock = new_s;
673920d93eaSCheng Xu 	erdma_cep_get(new_cep);
674920d93eaSCheng Xu 	new_s->sk->sk_user_data = new_cep;
675920d93eaSCheng Xu 
676920d93eaSCheng Xu 	tcp_sock_set_nodelay(new_s->sk);
677920d93eaSCheng Xu 	new_cep->state = ERDMA_EPSTATE_AWAIT_MPAREQ;
678920d93eaSCheng Xu 
679920d93eaSCheng Xu 	ret = erdma_cm_queue_work(new_cep, ERDMA_CM_WORK_MPATIMEOUT);
680920d93eaSCheng Xu 	if (ret)
681920d93eaSCheng Xu 		goto error;
682920d93eaSCheng Xu 
683920d93eaSCheng Xu 	new_cep->listen_cep = cep;
684920d93eaSCheng Xu 	erdma_cep_get(cep);
685920d93eaSCheng Xu 
686920d93eaSCheng Xu 	if (atomic_read(&new_s->sk->sk_rmem_alloc)) {
687920d93eaSCheng Xu 		/* MPA REQ already queued */
688920d93eaSCheng Xu 		erdma_cep_set_inuse(new_cep);
689920d93eaSCheng Xu 		ret = erdma_proc_mpareq(new_cep);
690920d93eaSCheng Xu 		if (ret != -EAGAIN) {
691920d93eaSCheng Xu 			erdma_cep_put(cep);
692920d93eaSCheng Xu 			new_cep->listen_cep = NULL;
693920d93eaSCheng Xu 			if (ret) {
694920d93eaSCheng Xu 				erdma_cep_set_free(new_cep);
695920d93eaSCheng Xu 				goto error;
696920d93eaSCheng Xu 			}
697920d93eaSCheng Xu 		}
698920d93eaSCheng Xu 		erdma_cep_set_free(new_cep);
699920d93eaSCheng Xu 	}
700920d93eaSCheng Xu 	return;
701920d93eaSCheng Xu 
702920d93eaSCheng Xu error:
703920d93eaSCheng Xu 	if (new_cep) {
704920d93eaSCheng Xu 		new_cep->state = ERDMA_EPSTATE_CLOSED;
705920d93eaSCheng Xu 		erdma_cancel_mpatimer(new_cep);
706920d93eaSCheng Xu 
707920d93eaSCheng Xu 		erdma_cep_put(new_cep);
708920d93eaSCheng Xu 		new_cep->sock = NULL;
709920d93eaSCheng Xu 	}
710920d93eaSCheng Xu 
711920d93eaSCheng Xu 	if (new_s) {
712920d93eaSCheng Xu 		erdma_socket_disassoc(new_s);
713920d93eaSCheng Xu 		sock_release(new_s);
714920d93eaSCheng Xu 	}
715920d93eaSCheng Xu }
716920d93eaSCheng Xu 
erdma_newconn_connected(struct erdma_cep * cep)717920d93eaSCheng Xu static int erdma_newconn_connected(struct erdma_cep *cep)
718920d93eaSCheng Xu {
719920d93eaSCheng Xu 	int ret = 0;
720920d93eaSCheng Xu 
721920d93eaSCheng Xu 	cep->mpa.hdr.params.bits = 0;
722920d93eaSCheng Xu 	__mpa_rr_set_revision(&cep->mpa.hdr.params.bits, MPA_REVISION_EXT_1);
723920d93eaSCheng Xu 
724920d93eaSCheng Xu 	memcpy(cep->mpa.hdr.key, MPA_KEY_REQ, MPA_KEY_SIZE);
725920d93eaSCheng Xu 	cep->mpa.ext_data.cookie = cpu_to_be32(cep->qp->attrs.cookie);
726920d93eaSCheng Xu 	__mpa_ext_set_cc(&cep->mpa.ext_data.bits, cep->qp->attrs.cc);
727920d93eaSCheng Xu 
728920d93eaSCheng Xu 	ret = erdma_send_mpareqrep(cep, cep->private_data, cep->pd_len);
729920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_AWAIT_MPAREP;
730920d93eaSCheng Xu 	cep->mpa.hdr.params.pd_len = 0;
731920d93eaSCheng Xu 
732920d93eaSCheng Xu 	if (ret >= 0)
733920d93eaSCheng Xu 		ret = erdma_cm_queue_work(cep, ERDMA_CM_WORK_MPATIMEOUT);
734920d93eaSCheng Xu 
735920d93eaSCheng Xu 	return ret;
736920d93eaSCheng Xu }
737920d93eaSCheng Xu 
erdma_cm_work_handler(struct work_struct * w)738920d93eaSCheng Xu static void erdma_cm_work_handler(struct work_struct *w)
739920d93eaSCheng Xu {
740920d93eaSCheng Xu 	struct erdma_cm_work *work;
741920d93eaSCheng Xu 	struct erdma_cep *cep;
742920d93eaSCheng Xu 	int release_cep = 0, ret = 0;
743920d93eaSCheng Xu 
744920d93eaSCheng Xu 	work = container_of(w, struct erdma_cm_work, work.work);
745920d93eaSCheng Xu 	cep = work->cep;
746920d93eaSCheng Xu 
747920d93eaSCheng Xu 	erdma_cep_set_inuse(cep);
748920d93eaSCheng Xu 
749920d93eaSCheng Xu 	switch (work->type) {
750920d93eaSCheng Xu 	case ERDMA_CM_WORK_CONNECTED:
751920d93eaSCheng Xu 		erdma_cancel_mpatimer(cep);
752920d93eaSCheng Xu 		if (cep->state == ERDMA_EPSTATE_CONNECTING) {
753920d93eaSCheng Xu 			ret = erdma_newconn_connected(cep);
754920d93eaSCheng Xu 			if (ret) {
755920d93eaSCheng Xu 				erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY,
756920d93eaSCheng Xu 						-EIO);
757920d93eaSCheng Xu 				release_cep = 1;
758920d93eaSCheng Xu 			}
759920d93eaSCheng Xu 		}
760920d93eaSCheng Xu 		break;
761920d93eaSCheng Xu 	case ERDMA_CM_WORK_CONNECTTIMEOUT:
762920d93eaSCheng Xu 		if (cep->state == ERDMA_EPSTATE_CONNECTING) {
763920d93eaSCheng Xu 			cep->mpa_timer = NULL;
764920d93eaSCheng Xu 			erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY,
765920d93eaSCheng Xu 					-ETIMEDOUT);
766920d93eaSCheng Xu 			release_cep = 1;
767920d93eaSCheng Xu 		}
768920d93eaSCheng Xu 		break;
769920d93eaSCheng Xu 	case ERDMA_CM_WORK_ACCEPT:
770920d93eaSCheng Xu 		erdma_accept_newconn(cep);
771920d93eaSCheng Xu 		break;
772920d93eaSCheng Xu 	case ERDMA_CM_WORK_READ_MPAHDR:
773920d93eaSCheng Xu 		if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ) {
774920d93eaSCheng Xu 			if (cep->listen_cep) {
775920d93eaSCheng Xu 				erdma_cep_set_inuse(cep->listen_cep);
776920d93eaSCheng Xu 
777920d93eaSCheng Xu 				if (cep->listen_cep->state ==
778920d93eaSCheng Xu 				    ERDMA_EPSTATE_LISTENING)
779920d93eaSCheng Xu 					ret = erdma_proc_mpareq(cep);
780920d93eaSCheng Xu 				else
781920d93eaSCheng Xu 					ret = -EFAULT;
782920d93eaSCheng Xu 
783920d93eaSCheng Xu 				erdma_cep_set_free(cep->listen_cep);
784920d93eaSCheng Xu 
785920d93eaSCheng Xu 				if (ret != -EAGAIN) {
786920d93eaSCheng Xu 					erdma_cep_put(cep->listen_cep);
787920d93eaSCheng Xu 					cep->listen_cep = NULL;
788920d93eaSCheng Xu 					if (ret)
789920d93eaSCheng Xu 						erdma_cep_put(cep);
790920d93eaSCheng Xu 				}
791920d93eaSCheng Xu 			}
792920d93eaSCheng Xu 		} else if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) {
793920d93eaSCheng Xu 			ret = erdma_proc_mpareply(cep);
794920d93eaSCheng Xu 		}
795920d93eaSCheng Xu 
796920d93eaSCheng Xu 		if (ret && ret != -EAGAIN)
797920d93eaSCheng Xu 			release_cep = 1;
798920d93eaSCheng Xu 		break;
799920d93eaSCheng Xu 	case ERDMA_CM_WORK_CLOSE_LLP:
800920d93eaSCheng Xu 		if (cep->cm_id)
801920d93eaSCheng Xu 			erdma_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0);
802920d93eaSCheng Xu 		release_cep = 1;
803920d93eaSCheng Xu 		break;
804920d93eaSCheng Xu 	case ERDMA_CM_WORK_PEER_CLOSE:
805920d93eaSCheng Xu 		if (cep->cm_id) {
806920d93eaSCheng Xu 			if (cep->state == ERDMA_EPSTATE_CONNECTING ||
807920d93eaSCheng Xu 			    cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) {
808920d93eaSCheng Xu 				/*
809920d93eaSCheng Xu 				 * MPA reply not received, but connection drop
810920d93eaSCheng Xu 				 */
811920d93eaSCheng Xu 				erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY,
812920d93eaSCheng Xu 						-ECONNRESET);
813920d93eaSCheng Xu 			} else if (cep->state == ERDMA_EPSTATE_RDMA_MODE) {
814920d93eaSCheng Xu 				/*
815920d93eaSCheng Xu 				 * NOTE: IW_CM_EVENT_DISCONNECT is given just
816920d93eaSCheng Xu 				 *       to transition IWCM into CLOSING.
817920d93eaSCheng Xu 				 */
818920d93eaSCheng Xu 				erdma_cm_upcall(cep, IW_CM_EVENT_DISCONNECT, 0);
819920d93eaSCheng Xu 				erdma_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0);
820920d93eaSCheng Xu 			}
821920d93eaSCheng Xu 		} else if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ) {
822920d93eaSCheng Xu 			/* Socket close before MPA request received. */
823920d93eaSCheng Xu 			erdma_disassoc_listen_cep(cep);
824920d93eaSCheng Xu 			erdma_cep_put(cep);
825920d93eaSCheng Xu 		}
826920d93eaSCheng Xu 		release_cep = 1;
827920d93eaSCheng Xu 		break;
828920d93eaSCheng Xu 	case ERDMA_CM_WORK_MPATIMEOUT:
829920d93eaSCheng Xu 		cep->mpa_timer = NULL;
830920d93eaSCheng Xu 		if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) {
831920d93eaSCheng Xu 			/*
832920d93eaSCheng Xu 			 * MPA request timed out:
833920d93eaSCheng Xu 			 * Hide any partially received private data and signal
834920d93eaSCheng Xu 			 * timeout
835920d93eaSCheng Xu 			 */
836920d93eaSCheng Xu 			cep->mpa.hdr.params.pd_len = 0;
837920d93eaSCheng Xu 
838920d93eaSCheng Xu 			if (cep->cm_id)
839920d93eaSCheng Xu 				erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY,
840920d93eaSCheng Xu 						-ETIMEDOUT);
841920d93eaSCheng Xu 			release_cep = 1;
842920d93eaSCheng Xu 		} else if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ) {
843920d93eaSCheng Xu 			/* No MPA req received after peer TCP stream setup. */
844920d93eaSCheng Xu 			erdma_disassoc_listen_cep(cep);
845920d93eaSCheng Xu 
846920d93eaSCheng Xu 			erdma_cep_put(cep);
847920d93eaSCheng Xu 			release_cep = 1;
848920d93eaSCheng Xu 		}
849920d93eaSCheng Xu 		break;
850920d93eaSCheng Xu 	default:
851920d93eaSCheng Xu 		WARN(1, "Undefined CM work type: %d\n", work->type);
852920d93eaSCheng Xu 	}
853920d93eaSCheng Xu 
854920d93eaSCheng Xu 	if (release_cep) {
855920d93eaSCheng Xu 		erdma_cancel_mpatimer(cep);
856920d93eaSCheng Xu 		cep->state = ERDMA_EPSTATE_CLOSED;
857920d93eaSCheng Xu 		if (cep->qp) {
858920d93eaSCheng Xu 			struct erdma_qp *qp = cep->qp;
859920d93eaSCheng Xu 			/*
860920d93eaSCheng Xu 			 * Serialize a potential race with application
861920d93eaSCheng Xu 			 * closing the QP and calling erdma_qp_cm_drop()
862920d93eaSCheng Xu 			 */
863920d93eaSCheng Xu 			erdma_qp_get(qp);
864920d93eaSCheng Xu 			erdma_cep_set_free(cep);
865920d93eaSCheng Xu 
866920d93eaSCheng Xu 			erdma_qp_llp_close(qp);
867920d93eaSCheng Xu 			erdma_qp_put(qp);
868920d93eaSCheng Xu 
869920d93eaSCheng Xu 			erdma_cep_set_inuse(cep);
870920d93eaSCheng Xu 			cep->qp = NULL;
871920d93eaSCheng Xu 			erdma_qp_put(qp);
872920d93eaSCheng Xu 		}
873920d93eaSCheng Xu 
874920d93eaSCheng Xu 		if (cep->sock) {
875920d93eaSCheng Xu 			erdma_socket_disassoc(cep->sock);
876920d93eaSCheng Xu 			sock_release(cep->sock);
877920d93eaSCheng Xu 			cep->sock = NULL;
878920d93eaSCheng Xu 		}
879920d93eaSCheng Xu 
880920d93eaSCheng Xu 		if (cep->cm_id) {
881920d93eaSCheng Xu 			cep->cm_id->rem_ref(cep->cm_id);
882920d93eaSCheng Xu 			cep->cm_id = NULL;
883920d93eaSCheng Xu 			if (cep->state != ERDMA_EPSTATE_LISTENING)
884920d93eaSCheng Xu 				erdma_cep_put(cep);
885920d93eaSCheng Xu 		}
886920d93eaSCheng Xu 	}
887920d93eaSCheng Xu 	erdma_cep_set_free(cep);
888920d93eaSCheng Xu 	erdma_put_work(work);
889920d93eaSCheng Xu 	erdma_cep_put(cep);
890920d93eaSCheng Xu }
891920d93eaSCheng Xu 
erdma_cm_queue_work(struct erdma_cep * cep,enum erdma_work_type type)892920d93eaSCheng Xu int erdma_cm_queue_work(struct erdma_cep *cep, enum erdma_work_type type)
893920d93eaSCheng Xu {
894920d93eaSCheng Xu 	struct erdma_cm_work *work = erdma_get_work(cep);
895920d93eaSCheng Xu 	unsigned long delay = 0;
896920d93eaSCheng Xu 
897920d93eaSCheng Xu 	if (!work)
898920d93eaSCheng Xu 		return -ENOMEM;
899920d93eaSCheng Xu 
900920d93eaSCheng Xu 	work->type = type;
901920d93eaSCheng Xu 	work->cep = cep;
902920d93eaSCheng Xu 
903920d93eaSCheng Xu 	erdma_cep_get(cep);
904920d93eaSCheng Xu 
905920d93eaSCheng Xu 	INIT_DELAYED_WORK(&work->work, erdma_cm_work_handler);
906920d93eaSCheng Xu 
907920d93eaSCheng Xu 	if (type == ERDMA_CM_WORK_MPATIMEOUT) {
908920d93eaSCheng Xu 		cep->mpa_timer = work;
909920d93eaSCheng Xu 
910920d93eaSCheng Xu 		if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREP)
911920d93eaSCheng Xu 			delay = MPAREP_TIMEOUT;
912920d93eaSCheng Xu 		else
913920d93eaSCheng Xu 			delay = MPAREQ_TIMEOUT;
914920d93eaSCheng Xu 	} else if (type == ERDMA_CM_WORK_CONNECTTIMEOUT) {
915920d93eaSCheng Xu 		cep->mpa_timer = work;
916920d93eaSCheng Xu 
917920d93eaSCheng Xu 		delay = CONNECT_TIMEOUT;
918920d93eaSCheng Xu 	}
919920d93eaSCheng Xu 
920920d93eaSCheng Xu 	queue_delayed_work(erdma_cm_wq, &work->work, delay);
921920d93eaSCheng Xu 
922920d93eaSCheng Xu 	return 0;
923920d93eaSCheng Xu }
924920d93eaSCheng Xu 
erdma_cm_llp_data_ready(struct sock * sk)925920d93eaSCheng Xu static void erdma_cm_llp_data_ready(struct sock *sk)
926920d93eaSCheng Xu {
927920d93eaSCheng Xu 	struct erdma_cep *cep;
928920d93eaSCheng Xu 
929*40e0b090SPeilin Ye 	trace_sk_data_ready(sk);
930*40e0b090SPeilin Ye 
931920d93eaSCheng Xu 	read_lock(&sk->sk_callback_lock);
932920d93eaSCheng Xu 
933920d93eaSCheng Xu 	cep = sk_to_cep(sk);
934920d93eaSCheng Xu 	if (!cep)
935920d93eaSCheng Xu 		goto out;
936920d93eaSCheng Xu 
937920d93eaSCheng Xu 	if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ ||
938920d93eaSCheng Xu 	    cep->state == ERDMA_EPSTATE_AWAIT_MPAREP)
939920d93eaSCheng Xu 		erdma_cm_queue_work(cep, ERDMA_CM_WORK_READ_MPAHDR);
940920d93eaSCheng Xu 
941920d93eaSCheng Xu out:
942920d93eaSCheng Xu 	read_unlock(&sk->sk_callback_lock);
943920d93eaSCheng Xu }
944920d93eaSCheng Xu 
erdma_cm_llp_error_report(struct sock * sk)945920d93eaSCheng Xu static void erdma_cm_llp_error_report(struct sock *sk)
946920d93eaSCheng Xu {
947920d93eaSCheng Xu 	struct erdma_cep *cep = sk_to_cep(sk);
948920d93eaSCheng Xu 
949920d93eaSCheng Xu 	if (cep)
950920d93eaSCheng Xu 		cep->sk_error_report(sk);
951920d93eaSCheng Xu }
952920d93eaSCheng Xu 
erdma_cm_llp_state_change(struct sock * sk)953920d93eaSCheng Xu static void erdma_cm_llp_state_change(struct sock *sk)
954920d93eaSCheng Xu {
955920d93eaSCheng Xu 	struct erdma_cep *cep;
956920d93eaSCheng Xu 	void (*orig_state_change)(struct sock *sk);
957920d93eaSCheng Xu 
958920d93eaSCheng Xu 	read_lock(&sk->sk_callback_lock);
959920d93eaSCheng Xu 
960920d93eaSCheng Xu 	cep = sk_to_cep(sk);
961920d93eaSCheng Xu 	if (!cep) {
962920d93eaSCheng Xu 		read_unlock(&sk->sk_callback_lock);
963920d93eaSCheng Xu 		return;
964920d93eaSCheng Xu 	}
965920d93eaSCheng Xu 	orig_state_change = cep->sk_state_change;
966920d93eaSCheng Xu 
967920d93eaSCheng Xu 	switch (sk->sk_state) {
968920d93eaSCheng Xu 	case TCP_ESTABLISHED:
969920d93eaSCheng Xu 		if (cep->state == ERDMA_EPSTATE_CONNECTING)
970920d93eaSCheng Xu 			erdma_cm_queue_work(cep, ERDMA_CM_WORK_CONNECTED);
971920d93eaSCheng Xu 		else
972920d93eaSCheng Xu 			erdma_cm_queue_work(cep, ERDMA_CM_WORK_ACCEPT);
973920d93eaSCheng Xu 		break;
974920d93eaSCheng Xu 	case TCP_CLOSE:
975920d93eaSCheng Xu 	case TCP_CLOSE_WAIT:
976920d93eaSCheng Xu 		if (cep->state != ERDMA_EPSTATE_LISTENING)
977920d93eaSCheng Xu 			erdma_cm_queue_work(cep, ERDMA_CM_WORK_PEER_CLOSE);
978920d93eaSCheng Xu 		break;
979920d93eaSCheng Xu 	default:
980920d93eaSCheng Xu 		break;
981920d93eaSCheng Xu 	}
982920d93eaSCheng Xu 	read_unlock(&sk->sk_callback_lock);
983920d93eaSCheng Xu 	orig_state_change(sk);
984920d93eaSCheng Xu }
985920d93eaSCheng Xu 
kernel_bindconnect(struct socket * s,struct sockaddr * laddr,int laddrlen,struct sockaddr * raddr,int raddrlen,int flags)986920d93eaSCheng Xu static int kernel_bindconnect(struct socket *s, struct sockaddr *laddr,
987920d93eaSCheng Xu 			      int laddrlen, struct sockaddr *raddr,
988920d93eaSCheng Xu 			      int raddrlen, int flags)
989920d93eaSCheng Xu {
990920d93eaSCheng Xu 	int ret;
991920d93eaSCheng Xu 
992920d93eaSCheng Xu 	sock_set_reuseaddr(s->sk);
993920d93eaSCheng Xu 	ret = s->ops->bind(s, laddr, laddrlen);
994920d93eaSCheng Xu 	if (ret)
995920d93eaSCheng Xu 		return ret;
996920d93eaSCheng Xu 	ret = s->ops->connect(s, raddr, raddrlen, flags);
997920d93eaSCheng Xu 	return ret < 0 ? ret : 0;
998920d93eaSCheng Xu }
999920d93eaSCheng Xu 
erdma_connect(struct iw_cm_id * id,struct iw_cm_conn_param * params)1000920d93eaSCheng Xu int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params)
1001920d93eaSCheng Xu {
1002920d93eaSCheng Xu 	struct erdma_dev *dev = to_edev(id->device);
1003920d93eaSCheng Xu 	struct erdma_qp *qp;
1004920d93eaSCheng Xu 	struct erdma_cep *cep = NULL;
1005920d93eaSCheng Xu 	struct socket *s = NULL;
1006920d93eaSCheng Xu 	struct sockaddr *laddr = (struct sockaddr *)&id->m_local_addr;
1007920d93eaSCheng Xu 	struct sockaddr *raddr = (struct sockaddr *)&id->m_remote_addr;
1008920d93eaSCheng Xu 	u16 pd_len = params->private_data_len;
1009920d93eaSCheng Xu 	int ret;
1010920d93eaSCheng Xu 
1011920d93eaSCheng Xu 	if (pd_len > MPA_MAX_PRIVDATA)
1012920d93eaSCheng Xu 		return -EINVAL;
1013920d93eaSCheng Xu 
1014920d93eaSCheng Xu 	if (params->ird > dev->attrs.max_ird ||
1015920d93eaSCheng Xu 	    params->ord > dev->attrs.max_ord)
1016920d93eaSCheng Xu 		return -EINVAL;
1017920d93eaSCheng Xu 
1018920d93eaSCheng Xu 	if (laddr->sa_family != AF_INET || raddr->sa_family != AF_INET)
1019920d93eaSCheng Xu 		return -EAFNOSUPPORT;
1020920d93eaSCheng Xu 
1021920d93eaSCheng Xu 	qp = find_qp_by_qpn(dev, params->qpn);
1022920d93eaSCheng Xu 	if (!qp)
1023920d93eaSCheng Xu 		return -ENOENT;
1024920d93eaSCheng Xu 	erdma_qp_get(qp);
1025920d93eaSCheng Xu 
1026920d93eaSCheng Xu 	ret = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &s);
1027920d93eaSCheng Xu 	if (ret < 0)
1028920d93eaSCheng Xu 		goto error_put_qp;
1029920d93eaSCheng Xu 
1030920d93eaSCheng Xu 	cep = erdma_cep_alloc(dev);
1031920d93eaSCheng Xu 	if (!cep) {
1032920d93eaSCheng Xu 		ret = -ENOMEM;
1033920d93eaSCheng Xu 		goto error_release_sock;
1034920d93eaSCheng Xu 	}
1035920d93eaSCheng Xu 
1036920d93eaSCheng Xu 	erdma_cep_set_inuse(cep);
1037920d93eaSCheng Xu 
1038920d93eaSCheng Xu 	/* Associate QP with CEP */
1039920d93eaSCheng Xu 	erdma_cep_get(cep);
1040920d93eaSCheng Xu 	qp->cep = cep;
1041920d93eaSCheng Xu 	cep->qp = qp;
1042920d93eaSCheng Xu 
1043920d93eaSCheng Xu 	/* Associate cm_id with CEP */
1044920d93eaSCheng Xu 	id->add_ref(id);
1045920d93eaSCheng Xu 	cep->cm_id = id;
1046920d93eaSCheng Xu 
1047920d93eaSCheng Xu 	/*
1048920d93eaSCheng Xu 	 * 6: Allocate a sufficient number of work elements
1049920d93eaSCheng Xu 	 * to allow concurrent handling of local + peer close
1050920d93eaSCheng Xu 	 * events, MPA header processing + MPA timeout, connected event
1051920d93eaSCheng Xu 	 * and connect timeout.
1052920d93eaSCheng Xu 	 */
1053920d93eaSCheng Xu 	ret = erdma_cm_alloc_work(cep, 6);
1054920d93eaSCheng Xu 	if (ret != 0) {
1055920d93eaSCheng Xu 		ret = -ENOMEM;
1056920d93eaSCheng Xu 		goto error_release_cep;
1057920d93eaSCheng Xu 	}
1058920d93eaSCheng Xu 
1059920d93eaSCheng Xu 	cep->ird = params->ird;
1060920d93eaSCheng Xu 	cep->ord = params->ord;
1061920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_CONNECTING;
1062920d93eaSCheng Xu 
1063920d93eaSCheng Xu 	erdma_cep_socket_assoc(cep, s);
1064920d93eaSCheng Xu 
1065920d93eaSCheng Xu 	if (pd_len) {
1066920d93eaSCheng Xu 		cep->pd_len = pd_len;
1067920d93eaSCheng Xu 		cep->private_data = kmalloc(pd_len, GFP_KERNEL);
1068920d93eaSCheng Xu 		if (!cep->private_data) {
1069920d93eaSCheng Xu 			ret = -ENOMEM;
1070920d93eaSCheng Xu 			goto error_disassoc;
1071920d93eaSCheng Xu 		}
1072920d93eaSCheng Xu 
1073920d93eaSCheng Xu 		memcpy(cep->private_data, params->private_data,
1074920d93eaSCheng Xu 		       params->private_data_len);
1075920d93eaSCheng Xu 	}
1076920d93eaSCheng Xu 
1077920d93eaSCheng Xu 	ret = kernel_bindconnect(s, laddr, sizeof(*laddr), raddr,
1078920d93eaSCheng Xu 				 sizeof(*raddr), O_NONBLOCK);
1079920d93eaSCheng Xu 	if (ret != -EINPROGRESS && ret != 0) {
1080920d93eaSCheng Xu 		goto error_disassoc;
1081920d93eaSCheng Xu 	} else if (ret == 0) {
1082920d93eaSCheng Xu 		ret = erdma_cm_queue_work(cep, ERDMA_CM_WORK_CONNECTED);
1083920d93eaSCheng Xu 		if (ret)
1084920d93eaSCheng Xu 			goto error_disassoc;
1085920d93eaSCheng Xu 	} else {
1086920d93eaSCheng Xu 		ret = erdma_cm_queue_work(cep, ERDMA_CM_WORK_CONNECTTIMEOUT);
1087920d93eaSCheng Xu 		if (ret)
1088920d93eaSCheng Xu 			goto error_disassoc;
1089920d93eaSCheng Xu 	}
1090920d93eaSCheng Xu 
1091920d93eaSCheng Xu 	erdma_cep_set_free(cep);
1092920d93eaSCheng Xu 	return 0;
1093920d93eaSCheng Xu 
1094920d93eaSCheng Xu error_disassoc:
1095920d93eaSCheng Xu 	kfree(cep->private_data);
1096920d93eaSCheng Xu 	cep->private_data = NULL;
1097920d93eaSCheng Xu 	cep->pd_len = 0;
1098920d93eaSCheng Xu 
1099920d93eaSCheng Xu 	erdma_socket_disassoc(s);
1100920d93eaSCheng Xu 
1101920d93eaSCheng Xu error_release_cep:
1102920d93eaSCheng Xu 	/* disassoc with cm_id */
1103920d93eaSCheng Xu 	cep->cm_id = NULL;
1104920d93eaSCheng Xu 	id->rem_ref(id);
1105920d93eaSCheng Xu 
1106920d93eaSCheng Xu 	/* disassoc with qp */
1107920d93eaSCheng Xu 	qp->cep = NULL;
1108920d93eaSCheng Xu 	erdma_cep_put(cep);
1109920d93eaSCheng Xu 	cep->qp = NULL;
1110920d93eaSCheng Xu 
1111920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_CLOSED;
1112920d93eaSCheng Xu 
1113920d93eaSCheng Xu 	erdma_cep_set_free(cep);
1114920d93eaSCheng Xu 
1115920d93eaSCheng Xu 	/* release the cep. */
1116920d93eaSCheng Xu 	erdma_cep_put(cep);
1117920d93eaSCheng Xu 
1118920d93eaSCheng Xu error_release_sock:
1119920d93eaSCheng Xu 	if (s)
1120920d93eaSCheng Xu 		sock_release(s);
1121920d93eaSCheng Xu error_put_qp:
1122920d93eaSCheng Xu 	erdma_qp_put(qp);
1123920d93eaSCheng Xu 
1124920d93eaSCheng Xu 	return ret;
1125920d93eaSCheng Xu }
1126920d93eaSCheng Xu 
erdma_accept(struct iw_cm_id * id,struct iw_cm_conn_param * params)1127920d93eaSCheng Xu int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params)
1128920d93eaSCheng Xu {
1129920d93eaSCheng Xu 	struct erdma_dev *dev = to_edev(id->device);
1130920d93eaSCheng Xu 	struct erdma_cep *cep = (struct erdma_cep *)id->provider_data;
1131920d93eaSCheng Xu 	struct erdma_qp *qp;
1132920d93eaSCheng Xu 	struct erdma_qp_attrs qp_attrs;
1133920d93eaSCheng Xu 	int ret;
1134920d93eaSCheng Xu 
1135920d93eaSCheng Xu 	erdma_cep_set_inuse(cep);
1136920d93eaSCheng Xu 	erdma_cep_put(cep);
1137920d93eaSCheng Xu 
1138920d93eaSCheng Xu 	/* Free lingering inbound private data */
1139920d93eaSCheng Xu 	if (cep->mpa.hdr.params.pd_len) {
1140920d93eaSCheng Xu 		cep->mpa.hdr.params.pd_len = 0;
1141920d93eaSCheng Xu 		kfree(cep->mpa.pdata);
1142920d93eaSCheng Xu 		cep->mpa.pdata = NULL;
1143920d93eaSCheng Xu 	}
1144920d93eaSCheng Xu 	erdma_cancel_mpatimer(cep);
1145920d93eaSCheng Xu 
1146920d93eaSCheng Xu 	if (cep->state != ERDMA_EPSTATE_RECVD_MPAREQ) {
1147920d93eaSCheng Xu 		erdma_cep_set_free(cep);
1148920d93eaSCheng Xu 		erdma_cep_put(cep);
1149920d93eaSCheng Xu 
1150920d93eaSCheng Xu 		return -ECONNRESET;
1151920d93eaSCheng Xu 	}
1152920d93eaSCheng Xu 
1153920d93eaSCheng Xu 	qp = find_qp_by_qpn(dev, params->qpn);
1154920d93eaSCheng Xu 	if (!qp)
1155920d93eaSCheng Xu 		return -ENOENT;
1156920d93eaSCheng Xu 	erdma_qp_get(qp);
1157920d93eaSCheng Xu 
1158920d93eaSCheng Xu 	down_write(&qp->state_lock);
1159920d93eaSCheng Xu 	if (qp->attrs.state > ERDMA_QP_STATE_RTR) {
1160920d93eaSCheng Xu 		ret = -EINVAL;
1161920d93eaSCheng Xu 		up_write(&qp->state_lock);
1162920d93eaSCheng Xu 		goto error;
1163920d93eaSCheng Xu 	}
1164920d93eaSCheng Xu 
1165920d93eaSCheng Xu 	if (params->ord > dev->attrs.max_ord ||
1166920d93eaSCheng Xu 	    params->ird > dev->attrs.max_ord) {
1167920d93eaSCheng Xu 		ret = -EINVAL;
1168920d93eaSCheng Xu 		up_write(&qp->state_lock);
1169920d93eaSCheng Xu 		goto error;
1170920d93eaSCheng Xu 	}
1171920d93eaSCheng Xu 
1172920d93eaSCheng Xu 	if (params->private_data_len > MPA_MAX_PRIVDATA) {
1173920d93eaSCheng Xu 		ret = -EINVAL;
1174920d93eaSCheng Xu 		up_write(&qp->state_lock);
1175920d93eaSCheng Xu 		goto error;
1176920d93eaSCheng Xu 	}
1177920d93eaSCheng Xu 
1178920d93eaSCheng Xu 	cep->ird = params->ird;
1179920d93eaSCheng Xu 	cep->ord = params->ord;
1180920d93eaSCheng Xu 
1181920d93eaSCheng Xu 	cep->cm_id = id;
1182920d93eaSCheng Xu 	id->add_ref(id);
1183920d93eaSCheng Xu 
1184920d93eaSCheng Xu 	memset(&qp_attrs, 0, sizeof(qp_attrs));
1185920d93eaSCheng Xu 	qp_attrs.orq_size = params->ord;
1186920d93eaSCheng Xu 	qp_attrs.irq_size = params->ird;
1187920d93eaSCheng Xu 
1188920d93eaSCheng Xu 	qp_attrs.state = ERDMA_QP_STATE_RTS;
1189920d93eaSCheng Xu 
1190920d93eaSCheng Xu 	/* Associate QP with CEP */
1191920d93eaSCheng Xu 	erdma_cep_get(cep);
1192920d93eaSCheng Xu 	qp->cep = cep;
1193920d93eaSCheng Xu 	cep->qp = qp;
1194920d93eaSCheng Xu 
1195920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_RDMA_MODE;
1196920d93eaSCheng Xu 
1197920d93eaSCheng Xu 	qp->attrs.qp_type = ERDMA_QP_PASSIVE;
1198920d93eaSCheng Xu 	qp->attrs.pd_len = params->private_data_len;
1199920d93eaSCheng Xu 
1200920d93eaSCheng Xu 	if (qp->attrs.cc != __mpa_ext_cc(cep->mpa.ext_data.bits))
1201920d93eaSCheng Xu 		qp->attrs.cc = COMPROMISE_CC;
1202920d93eaSCheng Xu 
1203920d93eaSCheng Xu 	/* move to rts */
1204920d93eaSCheng Xu 	ret = erdma_modify_qp_internal(qp, &qp_attrs,
1205920d93eaSCheng Xu 				       ERDMA_QP_ATTR_STATE |
1206920d93eaSCheng Xu 				       ERDMA_QP_ATTR_ORD |
1207920d93eaSCheng Xu 				       ERDMA_QP_ATTR_LLP_HANDLE |
1208920d93eaSCheng Xu 				       ERDMA_QP_ATTR_IRD |
1209920d93eaSCheng Xu 				       ERDMA_QP_ATTR_MPA);
1210920d93eaSCheng Xu 	up_write(&qp->state_lock);
1211920d93eaSCheng Xu 
1212920d93eaSCheng Xu 	if (ret)
1213920d93eaSCheng Xu 		goto error;
1214920d93eaSCheng Xu 
1215920d93eaSCheng Xu 	cep->mpa.ext_data.bits = 0;
1216920d93eaSCheng Xu 	__mpa_ext_set_cc(&cep->mpa.ext_data.bits, qp->attrs.cc);
1217920d93eaSCheng Xu 	cep->mpa.ext_data.cookie = cpu_to_be32(cep->qp->attrs.cookie);
1218920d93eaSCheng Xu 
1219920d93eaSCheng Xu 	ret = erdma_send_mpareqrep(cep, params->private_data,
1220920d93eaSCheng Xu 				   params->private_data_len);
1221920d93eaSCheng Xu 	if (!ret) {
1222920d93eaSCheng Xu 		ret = erdma_cm_upcall(cep, IW_CM_EVENT_ESTABLISHED, 0);
1223920d93eaSCheng Xu 		if (ret)
1224920d93eaSCheng Xu 			goto error;
1225920d93eaSCheng Xu 
1226920d93eaSCheng Xu 		erdma_cep_set_free(cep);
1227920d93eaSCheng Xu 
1228920d93eaSCheng Xu 		return 0;
1229920d93eaSCheng Xu 	}
1230920d93eaSCheng Xu 
1231920d93eaSCheng Xu error:
1232920d93eaSCheng Xu 	erdma_socket_disassoc(cep->sock);
1233920d93eaSCheng Xu 	sock_release(cep->sock);
1234920d93eaSCheng Xu 	cep->sock = NULL;
1235920d93eaSCheng Xu 
1236920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_CLOSED;
1237920d93eaSCheng Xu 
1238920d93eaSCheng Xu 	if (cep->cm_id) {
1239920d93eaSCheng Xu 		cep->cm_id->rem_ref(id);
1240920d93eaSCheng Xu 		cep->cm_id = NULL;
1241920d93eaSCheng Xu 	}
1242920d93eaSCheng Xu 
1243920d93eaSCheng Xu 	if (qp->cep) {
1244920d93eaSCheng Xu 		erdma_cep_put(cep);
1245920d93eaSCheng Xu 		qp->cep = NULL;
1246920d93eaSCheng Xu 	}
1247920d93eaSCheng Xu 
1248920d93eaSCheng Xu 	cep->qp = NULL;
1249920d93eaSCheng Xu 	erdma_qp_put(qp);
1250920d93eaSCheng Xu 
1251920d93eaSCheng Xu 	erdma_cep_set_free(cep);
1252920d93eaSCheng Xu 	erdma_cep_put(cep);
1253920d93eaSCheng Xu 
1254920d93eaSCheng Xu 	return ret;
1255920d93eaSCheng Xu }
1256920d93eaSCheng Xu 
erdma_reject(struct iw_cm_id * id,const void * pdata,u8 plen)1257920d93eaSCheng Xu int erdma_reject(struct iw_cm_id *id, const void *pdata, u8 plen)
1258920d93eaSCheng Xu {
1259920d93eaSCheng Xu 	struct erdma_cep *cep = (struct erdma_cep *)id->provider_data;
1260920d93eaSCheng Xu 
1261920d93eaSCheng Xu 	erdma_cep_set_inuse(cep);
1262920d93eaSCheng Xu 	erdma_cep_put(cep);
1263920d93eaSCheng Xu 
1264920d93eaSCheng Xu 	erdma_cancel_mpatimer(cep);
1265920d93eaSCheng Xu 
1266920d93eaSCheng Xu 	if (cep->state != ERDMA_EPSTATE_RECVD_MPAREQ) {
1267920d93eaSCheng Xu 		erdma_cep_set_free(cep);
1268920d93eaSCheng Xu 		erdma_cep_put(cep);
1269920d93eaSCheng Xu 
1270920d93eaSCheng Xu 		return -ECONNRESET;
1271920d93eaSCheng Xu 	}
1272920d93eaSCheng Xu 
1273920d93eaSCheng Xu 	if (__mpa_rr_revision(cep->mpa.hdr.params.bits) == MPA_REVISION_EXT_1) {
1274920d93eaSCheng Xu 		cep->mpa.hdr.params.bits |= MPA_RR_FLAG_REJECT; /* reject */
1275920d93eaSCheng Xu 		erdma_send_mpareqrep(cep, pdata, plen);
1276920d93eaSCheng Xu 	}
1277920d93eaSCheng Xu 
1278920d93eaSCheng Xu 	erdma_socket_disassoc(cep->sock);
1279920d93eaSCheng Xu 	sock_release(cep->sock);
1280920d93eaSCheng Xu 	cep->sock = NULL;
1281920d93eaSCheng Xu 
1282920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_CLOSED;
1283920d93eaSCheng Xu 
1284920d93eaSCheng Xu 	erdma_cep_set_free(cep);
1285920d93eaSCheng Xu 	erdma_cep_put(cep);
1286920d93eaSCheng Xu 
1287920d93eaSCheng Xu 	return 0;
1288920d93eaSCheng Xu }
1289920d93eaSCheng Xu 
erdma_create_listen(struct iw_cm_id * id,int backlog)1290920d93eaSCheng Xu int erdma_create_listen(struct iw_cm_id *id, int backlog)
1291920d93eaSCheng Xu {
1292920d93eaSCheng Xu 	struct socket *s;
1293920d93eaSCheng Xu 	struct erdma_cep *cep = NULL;
1294920d93eaSCheng Xu 	int ret = 0;
1295920d93eaSCheng Xu 	struct erdma_dev *dev = to_edev(id->device);
1296920d93eaSCheng Xu 	int addr_family = id->local_addr.ss_family;
1297920d93eaSCheng Xu 	struct sockaddr_in *laddr = &to_sockaddr_in(id->local_addr);
1298920d93eaSCheng Xu 
1299920d93eaSCheng Xu 	if (addr_family != AF_INET)
1300920d93eaSCheng Xu 		return -EAFNOSUPPORT;
1301920d93eaSCheng Xu 
1302920d93eaSCheng Xu 	ret = sock_create(addr_family, SOCK_STREAM, IPPROTO_TCP, &s);
1303920d93eaSCheng Xu 	if (ret < 0)
1304920d93eaSCheng Xu 		return ret;
1305920d93eaSCheng Xu 
1306920d93eaSCheng Xu 	sock_set_reuseaddr(s->sk);
1307920d93eaSCheng Xu 
1308920d93eaSCheng Xu 	/* For wildcard addr, limit binding to current device only */
1309920d93eaSCheng Xu 	if (ipv4_is_zeronet(laddr->sin_addr.s_addr))
1310920d93eaSCheng Xu 		s->sk->sk_bound_dev_if = dev->netdev->ifindex;
1311920d93eaSCheng Xu 
1312920d93eaSCheng Xu 	ret = s->ops->bind(s, (struct sockaddr *)laddr,
1313920d93eaSCheng Xu 			   sizeof(struct sockaddr_in));
1314920d93eaSCheng Xu 	if (ret)
1315920d93eaSCheng Xu 		goto error;
1316920d93eaSCheng Xu 
1317920d93eaSCheng Xu 	cep = erdma_cep_alloc(dev);
1318920d93eaSCheng Xu 	if (!cep) {
1319920d93eaSCheng Xu 		ret = -ENOMEM;
1320920d93eaSCheng Xu 		goto error;
1321920d93eaSCheng Xu 	}
1322920d93eaSCheng Xu 	erdma_cep_socket_assoc(cep, s);
1323920d93eaSCheng Xu 
1324920d93eaSCheng Xu 	ret = erdma_cm_alloc_work(cep, backlog);
1325920d93eaSCheng Xu 	if (ret)
1326920d93eaSCheng Xu 		goto error;
1327920d93eaSCheng Xu 
1328920d93eaSCheng Xu 	ret = s->ops->listen(s, backlog);
1329920d93eaSCheng Xu 	if (ret)
1330920d93eaSCheng Xu 		goto error;
1331920d93eaSCheng Xu 
1332920d93eaSCheng Xu 	cep->cm_id = id;
1333920d93eaSCheng Xu 	id->add_ref(id);
1334920d93eaSCheng Xu 
1335920d93eaSCheng Xu 	if (!id->provider_data) {
1336920d93eaSCheng Xu 		id->provider_data =
1337920d93eaSCheng Xu 			kmalloc(sizeof(struct list_head), GFP_KERNEL);
1338920d93eaSCheng Xu 		if (!id->provider_data) {
1339920d93eaSCheng Xu 			ret = -ENOMEM;
1340920d93eaSCheng Xu 			goto error;
1341920d93eaSCheng Xu 		}
1342920d93eaSCheng Xu 		INIT_LIST_HEAD((struct list_head *)id->provider_data);
1343920d93eaSCheng Xu 	}
1344920d93eaSCheng Xu 
1345920d93eaSCheng Xu 	list_add_tail(&cep->listenq, (struct list_head *)id->provider_data);
1346920d93eaSCheng Xu 	cep->state = ERDMA_EPSTATE_LISTENING;
1347920d93eaSCheng Xu 
1348920d93eaSCheng Xu 	return 0;
1349920d93eaSCheng Xu 
1350920d93eaSCheng Xu error:
1351920d93eaSCheng Xu 	if (cep) {
1352920d93eaSCheng Xu 		erdma_cep_set_inuse(cep);
1353920d93eaSCheng Xu 
1354920d93eaSCheng Xu 		if (cep->cm_id) {
1355920d93eaSCheng Xu 			cep->cm_id->rem_ref(cep->cm_id);
1356920d93eaSCheng Xu 			cep->cm_id = NULL;
1357920d93eaSCheng Xu 		}
1358920d93eaSCheng Xu 		cep->sock = NULL;
1359920d93eaSCheng Xu 		erdma_socket_disassoc(s);
1360920d93eaSCheng Xu 		cep->state = ERDMA_EPSTATE_CLOSED;
1361920d93eaSCheng Xu 
1362920d93eaSCheng Xu 		erdma_cep_set_free(cep);
1363920d93eaSCheng Xu 		erdma_cep_put(cep);
1364920d93eaSCheng Xu 	}
1365920d93eaSCheng Xu 	sock_release(s);
1366920d93eaSCheng Xu 
1367920d93eaSCheng Xu 	return ret;
1368920d93eaSCheng Xu }
1369920d93eaSCheng Xu 
erdma_drop_listeners(struct iw_cm_id * id)1370920d93eaSCheng Xu static void erdma_drop_listeners(struct iw_cm_id *id)
1371920d93eaSCheng Xu {
1372920d93eaSCheng Xu 	struct list_head *p, *tmp;
1373920d93eaSCheng Xu 	/*
1374920d93eaSCheng Xu 	 * In case of a wildcard rdma_listen on a multi-homed device,
1375920d93eaSCheng Xu 	 * a listener's IWCM id is associated with more than one listening CEP.
1376920d93eaSCheng Xu 	 */
1377920d93eaSCheng Xu 	list_for_each_safe(p, tmp, (struct list_head *)id->provider_data) {
1378920d93eaSCheng Xu 		struct erdma_cep *cep =
1379920d93eaSCheng Xu 			list_entry(p, struct erdma_cep, listenq);
1380920d93eaSCheng Xu 
1381920d93eaSCheng Xu 		list_del(p);
1382920d93eaSCheng Xu 
1383920d93eaSCheng Xu 		erdma_cep_set_inuse(cep);
1384920d93eaSCheng Xu 
1385920d93eaSCheng Xu 		if (cep->cm_id) {
1386920d93eaSCheng Xu 			cep->cm_id->rem_ref(cep->cm_id);
1387920d93eaSCheng Xu 			cep->cm_id = NULL;
1388920d93eaSCheng Xu 		}
1389920d93eaSCheng Xu 		if (cep->sock) {
1390920d93eaSCheng Xu 			erdma_socket_disassoc(cep->sock);
1391920d93eaSCheng Xu 			sock_release(cep->sock);
1392920d93eaSCheng Xu 			cep->sock = NULL;
1393920d93eaSCheng Xu 		}
1394920d93eaSCheng Xu 		cep->state = ERDMA_EPSTATE_CLOSED;
1395920d93eaSCheng Xu 		erdma_cep_set_free(cep);
1396920d93eaSCheng Xu 		erdma_cep_put(cep);
1397920d93eaSCheng Xu 	}
1398920d93eaSCheng Xu }
1399920d93eaSCheng Xu 
erdma_destroy_listen(struct iw_cm_id * id)1400920d93eaSCheng Xu int erdma_destroy_listen(struct iw_cm_id *id)
1401920d93eaSCheng Xu {
1402920d93eaSCheng Xu 	if (!id->provider_data)
1403920d93eaSCheng Xu 		return 0;
1404920d93eaSCheng Xu 
1405920d93eaSCheng Xu 	erdma_drop_listeners(id);
1406920d93eaSCheng Xu 	kfree(id->provider_data);
1407920d93eaSCheng Xu 	id->provider_data = NULL;
1408920d93eaSCheng Xu 
1409920d93eaSCheng Xu 	return 0;
1410920d93eaSCheng Xu }
1411920d93eaSCheng Xu 
erdma_cm_init(void)1412920d93eaSCheng Xu int erdma_cm_init(void)
1413920d93eaSCheng Xu {
1414920d93eaSCheng Xu 	erdma_cm_wq = create_singlethread_workqueue("erdma_cm_wq");
1415920d93eaSCheng Xu 	if (!erdma_cm_wq)
1416920d93eaSCheng Xu 		return -ENOMEM;
1417920d93eaSCheng Xu 
1418920d93eaSCheng Xu 	return 0;
1419920d93eaSCheng Xu }
1420920d93eaSCheng Xu 
erdma_cm_exit(void)1421920d93eaSCheng Xu void erdma_cm_exit(void)
1422920d93eaSCheng Xu {
1423920d93eaSCheng Xu 	if (erdma_cm_wq)
1424920d93eaSCheng Xu 		destroy_workqueue(erdma_cm_wq);
1425920d93eaSCheng Xu }
1426