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