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 * Connection Data Control (CDC) 662306a36Sopenharmony_ci * handles flow control 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright IBM Corp. 2016 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "smc.h" 1662306a36Sopenharmony_ci#include "smc_wr.h" 1762306a36Sopenharmony_ci#include "smc_cdc.h" 1862306a36Sopenharmony_ci#include "smc_tx.h" 1962306a36Sopenharmony_ci#include "smc_rx.h" 2062306a36Sopenharmony_ci#include "smc_close.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/********************************** send *************************************/ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* handler for send/transmission completion of a CDC msg */ 2562306a36Sopenharmony_cistatic void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd, 2662306a36Sopenharmony_ci struct smc_link *link, 2762306a36Sopenharmony_ci enum ib_wc_status wc_status) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct smc_cdc_tx_pend *cdcpend = (struct smc_cdc_tx_pend *)pnd_snd; 3062306a36Sopenharmony_ci struct smc_connection *conn = cdcpend->conn; 3162306a36Sopenharmony_ci struct smc_buf_desc *sndbuf_desc; 3262306a36Sopenharmony_ci struct smc_sock *smc; 3362306a36Sopenharmony_ci int diff; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci sndbuf_desc = conn->sndbuf_desc; 3662306a36Sopenharmony_ci smc = container_of(conn, struct smc_sock, conn); 3762306a36Sopenharmony_ci bh_lock_sock(&smc->sk); 3862306a36Sopenharmony_ci if (!wc_status && sndbuf_desc) { 3962306a36Sopenharmony_ci diff = smc_curs_diff(sndbuf_desc->len, 4062306a36Sopenharmony_ci &cdcpend->conn->tx_curs_fin, 4162306a36Sopenharmony_ci &cdcpend->cursor); 4262306a36Sopenharmony_ci /* sndbuf_space is decreased in smc_sendmsg */ 4362306a36Sopenharmony_ci smp_mb__before_atomic(); 4462306a36Sopenharmony_ci atomic_add(diff, &cdcpend->conn->sndbuf_space); 4562306a36Sopenharmony_ci /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */ 4662306a36Sopenharmony_ci smp_mb__after_atomic(); 4762306a36Sopenharmony_ci smc_curs_copy(&conn->tx_curs_fin, &cdcpend->cursor, conn); 4862306a36Sopenharmony_ci smc_curs_copy(&conn->local_tx_ctrl_fin, &cdcpend->p_cursor, 4962306a36Sopenharmony_ci conn); 5062306a36Sopenharmony_ci conn->tx_cdc_seq_fin = cdcpend->ctrl_seq; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (atomic_dec_and_test(&conn->cdc_pend_tx_wr)) { 5462306a36Sopenharmony_ci /* If user owns the sock_lock, mark the connection need sending. 5562306a36Sopenharmony_ci * User context will later try to send when it release sock_lock 5662306a36Sopenharmony_ci * in smc_release_cb() 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci if (sock_owned_by_user(&smc->sk)) 5962306a36Sopenharmony_ci conn->tx_in_release_sock = true; 6062306a36Sopenharmony_ci else 6162306a36Sopenharmony_ci smc_tx_pending(conn); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (unlikely(wq_has_sleeper(&conn->cdc_pend_tx_wq))) 6462306a36Sopenharmony_ci wake_up(&conn->cdc_pend_tx_wq); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci WARN_ON(atomic_read(&conn->cdc_pend_tx_wr) < 0); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci smc_tx_sndbuf_nonfull(smc); 6962306a36Sopenharmony_ci bh_unlock_sock(&smc->sk); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciint smc_cdc_get_free_slot(struct smc_connection *conn, 7362306a36Sopenharmony_ci struct smc_link *link, 7462306a36Sopenharmony_ci struct smc_wr_buf **wr_buf, 7562306a36Sopenharmony_ci struct smc_rdma_wr **wr_rdma_buf, 7662306a36Sopenharmony_ci struct smc_cdc_tx_pend **pend) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci int rc; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci rc = smc_wr_tx_get_free_slot(link, smc_cdc_tx_handler, wr_buf, 8162306a36Sopenharmony_ci wr_rdma_buf, 8262306a36Sopenharmony_ci (struct smc_wr_tx_pend_priv **)pend); 8362306a36Sopenharmony_ci if (conn->killed) { 8462306a36Sopenharmony_ci /* abnormal termination */ 8562306a36Sopenharmony_ci if (!rc) 8662306a36Sopenharmony_ci smc_wr_tx_put_slot(link, 8762306a36Sopenharmony_ci (struct smc_wr_tx_pend_priv *)(*pend)); 8862306a36Sopenharmony_ci rc = -EPIPE; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci return rc; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic inline void smc_cdc_add_pending_send(struct smc_connection *conn, 9462306a36Sopenharmony_ci struct smc_cdc_tx_pend *pend) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci BUILD_BUG_ON_MSG( 9762306a36Sopenharmony_ci sizeof(struct smc_cdc_msg) > SMC_WR_BUF_SIZE, 9862306a36Sopenharmony_ci "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_cdc_msg)"); 9962306a36Sopenharmony_ci BUILD_BUG_ON_MSG( 10062306a36Sopenharmony_ci offsetofend(struct smc_cdc_msg, reserved) > SMC_WR_TX_SIZE, 10162306a36Sopenharmony_ci "must adapt SMC_WR_TX_SIZE to sizeof(struct smc_cdc_msg); if not all smc_wr upper layer protocols use the same message size any more, must start to set link->wr_tx_sges[i].length on each individual smc_wr_tx_send()"); 10262306a36Sopenharmony_ci BUILD_BUG_ON_MSG( 10362306a36Sopenharmony_ci sizeof(struct smc_cdc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, 10462306a36Sopenharmony_ci "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_cdc_tx_pend)"); 10562306a36Sopenharmony_ci pend->conn = conn; 10662306a36Sopenharmony_ci pend->cursor = conn->tx_curs_sent; 10762306a36Sopenharmony_ci pend->p_cursor = conn->local_tx_ctrl.prod; 10862306a36Sopenharmony_ci pend->ctrl_seq = conn->tx_cdc_seq; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint smc_cdc_msg_send(struct smc_connection *conn, 11262306a36Sopenharmony_ci struct smc_wr_buf *wr_buf, 11362306a36Sopenharmony_ci struct smc_cdc_tx_pend *pend) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct smc_link *link = conn->lnk; 11662306a36Sopenharmony_ci union smc_host_cursor cfed; 11762306a36Sopenharmony_ci int rc; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci smc_cdc_add_pending_send(conn, pend); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci conn->tx_cdc_seq++; 12262306a36Sopenharmony_ci conn->local_tx_ctrl.seqno = conn->tx_cdc_seq; 12362306a36Sopenharmony_ci smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf, conn, &cfed); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci atomic_inc(&conn->cdc_pend_tx_wr); 12662306a36Sopenharmony_ci smp_mb__after_atomic(); /* Make sure cdc_pend_tx_wr added before post */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend); 12962306a36Sopenharmony_ci if (!rc) { 13062306a36Sopenharmony_ci smc_curs_copy(&conn->rx_curs_confirmed, &cfed, conn); 13162306a36Sopenharmony_ci conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0; 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci conn->tx_cdc_seq--; 13462306a36Sopenharmony_ci conn->local_tx_ctrl.seqno = conn->tx_cdc_seq; 13562306a36Sopenharmony_ci atomic_dec(&conn->cdc_pend_tx_wr); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return rc; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* send a validation msg indicating the move of a conn to an other QP link */ 14262306a36Sopenharmony_ciint smcr_cdc_msg_send_validation(struct smc_connection *conn, 14362306a36Sopenharmony_ci struct smc_cdc_tx_pend *pend, 14462306a36Sopenharmony_ci struct smc_wr_buf *wr_buf) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct smc_host_cdc_msg *local = &conn->local_tx_ctrl; 14762306a36Sopenharmony_ci struct smc_link *link = conn->lnk; 14862306a36Sopenharmony_ci struct smc_cdc_msg *peer; 14962306a36Sopenharmony_ci int rc; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci peer = (struct smc_cdc_msg *)wr_buf; 15262306a36Sopenharmony_ci peer->common.type = local->common.type; 15362306a36Sopenharmony_ci peer->len = local->len; 15462306a36Sopenharmony_ci peer->seqno = htons(conn->tx_cdc_seq_fin); /* seqno last compl. tx */ 15562306a36Sopenharmony_ci peer->token = htonl(local->token); 15662306a36Sopenharmony_ci peer->prod_flags.failover_validation = 1; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* We need to set pend->conn here to make sure smc_cdc_tx_handler() 15962306a36Sopenharmony_ci * can handle properly 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci smc_cdc_add_pending_send(conn, pend); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci atomic_inc(&conn->cdc_pend_tx_wr); 16462306a36Sopenharmony_ci smp_mb__after_atomic(); /* Make sure cdc_pend_tx_wr added before post */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend); 16762306a36Sopenharmony_ci if (unlikely(rc)) 16862306a36Sopenharmony_ci atomic_dec(&conn->cdc_pend_tx_wr); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return rc; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int smcr_cdc_get_slot_and_msg_send(struct smc_connection *conn) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct smc_cdc_tx_pend *pend; 17662306a36Sopenharmony_ci struct smc_wr_buf *wr_buf; 17762306a36Sopenharmony_ci struct smc_link *link; 17862306a36Sopenharmony_ci bool again = false; 17962306a36Sopenharmony_ci int rc; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciagain: 18262306a36Sopenharmony_ci link = conn->lnk; 18362306a36Sopenharmony_ci if (!smc_wr_tx_link_hold(link)) 18462306a36Sopenharmony_ci return -ENOLINK; 18562306a36Sopenharmony_ci rc = smc_cdc_get_free_slot(conn, link, &wr_buf, NULL, &pend); 18662306a36Sopenharmony_ci if (rc) 18762306a36Sopenharmony_ci goto put_out; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci spin_lock_bh(&conn->send_lock); 19062306a36Sopenharmony_ci if (link != conn->lnk) { 19162306a36Sopenharmony_ci /* link of connection changed, try again one time*/ 19262306a36Sopenharmony_ci spin_unlock_bh(&conn->send_lock); 19362306a36Sopenharmony_ci smc_wr_tx_put_slot(link, 19462306a36Sopenharmony_ci (struct smc_wr_tx_pend_priv *)pend); 19562306a36Sopenharmony_ci smc_wr_tx_link_put(link); 19662306a36Sopenharmony_ci if (again) 19762306a36Sopenharmony_ci return -ENOLINK; 19862306a36Sopenharmony_ci again = true; 19962306a36Sopenharmony_ci goto again; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci rc = smc_cdc_msg_send(conn, wr_buf, pend); 20262306a36Sopenharmony_ci spin_unlock_bh(&conn->send_lock); 20362306a36Sopenharmony_ciput_out: 20462306a36Sopenharmony_ci smc_wr_tx_link_put(link); 20562306a36Sopenharmony_ci return rc; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint smc_cdc_get_slot_and_msg_send(struct smc_connection *conn) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci int rc; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!smc_conn_lgr_valid(conn) || 21362306a36Sopenharmony_ci (conn->lgr->is_smcd && conn->lgr->peer_shutdown)) 21462306a36Sopenharmony_ci return -EPIPE; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (conn->lgr->is_smcd) { 21762306a36Sopenharmony_ci spin_lock_bh(&conn->send_lock); 21862306a36Sopenharmony_ci rc = smcd_cdc_msg_send(conn); 21962306a36Sopenharmony_ci spin_unlock_bh(&conn->send_lock); 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci rc = smcr_cdc_get_slot_and_msg_send(conn); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return rc; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_civoid smc_cdc_wait_pend_tx_wr(struct smc_connection *conn) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci wait_event(conn->cdc_pend_tx_wq, !atomic_read(&conn->cdc_pend_tx_wr)); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* Send a SMC-D CDC header. 23362306a36Sopenharmony_ci * This increments the free space available in our send buffer. 23462306a36Sopenharmony_ci * Also update the confirmed receive buffer with what was sent to the peer. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ciint smcd_cdc_msg_send(struct smc_connection *conn) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct smc_sock *smc = container_of(conn, struct smc_sock, conn); 23962306a36Sopenharmony_ci union smc_host_cursor curs; 24062306a36Sopenharmony_ci struct smcd_cdc_msg cdc; 24162306a36Sopenharmony_ci int rc, diff; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci memset(&cdc, 0, sizeof(cdc)); 24462306a36Sopenharmony_ci cdc.common.type = SMC_CDC_MSG_TYPE; 24562306a36Sopenharmony_ci curs.acurs.counter = atomic64_read(&conn->local_tx_ctrl.prod.acurs); 24662306a36Sopenharmony_ci cdc.prod.wrap = curs.wrap; 24762306a36Sopenharmony_ci cdc.prod.count = curs.count; 24862306a36Sopenharmony_ci curs.acurs.counter = atomic64_read(&conn->local_tx_ctrl.cons.acurs); 24962306a36Sopenharmony_ci cdc.cons.wrap = curs.wrap; 25062306a36Sopenharmony_ci cdc.cons.count = curs.count; 25162306a36Sopenharmony_ci cdc.cons.prod_flags = conn->local_tx_ctrl.prod_flags; 25262306a36Sopenharmony_ci cdc.cons.conn_state_flags = conn->local_tx_ctrl.conn_state_flags; 25362306a36Sopenharmony_ci rc = smcd_tx_ism_write(conn, &cdc, sizeof(cdc), 0, 1); 25462306a36Sopenharmony_ci if (rc) 25562306a36Sopenharmony_ci return rc; 25662306a36Sopenharmony_ci smc_curs_copy(&conn->rx_curs_confirmed, &curs, conn); 25762306a36Sopenharmony_ci conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0; 25862306a36Sopenharmony_ci /* Calculate transmitted data and increment free send buffer space */ 25962306a36Sopenharmony_ci diff = smc_curs_diff(conn->sndbuf_desc->len, &conn->tx_curs_fin, 26062306a36Sopenharmony_ci &conn->tx_curs_sent); 26162306a36Sopenharmony_ci /* increased by confirmed number of bytes */ 26262306a36Sopenharmony_ci smp_mb__before_atomic(); 26362306a36Sopenharmony_ci atomic_add(diff, &conn->sndbuf_space); 26462306a36Sopenharmony_ci /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */ 26562306a36Sopenharmony_ci smp_mb__after_atomic(); 26662306a36Sopenharmony_ci smc_curs_copy(&conn->tx_curs_fin, &conn->tx_curs_sent, conn); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci smc_tx_sndbuf_nonfull(smc); 26962306a36Sopenharmony_ci return rc; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/********************************* receive ***********************************/ 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic inline bool smc_cdc_before(u16 seq1, u16 seq2) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return (s16)(seq1 - seq2) < 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void smc_cdc_handle_urg_data_arrival(struct smc_sock *smc, 28062306a36Sopenharmony_ci int *diff_prod) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct smc_connection *conn = &smc->conn; 28362306a36Sopenharmony_ci char *base; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* new data included urgent business */ 28662306a36Sopenharmony_ci smc_curs_copy(&conn->urg_curs, &conn->local_rx_ctrl.prod, conn); 28762306a36Sopenharmony_ci conn->urg_state = SMC_URG_VALID; 28862306a36Sopenharmony_ci if (!sock_flag(&smc->sk, SOCK_URGINLINE)) 28962306a36Sopenharmony_ci /* we'll skip the urgent byte, so don't account for it */ 29062306a36Sopenharmony_ci (*diff_prod)--; 29162306a36Sopenharmony_ci base = (char *)conn->rmb_desc->cpu_addr + conn->rx_off; 29262306a36Sopenharmony_ci if (conn->urg_curs.count) 29362306a36Sopenharmony_ci conn->urg_rx_byte = *(base + conn->urg_curs.count - 1); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci conn->urg_rx_byte = *(base + conn->rmb_desc->len - 1); 29662306a36Sopenharmony_ci sk_send_sigurg(&smc->sk); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void smc_cdc_msg_validate(struct smc_sock *smc, struct smc_cdc_msg *cdc, 30062306a36Sopenharmony_ci struct smc_link *link) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct smc_connection *conn = &smc->conn; 30362306a36Sopenharmony_ci u16 recv_seq = ntohs(cdc->seqno); 30462306a36Sopenharmony_ci s16 diff; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* check that seqnum was seen before */ 30762306a36Sopenharmony_ci diff = conn->local_rx_ctrl.seqno - recv_seq; 30862306a36Sopenharmony_ci if (diff < 0) { /* diff larger than 0x7fff */ 30962306a36Sopenharmony_ci /* drop connection */ 31062306a36Sopenharmony_ci conn->out_of_sync = 1; /* prevent any further receives */ 31162306a36Sopenharmony_ci spin_lock_bh(&conn->send_lock); 31262306a36Sopenharmony_ci conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; 31362306a36Sopenharmony_ci conn->lnk = link; 31462306a36Sopenharmony_ci spin_unlock_bh(&conn->send_lock); 31562306a36Sopenharmony_ci sock_hold(&smc->sk); /* sock_put in abort_work */ 31662306a36Sopenharmony_ci if (!queue_work(smc_close_wq, &conn->abort_work)) 31762306a36Sopenharmony_ci sock_put(&smc->sk); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void smc_cdc_msg_recv_action(struct smc_sock *smc, 32262306a36Sopenharmony_ci struct smc_cdc_msg *cdc) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci union smc_host_cursor cons_old, prod_old; 32562306a36Sopenharmony_ci struct smc_connection *conn = &smc->conn; 32662306a36Sopenharmony_ci int diff_cons, diff_prod; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci smc_curs_copy(&prod_old, &conn->local_rx_ctrl.prod, conn); 32962306a36Sopenharmony_ci smc_curs_copy(&cons_old, &conn->local_rx_ctrl.cons, conn); 33062306a36Sopenharmony_ci smc_cdc_msg_to_host(&conn->local_rx_ctrl, cdc, conn); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci diff_cons = smc_curs_diff(conn->peer_rmbe_size, &cons_old, 33362306a36Sopenharmony_ci &conn->local_rx_ctrl.cons); 33462306a36Sopenharmony_ci if (diff_cons) { 33562306a36Sopenharmony_ci /* peer_rmbe_space is decreased during data transfer with RDMA 33662306a36Sopenharmony_ci * write 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci smp_mb__before_atomic(); 33962306a36Sopenharmony_ci atomic_add(diff_cons, &conn->peer_rmbe_space); 34062306a36Sopenharmony_ci /* guarantee 0 <= peer_rmbe_space <= peer_rmbe_size */ 34162306a36Sopenharmony_ci smp_mb__after_atomic(); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci diff_prod = smc_curs_diff(conn->rmb_desc->len, &prod_old, 34562306a36Sopenharmony_ci &conn->local_rx_ctrl.prod); 34662306a36Sopenharmony_ci if (diff_prod) { 34762306a36Sopenharmony_ci if (conn->local_rx_ctrl.prod_flags.urg_data_present) 34862306a36Sopenharmony_ci smc_cdc_handle_urg_data_arrival(smc, &diff_prod); 34962306a36Sopenharmony_ci /* bytes_to_rcv is decreased in smc_recvmsg */ 35062306a36Sopenharmony_ci smp_mb__before_atomic(); 35162306a36Sopenharmony_ci atomic_add(diff_prod, &conn->bytes_to_rcv); 35262306a36Sopenharmony_ci /* guarantee 0 <= bytes_to_rcv <= rmb_desc->len */ 35362306a36Sopenharmony_ci smp_mb__after_atomic(); 35462306a36Sopenharmony_ci smc->sk.sk_data_ready(&smc->sk); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci if (conn->local_rx_ctrl.prod_flags.write_blocked) 35762306a36Sopenharmony_ci smc->sk.sk_data_ready(&smc->sk); 35862306a36Sopenharmony_ci if (conn->local_rx_ctrl.prod_flags.urg_data_pending) 35962306a36Sopenharmony_ci conn->urg_state = SMC_URG_NOTYET; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */ 36362306a36Sopenharmony_ci if ((diff_cons && smc_tx_prepared_sends(conn)) || 36462306a36Sopenharmony_ci conn->local_rx_ctrl.prod_flags.cons_curs_upd_req || 36562306a36Sopenharmony_ci conn->local_rx_ctrl.prod_flags.urg_data_pending) { 36662306a36Sopenharmony_ci if (!sock_owned_by_user(&smc->sk)) 36762306a36Sopenharmony_ci smc_tx_pending(conn); 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci conn->tx_in_release_sock = true; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (diff_cons && conn->urg_tx_pend && 37362306a36Sopenharmony_ci atomic_read(&conn->peer_rmbe_space) == conn->peer_rmbe_size) { 37462306a36Sopenharmony_ci /* urg data confirmed by peer, indicate we're ready for more */ 37562306a36Sopenharmony_ci conn->urg_tx_pend = false; 37662306a36Sopenharmony_ci smc->sk.sk_write_space(&smc->sk); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (conn->local_rx_ctrl.conn_state_flags.peer_conn_abort) { 38062306a36Sopenharmony_ci smc->sk.sk_err = ECONNRESET; 38162306a36Sopenharmony_ci conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci if (smc_cdc_rxed_any_close_or_senddone(conn)) { 38462306a36Sopenharmony_ci smc->sk.sk_shutdown |= RCV_SHUTDOWN; 38562306a36Sopenharmony_ci if (smc->clcsock && smc->clcsock->sk) 38662306a36Sopenharmony_ci smc->clcsock->sk->sk_shutdown |= RCV_SHUTDOWN; 38762306a36Sopenharmony_ci smc_sock_set_flag(&smc->sk, SOCK_DONE); 38862306a36Sopenharmony_ci sock_hold(&smc->sk); /* sock_put in close_work */ 38962306a36Sopenharmony_ci if (!queue_work(smc_close_wq, &conn->close_work)) 39062306a36Sopenharmony_ci sock_put(&smc->sk); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* called under tasklet context */ 39562306a36Sopenharmony_cistatic void smc_cdc_msg_recv(struct smc_sock *smc, struct smc_cdc_msg *cdc) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci sock_hold(&smc->sk); 39862306a36Sopenharmony_ci bh_lock_sock(&smc->sk); 39962306a36Sopenharmony_ci smc_cdc_msg_recv_action(smc, cdc); 40062306a36Sopenharmony_ci bh_unlock_sock(&smc->sk); 40162306a36Sopenharmony_ci sock_put(&smc->sk); /* no free sk in softirq-context */ 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/* Schedule a tasklet for this connection. Triggered from the ISM device IRQ 40562306a36Sopenharmony_ci * handler to indicate update in the DMBE. 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * Context: 40862306a36Sopenharmony_ci * - tasklet context 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic void smcd_cdc_rx_tsklet(struct tasklet_struct *t) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct smc_connection *conn = from_tasklet(conn, t, rx_tsklet); 41362306a36Sopenharmony_ci struct smcd_cdc_msg *data_cdc; 41462306a36Sopenharmony_ci struct smcd_cdc_msg cdc; 41562306a36Sopenharmony_ci struct smc_sock *smc; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (!conn || conn->killed) 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr; 42162306a36Sopenharmony_ci smcd_curs_copy(&cdc.prod, &data_cdc->prod, conn); 42262306a36Sopenharmony_ci smcd_curs_copy(&cdc.cons, &data_cdc->cons, conn); 42362306a36Sopenharmony_ci smc = container_of(conn, struct smc_sock, conn); 42462306a36Sopenharmony_ci smc_cdc_msg_recv(smc, (struct smc_cdc_msg *)&cdc); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci/* Initialize receive tasklet. Called from ISM device IRQ handler to start 42862306a36Sopenharmony_ci * receiver side. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_civoid smcd_cdc_rx_init(struct smc_connection *conn) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci tasklet_setup(&conn->rx_tsklet, smcd_cdc_rx_tsklet); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/***************************** init, exit, misc ******************************/ 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct smc_link *link = (struct smc_link *)wc->qp->qp_context; 44062306a36Sopenharmony_ci struct smc_cdc_msg *cdc = buf; 44162306a36Sopenharmony_ci struct smc_connection *conn; 44262306a36Sopenharmony_ci struct smc_link_group *lgr; 44362306a36Sopenharmony_ci struct smc_sock *smc; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved)) 44662306a36Sopenharmony_ci return; /* short message */ 44762306a36Sopenharmony_ci if (cdc->len != SMC_WR_TX_SIZE) 44862306a36Sopenharmony_ci return; /* invalid message */ 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* lookup connection */ 45162306a36Sopenharmony_ci lgr = smc_get_lgr(link); 45262306a36Sopenharmony_ci read_lock_bh(&lgr->conns_lock); 45362306a36Sopenharmony_ci conn = smc_lgr_find_conn(ntohl(cdc->token), lgr); 45462306a36Sopenharmony_ci read_unlock_bh(&lgr->conns_lock); 45562306a36Sopenharmony_ci if (!conn || conn->out_of_sync) 45662306a36Sopenharmony_ci return; 45762306a36Sopenharmony_ci smc = container_of(conn, struct smc_sock, conn); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (cdc->prod_flags.failover_validation) { 46062306a36Sopenharmony_ci smc_cdc_msg_validate(smc, cdc, link); 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci if (smc_cdc_before(ntohs(cdc->seqno), 46462306a36Sopenharmony_ci conn->local_rx_ctrl.seqno)) 46562306a36Sopenharmony_ci /* received seqno is old */ 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci smc_cdc_msg_recv(smc, cdc); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic struct smc_wr_rx_handler smc_cdc_rx_handlers[] = { 47262306a36Sopenharmony_ci { 47362306a36Sopenharmony_ci .handler = smc_cdc_rx_handler, 47462306a36Sopenharmony_ci .type = SMC_CDC_MSG_TYPE 47562306a36Sopenharmony_ci }, 47662306a36Sopenharmony_ci { 47762306a36Sopenharmony_ci .handler = NULL, 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci}; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ciint __init smc_cdc_init(void) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct smc_wr_rx_handler *handler; 48462306a36Sopenharmony_ci int rc = 0; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci for (handler = smc_cdc_rx_handlers; handler->handler; handler++) { 48762306a36Sopenharmony_ci INIT_HLIST_NODE(&handler->list); 48862306a36Sopenharmony_ci rc = smc_wr_rx_register_handler(handler); 48962306a36Sopenharmony_ci if (rc) 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci return rc; 49362306a36Sopenharmony_ci} 494