xref: /openbmc/linux/net/smc/smc_close.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b38d7324SUrsula Braun /*
3b38d7324SUrsula Braun  *  Shared Memory Communications over RDMA (SMC-R) and RoCE
4b38d7324SUrsula Braun  *
5b38d7324SUrsula Braun  *  Socket Closing - normal and abnormal
6b38d7324SUrsula Braun  *
7b38d7324SUrsula Braun  *  Copyright IBM Corp. 2016
8b38d7324SUrsula Braun  *
9b38d7324SUrsula Braun  *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
10b38d7324SUrsula Braun  */
11b38d7324SUrsula Braun 
12b38d7324SUrsula Braun #include <linux/workqueue.h>
13c3edc401SIngo Molnar #include <linux/sched/signal.h>
14c3edc401SIngo Molnar 
15b38d7324SUrsula Braun #include <net/sock.h>
1683179760SUrsula Braun #include <net/tcp.h>
17b38d7324SUrsula Braun 
18b38d7324SUrsula Braun #include "smc.h"
19b38d7324SUrsula Braun #include "smc_tx.h"
20b38d7324SUrsula Braun #include "smc_cdc.h"
21b38d7324SUrsula Braun #include "smc_close.h"
22b38d7324SUrsula Braun 
23fd57770dSKarsten Graul /* release the clcsock that is assigned to the smc_sock */
smc_clcsock_release(struct smc_sock * smc)24fd57770dSKarsten Graul void smc_clcsock_release(struct smc_sock *smc)
25fd57770dSKarsten Graul {
26fd57770dSKarsten Graul 	struct socket *tcp;
27fd57770dSKarsten Graul 
28fd57770dSKarsten Graul 	if (smc->listen_smc && current_work() != &smc->smc_listen_work)
29fd57770dSKarsten Graul 		cancel_work_sync(&smc->smc_listen_work);
30fd57770dSKarsten Graul 	mutex_lock(&smc->clcsock_release_lock);
31fd57770dSKarsten Graul 	if (smc->clcsock) {
32fd57770dSKarsten Graul 		tcp = smc->clcsock;
33fd57770dSKarsten Graul 		smc->clcsock = NULL;
34fd57770dSKarsten Graul 		sock_release(tcp);
35fd57770dSKarsten Graul 	}
36fd57770dSKarsten Graul 	mutex_unlock(&smc->clcsock_release_lock);
37fd57770dSKarsten Graul }
38fd57770dSKarsten Graul 
smc_close_cleanup_listen(struct sock * parent)39b38d7324SUrsula Braun static void smc_close_cleanup_listen(struct sock *parent)
40b38d7324SUrsula Braun {
41b38d7324SUrsula Braun 	struct sock *sk;
42b38d7324SUrsula Braun 
43b38d7324SUrsula Braun 	/* Close non-accepted connections */
44b38d7324SUrsula Braun 	while ((sk = smc_accept_dequeue(parent, NULL)))
45b38d7324SUrsula Braun 		smc_close_non_accepted(sk);
46b38d7324SUrsula Braun }
47b38d7324SUrsula Braun 
48b38d7324SUrsula Braun /* wait for sndbuf data being transmitted */
smc_close_stream_wait(struct smc_sock * smc,long timeout)49b38d7324SUrsula Braun static void smc_close_stream_wait(struct smc_sock *smc, long timeout)
50b38d7324SUrsula Braun {
51b38d7324SUrsula Braun 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
52b38d7324SUrsula Braun 	struct sock *sk = &smc->sk;
53b38d7324SUrsula Braun 
54b38d7324SUrsula Braun 	if (!timeout)
55b38d7324SUrsula Braun 		return;
56b38d7324SUrsula Braun 
57b38d7324SUrsula Braun 	if (!smc_tx_prepared_sends(&smc->conn))
58b38d7324SUrsula Braun 		return;
59b38d7324SUrsula Braun 
60906b3d64SWen Gu 	/* Send out corked data remaining in sndbuf */
61906b3d64SWen Gu 	smc_tx_pending(&smc->conn);
62906b3d64SWen Gu 
63b38d7324SUrsula Braun 	smc->wait_close_tx_prepared = 1;
64b38d7324SUrsula Braun 	add_wait_queue(sk_sleep(sk), &wait);
65b38d7324SUrsula Braun 	while (!signal_pending(current) && timeout) {
66b38d7324SUrsula Braun 		int rc;
67b38d7324SUrsula Braun 
68b38d7324SUrsula Braun 		rc = sk_wait_event(sk, &timeout,
69b38d7324SUrsula Braun 				   !smc_tx_prepared_sends(&smc->conn) ||
70d0ac89f6SEric Dumazet 				   READ_ONCE(sk->sk_err) == ECONNABORTED ||
71d0ac89f6SEric Dumazet 				   READ_ONCE(sk->sk_err) == ECONNRESET ||
72b2900980SUrsula Braun 				   smc->conn.killed,
73b38d7324SUrsula Braun 				   &wait);
74b38d7324SUrsula Braun 		if (rc)
75b38d7324SUrsula Braun 			break;
76b38d7324SUrsula Braun 	}
77b38d7324SUrsula Braun 	remove_wait_queue(sk_sleep(sk), &wait);
78b38d7324SUrsula Braun 	smc->wait_close_tx_prepared = 0;
79b38d7324SUrsula Braun }
80b38d7324SUrsula Braun 
smc_close_wake_tx_prepared(struct smc_sock * smc)81b38d7324SUrsula Braun void smc_close_wake_tx_prepared(struct smc_sock *smc)
82b38d7324SUrsula Braun {
83b38d7324SUrsula Braun 	if (smc->wait_close_tx_prepared)
84b38d7324SUrsula Braun 		/* wake up socket closing */
85b38d7324SUrsula Braun 		smc->sk.sk_state_change(&smc->sk);
86b38d7324SUrsula Braun }
87b38d7324SUrsula Braun 
smc_close_wr(struct smc_connection * conn)88b38d7324SUrsula Braun static int smc_close_wr(struct smc_connection *conn)
89b38d7324SUrsula Braun {
90b38d7324SUrsula Braun 	conn->local_tx_ctrl.conn_state_flags.peer_done_writing = 1;
91b38d7324SUrsula Braun 
92b38d7324SUrsula Braun 	return smc_cdc_get_slot_and_msg_send(conn);
93b38d7324SUrsula Braun }
94b38d7324SUrsula Braun 
smc_close_final(struct smc_connection * conn)95b38d7324SUrsula Braun static int smc_close_final(struct smc_connection *conn)
96b38d7324SUrsula Braun {
97b38d7324SUrsula Braun 	if (atomic_read(&conn->bytes_to_rcv))
98b38d7324SUrsula Braun 		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
99b38d7324SUrsula Braun 	else
100b38d7324SUrsula Braun 		conn->local_tx_ctrl.conn_state_flags.peer_conn_closed = 1;
101b2900980SUrsula Braun 	if (conn->killed)
102b2900980SUrsula Braun 		return -EPIPE;
103b38d7324SUrsula Braun 
104b38d7324SUrsula Braun 	return smc_cdc_get_slot_and_msg_send(conn);
105b38d7324SUrsula Braun }
106b38d7324SUrsula Braun 
smc_close_abort(struct smc_connection * conn)10783179760SUrsula Braun int smc_close_abort(struct smc_connection *conn)
108b38d7324SUrsula Braun {
109b38d7324SUrsula Braun 	conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
110b38d7324SUrsula Braun 
111b38d7324SUrsula Braun 	return smc_cdc_get_slot_and_msg_send(conn);
112b38d7324SUrsula Braun }
113b38d7324SUrsula Braun 
smc_close_cancel_work(struct smc_sock * smc)11442bfba9eSUrsula Braun static void smc_close_cancel_work(struct smc_sock *smc)
11542bfba9eSUrsula Braun {
11642bfba9eSUrsula Braun 	struct sock *sk = &smc->sk;
11742bfba9eSUrsula Braun 
11842bfba9eSUrsula Braun 	release_sock(sk);
119*fdfccd3dSD. Wythe 	if (cancel_work_sync(&smc->conn.close_work))
120*fdfccd3dSD. Wythe 		sock_put(sk);
12142bfba9eSUrsula Braun 	cancel_delayed_work_sync(&smc->conn.tx_work);
12242bfba9eSUrsula Braun 	lock_sock(sk);
12342bfba9eSUrsula Braun }
12442bfba9eSUrsula Braun 
125b38d7324SUrsula Braun /* terminate smc socket abnormally - active abort
126732720faSUrsula Braun  * link group is terminated, i.e. RDMA communication no longer possible
127b38d7324SUrsula Braun  */
smc_close_active_abort(struct smc_sock * smc)12881cf4f47SUrsula Braun void smc_close_active_abort(struct smc_sock *smc)
129b38d7324SUrsula Braun {
1303163c507SUrsula Braun 	struct sock *sk = &smc->sk;
13181cf4f47SUrsula Braun 	bool release_clcsock = false;
1323163c507SUrsula Braun 
133dd65d87aSUrsula Braun 	if (sk->sk_state != SMC_INIT && smc->clcsock && smc->clcsock->sk) {
1343163c507SUrsula Braun 		sk->sk_err = ECONNABORTED;
13583179760SUrsula Braun 		if (smc->clcsock && smc->clcsock->sk)
13683179760SUrsula Braun 			tcp_abort(smc->clcsock->sk, ECONNABORTED);
137dd65d87aSUrsula Braun 	}
1383163c507SUrsula Braun 	switch (sk->sk_state) {
13946c28dbdSUrsula Braun 	case SMC_ACTIVE:
140b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT1:
141b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT2:
1425fb8642aSUrsula Braun 		sk->sk_state = SMC_PEERABORTWAIT;
14342bfba9eSUrsula Braun 		smc_close_cancel_work(smc);
1445fb8642aSUrsula Braun 		if (sk->sk_state != SMC_PEERABORTWAIT)
1455fb8642aSUrsula Braun 			break;
146d18963cfSUrsula Braun 		sk->sk_state = SMC_CLOSED;
1475fb8642aSUrsula Braun 		sock_put(sk); /* (postponed) passive closing */
148b38d7324SUrsula Braun 		break;
149b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT1:
150b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT2:
151d18963cfSUrsula Braun 	case SMC_PEERFINCLOSEWAIT:
15242bfba9eSUrsula Braun 		sk->sk_state = SMC_PEERABORTWAIT;
15342bfba9eSUrsula Braun 		smc_close_cancel_work(smc);
1545fb8642aSUrsula Braun 		if (sk->sk_state != SMC_PEERABORTWAIT)
1555fb8642aSUrsula Braun 			break;
1563163c507SUrsula Braun 		sk->sk_state = SMC_CLOSED;
15781cf4f47SUrsula Braun 		smc_conn_free(&smc->conn);
15881cf4f47SUrsula Braun 		release_clcsock = true;
15951f1de79SUrsula Braun 		sock_put(sk); /* passive closing */
160b38d7324SUrsula Braun 		break;
161b38d7324SUrsula Braun 	case SMC_PROCESSABORT:
162b38d7324SUrsula Braun 	case SMC_APPFINCLOSEWAIT:
16342bfba9eSUrsula Braun 		sk->sk_state = SMC_PEERABORTWAIT;
16442bfba9eSUrsula Braun 		smc_close_cancel_work(smc);
1655fb8642aSUrsula Braun 		if (sk->sk_state != SMC_PEERABORTWAIT)
1665fb8642aSUrsula Braun 			break;
1673163c507SUrsula Braun 		sk->sk_state = SMC_CLOSED;
16842bfba9eSUrsula Braun 		smc_conn_free(&smc->conn);
16942bfba9eSUrsula Braun 		release_clcsock = true;
170b38d7324SUrsula Braun 		break;
171dd65d87aSUrsula Braun 	case SMC_INIT:
172b38d7324SUrsula Braun 	case SMC_PEERABORTWAIT:
173b38d7324SUrsula Braun 	case SMC_CLOSED:
174b38d7324SUrsula Braun 		break;
175b38d7324SUrsula Braun 	}
176b38d7324SUrsula Braun 
177afc6a04fSD. Wythe 	smc_sock_set_flag(sk, SOCK_DEAD);
1783163c507SUrsula Braun 	sk->sk_state_change(sk);
17981cf4f47SUrsula Braun 
18081cf4f47SUrsula Braun 	if (release_clcsock) {
18181cf4f47SUrsula Braun 		release_sock(sk);
18281cf4f47SUrsula Braun 		smc_clcsock_release(smc);
18381cf4f47SUrsula Braun 		lock_sock(sk);
18481cf4f47SUrsula Braun 	}
185b38d7324SUrsula Braun }
186b38d7324SUrsula Braun 
smc_close_sent_any_close(struct smc_connection * conn)187a98bf8c0SUrsula Braun static inline bool smc_close_sent_any_close(struct smc_connection *conn)
188a98bf8c0SUrsula Braun {
189a98bf8c0SUrsula Braun 	return conn->local_tx_ctrl.conn_state_flags.peer_conn_abort ||
190a98bf8c0SUrsula Braun 	       conn->local_tx_ctrl.conn_state_flags.peer_conn_closed;
191a98bf8c0SUrsula Braun }
192a98bf8c0SUrsula Braun 
smc_close_active(struct smc_sock * smc)193b38d7324SUrsula Braun int smc_close_active(struct smc_sock *smc)
194b38d7324SUrsula Braun {
195b38d7324SUrsula Braun 	struct smc_cdc_conn_state_flags *txflags =
196b38d7324SUrsula Braun 		&smc->conn.local_tx_ctrl.conn_state_flags;
197b38d7324SUrsula Braun 	struct smc_connection *conn = &smc->conn;
198b38d7324SUrsula Braun 	struct sock *sk = &smc->sk;
199b38d7324SUrsula Braun 	int old_state;
2008c96feeeSUrsula Braun 	long timeout;
201b38d7324SUrsula Braun 	int rc = 0;
20200e158fbSTony Lu 	int rc1 = 0;
203b38d7324SUrsula Braun 
2048c96feeeSUrsula Braun 	timeout = current->flags & PF_EXITING ?
2058c96feeeSUrsula Braun 		  0 : sock_flag(sk, SOCK_LINGER) ?
2068c96feeeSUrsula Braun 		      sk->sk_lingertime : SMC_MAX_STREAM_WAIT_TIMEOUT;
207b38d7324SUrsula Braun 
208b38d7324SUrsula Braun 	old_state = sk->sk_state;
209bbb96bf2SUrsula Braun again:
210bbb96bf2SUrsula Braun 	switch (sk->sk_state) {
211b38d7324SUrsula Braun 	case SMC_INIT:
212b38d7324SUrsula Braun 		sk->sk_state = SMC_CLOSED;
213b38d7324SUrsula Braun 		break;
214b38d7324SUrsula Braun 	case SMC_LISTEN:
215b38d7324SUrsula Braun 		sk->sk_state = SMC_CLOSED;
216ddcc9b7fSKarsten Graul 		sk->sk_state_change(sk); /* wake up accept */
217ddcc9b7fSKarsten Graul 		if (smc->clcsock && smc->clcsock->sk) {
2180558226cSWen Gu 			write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
21997b9af7aSWen Gu 			smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
22097b9af7aSWen Gu 					       &smc->clcsk_data_ready);
221a60a2b1eSUrsula Braun 			smc->clcsock->sk->sk_user_data = NULL;
2220558226cSWen Gu 			write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
223b38d7324SUrsula Braun 			rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
224ddcc9b7fSKarsten Graul 		}
225b38d7324SUrsula Braun 		smc_close_cleanup_listen(sk);
2263d502067SUrsula Braun 		release_sock(sk);
2273d502067SUrsula Braun 		flush_work(&smc->tcp_listen_work);
2283d502067SUrsula Braun 		lock_sock(sk);
229b38d7324SUrsula Braun 		break;
230b38d7324SUrsula Braun 	case SMC_ACTIVE:
231b38d7324SUrsula Braun 		smc_close_stream_wait(smc, timeout);
232b38d7324SUrsula Braun 		release_sock(sk);
23318e537cdSUrsula Braun 		cancel_delayed_work_sync(&conn->tx_work);
234b38d7324SUrsula Braun 		lock_sock(sk);
235b38d7324SUrsula Braun 		if (sk->sk_state == SMC_ACTIVE) {
236b38d7324SUrsula Braun 			/* send close request */
237b38d7324SUrsula Braun 			rc = smc_close_final(conn);
238b38d7324SUrsula Braun 			sk->sk_state = SMC_PEERCLOSEWAIT1;
239606a63c9STony Lu 
240606a63c9STony Lu 			/* actively shutdown clcsock before peer close it,
241606a63c9STony Lu 			 * prevent peer from entering TIME_WAIT state.
242606a63c9STony Lu 			 */
24300e158fbSTony Lu 			if (smc->clcsock && smc->clcsock->sk) {
24400e158fbSTony Lu 				rc1 = kernel_sock_shutdown(smc->clcsock,
24500e158fbSTony Lu 							   SHUT_RDWR);
24600e158fbSTony Lu 				rc = rc ? rc : rc1;
24700e158fbSTony Lu 			}
248b38d7324SUrsula Braun 		} else {
249b38d7324SUrsula Braun 			/* peer event has changed the state */
250b38d7324SUrsula Braun 			goto again;
251b38d7324SUrsula Braun 		}
252b38d7324SUrsula Braun 		break;
253b38d7324SUrsula Braun 	case SMC_APPFINCLOSEWAIT:
254b38d7324SUrsula Braun 		/* socket already shutdown wr or both (active close) */
255b38d7324SUrsula Braun 		if (txflags->peer_done_writing &&
256a98bf8c0SUrsula Braun 		    !smc_close_sent_any_close(conn)) {
257b38d7324SUrsula Braun 			/* just shutdown wr done, send close request */
258b38d7324SUrsula Braun 			rc = smc_close_final(conn);
259b38d7324SUrsula Braun 		}
260b38d7324SUrsula Braun 		sk->sk_state = SMC_CLOSED;
261b38d7324SUrsula Braun 		break;
262b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT1:
263b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT2:
264b38d7324SUrsula Braun 		if (!smc_cdc_rxed_any_close(conn))
265b38d7324SUrsula Braun 			smc_close_stream_wait(smc, timeout);
266b38d7324SUrsula Braun 		release_sock(sk);
26718e537cdSUrsula Braun 		cancel_delayed_work_sync(&conn->tx_work);
268b38d7324SUrsula Braun 		lock_sock(sk);
269bbb96bf2SUrsula Braun 		if (sk->sk_state != SMC_APPCLOSEWAIT1 &&
270bbb96bf2SUrsula Braun 		    sk->sk_state != SMC_APPCLOSEWAIT2)
271bbb96bf2SUrsula Braun 			goto again;
272b38d7324SUrsula Braun 		/* confirm close from peer */
273b38d7324SUrsula Braun 		rc = smc_close_final(conn);
27451f1de79SUrsula Braun 		if (smc_cdc_rxed_any_close(conn)) {
275b38d7324SUrsula Braun 			/* peer has closed the socket already */
276b38d7324SUrsula Braun 			sk->sk_state = SMC_CLOSED;
27751f1de79SUrsula Braun 			sock_put(sk); /* postponed passive closing */
27851f1de79SUrsula Braun 		} else {
279b38d7324SUrsula Braun 			/* peer has just issued a shutdown write */
280b38d7324SUrsula Braun 			sk->sk_state = SMC_PEERFINCLOSEWAIT;
28151f1de79SUrsula Braun 		}
282b38d7324SUrsula Braun 		break;
283b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT1:
284b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT2:
285a98bf8c0SUrsula Braun 		if (txflags->peer_done_writing &&
286a98bf8c0SUrsula Braun 		    !smc_close_sent_any_close(conn)) {
287a98bf8c0SUrsula Braun 			/* just shutdown wr done, send close request */
288a98bf8c0SUrsula Braun 			rc = smc_close_final(conn);
289a98bf8c0SUrsula Braun 		}
290a98bf8c0SUrsula Braun 		/* peer sending PeerConnectionClosed will cause transition */
291a98bf8c0SUrsula Braun 		break;
292b38d7324SUrsula Braun 	case SMC_PEERFINCLOSEWAIT:
293b38d7324SUrsula Braun 		/* peer sending PeerConnectionClosed will cause transition */
294b38d7324SUrsula Braun 		break;
295b38d7324SUrsula Braun 	case SMC_PROCESSABORT:
296d18963cfSUrsula Braun 		rc = smc_close_abort(conn);
297b38d7324SUrsula Braun 		sk->sk_state = SMC_CLOSED;
298b38d7324SUrsula Braun 		break;
299b38d7324SUrsula Braun 	case SMC_PEERABORTWAIT:
300d18963cfSUrsula Braun 		sk->sk_state = SMC_CLOSED;
301d18963cfSUrsula Braun 		break;
302b38d7324SUrsula Braun 	case SMC_CLOSED:
303b38d7324SUrsula Braun 		/* nothing to do, add tracing in future patch */
304b38d7324SUrsula Braun 		break;
305b38d7324SUrsula Braun 	}
306b38d7324SUrsula Braun 
307b38d7324SUrsula Braun 	if (old_state != sk->sk_state)
3083163c507SUrsula Braun 		sk->sk_state_change(sk);
309b38d7324SUrsula Braun 	return rc;
310b38d7324SUrsula Braun }
311b38d7324SUrsula Braun 
smc_close_passive_abort_received(struct smc_sock * smc)312b38d7324SUrsula Braun static void smc_close_passive_abort_received(struct smc_sock *smc)
313b38d7324SUrsula Braun {
314b38d7324SUrsula Braun 	struct smc_cdc_conn_state_flags *txflags =
315b38d7324SUrsula Braun 		&smc->conn.local_tx_ctrl.conn_state_flags;
316b38d7324SUrsula Braun 	struct sock *sk = &smc->sk;
317b38d7324SUrsula Braun 
318b38d7324SUrsula Braun 	switch (sk->sk_state) {
31951f1de79SUrsula Braun 	case SMC_INIT:
320b38d7324SUrsula Braun 	case SMC_ACTIVE:
321b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT1:
32251f1de79SUrsula Braun 		sk->sk_state = SMC_PROCESSABORT;
32351f1de79SUrsula Braun 		sock_put(sk); /* passive closing */
32451f1de79SUrsula Braun 		break;
32551f1de79SUrsula Braun 	case SMC_APPFINCLOSEWAIT:
326b38d7324SUrsula Braun 		sk->sk_state = SMC_PROCESSABORT;
327b38d7324SUrsula Braun 		break;
328b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT1:
329b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT2:
330b38d7324SUrsula Braun 		if (txflags->peer_done_writing &&
33151f1de79SUrsula Braun 		    !smc_close_sent_any_close(&smc->conn))
332b38d7324SUrsula Braun 			/* just shutdown, but not yet closed locally */
333b38d7324SUrsula Braun 			sk->sk_state = SMC_PROCESSABORT;
33451f1de79SUrsula Braun 		else
335b38d7324SUrsula Braun 			sk->sk_state = SMC_CLOSED;
33651f1de79SUrsula Braun 		sock_put(sk); /* passive closing */
337b38d7324SUrsula Braun 		break;
33851f1de79SUrsula Braun 	case SMC_APPCLOSEWAIT2:
339b38d7324SUrsula Braun 	case SMC_PEERFINCLOSEWAIT:
34051f1de79SUrsula Braun 		sk->sk_state = SMC_CLOSED;
34151f1de79SUrsula Braun 		sock_put(sk); /* passive closing */
34251f1de79SUrsula Braun 		break;
343b38d7324SUrsula Braun 	case SMC_PEERABORTWAIT:
344b38d7324SUrsula Braun 		sk->sk_state = SMC_CLOSED;
345b38d7324SUrsula Braun 		break;
346b38d7324SUrsula Braun 	case SMC_PROCESSABORT:
347b38d7324SUrsula Braun 	/* nothing to do, add tracing in future patch */
348b38d7324SUrsula Braun 		break;
349b38d7324SUrsula Braun 	}
350b38d7324SUrsula Braun }
351b38d7324SUrsula Braun 
352732720faSUrsula Braun /* Either some kind of closing has been received: peer_conn_closed,
353732720faSUrsula Braun  * peer_conn_abort, or peer_done_writing
354732720faSUrsula Braun  * or the link group of the connection terminates abnormally.
355b38d7324SUrsula Braun  */
smc_close_passive_work(struct work_struct * work)35646c28dbdSUrsula Braun static void smc_close_passive_work(struct work_struct *work)
357b38d7324SUrsula Braun {
35846c28dbdSUrsula Braun 	struct smc_connection *conn = container_of(work,
35946c28dbdSUrsula Braun 						   struct smc_connection,
36046c28dbdSUrsula Braun 						   close_work);
36146c28dbdSUrsula Braun 	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
36246c28dbdSUrsula Braun 	struct smc_cdc_conn_state_flags *rxflags;
363fd57770dSKarsten Graul 	bool release_clcsock = false;
364b38d7324SUrsula Braun 	struct sock *sk = &smc->sk;
365b38d7324SUrsula Braun 	int old_state;
366b38d7324SUrsula Braun 
3673163c507SUrsula Braun 	lock_sock(sk);
368b38d7324SUrsula Braun 	old_state = sk->sk_state;
369b38d7324SUrsula Braun 
3703163c507SUrsula Braun 	rxflags = &conn->local_rx_ctrl.conn_state_flags;
371b38d7324SUrsula Braun 	if (rxflags->peer_conn_abort) {
372732720faSUrsula Braun 		/* peer has not received all data */
373b38d7324SUrsula Braun 		smc_close_passive_abort_received(smc);
37445c3ff7aSTony Lu 		release_sock(sk);
375611b63a1SUrsula Braun 		cancel_delayed_work_sync(&conn->tx_work);
37645c3ff7aSTony Lu 		lock_sock(sk);
377b38d7324SUrsula Braun 		goto wakeup;
378b38d7324SUrsula Braun 	}
379b38d7324SUrsula Braun 
380b38d7324SUrsula Braun 	switch (sk->sk_state) {
381b38d7324SUrsula Braun 	case SMC_INIT:
382b38d7324SUrsula Braun 		sk->sk_state = SMC_APPCLOSEWAIT1;
383b38d7324SUrsula Braun 		break;
384b38d7324SUrsula Braun 	case SMC_ACTIVE:
385b38d7324SUrsula Braun 		sk->sk_state = SMC_APPCLOSEWAIT1;
38651f1de79SUrsula Braun 		/* postpone sock_put() for passive closing to cover
38751f1de79SUrsula Braun 		 * received SEND_SHUTDOWN as well
38851f1de79SUrsula Braun 		 */
389b38d7324SUrsula Braun 		break;
390b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT1:
391b38d7324SUrsula Braun 		if (rxflags->peer_done_writing)
392b38d7324SUrsula Braun 			sk->sk_state = SMC_PEERCLOSEWAIT2;
393df561f66SGustavo A. R. Silva 		fallthrough;
3947f6b437eSGustavo A. R. Silva 		/* to check for closing */
395b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT2:
3963163c507SUrsula Braun 		if (!smc_cdc_rxed_any_close(conn))
397b38d7324SUrsula Braun 			break;
398b38d7324SUrsula Braun 		if (sock_flag(sk, SOCK_DEAD) &&
399a98bf8c0SUrsula Braun 		    smc_close_sent_any_close(conn)) {
400b38d7324SUrsula Braun 			/* smc_release has already been called locally */
401b38d7324SUrsula Braun 			sk->sk_state = SMC_CLOSED;
402b38d7324SUrsula Braun 		} else {
403b38d7324SUrsula Braun 			/* just shutdown, but not yet closed locally */
404b38d7324SUrsula Braun 			sk->sk_state = SMC_APPFINCLOSEWAIT;
405b38d7324SUrsula Braun 		}
40651f1de79SUrsula Braun 		sock_put(sk); /* passive closing */
407b38d7324SUrsula Braun 		break;
4085ac92a00SUrsula Braun 	case SMC_PEERFINCLOSEWAIT:
40951f1de79SUrsula Braun 		if (smc_cdc_rxed_any_close(conn)) {
4105ac92a00SUrsula Braun 			sk->sk_state = SMC_CLOSED;
41151f1de79SUrsula Braun 			sock_put(sk); /* passive closing */
41251f1de79SUrsula Braun 		}
4135ac92a00SUrsula Braun 		break;
414b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT1:
415b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT2:
41651f1de79SUrsula Braun 		/* postpone sock_put() for passive closing to cover
41751f1de79SUrsula Braun 		 * received SEND_SHUTDOWN as well
41851f1de79SUrsula Braun 		 */
41951f1de79SUrsula Braun 		break;
420b38d7324SUrsula Braun 	case SMC_APPFINCLOSEWAIT:
421b38d7324SUrsula Braun 	case SMC_PEERABORTWAIT:
422b38d7324SUrsula Braun 	case SMC_PROCESSABORT:
423b38d7324SUrsula Braun 	case SMC_CLOSED:
424b38d7324SUrsula Braun 		/* nothing to do, add tracing in future patch */
425b38d7324SUrsula Braun 		break;
426b38d7324SUrsula Braun 	}
427b38d7324SUrsula Braun 
428b38d7324SUrsula Braun wakeup:
429b38d7324SUrsula Braun 	sk->sk_data_ready(sk); /* wakeup blocked rcvbuf consumers */
430b38d7324SUrsula Braun 	sk->sk_write_space(sk); /* wakeup blocked sndbuf producers */
431b38d7324SUrsula Braun 
432a98bf8c0SUrsula Braun 	if (old_state != sk->sk_state) {
433a98bf8c0SUrsula Braun 		sk->sk_state_change(sk);
434b38d7324SUrsula Braun 		if ((sk->sk_state == SMC_CLOSED) &&
435b03faa1fSUrsula Braun 		    (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) {
4363163c507SUrsula Braun 			smc_conn_free(conn);
437fd57770dSKarsten Graul 			if (smc->clcsock)
438fd57770dSKarsten Graul 				release_clcsock = true;
439b03faa1fSUrsula Braun 		}
440a98bf8c0SUrsula Braun 	}
4413163c507SUrsula Braun 	release_sock(sk);
442fd57770dSKarsten Graul 	if (release_clcsock)
443fd57770dSKarsten Graul 		smc_clcsock_release(smc);
44451f1de79SUrsula Braun 	sock_put(sk); /* sock_hold done by schedulers of close_work */
445b38d7324SUrsula Braun }
446b38d7324SUrsula Braun 
smc_close_shutdown_write(struct smc_sock * smc)447b38d7324SUrsula Braun int smc_close_shutdown_write(struct smc_sock *smc)
448b38d7324SUrsula Braun {
449b38d7324SUrsula Braun 	struct smc_connection *conn = &smc->conn;
450b38d7324SUrsula Braun 	struct sock *sk = &smc->sk;
451b38d7324SUrsula Braun 	int old_state;
4528c96feeeSUrsula Braun 	long timeout;
453b38d7324SUrsula Braun 	int rc = 0;
454b38d7324SUrsula Braun 
4558c96feeeSUrsula Braun 	timeout = current->flags & PF_EXITING ?
4568c96feeeSUrsula Braun 		  0 : sock_flag(sk, SOCK_LINGER) ?
4578c96feeeSUrsula Braun 		      sk->sk_lingertime : SMC_MAX_STREAM_WAIT_TIMEOUT;
458b38d7324SUrsula Braun 
459b38d7324SUrsula Braun 	old_state = sk->sk_state;
460bbb96bf2SUrsula Braun again:
461bbb96bf2SUrsula Braun 	switch (sk->sk_state) {
462b38d7324SUrsula Braun 	case SMC_ACTIVE:
463b38d7324SUrsula Braun 		smc_close_stream_wait(smc, timeout);
464b38d7324SUrsula Braun 		release_sock(sk);
46518e537cdSUrsula Braun 		cancel_delayed_work_sync(&conn->tx_work);
466b38d7324SUrsula Braun 		lock_sock(sk);
467bbb96bf2SUrsula Braun 		if (sk->sk_state != SMC_ACTIVE)
468bbb96bf2SUrsula Braun 			goto again;
469b38d7324SUrsula Braun 		/* send close wr request */
470b38d7324SUrsula Braun 		rc = smc_close_wr(conn);
471b38d7324SUrsula Braun 		sk->sk_state = SMC_PEERCLOSEWAIT1;
472b38d7324SUrsula Braun 		break;
473b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT1:
474b38d7324SUrsula Braun 		/* passive close */
475b38d7324SUrsula Braun 		if (!smc_cdc_rxed_any_close(conn))
476b38d7324SUrsula Braun 			smc_close_stream_wait(smc, timeout);
477b38d7324SUrsula Braun 		release_sock(sk);
47818e537cdSUrsula Braun 		cancel_delayed_work_sync(&conn->tx_work);
479b38d7324SUrsula Braun 		lock_sock(sk);
480bbb96bf2SUrsula Braun 		if (sk->sk_state != SMC_APPCLOSEWAIT1)
481bbb96bf2SUrsula Braun 			goto again;
482b38d7324SUrsula Braun 		/* confirm close from peer */
483b38d7324SUrsula Braun 		rc = smc_close_wr(conn);
484b38d7324SUrsula Braun 		sk->sk_state = SMC_APPCLOSEWAIT2;
485b38d7324SUrsula Braun 		break;
486b38d7324SUrsula Braun 	case SMC_APPCLOSEWAIT2:
487b38d7324SUrsula Braun 	case SMC_PEERFINCLOSEWAIT:
488b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT1:
489b38d7324SUrsula Braun 	case SMC_PEERCLOSEWAIT2:
490b38d7324SUrsula Braun 	case SMC_APPFINCLOSEWAIT:
491b38d7324SUrsula Braun 	case SMC_PROCESSABORT:
492b38d7324SUrsula Braun 	case SMC_PEERABORTWAIT:
493b38d7324SUrsula Braun 		/* nothing to do, add tracing in future patch */
494b38d7324SUrsula Braun 		break;
495b38d7324SUrsula Braun 	}
496b38d7324SUrsula Braun 
497b38d7324SUrsula Braun 	if (old_state != sk->sk_state)
4983163c507SUrsula Braun 		sk->sk_state_change(sk);
499b38d7324SUrsula Braun 	return rc;
500b38d7324SUrsula Braun }
50146c28dbdSUrsula Braun 
50246c28dbdSUrsula Braun /* Initialize close properties on connection establishment. */
smc_close_init(struct smc_sock * smc)50346c28dbdSUrsula Braun void smc_close_init(struct smc_sock *smc)
50446c28dbdSUrsula Braun {
50546c28dbdSUrsula Braun 	INIT_WORK(&smc->conn.close_work, smc_close_passive_work);
50646c28dbdSUrsula Braun }
507