1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Shared Memory Communications over RDMA (SMC-R) and RoCE 4 * 5 * Connection Data Control (CDC) 6 * 7 * Copyright IBM Corp. 2016 8 * 9 * Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com> 10 */ 11 12 #ifndef SMC_CDC_H 13 #define SMC_CDC_H 14 15 #include <linux/kernel.h> /* max_t */ 16 #include <linux/atomic.h> 17 #include <linux/in.h> 18 #include <linux/compiler.h> 19 20 #include "smc.h" 21 #include "smc_core.h" 22 #include "smc_wr.h" 23 24 #define SMC_CDC_MSG_TYPE 0xFE 25 26 /* in network byte order */ 27 union smc_cdc_cursor { /* SMC cursor */ 28 struct { 29 __be16 reserved; 30 __be16 wrap; 31 __be32 count; 32 }; 33 #ifdef KERNEL_HAS_ATOMIC64 34 atomic64_t acurs; /* for atomic processing */ 35 #else 36 u64 acurs; /* for atomic processing */ 37 #endif 38 } __aligned(8); 39 40 /* in network byte order */ 41 struct smc_cdc_msg { 42 struct smc_wr_rx_hdr common; /* .type = 0xFE */ 43 u8 len; /* 44 */ 44 __be16 seqno; 45 __be32 token; 46 union smc_cdc_cursor prod; 47 union smc_cdc_cursor cons; /* piggy backed "ack" */ 48 struct smc_cdc_producer_flags prod_flags; 49 struct smc_cdc_conn_state_flags conn_state_flags; 50 u8 reserved[18]; 51 } __packed; /* format defined in RFC7609 */ 52 53 static inline bool smc_cdc_rxed_any_close(struct smc_connection *conn) 54 { 55 return conn->local_rx_ctrl.conn_state_flags.peer_conn_abort || 56 conn->local_rx_ctrl.conn_state_flags.peer_conn_closed; 57 } 58 59 static inline bool smc_cdc_rxed_any_close_or_senddone( 60 struct smc_connection *conn) 61 { 62 return smc_cdc_rxed_any_close(conn) || 63 conn->local_rx_ctrl.conn_state_flags.peer_done_writing; 64 } 65 66 static inline void smc_curs_add(int size, union smc_host_cursor *curs, 67 int value) 68 { 69 curs->count += value; 70 if (curs->count >= size) { 71 curs->wrap++; 72 curs->count -= size; 73 } 74 } 75 76 /* SMC cursors are 8 bytes long and require atomic reading and writing */ 77 static inline u64 smc_curs_read(union smc_host_cursor *curs, 78 struct smc_connection *conn) 79 { 80 #ifndef KERNEL_HAS_ATOMIC64 81 unsigned long flags; 82 u64 ret; 83 84 spin_lock_irqsave(&conn->acurs_lock, flags); 85 ret = curs->acurs; 86 spin_unlock_irqrestore(&conn->acurs_lock, flags); 87 return ret; 88 #else 89 return atomic64_read(&curs->acurs); 90 #endif 91 } 92 93 static inline u64 smc_curs_read_net(union smc_cdc_cursor *curs, 94 struct smc_connection *conn) 95 { 96 #ifndef KERNEL_HAS_ATOMIC64 97 unsigned long flags; 98 u64 ret; 99 100 spin_lock_irqsave(&conn->acurs_lock, flags); 101 ret = curs->acurs; 102 spin_unlock_irqrestore(&conn->acurs_lock, flags); 103 return ret; 104 #else 105 return atomic64_read(&curs->acurs); 106 #endif 107 } 108 109 static inline void smc_curs_write(union smc_host_cursor *curs, u64 val, 110 struct smc_connection *conn) 111 { 112 #ifndef KERNEL_HAS_ATOMIC64 113 unsigned long flags; 114 115 spin_lock_irqsave(&conn->acurs_lock, flags); 116 curs->acurs = val; 117 spin_unlock_irqrestore(&conn->acurs_lock, flags); 118 #else 119 atomic64_set(&curs->acurs, val); 120 #endif 121 } 122 123 static inline void smc_curs_write_net(union smc_cdc_cursor *curs, u64 val, 124 struct smc_connection *conn) 125 { 126 #ifndef KERNEL_HAS_ATOMIC64 127 unsigned long flags; 128 129 spin_lock_irqsave(&conn->acurs_lock, flags); 130 curs->acurs = val; 131 spin_unlock_irqrestore(&conn->acurs_lock, flags); 132 #else 133 atomic64_set(&curs->acurs, val); 134 #endif 135 } 136 137 /* calculate cursor difference between old and new, where old <= new */ 138 static inline int smc_curs_diff(unsigned int size, 139 union smc_host_cursor *old, 140 union smc_host_cursor *new) 141 { 142 if (old->wrap != new->wrap) 143 return max_t(int, 0, 144 ((size - old->count) + new->count)); 145 146 return max_t(int, 0, (new->count - old->count)); 147 } 148 149 /* calculate cursor difference between old and new - returns negative 150 * value in case old > new 151 */ 152 static inline int smc_curs_comp(unsigned int size, 153 union smc_host_cursor *old, 154 union smc_host_cursor *new) 155 { 156 if (old->wrap > new->wrap || 157 (old->wrap == new->wrap && old->count > new->count)) 158 return -smc_curs_diff(size, new, old); 159 return smc_curs_diff(size, old, new); 160 } 161 162 static inline void smc_host_cursor_to_cdc(union smc_cdc_cursor *peer, 163 union smc_host_cursor *local, 164 struct smc_connection *conn) 165 { 166 union smc_host_cursor temp; 167 168 smc_curs_write(&temp, smc_curs_read(local, conn), conn); 169 peer->count = htonl(temp.count); 170 peer->wrap = htons(temp.wrap); 171 /* peer->reserved = htons(0); must be ensured by caller */ 172 } 173 174 static inline void smc_host_msg_to_cdc(struct smc_cdc_msg *peer, 175 struct smc_host_cdc_msg *local, 176 struct smc_connection *conn) 177 { 178 peer->common.type = local->common.type; 179 peer->len = local->len; 180 peer->seqno = htons(local->seqno); 181 peer->token = htonl(local->token); 182 smc_host_cursor_to_cdc(&peer->prod, &local->prod, conn); 183 smc_host_cursor_to_cdc(&peer->cons, &local->cons, conn); 184 peer->prod_flags = local->prod_flags; 185 peer->conn_state_flags = local->conn_state_flags; 186 } 187 188 static inline void smc_cdc_cursor_to_host(union smc_host_cursor *local, 189 union smc_cdc_cursor *peer, 190 struct smc_connection *conn) 191 { 192 union smc_host_cursor temp, old; 193 union smc_cdc_cursor net; 194 195 smc_curs_write(&old, smc_curs_read(local, conn), conn); 196 smc_curs_write_net(&net, smc_curs_read_net(peer, conn), conn); 197 temp.count = ntohl(net.count); 198 temp.wrap = ntohs(net.wrap); 199 if ((old.wrap > temp.wrap) && temp.wrap) 200 return; 201 if ((old.wrap == temp.wrap) && 202 (old.count > temp.count)) 203 return; 204 smc_curs_write(local, smc_curs_read(&temp, conn), conn); 205 } 206 207 static inline void smc_cdc_msg_to_host(struct smc_host_cdc_msg *local, 208 struct smc_cdc_msg *peer, 209 struct smc_connection *conn) 210 { 211 local->common.type = peer->common.type; 212 local->len = peer->len; 213 local->seqno = ntohs(peer->seqno); 214 local->token = ntohl(peer->token); 215 smc_cdc_cursor_to_host(&local->prod, &peer->prod, conn); 216 smc_cdc_cursor_to_host(&local->cons, &peer->cons, conn); 217 local->prod_flags = peer->prod_flags; 218 local->conn_state_flags = peer->conn_state_flags; 219 } 220 221 struct smc_cdc_tx_pend; 222 223 int smc_cdc_get_free_slot(struct smc_connection *conn, 224 struct smc_wr_buf **wr_buf, 225 struct smc_cdc_tx_pend **pend); 226 void smc_cdc_tx_dismiss_slots(struct smc_connection *conn); 227 int smc_cdc_msg_send(struct smc_connection *conn, struct smc_wr_buf *wr_buf, 228 struct smc_cdc_tx_pend *pend); 229 int smc_cdc_get_slot_and_msg_send(struct smc_connection *conn); 230 int smc_cdc_init(void) __init; 231 232 #endif /* SMC_CDC_H */ 233