18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Shared Memory Communications over RDMA (SMC-R) and RoCE
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Socket Closing - normal and abnormal
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2016
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
138c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <net/sock.h>
168c2ecf20Sopenharmony_ci#include <net/tcp.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "smc.h"
198c2ecf20Sopenharmony_ci#include "smc_tx.h"
208c2ecf20Sopenharmony_ci#include "smc_cdc.h"
218c2ecf20Sopenharmony_ci#include "smc_close.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* release the clcsock that is assigned to the smc_sock */
248c2ecf20Sopenharmony_civoid smc_clcsock_release(struct smc_sock *smc)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct socket *tcp;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (smc->listen_smc && current_work() != &smc->smc_listen_work)
298c2ecf20Sopenharmony_ci		cancel_work_sync(&smc->smc_listen_work);
308c2ecf20Sopenharmony_ci	mutex_lock(&smc->clcsock_release_lock);
318c2ecf20Sopenharmony_ci	if (smc->clcsock) {
328c2ecf20Sopenharmony_ci		tcp = smc->clcsock;
338c2ecf20Sopenharmony_ci		smc->clcsock = NULL;
348c2ecf20Sopenharmony_ci		sock_release(tcp);
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	mutex_unlock(&smc->clcsock_release_lock);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void smc_close_cleanup_listen(struct sock *parent)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct sock *sk;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Close non-accepted connections */
448c2ecf20Sopenharmony_ci	while ((sk = smc_accept_dequeue(parent, NULL)))
458c2ecf20Sopenharmony_ci		smc_close_non_accepted(sk);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* wait for sndbuf data being transmitted */
498c2ecf20Sopenharmony_cistatic void smc_close_stream_wait(struct smc_sock *smc, long timeout)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	DEFINE_WAIT_FUNC(wait, woken_wake_function);
528c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!timeout)
558c2ecf20Sopenharmony_ci		return;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (!smc_tx_prepared_sends(&smc->conn))
588c2ecf20Sopenharmony_ci		return;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	smc->wait_close_tx_prepared = 1;
618c2ecf20Sopenharmony_ci	add_wait_queue(sk_sleep(sk), &wait);
628c2ecf20Sopenharmony_ci	while (!signal_pending(current) && timeout) {
638c2ecf20Sopenharmony_ci		int rc;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		rc = sk_wait_event(sk, &timeout,
668c2ecf20Sopenharmony_ci				   !smc_tx_prepared_sends(&smc->conn) ||
678c2ecf20Sopenharmony_ci				   READ_ONCE(sk->sk_err) == ECONNABORTED ||
688c2ecf20Sopenharmony_ci				   READ_ONCE(sk->sk_err) == ECONNRESET ||
698c2ecf20Sopenharmony_ci				   smc->conn.killed,
708c2ecf20Sopenharmony_ci				   &wait);
718c2ecf20Sopenharmony_ci		if (rc)
728c2ecf20Sopenharmony_ci			break;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	remove_wait_queue(sk_sleep(sk), &wait);
758c2ecf20Sopenharmony_ci	smc->wait_close_tx_prepared = 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_civoid smc_close_wake_tx_prepared(struct smc_sock *smc)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	if (smc->wait_close_tx_prepared)
818c2ecf20Sopenharmony_ci		/* wake up socket closing */
828c2ecf20Sopenharmony_ci		smc->sk.sk_state_change(&smc->sk);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int smc_close_wr(struct smc_connection *conn)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	conn->local_tx_ctrl.conn_state_flags.peer_done_writing = 1;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return smc_cdc_get_slot_and_msg_send(conn);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int smc_close_final(struct smc_connection *conn)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	if (atomic_read(&conn->bytes_to_rcv))
958c2ecf20Sopenharmony_ci		conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
968c2ecf20Sopenharmony_ci	else
978c2ecf20Sopenharmony_ci		conn->local_tx_ctrl.conn_state_flags.peer_conn_closed = 1;
988c2ecf20Sopenharmony_ci	if (conn->killed)
998c2ecf20Sopenharmony_ci		return -EPIPE;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return smc_cdc_get_slot_and_msg_send(conn);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciint smc_close_abort(struct smc_connection *conn)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return smc_cdc_get_slot_and_msg_send(conn);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void smc_close_cancel_work(struct smc_sock *smc)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	release_sock(sk);
1168c2ecf20Sopenharmony_ci	if (cancel_work_sync(&smc->conn.close_work))
1178c2ecf20Sopenharmony_ci		sock_put(sk);
1188c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&smc->conn.tx_work);
1198c2ecf20Sopenharmony_ci	lock_sock(sk);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* terminate smc socket abnormally - active abort
1238c2ecf20Sopenharmony_ci * link group is terminated, i.e. RDMA communication no longer possible
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_civoid smc_close_active_abort(struct smc_sock *smc)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
1288c2ecf20Sopenharmony_ci	bool release_clcsock = false;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (sk->sk_state != SMC_INIT && smc->clcsock && smc->clcsock->sk) {
1318c2ecf20Sopenharmony_ci		sk->sk_err = ECONNABORTED;
1328c2ecf20Sopenharmony_ci		if (smc->clcsock && smc->clcsock->sk)
1338c2ecf20Sopenharmony_ci			tcp_abort(smc->clcsock->sk, ECONNABORTED);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
1368c2ecf20Sopenharmony_ci	case SMC_ACTIVE:
1378c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
1388c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
1398c2ecf20Sopenharmony_ci		sk->sk_state = SMC_PEERABORTWAIT;
1408c2ecf20Sopenharmony_ci		smc_close_cancel_work(smc);
1418c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_PEERABORTWAIT)
1428c2ecf20Sopenharmony_ci			break;
1438c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
1448c2ecf20Sopenharmony_ci		sock_put(sk); /* (postponed) passive closing */
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
1478c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
1488c2ecf20Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
1498c2ecf20Sopenharmony_ci		sk->sk_state = SMC_PEERABORTWAIT;
1508c2ecf20Sopenharmony_ci		smc_close_cancel_work(smc);
1518c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_PEERABORTWAIT)
1528c2ecf20Sopenharmony_ci			break;
1538c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
1548c2ecf20Sopenharmony_ci		smc_conn_free(&smc->conn);
1558c2ecf20Sopenharmony_ci		release_clcsock = true;
1568c2ecf20Sopenharmony_ci		sock_put(sk); /* passive closing */
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci	case SMC_PROCESSABORT:
1598c2ecf20Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
1608c2ecf20Sopenharmony_ci		sk->sk_state = SMC_PEERABORTWAIT;
1618c2ecf20Sopenharmony_ci		smc_close_cancel_work(smc);
1628c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_PEERABORTWAIT)
1638c2ecf20Sopenharmony_ci			break;
1648c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
1658c2ecf20Sopenharmony_ci		smc_conn_free(&smc->conn);
1668c2ecf20Sopenharmony_ci		release_clcsock = true;
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case SMC_INIT:
1698c2ecf20Sopenharmony_ci	case SMC_PEERABORTWAIT:
1708c2ecf20Sopenharmony_ci	case SMC_CLOSED:
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	smc_sock_set_flag(sk, SOCK_DEAD);
1758c2ecf20Sopenharmony_ci	sk->sk_state_change(sk);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (release_clcsock) {
1788c2ecf20Sopenharmony_ci		release_sock(sk);
1798c2ecf20Sopenharmony_ci		smc_clcsock_release(smc);
1808c2ecf20Sopenharmony_ci		lock_sock(sk);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic inline bool smc_close_sent_any_close(struct smc_connection *conn)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	return conn->local_tx_ctrl.conn_state_flags.peer_conn_abort ||
1878c2ecf20Sopenharmony_ci	       conn->local_tx_ctrl.conn_state_flags.peer_conn_closed;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciint smc_close_active(struct smc_sock *smc)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct smc_cdc_conn_state_flags *txflags =
1938c2ecf20Sopenharmony_ci		&smc->conn.local_tx_ctrl.conn_state_flags;
1948c2ecf20Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
1958c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
1968c2ecf20Sopenharmony_ci	int old_state;
1978c2ecf20Sopenharmony_ci	long timeout;
1988c2ecf20Sopenharmony_ci	int rc = 0;
1998c2ecf20Sopenharmony_ci	int rc1 = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	timeout = current->flags & PF_EXITING ?
2028c2ecf20Sopenharmony_ci		  0 : sock_flag(sk, SOCK_LINGER) ?
2038c2ecf20Sopenharmony_ci		      sk->sk_lingertime : SMC_MAX_STREAM_WAIT_TIMEOUT;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	old_state = sk->sk_state;
2068c2ecf20Sopenharmony_ciagain:
2078c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
2088c2ecf20Sopenharmony_ci	case SMC_INIT:
2098c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
2108c2ecf20Sopenharmony_ci		break;
2118c2ecf20Sopenharmony_ci	case SMC_LISTEN:
2128c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
2138c2ecf20Sopenharmony_ci		sk->sk_state_change(sk); /* wake up accept */
2148c2ecf20Sopenharmony_ci		if (smc->clcsock && smc->clcsock->sk) {
2158c2ecf20Sopenharmony_ci			smc->clcsock->sk->sk_data_ready = smc->clcsk_data_ready;
2168c2ecf20Sopenharmony_ci			smc->clcsock->sk->sk_user_data = NULL;
2178c2ecf20Sopenharmony_ci			rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci		smc_close_cleanup_listen(sk);
2208c2ecf20Sopenharmony_ci		release_sock(sk);
2218c2ecf20Sopenharmony_ci		flush_work(&smc->tcp_listen_work);
2228c2ecf20Sopenharmony_ci		lock_sock(sk);
2238c2ecf20Sopenharmony_ci		break;
2248c2ecf20Sopenharmony_ci	case SMC_ACTIVE:
2258c2ecf20Sopenharmony_ci		smc_close_stream_wait(smc, timeout);
2268c2ecf20Sopenharmony_ci		release_sock(sk);
2278c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
2288c2ecf20Sopenharmony_ci		lock_sock(sk);
2298c2ecf20Sopenharmony_ci		if (sk->sk_state == SMC_ACTIVE) {
2308c2ecf20Sopenharmony_ci			/* send close request */
2318c2ecf20Sopenharmony_ci			rc = smc_close_final(conn);
2328c2ecf20Sopenharmony_ci			sk->sk_state = SMC_PEERCLOSEWAIT1;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci			/* actively shutdown clcsock before peer close it,
2358c2ecf20Sopenharmony_ci			 * prevent peer from entering TIME_WAIT state.
2368c2ecf20Sopenharmony_ci			 */
2378c2ecf20Sopenharmony_ci			if (smc->clcsock && smc->clcsock->sk) {
2388c2ecf20Sopenharmony_ci				rc1 = kernel_sock_shutdown(smc->clcsock,
2398c2ecf20Sopenharmony_ci							   SHUT_RDWR);
2408c2ecf20Sopenharmony_ci				rc = rc ? rc : rc1;
2418c2ecf20Sopenharmony_ci			}
2428c2ecf20Sopenharmony_ci		} else {
2438c2ecf20Sopenharmony_ci			/* peer event has changed the state */
2448c2ecf20Sopenharmony_ci			goto again;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
2488c2ecf20Sopenharmony_ci		/* socket already shutdown wr or both (active close) */
2498c2ecf20Sopenharmony_ci		if (txflags->peer_done_writing &&
2508c2ecf20Sopenharmony_ci		    !smc_close_sent_any_close(conn)) {
2518c2ecf20Sopenharmony_ci			/* just shutdown wr done, send close request */
2528c2ecf20Sopenharmony_ci			rc = smc_close_final(conn);
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
2578c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
2588c2ecf20Sopenharmony_ci		if (!smc_cdc_rxed_any_close(conn))
2598c2ecf20Sopenharmony_ci			smc_close_stream_wait(smc, timeout);
2608c2ecf20Sopenharmony_ci		release_sock(sk);
2618c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
2628c2ecf20Sopenharmony_ci		lock_sock(sk);
2638c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_APPCLOSEWAIT1 &&
2648c2ecf20Sopenharmony_ci		    sk->sk_state != SMC_APPCLOSEWAIT2)
2658c2ecf20Sopenharmony_ci			goto again;
2668c2ecf20Sopenharmony_ci		/* confirm close from peer */
2678c2ecf20Sopenharmony_ci		rc = smc_close_final(conn);
2688c2ecf20Sopenharmony_ci		if (smc_cdc_rxed_any_close(conn)) {
2698c2ecf20Sopenharmony_ci			/* peer has closed the socket already */
2708c2ecf20Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
2718c2ecf20Sopenharmony_ci			sock_put(sk); /* postponed passive closing */
2728c2ecf20Sopenharmony_ci		} else {
2738c2ecf20Sopenharmony_ci			/* peer has just issued a shutdown write */
2748c2ecf20Sopenharmony_ci			sk->sk_state = SMC_PEERFINCLOSEWAIT;
2758c2ecf20Sopenharmony_ci		}
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
2788c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
2798c2ecf20Sopenharmony_ci		if (txflags->peer_done_writing &&
2808c2ecf20Sopenharmony_ci		    !smc_close_sent_any_close(conn)) {
2818c2ecf20Sopenharmony_ci			/* just shutdown wr done, send close request */
2828c2ecf20Sopenharmony_ci			rc = smc_close_final(conn);
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci		/* peer sending PeerConnectionClosed will cause transition */
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
2878c2ecf20Sopenharmony_ci		/* peer sending PeerConnectionClosed will cause transition */
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	case SMC_PROCESSABORT:
2908c2ecf20Sopenharmony_ci		rc = smc_close_abort(conn);
2918c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
2928c2ecf20Sopenharmony_ci		break;
2938c2ecf20Sopenharmony_ci	case SMC_PEERABORTWAIT:
2948c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
2958c2ecf20Sopenharmony_ci		break;
2968c2ecf20Sopenharmony_ci	case SMC_CLOSED:
2978c2ecf20Sopenharmony_ci		/* nothing to do, add tracing in future patch */
2988c2ecf20Sopenharmony_ci		break;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (old_state != sk->sk_state)
3028c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
3038c2ecf20Sopenharmony_ci	return rc;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void smc_close_passive_abort_received(struct smc_sock *smc)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct smc_cdc_conn_state_flags *txflags =
3098c2ecf20Sopenharmony_ci		&smc->conn.local_tx_ctrl.conn_state_flags;
3108c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
3138c2ecf20Sopenharmony_ci	case SMC_INIT:
3148c2ecf20Sopenharmony_ci	case SMC_ACTIVE:
3158c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
3168c2ecf20Sopenharmony_ci		sk->sk_state = SMC_PROCESSABORT;
3178c2ecf20Sopenharmony_ci		sock_put(sk); /* passive closing */
3188c2ecf20Sopenharmony_ci		break;
3198c2ecf20Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
3208c2ecf20Sopenharmony_ci		sk->sk_state = SMC_PROCESSABORT;
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
3238c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
3248c2ecf20Sopenharmony_ci		if (txflags->peer_done_writing &&
3258c2ecf20Sopenharmony_ci		    !smc_close_sent_any_close(&smc->conn))
3268c2ecf20Sopenharmony_ci			/* just shutdown, but not yet closed locally */
3278c2ecf20Sopenharmony_ci			sk->sk_state = SMC_PROCESSABORT;
3288c2ecf20Sopenharmony_ci		else
3298c2ecf20Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
3308c2ecf20Sopenharmony_ci		sock_put(sk); /* passive closing */
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
3338c2ecf20Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
3348c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
3358c2ecf20Sopenharmony_ci		sock_put(sk); /* passive closing */
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case SMC_PEERABORTWAIT:
3388c2ecf20Sopenharmony_ci		sk->sk_state = SMC_CLOSED;
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	case SMC_PROCESSABORT:
3418c2ecf20Sopenharmony_ci	/* nothing to do, add tracing in future patch */
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/* Either some kind of closing has been received: peer_conn_closed,
3478c2ecf20Sopenharmony_ci * peer_conn_abort, or peer_done_writing
3488c2ecf20Sopenharmony_ci * or the link group of the connection terminates abnormally.
3498c2ecf20Sopenharmony_ci */
3508c2ecf20Sopenharmony_cistatic void smc_close_passive_work(struct work_struct *work)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct smc_connection *conn = container_of(work,
3538c2ecf20Sopenharmony_ci						   struct smc_connection,
3548c2ecf20Sopenharmony_ci						   close_work);
3558c2ecf20Sopenharmony_ci	struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
3568c2ecf20Sopenharmony_ci	struct smc_cdc_conn_state_flags *rxflags;
3578c2ecf20Sopenharmony_ci	bool release_clcsock = false;
3588c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
3598c2ecf20Sopenharmony_ci	int old_state;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	lock_sock(sk);
3628c2ecf20Sopenharmony_ci	old_state = sk->sk_state;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	rxflags = &conn->local_rx_ctrl.conn_state_flags;
3658c2ecf20Sopenharmony_ci	if (rxflags->peer_conn_abort) {
3668c2ecf20Sopenharmony_ci		/* peer has not received all data */
3678c2ecf20Sopenharmony_ci		smc_close_passive_abort_received(smc);
3688c2ecf20Sopenharmony_ci		release_sock(&smc->sk);
3698c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
3708c2ecf20Sopenharmony_ci		lock_sock(&smc->sk);
3718c2ecf20Sopenharmony_ci		goto wakeup;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
3758c2ecf20Sopenharmony_ci	case SMC_INIT:
3768c2ecf20Sopenharmony_ci		sk->sk_state = SMC_APPCLOSEWAIT1;
3778c2ecf20Sopenharmony_ci		break;
3788c2ecf20Sopenharmony_ci	case SMC_ACTIVE:
3798c2ecf20Sopenharmony_ci		sk->sk_state = SMC_APPCLOSEWAIT1;
3808c2ecf20Sopenharmony_ci		/* postpone sock_put() for passive closing to cover
3818c2ecf20Sopenharmony_ci		 * received SEND_SHUTDOWN as well
3828c2ecf20Sopenharmony_ci		 */
3838c2ecf20Sopenharmony_ci		break;
3848c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
3858c2ecf20Sopenharmony_ci		if (rxflags->peer_done_writing)
3868c2ecf20Sopenharmony_ci			sk->sk_state = SMC_PEERCLOSEWAIT2;
3878c2ecf20Sopenharmony_ci		fallthrough;
3888c2ecf20Sopenharmony_ci		/* to check for closing */
3898c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
3908c2ecf20Sopenharmony_ci		if (!smc_cdc_rxed_any_close(conn))
3918c2ecf20Sopenharmony_ci			break;
3928c2ecf20Sopenharmony_ci		if (sock_flag(sk, SOCK_DEAD) &&
3938c2ecf20Sopenharmony_ci		    smc_close_sent_any_close(conn)) {
3948c2ecf20Sopenharmony_ci			/* smc_release has already been called locally */
3958c2ecf20Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
3968c2ecf20Sopenharmony_ci		} else {
3978c2ecf20Sopenharmony_ci			/* just shutdown, but not yet closed locally */
3988c2ecf20Sopenharmony_ci			sk->sk_state = SMC_APPFINCLOSEWAIT;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci		sock_put(sk); /* passive closing */
4018c2ecf20Sopenharmony_ci		break;
4028c2ecf20Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
4038c2ecf20Sopenharmony_ci		if (smc_cdc_rxed_any_close(conn)) {
4048c2ecf20Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
4058c2ecf20Sopenharmony_ci			sock_put(sk); /* passive closing */
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci		break;
4088c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
4098c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
4108c2ecf20Sopenharmony_ci		/* postpone sock_put() for passive closing to cover
4118c2ecf20Sopenharmony_ci		 * received SEND_SHUTDOWN as well
4128c2ecf20Sopenharmony_ci		 */
4138c2ecf20Sopenharmony_ci		break;
4148c2ecf20Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
4158c2ecf20Sopenharmony_ci	case SMC_PEERABORTWAIT:
4168c2ecf20Sopenharmony_ci	case SMC_PROCESSABORT:
4178c2ecf20Sopenharmony_ci	case SMC_CLOSED:
4188c2ecf20Sopenharmony_ci		/* nothing to do, add tracing in future patch */
4198c2ecf20Sopenharmony_ci		break;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ciwakeup:
4238c2ecf20Sopenharmony_ci	sk->sk_data_ready(sk); /* wakeup blocked rcvbuf consumers */
4248c2ecf20Sopenharmony_ci	sk->sk_write_space(sk); /* wakeup blocked sndbuf producers */
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (old_state != sk->sk_state) {
4278c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
4288c2ecf20Sopenharmony_ci		if ((sk->sk_state == SMC_CLOSED) &&
4298c2ecf20Sopenharmony_ci		    (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) {
4308c2ecf20Sopenharmony_ci			smc_conn_free(conn);
4318c2ecf20Sopenharmony_ci			if (smc->clcsock)
4328c2ecf20Sopenharmony_ci				release_clcsock = true;
4338c2ecf20Sopenharmony_ci		}
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci	release_sock(sk);
4368c2ecf20Sopenharmony_ci	if (release_clcsock)
4378c2ecf20Sopenharmony_ci		smc_clcsock_release(smc);
4388c2ecf20Sopenharmony_ci	sock_put(sk); /* sock_hold done by schedulers of close_work */
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ciint smc_close_shutdown_write(struct smc_sock *smc)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	struct smc_connection *conn = &smc->conn;
4448c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
4458c2ecf20Sopenharmony_ci	int old_state;
4468c2ecf20Sopenharmony_ci	long timeout;
4478c2ecf20Sopenharmony_ci	int rc = 0;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	timeout = current->flags & PF_EXITING ?
4508c2ecf20Sopenharmony_ci		  0 : sock_flag(sk, SOCK_LINGER) ?
4518c2ecf20Sopenharmony_ci		      sk->sk_lingertime : SMC_MAX_STREAM_WAIT_TIMEOUT;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	old_state = sk->sk_state;
4548c2ecf20Sopenharmony_ciagain:
4558c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
4568c2ecf20Sopenharmony_ci	case SMC_ACTIVE:
4578c2ecf20Sopenharmony_ci		smc_close_stream_wait(smc, timeout);
4588c2ecf20Sopenharmony_ci		release_sock(sk);
4598c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
4608c2ecf20Sopenharmony_ci		lock_sock(sk);
4618c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_ACTIVE)
4628c2ecf20Sopenharmony_ci			goto again;
4638c2ecf20Sopenharmony_ci		/* send close wr request */
4648c2ecf20Sopenharmony_ci		rc = smc_close_wr(conn);
4658c2ecf20Sopenharmony_ci		sk->sk_state = SMC_PEERCLOSEWAIT1;
4668c2ecf20Sopenharmony_ci		break;
4678c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT1:
4688c2ecf20Sopenharmony_ci		/* passive close */
4698c2ecf20Sopenharmony_ci		if (!smc_cdc_rxed_any_close(conn))
4708c2ecf20Sopenharmony_ci			smc_close_stream_wait(smc, timeout);
4718c2ecf20Sopenharmony_ci		release_sock(sk);
4728c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&conn->tx_work);
4738c2ecf20Sopenharmony_ci		lock_sock(sk);
4748c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_APPCLOSEWAIT1)
4758c2ecf20Sopenharmony_ci			goto again;
4768c2ecf20Sopenharmony_ci		/* confirm close from peer */
4778c2ecf20Sopenharmony_ci		rc = smc_close_wr(conn);
4788c2ecf20Sopenharmony_ci		sk->sk_state = SMC_APPCLOSEWAIT2;
4798c2ecf20Sopenharmony_ci		break;
4808c2ecf20Sopenharmony_ci	case SMC_APPCLOSEWAIT2:
4818c2ecf20Sopenharmony_ci	case SMC_PEERFINCLOSEWAIT:
4828c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT1:
4838c2ecf20Sopenharmony_ci	case SMC_PEERCLOSEWAIT2:
4848c2ecf20Sopenharmony_ci	case SMC_APPFINCLOSEWAIT:
4858c2ecf20Sopenharmony_ci	case SMC_PROCESSABORT:
4868c2ecf20Sopenharmony_ci	case SMC_PEERABORTWAIT:
4878c2ecf20Sopenharmony_ci		/* nothing to do, add tracing in future patch */
4888c2ecf20Sopenharmony_ci		break;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (old_state != sk->sk_state)
4928c2ecf20Sopenharmony_ci		sk->sk_state_change(sk);
4938c2ecf20Sopenharmony_ci	return rc;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/* Initialize close properties on connection establishment. */
4978c2ecf20Sopenharmony_civoid smc_close_init(struct smc_sock *smc)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	INIT_WORK(&smc->conn.close_work, smc_close_passive_work);
5008c2ecf20Sopenharmony_ci}
501