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 <Klaus.Wacker@de.ibm.com> | |
10 | * Ursula Braun <ubraun@linux.vnet.ibm.com> | |
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" | |
336ba09f | 20 | #include "smc_pnet.h" |
9bf9abea | 21 | |
0f627126 SR |
22 | #define SMC_LLC_DATA_LEN 40 |
23 | ||
24 | struct smc_llc_hdr { | |
25 | struct smc_wr_rx_hdr common; | |
b4ba4652 KG |
26 | union { |
27 | struct { | |
28 | u8 length; /* 44 */ | |
29 | #if defined(__BIG_ENDIAN_BITFIELD) | |
30 | u8 reserved:4, | |
31 | add_link_rej_rsn:4; | |
52bedf37 | 32 | #elif defined(__LITTLE_ENDIAN_BITFIELD) |
b4ba4652 KG |
33 | u8 add_link_rej_rsn:4, |
34 | reserved:4; | |
52bedf37 | 35 | #endif |
b4ba4652 KG |
36 | }; |
37 | u16 length_v2; /* 44 - 8192*/ | |
38 | }; | |
0f627126 | 39 | u8 flags; |
b4ba4652 KG |
40 | } __packed; /* format defined in |
41 | * IBM Shared Memory Communications Version 2 | |
42 | * (https://www.ibm.com/support/pages/node/6326337) | |
43 | */ | |
0f627126 | 44 | |
75d320d6 KG |
45 | #define SMC_LLC_FLAG_NO_RMBE_EYEC 0x03 |
46 | ||
0f627126 SR |
47 | struct smc_llc_msg_confirm_link { /* type 0x01 */ |
48 | struct smc_llc_hdr hd; | |
49 | u8 sender_mac[ETH_ALEN]; | |
50 | u8 sender_gid[SMC_GID_SIZE]; | |
51 | u8 sender_qp_num[3]; | |
52 | u8 link_num; | |
53 | u8 link_uid[SMC_LGR_ID_SIZE]; | |
54 | u8 max_links; | |
7f0620b9 GW |
55 | u8 max_conns; |
56 | u8 reserved[8]; | |
0f627126 SR |
57 | }; |
58 | ||
52bedf37 KG |
59 | #define SMC_LLC_FLAG_ADD_LNK_REJ 0x40 |
60 | #define SMC_LLC_REJ_RSN_NO_ALT_PATH 1 | |
61 | ||
52bedf37 KG |
62 | struct smc_llc_msg_add_link { /* type 0x02 */ |
63 | struct smc_llc_hdr hd; | |
64 | u8 sender_mac[ETH_ALEN]; | |
65 | u8 reserved2[2]; | |
66 | u8 sender_gid[SMC_GID_SIZE]; | |
67 | u8 sender_qp_num[3]; | |
68 | u8 link_num; | |
fbed3b37 KG |
69 | #if defined(__BIG_ENDIAN_BITFIELD) |
70 | u8 reserved3 : 4, | |
71 | qp_mtu : 4; | |
72 | #elif defined(__LITTLE_ENDIAN_BITFIELD) | |
73 | u8 qp_mtu : 4, | |
74 | reserved3 : 4; | |
75 | #endif | |
52bedf37 KG |
76 | u8 initial_psn[3]; |
77 | u8 reserved[8]; | |
78 | }; | |
79 | ||
87f88cda KG |
80 | struct smc_llc_msg_add_link_cont_rt { |
81 | __be32 rmb_key; | |
82 | __be32 rmb_key_new; | |
83 | __be64 rmb_vaddr_new; | |
84 | }; | |
85 | ||
b4ba4652 KG |
86 | struct smc_llc_msg_add_link_v2_ext { |
87 | #if defined(__BIG_ENDIAN_BITFIELD) | |
88 | u8 v2_direct : 1, | |
89 | reserved : 7; | |
90 | #elif defined(__LITTLE_ENDIAN_BITFIELD) | |
91 | u8 reserved : 7, | |
92 | v2_direct : 1; | |
93 | #endif | |
94 | u8 reserved2; | |
95 | u8 client_target_gid[SMC_GID_SIZE]; | |
96 | u8 reserved3[8]; | |
97 | u16 num_rkeys; | |
98 | struct smc_llc_msg_add_link_cont_rt rt[]; | |
99 | } __packed; /* format defined in | |
100 | * IBM Shared Memory Communications Version 2 | |
101 | * (https://www.ibm.com/support/pages/node/6326337) | |
102 | */ | |
103 | ||
104 | struct smc_llc_msg_req_add_link_v2 { | |
105 | struct smc_llc_hdr hd; | |
106 | u8 reserved[20]; | |
107 | u8 gid_cnt; | |
108 | u8 reserved2[3]; | |
109 | u8 gid[][SMC_GID_SIZE]; | |
110 | }; | |
111 | ||
87f88cda KG |
112 | #define SMC_LLC_RKEYS_PER_CONT_MSG 2 |
113 | ||
114 | struct smc_llc_msg_add_link_cont { /* type 0x03 */ | |
115 | struct smc_llc_hdr hd; | |
116 | u8 link_num; | |
117 | u8 num_rkeys; | |
118 | u8 reserved2[2]; | |
119 | struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG]; | |
120 | u8 reserved[4]; | |
121 | } __packed; /* format defined in RFC7609 */ | |
122 | ||
52bedf37 KG |
123 | #define SMC_LLC_FLAG_DEL_LINK_ALL 0x40 |
124 | #define SMC_LLC_FLAG_DEL_LINK_ORDERLY 0x20 | |
125 | ||
126 | struct smc_llc_msg_del_link { /* type 0x04 */ | |
127 | struct smc_llc_hdr hd; | |
128 | u8 link_num; | |
129 | __be32 reason; | |
130 | u8 reserved[35]; | |
131 | } __packed; /* format defined in RFC7609 */ | |
132 | ||
313164da KG |
133 | struct smc_llc_msg_test_link { /* type 0x07 */ |
134 | struct smc_llc_hdr hd; | |
135 | u8 user_data[16]; | |
136 | u8 reserved[24]; | |
137 | }; | |
138 | ||
4ed75de5 KG |
139 | struct smc_rmb_rtoken { |
140 | union { | |
141 | u8 num_rkeys; /* first rtoken byte of CONFIRM LINK msg */ | |
142 | /* is actually the num of rtokens, first */ | |
143 | /* rtoken is always for the current link */ | |
144 | u8 link_id; /* link id of the rtoken */ | |
145 | }; | |
146 | __be32 rmb_key; | |
147 | __be64 rmb_vaddr; | |
148 | } __packed; /* format defined in RFC7609 */ | |
149 | ||
b4ba4652 KG |
150 | #define SMC_LLC_RKEYS_PER_MSG 3 |
151 | #define SMC_LLC_RKEYS_PER_MSG_V2 255 | |
4ed75de5 KG |
152 | |
153 | struct smc_llc_msg_confirm_rkey { /* type 0x06 */ | |
154 | struct smc_llc_hdr hd; | |
155 | struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG]; | |
156 | u8 reserved; | |
157 | }; | |
158 | ||
4ed75de5 | 159 | #define SMC_LLC_DEL_RKEY_MAX 8 |
3bc67e09 | 160 | #define SMC_LLC_FLAG_RKEY_RETRY 0x10 |
4ed75de5 KG |
161 | #define SMC_LLC_FLAG_RKEY_NEG 0x20 |
162 | ||
163 | struct smc_llc_msg_delete_rkey { /* type 0x09 */ | |
164 | struct smc_llc_hdr hd; | |
165 | u8 num_rkeys; | |
166 | u8 err_mask; | |
167 | u8 reserved[2]; | |
168 | __be32 rkey[8]; | |
169 | u8 reserved2[4]; | |
170 | }; | |
171 | ||
b4ba4652 KG |
172 | struct smc_llc_msg_delete_rkey_v2 { /* type 0x29 */ |
173 | struct smc_llc_hdr hd; | |
174 | u8 num_rkeys; | |
175 | u8 num_inval_rkeys; | |
176 | u8 reserved[2]; | |
177 | __be32 rkey[]; | |
178 | }; | |
179 | ||
0f627126 SR |
180 | union smc_llc_msg { |
181 | struct smc_llc_msg_confirm_link confirm_link; | |
52bedf37 | 182 | struct smc_llc_msg_add_link add_link; |
b4ba4652 | 183 | struct smc_llc_msg_req_add_link_v2 req_add_link; |
87f88cda | 184 | struct smc_llc_msg_add_link_cont add_link_cont; |
52bedf37 | 185 | struct smc_llc_msg_del_link delete_link; |
4ed75de5 KG |
186 | |
187 | struct smc_llc_msg_confirm_rkey confirm_rkey; | |
4ed75de5 KG |
188 | struct smc_llc_msg_delete_rkey delete_rkey; |
189 | ||
313164da | 190 | struct smc_llc_msg_test_link test_link; |
0f627126 SR |
191 | struct { |
192 | struct smc_llc_hdr hdr; | |
193 | u8 data[SMC_LLC_DATA_LEN]; | |
194 | } raw; | |
195 | }; | |
196 | ||
197 | #define SMC_LLC_FLAG_RESP 0x80 | |
198 | ||
6c8968c4 KG |
199 | struct smc_llc_qentry { |
200 | struct list_head list; | |
201 | struct smc_link *link; | |
202 | union smc_llc_msg msg; | |
203 | }; | |
204 | ||
4dadd151 KG |
205 | static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc); |
206 | ||
555da9af KG |
207 | struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow) |
208 | { | |
209 | struct smc_llc_qentry *qentry = flow->qentry; | |
210 | ||
211 | flow->qentry = NULL; | |
212 | return qentry; | |
213 | } | |
214 | ||
215 | void smc_llc_flow_qentry_del(struct smc_llc_flow *flow) | |
216 | { | |
217 | struct smc_llc_qentry *qentry; | |
218 | ||
219 | if (flow->qentry) { | |
220 | qentry = flow->qentry; | |
221 | flow->qentry = NULL; | |
222 | kfree(qentry); | |
223 | } | |
224 | } | |
225 | ||
226 | static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow, | |
227 | struct smc_llc_qentry *qentry) | |
228 | { | |
229 | flow->qentry = qentry; | |
230 | } | |
231 | ||
6778a6be KG |
232 | static void smc_llc_flow_parallel(struct smc_link_group *lgr, u8 flow_type, |
233 | struct smc_llc_qentry *qentry) | |
234 | { | |
b4ba4652 | 235 | u8 msg_type = qentry->msg.raw.hdr.common.llc_type; |
6778a6be KG |
236 | |
237 | if ((msg_type == SMC_LLC_ADD_LINK || msg_type == SMC_LLC_DELETE_LINK) && | |
238 | flow_type != msg_type && !lgr->delayed_event) { | |
239 | lgr->delayed_event = qentry; | |
240 | return; | |
241 | } | |
242 | /* drop parallel or already-in-progress llc requests */ | |
243 | if (flow_type != msg_type) | |
de2fea7b | 244 | pr_warn_once("smc: SMC-R lg %*phN net %llu dropped parallel " |
6778a6be KG |
245 | "LLC msg: msg %d flow %d role %d\n", |
246 | SMC_LGR_ID_SIZE, &lgr->id, | |
de2fea7b | 247 | lgr->net->net_cookie, |
6778a6be KG |
248 | qentry->msg.raw.hdr.common.type, |
249 | flow_type, lgr->role); | |
250 | kfree(qentry); | |
251 | } | |
252 | ||
555da9af KG |
253 | /* try to start a new llc flow, initiated by an incoming llc msg */ |
254 | static bool smc_llc_flow_start(struct smc_llc_flow *flow, | |
255 | struct smc_llc_qentry *qentry) | |
256 | { | |
257 | struct smc_link_group *lgr = qentry->link->lgr; | |
258 | ||
259 | spin_lock_bh(&lgr->llc_flow_lock); | |
260 | if (flow->type) { | |
261 | /* a flow is already active */ | |
6778a6be | 262 | smc_llc_flow_parallel(lgr, flow->type, qentry); |
555da9af KG |
263 | spin_unlock_bh(&lgr->llc_flow_lock); |
264 | return false; | |
265 | } | |
b4ba4652 | 266 | switch (qentry->msg.raw.hdr.common.llc_type) { |
555da9af KG |
267 | case SMC_LLC_ADD_LINK: |
268 | flow->type = SMC_LLC_FLOW_ADD_LINK; | |
269 | break; | |
270 | case SMC_LLC_DELETE_LINK: | |
271 | flow->type = SMC_LLC_FLOW_DEL_LINK; | |
272 | break; | |
273 | case SMC_LLC_CONFIRM_RKEY: | |
274 | case SMC_LLC_DELETE_RKEY: | |
275 | flow->type = SMC_LLC_FLOW_RKEY; | |
276 | break; | |
277 | default: | |
278 | flow->type = SMC_LLC_FLOW_NONE; | |
279 | } | |
555da9af | 280 | smc_llc_flow_qentry_set(flow, qentry); |
6778a6be | 281 | spin_unlock_bh(&lgr->llc_flow_lock); |
555da9af KG |
282 | return true; |
283 | } | |
284 | ||
285 | /* start a new local llc flow, wait till current flow finished */ | |
286 | int smc_llc_flow_initiate(struct smc_link_group *lgr, | |
287 | enum smc_llc_flowtype type) | |
288 | { | |
289 | enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE; | |
290 | int rc; | |
291 | ||
292 | /* all flows except confirm_rkey and delete_rkey are exclusive, | |
293 | * confirm/delete rkey flows can run concurrently (local and remote) | |
294 | */ | |
295 | if (type == SMC_LLC_FLOW_RKEY) | |
296 | allowed_remote = SMC_LLC_FLOW_RKEY; | |
297 | again: | |
298 | if (list_empty(&lgr->list)) | |
299 | return -ENODEV; | |
300 | spin_lock_bh(&lgr->llc_flow_lock); | |
301 | if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && | |
302 | (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || | |
303 | lgr->llc_flow_rmt.type == allowed_remote)) { | |
304 | lgr->llc_flow_lcl.type = type; | |
305 | spin_unlock_bh(&lgr->llc_flow_lock); | |
306 | return 0; | |
307 | } | |
308 | spin_unlock_bh(&lgr->llc_flow_lock); | |
6778a6be KG |
309 | rc = wait_event_timeout(lgr->llc_flow_waiter, (list_empty(&lgr->list) || |
310 | (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE && | |
311 | (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE || | |
312 | lgr->llc_flow_rmt.type == allowed_remote))), | |
313 | SMC_LLC_WAIT_TIME * 10); | |
555da9af KG |
314 | if (!rc) |
315 | return -ETIMEDOUT; | |
316 | goto again; | |
317 | } | |
318 | ||
319 | /* finish the current llc flow */ | |
320 | void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow) | |
321 | { | |
322 | spin_lock_bh(&lgr->llc_flow_lock); | |
323 | memset(flow, 0, sizeof(*flow)); | |
324 | flow->type = SMC_LLC_FLOW_NONE; | |
325 | spin_unlock_bh(&lgr->llc_flow_lock); | |
326 | if (!list_empty(&lgr->list) && lgr->delayed_event && | |
327 | flow == &lgr->llc_flow_lcl) | |
328 | schedule_work(&lgr->llc_event_work); | |
329 | else | |
6778a6be | 330 | wake_up(&lgr->llc_flow_waiter); |
555da9af KG |
331 | } |
332 | ||
333 | /* lnk is optional and used for early wakeup when link goes down, useful in | |
334 | * cases where we wait for a response on the link after we sent a request | |
335 | */ | |
336 | struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr, | |
337 | struct smc_link *lnk, | |
338 | int time_out, u8 exp_msg) | |
339 | { | |
340 | struct smc_llc_flow *flow = &lgr->llc_flow_lcl; | |
6778a6be | 341 | u8 rcv_msg; |
555da9af | 342 | |
6778a6be KG |
343 | wait_event_timeout(lgr->llc_msg_waiter, |
344 | (flow->qentry || | |
345 | (lnk && !smc_link_usable(lnk)) || | |
346 | list_empty(&lgr->list)), | |
347 | time_out); | |
555da9af KG |
348 | if (!flow->qentry || |
349 | (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) { | |
350 | smc_llc_flow_qentry_del(flow); | |
351 | goto out; | |
352 | } | |
b4ba4652 | 353 | rcv_msg = flow->qentry->msg.raw.hdr.common.llc_type; |
6778a6be | 354 | if (exp_msg && rcv_msg != exp_msg) { |
555da9af | 355 | if (exp_msg == SMC_LLC_ADD_LINK && |
6778a6be | 356 | rcv_msg == SMC_LLC_DELETE_LINK) { |
555da9af KG |
357 | /* flow_start will delay the unexpected msg */ |
358 | smc_llc_flow_start(&lgr->llc_flow_lcl, | |
359 | smc_llc_flow_qentry_clr(flow)); | |
360 | return NULL; | |
361 | } | |
de2fea7b | 362 | pr_warn_once("smc: SMC-R lg %*phN net %llu dropped unexpected LLC msg: " |
6778a6be | 363 | "msg %d exp %d flow %d role %d flags %x\n", |
de2fea7b TL |
364 | SMC_LGR_ID_SIZE, &lgr->id, lgr->net->net_cookie, |
365 | rcv_msg, exp_msg, | |
6778a6be KG |
366 | flow->type, lgr->role, |
367 | flow->qentry->msg.raw.hdr.flags); | |
555da9af KG |
368 | smc_llc_flow_qentry_del(flow); |
369 | } | |
370 | out: | |
371 | return flow->qentry; | |
372 | } | |
373 | ||
9bf9abea UB |
374 | /********************************** send *************************************/ |
375 | ||
376 | struct smc_llc_tx_pend { | |
377 | }; | |
378 | ||
379 | /* handler for send/transmission completion of an LLC msg */ | |
380 | static void smc_llc_tx_handler(struct smc_wr_tx_pend_priv *pend, | |
381 | struct smc_link *link, | |
382 | enum ib_wc_status wc_status) | |
383 | { | |
384 | /* future work: handle wc_status error for recovery and failover */ | |
385 | } | |
386 | ||
387 | /** | |
388 | * smc_llc_add_pending_send() - add LLC control message to pending WQE transmits | |
389 | * @link: Pointer to SMC link used for sending LLC control message. | |
390 | * @wr_buf: Out variable returning pointer to work request payload buffer. | |
391 | * @pend: Out variable returning pointer to private pending WR tracking. | |
392 | * It's the context the transmit complete handler will get. | |
393 | * | |
394 | * Reserves and pre-fills an entry for a pending work request send/tx. | |
395 | * Used by mid-level smc_llc_send_msg() to prepare for later actual send/tx. | |
396 | * Can sleep due to smc_get_ctrl_buf (if not in softirq context). | |
397 | * | |
398 | * Return: 0 on success, otherwise an error value. | |
399 | */ | |
400 | static int smc_llc_add_pending_send(struct smc_link *link, | |
401 | struct smc_wr_buf **wr_buf, | |
402 | struct smc_wr_tx_pend_priv **pend) | |
403 | { | |
404 | int rc; | |
405 | ||
ad6f317f UB |
406 | rc = smc_wr_tx_get_free_slot(link, smc_llc_tx_handler, wr_buf, NULL, |
407 | pend); | |
9bf9abea UB |
408 | if (rc < 0) |
409 | return rc; | |
410 | BUILD_BUG_ON_MSG( | |
411 | sizeof(union smc_llc_msg) > SMC_WR_BUF_SIZE, | |
412 | "must increase SMC_WR_BUF_SIZE to at least sizeof(struct smc_llc_msg)"); | |
413 | BUILD_BUG_ON_MSG( | |
414 | sizeof(union smc_llc_msg) != SMC_WR_TX_SIZE, | |
415 | "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()"); | |
416 | BUILD_BUG_ON_MSG( | |
417 | sizeof(struct smc_llc_tx_pend) > SMC_WR_TX_PEND_PRIV_SIZE, | |
418 | "must increase SMC_WR_TX_PEND_PRIV_SIZE to at least sizeof(struct smc_llc_tx_pend)"); | |
419 | return 0; | |
420 | } | |
421 | ||
b4ba4652 KG |
422 | static int smc_llc_add_pending_send_v2(struct smc_link *link, |
423 | struct smc_wr_v2_buf **wr_buf, | |
424 | struct smc_wr_tx_pend_priv **pend) | |
425 | { | |
426 | int rc; | |
427 | ||
428 | rc = smc_wr_tx_get_v2_slot(link, smc_llc_tx_handler, wr_buf, pend); | |
429 | if (rc < 0) | |
430 | return rc; | |
431 | return 0; | |
432 | } | |
433 | ||
434 | static void smc_llc_init_msg_hdr(struct smc_llc_hdr *hdr, | |
435 | struct smc_link_group *lgr, size_t len) | |
436 | { | |
437 | if (lgr->smc_version == SMC_V2) { | |
438 | hdr->common.llc_version = SMC_V2; | |
439 | hdr->length_v2 = len; | |
440 | } else { | |
441 | hdr->common.llc_version = 0; | |
442 | hdr->length = len; | |
443 | } | |
444 | } | |
445 | ||
9bf9abea | 446 | /* high-level API to send LLC confirm link */ |
947541f3 | 447 | int smc_llc_send_confirm_link(struct smc_link *link, |
9bf9abea UB |
448 | enum smc_llc_reqresp reqresp) |
449 | { | |
9bf9abea UB |
450 | struct smc_llc_msg_confirm_link *confllc; |
451 | struct smc_wr_tx_pend_priv *pend; | |
452 | struct smc_wr_buf *wr_buf; | |
453 | int rc; | |
454 | ||
95f7f3e7 KG |
455 | if (!smc_wr_tx_link_hold(link)) |
456 | return -ENOLINK; | |
9bf9abea UB |
457 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); |
458 | if (rc) | |
95f7f3e7 | 459 | goto put_out; |
9bf9abea UB |
460 | confllc = (struct smc_llc_msg_confirm_link *)wr_buf; |
461 | memset(confllc, 0, sizeof(*confllc)); | |
b4ba4652 KG |
462 | confllc->hd.common.llc_type = SMC_LLC_CONFIRM_LINK; |
463 | smc_llc_init_msg_hdr(&confllc->hd, link->lgr, sizeof(*confllc)); | |
75d320d6 | 464 | confllc->hd.flags |= SMC_LLC_FLAG_NO_RMBE_EYEC; |
9bf9abea UB |
465 | if (reqresp == SMC_LLC_RESP) |
466 | confllc->hd.flags |= SMC_LLC_FLAG_RESP; | |
947541f3 UB |
467 | memcpy(confllc->sender_mac, link->smcibdev->mac[link->ibport - 1], |
468 | ETH_ALEN); | |
7005ada6 | 469 | memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE); |
9bf9abea | 470 | hton24(confllc->sender_qp_num, link->roce_qp->qp_num); |
2be922f3 | 471 | confllc->link_num = link->link_id; |
45fa8da0 | 472 | memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE); |
69b888e3 | 473 | confllc->max_links = SMC_LINKS_ADD_LNK_MAX; |
7f0620b9 | 474 | if (link->lgr->smc_version == SMC_V2 && |
69b888e3 | 475 | link->lgr->peer_smc_release >= SMC_RELEASE_1) { |
7f0620b9 | 476 | confllc->max_conns = link->lgr->max_conns; |
69b888e3 GW |
477 | confllc->max_links = link->lgr->max_links; |
478 | } | |
52bedf37 KG |
479 | /* send llc message */ |
480 | rc = smc_wr_tx_send(link, pend); | |
95f7f3e7 KG |
481 | put_out: |
482 | smc_wr_tx_link_put(link); | |
52bedf37 KG |
483 | return rc; |
484 | } | |
485 | ||
44aa81ce | 486 | /* send LLC confirm rkey request */ |
3d88a21b | 487 | static int smc_llc_send_confirm_rkey(struct smc_link *send_link, |
44aa81ce KG |
488 | struct smc_buf_desc *rmb_desc) |
489 | { | |
490 | struct smc_llc_msg_confirm_rkey *rkeyllc; | |
491 | struct smc_wr_tx_pend_priv *pend; | |
492 | struct smc_wr_buf *wr_buf; | |
3d88a21b KG |
493 | struct smc_link *link; |
494 | int i, rc, rtok_ix; | |
44aa81ce | 495 | |
95f7f3e7 KG |
496 | if (!smc_wr_tx_link_hold(send_link)) |
497 | return -ENOLINK; | |
3d88a21b | 498 | rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend); |
44aa81ce | 499 | if (rc) |
95f7f3e7 | 500 | goto put_out; |
44aa81ce KG |
501 | rkeyllc = (struct smc_llc_msg_confirm_rkey *)wr_buf; |
502 | memset(rkeyllc, 0, sizeof(*rkeyllc)); | |
b4ba4652 KG |
503 | rkeyllc->hd.common.llc_type = SMC_LLC_CONFIRM_RKEY; |
504 | smc_llc_init_msg_hdr(&rkeyllc->hd, send_link->lgr, sizeof(*rkeyllc)); | |
3d88a21b KG |
505 | |
506 | rtok_ix = 1; | |
507 | for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { | |
508 | link = &send_link->lgr->lnk[i]; | |
741a49a4 | 509 | if (smc_link_active(link) && link != send_link) { |
3d88a21b KG |
510 | rkeyllc->rtoken[rtok_ix].link_id = link->link_id; |
511 | rkeyllc->rtoken[rtok_ix].rmb_key = | |
b8d19945 WG |
512 | htonl(rmb_desc->mr[link->link_idx]->rkey); |
513 | rkeyllc->rtoken[rtok_ix].rmb_vaddr = rmb_desc->is_vm ? | |
514 | cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) : | |
515 | cpu_to_be64((u64)sg_dma_address | |
516 | (rmb_desc->sgt[link->link_idx].sgl)); | |
3d88a21b KG |
517 | rtok_ix++; |
518 | } | |
519 | } | |
520 | /* rkey of send_link is in rtoken[0] */ | |
521 | rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1; | |
44aa81ce | 522 | rkeyllc->rtoken[0].rmb_key = |
b8d19945 WG |
523 | htonl(rmb_desc->mr[send_link->link_idx]->rkey); |
524 | rkeyllc->rtoken[0].rmb_vaddr = rmb_desc->is_vm ? | |
525 | cpu_to_be64((uintptr_t)rmb_desc->cpu_addr) : | |
526 | cpu_to_be64((u64)sg_dma_address | |
527 | (rmb_desc->sgt[send_link->link_idx].sgl)); | |
44aa81ce | 528 | /* send llc message */ |
3d88a21b | 529 | rc = smc_wr_tx_send(send_link, pend); |
95f7f3e7 KG |
530 | put_out: |
531 | smc_wr_tx_link_put(send_link); | |
44aa81ce KG |
532 | return rc; |
533 | } | |
534 | ||
60e03c62 KG |
535 | /* send LLC delete rkey request */ |
536 | static int smc_llc_send_delete_rkey(struct smc_link *link, | |
537 | struct smc_buf_desc *rmb_desc) | |
538 | { | |
539 | struct smc_llc_msg_delete_rkey *rkeyllc; | |
540 | struct smc_wr_tx_pend_priv *pend; | |
541 | struct smc_wr_buf *wr_buf; | |
542 | int rc; | |
543 | ||
95f7f3e7 KG |
544 | if (!smc_wr_tx_link_hold(link)) |
545 | return -ENOLINK; | |
60e03c62 KG |
546 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); |
547 | if (rc) | |
95f7f3e7 | 548 | goto put_out; |
60e03c62 KG |
549 | rkeyllc = (struct smc_llc_msg_delete_rkey *)wr_buf; |
550 | memset(rkeyllc, 0, sizeof(*rkeyllc)); | |
b4ba4652 KG |
551 | rkeyllc->hd.common.llc_type = SMC_LLC_DELETE_RKEY; |
552 | smc_llc_init_msg_hdr(&rkeyllc->hd, link->lgr, sizeof(*rkeyllc)); | |
60e03c62 | 553 | rkeyllc->num_rkeys = 1; |
b8d19945 | 554 | rkeyllc->rkey[0] = htonl(rmb_desc->mr[link->link_idx]->rkey); |
60e03c62 KG |
555 | /* send llc message */ |
556 | rc = smc_wr_tx_send(link, pend); | |
95f7f3e7 KG |
557 | put_out: |
558 | smc_wr_tx_link_put(link); | |
60e03c62 KG |
559 | return rc; |
560 | } | |
561 | ||
b4ba4652 KG |
562 | /* return first buffer from any of the next buf lists */ |
563 | static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr, | |
564 | int *buf_lst) | |
565 | { | |
566 | struct smc_buf_desc *buf_pos; | |
567 | ||
568 | while (*buf_lst < SMC_RMBE_SIZES) { | |
569 | buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst], | |
570 | struct smc_buf_desc, list); | |
571 | if (buf_pos) | |
572 | return buf_pos; | |
573 | (*buf_lst)++; | |
574 | } | |
575 | return NULL; | |
576 | } | |
577 | ||
578 | /* return next rmb from buffer lists */ | |
579 | static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr, | |
580 | int *buf_lst, | |
581 | struct smc_buf_desc *buf_pos) | |
582 | { | |
583 | struct smc_buf_desc *buf_next; | |
584 | ||
b24aa141 WG |
585 | if (!buf_pos) |
586 | return _smc_llc_get_next_rmb(lgr, buf_lst); | |
587 | ||
588 | if (list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) { | |
b4ba4652 KG |
589 | (*buf_lst)++; |
590 | return _smc_llc_get_next_rmb(lgr, buf_lst); | |
591 | } | |
592 | buf_next = list_next_entry(buf_pos, list); | |
593 | return buf_next; | |
594 | } | |
595 | ||
596 | static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr, | |
597 | int *buf_lst) | |
598 | { | |
599 | *buf_lst = 0; | |
600 | return smc_llc_get_next_rmb(lgr, buf_lst, NULL); | |
601 | } | |
602 | ||
603 | static int smc_llc_fill_ext_v2(struct smc_llc_msg_add_link_v2_ext *ext, | |
604 | struct smc_link *link, struct smc_link *link_new) | |
605 | { | |
606 | struct smc_link_group *lgr = link->lgr; | |
607 | struct smc_buf_desc *buf_pos; | |
608 | int prim_lnk_idx, lnk_idx, i; | |
609 | struct smc_buf_desc *rmb; | |
610 | int len = sizeof(*ext); | |
611 | int buf_lst; | |
612 | ||
613 | ext->v2_direct = !lgr->uses_gateway; | |
614 | memcpy(ext->client_target_gid, link_new->gid, SMC_GID_SIZE); | |
615 | ||
616 | prim_lnk_idx = link->link_idx; | |
617 | lnk_idx = link_new->link_idx; | |
aff7bfed | 618 | down_write(&lgr->rmbs_lock); |
b4ba4652 KG |
619 | ext->num_rkeys = lgr->conns_num; |
620 | if (!ext->num_rkeys) | |
621 | goto out; | |
622 | buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); | |
623 | for (i = 0; i < ext->num_rkeys; i++) { | |
71c6aa03 WG |
624 | while (buf_pos && !(buf_pos)->used) |
625 | buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); | |
b4ba4652 KG |
626 | if (!buf_pos) |
627 | break; | |
628 | rmb = buf_pos; | |
b8d19945 WG |
629 | ext->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey); |
630 | ext->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey); | |
631 | ext->rt[i].rmb_vaddr_new = rmb->is_vm ? | |
632 | cpu_to_be64((uintptr_t)rmb->cpu_addr) : | |
b4ba4652 KG |
633 | cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); |
634 | buf_pos = smc_llc_get_next_rmb(lgr, &buf_lst, buf_pos); | |
b4ba4652 KG |
635 | } |
636 | len += i * sizeof(ext->rt[0]); | |
637 | out: | |
aff7bfed | 638 | up_write(&lgr->rmbs_lock); |
b4ba4652 KG |
639 | return len; |
640 | } | |
641 | ||
52bedf37 | 642 | /* send ADD LINK request or response */ |
7005ada6 | 643 | int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[], |
fbed3b37 | 644 | struct smc_link *link_new, |
52bedf37 KG |
645 | enum smc_llc_reqresp reqresp) |
646 | { | |
b4ba4652 | 647 | struct smc_llc_msg_add_link_v2_ext *ext = NULL; |
52bedf37 KG |
648 | struct smc_llc_msg_add_link *addllc; |
649 | struct smc_wr_tx_pend_priv *pend; | |
b4ba4652 | 650 | int len = sizeof(*addllc); |
52bedf37 KG |
651 | int rc; |
652 | ||
95f7f3e7 KG |
653 | if (!smc_wr_tx_link_hold(link)) |
654 | return -ENOLINK; | |
b4ba4652 KG |
655 | if (link->lgr->smc_version == SMC_V2) { |
656 | struct smc_wr_v2_buf *wr_buf; | |
657 | ||
658 | rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend); | |
659 | if (rc) | |
660 | goto put_out; | |
661 | addllc = (struct smc_llc_msg_add_link *)wr_buf; | |
662 | ext = (struct smc_llc_msg_add_link_v2_ext *) | |
663 | &wr_buf->raw[sizeof(*addllc)]; | |
664 | memset(ext, 0, SMC_WR_TX_SIZE); | |
665 | } else { | |
666 | struct smc_wr_buf *wr_buf; | |
667 | ||
668 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
669 | if (rc) | |
670 | goto put_out; | |
671 | addllc = (struct smc_llc_msg_add_link *)wr_buf; | |
672 | } | |
fbed3b37 KG |
673 | |
674 | memset(addllc, 0, sizeof(*addllc)); | |
b4ba4652 | 675 | addllc->hd.common.llc_type = SMC_LLC_ADD_LINK; |
fbed3b37 KG |
676 | if (reqresp == SMC_LLC_RESP) |
677 | addllc->hd.flags |= SMC_LLC_FLAG_RESP; | |
678 | memcpy(addllc->sender_mac, mac, ETH_ALEN); | |
679 | memcpy(addllc->sender_gid, gid, SMC_GID_SIZE); | |
680 | if (link_new) { | |
681 | addllc->link_num = link_new->link_id; | |
682 | hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num); | |
683 | hton24(addllc->initial_psn, link_new->psn_initial); | |
684 | if (reqresp == SMC_LLC_REQ) | |
685 | addllc->qp_mtu = link_new->path_mtu; | |
686 | else | |
687 | addllc->qp_mtu = min(link_new->path_mtu, | |
688 | link_new->peer_mtu); | |
689 | } | |
b4ba4652 KG |
690 | if (ext && link_new) |
691 | len += smc_llc_fill_ext_v2(ext, link, link_new); | |
692 | smc_llc_init_msg_hdr(&addllc->hd, link->lgr, len); | |
52bedf37 | 693 | /* send llc message */ |
b4ba4652 KG |
694 | if (link->lgr->smc_version == SMC_V2) |
695 | rc = smc_wr_tx_v2_send(link, pend, len); | |
696 | else | |
697 | rc = smc_wr_tx_send(link, pend); | |
95f7f3e7 KG |
698 | put_out: |
699 | smc_wr_tx_link_put(link); | |
52bedf37 KG |
700 | return rc; |
701 | } | |
702 | ||
703 | /* send DELETE LINK request or response */ | |
fbed3b37 KG |
704 | int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id, |
705 | enum smc_llc_reqresp reqresp, bool orderly, | |
706 | u32 reason) | |
52bedf37 KG |
707 | { |
708 | struct smc_llc_msg_del_link *delllc; | |
709 | struct smc_wr_tx_pend_priv *pend; | |
710 | struct smc_wr_buf *wr_buf; | |
711 | int rc; | |
712 | ||
95f7f3e7 KG |
713 | if (!smc_wr_tx_link_hold(link)) |
714 | return -ENOLINK; | |
52bedf37 KG |
715 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); |
716 | if (rc) | |
95f7f3e7 | 717 | goto put_out; |
52bedf37 | 718 | delllc = (struct smc_llc_msg_del_link *)wr_buf; |
fbed3b37 KG |
719 | |
720 | memset(delllc, 0, sizeof(*delllc)); | |
b4ba4652 KG |
721 | delllc->hd.common.llc_type = SMC_LLC_DELETE_LINK; |
722 | smc_llc_init_msg_hdr(&delllc->hd, link->lgr, sizeof(*delllc)); | |
fbed3b37 KG |
723 | if (reqresp == SMC_LLC_RESP) |
724 | delllc->hd.flags |= SMC_LLC_FLAG_RESP; | |
725 | if (orderly) | |
726 | delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; | |
727 | if (link_del_id) | |
728 | delllc->link_num = link_del_id; | |
729 | else | |
730 | delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; | |
731 | delllc->reason = htonl(reason); | |
9bf9abea UB |
732 | /* send llc message */ |
733 | rc = smc_wr_tx_send(link, pend); | |
95f7f3e7 KG |
734 | put_out: |
735 | smc_wr_tx_link_put(link); | |
9bf9abea UB |
736 | return rc; |
737 | } | |
738 | ||
d97935fa KG |
739 | /* send LLC test link request */ |
740 | static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16]) | |
313164da KG |
741 | { |
742 | struct smc_llc_msg_test_link *testllc; | |
743 | struct smc_wr_tx_pend_priv *pend; | |
744 | struct smc_wr_buf *wr_buf; | |
745 | int rc; | |
746 | ||
95f7f3e7 KG |
747 | if (!smc_wr_tx_link_hold(link)) |
748 | return -ENOLINK; | |
313164da KG |
749 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); |
750 | if (rc) | |
95f7f3e7 | 751 | goto put_out; |
313164da KG |
752 | testllc = (struct smc_llc_msg_test_link *)wr_buf; |
753 | memset(testllc, 0, sizeof(*testllc)); | |
b4ba4652 KG |
754 | testllc->hd.common.llc_type = SMC_LLC_TEST_LINK; |
755 | smc_llc_init_msg_hdr(&testllc->hd, link->lgr, sizeof(*testllc)); | |
313164da KG |
756 | memcpy(testllc->user_data, user_data, sizeof(testllc->user_data)); |
757 | /* send llc message */ | |
758 | rc = smc_wr_tx_send(link, pend); | |
95f7f3e7 KG |
759 | put_out: |
760 | smc_wr_tx_link_put(link); | |
313164da KG |
761 | return rc; |
762 | } | |
763 | ||
6c8968c4 KG |
764 | /* schedule an llc send on link, may wait for buffers */ |
765 | static int smc_llc_send_message(struct smc_link *link, void *llcbuf) | |
4ed75de5 KG |
766 | { |
767 | struct smc_wr_tx_pend_priv *pend; | |
768 | struct smc_wr_buf *wr_buf; | |
769 | int rc; | |
770 | ||
95f7f3e7 | 771 | if (!smc_wr_tx_link_hold(link)) |
6c8968c4 KG |
772 | return -ENOLINK; |
773 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
4ed75de5 | 774 | if (rc) |
95f7f3e7 | 775 | goto put_out; |
6c8968c4 | 776 | memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); |
95f7f3e7 KG |
777 | rc = smc_wr_tx_send(link, pend); |
778 | put_out: | |
779 | smc_wr_tx_link_put(link); | |
780 | return rc; | |
4ed75de5 KG |
781 | } |
782 | ||
f3811fd7 KG |
783 | /* schedule an llc send on link, may wait for buffers, |
784 | * and wait for send completion notification. | |
785 | * @return 0 on success | |
786 | */ | |
787 | static int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf) | |
788 | { | |
789 | struct smc_wr_tx_pend_priv *pend; | |
790 | struct smc_wr_buf *wr_buf; | |
791 | int rc; | |
792 | ||
95f7f3e7 | 793 | if (!smc_wr_tx_link_hold(link)) |
f3811fd7 KG |
794 | return -ENOLINK; |
795 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); | |
796 | if (rc) | |
95f7f3e7 | 797 | goto put_out; |
f3811fd7 | 798 | memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg)); |
95f7f3e7 KG |
799 | rc = smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME); |
800 | put_out: | |
801 | smc_wr_tx_link_put(link); | |
802 | return rc; | |
f3811fd7 KG |
803 | } |
804 | ||
9bf9abea UB |
805 | /********************************* receive ***********************************/ |
806 | ||
336ba09f KG |
807 | static int smc_llc_alloc_alt_link(struct smc_link_group *lgr, |
808 | enum smc_lgr_type lgr_new_t) | |
809 | { | |
810 | int i; | |
811 | ||
812 | if (lgr->type == SMC_LGR_SYMMETRIC || | |
813 | (lgr->type != SMC_LGR_SINGLE && | |
814 | (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || | |
815 | lgr_new_t == SMC_LGR_ASYMMETRIC_PEER))) | |
816 | return -EMLINK; | |
817 | ||
818 | if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || | |
819 | lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) { | |
820 | for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--) | |
821 | if (lgr->lnk[i].state == SMC_LNK_UNUSED) | |
822 | return i; | |
823 | } else { | |
824 | for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) | |
825 | if (lgr->lnk[i].state == SMC_LNK_UNUSED) | |
826 | return i; | |
827 | } | |
828 | return -EMLINK; | |
829 | } | |
830 | ||
87f88cda KG |
831 | /* send one add_link_continue msg */ |
832 | static int smc_llc_add_link_cont(struct smc_link *link, | |
833 | struct smc_link *link_new, u8 *num_rkeys_todo, | |
834 | int *buf_lst, struct smc_buf_desc **buf_pos) | |
835 | { | |
836 | struct smc_llc_msg_add_link_cont *addc_llc; | |
837 | struct smc_link_group *lgr = link->lgr; | |
838 | int prim_lnk_idx, lnk_idx, i, rc; | |
839 | struct smc_wr_tx_pend_priv *pend; | |
840 | struct smc_wr_buf *wr_buf; | |
841 | struct smc_buf_desc *rmb; | |
842 | u8 n; | |
843 | ||
95f7f3e7 KG |
844 | if (!smc_wr_tx_link_hold(link)) |
845 | return -ENOLINK; | |
87f88cda KG |
846 | rc = smc_llc_add_pending_send(link, &wr_buf, &pend); |
847 | if (rc) | |
95f7f3e7 | 848 | goto put_out; |
87f88cda KG |
849 | addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf; |
850 | memset(addc_llc, 0, sizeof(*addc_llc)); | |
851 | ||
852 | prim_lnk_idx = link->link_idx; | |
853 | lnk_idx = link_new->link_idx; | |
854 | addc_llc->link_num = link_new->link_id; | |
855 | addc_llc->num_rkeys = *num_rkeys_todo; | |
856 | n = *num_rkeys_todo; | |
857 | for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) { | |
c308e9ec WG |
858 | while (*buf_pos && !(*buf_pos)->used) |
859 | *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); | |
87f88cda KG |
860 | if (!*buf_pos) { |
861 | addc_llc->num_rkeys = addc_llc->num_rkeys - | |
862 | *num_rkeys_todo; | |
863 | *num_rkeys_todo = 0; | |
864 | break; | |
865 | } | |
866 | rmb = *buf_pos; | |
867 | ||
b8d19945 WG |
868 | addc_llc->rt[i].rmb_key = htonl(rmb->mr[prim_lnk_idx]->rkey); |
869 | addc_llc->rt[i].rmb_key_new = htonl(rmb->mr[lnk_idx]->rkey); | |
870 | addc_llc->rt[i].rmb_vaddr_new = rmb->is_vm ? | |
871 | cpu_to_be64((uintptr_t)rmb->cpu_addr) : | |
87f88cda KG |
872 | cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl)); |
873 | ||
874 | (*num_rkeys_todo)--; | |
875 | *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos); | |
87f88cda | 876 | } |
b4ba4652 | 877 | addc_llc->hd.common.llc_type = SMC_LLC_ADD_LINK_CONT; |
87f88cda KG |
878 | addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont); |
879 | if (lgr->role == SMC_CLNT) | |
880 | addc_llc->hd.flags |= SMC_LLC_FLAG_RESP; | |
95f7f3e7 KG |
881 | rc = smc_wr_tx_send(link, pend); |
882 | put_out: | |
883 | smc_wr_tx_link_put(link); | |
884 | return rc; | |
87f88cda KG |
885 | } |
886 | ||
887 | static int smc_llc_cli_rkey_exchange(struct smc_link *link, | |
888 | struct smc_link *link_new) | |
889 | { | |
890 | struct smc_llc_msg_add_link_cont *addc_llc; | |
891 | struct smc_link_group *lgr = link->lgr; | |
892 | u8 max, num_rkeys_send, num_rkeys_recv; | |
893 | struct smc_llc_qentry *qentry; | |
894 | struct smc_buf_desc *buf_pos; | |
895 | int buf_lst; | |
896 | int rc = 0; | |
897 | int i; | |
898 | ||
aff7bfed | 899 | down_write(&lgr->rmbs_lock); |
87f88cda KG |
900 | num_rkeys_send = lgr->conns_num; |
901 | buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); | |
902 | do { | |
903 | qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME, | |
904 | SMC_LLC_ADD_LINK_CONT); | |
905 | if (!qentry) { | |
906 | rc = -ETIMEDOUT; | |
907 | break; | |
908 | } | |
909 | addc_llc = &qentry->msg.add_link_cont; | |
910 | num_rkeys_recv = addc_llc->num_rkeys; | |
911 | max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); | |
912 | for (i = 0; i < max; i++) { | |
913 | smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, | |
914 | addc_llc->rt[i].rmb_key, | |
915 | addc_llc->rt[i].rmb_vaddr_new, | |
916 | addc_llc->rt[i].rmb_key_new); | |
917 | num_rkeys_recv--; | |
918 | } | |
919 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
920 | rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send, | |
921 | &buf_lst, &buf_pos); | |
922 | if (rc) | |
923 | break; | |
924 | } while (num_rkeys_send || num_rkeys_recv); | |
925 | ||
aff7bfed | 926 | up_write(&lgr->rmbs_lock); |
87f88cda KG |
927 | return rc; |
928 | } | |
929 | ||
336ba09f KG |
930 | /* prepare and send an add link reject response */ |
931 | static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry) | |
932 | { | |
933 | qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; | |
934 | qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ; | |
935 | qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH; | |
b4ba4652 KG |
936 | smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr, |
937 | sizeof(qentry->msg)); | |
336ba09f KG |
938 | return smc_llc_send_message(qentry->link, &qentry->msg); |
939 | } | |
940 | ||
b1570a87 KG |
941 | static int smc_llc_cli_conf_link(struct smc_link *link, |
942 | struct smc_init_info *ini, | |
943 | struct smc_link *link_new, | |
944 | enum smc_lgr_type lgr_new_t) | |
945 | { | |
946 | struct smc_link_group *lgr = link->lgr; | |
b1570a87 KG |
947 | struct smc_llc_qentry *qentry = NULL; |
948 | int rc = 0; | |
949 | ||
950 | /* receive CONFIRM LINK request over RoCE fabric */ | |
951 | qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0); | |
952 | if (!qentry) { | |
953 | rc = smc_llc_send_delete_link(link, link_new->link_id, | |
954 | SMC_LLC_REQ, false, | |
955 | SMC_LLC_DEL_LOST_PATH); | |
956 | return -ENOLINK; | |
957 | } | |
b4ba4652 | 958 | if (qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) { |
b1570a87 | 959 | /* received DELETE_LINK instead */ |
b1570a87 KG |
960 | qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; |
961 | smc_llc_send_message(link, &qentry->msg); | |
962 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
963 | return -ENOLINK; | |
964 | } | |
649758ff | 965 | smc_llc_save_peer_uid(qentry); |
b1570a87 KG |
966 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); |
967 | ||
968 | rc = smc_ib_modify_qp_rts(link_new); | |
969 | if (rc) { | |
970 | smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, | |
971 | false, SMC_LLC_DEL_LOST_PATH); | |
972 | return -ENOLINK; | |
973 | } | |
974 | smc_wr_remember_qp_attr(link_new); | |
975 | ||
976 | rc = smcr_buf_reg_lgr(link_new); | |
977 | if (rc) { | |
978 | smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, | |
979 | false, SMC_LLC_DEL_LOST_PATH); | |
980 | return -ENOLINK; | |
981 | } | |
982 | ||
983 | /* send CONFIRM LINK response over RoCE fabric */ | |
984 | rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP); | |
985 | if (rc) { | |
986 | smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, | |
987 | false, SMC_LLC_DEL_LOST_PATH); | |
988 | return -ENOLINK; | |
989 | } | |
990 | smc_llc_link_active(link_new); | |
ad6c111b KG |
991 | if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || |
992 | lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) | |
993 | smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); | |
994 | else | |
995 | smcr_lgr_set_type(lgr, lgr_new_t); | |
b1570a87 KG |
996 | return 0; |
997 | } | |
998 | ||
b4ba4652 | 999 | static void smc_llc_save_add_link_rkeys(struct smc_link *link, |
27ef6a99 GW |
1000 | struct smc_link *link_new, |
1001 | u8 *llc_msg) | |
b4ba4652 KG |
1002 | { |
1003 | struct smc_llc_msg_add_link_v2_ext *ext; | |
1004 | struct smc_link_group *lgr = link->lgr; | |
1005 | int max, i; | |
1006 | ||
27ef6a99 | 1007 | ext = (struct smc_llc_msg_add_link_v2_ext *)(llc_msg + |
b4ba4652 KG |
1008 | SMC_WR_TX_SIZE); |
1009 | max = min_t(u8, ext->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); | |
aff7bfed | 1010 | down_write(&lgr->rmbs_lock); |
b4ba4652 KG |
1011 | for (i = 0; i < max; i++) { |
1012 | smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, | |
1013 | ext->rt[i].rmb_key, | |
1014 | ext->rt[i].rmb_vaddr_new, | |
1015 | ext->rt[i].rmb_key_new); | |
1016 | } | |
aff7bfed | 1017 | up_write(&lgr->rmbs_lock); |
b4ba4652 KG |
1018 | } |
1019 | ||
336ba09f KG |
1020 | static void smc_llc_save_add_link_info(struct smc_link *link, |
1021 | struct smc_llc_msg_add_link *add_llc) | |
1022 | { | |
1023 | link->peer_qpn = ntoh24(add_llc->sender_qp_num); | |
1024 | memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE); | |
1025 | memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN); | |
1026 | link->peer_psn = ntoh24(add_llc->initial_psn); | |
1027 | link->peer_mtu = add_llc->qp_mtu; | |
1028 | } | |
1029 | ||
1030 | /* as an SMC client, process an add link request */ | |
1031 | int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry) | |
1032 | { | |
1033 | struct smc_llc_msg_add_link *llc = &qentry->msg.add_link; | |
1034 | enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; | |
1035 | struct smc_link_group *lgr = smc_get_lgr(link); | |
ed990df2 | 1036 | struct smc_init_info *ini = NULL; |
336ba09f | 1037 | struct smc_link *lnk_new = NULL; |
336ba09f KG |
1038 | int lnk_idx, rc = 0; |
1039 | ||
fffe83c8 KG |
1040 | if (!llc->qp_mtu) |
1041 | goto out_reject; | |
1042 | ||
ed990df2 KG |
1043 | ini = kzalloc(sizeof(*ini), GFP_KERNEL); |
1044 | if (!ini) { | |
1045 | rc = -ENOMEM; | |
1046 | goto out_reject; | |
1047 | } | |
1048 | ||
69b888e3 GW |
1049 | if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) { |
1050 | rc = 0; | |
1051 | goto out_reject; | |
1052 | } | |
1053 | ||
ed990df2 | 1054 | ini->vlan_id = lgr->vlan_id; |
b4ba4652 KG |
1055 | if (lgr->smc_version == SMC_V2) { |
1056 | ini->check_smcrv2 = true; | |
1057 | ini->smcrv2.saddr = lgr->saddr; | |
1058 | ini->smcrv2.daddr = smc_ib_gid_to_ipv4(llc->sender_gid); | |
1059 | } | |
ed990df2 | 1060 | smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); |
336ba09f | 1061 | if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && |
b4ba4652 KG |
1062 | (lgr->smc_version == SMC_V2 || |
1063 | !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN))) { | |
1064 | if (!ini->ib_dev && !ini->smcrv2.ib_dev_v2) | |
336ba09f KG |
1065 | goto out_reject; |
1066 | lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; | |
1067 | } | |
b4ba4652 KG |
1068 | if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) { |
1069 | lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; | |
1070 | ini->smcrv2.ib_dev_v2 = link->smcibdev; | |
1071 | ini->smcrv2.ib_port_v2 = link->ibport; | |
1072 | } else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) { | |
336ba09f | 1073 | lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; |
ed990df2 KG |
1074 | ini->ib_dev = link->smcibdev; |
1075 | ini->ib_port = link->ibport; | |
336ba09f KG |
1076 | } |
1077 | lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); | |
1078 | if (lnk_idx < 0) | |
1079 | goto out_reject; | |
1080 | lnk_new = &lgr->lnk[lnk_idx]; | |
ed990df2 | 1081 | rc = smcr_link_init(lgr, lnk_new, lnk_idx, ini); |
336ba09f KG |
1082 | if (rc) |
1083 | goto out_reject; | |
1084 | smc_llc_save_add_link_info(lnk_new, llc); | |
45fa8da0 KG |
1085 | lnk_new->link_id = llc->link_num; /* SMC server assigns link id */ |
1086 | smc_llc_link_set_uid(lnk_new); | |
336ba09f KG |
1087 | |
1088 | rc = smc_ib_ready_link(lnk_new); | |
1089 | if (rc) | |
1090 | goto out_clear_lnk; | |
1091 | ||
1092 | rc = smcr_buf_map_lgr(lnk_new); | |
1093 | if (rc) | |
1094 | goto out_clear_lnk; | |
1095 | ||
1096 | rc = smc_llc_send_add_link(link, | |
b4ba4652 | 1097 | lnk_new->smcibdev->mac[lnk_new->ibport - 1], |
336ba09f KG |
1098 | lnk_new->gid, lnk_new, SMC_LLC_RESP); |
1099 | if (rc) | |
1100 | goto out_clear_lnk; | |
b4ba4652 | 1101 | if (lgr->smc_version == SMC_V2) { |
27ef6a99 GW |
1102 | u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ? |
1103 | (u8 *)lgr->wr_rx_buf_v2 : (u8 *)llc; | |
1104 | smc_llc_save_add_link_rkeys(link, lnk_new, llc_msg); | |
b4ba4652 KG |
1105 | } else { |
1106 | rc = smc_llc_cli_rkey_exchange(link, lnk_new); | |
1107 | if (rc) { | |
1108 | rc = 0; | |
1109 | goto out_clear_lnk; | |
1110 | } | |
336ba09f | 1111 | } |
ed990df2 | 1112 | rc = smc_llc_cli_conf_link(link, ini, lnk_new, lgr_new_t); |
336ba09f KG |
1113 | if (!rc) |
1114 | goto out; | |
1115 | out_clear_lnk: | |
8f3d65c1 | 1116 | lnk_new->state = SMC_LNK_INACTIVE; |
0a99be43 | 1117 | smcr_link_clear(lnk_new, false); |
336ba09f KG |
1118 | out_reject: |
1119 | smc_llc_cli_add_link_reject(qentry); | |
1120 | out: | |
ed990df2 | 1121 | kfree(ini); |
336ba09f KG |
1122 | kfree(qentry); |
1123 | return rc; | |
1124 | } | |
1125 | ||
b4ba4652 KG |
1126 | static void smc_llc_send_request_add_link(struct smc_link *link) |
1127 | { | |
1128 | struct smc_llc_msg_req_add_link_v2 *llc; | |
1129 | struct smc_wr_tx_pend_priv *pend; | |
1130 | struct smc_wr_v2_buf *wr_buf; | |
1131 | struct smc_gidlist gidlist; | |
1132 | int rc, len, i; | |
1133 | ||
1134 | if (!smc_wr_tx_link_hold(link)) | |
1135 | return; | |
1136 | if (link->lgr->type == SMC_LGR_SYMMETRIC || | |
1137 | link->lgr->type == SMC_LGR_ASYMMETRIC_PEER) | |
1138 | goto put_out; | |
1139 | ||
1140 | smc_fill_gid_list(link->lgr, &gidlist, link->smcibdev, link->gid); | |
1141 | if (gidlist.len <= 1) | |
1142 | goto put_out; | |
1143 | ||
1144 | rc = smc_llc_add_pending_send_v2(link, &wr_buf, &pend); | |
1145 | if (rc) | |
1146 | goto put_out; | |
1147 | llc = (struct smc_llc_msg_req_add_link_v2 *)wr_buf; | |
1148 | memset(llc, 0, SMC_WR_TX_SIZE); | |
1149 | ||
1150 | llc->hd.common.llc_type = SMC_LLC_REQ_ADD_LINK; | |
1151 | for (i = 0; i < gidlist.len; i++) | |
1152 | memcpy(llc->gid[i], gidlist.list[i], sizeof(gidlist.list[0])); | |
1153 | llc->gid_cnt = gidlist.len; | |
1154 | len = sizeof(*llc) + (gidlist.len * sizeof(gidlist.list[0])); | |
1155 | smc_llc_init_msg_hdr(&llc->hd, link->lgr, len); | |
1156 | rc = smc_wr_tx_v2_send(link, pend, len); | |
1157 | if (!rc) | |
1158 | /* set REQ_ADD_LINK flow and wait for response from peer */ | |
1159 | link->lgr->llc_flow_lcl.type = SMC_LLC_FLOW_REQ_ADD_LINK; | |
1160 | put_out: | |
1161 | smc_wr_tx_link_put(link); | |
1162 | } | |
1163 | ||
c48254fa KG |
1164 | /* as an SMC client, invite server to start the add_link processing */ |
1165 | static void smc_llc_cli_add_link_invite(struct smc_link *link, | |
1166 | struct smc_llc_qentry *qentry) | |
1167 | { | |
1168 | struct smc_link_group *lgr = smc_get_lgr(link); | |
ed990df2 | 1169 | struct smc_init_info *ini = NULL; |
c48254fa | 1170 | |
b4ba4652 KG |
1171 | if (lgr->smc_version == SMC_V2) { |
1172 | smc_llc_send_request_add_link(link); | |
1173 | goto out; | |
1174 | } | |
1175 | ||
c48254fa KG |
1176 | if (lgr->type == SMC_LGR_SYMMETRIC || |
1177 | lgr->type == SMC_LGR_ASYMMETRIC_PEER) | |
1178 | goto out; | |
1179 | ||
69b888e3 GW |
1180 | if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) |
1181 | goto out; | |
1182 | ||
ed990df2 KG |
1183 | ini = kzalloc(sizeof(*ini), GFP_KERNEL); |
1184 | if (!ini) | |
1185 | goto out; | |
1186 | ||
1187 | ini->vlan_id = lgr->vlan_id; | |
1188 | smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); | |
1189 | if (!ini->ib_dev) | |
c48254fa KG |
1190 | goto out; |
1191 | ||
ed990df2 KG |
1192 | smc_llc_send_add_link(link, ini->ib_dev->mac[ini->ib_port - 1], |
1193 | ini->ib_gid, NULL, SMC_LLC_REQ); | |
c48254fa | 1194 | out: |
ed990df2 | 1195 | kfree(ini); |
c48254fa KG |
1196 | kfree(qentry); |
1197 | } | |
1198 | ||
fffe83c8 KG |
1199 | static bool smc_llc_is_empty_llc_message(union smc_llc_msg *llc) |
1200 | { | |
1201 | int i; | |
1202 | ||
1203 | for (i = 0; i < ARRAY_SIZE(llc->raw.data); i++) | |
1204 | if (llc->raw.data[i]) | |
1205 | return false; | |
1206 | return true; | |
1207 | } | |
1208 | ||
c48254fa KG |
1209 | static bool smc_llc_is_local_add_link(union smc_llc_msg *llc) |
1210 | { | |
b4ba4652 | 1211 | if (llc->raw.hdr.common.llc_type == SMC_LLC_ADD_LINK && |
fffe83c8 | 1212 | smc_llc_is_empty_llc_message(llc)) |
c48254fa KG |
1213 | return true; |
1214 | return false; | |
1215 | } | |
1216 | ||
b1570a87 KG |
1217 | static void smc_llc_process_cli_add_link(struct smc_link_group *lgr) |
1218 | { | |
1219 | struct smc_llc_qentry *qentry; | |
1220 | ||
1221 | qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); | |
1222 | ||
b5dd4d69 | 1223 | down_write(&lgr->llc_conf_mutex); |
c48254fa KG |
1224 | if (smc_llc_is_local_add_link(&qentry->msg)) |
1225 | smc_llc_cli_add_link_invite(qentry->link, qentry); | |
1226 | else | |
1227 | smc_llc_cli_add_link(qentry->link, qentry); | |
b5dd4d69 | 1228 | up_write(&lgr->llc_conf_mutex); |
b1570a87 KG |
1229 | } |
1230 | ||
9c416878 KG |
1231 | static int smc_llc_active_link_count(struct smc_link_group *lgr) |
1232 | { | |
1233 | int i, link_count = 0; | |
1234 | ||
1235 | for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { | |
741a49a4 | 1236 | if (!smc_link_active(&lgr->lnk[i])) |
9c416878 KG |
1237 | continue; |
1238 | link_count++; | |
1239 | } | |
1240 | return link_count; | |
1241 | } | |
1242 | ||
c9a5d243 KG |
1243 | /* find the asymmetric link when 3 links are established */ |
1244 | static struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr) | |
1245 | { | |
1246 | int asym_idx = -ENOENT; | |
1247 | int i, j, k; | |
1248 | bool found; | |
1249 | ||
1250 | /* determine asymmetric link */ | |
1251 | found = false; | |
1252 | for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { | |
1253 | for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) { | |
1254 | if (!smc_link_usable(&lgr->lnk[i]) || | |
1255 | !smc_link_usable(&lgr->lnk[j])) | |
1256 | continue; | |
1257 | if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid, | |
1258 | SMC_GID_SIZE)) { | |
1259 | found = true; /* asym_lnk is i or j */ | |
1260 | break; | |
1261 | } | |
1262 | } | |
1263 | if (found) | |
1264 | break; | |
1265 | } | |
1266 | if (!found) | |
1267 | goto out; /* no asymmetric link */ | |
1268 | for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) { | |
1269 | if (!smc_link_usable(&lgr->lnk[k])) | |
1270 | continue; | |
1271 | if (k != i && | |
1272 | !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid, | |
1273 | SMC_GID_SIZE)) { | |
1274 | asym_idx = i; | |
1275 | break; | |
1276 | } | |
1277 | if (k != j && | |
1278 | !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid, | |
1279 | SMC_GID_SIZE)) { | |
1280 | asym_idx = j; | |
1281 | break; | |
1282 | } | |
1283 | } | |
1284 | out: | |
1285 | return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx]; | |
1286 | } | |
1287 | ||
1288 | static void smc_llc_delete_asym_link(struct smc_link_group *lgr) | |
1289 | { | |
1290 | struct smc_link *lnk_new = NULL, *lnk_asym; | |
1291 | struct smc_llc_qentry *qentry; | |
1292 | int rc; | |
1293 | ||
1294 | lnk_asym = smc_llc_find_asym_link(lgr); | |
1295 | if (!lnk_asym) | |
1296 | return; /* no asymmetric link */ | |
1297 | if (!smc_link_downing(&lnk_asym->state)) | |
1298 | return; | |
c6f02ebe | 1299 | lnk_new = smc_switch_conns(lgr, lnk_asym, false); |
c9a5d243 KG |
1300 | smc_wr_tx_wait_no_pending_sends(lnk_asym); |
1301 | if (!lnk_new) | |
1302 | goto out_free; | |
1303 | /* change flow type from ADD_LINK into DEL_LINK */ | |
1304 | lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK; | |
1305 | rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ, | |
1306 | true, SMC_LLC_DEL_NO_ASYM_NEEDED); | |
1307 | if (rc) { | |
1308 | smcr_link_down_cond(lnk_new); | |
1309 | goto out_free; | |
1310 | } | |
1311 | qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME, | |
1312 | SMC_LLC_DELETE_LINK); | |
1313 | if (!qentry) { | |
1314 | smcr_link_down_cond(lnk_new); | |
1315 | goto out_free; | |
1316 | } | |
1317 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1318 | out_free: | |
0a99be43 | 1319 | smcr_link_clear(lnk_asym, true); |
c9a5d243 KG |
1320 | } |
1321 | ||
57b49924 KG |
1322 | static int smc_llc_srv_rkey_exchange(struct smc_link *link, |
1323 | struct smc_link *link_new) | |
1324 | { | |
1325 | struct smc_llc_msg_add_link_cont *addc_llc; | |
1326 | struct smc_link_group *lgr = link->lgr; | |
1327 | u8 max, num_rkeys_send, num_rkeys_recv; | |
1328 | struct smc_llc_qentry *qentry = NULL; | |
1329 | struct smc_buf_desc *buf_pos; | |
1330 | int buf_lst; | |
1331 | int rc = 0; | |
1332 | int i; | |
1333 | ||
aff7bfed | 1334 | down_write(&lgr->rmbs_lock); |
57b49924 KG |
1335 | num_rkeys_send = lgr->conns_num; |
1336 | buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst); | |
1337 | do { | |
1338 | smc_llc_add_link_cont(link, link_new, &num_rkeys_send, | |
1339 | &buf_lst, &buf_pos); | |
1340 | qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, | |
1341 | SMC_LLC_ADD_LINK_CONT); | |
1342 | if (!qentry) { | |
1343 | rc = -ETIMEDOUT; | |
1344 | goto out; | |
1345 | } | |
1346 | addc_llc = &qentry->msg.add_link_cont; | |
1347 | num_rkeys_recv = addc_llc->num_rkeys; | |
1348 | max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG); | |
1349 | for (i = 0; i < max; i++) { | |
1350 | smc_rtoken_set(lgr, link->link_idx, link_new->link_idx, | |
1351 | addc_llc->rt[i].rmb_key, | |
1352 | addc_llc->rt[i].rmb_vaddr_new, | |
1353 | addc_llc->rt[i].rmb_key_new); | |
1354 | num_rkeys_recv--; | |
1355 | } | |
1356 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1357 | } while (num_rkeys_send || num_rkeys_recv); | |
1358 | out: | |
aff7bfed | 1359 | up_write(&lgr->rmbs_lock); |
57b49924 KG |
1360 | return rc; |
1361 | } | |
1362 | ||
1551c95b KG |
1363 | static int smc_llc_srv_conf_link(struct smc_link *link, |
1364 | struct smc_link *link_new, | |
1365 | enum smc_lgr_type lgr_new_t) | |
1366 | { | |
1367 | struct smc_link_group *lgr = link->lgr; | |
1368 | struct smc_llc_qentry *qentry = NULL; | |
1369 | int rc; | |
1370 | ||
1371 | /* send CONFIRM LINK request over the RoCE fabric */ | |
1372 | rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ); | |
1373 | if (rc) | |
1374 | return -ENOLINK; | |
1375 | /* receive CONFIRM LINK response over the RoCE fabric */ | |
a35fffbf KG |
1376 | qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME, 0); |
1377 | if (!qentry || | |
b4ba4652 | 1378 | qentry->msg.raw.hdr.common.llc_type != SMC_LLC_CONFIRM_LINK) { |
1551c95b KG |
1379 | /* send DELETE LINK */ |
1380 | smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ, | |
1381 | false, SMC_LLC_DEL_LOST_PATH); | |
a35fffbf KG |
1382 | if (qentry) |
1383 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1551c95b KG |
1384 | return -ENOLINK; |
1385 | } | |
649758ff | 1386 | smc_llc_save_peer_uid(qentry); |
1551c95b | 1387 | smc_llc_link_active(link_new); |
ad6c111b KG |
1388 | if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL || |
1389 | lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) | |
1390 | smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx); | |
1391 | else | |
1392 | smcr_lgr_set_type(lgr, lgr_new_t); | |
1551c95b KG |
1393 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); |
1394 | return 0; | |
1395 | } | |
1396 | ||
b4ba4652 KG |
1397 | static void smc_llc_send_req_add_link_response(struct smc_llc_qentry *qentry) |
1398 | { | |
1399 | qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP; | |
1400 | smc_llc_init_msg_hdr(&qentry->msg.raw.hdr, qentry->link->lgr, | |
1401 | sizeof(qentry->msg)); | |
1402 | memset(&qentry->msg.raw.data, 0, sizeof(qentry->msg.raw.data)); | |
1403 | smc_llc_send_message(qentry->link, &qentry->msg); | |
1404 | } | |
1405 | ||
1406 | int smc_llc_srv_add_link(struct smc_link *link, | |
1407 | struct smc_llc_qentry *req_qentry) | |
2d2209f2 KG |
1408 | { |
1409 | enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC; | |
1410 | struct smc_link_group *lgr = link->lgr; | |
1411 | struct smc_llc_msg_add_link *add_llc; | |
1412 | struct smc_llc_qentry *qentry = NULL; | |
b4ba4652 | 1413 | bool send_req_add_link_resp = false; |
ed990df2 | 1414 | struct smc_link *link_new = NULL; |
b4ba4652 | 1415 | struct smc_init_info *ini = NULL; |
2d2209f2 KG |
1416 | int lnk_idx, rc = 0; |
1417 | ||
b4ba4652 KG |
1418 | if (req_qentry && |
1419 | req_qentry->msg.raw.hdr.common.llc_type == SMC_LLC_REQ_ADD_LINK) | |
1420 | send_req_add_link_resp = true; | |
1421 | ||
ed990df2 | 1422 | ini = kzalloc(sizeof(*ini), GFP_KERNEL); |
b4ba4652 KG |
1423 | if (!ini) { |
1424 | rc = -ENOMEM; | |
1425 | goto out; | |
1426 | } | |
ed990df2 | 1427 | |
69b888e3 GW |
1428 | if (lgr->type == SMC_LGR_SINGLE && lgr->max_links <= 1) { |
1429 | rc = 0; | |
1430 | goto out; | |
1431 | } | |
1432 | ||
2d2209f2 | 1433 | /* ignore client add link recommendation, start new flow */ |
ed990df2 | 1434 | ini->vlan_id = lgr->vlan_id; |
b4ba4652 KG |
1435 | if (lgr->smc_version == SMC_V2) { |
1436 | ini->check_smcrv2 = true; | |
1437 | ini->smcrv2.saddr = lgr->saddr; | |
1438 | if (send_req_add_link_resp) { | |
1439 | struct smc_llc_msg_req_add_link_v2 *req_add = | |
1440 | &req_qentry->msg.req_add_link; | |
1441 | ||
1442 | ini->smcrv2.daddr = smc_ib_gid_to_ipv4(req_add->gid[0]); | |
1443 | } | |
1444 | } | |
ed990df2 | 1445 | smc_pnet_find_alt_roce(lgr, ini, link->smcibdev); |
b4ba4652 KG |
1446 | if (lgr->smc_version == SMC_V2 && !ini->smcrv2.ib_dev_v2) { |
1447 | lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; | |
1448 | ini->smcrv2.ib_dev_v2 = link->smcibdev; | |
1449 | ini->smcrv2.ib_port_v2 = link->ibport; | |
1450 | } else if (lgr->smc_version < SMC_V2 && !ini->ib_dev) { | |
2d2209f2 | 1451 | lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL; |
ed990df2 KG |
1452 | ini->ib_dev = link->smcibdev; |
1453 | ini->ib_port = link->ibport; | |
2d2209f2 KG |
1454 | } |
1455 | lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t); | |
ed990df2 KG |
1456 | if (lnk_idx < 0) { |
1457 | rc = 0; | |
1458 | goto out; | |
1459 | } | |
2d2209f2 | 1460 | |
ed990df2 | 1461 | rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, ini); |
2d2209f2 | 1462 | if (rc) |
ed990df2 | 1463 | goto out; |
2d2209f2 | 1464 | link_new = &lgr->lnk[lnk_idx]; |
b4ba4652 KG |
1465 | |
1466 | rc = smcr_buf_map_lgr(link_new); | |
1467 | if (rc) | |
1468 | goto out_err; | |
1469 | ||
2d2209f2 | 1470 | rc = smc_llc_send_add_link(link, |
b4ba4652 | 1471 | link_new->smcibdev->mac[link_new->ibport-1], |
2d2209f2 KG |
1472 | link_new->gid, link_new, SMC_LLC_REQ); |
1473 | if (rc) | |
1474 | goto out_err; | |
b4ba4652 | 1475 | send_req_add_link_resp = false; |
2d2209f2 KG |
1476 | /* receive ADD LINK response over the RoCE fabric */ |
1477 | qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK); | |
1478 | if (!qentry) { | |
1479 | rc = -ETIMEDOUT; | |
1480 | goto out_err; | |
1481 | } | |
1482 | add_llc = &qentry->msg.add_link; | |
1483 | if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) { | |
1484 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1485 | rc = -ENOLINK; | |
1486 | goto out_err; | |
1487 | } | |
1488 | if (lgr->type == SMC_LGR_SINGLE && | |
1489 | (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) && | |
b4ba4652 KG |
1490 | (lgr->smc_version == SMC_V2 || |
1491 | !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN)))) { | |
2d2209f2 KG |
1492 | lgr_new_t = SMC_LGR_ASYMMETRIC_PEER; |
1493 | } | |
1494 | smc_llc_save_add_link_info(link_new, add_llc); | |
1495 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1496 | ||
1497 | rc = smc_ib_ready_link(link_new); | |
2d2209f2 KG |
1498 | if (rc) |
1499 | goto out_err; | |
1500 | rc = smcr_buf_reg_lgr(link_new); | |
1501 | if (rc) | |
1502 | goto out_err; | |
b4ba4652 | 1503 | if (lgr->smc_version == SMC_V2) { |
27ef6a99 GW |
1504 | u8 *llc_msg = smc_link_shared_v2_rxbuf(link) ? |
1505 | (u8 *)lgr->wr_rx_buf_v2 : (u8 *)add_llc; | |
1506 | smc_llc_save_add_link_rkeys(link, link_new, llc_msg); | |
b4ba4652 KG |
1507 | } else { |
1508 | rc = smc_llc_srv_rkey_exchange(link, link_new); | |
1509 | if (rc) | |
1510 | goto out_err; | |
1511 | } | |
1551c95b | 1512 | rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t); |
2d2209f2 KG |
1513 | if (rc) |
1514 | goto out_err; | |
ed990df2 | 1515 | kfree(ini); |
2d2209f2 KG |
1516 | return 0; |
1517 | out_err: | |
ed990df2 KG |
1518 | if (link_new) { |
1519 | link_new->state = SMC_LNK_INACTIVE; | |
1520 | smcr_link_clear(link_new, false); | |
1521 | } | |
1522 | out: | |
1523 | kfree(ini); | |
b4ba4652 KG |
1524 | if (send_req_add_link_resp) |
1525 | smc_llc_send_req_add_link_response(req_qentry); | |
2d2209f2 KG |
1526 | return rc; |
1527 | } | |
1528 | ||
1529 | static void smc_llc_process_srv_add_link(struct smc_link_group *lgr) | |
1530 | { | |
1531 | struct smc_link *link = lgr->llc_flow_lcl.qentry->link; | |
b4ba4652 | 1532 | struct smc_llc_qentry *qentry; |
2d2209f2 KG |
1533 | int rc; |
1534 | ||
b4ba4652 | 1535 | qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); |
2d2209f2 | 1536 | |
b5dd4d69 | 1537 | down_write(&lgr->llc_conf_mutex); |
b4ba4652 | 1538 | rc = smc_llc_srv_add_link(link, qentry); |
2d2209f2 KG |
1539 | if (!rc && lgr->type == SMC_LGR_SYMMETRIC) { |
1540 | /* delete any asymmetric link */ | |
c9a5d243 | 1541 | smc_llc_delete_asym_link(lgr); |
2d2209f2 | 1542 | } |
b5dd4d69 | 1543 | up_write(&lgr->llc_conf_mutex); |
b4ba4652 | 1544 | kfree(qentry); |
2d2209f2 KG |
1545 | } |
1546 | ||
c48254fa KG |
1547 | /* enqueue a local add_link req to trigger a new add_link flow */ |
1548 | void smc_llc_add_link_local(struct smc_link *link) | |
4dadd151 | 1549 | { |
16cb3653 | 1550 | struct smc_llc_msg_add_link add_llc = {}; |
4dadd151 | 1551 | |
b4ba4652 KG |
1552 | add_llc.hd.common.llc_type = SMC_LLC_ADD_LINK; |
1553 | smc_llc_init_msg_hdr(&add_llc.hd, link->lgr, sizeof(add_llc)); | |
c48254fa | 1554 | /* no dev and port needed */ |
4dadd151 KG |
1555 | smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc); |
1556 | } | |
1557 | ||
b45e7f98 KG |
1558 | /* worker to process an add link message */ |
1559 | static void smc_llc_add_link_work(struct work_struct *work) | |
1560 | { | |
1561 | struct smc_link_group *lgr = container_of(work, struct smc_link_group, | |
1562 | llc_add_link_work); | |
1563 | ||
1564 | if (list_empty(&lgr->list)) { | |
1565 | /* link group is terminating */ | |
1566 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1567 | goto out; | |
1568 | } | |
1569 | ||
b1570a87 KG |
1570 | if (lgr->role == SMC_CLNT) |
1571 | smc_llc_process_cli_add_link(lgr); | |
2d2209f2 KG |
1572 | else |
1573 | smc_llc_process_srv_add_link(lgr); | |
b45e7f98 | 1574 | out: |
b4ba4652 KG |
1575 | if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_REQ_ADD_LINK) |
1576 | smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); | |
b45e7f98 KG |
1577 | } |
1578 | ||
4dadd151 KG |
1579 | /* enqueue a local del_link msg to trigger a new del_link flow, |
1580 | * called only for role SMC_SERV | |
1581 | */ | |
1582 | void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id) | |
1583 | { | |
16cb3653 | 1584 | struct smc_llc_msg_del_link del_llc = {}; |
4dadd151 | 1585 | |
b4ba4652 KG |
1586 | del_llc.hd.common.llc_type = SMC_LLC_DELETE_LINK; |
1587 | smc_llc_init_msg_hdr(&del_llc.hd, link->lgr, sizeof(del_llc)); | |
4dadd151 KG |
1588 | del_llc.link_num = del_link_id; |
1589 | del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH); | |
1590 | del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; | |
1591 | smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc); | |
1592 | } | |
1593 | ||
9c416878 KG |
1594 | static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr) |
1595 | { | |
1596 | struct smc_link *lnk_del = NULL, *lnk_asym, *lnk; | |
1597 | struct smc_llc_msg_del_link *del_llc; | |
1598 | struct smc_llc_qentry *qentry; | |
1599 | int active_links; | |
1600 | int lnk_idx; | |
1601 | ||
1602 | qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); | |
1603 | lnk = qentry->link; | |
1604 | del_llc = &qentry->msg.delete_link; | |
1605 | ||
1606 | if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { | |
1607 | smc_lgr_terminate_sched(lgr); | |
1608 | goto out; | |
1609 | } | |
b5dd4d69 | 1610 | down_write(&lgr->llc_conf_mutex); |
9c416878 KG |
1611 | /* delete single link */ |
1612 | for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) { | |
1613 | if (lgr->lnk[lnk_idx].link_id != del_llc->link_num) | |
1614 | continue; | |
1615 | lnk_del = &lgr->lnk[lnk_idx]; | |
1616 | break; | |
1617 | } | |
1618 | del_llc->hd.flags |= SMC_LLC_FLAG_RESP; | |
1619 | if (!lnk_del) { | |
1620 | /* link was not found */ | |
1621 | del_llc->reason = htonl(SMC_LLC_DEL_NOLNK); | |
1622 | smc_llc_send_message(lnk, &qentry->msg); | |
1623 | goto out_unlock; | |
1624 | } | |
1625 | lnk_asym = smc_llc_find_asym_link(lgr); | |
1626 | ||
1627 | del_llc->reason = 0; | |
1628 | smc_llc_send_message(lnk, &qentry->msg); /* response */ | |
1629 | ||
8f3d65c1 KG |
1630 | if (smc_link_downing(&lnk_del->state)) |
1631 | smc_switch_conns(lgr, lnk_del, false); | |
0a99be43 | 1632 | smcr_link_clear(lnk_del, true); |
9c416878 KG |
1633 | |
1634 | active_links = smc_llc_active_link_count(lgr); | |
1635 | if (lnk_del == lnk_asym) { | |
1636 | /* expected deletion of asym link, don't change lgr state */ | |
1637 | } else if (active_links == 1) { | |
ad6c111b | 1638 | smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); |
9c416878 | 1639 | } else if (!active_links) { |
ad6c111b | 1640 | smcr_lgr_set_type(lgr, SMC_LGR_NONE); |
9c416878 KG |
1641 | smc_lgr_terminate_sched(lgr); |
1642 | } | |
1643 | out_unlock: | |
b5dd4d69 | 1644 | up_write(&lgr->llc_conf_mutex); |
9c416878 KG |
1645 | out: |
1646 | kfree(qentry); | |
1647 | } | |
1648 | ||
f3811fd7 KG |
1649 | /* try to send a DELETE LINK ALL request on any active link, |
1650 | * waiting for send completion | |
1651 | */ | |
1652 | void smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn) | |
1653 | { | |
7e94e46c | 1654 | struct smc_llc_msg_del_link delllc = {}; |
f3811fd7 KG |
1655 | int i; |
1656 | ||
b4ba4652 KG |
1657 | delllc.hd.common.llc_type = SMC_LLC_DELETE_LINK; |
1658 | smc_llc_init_msg_hdr(&delllc.hd, lgr, sizeof(delllc)); | |
f3811fd7 KG |
1659 | if (ord) |
1660 | delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY; | |
1661 | delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL; | |
1662 | delllc.reason = htonl(rsn); | |
1663 | ||
1664 | for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { | |
90cee52f | 1665 | if (!smc_link_sendable(&lgr->lnk[i])) |
f3811fd7 KG |
1666 | continue; |
1667 | if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc)) | |
1668 | break; | |
1669 | } | |
1670 | } | |
1671 | ||
08ae27dd KG |
1672 | static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr) |
1673 | { | |
1674 | struct smc_llc_msg_del_link *del_llc; | |
1675 | struct smc_link *lnk, *lnk_del; | |
1676 | struct smc_llc_qentry *qentry; | |
1677 | int active_links; | |
1678 | int i; | |
1679 | ||
b5dd4d69 | 1680 | down_write(&lgr->llc_conf_mutex); |
08ae27dd KG |
1681 | qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl); |
1682 | lnk = qentry->link; | |
1683 | del_llc = &qentry->msg.delete_link; | |
1684 | ||
1685 | if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) { | |
1686 | /* delete entire lgr */ | |
f3811fd7 KG |
1687 | smc_llc_send_link_delete_all(lgr, true, ntohl( |
1688 | qentry->msg.delete_link.reason)); | |
08ae27dd KG |
1689 | smc_lgr_terminate_sched(lgr); |
1690 | goto out; | |
1691 | } | |
1692 | /* delete single link */ | |
1693 | lnk_del = NULL; | |
1694 | for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { | |
1695 | if (lgr->lnk[i].link_id == del_llc->link_num) { | |
1696 | lnk_del = &lgr->lnk[i]; | |
1697 | break; | |
1698 | } | |
1699 | } | |
1700 | if (!lnk_del) | |
1701 | goto out; /* asymmetric link already deleted */ | |
1702 | ||
1703 | if (smc_link_downing(&lnk_del->state)) { | |
b7eede75 KG |
1704 | if (smc_switch_conns(lgr, lnk_del, false)) |
1705 | smc_wr_tx_wait_no_pending_sends(lnk_del); | |
08ae27dd KG |
1706 | } |
1707 | if (!list_empty(&lgr->list)) { | |
1708 | /* qentry is either a request from peer (send it back to | |
1709 | * initiate the DELETE_LINK processing), or a locally | |
1710 | * enqueued DELETE_LINK request (forward it) | |
1711 | */ | |
1712 | if (!smc_llc_send_message(lnk, &qentry->msg)) { | |
08ae27dd KG |
1713 | struct smc_llc_qentry *qentry2; |
1714 | ||
1715 | qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME, | |
1716 | SMC_LLC_DELETE_LINK); | |
ca7e3edc | 1717 | if (qentry2) |
08ae27dd | 1718 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); |
08ae27dd KG |
1719 | } |
1720 | } | |
0a99be43 | 1721 | smcr_link_clear(lnk_del, true); |
08ae27dd KG |
1722 | |
1723 | active_links = smc_llc_active_link_count(lgr); | |
1724 | if (active_links == 1) { | |
ad6c111b | 1725 | smcr_lgr_set_type(lgr, SMC_LGR_SINGLE); |
08ae27dd | 1726 | } else if (!active_links) { |
ad6c111b | 1727 | smcr_lgr_set_type(lgr, SMC_LGR_NONE); |
08ae27dd KG |
1728 | smc_lgr_terminate_sched(lgr); |
1729 | } | |
1730 | ||
1731 | if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) { | |
1732 | /* trigger setup of asymm alt link */ | |
c48254fa | 1733 | smc_llc_add_link_local(lnk); |
08ae27dd KG |
1734 | } |
1735 | out: | |
b5dd4d69 | 1736 | up_write(&lgr->llc_conf_mutex); |
08ae27dd KG |
1737 | kfree(qentry); |
1738 | } | |
1739 | ||
9ec6bf19 | 1740 | static void smc_llc_delete_link_work(struct work_struct *work) |
52bedf37 | 1741 | { |
9ec6bf19 KG |
1742 | struct smc_link_group *lgr = container_of(work, struct smc_link_group, |
1743 | llc_del_link_work); | |
52bedf37 | 1744 | |
9ec6bf19 KG |
1745 | if (list_empty(&lgr->list)) { |
1746 | /* link group is terminating */ | |
1747 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
1748 | goto out; | |
52bedf37 | 1749 | } |
9c416878 KG |
1750 | |
1751 | if (lgr->role == SMC_CLNT) | |
1752 | smc_llc_process_cli_delete_link(lgr); | |
08ae27dd KG |
1753 | else |
1754 | smc_llc_process_srv_delete_link(lgr); | |
9ec6bf19 KG |
1755 | out: |
1756 | smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); | |
52bedf37 KG |
1757 | } |
1758 | ||
3bc67e09 KG |
1759 | /* process a confirm_rkey request from peer, remote flow */ |
1760 | static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr) | |
4ed75de5 | 1761 | { |
3bc67e09 KG |
1762 | struct smc_llc_msg_confirm_rkey *llc; |
1763 | struct smc_llc_qentry *qentry; | |
1764 | struct smc_link *link; | |
1765 | int num_entries; | |
1766 | int rk_idx; | |
1767 | int i; | |
1768 | ||
1769 | qentry = lgr->llc_flow_rmt.qentry; | |
1770 | llc = &qentry->msg.confirm_rkey; | |
1771 | link = qentry->link; | |
1772 | ||
1773 | num_entries = llc->rtoken[0].num_rkeys; | |
b4ba4652 KG |
1774 | if (num_entries > SMC_LLC_RKEYS_PER_MSG) |
1775 | goto out_err; | |
3bc67e09 KG |
1776 | /* first rkey entry is for receiving link */ |
1777 | rk_idx = smc_rtoken_add(link, | |
1778 | llc->rtoken[0].rmb_vaddr, | |
1779 | llc->rtoken[0].rmb_key); | |
1780 | if (rk_idx < 0) | |
1781 | goto out_err; | |
1782 | ||
1783 | for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++) | |
1784 | smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id, | |
1785 | llc->rtoken[i].rmb_vaddr, | |
1786 | llc->rtoken[i].rmb_key); | |
1787 | /* max links is 3 so there is no need to support conf_rkey_cont msgs */ | |
1788 | goto out; | |
1789 | out_err: | |
1790 | llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; | |
1791 | llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY; | |
1792 | out: | |
ef79d439 | 1793 | llc->hd.flags |= SMC_LLC_FLAG_RESP; |
b4ba4652 | 1794 | smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc)); |
3bc67e09 KG |
1795 | smc_llc_send_message(link, &qentry->msg); |
1796 | smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); | |
4ed75de5 KG |
1797 | } |
1798 | ||
218b24fe KG |
1799 | /* process a delete_rkey request from peer, remote flow */ |
1800 | static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr) | |
4ed75de5 | 1801 | { |
218b24fe KG |
1802 | struct smc_llc_msg_delete_rkey *llc; |
1803 | struct smc_llc_qentry *qentry; | |
1804 | struct smc_link *link; | |
4ed75de5 KG |
1805 | u8 err_mask = 0; |
1806 | int i, max; | |
1807 | ||
218b24fe KG |
1808 | qentry = lgr->llc_flow_rmt.qentry; |
1809 | llc = &qentry->msg.delete_rkey; | |
1810 | link = qentry->link; | |
1811 | ||
b4ba4652 KG |
1812 | if (lgr->smc_version == SMC_V2) { |
1813 | struct smc_llc_msg_delete_rkey_v2 *llcv2; | |
1814 | ||
27ef6a99 GW |
1815 | if (smc_link_shared_v2_rxbuf(link)) { |
1816 | memcpy(lgr->wr_rx_buf_v2, llc, sizeof(*llc)); | |
1817 | llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)lgr->wr_rx_buf_v2; | |
1818 | } else { | |
1819 | llcv2 = (struct smc_llc_msg_delete_rkey_v2 *)llc; | |
1820 | } | |
b4ba4652 KG |
1821 | llcv2->num_inval_rkeys = 0; |
1822 | ||
1823 | max = min_t(u8, llcv2->num_rkeys, SMC_LLC_RKEYS_PER_MSG_V2); | |
1824 | for (i = 0; i < max; i++) { | |
1825 | if (smc_rtoken_delete(link, llcv2->rkey[i])) | |
1826 | llcv2->num_inval_rkeys++; | |
1827 | } | |
1828 | memset(&llc->rkey[0], 0, sizeof(llc->rkey)); | |
1829 | memset(&llc->reserved2, 0, sizeof(llc->reserved2)); | |
1830 | smc_llc_init_msg_hdr(&llc->hd, link->lgr, sizeof(*llc)); | |
1831 | if (llcv2->num_inval_rkeys) { | |
1832 | llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; | |
1833 | llc->err_mask = llcv2->num_inval_rkeys; | |
1834 | } | |
1835 | goto finish; | |
1836 | } | |
1837 | ||
ef79d439 KG |
1838 | max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX); |
1839 | for (i = 0; i < max; i++) { | |
1840 | if (smc_rtoken_delete(link, llc->rkey[i])) | |
1841 | err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i); | |
1842 | } | |
ef79d439 KG |
1843 | if (err_mask) { |
1844 | llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG; | |
1845 | llc->err_mask = err_mask; | |
4ed75de5 | 1846 | } |
b4ba4652 | 1847 | finish: |
218b24fe KG |
1848 | llc->hd.flags |= SMC_LLC_FLAG_RESP; |
1849 | smc_llc_send_message(link, &qentry->msg); | |
1850 | smc_llc_flow_qentry_del(&lgr->llc_flow_rmt); | |
1851 | } | |
ef79d439 | 1852 | |
3e0c40af KG |
1853 | static void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type) |
1854 | { | |
de2fea7b TL |
1855 | pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu LLC protocol violation: " |
1856 | "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id, | |
1857 | lgr->net->net_cookie, type); | |
3e0c40af KG |
1858 | smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL); |
1859 | smc_lgr_terminate_sched(lgr); | |
1860 | } | |
1861 | ||
6c8968c4 | 1862 | /* flush the llc event queue */ |
00a049cf | 1863 | static void smc_llc_event_flush(struct smc_link_group *lgr) |
9bf9abea | 1864 | { |
6c8968c4 KG |
1865 | struct smc_llc_qentry *qentry, *q; |
1866 | ||
1867 | spin_lock_bh(&lgr->llc_event_q_lock); | |
1868 | list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) { | |
1869 | list_del_init(&qentry->list); | |
1870 | kfree(qentry); | |
1871 | } | |
1872 | spin_unlock_bh(&lgr->llc_event_q_lock); | |
1873 | } | |
1874 | ||
1875 | static void smc_llc_event_handler(struct smc_llc_qentry *qentry) | |
1876 | { | |
1877 | union smc_llc_msg *llc = &qentry->msg; | |
1878 | struct smc_link *link = qentry->link; | |
0fb0b02b | 1879 | struct smc_link_group *lgr = link->lgr; |
9bf9abea | 1880 | |
d854fcbf | 1881 | if (!smc_link_usable(link)) |
6c8968c4 | 1882 | goto out; |
313164da | 1883 | |
b4ba4652 | 1884 | switch (llc->raw.hdr.common.llc_type) { |
313164da | 1885 | case SMC_LLC_TEST_LINK: |
56e8091c KG |
1886 | llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP; |
1887 | smc_llc_send_message(link, llc); | |
313164da | 1888 | break; |
52bedf37 | 1889 | case SMC_LLC_ADD_LINK: |
0fb0b02b KG |
1890 | if (list_empty(&lgr->list)) |
1891 | goto out; /* lgr is terminating */ | |
1892 | if (lgr->role == SMC_CLNT) { | |
c48254fa KG |
1893 | if (smc_llc_is_local_add_link(llc)) { |
1894 | if (lgr->llc_flow_lcl.type == | |
1895 | SMC_LLC_FLOW_ADD_LINK) | |
1896 | break; /* add_link in progress */ | |
1897 | if (smc_llc_flow_start(&lgr->llc_flow_lcl, | |
1898 | qentry)) { | |
1899 | schedule_work(&lgr->llc_add_link_work); | |
1900 | } | |
1901 | return; | |
1902 | } | |
1903 | if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && | |
1904 | !lgr->llc_flow_lcl.qentry) { | |
0fb0b02b KG |
1905 | /* a flow is waiting for this message */ |
1906 | smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, | |
1907 | qentry); | |
6778a6be | 1908 | wake_up(&lgr->llc_msg_waiter); |
b4ba4652 KG |
1909 | return; |
1910 | } | |
1911 | if (lgr->llc_flow_lcl.type == | |
1912 | SMC_LLC_FLOW_REQ_ADD_LINK) { | |
1913 | /* server started add_link processing */ | |
1914 | lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK; | |
1915 | smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, | |
1916 | qentry); | |
1917 | schedule_work(&lgr->llc_add_link_work); | |
1918 | return; | |
1919 | } | |
1920 | if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { | |
b45e7f98 | 1921 | schedule_work(&lgr->llc_add_link_work); |
0fb0b02b KG |
1922 | } |
1923 | } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { | |
1924 | /* as smc server, handle client suggestion */ | |
b45e7f98 | 1925 | schedule_work(&lgr->llc_add_link_work); |
0fb0b02b KG |
1926 | } |
1927 | return; | |
1928 | case SMC_LLC_CONFIRM_LINK: | |
87f88cda | 1929 | case SMC_LLC_ADD_LINK_CONT: |
0fb0b02b KG |
1930 | if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { |
1931 | /* a flow is waiting for this message */ | |
1932 | smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); | |
6778a6be | 1933 | wake_up(&lgr->llc_msg_waiter); |
0fb0b02b KG |
1934 | return; |
1935 | } | |
52bedf37 KG |
1936 | break; |
1937 | case SMC_LLC_DELETE_LINK: | |
b9979c2e KG |
1938 | if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK && |
1939 | !lgr->llc_flow_lcl.qentry) { | |
1940 | /* DEL LINK REQ during ADD LINK SEQ */ | |
1941 | smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); | |
1942 | wake_up(&lgr->llc_msg_waiter); | |
1943 | } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { | |
1944 | schedule_work(&lgr->llc_del_link_work); | |
9ec6bf19 KG |
1945 | } |
1946 | return; | |
4ed75de5 | 1947 | case SMC_LLC_CONFIRM_RKEY: |
3bc67e09 KG |
1948 | /* new request from remote, assign to remote flow */ |
1949 | if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { | |
1950 | /* process here, does not wait for more llc msgs */ | |
1951 | smc_llc_rmt_conf_rkey(lgr); | |
1952 | smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); | |
1953 | } | |
1954 | return; | |
4ed75de5 | 1955 | case SMC_LLC_CONFIRM_RKEY_CONT: |
42d18acc KG |
1956 | /* not used because max links is 3, and 3 rkeys fit into |
1957 | * one CONFIRM_RKEY message | |
1958 | */ | |
4ed75de5 KG |
1959 | break; |
1960 | case SMC_LLC_DELETE_RKEY: | |
218b24fe KG |
1961 | /* new request from remote, assign to remote flow */ |
1962 | if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) { | |
1963 | /* process here, does not wait for more llc msgs */ | |
1964 | smc_llc_rmt_delete_rkey(lgr); | |
1965 | smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt); | |
1966 | } | |
1967 | return; | |
b4ba4652 KG |
1968 | case SMC_LLC_REQ_ADD_LINK: |
1969 | /* handle response here, smc_llc_flow_stop() cannot be called | |
1970 | * in tasklet context | |
1971 | */ | |
1972 | if (lgr->role == SMC_CLNT && | |
1973 | lgr->llc_flow_lcl.type == SMC_LLC_FLOW_REQ_ADD_LINK && | |
1974 | (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP)) { | |
1975 | smc_llc_flow_stop(link->lgr, &lgr->llc_flow_lcl); | |
1976 | } else if (lgr->role == SMC_SERV) { | |
1977 | if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) { | |
1978 | /* as smc server, handle client suggestion */ | |
1979 | lgr->llc_flow_lcl.type = SMC_LLC_FLOW_ADD_LINK; | |
1980 | schedule_work(&lgr->llc_add_link_work); | |
1981 | } | |
1982 | return; | |
1983 | } | |
1984 | break; | |
3e0c40af KG |
1985 | default: |
1986 | smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type); | |
1987 | break; | |
313164da | 1988 | } |
6c8968c4 KG |
1989 | out: |
1990 | kfree(qentry); | |
1991 | } | |
1992 | ||
1993 | /* worker to process llc messages on the event queue */ | |
1994 | static void smc_llc_event_work(struct work_struct *work) | |
1995 | { | |
1996 | struct smc_link_group *lgr = container_of(work, struct smc_link_group, | |
1997 | llc_event_work); | |
1998 | struct smc_llc_qentry *qentry; | |
1999 | ||
555da9af | 2000 | if (!lgr->llc_flow_lcl.type && lgr->delayed_event) { |
d535ca13 KG |
2001 | qentry = lgr->delayed_event; |
2002 | lgr->delayed_event = NULL; | |
2003 | if (smc_link_usable(qentry->link)) | |
2004 | smc_llc_event_handler(qentry); | |
2005 | else | |
555da9af | 2006 | kfree(qentry); |
555da9af KG |
2007 | } |
2008 | ||
6c8968c4 KG |
2009 | again: |
2010 | spin_lock_bh(&lgr->llc_event_q_lock); | |
2011 | if (!list_empty(&lgr->llc_event_q)) { | |
2012 | qentry = list_first_entry(&lgr->llc_event_q, | |
2013 | struct smc_llc_qentry, list); | |
2014 | list_del_init(&qentry->list); | |
2015 | spin_unlock_bh(&lgr->llc_event_q_lock); | |
2016 | smc_llc_event_handler(qentry); | |
2017 | goto again; | |
2018 | } | |
2019 | spin_unlock_bh(&lgr->llc_event_q_lock); | |
2020 | } | |
2021 | ||
ef79d439 | 2022 | /* process llc responses in tasklet context */ |
a6688d91 KG |
2023 | static void smc_llc_rx_response(struct smc_link *link, |
2024 | struct smc_llc_qentry *qentry) | |
ef79d439 | 2025 | { |
2ff08678 KG |
2026 | enum smc_llc_flowtype flowtype = link->lgr->llc_flow_lcl.type; |
2027 | struct smc_llc_flow *flow = &link->lgr->llc_flow_lcl; | |
b4ba4652 | 2028 | u8 llc_type = qentry->msg.raw.hdr.common.llc_type; |
ef79d439 | 2029 | |
a6688d91 | 2030 | switch (llc_type) { |
ef79d439 | 2031 | case SMC_LLC_TEST_LINK: |
741a49a4 | 2032 | if (smc_link_active(link)) |
ef79d439 KG |
2033 | complete(&link->llc_testlink_resp); |
2034 | break; | |
ef79d439 | 2035 | case SMC_LLC_ADD_LINK: |
87f88cda | 2036 | case SMC_LLC_ADD_LINK_CONT: |
2ff08678 KG |
2037 | case SMC_LLC_CONFIRM_LINK: |
2038 | if (flowtype != SMC_LLC_FLOW_ADD_LINK || flow->qentry) | |
2039 | break; /* drop out-of-flow response */ | |
2040 | goto assign; | |
2041 | case SMC_LLC_DELETE_LINK: | |
2042 | if (flowtype != SMC_LLC_FLOW_DEL_LINK || flow->qentry) | |
2043 | break; /* drop out-of-flow response */ | |
2044 | goto assign; | |
3d88a21b | 2045 | case SMC_LLC_CONFIRM_RKEY: |
6d74c3a8 | 2046 | case SMC_LLC_DELETE_RKEY: |
2ff08678 KG |
2047 | if (flowtype != SMC_LLC_FLOW_RKEY || flow->qentry) |
2048 | break; /* drop out-of-flow response */ | |
2049 | goto assign; | |
ef79d439 | 2050 | case SMC_LLC_CONFIRM_RKEY_CONT: |
42d18acc | 2051 | /* not used because max links is 3 */ |
ef79d439 | 2052 | break; |
3e0c40af | 2053 | default: |
b4ba4652 KG |
2054 | smc_llc_protocol_violation(link->lgr, |
2055 | qentry->msg.raw.hdr.common.type); | |
3e0c40af | 2056 | break; |
ef79d439 | 2057 | } |
a6688d91 | 2058 | kfree(qentry); |
2ff08678 KG |
2059 | return; |
2060 | assign: | |
2061 | /* assign responses to the local flow, we requested them */ | |
2062 | smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry); | |
2063 | wake_up(&link->lgr->llc_msg_waiter); | |
ef79d439 KG |
2064 | } |
2065 | ||
a6688d91 | 2066 | static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc) |
6c8968c4 | 2067 | { |
6c8968c4 KG |
2068 | struct smc_link_group *lgr = link->lgr; |
2069 | struct smc_llc_qentry *qentry; | |
6c8968c4 KG |
2070 | unsigned long flags; |
2071 | ||
6c8968c4 KG |
2072 | qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC); |
2073 | if (!qentry) | |
2074 | return; | |
2075 | qentry->link = link; | |
2076 | INIT_LIST_HEAD(&qentry->list); | |
2077 | memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg)); | |
a6688d91 KG |
2078 | |
2079 | /* process responses immediately */ | |
b4ba4652 KG |
2080 | if ((llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) && |
2081 | llc->raw.hdr.common.llc_type != SMC_LLC_REQ_ADD_LINK) { | |
a6688d91 KG |
2082 | smc_llc_rx_response(link, qentry); |
2083 | return; | |
2084 | } | |
2085 | ||
2086 | /* add requests to event queue */ | |
6c8968c4 KG |
2087 | spin_lock_irqsave(&lgr->llc_event_q_lock, flags); |
2088 | list_add_tail(&qentry->list, &lgr->llc_event_q); | |
2089 | spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags); | |
22ef473d | 2090 | queue_work(system_highpri_wq, &lgr->llc_event_work); |
9bf9abea UB |
2091 | } |
2092 | ||
a6688d91 KG |
2093 | /* copy received msg and add it to the event queue */ |
2094 | static void smc_llc_rx_handler(struct ib_wc *wc, void *buf) | |
2095 | { | |
2096 | struct smc_link *link = (struct smc_link *)wc->qp->qp_context; | |
2097 | union smc_llc_msg *llc = buf; | |
2098 | ||
2099 | if (wc->byte_len < sizeof(*llc)) | |
2100 | return; /* short message */ | |
b4ba4652 KG |
2101 | if (!llc->raw.hdr.common.llc_version) { |
2102 | if (llc->raw.hdr.length != sizeof(*llc)) | |
2103 | return; /* invalid message */ | |
2104 | } else { | |
2105 | if (llc->raw.hdr.length_v2 < sizeof(*llc)) | |
2106 | return; /* invalid message */ | |
2107 | } | |
a6688d91 KG |
2108 | |
2109 | smc_llc_enqueue(link, llc); | |
2110 | } | |
2111 | ||
44aa81ce | 2112 | /***************************** worker, utils *********************************/ |
877ae5be KG |
2113 | |
2114 | static void smc_llc_testlink_work(struct work_struct *work) | |
2115 | { | |
2116 | struct smc_link *link = container_of(to_delayed_work(work), | |
2117 | struct smc_link, llc_testlink_wrk); | |
2118 | unsigned long next_interval; | |
877ae5be KG |
2119 | unsigned long expire_time; |
2120 | u8 user_data[16] = { 0 }; | |
2121 | int rc; | |
2122 | ||
741a49a4 | 2123 | if (!smc_link_active(link)) |
877ae5be KG |
2124 | return; /* don't reschedule worker */ |
2125 | expire_time = link->wr_rx_tstamp + link->llc_testlink_time; | |
2126 | if (time_is_after_jiffies(expire_time)) { | |
2127 | next_interval = expire_time - jiffies; | |
2128 | goto out; | |
2129 | } | |
2130 | reinit_completion(&link->llc_testlink_resp); | |
d97935fa | 2131 | smc_llc_send_test_link(link, user_data); |
877ae5be KG |
2132 | /* receive TEST LINK response over RoCE fabric */ |
2133 | rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp, | |
2134 | SMC_LLC_WAIT_TIME); | |
741a49a4 | 2135 | if (!smc_link_active(link)) |
1020e1ef | 2136 | return; /* link state changed */ |
877ae5be | 2137 | if (rc <= 0) { |
87523930 | 2138 | smcr_link_down_cond_sched(link); |
877ae5be KG |
2139 | return; |
2140 | } | |
2141 | next_interval = link->llc_testlink_time; | |
2142 | out: | |
1020e1ef | 2143 | schedule_delayed_work(&link->llc_testlink_wrk, next_interval); |
877ae5be KG |
2144 | } |
2145 | ||
00a049cf KG |
2146 | void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc) |
2147 | { | |
2148 | struct net *net = sock_net(smc->clcsock->sk); | |
2149 | ||
2150 | INIT_WORK(&lgr->llc_event_work, smc_llc_event_work); | |
b45e7f98 | 2151 | INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work); |
9ec6bf19 | 2152 | INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work); |
00a049cf KG |
2153 | INIT_LIST_HEAD(&lgr->llc_event_q); |
2154 | spin_lock_init(&lgr->llc_event_q_lock); | |
555da9af | 2155 | spin_lock_init(&lgr->llc_flow_lock); |
6778a6be KG |
2156 | init_waitqueue_head(&lgr->llc_flow_waiter); |
2157 | init_waitqueue_head(&lgr->llc_msg_waiter); | |
b5dd4d69 | 2158 | init_rwsem(&lgr->llc_conf_mutex); |
77eee325 | 2159 | lgr->llc_testlink_time = READ_ONCE(net->smc.sysctl_smcr_testlink_time); |
00a049cf KG |
2160 | } |
2161 | ||
2162 | /* called after lgr was removed from lgr_list */ | |
2163 | void smc_llc_lgr_clear(struct smc_link_group *lgr) | |
2164 | { | |
2165 | smc_llc_event_flush(lgr); | |
6778a6be KG |
2166 | wake_up_all(&lgr->llc_flow_waiter); |
2167 | wake_up_all(&lgr->llc_msg_waiter); | |
00a049cf | 2168 | cancel_work_sync(&lgr->llc_event_work); |
b45e7f98 | 2169 | cancel_work_sync(&lgr->llc_add_link_work); |
9ec6bf19 | 2170 | cancel_work_sync(&lgr->llc_del_link_work); |
555da9af KG |
2171 | if (lgr->delayed_event) { |
2172 | kfree(lgr->delayed_event); | |
2173 | lgr->delayed_event = NULL; | |
2174 | } | |
00a049cf KG |
2175 | } |
2176 | ||
2a4c57a9 | 2177 | int smc_llc_link_init(struct smc_link *link) |
877ae5be KG |
2178 | { |
2179 | init_completion(&link->llc_testlink_resp); | |
2180 | INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work); | |
2a4c57a9 | 2181 | return 0; |
b32cf4ab KG |
2182 | } |
2183 | ||
00a049cf | 2184 | void smc_llc_link_active(struct smc_link *link) |
b32cf4ab | 2185 | { |
de2fea7b | 2186 | pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link added: id %*phN, " |
0a99be43 KG |
2187 | "peerid %*phN, ibdev %s, ibport %d\n", |
2188 | SMC_LGR_ID_SIZE, &link->lgr->id, | |
de2fea7b | 2189 | link->lgr->net->net_cookie, |
0a99be43 KG |
2190 | SMC_LGR_ID_SIZE, &link->link_uid, |
2191 | SMC_LGR_ID_SIZE, &link->peer_link_uid, | |
2192 | link->smcibdev->ibdev->name, link->ibport); | |
877ae5be | 2193 | link->state = SMC_LNK_ACTIVE; |
00a049cf | 2194 | if (link->lgr->llc_testlink_time) { |
c4a146c7 | 2195 | link->llc_testlink_time = link->lgr->llc_testlink_time; |
1020e1ef KG |
2196 | schedule_delayed_work(&link->llc_testlink_wrk, |
2197 | link->llc_testlink_time); | |
877ae5be KG |
2198 | } |
2199 | } | |
2200 | ||
877ae5be | 2201 | /* called in worker context */ |
0a99be43 | 2202 | void smc_llc_link_clear(struct smc_link *link, bool log) |
877ae5be | 2203 | { |
0a99be43 | 2204 | if (log) |
de2fea7b | 2205 | pr_warn_ratelimited("smc: SMC-R lg %*phN net %llu link removed: id %*phN" |
0a99be43 KG |
2206 | ", peerid %*phN, ibdev %s, ibport %d\n", |
2207 | SMC_LGR_ID_SIZE, &link->lgr->id, | |
de2fea7b | 2208 | link->lgr->net->net_cookie, |
0a99be43 KG |
2209 | SMC_LGR_ID_SIZE, &link->link_uid, |
2210 | SMC_LGR_ID_SIZE, &link->peer_link_uid, | |
2211 | link->smcibdev->ibdev->name, link->ibport); | |
2140ac26 KG |
2212 | complete(&link->llc_testlink_resp); |
2213 | cancel_delayed_work_sync(&link->llc_testlink_wrk); | |
877ae5be KG |
2214 | } |
2215 | ||
3d88a21b KG |
2216 | /* register a new rtoken at the remote peer (for all links) */ |
2217 | int smc_llc_do_confirm_rkey(struct smc_link *send_link, | |
44aa81ce KG |
2218 | struct smc_buf_desc *rmb_desc) |
2219 | { | |
3d88a21b KG |
2220 | struct smc_link_group *lgr = send_link->lgr; |
2221 | struct smc_llc_qentry *qentry = NULL; | |
2222 | int rc = 0; | |
44aa81ce | 2223 | |
3d88a21b KG |
2224 | rc = smc_llc_send_confirm_rkey(send_link, rmb_desc); |
2225 | if (rc) | |
2226 | goto out; | |
44aa81ce | 2227 | /* receive CONFIRM RKEY response from server over RoCE fabric */ |
3d88a21b KG |
2228 | qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, |
2229 | SMC_LLC_CONFIRM_RKEY); | |
2230 | if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) | |
2231 | rc = -EFAULT; | |
2232 | out: | |
2233 | if (qentry) | |
2234 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
3d88a21b | 2235 | return rc; |
44aa81ce KG |
2236 | } |
2237 | ||
60e03c62 | 2238 | /* unregister an rtoken at the remote peer */ |
6d74c3a8 | 2239 | int smc_llc_do_delete_rkey(struct smc_link_group *lgr, |
60e03c62 KG |
2240 | struct smc_buf_desc *rmb_desc) |
2241 | { | |
6d74c3a8 KG |
2242 | struct smc_llc_qentry *qentry = NULL; |
2243 | struct smc_link *send_link; | |
0b29ec64 | 2244 | int rc = 0; |
60e03c62 | 2245 | |
6d74c3a8 KG |
2246 | send_link = smc_llc_usable_link(lgr); |
2247 | if (!send_link) | |
2248 | return -ENOLINK; | |
2249 | ||
6d74c3a8 KG |
2250 | /* protected by llc_flow control */ |
2251 | rc = smc_llc_send_delete_rkey(send_link, rmb_desc); | |
60e03c62 KG |
2252 | if (rc) |
2253 | goto out; | |
2254 | /* receive DELETE RKEY response from server over RoCE fabric */ | |
6d74c3a8 KG |
2255 | qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME, |
2256 | SMC_LLC_DELETE_RKEY); | |
2257 | if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG)) | |
60e03c62 | 2258 | rc = -EFAULT; |
60e03c62 | 2259 | out: |
6d74c3a8 KG |
2260 | if (qentry) |
2261 | smc_llc_flow_qentry_del(&lgr->llc_flow_lcl); | |
60e03c62 KG |
2262 | return rc; |
2263 | } | |
2264 | ||
45fa8da0 KG |
2265 | void smc_llc_link_set_uid(struct smc_link *link) |
2266 | { | |
2267 | __be32 link_uid; | |
2268 | ||
2269 | link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id); | |
2270 | memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE); | |
2271 | } | |
2272 | ||
649758ff KG |
2273 | /* save peers link user id, used for debug purposes */ |
2274 | void smc_llc_save_peer_uid(struct smc_llc_qentry *qentry) | |
2275 | { | |
2276 | memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid, | |
2277 | SMC_LGR_ID_SIZE); | |
2278 | } | |
2279 | ||
92334cfc KG |
2280 | /* evaluate confirm link request or response */ |
2281 | int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry, | |
2282 | enum smc_llc_reqresp type) | |
2283 | { | |
45fa8da0 | 2284 | if (type == SMC_LLC_REQ) { /* SMC server assigns link_id */ |
92334cfc | 2285 | qentry->link->link_id = qentry->msg.confirm_link.link_num; |
45fa8da0 KG |
2286 | smc_llc_link_set_uid(qentry->link); |
2287 | } | |
92334cfc KG |
2288 | if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)) |
2289 | return -ENOTSUPP; | |
2290 | return 0; | |
2291 | } | |
2292 | ||
9bf9abea UB |
2293 | /***************************** init, exit, misc ******************************/ |
2294 | ||
2295 | static struct smc_wr_rx_handler smc_llc_rx_handlers[] = { | |
2296 | { | |
2297 | .handler = smc_llc_rx_handler, | |
2298 | .type = SMC_LLC_CONFIRM_LINK | |
2299 | }, | |
313164da KG |
2300 | { |
2301 | .handler = smc_llc_rx_handler, | |
2302 | .type = SMC_LLC_TEST_LINK | |
2303 | }, | |
52bedf37 KG |
2304 | { |
2305 | .handler = smc_llc_rx_handler, | |
2306 | .type = SMC_LLC_ADD_LINK | |
2307 | }, | |
87f88cda KG |
2308 | { |
2309 | .handler = smc_llc_rx_handler, | |
2310 | .type = SMC_LLC_ADD_LINK_CONT | |
2311 | }, | |
52bedf37 KG |
2312 | { |
2313 | .handler = smc_llc_rx_handler, | |
2314 | .type = SMC_LLC_DELETE_LINK | |
2315 | }, | |
4ed75de5 KG |
2316 | { |
2317 | .handler = smc_llc_rx_handler, | |
2318 | .type = SMC_LLC_CONFIRM_RKEY | |
2319 | }, | |
2320 | { | |
2321 | .handler = smc_llc_rx_handler, | |
2322 | .type = SMC_LLC_CONFIRM_RKEY_CONT | |
2323 | }, | |
2324 | { | |
2325 | .handler = smc_llc_rx_handler, | |
2326 | .type = SMC_LLC_DELETE_RKEY | |
2327 | }, | |
b4ba4652 KG |
2328 | /* V2 types */ |
2329 | { | |
2330 | .handler = smc_llc_rx_handler, | |
2331 | .type = SMC_LLC_CONFIRM_LINK_V2 | |
2332 | }, | |
2333 | { | |
2334 | .handler = smc_llc_rx_handler, | |
2335 | .type = SMC_LLC_TEST_LINK_V2 | |
2336 | }, | |
2337 | { | |
2338 | .handler = smc_llc_rx_handler, | |
2339 | .type = SMC_LLC_ADD_LINK_V2 | |
2340 | }, | |
2341 | { | |
2342 | .handler = smc_llc_rx_handler, | |
2343 | .type = SMC_LLC_DELETE_LINK_V2 | |
2344 | }, | |
2345 | { | |
2346 | .handler = smc_llc_rx_handler, | |
2347 | .type = SMC_LLC_REQ_ADD_LINK_V2 | |
2348 | }, | |
2349 | { | |
2350 | .handler = smc_llc_rx_handler, | |
2351 | .type = SMC_LLC_CONFIRM_RKEY_V2 | |
2352 | }, | |
2353 | { | |
2354 | .handler = smc_llc_rx_handler, | |
2355 | .type = SMC_LLC_DELETE_RKEY_V2 | |
2356 | }, | |
9bf9abea UB |
2357 | { |
2358 | .handler = NULL, | |
2359 | } | |
2360 | }; | |
2361 | ||
2362 | int __init smc_llc_init(void) | |
2363 | { | |
2364 | struct smc_wr_rx_handler *handler; | |
2365 | int rc = 0; | |
2366 | ||
2367 | for (handler = smc_llc_rx_handlers; handler->handler; handler++) { | |
2368 | INIT_HLIST_NODE(&handler->list); | |
2369 | rc = smc_wr_rx_register_handler(handler); | |
2370 | if (rc) | |
2371 | break; | |
2372 | } | |
2373 | return rc; | |
2374 | } |