Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
a046d57d UB |
2 | /* |
3 | * Shared Memory Communications over RDMA (SMC-R) and RoCE | |
4 | * | |
5 | * CLC (connection layer control) handshake over initial TCP socket to | |
6 | * prepare for RDMA traffic | |
7 | * | |
1a26d020 | 8 | * Copyright IBM Corp. 2016, 2018 |
a046d57d UB |
9 | * |
10 | * Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com> | |
11 | */ | |
12 | ||
13 | #include <linux/in.h> | |
696cd301 | 14 | #include <linux/inetdevice.h> |
143c0171 | 15 | #include <linux/if_ether.h> |
c3edc401 IM |
16 | #include <linux/sched/signal.h> |
17 | ||
1a26d020 | 18 | #include <net/addrconf.h> |
a046d57d UB |
19 | #include <net/sock.h> |
20 | #include <net/tcp.h> | |
21 | ||
22 | #include "smc.h" | |
0cfdd8f9 | 23 | #include "smc_core.h" |
a046d57d UB |
24 | #include "smc_clc.h" |
25 | #include "smc_ib.h" | |
c758dfdd HW |
26 | #include "smc_ism.h" |
27 | ||
28 | #define SMCR_CLC_ACCEPT_CONFIRM_LEN 68 | |
29 | #define SMCD_CLC_ACCEPT_CONFIRM_LEN 48 | |
a046d57d | 30 | |
0f627126 SR |
31 | /* eye catcher "SMCR" EBCDIC for CLC messages */ |
32 | static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'}; | |
c758dfdd HW |
33 | /* eye catcher "SMCD" EBCDIC for CLC messages */ |
34 | static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'}; | |
0f627126 | 35 | |
e7b7a64a UB |
36 | /* check if received message has a correct header length and contains valid |
37 | * heading and trailing eyecatchers | |
38 | */ | |
39 | static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm) | |
40 | { | |
41 | struct smc_clc_msg_proposal_prefix *pclc_prfx; | |
42 | struct smc_clc_msg_accept_confirm *clc; | |
43 | struct smc_clc_msg_proposal *pclc; | |
44 | struct smc_clc_msg_decline *dclc; | |
45 | struct smc_clc_msg_trail *trl; | |
46 | ||
c758dfdd HW |
47 | if (memcmp(clcm->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) && |
48 | memcmp(clcm->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER))) | |
e7b7a64a UB |
49 | return false; |
50 | switch (clcm->type) { | |
51 | case SMC_CLC_PROPOSAL: | |
c758dfdd HW |
52 | if (clcm->path != SMC_TYPE_R && clcm->path != SMC_TYPE_D && |
53 | clcm->path != SMC_TYPE_B) | |
54 | return false; | |
e7b7a64a UB |
55 | pclc = (struct smc_clc_msg_proposal *)clcm; |
56 | pclc_prfx = smc_clc_proposal_get_prefix(pclc); | |
57 | if (ntohs(pclc->hdr.length) != | |
58 | sizeof(*pclc) + ntohs(pclc->iparea_offset) + | |
59 | sizeof(*pclc_prfx) + | |
60 | pclc_prfx->ipv6_prefixes_cnt * | |
61 | sizeof(struct smc_clc_ipv6_prefix) + | |
62 | sizeof(*trl)) | |
63 | return false; | |
64 | trl = (struct smc_clc_msg_trail *) | |
65 | ((u8 *)pclc + ntohs(pclc->hdr.length) - sizeof(*trl)); | |
66 | break; | |
67 | case SMC_CLC_ACCEPT: | |
68 | case SMC_CLC_CONFIRM: | |
c758dfdd HW |
69 | if (clcm->path != SMC_TYPE_R && clcm->path != SMC_TYPE_D) |
70 | return false; | |
e7b7a64a | 71 | clc = (struct smc_clc_msg_accept_confirm *)clcm; |
c758dfdd HW |
72 | if ((clcm->path == SMC_TYPE_R && |
73 | ntohs(clc->hdr.length) != SMCR_CLC_ACCEPT_CONFIRM_LEN) || | |
74 | (clcm->path == SMC_TYPE_D && | |
75 | ntohs(clc->hdr.length) != SMCD_CLC_ACCEPT_CONFIRM_LEN)) | |
e7b7a64a | 76 | return false; |
c758dfdd HW |
77 | trl = (struct smc_clc_msg_trail *) |
78 | ((u8 *)clc + ntohs(clc->hdr.length) - sizeof(*trl)); | |
e7b7a64a UB |
79 | break; |
80 | case SMC_CLC_DECLINE: | |
81 | dclc = (struct smc_clc_msg_decline *)clcm; | |
82 | if (ntohs(dclc->hdr.length) != sizeof(*dclc)) | |
83 | return false; | |
84 | trl = &dclc->trl; | |
85 | break; | |
86 | default: | |
87 | return false; | |
88 | } | |
c758dfdd HW |
89 | if (memcmp(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) && |
90 | memcmp(trl->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER))) | |
e7b7a64a UB |
91 | return false; |
92 | return true; | |
93 | } | |
94 | ||
c246d942 KG |
95 | /* find ipv4 addr on device and get the prefix len, fill CLC proposal msg */ |
96 | static int smc_clc_prfx_set4_rcu(struct dst_entry *dst, __be32 ipv4, | |
97 | struct smc_clc_msg_proposal_prefix *prop) | |
98 | { | |
99 | struct in_device *in_dev = __in_dev_get_rcu(dst->dev); | |
100 | ||
101 | if (!in_dev) | |
102 | return -ENODEV; | |
103 | for_ifa(in_dev) { | |
104 | if (!inet_ifa_match(ipv4, ifa)) | |
105 | continue; | |
106 | prop->prefix_len = inet_mask_len(ifa->ifa_mask); | |
107 | prop->outgoing_subnet = ifa->ifa_address & ifa->ifa_mask; | |
108 | /* prop->ipv6_prefixes_cnt = 0; already done by memset before */ | |
109 | return 0; | |
110 | } endfor_ifa(in_dev); | |
111 | return -ENOENT; | |
112 | } | |
113 | ||
1a26d020 KG |
114 | /* fill CLC proposal msg with ipv6 prefixes from device */ |
115 | static int smc_clc_prfx_set6_rcu(struct dst_entry *dst, | |
116 | struct smc_clc_msg_proposal_prefix *prop, | |
117 | struct smc_clc_ipv6_prefix *ipv6_prfx) | |
118 | { | |
119 | #if IS_ENABLED(CONFIG_IPV6) | |
120 | struct inet6_dev *in6_dev = __in6_dev_get(dst->dev); | |
121 | struct inet6_ifaddr *ifa; | |
122 | int cnt = 0; | |
123 | ||
124 | if (!in6_dev) | |
125 | return -ENODEV; | |
126 | /* use a maximum of 8 IPv6 prefixes from device */ | |
127 | list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { | |
128 | if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL) | |
129 | continue; | |
130 | ipv6_addr_prefix(&ipv6_prfx[cnt].prefix, | |
131 | &ifa->addr, ifa->prefix_len); | |
132 | ipv6_prfx[cnt].prefix_len = ifa->prefix_len; | |
133 | cnt++; | |
134 | if (cnt == SMC_CLC_MAX_V6_PREFIX) | |
135 | break; | |
136 | } | |
137 | prop->ipv6_prefixes_cnt = cnt; | |
138 | if (cnt) | |
139 | return 0; | |
140 | #endif | |
141 | return -ENOENT; | |
142 | } | |
143 | ||
c246d942 KG |
144 | /* retrieve and set prefixes in CLC proposal msg */ |
145 | static int smc_clc_prfx_set(struct socket *clcsock, | |
1a26d020 KG |
146 | struct smc_clc_msg_proposal_prefix *prop, |
147 | struct smc_clc_ipv6_prefix *ipv6_prfx) | |
696cd301 KG |
148 | { |
149 | struct dst_entry *dst = sk_dst_get(clcsock->sk); | |
c246d942 | 150 | struct sockaddr_storage addrs; |
1a26d020 | 151 | struct sockaddr_in6 *addr6; |
c246d942 | 152 | struct sockaddr_in *addr; |
696cd301 KG |
153 | int rc = -ENOENT; |
154 | ||
c246d942 | 155 | memset(prop, 0, sizeof(*prop)); |
696cd301 KG |
156 | if (!dst) { |
157 | rc = -ENOTCONN; | |
158 | goto out; | |
159 | } | |
160 | if (!dst->dev) { | |
161 | rc = -ENODEV; | |
162 | goto out_rel; | |
163 | } | |
696cd301 | 164 | /* get address to which the internal TCP socket is bound */ |
c246d942 KG |
165 | kernel_getsockname(clcsock, (struct sockaddr *)&addrs); |
166 | /* analyze IP specific data of net_device belonging to TCP socket */ | |
1a26d020 | 167 | addr6 = (struct sockaddr_in6 *)&addrs; |
696cd301 | 168 | rcu_read_lock(); |
c246d942 KG |
169 | if (addrs.ss_family == PF_INET) { |
170 | /* IPv4 */ | |
171 | addr = (struct sockaddr_in *)&addrs; | |
172 | rc = smc_clc_prfx_set4_rcu(dst, addr->sin_addr.s_addr, prop); | |
1a26d020 KG |
173 | } else if (ipv6_addr_v4mapped(&addr6->sin6_addr)) { |
174 | /* mapped IPv4 address - peer is IPv4 only */ | |
175 | rc = smc_clc_prfx_set4_rcu(dst, addr6->sin6_addr.s6_addr32[3], | |
176 | prop); | |
177 | } else { | |
178 | /* IPv6 */ | |
179 | rc = smc_clc_prfx_set6_rcu(dst, prop, ipv6_prfx); | |
c246d942 KG |
180 | } |
181 | rcu_read_unlock(); | |
182 | out_rel: | |
183 | dst_release(dst); | |
184 | out: | |
185 | return rc; | |
186 | } | |
187 | ||
188 | /* match ipv4 addrs of dev against addr in CLC proposal */ | |
189 | static int smc_clc_prfx_match4_rcu(struct net_device *dev, | |
190 | struct smc_clc_msg_proposal_prefix *prop) | |
191 | { | |
192 | struct in_device *in_dev = __in_dev_get_rcu(dev); | |
193 | ||
194 | if (!in_dev) | |
195 | return -ENODEV; | |
696cd301 | 196 | for_ifa(in_dev) { |
c246d942 KG |
197 | if (prop->prefix_len == inet_mask_len(ifa->ifa_mask) && |
198 | inet_ifa_match(prop->outgoing_subnet, ifa)) | |
199 | return 0; | |
696cd301 | 200 | } endfor_ifa(in_dev); |
696cd301 | 201 | |
c246d942 KG |
202 | return -ENOENT; |
203 | } | |
204 | ||
1a26d020 KG |
205 | /* match ipv6 addrs of dev against addrs in CLC proposal */ |
206 | static int smc_clc_prfx_match6_rcu(struct net_device *dev, | |
207 | struct smc_clc_msg_proposal_prefix *prop) | |
208 | { | |
209 | #if IS_ENABLED(CONFIG_IPV6) | |
210 | struct inet6_dev *in6_dev = __in6_dev_get(dev); | |
211 | struct smc_clc_ipv6_prefix *ipv6_prfx; | |
212 | struct inet6_ifaddr *ifa; | |
213 | int i, max; | |
214 | ||
215 | if (!in6_dev) | |
216 | return -ENODEV; | |
217 | /* ipv6 prefix list starts behind smc_clc_msg_proposal_prefix */ | |
218 | ipv6_prfx = (struct smc_clc_ipv6_prefix *)((u8 *)prop + sizeof(*prop)); | |
219 | max = min_t(u8, prop->ipv6_prefixes_cnt, SMC_CLC_MAX_V6_PREFIX); | |
220 | list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { | |
221 | if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL) | |
222 | continue; | |
223 | for (i = 0; i < max; i++) { | |
224 | if (ifa->prefix_len == ipv6_prfx[i].prefix_len && | |
225 | ipv6_prefix_equal(&ifa->addr, &ipv6_prfx[i].prefix, | |
226 | ifa->prefix_len)) | |
227 | return 0; | |
228 | } | |
229 | } | |
230 | #endif | |
231 | return -ENOENT; | |
232 | } | |
233 | ||
c246d942 KG |
234 | /* check if proposed prefixes match one of our device prefixes */ |
235 | int smc_clc_prfx_match(struct socket *clcsock, | |
236 | struct smc_clc_msg_proposal_prefix *prop) | |
237 | { | |
238 | struct dst_entry *dst = sk_dst_get(clcsock->sk); | |
1a26d020 | 239 | int rc; |
c246d942 KG |
240 | |
241 | if (!dst) { | |
242 | rc = -ENOTCONN; | |
243 | goto out; | |
244 | } | |
245 | if (!dst->dev) { | |
246 | rc = -ENODEV; | |
247 | goto out_rel; | |
248 | } | |
249 | rcu_read_lock(); | |
250 | if (!prop->ipv6_prefixes_cnt) | |
251 | rc = smc_clc_prfx_match4_rcu(dst->dev, prop); | |
1a26d020 KG |
252 | else |
253 | rc = smc_clc_prfx_match6_rcu(dst->dev, prop); | |
c246d942 | 254 | rcu_read_unlock(); |
696cd301 KG |
255 | out_rel: |
256 | dst_release(dst); | |
257 | out: | |
258 | return rc; | |
259 | } | |
260 | ||
a046d57d UB |
261 | /* Wait for data on the tcp-socket, analyze received data |
262 | * Returns: | |
263 | * 0 if success and it was not a decline that we received. | |
264 | * SMC_CLC_DECL_REPLY if decline received for fallback w/o another decl send. | |
265 | * clcsock error, -EINTR, -ECONNRESET, -EPROTO otherwise. | |
266 | */ | |
267 | int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, | |
268 | u8 expected_type) | |
269 | { | |
f6bdc42f | 270 | long rcvtimeo = smc->clcsock->sk->sk_rcvtimeo; |
a046d57d UB |
271 | struct sock *clc_sk = smc->clcsock->sk; |
272 | struct smc_clc_msg_hdr *clcm = buf; | |
273 | struct msghdr msg = {NULL, 0}; | |
274 | int reason_code = 0; | |
d63d271c | 275 | struct kvec vec = {buf, buflen}; |
a046d57d UB |
276 | int len, datlen; |
277 | int krflags; | |
278 | ||
279 | /* peek the first few bytes to determine length of data to receive | |
280 | * so we don't consume any subsequent CLC message or payload data | |
281 | * in the TCP byte stream | |
282 | */ | |
d63d271c AV |
283 | /* |
284 | * Caller must make sure that buflen is no less than | |
285 | * sizeof(struct smc_clc_msg_hdr) | |
286 | */ | |
a046d57d UB |
287 | krflags = MSG_PEEK | MSG_WAITALL; |
288 | smc->clcsock->sk->sk_rcvtimeo = CLC_WAIT_TIME; | |
d63d271c AV |
289 | iov_iter_kvec(&msg.msg_iter, READ | ITER_KVEC, &vec, 1, |
290 | sizeof(struct smc_clc_msg_hdr)); | |
291 | len = sock_recvmsg(smc->clcsock, &msg, krflags); | |
a046d57d UB |
292 | if (signal_pending(current)) { |
293 | reason_code = -EINTR; | |
294 | clc_sk->sk_err = EINTR; | |
295 | smc->sk.sk_err = EINTR; | |
296 | goto out; | |
297 | } | |
298 | if (clc_sk->sk_err) { | |
299 | reason_code = -clc_sk->sk_err; | |
300 | smc->sk.sk_err = clc_sk->sk_err; | |
301 | goto out; | |
302 | } | |
303 | if (!len) { /* peer has performed orderly shutdown */ | |
304 | smc->sk.sk_err = ECONNRESET; | |
305 | reason_code = -ECONNRESET; | |
306 | goto out; | |
307 | } | |
308 | if (len < 0) { | |
309 | smc->sk.sk_err = -len; | |
310 | reason_code = len; | |
311 | goto out; | |
312 | } | |
313 | datlen = ntohs(clcm->length); | |
314 | if ((len < sizeof(struct smc_clc_msg_hdr)) || | |
e7b7a64a | 315 | (datlen > buflen) || |
c758dfdd HW |
316 | (clcm->version != SMC_CLC_V1) || |
317 | (clcm->path != SMC_TYPE_R && clcm->path != SMC_TYPE_D && | |
318 | clcm->path != SMC_TYPE_B) || | |
a046d57d UB |
319 | ((clcm->type != SMC_CLC_DECLINE) && |
320 | (clcm->type != expected_type))) { | |
321 | smc->sk.sk_err = EPROTO; | |
322 | reason_code = -EPROTO; | |
323 | goto out; | |
324 | } | |
325 | ||
326 | /* receive the complete CLC message */ | |
a046d57d | 327 | memset(&msg, 0, sizeof(struct msghdr)); |
ab6f6dd1 | 328 | iov_iter_kvec(&msg.msg_iter, READ | ITER_KVEC, &vec, 1, datlen); |
a046d57d | 329 | krflags = MSG_WAITALL; |
d63d271c | 330 | len = sock_recvmsg(smc->clcsock, &msg, krflags); |
e7b7a64a | 331 | if (len < datlen || !smc_clc_msg_hdr_valid(clcm)) { |
a046d57d UB |
332 | smc->sk.sk_err = EPROTO; |
333 | reason_code = -EPROTO; | |
334 | goto out; | |
335 | } | |
0cfdd8f9 | 336 | if (clcm->type == SMC_CLC_DECLINE) { |
603cc149 KG |
337 | struct smc_clc_msg_decline *dclc; |
338 | ||
339 | dclc = (struct smc_clc_msg_decline *)clcm; | |
340 | reason_code = SMC_CLC_DECL_PEERDECL; | |
341 | smc->peer_diagnosis = ntohl(dclc->peer_diagnosis); | |
bfbedfd3 | 342 | if (((struct smc_clc_msg_decline *)buf)->hdr.flag) { |
517c300e | 343 | smc->conn.lgr->sync_err = 1; |
bfbedfd3 UB |
344 | smc_lgr_terminate(smc->conn.lgr); |
345 | } | |
0cfdd8f9 UB |
346 | } |
347 | ||
a046d57d | 348 | out: |
f6bdc42f | 349 | smc->clcsock->sk->sk_rcvtimeo = rcvtimeo; |
a046d57d UB |
350 | return reason_code; |
351 | } | |
352 | ||
353 | /* send CLC DECLINE message across internal TCP socket */ | |
bfbedfd3 | 354 | int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info) |
a046d57d UB |
355 | { |
356 | struct smc_clc_msg_decline dclc; | |
357 | struct msghdr msg; | |
358 | struct kvec vec; | |
359 | int len; | |
360 | ||
361 | memset(&dclc, 0, sizeof(dclc)); | |
362 | memcpy(dclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); | |
363 | dclc.hdr.type = SMC_CLC_DECLINE; | |
364 | dclc.hdr.length = htons(sizeof(struct smc_clc_msg_decline)); | |
365 | dclc.hdr.version = SMC_CLC_V1; | |
bfbedfd3 | 366 | dclc.hdr.flag = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ? 1 : 0; |
a046d57d UB |
367 | memcpy(dclc.id_for_peer, local_systemid, sizeof(local_systemid)); |
368 | dclc.peer_diagnosis = htonl(peer_diag_info); | |
369 | memcpy(dclc.trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); | |
370 | ||
371 | memset(&msg, 0, sizeof(msg)); | |
372 | vec.iov_base = &dclc; | |
373 | vec.iov_len = sizeof(struct smc_clc_msg_decline); | |
374 | len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1, | |
375 | sizeof(struct smc_clc_msg_decline)); | |
376 | if (len < sizeof(struct smc_clc_msg_decline)) | |
377 | smc->sk.sk_err = EPROTO; | |
378 | if (len < 0) | |
379 | smc->sk.sk_err = -len; | |
0c9f1515 | 380 | return sock_error(&smc->sk); |
a046d57d UB |
381 | } |
382 | ||
383 | /* send CLC PROPOSAL message across internal TCP socket */ | |
c758dfdd | 384 | int smc_clc_send_proposal(struct smc_sock *smc, int smc_type, |
7005ada6 | 385 | struct smc_ib_device *ibdev, u8 ibport, u8 gid[], |
c758dfdd | 386 | struct smcd_dev *ismdev) |
a046d57d | 387 | { |
1a26d020 | 388 | struct smc_clc_ipv6_prefix ipv6_prfx[SMC_CLC_MAX_V6_PREFIX]; |
e7b7a64a | 389 | struct smc_clc_msg_proposal_prefix pclc_prfx; |
c758dfdd | 390 | struct smc_clc_msg_smcd pclc_smcd; |
a046d57d | 391 | struct smc_clc_msg_proposal pclc; |
e7b7a64a | 392 | struct smc_clc_msg_trail trl; |
1a26d020 | 393 | int len, i, plen, rc; |
a046d57d | 394 | int reason_code = 0; |
c758dfdd | 395 | struct kvec vec[5]; |
a046d57d | 396 | struct msghdr msg; |
a046d57d | 397 | |
c246d942 | 398 | /* retrieve ip prefixes for CLC proposal msg */ |
1a26d020 | 399 | rc = smc_clc_prfx_set(smc->clcsock, &pclc_prfx, ipv6_prfx); |
c246d942 KG |
400 | if (rc) |
401 | return SMC_CLC_DECL_CNFERR; /* configuration error */ | |
402 | ||
a046d57d | 403 | /* send SMC Proposal CLC message */ |
1a26d020 KG |
404 | plen = sizeof(pclc) + sizeof(pclc_prfx) + |
405 | (pclc_prfx.ipv6_prefixes_cnt * sizeof(ipv6_prfx[0])) + | |
406 | sizeof(trl); | |
a046d57d UB |
407 | memset(&pclc, 0, sizeof(pclc)); |
408 | memcpy(pclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); | |
409 | pclc.hdr.type = SMC_CLC_PROPOSAL; | |
a046d57d | 410 | pclc.hdr.version = SMC_CLC_V1; /* SMC version */ |
c758dfdd HW |
411 | pclc.hdr.path = smc_type; |
412 | if (smc_type == SMC_TYPE_R || smc_type == SMC_TYPE_B) { | |
413 | /* add SMC-R specifics */ | |
414 | memcpy(pclc.lcl.id_for_peer, local_systemid, | |
415 | sizeof(local_systemid)); | |
7005ada6 | 416 | memcpy(&pclc.lcl.gid, gid, SMC_GID_SIZE); |
c758dfdd HW |
417 | memcpy(&pclc.lcl.mac, &ibdev->mac[ibport - 1], ETH_ALEN); |
418 | pclc.iparea_offset = htons(0); | |
419 | } | |
420 | if (smc_type == SMC_TYPE_D || smc_type == SMC_TYPE_B) { | |
421 | /* add SMC-D specifics */ | |
422 | memset(&pclc_smcd, 0, sizeof(pclc_smcd)); | |
423 | plen += sizeof(pclc_smcd); | |
424 | pclc.iparea_offset = htons(SMC_CLC_PROPOSAL_MAX_OFFSET); | |
425 | pclc_smcd.gid = ismdev->local_gid; | |
426 | } | |
427 | pclc.hdr.length = htons(plen); | |
a046d57d | 428 | |
e7b7a64a | 429 | memcpy(trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); |
a046d57d | 430 | memset(&msg, 0, sizeof(msg)); |
1a26d020 KG |
431 | i = 0; |
432 | vec[i].iov_base = &pclc; | |
433 | vec[i++].iov_len = sizeof(pclc); | |
c758dfdd HW |
434 | if (smc_type == SMC_TYPE_D || smc_type == SMC_TYPE_B) { |
435 | vec[i].iov_base = &pclc_smcd; | |
436 | vec[i++].iov_len = sizeof(pclc_smcd); | |
437 | } | |
1a26d020 KG |
438 | vec[i].iov_base = &pclc_prfx; |
439 | vec[i++].iov_len = sizeof(pclc_prfx); | |
440 | if (pclc_prfx.ipv6_prefixes_cnt > 0) { | |
441 | vec[i].iov_base = &ipv6_prfx[0]; | |
442 | vec[i++].iov_len = pclc_prfx.ipv6_prefixes_cnt * | |
443 | sizeof(ipv6_prfx[0]); | |
444 | } | |
445 | vec[i].iov_base = &trl; | |
446 | vec[i++].iov_len = sizeof(trl); | |
a046d57d | 447 | /* due to the few bytes needed for clc-handshake this cannot block */ |
1a26d020 | 448 | len = kernel_sendmsg(smc->clcsock, &msg, vec, i, plen); |
a046d57d UB |
449 | if (len < sizeof(pclc)) { |
450 | if (len >= 0) { | |
451 | reason_code = -ENETUNREACH; | |
452 | smc->sk.sk_err = -reason_code; | |
453 | } else { | |
454 | smc->sk.sk_err = smc->clcsock->sk->sk_err; | |
455 | reason_code = -smc->sk.sk_err; | |
456 | } | |
457 | } | |
458 | ||
459 | return reason_code; | |
460 | } | |
461 | ||
462 | /* send CLC CONFIRM message across internal TCP socket */ | |
463 | int smc_clc_send_confirm(struct smc_sock *smc) | |
464 | { | |
0cfdd8f9 | 465 | struct smc_connection *conn = &smc->conn; |
a046d57d | 466 | struct smc_clc_msg_accept_confirm cclc; |
0cfdd8f9 | 467 | struct smc_link *link; |
a046d57d UB |
468 | int reason_code = 0; |
469 | struct msghdr msg; | |
470 | struct kvec vec; | |
471 | int len; | |
472 | ||
473 | /* send SMC Confirm CLC msg */ | |
474 | memset(&cclc, 0, sizeof(cclc)); | |
a046d57d | 475 | cclc.hdr.type = SMC_CLC_CONFIRM; |
a046d57d | 476 | cclc.hdr.version = SMC_CLC_V1; /* SMC version */ |
c758dfdd HW |
477 | if (smc->conn.lgr->is_smcd) { |
478 | /* SMC-D specific settings */ | |
479 | memcpy(cclc.hdr.eyecatcher, SMCD_EYECATCHER, | |
480 | sizeof(SMCD_EYECATCHER)); | |
481 | cclc.hdr.path = SMC_TYPE_D; | |
482 | cclc.hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN); | |
483 | cclc.gid = conn->lgr->smcd->local_gid; | |
484 | cclc.token = conn->rmb_desc->token; | |
485 | cclc.dmbe_size = conn->rmbe_size_short; | |
486 | cclc.dmbe_idx = 0; | |
487 | memcpy(&cclc.linkid, conn->lgr->id, SMC_LGR_ID_SIZE); | |
488 | memcpy(cclc.smcd_trl.eyecatcher, SMCD_EYECATCHER, | |
489 | sizeof(SMCD_EYECATCHER)); | |
490 | } else { | |
491 | /* SMC-R specific settings */ | |
492 | link = &conn->lgr->lnk[SMC_SINGLE_LINK]; | |
493 | memcpy(cclc.hdr.eyecatcher, SMC_EYECATCHER, | |
494 | sizeof(SMC_EYECATCHER)); | |
495 | cclc.hdr.path = SMC_TYPE_R; | |
496 | cclc.hdr.length = htons(SMCR_CLC_ACCEPT_CONFIRM_LEN); | |
497 | memcpy(cclc.lcl.id_for_peer, local_systemid, | |
498 | sizeof(local_systemid)); | |
7005ada6 | 499 | memcpy(&cclc.lcl.gid, link->gid, SMC_GID_SIZE); |
c758dfdd HW |
500 | memcpy(&cclc.lcl.mac, &link->smcibdev->mac[link->ibport - 1], |
501 | ETH_ALEN); | |
502 | hton24(cclc.qpn, link->roce_qp->qp_num); | |
503 | cclc.rmb_rkey = | |
504 | htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey); | |
505 | cclc.rmbe_idx = 1; /* for now: 1 RMB = 1 RMBE */ | |
506 | cclc.rmbe_alert_token = htonl(conn->alert_token_local); | |
507 | cclc.qp_mtu = min(link->path_mtu, link->peer_mtu); | |
508 | cclc.rmbe_size = conn->rmbe_size_short; | |
509 | cclc.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address | |
510 | (conn->rmb_desc->sgt[SMC_SINGLE_LINK].sgl)); | |
511 | hton24(cclc.psn, link->psn_initial); | |
512 | memcpy(cclc.smcr_trl.eyecatcher, SMC_EYECATCHER, | |
513 | sizeof(SMC_EYECATCHER)); | |
514 | } | |
a046d57d UB |
515 | |
516 | memset(&msg, 0, sizeof(msg)); | |
517 | vec.iov_base = &cclc; | |
c758dfdd HW |
518 | vec.iov_len = ntohs(cclc.hdr.length); |
519 | len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1, | |
520 | ntohs(cclc.hdr.length)); | |
521 | if (len < ntohs(cclc.hdr.length)) { | |
a046d57d UB |
522 | if (len >= 0) { |
523 | reason_code = -ENETUNREACH; | |
524 | smc->sk.sk_err = -reason_code; | |
525 | } else { | |
526 | smc->sk.sk_err = smc->clcsock->sk->sk_err; | |
527 | reason_code = -smc->sk.sk_err; | |
528 | } | |
529 | } | |
530 | return reason_code; | |
531 | } | |
532 | ||
533 | /* send CLC ACCEPT message across internal TCP socket */ | |
0cfdd8f9 | 534 | int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact) |
a046d57d | 535 | { |
0cfdd8f9 | 536 | struct smc_connection *conn = &new_smc->conn; |
a046d57d | 537 | struct smc_clc_msg_accept_confirm aclc; |
0cfdd8f9 | 538 | struct smc_link *link; |
a046d57d UB |
539 | struct msghdr msg; |
540 | struct kvec vec; | |
541 | int rc = 0; | |
542 | int len; | |
543 | ||
544 | memset(&aclc, 0, sizeof(aclc)); | |
a046d57d | 545 | aclc.hdr.type = SMC_CLC_ACCEPT; |
a046d57d | 546 | aclc.hdr.version = SMC_CLC_V1; /* SMC version */ |
0cfdd8f9 UB |
547 | if (srv_first_contact) |
548 | aclc.hdr.flag = 1; | |
c758dfdd HW |
549 | |
550 | if (new_smc->conn.lgr->is_smcd) { | |
551 | /* SMC-D specific settings */ | |
552 | aclc.hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN); | |
553 | memcpy(aclc.hdr.eyecatcher, SMCD_EYECATCHER, | |
554 | sizeof(SMCD_EYECATCHER)); | |
555 | aclc.hdr.path = SMC_TYPE_D; | |
556 | aclc.gid = conn->lgr->smcd->local_gid; | |
557 | aclc.token = conn->rmb_desc->token; | |
558 | aclc.dmbe_size = conn->rmbe_size_short; | |
559 | aclc.dmbe_idx = 0; | |
560 | memcpy(&aclc.linkid, conn->lgr->id, SMC_LGR_ID_SIZE); | |
561 | memcpy(aclc.smcd_trl.eyecatcher, SMCD_EYECATCHER, | |
562 | sizeof(SMCD_EYECATCHER)); | |
563 | } else { | |
564 | /* SMC-R specific settings */ | |
565 | aclc.hdr.length = htons(SMCR_CLC_ACCEPT_CONFIRM_LEN); | |
566 | memcpy(aclc.hdr.eyecatcher, SMC_EYECATCHER, | |
567 | sizeof(SMC_EYECATCHER)); | |
568 | aclc.hdr.path = SMC_TYPE_R; | |
569 | link = &conn->lgr->lnk[SMC_SINGLE_LINK]; | |
570 | memcpy(aclc.lcl.id_for_peer, local_systemid, | |
571 | sizeof(local_systemid)); | |
7005ada6 | 572 | memcpy(&aclc.lcl.gid, link->gid, SMC_GID_SIZE); |
c758dfdd HW |
573 | memcpy(&aclc.lcl.mac, link->smcibdev->mac[link->ibport - 1], |
574 | ETH_ALEN); | |
575 | hton24(aclc.qpn, link->roce_qp->qp_num); | |
576 | aclc.rmb_rkey = | |
577 | htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey); | |
578 | aclc.rmbe_idx = 1; /* as long as 1 RMB = 1 RMBE */ | |
579 | aclc.rmbe_alert_token = htonl(conn->alert_token_local); | |
580 | aclc.qp_mtu = link->path_mtu; | |
581 | aclc.rmbe_size = conn->rmbe_size_short, | |
582 | aclc.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address | |
583 | (conn->rmb_desc->sgt[SMC_SINGLE_LINK].sgl)); | |
584 | hton24(aclc.psn, link->psn_initial); | |
585 | memcpy(aclc.smcr_trl.eyecatcher, SMC_EYECATCHER, | |
586 | sizeof(SMC_EYECATCHER)); | |
587 | } | |
a046d57d UB |
588 | |
589 | memset(&msg, 0, sizeof(msg)); | |
590 | vec.iov_base = &aclc; | |
c758dfdd HW |
591 | vec.iov_len = ntohs(aclc.hdr.length); |
592 | len = kernel_sendmsg(new_smc->clcsock, &msg, &vec, 1, | |
593 | ntohs(aclc.hdr.length)); | |
594 | if (len < ntohs(aclc.hdr.length)) { | |
a046d57d UB |
595 | if (len >= 0) |
596 | new_smc->sk.sk_err = EPROTO; | |
597 | else | |
598 | new_smc->sk.sk_err = new_smc->clcsock->sk->sk_err; | |
599 | rc = sock_error(&new_smc->sk); | |
600 | } | |
601 | ||
602 | return rc; | |
603 | } |