diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /net/smc/smc_cdc.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'net/smc/smc_cdc.c')
-rw-r--r-- | net/smc/smc_cdc.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c new file mode 100644 index 000000000..94503f36b --- /dev/null +++ b/net/smc/smc_cdc.c | |||
@@ -0,0 +1,476 @@ | |||
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 | * handles flow control | ||
7 | * | ||
8 | * Copyright IBM Corp. 2016 | ||
9 | * | ||
10 | * Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com> | ||
11 | */ | ||
12 | |||
13 | #include <linux/spinlock.h> | ||
14 | |||
15 | #include "smc.h" | ||
16 | #include "smc_wr.h" | ||
17 | #include "smc_cdc.h" | ||
18 | #include "smc_tx.h" | ||
19 | #include "smc_rx.h" | ||
20 | #include "smc_close.h" | ||
21 | |||
22 | /********************************** send *************************************/ | ||
23 | |||
24 | /* handler for send/transmission completion of a CDC msg */ | ||
25 | static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd, | ||
26 | struct smc_link *link, | ||
27 | enum ib_wc_status wc_status) | ||
28 | { | ||
29 | struct smc_cdc_tx_pend *cdcpend = (struct smc_cdc_tx_pend *)pnd_snd; | ||
30 | struct smc_connection *conn = cdcpend->conn; | ||
31 | struct smc_sock *smc; | ||
32 | int diff; | ||
33 | |||
34 | smc = container_of(conn, struct smc_sock, conn); | ||
35 | bh_lock_sock(&smc->sk); | ||
36 | if (!wc_status) { | ||
37 | diff = smc_curs_diff(cdcpend->conn->sndbuf_desc->len, | ||
38 | &cdcpend->conn->tx_curs_fin, | ||
39 | &cdcpend->cursor); | ||
40 | /* sndbuf_space is decreased in smc_sendmsg */ | ||
41 | smp_mb__before_atomic(); | ||
42 | atomic_add(diff, &cdcpend->conn->sndbuf_space); | ||
43 | /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */ | ||
44 | smp_mb__after_atomic(); | ||
45 | smc_curs_copy(&conn->tx_curs_fin, &cdcpend->cursor, conn); | ||
46 | smc_curs_copy(&conn->local_tx_ctrl_fin, &cdcpend->p_cursor, | ||
47 | conn); | ||
48 | conn->tx_cdc_seq_fin = cdcpend->ctrl_seq; | ||
49 | } | ||
50 | |||
51 | if (atomic_dec_and_test(&conn->cdc_pend_tx_wr) && | ||
52 | unlikely(wq_has_sleeper(&conn->cdc_pend_tx_wq))) | ||
53 | wake_up(&conn->cdc_pend_tx_wq); | ||
54 | WARN_ON(atomic_read(&conn->cdc_pend_tx_wr) < 0); | ||
55 | |||
56 | smc_tx_sndbuf_nonfull(smc); | ||
57 | bh_unlock_sock(&smc->sk); | ||
58 | } | ||
59 | |||
60 | int smc_cdc_get_free_slot(struct smc_connection *conn, | ||
61 | struct smc_link *link, | ||
62 | struct smc_wr_buf **wr_buf, | ||
63 | struct smc_rdma_wr **wr_rdma_buf, | ||
64 | struct smc_cdc_tx_pend **pend) | ||
65 | { | ||
66 | int rc; | ||
67 | |||
68 | rc = smc_wr_tx_get_free_slot(link, smc_cdc_tx_handler, wr_buf, | ||
69 | wr_rdma_buf, | ||
70 | (struct smc_wr_tx_pend_priv **)pend); | ||
71 | if (conn->killed) { | ||
72 | /* abnormal termination */ | ||
73 | if (!rc) | ||
74 | smc_wr_tx_put_slot(link, | ||
75 | (struct smc_wr_tx_pend_priv *)(*pend)); | ||
76 | rc = -EPIPE; | ||
77 | } | ||
78 | return rc; | ||
79 | } | ||
80 | |||
81 | static inline void smc_cdc_add_pending_send(struct smc_connection *conn, | ||
82 | struct smc_cdc_tx_pend *pend) | ||
83 | { | ||
84 | BUILD_BUG_ON_MSG( | ||
85 | sizeof(struct smc_cdc_msg) > SMC_WR_BUF_SIZE, | ||
86 | "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_cdc_msg)"); | ||
87 | BUILD_BUG_ON_MSG( | ||
88 | offsetofend(struct smc_cdc_msg, reserved) > SMC_WR_TX_SIZE, | ||
89 | "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()"); | ||
90 | BUILD_BUG_ON_MSG( | ||
91 | sizeof(struct smc_cdc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, | ||
92 | "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_cdc_tx_pend)"); | ||
93 | pend->conn = conn; | ||
94 | pend->cursor = conn->tx_curs_sent; | ||
95 | pend->p_cursor = conn->local_tx_ctrl.prod; | ||
96 | pend->ctrl_seq = conn->tx_cdc_seq; | ||
97 | } | ||
98 | |||
99 | int smc_cdc_msg_send(struct smc_connection *conn, | ||
100 | struct smc_wr_buf *wr_buf, | ||
101 | struct smc_cdc_tx_pend *pend) | ||
102 | { | ||
103 | struct smc_link *link = conn->lnk; | ||
104 | union smc_host_cursor cfed; | ||
105 | int rc; | ||
106 | |||
107 | smc_cdc_add_pending_send(conn, pend); | ||
108 | |||
109 | conn->tx_cdc_seq++; | ||
110 | conn->local_tx_ctrl.seqno = conn->tx_cdc_seq; | ||
111 | smc_host_msg_to_cdc((struct smc_cdc_msg *)wr_buf, conn, &cfed); | ||
112 | |||
113 | atomic_inc(&conn->cdc_pend_tx_wr); | ||
114 | smp_mb__after_atomic(); /* Make sure cdc_pend_tx_wr added before post */ | ||
115 | |||
116 | rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend); | ||
117 | if (!rc) { | ||
118 | smc_curs_copy(&conn->rx_curs_confirmed, &cfed, conn); | ||
119 | conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0; | ||
120 | } else { | ||
121 | conn->tx_cdc_seq--; | ||
122 | conn->local_tx_ctrl.seqno = conn->tx_cdc_seq; | ||
123 | atomic_dec(&conn->cdc_pend_tx_wr); | ||
124 | } | ||
125 | |||
126 | return rc; | ||
127 | } | ||
128 | |||
129 | /* send a validation msg indicating the move of a conn to an other QP link */ | ||
130 | int smcr_cdc_msg_send_validation(struct smc_connection *conn, | ||
131 | struct smc_cdc_tx_pend *pend, | ||
132 | struct smc_wr_buf *wr_buf) | ||
133 | { | ||
134 | struct smc_host_cdc_msg *local = &conn->local_tx_ctrl; | ||
135 | struct smc_link *link = conn->lnk; | ||
136 | struct smc_cdc_msg *peer; | ||
137 | int rc; | ||
138 | |||
139 | peer = (struct smc_cdc_msg *)wr_buf; | ||
140 | peer->common.type = local->common.type; | ||
141 | peer->len = local->len; | ||
142 | peer->seqno = htons(conn->tx_cdc_seq_fin); /* seqno last compl. tx */ | ||
143 | peer->token = htonl(local->token); | ||
144 | peer->prod_flags.failover_validation = 1; | ||
145 | |||
146 | /* We need to set pend->conn here to make sure smc_cdc_tx_handler() | ||
147 | * can handle properly | ||
148 | */ | ||
149 | smc_cdc_add_pending_send(conn, pend); | ||
150 | |||
151 | atomic_inc(&conn->cdc_pend_tx_wr); | ||
152 | smp_mb__after_atomic(); /* Make sure cdc_pend_tx_wr added before post */ | ||
153 | |||
154 | rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend); | ||
155 | if (unlikely(rc)) | ||
156 | atomic_dec(&conn->cdc_pend_tx_wr); | ||
157 | |||
158 | return rc; | ||
159 | } | ||
160 | |||
161 | static int smcr_cdc_get_slot_and_msg_send(struct smc_connection *conn) | ||
162 | { | ||
163 | struct smc_cdc_tx_pend *pend; | ||
164 | struct smc_wr_buf *wr_buf; | ||
165 | struct smc_link *link; | ||
166 | bool again = false; | ||
167 | int rc; | ||
168 | |||
169 | again: | ||
170 | link = conn->lnk; | ||
171 | if (!smc_wr_tx_link_hold(link)) | ||
172 | return -ENOLINK; | ||
173 | rc = smc_cdc_get_free_slot(conn, link, &wr_buf, NULL, &pend); | ||
174 | if (rc) | ||
175 | goto put_out; | ||
176 | |||
177 | spin_lock_bh(&conn->send_lock); | ||
178 | if (link != conn->lnk) { | ||
179 | /* link of connection changed, try again one time*/ | ||
180 | spin_unlock_bh(&conn->send_lock); | ||
181 | smc_wr_tx_put_slot(link, | ||
182 | (struct smc_wr_tx_pend_priv *)pend); | ||
183 | smc_wr_tx_link_put(link); | ||
184 | if (again) | ||
185 | return -ENOLINK; | ||
186 | again = true; | ||
187 | goto again; | ||
188 | } | ||
189 | rc = smc_cdc_msg_send(conn, wr_buf, pend); | ||
190 | spin_unlock_bh(&conn->send_lock); | ||
191 | put_out: | ||
192 | smc_wr_tx_link_put(link); | ||
193 | return rc; | ||
194 | } | ||
195 | |||
196 | int smc_cdc_get_slot_and_msg_send(struct smc_connection *conn) | ||
197 | { | ||
198 | int rc; | ||
199 | |||
200 | if (!conn->lgr || (conn->lgr->is_smcd && conn->lgr->peer_shutdown)) | ||
201 | return -EPIPE; | ||
202 | |||
203 | if (conn->lgr->is_smcd) { | ||
204 | spin_lock_bh(&conn->send_lock); | ||
205 | rc = smcd_cdc_msg_send(conn); | ||
206 | spin_unlock_bh(&conn->send_lock); | ||
207 | } else { | ||
208 | rc = smcr_cdc_get_slot_and_msg_send(conn); | ||
209 | } | ||
210 | |||
211 | return rc; | ||
212 | } | ||
213 | |||
214 | void smc_cdc_wait_pend_tx_wr(struct smc_connection *conn) | ||
215 | { | ||
216 | wait_event(conn->cdc_pend_tx_wq, !atomic_read(&conn->cdc_pend_tx_wr)); | ||
217 | } | ||
218 | |||
219 | /* Send a SMC-D CDC header. | ||
220 | * This increments the free space available in our send buffer. | ||
221 | * Also update the confirmed receive buffer with what was sent to the peer. | ||
222 | */ | ||
223 | int smcd_cdc_msg_send(struct smc_connection *conn) | ||
224 | { | ||
225 | struct smc_sock *smc = container_of(conn, struct smc_sock, conn); | ||
226 | union smc_host_cursor curs; | ||
227 | struct smcd_cdc_msg cdc; | ||
228 | int rc, diff; | ||
229 | |||
230 | memset(&cdc, 0, sizeof(cdc)); | ||
231 | cdc.common.type = SMC_CDC_MSG_TYPE; | ||
232 | curs.acurs.counter = atomic64_read(&conn->local_tx_ctrl.prod.acurs); | ||
233 | cdc.prod.wrap = curs.wrap; | ||
234 | cdc.prod.count = curs.count; | ||
235 | curs.acurs.counter = atomic64_read(&conn->local_tx_ctrl.cons.acurs); | ||
236 | cdc.cons.wrap = curs.wrap; | ||
237 | cdc.cons.count = curs.count; | ||
238 | cdc.cons.prod_flags = conn->local_tx_ctrl.prod_flags; | ||
239 | cdc.cons.conn_state_flags = conn->local_tx_ctrl.conn_state_flags; | ||
240 | rc = smcd_tx_ism_write(conn, &cdc, sizeof(cdc), 0, 1); | ||
241 | if (rc) | ||
242 | return rc; | ||
243 | smc_curs_copy(&conn->rx_curs_confirmed, &curs, conn); | ||
244 | conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0; | ||
245 | /* Calculate transmitted data and increment free send buffer space */ | ||
246 | diff = smc_curs_diff(conn->sndbuf_desc->len, &conn->tx_curs_fin, | ||
247 | &conn->tx_curs_sent); | ||
248 | /* increased by confirmed number of bytes */ | ||
249 | smp_mb__before_atomic(); | ||
250 | atomic_add(diff, &conn->sndbuf_space); | ||
251 | /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */ | ||
252 | smp_mb__after_atomic(); | ||
253 | smc_curs_copy(&conn->tx_curs_fin, &conn->tx_curs_sent, conn); | ||
254 | |||
255 | smc_tx_sndbuf_nonfull(smc); | ||
256 | return rc; | ||
257 | } | ||
258 | |||
259 | /********************************* receive ***********************************/ | ||
260 | |||
261 | static inline bool smc_cdc_before(u16 seq1, u16 seq2) | ||
262 | { | ||
263 | return (s16)(seq1 - seq2) < 0; | ||
264 | } | ||
265 | |||
266 | static void smc_cdc_handle_urg_data_arrival(struct smc_sock *smc, | ||
267 | int *diff_prod) | ||
268 | { | ||
269 | struct smc_connection *conn = &smc->conn; | ||
270 | char *base; | ||
271 | |||
272 | /* new data included urgent business */ | ||
273 | smc_curs_copy(&conn->urg_curs, &conn->local_rx_ctrl.prod, conn); | ||
274 | conn->urg_state = SMC_URG_VALID; | ||
275 | if (!sock_flag(&smc->sk, SOCK_URGINLINE)) | ||
276 | /* we'll skip the urgent byte, so don't account for it */ | ||
277 | (*diff_prod)--; | ||
278 | base = (char *)conn->rmb_desc->cpu_addr + conn->rx_off; | ||
279 | if (conn->urg_curs.count) | ||
280 | conn->urg_rx_byte = *(base + conn->urg_curs.count - 1); | ||
281 | else | ||
282 | conn->urg_rx_byte = *(base + conn->rmb_desc->len - 1); | ||
283 | sk_send_sigurg(&smc->sk); | ||
284 | } | ||
285 | |||
286 | static void smc_cdc_msg_validate(struct smc_sock *smc, struct smc_cdc_msg *cdc, | ||
287 | struct smc_link *link) | ||
288 | { | ||
289 | struct smc_connection *conn = &smc->conn; | ||
290 | u16 recv_seq = ntohs(cdc->seqno); | ||
291 | s16 diff; | ||
292 | |||
293 | /* check that seqnum was seen before */ | ||
294 | diff = conn->local_rx_ctrl.seqno - recv_seq; | ||
295 | if (diff < 0) { /* diff larger than 0x7fff */ | ||
296 | /* drop connection */ | ||
297 | conn->out_of_sync = 1; /* prevent any further receives */ | ||
298 | spin_lock_bh(&conn->send_lock); | ||
299 | conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; | ||
300 | conn->lnk = link; | ||
301 | spin_unlock_bh(&conn->send_lock); | ||
302 | sock_hold(&smc->sk); /* sock_put in abort_work */ | ||
303 | if (!queue_work(smc_close_wq, &conn->abort_work)) | ||
304 | sock_put(&smc->sk); | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void smc_cdc_msg_recv_action(struct smc_sock *smc, | ||
309 | struct smc_cdc_msg *cdc) | ||
310 | { | ||
311 | union smc_host_cursor cons_old, prod_old; | ||
312 | struct smc_connection *conn = &smc->conn; | ||
313 | int diff_cons, diff_prod; | ||
314 | |||
315 | smc_curs_copy(&prod_old, &conn->local_rx_ctrl.prod, conn); | ||
316 | smc_curs_copy(&cons_old, &conn->local_rx_ctrl.cons, conn); | ||
317 | smc_cdc_msg_to_host(&conn->local_rx_ctrl, cdc, conn); | ||
318 | |||
319 | diff_cons = smc_curs_diff(conn->peer_rmbe_size, &cons_old, | ||
320 | &conn->local_rx_ctrl.cons); | ||
321 | if (diff_cons) { | ||
322 | /* peer_rmbe_space is decreased during data transfer with RDMA | ||
323 | * write | ||
324 | */ | ||
325 | smp_mb__before_atomic(); | ||
326 | atomic_add(diff_cons, &conn->peer_rmbe_space); | ||
327 | /* guarantee 0 <= peer_rmbe_space <= peer_rmbe_size */ | ||
328 | smp_mb__after_atomic(); | ||
329 | } | ||
330 | |||
331 | diff_prod = smc_curs_diff(conn->rmb_desc->len, &prod_old, | ||
332 | &conn->local_rx_ctrl.prod); | ||
333 | if (diff_prod) { | ||
334 | if (conn->local_rx_ctrl.prod_flags.urg_data_present) | ||
335 | smc_cdc_handle_urg_data_arrival(smc, &diff_prod); | ||
336 | /* bytes_to_rcv is decreased in smc_recvmsg */ | ||
337 | smp_mb__before_atomic(); | ||
338 | atomic_add(diff_prod, &conn->bytes_to_rcv); | ||
339 | /* guarantee 0 <= bytes_to_rcv <= rmb_desc->len */ | ||
340 | smp_mb__after_atomic(); | ||
341 | smc->sk.sk_data_ready(&smc->sk); | ||
342 | } else { | ||
343 | if (conn->local_rx_ctrl.prod_flags.write_blocked) | ||
344 | smc->sk.sk_data_ready(&smc->sk); | ||
345 | if (conn->local_rx_ctrl.prod_flags.urg_data_pending) | ||
346 | conn->urg_state = SMC_URG_NOTYET; | ||
347 | } | ||
348 | |||
349 | /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */ | ||
350 | if ((diff_cons && smc_tx_prepared_sends(conn)) || | ||
351 | conn->local_rx_ctrl.prod_flags.cons_curs_upd_req || | ||
352 | conn->local_rx_ctrl.prod_flags.urg_data_pending) | ||
353 | smc_tx_sndbuf_nonempty(conn); | ||
354 | |||
355 | if (diff_cons && conn->urg_tx_pend && | ||
356 | atomic_read(&conn->peer_rmbe_space) == conn->peer_rmbe_size) { | ||
357 | /* urg data confirmed by peer, indicate we're ready for more */ | ||
358 | conn->urg_tx_pend = false; | ||
359 | smc->sk.sk_write_space(&smc->sk); | ||
360 | } | ||
361 | |||
362 | if (conn->local_rx_ctrl.conn_state_flags.peer_conn_abort) { | ||
363 | smc->sk.sk_err = ECONNRESET; | ||
364 | conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; | ||
365 | } | ||
366 | if (smc_cdc_rxed_any_close_or_senddone(conn)) { | ||
367 | smc->sk.sk_shutdown |= RCV_SHUTDOWN; | ||
368 | if (smc->clcsock && smc->clcsock->sk) | ||
369 | smc->clcsock->sk->sk_shutdown |= RCV_SHUTDOWN; | ||
370 | sock_set_flag(&smc->sk, SOCK_DONE); | ||
371 | sock_hold(&smc->sk); /* sock_put in close_work */ | ||
372 | if (!queue_work(smc_close_wq, &conn->close_work)) | ||
373 | sock_put(&smc->sk); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | /* called under tasklet context */ | ||
378 | static void smc_cdc_msg_recv(struct smc_sock *smc, struct smc_cdc_msg *cdc) | ||
379 | { | ||
380 | sock_hold(&smc->sk); | ||
381 | bh_lock_sock(&smc->sk); | ||
382 | smc_cdc_msg_recv_action(smc, cdc); | ||
383 | bh_unlock_sock(&smc->sk); | ||
384 | sock_put(&smc->sk); /* no free sk in softirq-context */ | ||
385 | } | ||
386 | |||
387 | /* Schedule a tasklet for this connection. Triggered from the ISM device IRQ | ||
388 | * handler to indicate update in the DMBE. | ||
389 | * | ||
390 | * Context: | ||
391 | * - tasklet context | ||
392 | */ | ||
393 | static void smcd_cdc_rx_tsklet(unsigned long data) | ||
394 | { | ||
395 | struct smc_connection *conn = (struct smc_connection *)data; | ||
396 | struct smcd_cdc_msg *data_cdc; | ||
397 | struct smcd_cdc_msg cdc; | ||
398 | struct smc_sock *smc; | ||
399 | |||
400 | if (!conn || conn->killed) | ||
401 | return; | ||
402 | |||
403 | data_cdc = (struct smcd_cdc_msg *)conn->rmb_desc->cpu_addr; | ||
404 | smcd_curs_copy(&cdc.prod, &data_cdc->prod, conn); | ||
405 | smcd_curs_copy(&cdc.cons, &data_cdc->cons, conn); | ||
406 | smc = container_of(conn, struct smc_sock, conn); | ||
407 | smc_cdc_msg_recv(smc, (struct smc_cdc_msg *)&cdc); | ||
408 | } | ||
409 | |||
410 | /* Initialize receive tasklet. Called from ISM device IRQ handler to start | ||
411 | * receiver side. | ||
412 | */ | ||
413 | void smcd_cdc_rx_init(struct smc_connection *conn) | ||
414 | { | ||
415 | tasklet_init(&conn->rx_tsklet, smcd_cdc_rx_tsklet, (unsigned long)conn); | ||
416 | } | ||
417 | |||
418 | /***************************** init, exit, misc ******************************/ | ||
419 | |||
420 | static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf) | ||
421 | { | ||
422 | struct smc_link *link = (struct smc_link *)wc->qp->qp_context; | ||
423 | struct smc_cdc_msg *cdc = buf; | ||
424 | struct smc_connection *conn; | ||
425 | struct smc_link_group *lgr; | ||
426 | struct smc_sock *smc; | ||
427 | |||
428 | if (wc->byte_len < offsetof(struct smc_cdc_msg, reserved)) | ||
429 | return; /* short message */ | ||
430 | if (cdc->len != SMC_WR_TX_SIZE) | ||
431 | return; /* invalid message */ | ||
432 | |||
433 | /* lookup connection */ | ||
434 | lgr = smc_get_lgr(link); | ||
435 | read_lock_bh(&lgr->conns_lock); | ||
436 | conn = smc_lgr_find_conn(ntohl(cdc->token), lgr); | ||
437 | read_unlock_bh(&lgr->conns_lock); | ||
438 | if (!conn || conn->out_of_sync) | ||
439 | return; | ||
440 | smc = container_of(conn, struct smc_sock, conn); | ||
441 | |||
442 | if (cdc->prod_flags.failover_validation) { | ||
443 | smc_cdc_msg_validate(smc, cdc, link); | ||
444 | return; | ||
445 | } | ||
446 | if (smc_cdc_before(ntohs(cdc->seqno), | ||
447 | conn->local_rx_ctrl.seqno)) | ||
448 | /* received seqno is old */ | ||
449 | return; | ||
450 | |||
451 | smc_cdc_msg_recv(smc, cdc); | ||
452 | } | ||
453 | |||
454 | static struct smc_wr_rx_handler smc_cdc_rx_handlers[] = { | ||
455 | { | ||
456 | .handler = smc_cdc_rx_handler, | ||
457 | .type = SMC_CDC_MSG_TYPE | ||
458 | }, | ||
459 | { | ||
460 | .handler = NULL, | ||
461 | } | ||
462 | }; | ||
463 | |||
464 | int __init smc_cdc_init(void) | ||
465 | { | ||
466 | struct smc_wr_rx_handler *handler; | ||
467 | int rc = 0; | ||
468 | |||
469 | for (handler = smc_cdc_rx_handlers; handler->handler; handler++) { | ||
470 | INIT_HLIST_NODE(&handler->list); | ||
471 | rc = smc_wr_rx_register_handler(handler); | ||
472 | if (rc) | ||
473 | break; | ||
474 | } | ||
475 | return rc; | ||
476 | } | ||