]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
9bf9abea UB |
2 | /* |
3 | * Shared Memory Communications over RDMA (SMC-R) and RoCE | |
4 | * | |
5 | * Link Layer Control (LLC) | |
6 | * | |
9bf9abea UB |
7 | * Copyright IBM Corp. 2016 |
8 | * | |
9 | * Author(s): Klaus Wacker <[email protected]> | |
10 | * Ursula Braun <[email protected]> | |
11 | */ | |
12 | ||
13 | #include <net/tcp.h> | |
14 | #include <rdma/ib_verbs.h> | |
15 | ||
16 | #include "smc.h" | |
17 | #include "smc_core.h" | |
18 | #include "smc_clc.h" | |
19 | #include "smc_llc.h" | |
20 | ||
0f627126 SR |
21 | #define SMC_LLC_DATA_LEN 40 |
22 | ||
23 | struct smc_llc_hdr { | |
24 | struct smc_wr_rx_hdr common; | |
25 | u8 length; /* 44 */ | |
52bedf37 KG |
26 | #if defined(__BIG_ENDIAN_BITFIELD) |
27 | u8 reserved:4, | |
28 | add_link_rej_rsn:4; | |
29 | #elif defined(__LITTLE_ENDIAN_BITFIELD) | |
30 | u8 add_link_rej_rsn:4, | |
31 | reserved:4; | |
32 | #endif | |
0f627126 SR |
33 | u8 flags; |
34 | }; | |
35 | ||
75d320d6 KG |
36 | #define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 |
37 | ||
0f627126 SR |
38 | struct smc_llc_msg_confirm_link { /* type 0x01 */ |
39 | struct smc_llc_hdr hd; | |
40 | u8 sender_mac[ETH_ALEN]; | |
41 | u8 sender_gid[SMC_GID_SIZE]; | |
42 | u8 sender_qp_num[3]; | |
43 | u8 link_num; | |
44 | u8 link_uid[SMC_LGR_ID_SIZE]; | |
45 | u8 max_links; | |
46 | u8 reserved[9]; | |
47 | }; | |
48 | ||
52bedf37 KG |
49 | #define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 |
50 | #define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 | |
51 | ||
52 | #define SMC_LLC_ADD_LNK_MAX_LINKS 2 | |
53 | ||
54 | struct smc_llc_msg_add_link { /* type 0x02 */ | |
55 | struct smc_llc_hdr hd; | |
56 | u8 sender_mac[ETH_ALEN]; | |
57 | u8 reserved2[2]; | |
58 | u8 sender_gid[SMC_GID_SIZE]; | |
59 | u8 sender_qp_num[3]; | |
60 | u8 link_num; | |
61 | u8 flags2; /* QP mtu */ | |
62 | u8 initial_psn[3]; | |
63 | u8 reserved[8]; | |
64 | }; | |
65 | ||
66 | #define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 | |
67 | #define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 | |
68 | ||
69 | struct smc_llc_msg_del_link { /* type 0x04 */ | |
70 | struct smc_llc_hdr hd; | |
71 | u8 link_num; | |
72 | __be32 reason; | |
73 | u8 reserved[35]; | |
74 | } __packed; /* format defined in RFC7609 */ | |
75 | ||
313164da KG |
76 | struct smc_llc_msg_test_link { /* type 0x07 */ |
77 | struct smc_llc_hdr hd; | |
78 | u8 user_data[16]; | |
79 | u8 reserved[24]; | |
80 | }; | |
81 | ||
4ed75de5 KG |
82 | struct smc_rmb_rtoken { |
83 | union { | |
84 | u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ | |
85 | /* is actually the num of rtokens, first */ | |
86 | /* rtoken is always for the current link */ | |
87 | u8 link_id; /* link id of the rtoken */ | |
88 | }; | |
89 | __be32 rmb_key; | |
90 | __be64 rmb_vaddr; | |
91 | } __packed; /* format defined in RFC7609 */ | |
92 | ||
93 | #define SMC_LLC_RKEYS_PER_MSG 3 | |
94 | ||
95 | struct smc_llc_msg_confirm_rkey { /* type 0x06 */ | |
96 | struct smc_llc_hdr hd; | |
97 | struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; | |
98 | u8 reserved; | |
99 | }; | |
100 | ||
101 | struct smc_llc_msg_confirm_rkey_cont { /* type 0x08 */ | |
102 | struct smc_llc_hdr hd; | |
103 | u8 num_rkeys; | |
104 | struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; | |
105 | }; | |
106 | ||
107 | #define SMC_LLC_DEL_RKEY_MAX 8 | |
108 | #define SMC_LLC_FLAG_RKEY_NEG 0x20 | |
109 | ||
110 | struct smc_llc_msg_delete_rkey { /* type 0x09 */ | |
111 | struct smc_llc_hdr hd; | |
112 | u8 num_rkeys; | |
113 | u8 err_mask; | |
114 | u8 reserved[2]; | |
115 | __be32 rkey[8]; | |
116 | u8 reserved2[4]; | |
117 | }; | |
118 | ||
0f627126 SR |
119 | union smc_llc_msg { |
120 | struct smc_llc_msg_confirm_link confirm_link; | |
52bedf37 KG |
121 | struct smc_llc_msg_add_link add_link; |
122 | struct smc_llc_msg_del_link delete_link; | |
4ed75de5 KG |
123 | |
124 | struct smc_llc_msg_confirm_rkey confirm_rkey; | |
125 | struct smc_llc_msg_confirm_rkey_cont confirm_rkey_cont; | |
126 | struct smc_llc_msg_delete_rkey delete_rkey; | |
127 | ||
313164da | 128 | struct smc_llc_msg_test_link test_link; |
0f627126 SR |
129 | struct { |
130 | struct smc_llc_hdr hdr; | |
131 | u8 data[SMC_LLC_DATA_LEN]; | |
132 | } raw; | |
133 | }; | |
134 | ||
135 | #define SMC_LLC_FLAG_RESP 0x80 | |
136 | ||
9bf9abea UB |
137 | /********************************** send *************************************/ |
138 | ||
139 | struct smc_llc_tx_pend { | |
140 | }; | |
141 | ||
142 | /* handler for send/transmission completion of an LLC msg */ | |
143 | static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, | |
144 | struct smc_link *link, | |
145 | enum ib_wc_status wc_status) | |
146 | { | |
147 | /* future work: handle wc_status error for recovery and failover */ | |
148 | } | |
149 | ||
150 | /** | |
151 | * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits | |
152 | * @link: Pointer to SMC link used for sending LLC control message. | |
153 | * @wr_buf: Out variable returning pointer to work request payload buffer. | |
154 | * @pend: Out variable returning pointer to private pending WR tracking. | |
155 | * It's the context the transmit complete handler will get. | |
156 | * | |
157 | * Reserves and pre-fills an entry for a pending work request send/tx. | |
158 | * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. | |
159 | * Can sleep due to smc_get_ctrl_buf (if not in softirq context). | |
160 | * | |
161 | * Return: 0 on success, otherwise an error value. | |
162 | */ | |
163 | static int smc_llc_add_pending_send(struct smc_link *link, | |
164 | struct smc_wr_buf **wr_buf, | |
165 | struct smc_wr_tx_pend_priv **pend) | |
166 | { | |
167 | int rc; | |
168 | ||
169 | rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, pend); | |
170 | if (rc < 0) | |
171 | return rc; | |
172 | BUILD_BUG_ON_MSG( | |
173 | sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, | |
174 | "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); | |
175 | BUILD_BUG_ON_MSG( | |
176 | sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, | |
177 | "must adapt SMC_WR_TX_SIZE to sizeof(struct smc_llc_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()"); | |
178 | BUILD_BUG_ON_MSG( | |
179 | sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, | |
180 | "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); | |
181 | return 0; | |
182 | } | |
183 | ||
184 | /* high-level API to send LLC confirm link */ | |
947541f3 | 185 | int smc_llc_send_confirm_link(struct smc_link *link, |
9bf9abea UB |
186 | enum smc_llc_reqresp reqresp) |
187 | { | |
00e5fb26 | 188 | struct smc_link_group *lgr = smc_get_lgr(link); |
9bf9abea UB |
189 | struct smc_llc_msg_confirm_link *confllc; |
190 | struct smc_wr_tx_pend_priv *pend; | |
191 | struct smc_wr_buf *wr_buf; | |
192 | int rc; | |
193 | ||
194 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
195 | if (rc) | |
196 | return rc; | |
197 | confllc = (struct smc_llc_msg_confirm_link *)wr_buf; | |
198 | memset(confllc, 0, sizeof(*confllc)); | |
199 | confllc->hd.common.type = SMC_LLC_CONFIRM_LINK; | |
200 | confllc->hd.length = sizeof(struct smc_llc_msg_confirm_link); | |
75d320d6 | 201 | confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; |
9bf9abea UB |
202 | if (reqresp == SMC_LLC_RESP) |
203 | confllc->hd.flags |= SMC_LLC_FLAG_RESP; | |
947541f3 UB |
204 | memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], |
205 | ETH_ALEN); | |
7005ada6 | 206 | memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); |
9bf9abea | 207 | hton24(confllc->sender_qp_num, link->roce_qp->qp_num); |
2be922f3 | 208 | confllc->link_num = link->link_id; |
9bf9abea | 209 | memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE); |
52bedf37 KG |
210 | confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; /* enforce peer resp. */ |
211 | /* send llc message */ | |
212 | rc = smc_wr_tx_send(link, pend); | |
213 | return rc; | |
214 | } | |
215 | ||
44aa81ce KG |
216 | /* send LLC confirm rkey request */ |
217 | static int smc_llc_send_confirm_rkey(struct smc_link *link, | |
218 | struct smc_buf_desc *rmb_desc) | |
219 | { | |
220 | struct smc_llc_msg_confirm_rkey *rkeyllc; | |
221 | struct smc_wr_tx_pend_priv *pend; | |
222 | struct smc_wr_buf *wr_buf; | |
223 | int rc; | |
224 | ||
225 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
226 | if (rc) | |
227 | return rc; | |
228 | rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; | |
229 | memset(rkeyllc, 0, sizeof(*rkeyllc)); | |
230 | rkeyllc->hd.common.type = SMC_LLC_CONFIRM_RKEY; | |
231 | rkeyllc->hd.length = sizeof(struct smc_llc_msg_confirm_rkey); | |
232 | rkeyllc->rtoken[0].rmb_key = | |
233 | htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey); | |
234 | rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64( | |
235 | (u64)sg_dma_address(rmb_desc->sgt[SMC_SINGLE_LINK].sgl)); | |
236 | /* send llc message */ | |
237 | rc = smc_wr_tx_send(link, pend); | |
238 | return rc; | |
239 | } | |
240 | ||
2a4c57a9 KG |
241 | /* prepare an add link message */ |
242 | static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc, | |
7005ada6 | 243 | struct smc_link *link, u8 mac[], u8 gid[], |
2a4c57a9 KG |
244 | enum smc_llc_reqresp reqresp) |
245 | { | |
246 | memset(addllc, 0, sizeof(*addllc)); | |
247 | addllc->hd.common.type = SMC_LLC_ADD_LINK; | |
248 | addllc->hd.length = sizeof(struct smc_llc_msg_add_link); | |
249 | if (reqresp == SMC_LLC_RESP) { | |
250 | addllc->hd.flags |= SMC_LLC_FLAG_RESP; | |
251 | /* always reject more links for now */ | |
252 | addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; | |
253 | addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; | |
254 | } | |
255 | memcpy(addllc->sender_mac, mac, ETH_ALEN); | |
256 | memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); | |
257 | } | |
258 | ||
52bedf37 | 259 | /* send ADD LINK request or response */ |
7005ada6 | 260 | int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], |
52bedf37 KG |
261 | enum smc_llc_reqresp reqresp) |
262 | { | |
263 | struct smc_llc_msg_add_link *addllc; | |
264 | struct smc_wr_tx_pend_priv *pend; | |
265 | struct smc_wr_buf *wr_buf; | |
266 | int rc; | |
267 | ||
268 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
269 | if (rc) | |
270 | return rc; | |
271 | addllc = (struct smc_llc_msg_add_link *)wr_buf; | |
2a4c57a9 | 272 | smc_llc_prep_add_link(addllc, link, mac, gid, reqresp); |
52bedf37 KG |
273 | /* send llc message */ |
274 | rc = smc_wr_tx_send(link, pend); | |
275 | return rc; | |
276 | } | |
277 | ||
2a4c57a9 KG |
278 | /* prepare a delete link message */ |
279 | static void smc_llc_prep_delete_link(struct smc_llc_msg_del_link *delllc, | |
280 | struct smc_link *link, | |
0d18a0cb | 281 | enum smc_llc_reqresp reqresp, bool orderly) |
2a4c57a9 KG |
282 | { |
283 | memset(delllc, 0, sizeof(*delllc)); | |
284 | delllc->hd.common.type = SMC_LLC_DELETE_LINK; | |
285 | delllc->hd.length = sizeof(struct smc_llc_msg_add_link); | |
286 | if (reqresp == SMC_LLC_RESP) | |
287 | delllc->hd.flags |= SMC_LLC_FLAG_RESP; | |
288 | /* DEL_LINK_ALL because only 1 link supported */ | |
289 | delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; | |
0d18a0cb KG |
290 | if (orderly) |
291 | delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; | |
2a4c57a9 KG |
292 | delllc->link_num = link->link_id; |
293 | } | |
294 | ||
52bedf37 KG |
295 | /* send DELETE LINK request or response */ |
296 | int smc_llc_send_delete_link(struct smc_link *link, | |
0d18a0cb | 297 | enum smc_llc_reqresp reqresp, bool orderly) |
52bedf37 KG |
298 | { |
299 | struct smc_llc_msg_del_link *delllc; | |
300 | struct smc_wr_tx_pend_priv *pend; | |
301 | struct smc_wr_buf *wr_buf; | |
302 | int rc; | |
303 | ||
304 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
305 | if (rc) | |
306 | return rc; | |
307 | delllc = (struct smc_llc_msg_del_link *)wr_buf; | |
0d18a0cb | 308 | smc_llc_prep_delete_link(delllc, link, reqresp, orderly); |
9bf9abea UB |
309 | /* send llc message */ |
310 | rc = smc_wr_tx_send(link, pend); | |
311 | return rc; | |
312 | } | |
313 | ||
d97935fa KG |
314 | /* send LLC test link request */ |
315 | static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) | |
313164da KG |
316 | { |
317 | struct smc_llc_msg_test_link *testllc; | |
318 | struct smc_wr_tx_pend_priv *pend; | |
319 | struct smc_wr_buf *wr_buf; | |
320 | int rc; | |
321 | ||
322 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
323 | if (rc) | |
324 | return rc; | |
325 | testllc = (struct smc_llc_msg_test_link *)wr_buf; | |
326 | memset(testllc, 0, sizeof(*testllc)); | |
327 | testllc->hd.common.type = SMC_LLC_TEST_LINK; | |
328 | testllc->hd.length = sizeof(struct smc_llc_msg_test_link); | |
313164da KG |
329 | memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); |
330 | /* send llc message */ | |
331 | rc = smc_wr_tx_send(link, pend); | |
332 | return rc; | |
333 | } | |
334 | ||
2a4c57a9 KG |
335 | struct smc_llc_send_work { |
336 | struct work_struct work; | |
337 | struct smc_link *link; | |
338 | int llclen; | |
339 | union smc_llc_msg llcbuf; | |
340 | }; | |
341 | ||
342 | /* worker that sends a prepared message */ | |
343 | static void smc_llc_send_message_work(struct work_struct *work) | |
4ed75de5 | 344 | { |
2a4c57a9 KG |
345 | struct smc_llc_send_work *llcwrk = container_of(work, |
346 | struct smc_llc_send_work, work); | |
4ed75de5 KG |
347 | struct smc_wr_tx_pend_priv *pend; |
348 | struct smc_wr_buf *wr_buf; | |
349 | int rc; | |
350 | ||
2a4c57a9 KG |
351 | if (llcwrk->link->state == SMC_LNK_INACTIVE) |
352 | goto out; | |
353 | rc = smc_llc_add_pending_send(llcwrk->link, &wr_buf, &pend); | |
4ed75de5 | 354 | if (rc) |
2a4c57a9 KG |
355 | goto out; |
356 | memcpy(wr_buf, &llcwrk->llcbuf, llcwrk->llclen); | |
357 | smc_wr_tx_send(llcwrk->link, pend); | |
358 | out: | |
359 | kfree(llcwrk); | |
360 | } | |
361 | ||
362 | /* copy llcbuf and schedule an llc send on link */ | |
363 | static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen) | |
364 | { | |
365 | struct smc_llc_send_work *wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC); | |
366 | ||
367 | if (!wrk) | |
368 | return -ENOMEM; | |
369 | INIT_WORK(&wrk->work, smc_llc_send_message_work); | |
370 | wrk->link = link; | |
371 | wrk->llclen = llclen; | |
372 | memcpy(&wrk->llcbuf, llcbuf, llclen); | |
373 | queue_work(link->llc_wq, &wrk->work); | |
374 | return 0; | |
4ed75de5 KG |
375 | } |
376 | ||
9bf9abea UB |
377 | /********************************* receive ***********************************/ |
378 | ||
379 | static void smc_llc_rx_confirm_link(struct smc_link *link, | |
380 | struct smc_llc_msg_confirm_link *llc) | |
381 | { | |
00e5fb26 | 382 | struct smc_link_group *lgr = smc_get_lgr(link); |
75d320d6 | 383 | int conf_rc; |
9bf9abea | 384 | |
75d320d6 KG |
385 | /* RMBE eyecatchers are not supported */ |
386 | if (llc->hd.flags & SMC_LLC_FLAG_NO_RMBE_EYEC) | |
387 | conf_rc = 0; | |
388 | else | |
389 | conf_rc = ENOTSUPP; | |
390 | ||
9bf9abea | 391 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { |
52bedf37 KG |
392 | if (lgr->role == SMC_SERV && |
393 | link->state == SMC_LNK_ACTIVATING) { | |
75d320d6 | 394 | link->llc_confirm_resp_rc = conf_rc; |
9bf9abea | 395 | complete(&link->llc_confirm_resp); |
75d320d6 | 396 | } |
9bf9abea | 397 | } else { |
52bedf37 KG |
398 | if (lgr->role == SMC_CLNT && |
399 | link->state == SMC_LNK_ACTIVATING) { | |
75d320d6 | 400 | link->llc_confirm_rc = conf_rc; |
9bf9abea UB |
401 | link->link_id = llc->link_num; |
402 | complete(&link->llc_confirm); | |
403 | } | |
404 | } | |
405 | } | |
406 | ||
52bedf37 KG |
407 | static void smc_llc_rx_add_link(struct smc_link *link, |
408 | struct smc_llc_msg_add_link *llc) | |
409 | { | |
00e5fb26 | 410 | struct smc_link_group *lgr = smc_get_lgr(link); |
52bedf37 KG |
411 | |
412 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { | |
413 | if (link->state == SMC_LNK_ACTIVATING) | |
414 | complete(&link->llc_add_resp); | |
415 | } else { | |
416 | if (link->state == SMC_LNK_ACTIVATING) { | |
417 | complete(&link->llc_add); | |
418 | return; | |
419 | } | |
420 | ||
421 | if (lgr->role == SMC_SERV) { | |
2a4c57a9 | 422 | smc_llc_prep_add_link(llc, link, |
52bedf37 | 423 | link->smcibdev->mac[link->ibport - 1], |
7005ada6 | 424 | link->gid, SMC_LLC_REQ); |
52bedf37 KG |
425 | |
426 | } else { | |
2a4c57a9 | 427 | smc_llc_prep_add_link(llc, link, |
52bedf37 | 428 | link->smcibdev->mac[link->ibport - 1], |
7005ada6 | 429 | link->gid, SMC_LLC_RESP); |
52bedf37 | 430 | } |
2a4c57a9 | 431 | smc_llc_send_message(link, llc, sizeof(*llc)); |
52bedf37 KG |
432 | } |
433 | } | |
434 | ||
435 | static void smc_llc_rx_delete_link(struct smc_link *link, | |
436 | struct smc_llc_msg_del_link *llc) | |
437 | { | |
00e5fb26 | 438 | struct smc_link_group *lgr = smc_get_lgr(link); |
52bedf37 KG |
439 | |
440 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { | |
441 | if (lgr->role == SMC_SERV) | |
0d18a0cb | 442 | smc_lgr_schedule_free_work_fast(lgr); |
52bedf37 | 443 | } else { |
0d18a0cb KG |
444 | smc_lgr_forget(lgr); |
445 | smc_llc_link_deleting(link); | |
52bedf37 | 446 | if (lgr->role == SMC_SERV) { |
0d18a0cb KG |
447 | /* client asks to delete this link, send request */ |
448 | smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ, true); | |
52bedf37 | 449 | } else { |
0d18a0cb KG |
450 | /* server requests to delete this link, send response */ |
451 | smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true); | |
52bedf37 | 452 | } |
0d18a0cb KG |
453 | smc_llc_send_message(link, llc, sizeof(*llc)); |
454 | smc_lgr_schedule_free_work_fast(lgr); | |
52bedf37 KG |
455 | } |
456 | } | |
457 | ||
313164da KG |
458 | static void smc_llc_rx_test_link(struct smc_link *link, |
459 | struct smc_llc_msg_test_link *llc) | |
460 | { | |
461 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { | |
877ae5be KG |
462 | if (link->state == SMC_LNK_ACTIVE) |
463 | complete(&link->llc_testlink_resp); | |
313164da | 464 | } else { |
d97935fa KG |
465 | llc->hd.flags |= SMC_LLC_FLAG_RESP; |
466 | smc_llc_send_message(link, llc, sizeof(*llc)); | |
313164da KG |
467 | } |
468 | } | |
469 | ||
4ed75de5 KG |
470 | static void smc_llc_rx_confirm_rkey(struct smc_link *link, |
471 | struct smc_llc_msg_confirm_rkey *llc) | |
472 | { | |
4ed75de5 KG |
473 | int rc; |
474 | ||
4ed75de5 | 475 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { |
44aa81ce KG |
476 | link->llc_confirm_rkey_rc = llc->hd.flags & |
477 | SMC_LLC_FLAG_RKEY_NEG; | |
478 | complete(&link->llc_confirm_rkey); | |
4ed75de5 | 479 | } else { |
00e5fb26 | 480 | rc = smc_rtoken_add(smc_get_lgr(link), |
4ed75de5 KG |
481 | llc->rtoken[0].rmb_vaddr, |
482 | llc->rtoken[0].rmb_key); | |
483 | ||
484 | /* ignore rtokens for other links, we have only one link */ | |
485 | ||
486 | llc->hd.flags |= SMC_LLC_FLAG_RESP; | |
487 | if (rc < 0) | |
488 | llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; | |
9fcdf8e9 | 489 | smc_llc_send_message(link, llc, sizeof(*llc)); |
4ed75de5 KG |
490 | } |
491 | } | |
492 | ||
493 | static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link, | |
494 | struct smc_llc_msg_confirm_rkey_cont *llc) | |
495 | { | |
496 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { | |
497 | /* unused as long as we don't send this type of msg */ | |
498 | } else { | |
499 | /* ignore rtokens for other links, we have only one link */ | |
500 | llc->hd.flags |= SMC_LLC_FLAG_RESP; | |
9fcdf8e9 | 501 | smc_llc_send_message(link, llc, sizeof(*llc)); |
4ed75de5 KG |
502 | } |
503 | } | |
504 | ||
505 | static void smc_llc_rx_delete_rkey(struct smc_link *link, | |
506 | struct smc_llc_msg_delete_rkey *llc) | |
507 | { | |
4ed75de5 KG |
508 | u8 err_mask = 0; |
509 | int i, max; | |
510 | ||
4ed75de5 KG |
511 | if (llc->hd.flags & SMC_LLC_FLAG_RESP) { |
512 | /* unused as long as we don't send this type of msg */ | |
513 | } else { | |
514 | max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); | |
515 | for (i = 0; i < max; i++) { | |
00e5fb26 | 516 | if (smc_rtoken_delete(smc_get_lgr(link), llc->rkey[i])) |
4ed75de5 KG |
517 | err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); |
518 | } | |
519 | ||
520 | if (err_mask) { | |
521 | llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; | |
522 | llc->err_mask = err_mask; | |
523 | } | |
524 | ||
525 | llc->hd.flags |= SMC_LLC_FLAG_RESP; | |
9fcdf8e9 | 526 | smc_llc_send_message(link, llc, sizeof(*llc)); |
4ed75de5 KG |
527 | } |
528 | } | |
529 | ||
9bf9abea UB |
530 | static void smc_llc_rx_handler(struct ib_wc *wc, void *buf) |
531 | { | |
532 | struct smc_link *link = (struct smc_link *)wc->qp->qp_context; | |
533 | union smc_llc_msg *llc = buf; | |
534 | ||
535 | if (wc->byte_len < sizeof(*llc)) | |
536 | return; /* short message */ | |
537 | if (llc->raw.hdr.length != sizeof(*llc)) | |
538 | return; /* invalid message */ | |
8f332a74 KG |
539 | if (link->state == SMC_LNK_INACTIVE) |
540 | return; /* link not active, drop msg */ | |
313164da KG |
541 | |
542 | switch (llc->raw.hdr.common.type) { | |
543 | case SMC_LLC_TEST_LINK: | |
544 | smc_llc_rx_test_link(link, &llc->test_link); | |
545 | break; | |
546 | case SMC_LLC_CONFIRM_LINK: | |
9bf9abea | 547 | smc_llc_rx_confirm_link(link, &llc->confirm_link); |
313164da | 548 | break; |
52bedf37 KG |
549 | case SMC_LLC_ADD_LINK: |
550 | smc_llc_rx_add_link(link, &llc->add_link); | |
551 | break; | |
552 | case SMC_LLC_DELETE_LINK: | |
553 | smc_llc_rx_delete_link(link, &llc->delete_link); | |
554 | break; | |
4ed75de5 KG |
555 | case SMC_LLC_CONFIRM_RKEY: |
556 | smc_llc_rx_confirm_rkey(link, &llc->confirm_rkey); | |
557 | break; | |
558 | case SMC_LLC_CONFIRM_RKEY_CONT: | |
559 | smc_llc_rx_confirm_rkey_cont(link, &llc->confirm_rkey_cont); | |
560 | break; | |
561 | case SMC_LLC_DELETE_RKEY: | |
562 | smc_llc_rx_delete_rkey(link, &llc->delete_rkey); | |
563 | break; | |
313164da | 564 | } |
9bf9abea UB |
565 | } |
566 | ||
44aa81ce | 567 | /***************************** worker, utils *********************************/ |
877ae5be KG |
568 | |
569 | static void smc_llc_testlink_work(struct work_struct *work) | |
570 | { | |
571 | struct smc_link *link = container_of(to_delayed_work(work), | |
572 | struct smc_link, llc_testlink_wrk); | |
573 | unsigned long next_interval; | |
877ae5be KG |
574 | unsigned long expire_time; |
575 | u8 user_data[16] = { 0 }; | |
576 | int rc; | |
577 | ||
877ae5be KG |
578 | if (link->state != SMC_LNK_ACTIVE) |
579 | return; /* don't reschedule worker */ | |
580 | expire_time = link->wr_rx_tstamp + link->llc_testlink_time; | |
581 | if (time_is_after_jiffies(expire_time)) { | |
582 | next_interval = expire_time - jiffies; | |
583 | goto out; | |
584 | } | |
585 | reinit_completion(&link->llc_testlink_resp); | |
d97935fa | 586 | smc_llc_send_test_link(link, user_data); |
877ae5be KG |
587 | /* receive TEST LINK response over RoCE fabric */ |
588 | rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, | |
589 | SMC_LLC_WAIT_TIME); | |
590 | if (rc <= 0) { | |
00e5fb26 | 591 | smc_lgr_terminate(smc_get_lgr(link)); |
877ae5be KG |
592 | return; |
593 | } | |
594 | next_interval = link->llc_testlink_time; | |
595 | out: | |
2a4c57a9 KG |
596 | queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk, |
597 | next_interval); | |
877ae5be KG |
598 | } |
599 | ||
2a4c57a9 | 600 | int smc_llc_link_init(struct smc_link *link) |
877ae5be | 601 | { |
00e5fb26 | 602 | struct smc_link_group *lgr = smc_get_lgr(link); |
2a4c57a9 KG |
603 | link->llc_wq = alloc_ordered_workqueue("llc_wq-%x:%x)", WQ_MEM_RECLAIM, |
604 | *((u32 *)lgr->id), | |
605 | link->link_id); | |
606 | if (!link->llc_wq) | |
607 | return -ENOMEM; | |
b32cf4ab KG |
608 | init_completion(&link->llc_confirm); |
609 | init_completion(&link->llc_confirm_resp); | |
610 | init_completion(&link->llc_add); | |
611 | init_completion(&link->llc_add_resp); | |
612 | init_completion(&link->llc_confirm_rkey); | |
877ae5be KG |
613 | init_completion(&link->llc_testlink_resp); |
614 | INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); | |
2a4c57a9 | 615 | return 0; |
b32cf4ab KG |
616 | } |
617 | ||
618 | void smc_llc_link_active(struct smc_link *link, int testlink_time) | |
619 | { | |
877ae5be KG |
620 | link->state = SMC_LNK_ACTIVE; |
621 | if (testlink_time) { | |
622 | link->llc_testlink_time = testlink_time * HZ; | |
2a4c57a9 KG |
623 | queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk, |
624 | link->llc_testlink_time); | |
877ae5be KG |
625 | } |
626 | } | |
627 | ||
0d18a0cb KG |
628 | void smc_llc_link_deleting(struct smc_link *link) |
629 | { | |
630 | link->state = SMC_LNK_DELETING; | |
631 | } | |
632 | ||
877ae5be KG |
633 | /* called in tasklet context */ |
634 | void smc_llc_link_inactive(struct smc_link *link) | |
635 | { | |
636 | link->state = SMC_LNK_INACTIVE; | |
637 | cancel_delayed_work(&link->llc_testlink_wrk); | |
638 | } | |
639 | ||
640 | /* called in worker context */ | |
2a4c57a9 | 641 | void smc_llc_link_clear(struct smc_link *link) |
877ae5be | 642 | { |
2a4c57a9 KG |
643 | flush_workqueue(link->llc_wq); |
644 | destroy_workqueue(link->llc_wq); | |
877ae5be KG |
645 | } |
646 | ||
44aa81ce KG |
647 | /* register a new rtoken at the remote peer */ |
648 | int smc_llc_do_confirm_rkey(struct smc_link *link, | |
649 | struct smc_buf_desc *rmb_desc) | |
650 | { | |
651 | int rc; | |
652 | ||
653 | reinit_completion(&link->llc_confirm_rkey); | |
654 | smc_llc_send_confirm_rkey(link, rmb_desc); | |
655 | /* receive CONFIRM RKEY response from server over RoCE fabric */ | |
656 | rc = wait_for_completion_interruptible_timeout(&link->llc_confirm_rkey, | |
657 | SMC_LLC_WAIT_TIME); | |
658 | if (rc <= 0 || link->llc_confirm_rkey_rc) | |
659 | return -EFAULT; | |
660 | return 0; | |
661 | } | |
662 | ||
9bf9abea UB |
663 | /***************************** init, exit, misc ******************************/ |
664 | ||
665 | static struct smc_wr_rx_handler smc_llc_rx_handlers[] = { | |
666 | { | |
667 | .handler = smc_llc_rx_handler, | |
668 | .type = SMC_LLC_CONFIRM_LINK | |
669 | }, | |
313164da KG |
670 | { |
671 | .handler = smc_llc_rx_handler, | |
672 | .type = SMC_LLC_TEST_LINK | |
673 | }, | |
52bedf37 KG |
674 | { |
675 | .handler = smc_llc_rx_handler, | |
676 | .type = SMC_LLC_ADD_LINK | |
677 | }, | |
678 | { | |
679 | .handler = smc_llc_rx_handler, | |
680 | .type = SMC_LLC_DELETE_LINK | |
681 | }, | |
4ed75de5 KG |
682 | { |
683 | .handler = smc_llc_rx_handler, | |
684 | .type = SMC_LLC_CONFIRM_RKEY | |
685 | }, | |
686 | { | |
687 | .handler = smc_llc_rx_handler, | |
688 | .type = SMC_LLC_CONFIRM_RKEY_CONT | |
689 | }, | |
690 | { | |
691 | .handler = smc_llc_rx_handler, | |
692 | .type = SMC_LLC_DELETE_RKEY | |
693 | }, | |
9bf9abea UB |
694 | { |
695 | .handler = NULL, | |
696 | } | |
697 | }; | |
698 | ||
699 | int __init smc_llc_init(void) | |
700 | { | |
701 | struct smc_wr_rx_handler *handler; | |
702 | int rc = 0; | |
703 | ||
704 | for (handler = smc_llc_rx_handlers; handler->handler; handler++) { | |
705 | INIT_HLIST_NODE(&handler->list); | |
706 | rc = smc_wr_rx_register_handler(handler); | |
707 | if (rc) | |
708 | break; | |
709 | } | |
710 | return rc; | |
711 | } |