162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Shared Memory Communications over RDMA (SMC-R) and RoCE
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Socket Closing - normal and abnormal
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright IBM Corp. 2016
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/workqueue.h>
1362306a36Sopenharmony_ci#include <linux/sched/signal.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <net/sock.h>
1662306a36Sopenharmony_ci#include <net/tcp.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "smc.h"
1962306a36Sopenharmony_ci#include "smc_tx.h"
2062306a36Sopenharmony_ci#include "smc_cdc.h"
2162306a36Sopenharmony_ci#include "smc_close.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* release the clcsock that is assigned to the smc_sock */
2462306a36Sopenharmony_civoid smc_clcsock_release(struct smc_sock *smc)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct socket *tcp;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (smc->listen_smc && current_work() != &smc->smc_listen_work)
2962306a36Sopenharmony_ci		cancel_work_sync(&smc->smc_listen_work);
3062306a36Sopenharmony_ci	mutex_lock(&smc->clcsock_release_lock);
3162306a36Sopenharmony_ci	if (smc->clcsock) {
3262306a36Sopenharmony_ci		tcp = smc->clcsock;
3362306a36Sopenharmony_ci		smc->clcsock = NULL;
3462306a36Sopenharmony_ci		sock_release(tcp);
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci	mutex_unlock(&smc->clcsock_release_lock);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void smc_close_cleanup_listen(struct sock *parent)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct sock *sk;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Close non-accepted connections */
4462306a36Sopenharmony_ci	while ((sk = smc_accept_dequeue(parent, NULL)))
4562306a36Sopenharmony_ci		smc_close_non_accepted(sk);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* wait for sndbuf data being transmitted */
4962306a36Sopenharmony_cistatic void smc_close_stream_wait(struct smc_sock *smc, long timeout)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	DEFINE_WAIT_FUNC(wait, woken_wake_function);
5262306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!timeout)
5562306a36Sopenharmony_ci		return;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!smc_tx_prepared_sends(&smc->conn))
5862306a36Sopenharmony_ci		return;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Send out corked data remaining in sndbuf */
6162306a36Sopenharmony_ci	smc_tx_pending(&smc->conn);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	smc->wait_close_tx_prepared = 1;
6462306a36Sopenharmony_ci	add_wait_queue(sk_sleep(sk), &wait);
6562306a36Sopenharmony_ci	while (!signal_pending(current) && timeout) {
6662306a36Sopenharmony_ci		int rc;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		rc = sk_wait_event(sk, &timeout,
6962306a36Sopenharmony_ci				   !smc_tx_prepared_sends(&smc->conn) ||
7062306a36Sopenharmony_ci				   READ_ONCE(sk->sk_err) == ECONNABORTED ||
7162306a36Sopenharmony_ci				   READ_ONCE(sk->sk_err) == ECONNRESET ||
7262306a36Sopenharmony_ci				   smc->conn.killed,
7362306a36Sopenharmony_ci				   &wait);
7462306a36Sopenharmony_ci		if (rc)
7562306a36Sopenharmony_ci			break;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci	remove_wait_queue(sk_sleep(sk), &wait);
7862306a36Sopenharmony_ci	smc->wait_close_tx_prepared = 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_civoid smc_close_wake_tx_prepared(struct smc_sock *smc)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	if (smc->wait_close_tx_prepared)
8462306a36Sopenharmony_ci		/* wake up socket closing */
8562306a36Sopenharmony_ci		smc->sk.sk_state_change(&smc->sk);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int smc_close_wr(struct smc_connection *conn)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	conn->local_tx_ctrl.conn_state_flags.peer_done_writing = 1;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return smc_cdc_get_slot_and_msg_send(conn);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int smc_close_final(struct smc_connection *conn)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	if (atomic_read(&conn->bytes_to_rcv))
9862306a36Sopenharmony_ci		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
9962306a36Sopenharmony_ci	else
10062306a36Sopenharmony_ci		conn->local_tx_ctrl.conn_state_flags.peer_conn_closed = 1;
10162306a36Sopenharmony_ci	if (conn->killed)
10262306a36Sopenharmony_ci		return -EPIPE;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return smc_cdc_get_slot_and_msg_send(conn);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciint smc_close_abort(struct smc_connection *conn)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return smc_cdc_get_slot_and_msg_send(conn);
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic void smc_close_cancel_work(struct smc_sock *smc)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	release_sock(sk);
11962306a36Sopenharmony_ci	if (cancel_work_sync(&smc->conn.close_work))
12062306a36Sopenharmony_ci		sock_put(sk);
12162306a36Sopenharmony_ci	cancel_delayed_work_sync(&smc->conn.tx_work);
12262306a36Sopenharmony_ci	lock_sock(sk);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* terminate smc socket abnormally - active abort
12662306a36Sopenharmony_ci * link group is terminated, i.e. RDMA communication no longer possible
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_civoid smc_close_active_abort(struct smc_sock *smc)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
13162306a36Sopenharmony_ci	bool release_clcsock = false;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (sk->sk_state != SMC_INIT && smc->clcsock && smc->clcsock->sk) {
13462306a36Sopenharmony_ci		sk->sk_err = ECONNABORTED;
13562306a36Sopenharmony_ci		if (smc->clcsock && smc->clcsock->sk)
13662306a36Sopenharmony_ci			tcp_abort(smc->clcsock->sk, ECONNABORTED);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	switch (sk->sk_state) {
13962306a36Sopenharmony_ci	case SMC_ACTIVE:
14062306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
14162306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
14262306a36Sopenharmony_ci		sk->sk_state = SMC_PEERABORTWAIT;
14362306a36Sopenharmony_ci		smc_close_cancel_work(smc);
14462306a36Sopenharmony_ci		if (sk->sk_state != SMC_PEERABORTWAIT)
14562306a36Sopenharmony_ci			break;
14662306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
14762306a36Sopenharmony_ci		sock_put(sk); /* (postponed) passive closing */
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
15062306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
15162306a36Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
15262306a36Sopenharmony_ci		sk->sk_state = SMC_PEERABORTWAIT;
15362306a36Sopenharmony_ci		smc_close_cancel_work(smc);
15462306a36Sopenharmony_ci		if (sk->sk_state != SMC_PEERABORTWAIT)
15562306a36Sopenharmony_ci			break;
15662306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
15762306a36Sopenharmony_ci		smc_conn_free(&smc->conn);
15862306a36Sopenharmony_ci		release_clcsock = true;
15962306a36Sopenharmony_ci		sock_put(sk); /* passive closing */
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case SMC_PROCESSABORT:
16262306a36Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
16362306a36Sopenharmony_ci		sk->sk_state = SMC_PEERABORTWAIT;
16462306a36Sopenharmony_ci		smc_close_cancel_work(smc);
16562306a36Sopenharmony_ci		if (sk->sk_state != SMC_PEERABORTWAIT)
16662306a36Sopenharmony_ci			break;
16762306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
16862306a36Sopenharmony_ci		smc_conn_free(&smc->conn);
16962306a36Sopenharmony_ci		release_clcsock = true;
17062306a36Sopenharmony_ci		break;
17162306a36Sopenharmony_ci	case SMC_INIT:
17262306a36Sopenharmony_ci	case SMC_PEERABORTWAIT:
17362306a36Sopenharmony_ci	case SMC_CLOSED:
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	smc_sock_set_flag(sk, SOCK_DEAD);
17862306a36Sopenharmony_ci	sk->sk_state_change(sk);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (release_clcsock) {
18162306a36Sopenharmony_ci		release_sock(sk);
18262306a36Sopenharmony_ci		smc_clcsock_release(smc);
18362306a36Sopenharmony_ci		lock_sock(sk);
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline bool smc_close_sent_any_close(struct smc_connection *conn)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	return conn->local_tx_ctrl.conn_state_flags.peer_conn_abort ||
19062306a36Sopenharmony_ci	       conn->local_tx_ctrl.conn_state_flags.peer_conn_closed;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciint smc_close_active(struct smc_sock *smc)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct smc_cdc_conn_state_flags *txflags =
19662306a36Sopenharmony_ci		&smc->conn.local_tx_ctrl.conn_state_flags;
19762306a36Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
19862306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
19962306a36Sopenharmony_ci	int old_state;
20062306a36Sopenharmony_ci	long timeout;
20162306a36Sopenharmony_ci	int rc = 0;
20262306a36Sopenharmony_ci	int rc1 = 0;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	timeout = current->flags & PF_EXITING ?
20562306a36Sopenharmony_ci		  0 : sock_flag(sk, SOCK_LINGER) ?
20662306a36Sopenharmony_ci		      sk->sk_lingertime : SMC_MAX_STREAM_WAIT_TIMEOUT;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	old_state = sk->sk_state;
20962306a36Sopenharmony_ciagain:
21062306a36Sopenharmony_ci	switch (sk->sk_state) {
21162306a36Sopenharmony_ci	case SMC_INIT:
21262306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci	case SMC_LISTEN:
21562306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
21662306a36Sopenharmony_ci		sk->sk_state_change(sk); /* wake up accept */
21762306a36Sopenharmony_ci		if (smc->clcsock && smc->clcsock->sk) {
21862306a36Sopenharmony_ci			write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
21962306a36Sopenharmony_ci			smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
22062306a36Sopenharmony_ci					       &smc->clcsk_data_ready);
22162306a36Sopenharmony_ci			smc->clcsock->sk->sk_user_data = NULL;
22262306a36Sopenharmony_ci			write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
22362306a36Sopenharmony_ci			rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci		smc_close_cleanup_listen(sk);
22662306a36Sopenharmony_ci		release_sock(sk);
22762306a36Sopenharmony_ci		flush_work(&smc->tcp_listen_work);
22862306a36Sopenharmony_ci		lock_sock(sk);
22962306a36Sopenharmony_ci		break;
23062306a36Sopenharmony_ci	case SMC_ACTIVE:
23162306a36Sopenharmony_ci		smc_close_stream_wait(smc, timeout);
23262306a36Sopenharmony_ci		release_sock(sk);
23362306a36Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
23462306a36Sopenharmony_ci		lock_sock(sk);
23562306a36Sopenharmony_ci		if (sk->sk_state == SMC_ACTIVE) {
23662306a36Sopenharmony_ci			/* send close request */
23762306a36Sopenharmony_ci			rc = smc_close_final(conn);
23862306a36Sopenharmony_ci			sk->sk_state = SMC_PEERCLOSEWAIT1;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci			/* actively shutdown clcsock before peer close it,
24162306a36Sopenharmony_ci			 * prevent peer from entering TIME_WAIT state.
24262306a36Sopenharmony_ci			 */
24362306a36Sopenharmony_ci			if (smc->clcsock && smc->clcsock->sk) {
24462306a36Sopenharmony_ci				rc1 = kernel_sock_shutdown(smc->clcsock,
24562306a36Sopenharmony_ci							   SHUT_RDWR);
24662306a36Sopenharmony_ci				rc = rc ? rc : rc1;
24762306a36Sopenharmony_ci			}
24862306a36Sopenharmony_ci		} else {
24962306a36Sopenharmony_ci			/* peer event has changed the state */
25062306a36Sopenharmony_ci			goto again;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci		break;
25362306a36Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
25462306a36Sopenharmony_ci		/* socket already shutdown wr or both (active close) */
25562306a36Sopenharmony_ci		if (txflags->peer_done_writing &&
25662306a36Sopenharmony_ci		    !smc_close_sent_any_close(conn)) {
25762306a36Sopenharmony_ci			/* just shutdown wr done, send close request */
25862306a36Sopenharmony_ci			rc = smc_close_final(conn);
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
26362306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
26462306a36Sopenharmony_ci		if (!smc_cdc_rxed_any_close(conn))
26562306a36Sopenharmony_ci			smc_close_stream_wait(smc, timeout);
26662306a36Sopenharmony_ci		release_sock(sk);
26762306a36Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
26862306a36Sopenharmony_ci		lock_sock(sk);
26962306a36Sopenharmony_ci		if (sk->sk_state != SMC_APPCLOSEWAIT1 &&
27062306a36Sopenharmony_ci		    sk->sk_state != SMC_APPCLOSEWAIT2)
27162306a36Sopenharmony_ci			goto again;
27262306a36Sopenharmony_ci		/* confirm close from peer */
27362306a36Sopenharmony_ci		rc = smc_close_final(conn);
27462306a36Sopenharmony_ci		if (smc_cdc_rxed_any_close(conn)) {
27562306a36Sopenharmony_ci			/* peer has closed the socket already */
27662306a36Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
27762306a36Sopenharmony_ci			sock_put(sk); /* postponed passive closing */
27862306a36Sopenharmony_ci		} else {
27962306a36Sopenharmony_ci			/* peer has just issued a shutdown write */
28062306a36Sopenharmony_ci			sk->sk_state = SMC_PEERFINCLOSEWAIT;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
28462306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
28562306a36Sopenharmony_ci		if (txflags->peer_done_writing &&
28662306a36Sopenharmony_ci		    !smc_close_sent_any_close(conn)) {
28762306a36Sopenharmony_ci			/* just shutdown wr done, send close request */
28862306a36Sopenharmony_ci			rc = smc_close_final(conn);
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci		/* peer sending PeerConnectionClosed will cause transition */
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
29362306a36Sopenharmony_ci		/* peer sending PeerConnectionClosed will cause transition */
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case SMC_PROCESSABORT:
29662306a36Sopenharmony_ci		rc = smc_close_abort(conn);
29762306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci	case SMC_PEERABORTWAIT:
30062306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	case SMC_CLOSED:
30362306a36Sopenharmony_ci		/* nothing to do, add tracing in future patch */
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (old_state != sk->sk_state)
30862306a36Sopenharmony_ci		sk->sk_state_change(sk);
30962306a36Sopenharmony_ci	return rc;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void smc_close_passive_abort_received(struct smc_sock *smc)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	struct smc_cdc_conn_state_flags *txflags =
31562306a36Sopenharmony_ci		&smc->conn.local_tx_ctrl.conn_state_flags;
31662306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	switch (sk->sk_state) {
31962306a36Sopenharmony_ci	case SMC_INIT:
32062306a36Sopenharmony_ci	case SMC_ACTIVE:
32162306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
32262306a36Sopenharmony_ci		sk->sk_state = SMC_PROCESSABORT;
32362306a36Sopenharmony_ci		sock_put(sk); /* passive closing */
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
32662306a36Sopenharmony_ci		sk->sk_state = SMC_PROCESSABORT;
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
32962306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
33062306a36Sopenharmony_ci		if (txflags->peer_done_writing &&
33162306a36Sopenharmony_ci		    !smc_close_sent_any_close(&smc->conn))
33262306a36Sopenharmony_ci			/* just shutdown, but not yet closed locally */
33362306a36Sopenharmony_ci			sk->sk_state = SMC_PROCESSABORT;
33462306a36Sopenharmony_ci		else
33562306a36Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
33662306a36Sopenharmony_ci		sock_put(sk); /* passive closing */
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
33962306a36Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
34062306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
34162306a36Sopenharmony_ci		sock_put(sk); /* passive closing */
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	case SMC_PEERABORTWAIT:
34462306a36Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
34562306a36Sopenharmony_ci		break;
34662306a36Sopenharmony_ci	case SMC_PROCESSABORT:
34762306a36Sopenharmony_ci	/* nothing to do, add tracing in future patch */
34862306a36Sopenharmony_ci		break;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/* Either some kind of closing has been received: peer_conn_closed,
35362306a36Sopenharmony_ci * peer_conn_abort, or peer_done_writing
35462306a36Sopenharmony_ci * or the link group of the connection terminates abnormally.
35562306a36Sopenharmony_ci */
35662306a36Sopenharmony_cistatic void smc_close_passive_work(struct work_struct *work)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct smc_connection *conn = container_of(work,
35962306a36Sopenharmony_ci						   struct smc_connection,
36062306a36Sopenharmony_ci						   close_work);
36162306a36Sopenharmony_ci	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
36262306a36Sopenharmony_ci	struct smc_cdc_conn_state_flags *rxflags;
36362306a36Sopenharmony_ci	bool release_clcsock = false;
36462306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
36562306a36Sopenharmony_ci	int old_state;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	lock_sock(sk);
36862306a36Sopenharmony_ci	old_state = sk->sk_state;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	rxflags = &conn->local_rx_ctrl.conn_state_flags;
37162306a36Sopenharmony_ci	if (rxflags->peer_conn_abort) {
37262306a36Sopenharmony_ci		/* peer has not received all data */
37362306a36Sopenharmony_ci		smc_close_passive_abort_received(smc);
37462306a36Sopenharmony_ci		release_sock(sk);
37562306a36Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
37662306a36Sopenharmony_ci		lock_sock(sk);
37762306a36Sopenharmony_ci		goto wakeup;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	switch (sk->sk_state) {
38162306a36Sopenharmony_ci	case SMC_INIT:
38262306a36Sopenharmony_ci		sk->sk_state = SMC_APPCLOSEWAIT1;
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	case SMC_ACTIVE:
38562306a36Sopenharmony_ci		sk->sk_state = SMC_APPCLOSEWAIT1;
38662306a36Sopenharmony_ci		/* postpone sock_put() for passive closing to cover
38762306a36Sopenharmony_ci		 * received SEND_SHUTDOWN as well
38862306a36Sopenharmony_ci		 */
38962306a36Sopenharmony_ci		break;
39062306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
39162306a36Sopenharmony_ci		if (rxflags->peer_done_writing)
39262306a36Sopenharmony_ci			sk->sk_state = SMC_PEERCLOSEWAIT2;
39362306a36Sopenharmony_ci		fallthrough;
39462306a36Sopenharmony_ci		/* to check for closing */
39562306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
39662306a36Sopenharmony_ci		if (!smc_cdc_rxed_any_close(conn))
39762306a36Sopenharmony_ci			break;
39862306a36Sopenharmony_ci		if (sock_flag(sk, SOCK_DEAD) &&
39962306a36Sopenharmony_ci		    smc_close_sent_any_close(conn)) {
40062306a36Sopenharmony_ci			/* smc_release has already been called locally */
40162306a36Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
40262306a36Sopenharmony_ci		} else {
40362306a36Sopenharmony_ci			/* just shutdown, but not yet closed locally */
40462306a36Sopenharmony_ci			sk->sk_state = SMC_APPFINCLOSEWAIT;
40562306a36Sopenharmony_ci		}
40662306a36Sopenharmony_ci		sock_put(sk); /* passive closing */
40762306a36Sopenharmony_ci		break;
40862306a36Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
40962306a36Sopenharmony_ci		if (smc_cdc_rxed_any_close(conn)) {
41062306a36Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
41162306a36Sopenharmony_ci			sock_put(sk); /* passive closing */
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci		break;
41462306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
41562306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
41662306a36Sopenharmony_ci		/* postpone sock_put() for passive closing to cover
41762306a36Sopenharmony_ci		 * received SEND_SHUTDOWN as well
41862306a36Sopenharmony_ci		 */
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
42162306a36Sopenharmony_ci	case SMC_PEERABORTWAIT:
42262306a36Sopenharmony_ci	case SMC_PROCESSABORT:
42362306a36Sopenharmony_ci	case SMC_CLOSED:
42462306a36Sopenharmony_ci		/* nothing to do, add tracing in future patch */
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciwakeup:
42962306a36Sopenharmony_ci	sk->sk_data_ready(sk); /* wakeup blocked rcvbuf consumers */
43062306a36Sopenharmony_ci	sk->sk_write_space(sk); /* wakeup blocked sndbuf producers */
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (old_state != sk->sk_state) {
43362306a36Sopenharmony_ci		sk->sk_state_change(sk);
43462306a36Sopenharmony_ci		if ((sk->sk_state == SMC_CLOSED) &&
43562306a36Sopenharmony_ci		    (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) {
43662306a36Sopenharmony_ci			smc_conn_free(conn);
43762306a36Sopenharmony_ci			if (smc->clcsock)
43862306a36Sopenharmony_ci				release_clcsock = true;
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	release_sock(sk);
44262306a36Sopenharmony_ci	if (release_clcsock)
44362306a36Sopenharmony_ci		smc_clcsock_release(smc);
44462306a36Sopenharmony_ci	sock_put(sk); /* sock_hold done by schedulers of close_work */
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ciint smc_close_shutdown_write(struct smc_sock *smc)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
45062306a36Sopenharmony_ci	struct sock *sk = &smc->sk;
45162306a36Sopenharmony_ci	int old_state;
45262306a36Sopenharmony_ci	long timeout;
45362306a36Sopenharmony_ci	int rc = 0;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	timeout = current->flags & PF_EXITING ?
45662306a36Sopenharmony_ci		  0 : sock_flag(sk, SOCK_LINGER) ?
45762306a36Sopenharmony_ci		      sk->sk_lingertime : SMC_MAX_STREAM_WAIT_TIMEOUT;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	old_state = sk->sk_state;
46062306a36Sopenharmony_ciagain:
46162306a36Sopenharmony_ci	switch (sk->sk_state) {
46262306a36Sopenharmony_ci	case SMC_ACTIVE:
46362306a36Sopenharmony_ci		smc_close_stream_wait(smc, timeout);
46462306a36Sopenharmony_ci		release_sock(sk);
46562306a36Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
46662306a36Sopenharmony_ci		lock_sock(sk);
46762306a36Sopenharmony_ci		if (sk->sk_state != SMC_ACTIVE)
46862306a36Sopenharmony_ci			goto again;
46962306a36Sopenharmony_ci		/* send close wr request */
47062306a36Sopenharmony_ci		rc = smc_close_wr(conn);
47162306a36Sopenharmony_ci		sk->sk_state = SMC_PEERCLOSEWAIT1;
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
47462306a36Sopenharmony_ci		/* passive close */
47562306a36Sopenharmony_ci		if (!smc_cdc_rxed_any_close(conn))
47662306a36Sopenharmony_ci			smc_close_stream_wait(smc, timeout);
47762306a36Sopenharmony_ci		release_sock(sk);
47862306a36Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
47962306a36Sopenharmony_ci		lock_sock(sk);
48062306a36Sopenharmony_ci		if (sk->sk_state != SMC_APPCLOSEWAIT1)
48162306a36Sopenharmony_ci			goto again;
48262306a36Sopenharmony_ci		/* confirm close from peer */
48362306a36Sopenharmony_ci		rc = smc_close_wr(conn);
48462306a36Sopenharmony_ci		sk->sk_state = SMC_APPCLOSEWAIT2;
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
48762306a36Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
48862306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
48962306a36Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
49062306a36Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
49162306a36Sopenharmony_ci	case SMC_PROCESSABORT:
49262306a36Sopenharmony_ci	case SMC_PEERABORTWAIT:
49362306a36Sopenharmony_ci		/* nothing to do, add tracing in future patch */
49462306a36Sopenharmony_ci		break;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (old_state != sk->sk_state)
49862306a36Sopenharmony_ci		sk->sk_state_change(sk);
49962306a36Sopenharmony_ci	return rc;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci/* Initialize close properties on connection establishment. */
50362306a36Sopenharmony_civoid smc_close_init(struct smc_sock *smc)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	INIT_WORK(&smc->conn.close_work, smc_close_passive_work);
50662306a36Sopenharmony_ci}
507