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 * Connection Data Control (CDC) 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#ifndef SMC_CDC_H 138c2ecf20Sopenharmony_ci#define SMC_CDC_H 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* max_t */ 168c2ecf20Sopenharmony_ci#include <linux/atomic.h> 178c2ecf20Sopenharmony_ci#include <linux/in.h> 188c2ecf20Sopenharmony_ci#include <linux/compiler.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "smc.h" 218c2ecf20Sopenharmony_ci#include "smc_core.h" 228c2ecf20Sopenharmony_ci#include "smc_wr.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define SMC_CDC_MSG_TYPE 0xFE 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* in network byte order */ 278c2ecf20Sopenharmony_ciunion smc_cdc_cursor { /* SMC cursor */ 288c2ecf20Sopenharmony_ci struct { 298c2ecf20Sopenharmony_ci __be16 reserved; 308c2ecf20Sopenharmony_ci __be16 wrap; 318c2ecf20Sopenharmony_ci __be32 count; 328c2ecf20Sopenharmony_ci }; 338c2ecf20Sopenharmony_ci#ifdef KERNEL_HAS_ATOMIC64 348c2ecf20Sopenharmony_ci atomic64_t acurs; /* for atomic processing */ 358c2ecf20Sopenharmony_ci#else 368c2ecf20Sopenharmony_ci u64 acurs; /* for atomic processing */ 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci} __aligned(8); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* in network byte order */ 418c2ecf20Sopenharmony_cistruct smc_cdc_msg { 428c2ecf20Sopenharmony_ci struct smc_wr_rx_hdr common; /* .type = 0xFE */ 438c2ecf20Sopenharmony_ci u8 len; /* 44 */ 448c2ecf20Sopenharmony_ci __be16 seqno; 458c2ecf20Sopenharmony_ci __be32 token; 468c2ecf20Sopenharmony_ci union smc_cdc_cursor prod; 478c2ecf20Sopenharmony_ci union smc_cdc_cursor cons; /* piggy backed "ack" */ 488c2ecf20Sopenharmony_ci struct smc_cdc_producer_flags prod_flags; 498c2ecf20Sopenharmony_ci struct smc_cdc_conn_state_flags conn_state_flags; 508c2ecf20Sopenharmony_ci u8 reserved[18]; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* SMC-D cursor format */ 548c2ecf20Sopenharmony_ciunion smcd_cdc_cursor { 558c2ecf20Sopenharmony_ci struct { 568c2ecf20Sopenharmony_ci u16 wrap; 578c2ecf20Sopenharmony_ci u32 count; 588c2ecf20Sopenharmony_ci struct smc_cdc_producer_flags prod_flags; 598c2ecf20Sopenharmony_ci struct smc_cdc_conn_state_flags conn_state_flags; 608c2ecf20Sopenharmony_ci } __packed; 618c2ecf20Sopenharmony_ci#ifdef KERNEL_HAS_ATOMIC64 628c2ecf20Sopenharmony_ci atomic64_t acurs; /* for atomic processing */ 638c2ecf20Sopenharmony_ci#else 648c2ecf20Sopenharmony_ci u64 acurs; /* for atomic processing */ 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci} __aligned(8); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* CDC message for SMC-D */ 698c2ecf20Sopenharmony_cistruct smcd_cdc_msg { 708c2ecf20Sopenharmony_ci struct smc_wr_rx_hdr common; /* Type = 0xFE */ 718c2ecf20Sopenharmony_ci u8 res1[7]; 728c2ecf20Sopenharmony_ci union smcd_cdc_cursor prod; 738c2ecf20Sopenharmony_ci union smcd_cdc_cursor cons; 748c2ecf20Sopenharmony_ci u8 res3[8]; 758c2ecf20Sopenharmony_ci} __aligned(8); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline bool smc_cdc_rxed_any_close(struct smc_connection *conn) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return conn->local_rx_ctrl.conn_state_flags.peer_conn_abort || 808c2ecf20Sopenharmony_ci conn->local_rx_ctrl.conn_state_flags.peer_conn_closed; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline bool smc_cdc_rxed_any_close_or_senddone( 848c2ecf20Sopenharmony_ci struct smc_connection *conn) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci return smc_cdc_rxed_any_close(conn) || 878c2ecf20Sopenharmony_ci conn->local_rx_ctrl.conn_state_flags.peer_done_writing; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic inline void smc_curs_add(int size, union smc_host_cursor *curs, 918c2ecf20Sopenharmony_ci int value) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci curs->count += value; 948c2ecf20Sopenharmony_ci if (curs->count >= size) { 958c2ecf20Sopenharmony_ci curs->wrap++; 968c2ecf20Sopenharmony_ci curs->count -= size; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Copy cursor src into tgt */ 1018c2ecf20Sopenharmony_cistatic inline void smc_curs_copy(union smc_host_cursor *tgt, 1028c2ecf20Sopenharmony_ci union smc_host_cursor *src, 1038c2ecf20Sopenharmony_ci struct smc_connection *conn) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci#ifndef KERNEL_HAS_ATOMIC64 1068c2ecf20Sopenharmony_ci unsigned long flags; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->acurs_lock, flags); 1098c2ecf20Sopenharmony_ci tgt->acurs = src->acurs; 1108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->acurs_lock, flags); 1118c2ecf20Sopenharmony_ci#else 1128c2ecf20Sopenharmony_ci atomic64_set(&tgt->acurs, atomic64_read(&src->acurs)); 1138c2ecf20Sopenharmony_ci#endif 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic inline void smc_curs_copy_net(union smc_cdc_cursor *tgt, 1178c2ecf20Sopenharmony_ci union smc_cdc_cursor *src, 1188c2ecf20Sopenharmony_ci struct smc_connection *conn) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci#ifndef KERNEL_HAS_ATOMIC64 1218c2ecf20Sopenharmony_ci unsigned long flags; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->acurs_lock, flags); 1248c2ecf20Sopenharmony_ci tgt->acurs = src->acurs; 1258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->acurs_lock, flags); 1268c2ecf20Sopenharmony_ci#else 1278c2ecf20Sopenharmony_ci atomic64_set(&tgt->acurs, atomic64_read(&src->acurs)); 1288c2ecf20Sopenharmony_ci#endif 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic inline void smcd_curs_copy(union smcd_cdc_cursor *tgt, 1328c2ecf20Sopenharmony_ci union smcd_cdc_cursor *src, 1338c2ecf20Sopenharmony_ci struct smc_connection *conn) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci#ifndef KERNEL_HAS_ATOMIC64 1368c2ecf20Sopenharmony_ci unsigned long flags; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->acurs_lock, flags); 1398c2ecf20Sopenharmony_ci tgt->acurs = src->acurs; 1408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->acurs_lock, flags); 1418c2ecf20Sopenharmony_ci#else 1428c2ecf20Sopenharmony_ci atomic64_set(&tgt->acurs, atomic64_read(&src->acurs)); 1438c2ecf20Sopenharmony_ci#endif 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* calculate cursor difference between old and new, where old <= new and 1478c2ecf20Sopenharmony_ci * difference cannot exceed size 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic inline int smc_curs_diff(unsigned int size, 1508c2ecf20Sopenharmony_ci union smc_host_cursor *old, 1518c2ecf20Sopenharmony_ci union smc_host_cursor *new) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci if (old->wrap != new->wrap) 1548c2ecf20Sopenharmony_ci return max_t(int, 0, 1558c2ecf20Sopenharmony_ci ((size - old->count) + new->count)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return max_t(int, 0, (new->count - old->count)); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* calculate cursor difference between old and new - returns negative 1618c2ecf20Sopenharmony_ci * value in case old > new 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic inline int smc_curs_comp(unsigned int size, 1648c2ecf20Sopenharmony_ci union smc_host_cursor *old, 1658c2ecf20Sopenharmony_ci union smc_host_cursor *new) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (old->wrap > new->wrap || 1688c2ecf20Sopenharmony_ci (old->wrap == new->wrap && old->count > new->count)) 1698c2ecf20Sopenharmony_ci return -smc_curs_diff(size, new, old); 1708c2ecf20Sopenharmony_ci return smc_curs_diff(size, old, new); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* calculate cursor difference between old and new, where old <= new and 1748c2ecf20Sopenharmony_ci * difference may exceed size 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic inline int smc_curs_diff_large(unsigned int size, 1778c2ecf20Sopenharmony_ci union smc_host_cursor *old, 1788c2ecf20Sopenharmony_ci union smc_host_cursor *new) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci if (old->wrap < new->wrap) 1818c2ecf20Sopenharmony_ci return min_t(int, 1828c2ecf20Sopenharmony_ci (size - old->count) + new->count + 1838c2ecf20Sopenharmony_ci (new->wrap - old->wrap - 1) * size, 1848c2ecf20Sopenharmony_ci size); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (old->wrap > new->wrap) /* wrap has switched from 0xffff to 0x0000 */ 1878c2ecf20Sopenharmony_ci return min_t(int, 1888c2ecf20Sopenharmony_ci (size - old->count) + new->count + 1898c2ecf20Sopenharmony_ci (new->wrap + 0xffff - old->wrap) * size, 1908c2ecf20Sopenharmony_ci size); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return max_t(int, 0, (new->count - old->count)); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer, 1968c2ecf20Sopenharmony_ci union smc_host_cursor *local, 1978c2ecf20Sopenharmony_ci union smc_host_cursor *save, 1988c2ecf20Sopenharmony_ci struct smc_connection *conn) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci smc_curs_copy(save, local, conn); 2018c2ecf20Sopenharmony_ci peer->count = htonl(save->count); 2028c2ecf20Sopenharmony_ci peer->wrap = htons(save->wrap); 2038c2ecf20Sopenharmony_ci /* peer->reserved = htons(0); must be ensured by caller */ 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic inline void smc_host_msg_to_cdc(struct smc_cdc_msg *peer, 2078c2ecf20Sopenharmony_ci struct smc_connection *conn, 2088c2ecf20Sopenharmony_ci union smc_host_cursor *save) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct smc_host_cdc_msg *local = &conn->local_tx_ctrl; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci peer->common.type = local->common.type; 2138c2ecf20Sopenharmony_ci peer->len = local->len; 2148c2ecf20Sopenharmony_ci peer->seqno = htons(local->seqno); 2158c2ecf20Sopenharmony_ci peer->token = htonl(local->token); 2168c2ecf20Sopenharmony_ci smc_host_cursor_to_cdc(&peer->prod, &local->prod, save, conn); 2178c2ecf20Sopenharmony_ci smc_host_cursor_to_cdc(&peer->cons, &local->cons, save, conn); 2188c2ecf20Sopenharmony_ci peer->prod_flags = local->prod_flags; 2198c2ecf20Sopenharmony_ci peer->conn_state_flags = local->conn_state_flags; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline void smc_cdc_cursor_to_host(union smc_host_cursor *local, 2238c2ecf20Sopenharmony_ci union smc_cdc_cursor *peer, 2248c2ecf20Sopenharmony_ci struct smc_connection *conn) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci union smc_host_cursor temp, old; 2278c2ecf20Sopenharmony_ci union smc_cdc_cursor net; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci smc_curs_copy(&old, local, conn); 2308c2ecf20Sopenharmony_ci smc_curs_copy_net(&net, peer, conn); 2318c2ecf20Sopenharmony_ci temp.count = ntohl(net.count); 2328c2ecf20Sopenharmony_ci temp.wrap = ntohs(net.wrap); 2338c2ecf20Sopenharmony_ci if ((old.wrap > temp.wrap) && temp.wrap) 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci if ((old.wrap == temp.wrap) && 2368c2ecf20Sopenharmony_ci (old.count > temp.count)) 2378c2ecf20Sopenharmony_ci return; 2388c2ecf20Sopenharmony_ci smc_curs_copy(local, &temp, conn); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic inline void smcr_cdc_msg_to_host(struct smc_host_cdc_msg *local, 2428c2ecf20Sopenharmony_ci struct smc_cdc_msg *peer, 2438c2ecf20Sopenharmony_ci struct smc_connection *conn) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci local->common.type = peer->common.type; 2468c2ecf20Sopenharmony_ci local->len = peer->len; 2478c2ecf20Sopenharmony_ci local->seqno = ntohs(peer->seqno); 2488c2ecf20Sopenharmony_ci local->token = ntohl(peer->token); 2498c2ecf20Sopenharmony_ci smc_cdc_cursor_to_host(&local->prod, &peer->prod, conn); 2508c2ecf20Sopenharmony_ci smc_cdc_cursor_to_host(&local->cons, &peer->cons, conn); 2518c2ecf20Sopenharmony_ci local->prod_flags = peer->prod_flags; 2528c2ecf20Sopenharmony_ci local->conn_state_flags = peer->conn_state_flags; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic inline void smcd_cdc_msg_to_host(struct smc_host_cdc_msg *local, 2568c2ecf20Sopenharmony_ci struct smcd_cdc_msg *peer, 2578c2ecf20Sopenharmony_ci struct smc_connection *conn) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci union smc_host_cursor temp; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci temp.wrap = peer->prod.wrap; 2628c2ecf20Sopenharmony_ci temp.count = peer->prod.count; 2638c2ecf20Sopenharmony_ci smc_curs_copy(&local->prod, &temp, conn); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci temp.wrap = peer->cons.wrap; 2668c2ecf20Sopenharmony_ci temp.count = peer->cons.count; 2678c2ecf20Sopenharmony_ci smc_curs_copy(&local->cons, &temp, conn); 2688c2ecf20Sopenharmony_ci local->prod_flags = peer->cons.prod_flags; 2698c2ecf20Sopenharmony_ci local->conn_state_flags = peer->cons.conn_state_flags; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic inline void smc_cdc_msg_to_host(struct smc_host_cdc_msg *local, 2738c2ecf20Sopenharmony_ci struct smc_cdc_msg *peer, 2748c2ecf20Sopenharmony_ci struct smc_connection *conn) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci if (conn->lgr->is_smcd) 2778c2ecf20Sopenharmony_ci smcd_cdc_msg_to_host(local, (struct smcd_cdc_msg *)peer, conn); 2788c2ecf20Sopenharmony_ci else 2798c2ecf20Sopenharmony_ci smcr_cdc_msg_to_host(local, peer, conn); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistruct smc_cdc_tx_pend { 2838c2ecf20Sopenharmony_ci struct smc_connection *conn; /* socket connection */ 2848c2ecf20Sopenharmony_ci union smc_host_cursor cursor; /* tx sndbuf cursor sent */ 2858c2ecf20Sopenharmony_ci union smc_host_cursor p_cursor; /* rx RMBE cursor produced */ 2868c2ecf20Sopenharmony_ci u16 ctrl_seq; /* conn. tx sequence # */ 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciint smc_cdc_get_free_slot(struct smc_connection *conn, 2908c2ecf20Sopenharmony_ci struct smc_link *link, 2918c2ecf20Sopenharmony_ci struct smc_wr_buf **wr_buf, 2928c2ecf20Sopenharmony_ci struct smc_rdma_wr **wr_rdma_buf, 2938c2ecf20Sopenharmony_ci struct smc_cdc_tx_pend **pend); 2948c2ecf20Sopenharmony_civoid smc_cdc_wait_pend_tx_wr(struct smc_connection *conn); 2958c2ecf20Sopenharmony_ciint smc_cdc_msg_send(struct smc_connection *conn, struct smc_wr_buf *wr_buf, 2968c2ecf20Sopenharmony_ci struct smc_cdc_tx_pend *pend); 2978c2ecf20Sopenharmony_ciint smc_cdc_get_slot_and_msg_send(struct smc_connection *conn); 2988c2ecf20Sopenharmony_ciint smcd_cdc_msg_send(struct smc_connection *conn); 2998c2ecf20Sopenharmony_ciint smcr_cdc_msg_send_validation(struct smc_connection *conn, 3008c2ecf20Sopenharmony_ci struct smc_cdc_tx_pend *pend, 3018c2ecf20Sopenharmony_ci struct smc_wr_buf *wr_buf); 3028c2ecf20Sopenharmony_ciint smc_cdc_init(void) __init; 3038c2ecf20Sopenharmony_civoid smcd_cdc_rx_init(struct smc_connection *conn); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci#endif /* SMC_CDC_H */ 306