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 | 16 | #include <linux/sched/signal.h> |
b81a5eb7 UB |
17 | #include <linux/utsname.h> |
18 | #include <linux/ctype.h> | |
c3edc401 | 19 | |
1a26d020 | 20 | #include <net/addrconf.h> |
a046d57d UB |
21 | #include <net/sock.h> |
22 | #include <net/tcp.h> | |
23 | ||
24 | #include "smc.h" | |
0cfdd8f9 | 25 | #include "smc_core.h" |
a046d57d UB |
26 | #include "smc_clc.h" |
27 | #include "smc_ib.h" | |
c758dfdd HW |
28 | #include "smc_ism.h" |
29 | ||
30 | #define SMCR_CLC_ACCEPT_CONFIRM_LEN 68 | |
31 | #define SMCD_CLC_ACCEPT_CONFIRM_LEN 48 | |
a7c9c5f4 | 32 | #define SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 78 |
fb4f7926 | 33 | #define SMC_CLC_RECV_BUF_LEN 100 |
a046d57d | 34 | |
0f627126 SR |
35 | /* eye catcher "SMCR" EBCDIC for CLC messages */ |
36 | static const char SMC_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xd9'}; | |
c758dfdd HW |
37 | /* eye catcher "SMCD" EBCDIC for CLC messages */ |
38 | static const char SMCD_EYECATCHER[4] = {'\xe2', '\xd4', '\xc3', '\xc4'}; | |
0f627126 | 39 | |
b81a5eb7 UB |
40 | static u8 smc_hostname[SMC_MAX_HOSTNAME_LEN]; |
41 | ||
8c3dca34 UB |
42 | /* check arriving CLC proposal */ |
43 | static bool smc_clc_msg_prop_valid(struct smc_clc_msg_proposal *pclc) | |
44 | { | |
45 | struct smc_clc_msg_proposal_prefix *pclc_prfx; | |
46 | struct smc_clc_smcd_v2_extension *smcd_v2_ext; | |
47 | struct smc_clc_msg_hdr *hdr = &pclc->hdr; | |
48 | struct smc_clc_v2_extension *v2_ext; | |
49 | ||
50 | v2_ext = smc_get_clc_v2_ext(pclc); | |
51 | pclc_prfx = smc_clc_proposal_get_prefix(pclc); | |
52 | if (hdr->version == SMC_V1) { | |
53 | if (hdr->typev1 == SMC_TYPE_N) | |
54 | return false; | |
55 | if (ntohs(hdr->length) != | |
56 | sizeof(*pclc) + ntohs(pclc->iparea_offset) + | |
57 | sizeof(*pclc_prfx) + | |
58 | pclc_prfx->ipv6_prefixes_cnt * | |
59 | sizeof(struct smc_clc_ipv6_prefix) + | |
60 | sizeof(struct smc_clc_msg_trail)) | |
61 | return false; | |
62 | } else { | |
63 | if (ntohs(hdr->length) != | |
64 | sizeof(*pclc) + | |
65 | sizeof(struct smc_clc_msg_smcd) + | |
66 | (hdr->typev1 != SMC_TYPE_N ? | |
67 | sizeof(*pclc_prfx) + | |
68 | pclc_prfx->ipv6_prefixes_cnt * | |
69 | sizeof(struct smc_clc_ipv6_prefix) : 0) + | |
70 | (hdr->typev2 != SMC_TYPE_N ? | |
71 | sizeof(*v2_ext) + | |
72 | v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN : 0) + | |
73 | (smcd_indicated(hdr->typev2) ? | |
74 | sizeof(*smcd_v2_ext) + v2_ext->hdr.ism_gid_cnt * | |
75 | sizeof(struct smc_clc_smcd_gid_chid) : | |
76 | 0) + | |
77 | sizeof(struct smc_clc_msg_trail)) | |
78 | return false; | |
79 | } | |
80 | return true; | |
81 | } | |
82 | ||
a7c9c5f4 UB |
83 | /* check arriving CLC accept or confirm */ |
84 | static bool | |
85 | smc_clc_msg_acc_conf_valid(struct smc_clc_msg_accept_confirm_v2 *clc_v2) | |
86 | { | |
87 | struct smc_clc_msg_hdr *hdr = &clc_v2->hdr; | |
88 | ||
89 | if (hdr->typev1 != SMC_TYPE_R && hdr->typev1 != SMC_TYPE_D) | |
90 | return false; | |
91 | if (hdr->version == SMC_V1) { | |
92 | if ((hdr->typev1 == SMC_TYPE_R && | |
93 | ntohs(hdr->length) != SMCR_CLC_ACCEPT_CONFIRM_LEN) || | |
94 | (hdr->typev1 == SMC_TYPE_D && | |
95 | ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN)) | |
96 | return false; | |
97 | } else { | |
98 | if (hdr->typev1 == SMC_TYPE_D && | |
b81a5eb7 UB |
99 | ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 && |
100 | (ntohs(hdr->length) != SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 + | |
101 | sizeof(struct smc_clc_first_contact_ext))) | |
a7c9c5f4 UB |
102 | return false; |
103 | } | |
104 | return true; | |
105 | } | |
106 | ||
b81a5eb7 UB |
107 | static void smc_clc_fill_fce(struct smc_clc_first_contact_ext *fce, int *len) |
108 | { | |
109 | memset(fce, 0, sizeof(*fce)); | |
110 | fce->os_type = SMC_CLC_OS_LINUX; | |
111 | fce->release = SMC_RELEASE; | |
112 | memcpy(fce->hostname, smc_hostname, sizeof(smc_hostname)); | |
113 | (*len) += sizeof(*fce); | |
114 | } | |
115 | ||
e7b7a64a UB |
116 | /* check if received message has a correct header length and contains valid |
117 | * heading and trailing eyecatchers | |
118 | */ | |
fb4f7926 | 119 | static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl) |
e7b7a64a | 120 | { |
a7c9c5f4 | 121 | struct smc_clc_msg_accept_confirm_v2 *clc_v2; |
e7b7a64a UB |
122 | struct smc_clc_msg_proposal *pclc; |
123 | struct smc_clc_msg_decline *dclc; | |
124 | struct smc_clc_msg_trail *trl; | |
125 | ||
c758dfdd HW |
126 | if (memcmp(clcm->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) && |
127 | memcmp(clcm->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER))) | |
e7b7a64a UB |
128 | return false; |
129 | switch (clcm->type) { | |
130 | case SMC_CLC_PROPOSAL: | |
131 | pclc = (struct smc_clc_msg_proposal *)clcm; | |
8c3dca34 | 132 | if (!smc_clc_msg_prop_valid(pclc)) |
e7b7a64a UB |
133 | return false; |
134 | trl = (struct smc_clc_msg_trail *) | |
135 | ((u8 *)pclc + ntohs(pclc->hdr.length) - sizeof(*trl)); | |
136 | break; | |
137 | case SMC_CLC_ACCEPT: | |
138 | case SMC_CLC_CONFIRM: | |
a7c9c5f4 UB |
139 | clc_v2 = (struct smc_clc_msg_accept_confirm_v2 *)clcm; |
140 | if (!smc_clc_msg_acc_conf_valid(clc_v2)) | |
e7b7a64a | 141 | return false; |
c758dfdd | 142 | trl = (struct smc_clc_msg_trail *) |
a7c9c5f4 UB |
143 | ((u8 *)clc_v2 + ntohs(clc_v2->hdr.length) - |
144 | sizeof(*trl)); | |
e7b7a64a UB |
145 | break; |
146 | case SMC_CLC_DECLINE: | |
147 | dclc = (struct smc_clc_msg_decline *)clcm; | |
148 | if (ntohs(dclc->hdr.length) != sizeof(*dclc)) | |
149 | return false; | |
150 | trl = &dclc->trl; | |
151 | break; | |
152 | default: | |
153 | return false; | |
154 | } | |
fb4f7926 UB |
155 | if (check_trl && |
156 | memcmp(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)) && | |
c758dfdd | 157 | memcmp(trl->eyecatcher, SMCD_EYECATCHER, sizeof(SMCD_EYECATCHER))) |
e7b7a64a UB |
158 | return false; |
159 | return true; | |
160 | } | |
161 | ||
c246d942 KG |
162 | /* find ipv4 addr on device and get the prefix len, fill CLC proposal msg */ |
163 | static int smc_clc_prfx_set4_rcu(struct dst_entry *dst, __be32 ipv4, | |
164 | struct smc_clc_msg_proposal_prefix *prop) | |
165 | { | |
166 | struct in_device *in_dev = __in_dev_get_rcu(dst->dev); | |
cd5a411d | 167 | const struct in_ifaddr *ifa; |
c246d942 KG |
168 | |
169 | if (!in_dev) | |
170 | return -ENODEV; | |
cd5a411d FW |
171 | |
172 | in_dev_for_each_ifa_rcu(ifa, in_dev) { | |
c246d942 KG |
173 | if (!inet_ifa_match(ipv4, ifa)) |
174 | continue; | |
175 | prop->prefix_len = inet_mask_len(ifa->ifa_mask); | |
176 | prop->outgoing_subnet = ifa->ifa_address & ifa->ifa_mask; | |
177 | /* prop->ipv6_prefixes_cnt = 0; already done by memset before */ | |
178 | return 0; | |
cd5a411d | 179 | } |
c246d942 KG |
180 | return -ENOENT; |
181 | } | |
182 | ||
1a26d020 KG |
183 | /* fill CLC proposal msg with ipv6 prefixes from device */ |
184 | static int smc_clc_prfx_set6_rcu(struct dst_entry *dst, | |
185 | struct smc_clc_msg_proposal_prefix *prop, | |
186 | struct smc_clc_ipv6_prefix *ipv6_prfx) | |
187 | { | |
188 | #if IS_ENABLED(CONFIG_IPV6) | |
189 | struct inet6_dev *in6_dev = __in6_dev_get(dst->dev); | |
190 | struct inet6_ifaddr *ifa; | |
191 | int cnt = 0; | |
192 | ||
193 | if (!in6_dev) | |
194 | return -ENODEV; | |
195 | /* use a maximum of 8 IPv6 prefixes from device */ | |
196 | list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { | |
197 | if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL) | |
198 | continue; | |
199 | ipv6_addr_prefix(&ipv6_prfx[cnt].prefix, | |
200 | &ifa->addr, ifa->prefix_len); | |
201 | ipv6_prfx[cnt].prefix_len = ifa->prefix_len; | |
202 | cnt++; | |
203 | if (cnt == SMC_CLC_MAX_V6_PREFIX) | |
204 | break; | |
205 | } | |
206 | prop->ipv6_prefixes_cnt = cnt; | |
207 | if (cnt) | |
208 | return 0; | |
209 | #endif | |
210 | return -ENOENT; | |
211 | } | |
212 | ||
c246d942 KG |
213 | /* retrieve and set prefixes in CLC proposal msg */ |
214 | static int smc_clc_prfx_set(struct socket *clcsock, | |
1a26d020 KG |
215 | struct smc_clc_msg_proposal_prefix *prop, |
216 | struct smc_clc_ipv6_prefix *ipv6_prfx) | |
696cd301 KG |
217 | { |
218 | struct dst_entry *dst = sk_dst_get(clcsock->sk); | |
c246d942 | 219 | struct sockaddr_storage addrs; |
1a26d020 | 220 | struct sockaddr_in6 *addr6; |
c246d942 | 221 | struct sockaddr_in *addr; |
696cd301 KG |
222 | int rc = -ENOENT; |
223 | ||
224 | if (!dst) { | |
225 | rc = -ENOTCONN; | |
226 | goto out; | |
227 | } | |
228 | if (!dst->dev) { | |
229 | rc = -ENODEV; | |
230 | goto out_rel; | |
231 | } | |
696cd301 | 232 | /* get address to which the internal TCP socket is bound */ |
c246d942 KG |
233 | kernel_getsockname(clcsock, (struct sockaddr *)&addrs); |
234 | /* analyze IP specific data of net_device belonging to TCP socket */ | |
1a26d020 | 235 | addr6 = (struct sockaddr_in6 *)&addrs; |
696cd301 | 236 | rcu_read_lock(); |
c246d942 KG |
237 | if (addrs.ss_family == PF_INET) { |
238 | /* IPv4 */ | |
239 | addr = (struct sockaddr_in *)&addrs; | |
240 | rc = smc_clc_prfx_set4_rcu(dst, addr->sin_addr.s_addr, prop); | |
1a26d020 KG |
241 | } else if (ipv6_addr_v4mapped(&addr6->sin6_addr)) { |
242 | /* mapped IPv4 address - peer is IPv4 only */ | |
243 | rc = smc_clc_prfx_set4_rcu(dst, addr6->sin6_addr.s6_addr32[3], | |
244 | prop); | |
245 | } else { | |
246 | /* IPv6 */ | |
247 | rc = smc_clc_prfx_set6_rcu(dst, prop, ipv6_prfx); | |
c246d942 KG |
248 | } |
249 | rcu_read_unlock(); | |
250 | out_rel: | |
251 | dst_release(dst); | |
252 | out: | |
253 | return rc; | |
254 | } | |
255 | ||
256 | /* match ipv4 addrs of dev against addr in CLC proposal */ | |
257 | static int smc_clc_prfx_match4_rcu(struct net_device *dev, | |
258 | struct smc_clc_msg_proposal_prefix *prop) | |
259 | { | |
260 | struct in_device *in_dev = __in_dev_get_rcu(dev); | |
cd5a411d | 261 | const struct in_ifaddr *ifa; |
c246d942 KG |
262 | |
263 | if (!in_dev) | |
264 | return -ENODEV; | |
cd5a411d | 265 | in_dev_for_each_ifa_rcu(ifa, in_dev) { |
c246d942 KG |
266 | if (prop->prefix_len == inet_mask_len(ifa->ifa_mask) && |
267 | inet_ifa_match(prop->outgoing_subnet, ifa)) | |
268 | return 0; | |
cd5a411d | 269 | } |
696cd301 | 270 | |
c246d942 KG |
271 | return -ENOENT; |
272 | } | |
273 | ||
1a26d020 KG |
274 | /* match ipv6 addrs of dev against addrs in CLC proposal */ |
275 | static int smc_clc_prfx_match6_rcu(struct net_device *dev, | |
276 | struct smc_clc_msg_proposal_prefix *prop) | |
277 | { | |
278 | #if IS_ENABLED(CONFIG_IPV6) | |
279 | struct inet6_dev *in6_dev = __in6_dev_get(dev); | |
280 | struct smc_clc_ipv6_prefix *ipv6_prfx; | |
281 | struct inet6_ifaddr *ifa; | |
282 | int i, max; | |
283 | ||
284 | if (!in6_dev) | |
285 | return -ENODEV; | |
286 | /* ipv6 prefix list starts behind smc_clc_msg_proposal_prefix */ | |
287 | ipv6_prfx = (struct smc_clc_ipv6_prefix *)((u8 *)prop + sizeof(*prop)); | |
288 | max = min_t(u8, prop->ipv6_prefixes_cnt, SMC_CLC_MAX_V6_PREFIX); | |
289 | list_for_each_entry(ifa, &in6_dev->addr_list, if_list) { | |
290 | if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL) | |
291 | continue; | |
292 | for (i = 0; i < max; i++) { | |
293 | if (ifa->prefix_len == ipv6_prfx[i].prefix_len && | |
294 | ipv6_prefix_equal(&ifa->addr, &ipv6_prfx[i].prefix, | |
295 | ifa->prefix_len)) | |
296 | return 0; | |
297 | } | |
298 | } | |
299 | #endif | |
300 | return -ENOENT; | |
301 | } | |
302 | ||
c246d942 KG |
303 | /* check if proposed prefixes match one of our device prefixes */ |
304 | int smc_clc_prfx_match(struct socket *clcsock, | |
305 | struct smc_clc_msg_proposal_prefix *prop) | |
306 | { | |
307 | struct dst_entry *dst = sk_dst_get(clcsock->sk); | |
1a26d020 | 308 | int rc; |
c246d942 KG |
309 | |
310 | if (!dst) { | |
311 | rc = -ENOTCONN; | |
312 | goto out; | |
313 | } | |
314 | if (!dst->dev) { | |
315 | rc = -ENODEV; | |
316 | goto out_rel; | |
317 | } | |
318 | rcu_read_lock(); | |
319 | if (!prop->ipv6_prefixes_cnt) | |
320 | rc = smc_clc_prfx_match4_rcu(dst->dev, prop); | |
1a26d020 KG |
321 | else |
322 | rc = smc_clc_prfx_match6_rcu(dst->dev, prop); | |
c246d942 | 323 | rcu_read_unlock(); |
696cd301 KG |
324 | out_rel: |
325 | dst_release(dst); | |
326 | out: | |
327 | return rc; | |
328 | } | |
329 | ||
a046d57d UB |
330 | /* Wait for data on the tcp-socket, analyze received data |
331 | * Returns: | |
332 | * 0 if success and it was not a decline that we received. | |
333 | * SMC_CLC_DECL_REPLY if decline received for fallback w/o another decl send. | |
334 | * clcsock error, -EINTR, -ECONNRESET, -EPROTO otherwise. | |
335 | */ | |
336 | int smc_clc_wait_msg(struct smc_sock *smc, void *buf, int buflen, | |
2b59f58e | 337 | u8 expected_type, unsigned long timeout) |
a046d57d | 338 | { |
f6bdc42f | 339 | long rcvtimeo = smc->clcsock->sk->sk_rcvtimeo; |
a046d57d UB |
340 | struct sock *clc_sk = smc->clcsock->sk; |
341 | struct smc_clc_msg_hdr *clcm = buf; | |
342 | struct msghdr msg = {NULL, 0}; | |
343 | int reason_code = 0; | |
d63d271c | 344 | struct kvec vec = {buf, buflen}; |
fb4f7926 UB |
345 | int len, datlen, recvlen; |
346 | bool check_trl = true; | |
a046d57d UB |
347 | int krflags; |
348 | ||
349 | /* peek the first few bytes to determine length of data to receive | |
350 | * so we don't consume any subsequent CLC message or payload data | |
351 | * in the TCP byte stream | |
352 | */ | |
d63d271c AV |
353 | /* |
354 | * Caller must make sure that buflen is no less than | |
355 | * sizeof(struct smc_clc_msg_hdr) | |
356 | */ | |
a046d57d | 357 | krflags = MSG_PEEK | MSG_WAITALL; |
2b59f58e | 358 | clc_sk->sk_rcvtimeo = timeout; |
aa563d7b | 359 | iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, |
d63d271c AV |
360 | sizeof(struct smc_clc_msg_hdr)); |
361 | len = sock_recvmsg(smc->clcsock, &msg, krflags); | |
a046d57d UB |
362 | if (signal_pending(current)) { |
363 | reason_code = -EINTR; | |
364 | clc_sk->sk_err = EINTR; | |
365 | smc->sk.sk_err = EINTR; | |
366 | goto out; | |
367 | } | |
368 | if (clc_sk->sk_err) { | |
369 | reason_code = -clc_sk->sk_err; | |
9ed28556 UB |
370 | if (clc_sk->sk_err == EAGAIN && |
371 | expected_type == SMC_CLC_DECLINE) | |
372 | clc_sk->sk_err = 0; /* reset for fallback usage */ | |
373 | else | |
374 | smc->sk.sk_err = clc_sk->sk_err; | |
a046d57d UB |
375 | goto out; |
376 | } | |
377 | if (!len) { /* peer has performed orderly shutdown */ | |
378 | smc->sk.sk_err = ECONNRESET; | |
379 | reason_code = -ECONNRESET; | |
380 | goto out; | |
381 | } | |
382 | if (len < 0) { | |
9ed28556 UB |
383 | if (len != -EAGAIN || expected_type != SMC_CLC_DECLINE) |
384 | smc->sk.sk_err = -len; | |
a046d57d UB |
385 | reason_code = len; |
386 | goto out; | |
387 | } | |
388 | datlen = ntohs(clcm->length); | |
389 | if ((len < sizeof(struct smc_clc_msg_hdr)) || | |
5ac54d87 | 390 | (clcm->version < SMC_V1) || |
a046d57d UB |
391 | ((clcm->type != SMC_CLC_DECLINE) && |
392 | (clcm->type != expected_type))) { | |
393 | smc->sk.sk_err = EPROTO; | |
394 | reason_code = -EPROTO; | |
395 | goto out; | |
396 | } | |
397 | ||
398 | /* receive the complete CLC message */ | |
a046d57d | 399 | memset(&msg, 0, sizeof(struct msghdr)); |
fb4f7926 UB |
400 | if (datlen > buflen) { |
401 | check_trl = false; | |
402 | recvlen = buflen; | |
403 | } else { | |
404 | recvlen = datlen; | |
405 | } | |
406 | iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen); | |
a046d57d | 407 | krflags = MSG_WAITALL; |
d63d271c | 408 | len = sock_recvmsg(smc->clcsock, &msg, krflags); |
fb4f7926 | 409 | if (len < recvlen || !smc_clc_msg_hdr_valid(clcm, check_trl)) { |
a046d57d UB |
410 | smc->sk.sk_err = EPROTO; |
411 | reason_code = -EPROTO; | |
412 | goto out; | |
413 | } | |
fb4f7926 UB |
414 | datlen -= len; |
415 | while (datlen) { | |
416 | u8 tmp[SMC_CLC_RECV_BUF_LEN]; | |
417 | ||
418 | vec.iov_base = &tmp; | |
419 | vec.iov_len = SMC_CLC_RECV_BUF_LEN; | |
420 | /* receive remaining proposal message */ | |
421 | recvlen = datlen > SMC_CLC_RECV_BUF_LEN ? | |
422 | SMC_CLC_RECV_BUF_LEN : datlen; | |
423 | iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen); | |
424 | len = sock_recvmsg(smc->clcsock, &msg, krflags); | |
425 | datlen -= len; | |
426 | } | |
0cfdd8f9 | 427 | if (clcm->type == SMC_CLC_DECLINE) { |
603cc149 KG |
428 | struct smc_clc_msg_decline *dclc; |
429 | ||
430 | dclc = (struct smc_clc_msg_decline *)clcm; | |
431 | reason_code = SMC_CLC_DECL_PEERDECL; | |
432 | smc->peer_diagnosis = ntohl(dclc->peer_diagnosis); | |
f1eb02f9 UB |
433 | if (((struct smc_clc_msg_decline *)buf)->hdr.typev2 & |
434 | SMC_FIRST_CONTACT_MASK) { | |
517c300e | 435 | smc->conn.lgr->sync_err = 1; |
5f78fe96 | 436 | smc_lgr_terminate_sched(smc->conn.lgr); |
bfbedfd3 | 437 | } |
0cfdd8f9 UB |
438 | } |
439 | ||
a046d57d | 440 | out: |
2b59f58e | 441 | clc_sk->sk_rcvtimeo = rcvtimeo; |
a046d57d UB |
442 | return reason_code; |
443 | } | |
444 | ||
445 | /* send CLC DECLINE message across internal TCP socket */ | |
e8d726c8 | 446 | int smc_clc_send_decline(struct smc_sock *smc, u32 peer_diag_info, u8 version) |
a046d57d UB |
447 | { |
448 | struct smc_clc_msg_decline dclc; | |
449 | struct msghdr msg; | |
450 | struct kvec vec; | |
451 | int len; | |
452 | ||
453 | memset(&dclc, 0, sizeof(dclc)); | |
454 | memcpy(dclc.hdr.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); | |
455 | dclc.hdr.type = SMC_CLC_DECLINE; | |
456 | dclc.hdr.length = htons(sizeof(struct smc_clc_msg_decline)); | |
e8d726c8 UB |
457 | dclc.hdr.version = version; |
458 | dclc.os_type = version == SMC_V1 ? 0 : SMC_CLC_OS_LINUX; | |
f1eb02f9 UB |
459 | dclc.hdr.typev2 = (peer_diag_info == SMC_CLC_DECL_SYNCERR) ? |
460 | SMC_FIRST_CONTACT_MASK : 0; | |
a082ec89 HW |
461 | if ((!smc->conn.lgr || !smc->conn.lgr->is_smcd) && |
462 | smc_ib_is_valid_local_systemid()) | |
369537c9 UB |
463 | memcpy(dclc.id_for_peer, local_systemid, |
464 | sizeof(local_systemid)); | |
a046d57d UB |
465 | dclc.peer_diagnosis = htonl(peer_diag_info); |
466 | memcpy(dclc.trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); | |
467 | ||
468 | memset(&msg, 0, sizeof(msg)); | |
469 | vec.iov_base = &dclc; | |
470 | vec.iov_len = sizeof(struct smc_clc_msg_decline); | |
471 | len = kernel_sendmsg(smc->clcsock, &msg, &vec, 1, | |
472 | sizeof(struct smc_clc_msg_decline)); | |
14d22d4d | 473 | if (len < 0 || len < sizeof(struct smc_clc_msg_decline)) |
6ae36bff UB |
474 | len = -EPROTO; |
475 | return len > 0 ? 0 : len; | |
a046d57d UB |
476 | } |
477 | ||
478 | /* send CLC PROPOSAL message across internal TCP socket */ | |
d70bf4f7 | 479 | int smc_clc_send_proposal(struct smc_sock *smc, struct smc_init_info *ini) |
a046d57d | 480 | { |
8c3dca34 | 481 | struct smc_clc_smcd_v2_extension *smcd_v2_ext; |
6bb14e48 UB |
482 | struct smc_clc_msg_proposal_prefix *pclc_prfx; |
483 | struct smc_clc_msg_proposal *pclc_base; | |
8c3dca34 | 484 | struct smc_clc_smcd_gid_chid *gidchids; |
6bb14e48 UB |
485 | struct smc_clc_msg_proposal_area *pclc; |
486 | struct smc_clc_ipv6_prefix *ipv6_prfx; | |
8c3dca34 | 487 | struct smc_clc_v2_extension *v2_ext; |
6bb14e48 UB |
488 | struct smc_clc_msg_smcd *pclc_smcd; |
489 | struct smc_clc_msg_trail *trl; | |
1a26d020 | 490 | int len, i, plen, rc; |
a046d57d | 491 | int reason_code = 0; |
8c3dca34 | 492 | struct kvec vec[8]; |
a046d57d | 493 | struct msghdr msg; |
a046d57d | 494 | |
6bb14e48 UB |
495 | pclc = kzalloc(sizeof(*pclc), GFP_KERNEL); |
496 | if (!pclc) | |
497 | return -ENOMEM; | |
498 | ||
499 | pclc_base = &pclc->pclc_base; | |
500 | pclc_smcd = &pclc->pclc_smcd; | |
501 | pclc_prfx = &pclc->pclc_prfx; | |
502 | ipv6_prfx = pclc->pclc_prfx_ipv6; | |
8c3dca34 UB |
503 | v2_ext = &pclc->pclc_v2_ext; |
504 | smcd_v2_ext = &pclc->pclc_smcd_v2_ext; | |
505 | gidchids = pclc->pclc_gidchids; | |
6bb14e48 UB |
506 | trl = &pclc->pclc_trl; |
507 | ||
8c3dca34 UB |
508 | pclc_base->hdr.version = SMC_V2; |
509 | pclc_base->hdr.typev1 = ini->smc_type_v1; | |
510 | pclc_base->hdr.typev2 = ini->smc_type_v2; | |
511 | plen = sizeof(*pclc_base) + sizeof(*pclc_smcd) + sizeof(*trl); | |
512 | ||
c246d942 | 513 | /* retrieve ip prefixes for CLC proposal msg */ |
8c3dca34 UB |
514 | if (ini->smc_type_v1 != SMC_TYPE_N) { |
515 | rc = smc_clc_prfx_set(smc->clcsock, pclc_prfx, ipv6_prfx); | |
516 | if (rc) { | |
517 | if (ini->smc_type_v2 == SMC_TYPE_N) { | |
518 | kfree(pclc); | |
519 | return SMC_CLC_DECL_CNFERR; | |
520 | } | |
521 | pclc_base->hdr.typev1 = SMC_TYPE_N; | |
522 | } else { | |
523 | pclc_base->iparea_offset = htons(sizeof(*pclc_smcd)); | |
524 | plen += sizeof(*pclc_prfx) + | |
525 | pclc_prfx->ipv6_prefixes_cnt * | |
526 | sizeof(ipv6_prfx[0]); | |
527 | } | |
6bb14e48 | 528 | } |
c246d942 | 529 | |
8c3dca34 | 530 | /* build SMC Proposal CLC message */ |
6bb14e48 UB |
531 | memcpy(pclc_base->hdr.eyecatcher, SMC_EYECATCHER, |
532 | sizeof(SMC_EYECATCHER)); | |
533 | pclc_base->hdr.type = SMC_CLC_PROPOSAL; | |
d70bf4f7 | 534 | if (smcr_indicated(ini->smc_type_v1)) { |
c758dfdd | 535 | /* add SMC-R specifics */ |
6bb14e48 | 536 | memcpy(pclc_base->lcl.id_for_peer, local_systemid, |
c758dfdd | 537 | sizeof(local_systemid)); |
6bb14e48 UB |
538 | memcpy(pclc_base->lcl.gid, ini->ib_gid, SMC_GID_SIZE); |
539 | memcpy(pclc_base->lcl.mac, &ini->ib_dev->mac[ini->ib_port - 1], | |
bc36d2fc | 540 | ETH_ALEN); |
c758dfdd | 541 | } |
d70bf4f7 | 542 | if (smcd_indicated(ini->smc_type_v1)) { |
c758dfdd | 543 | /* add SMC-D specifics */ |
8c3dca34 UB |
544 | if (ini->ism_dev[0]) { |
545 | pclc_smcd->ism.gid = htonll(ini->ism_dev[0]->local_gid); | |
546 | pclc_smcd->ism.chid = | |
547 | htons(smc_ism_get_chid(ini->ism_dev[0])); | |
548 | } | |
549 | } | |
550 | if (ini->smc_type_v2 == SMC_TYPE_N) { | |
551 | pclc_smcd->v2_ext_offset = 0; | |
552 | } else { | |
553 | u16 v2_ext_offset; | |
554 | u8 *eid = NULL; | |
555 | ||
556 | v2_ext_offset = sizeof(*pclc_smcd) - | |
557 | offsetofend(struct smc_clc_msg_smcd, v2_ext_offset); | |
558 | if (ini->smc_type_v1 != SMC_TYPE_N) | |
559 | v2_ext_offset += sizeof(*pclc_prfx) + | |
560 | pclc_prfx->ipv6_prefixes_cnt * | |
561 | sizeof(ipv6_prfx[0]); | |
562 | pclc_smcd->v2_ext_offset = htons(v2_ext_offset); | |
563 | v2_ext->hdr.eid_cnt = 0; | |
564 | v2_ext->hdr.ism_gid_cnt = ini->ism_offered_cnt; | |
565 | v2_ext->hdr.flag.release = SMC_RELEASE; | |
566 | v2_ext->hdr.flag.seid = 1; | |
567 | v2_ext->hdr.smcd_v2_ext_offset = htons(sizeof(*v2_ext) - | |
568 | offsetofend(struct smc_clnt_opts_area_hdr, | |
569 | smcd_v2_ext_offset) + | |
570 | v2_ext->hdr.eid_cnt * SMC_MAX_EID_LEN); | |
571 | if (ini->ism_dev[0]) | |
572 | smc_ism_get_system_eid(ini->ism_dev[0], &eid); | |
573 | else | |
574 | smc_ism_get_system_eid(ini->ism_dev[1], &eid); | |
575 | if (eid) | |
576 | memcpy(smcd_v2_ext->system_eid, eid, SMC_MAX_EID_LEN); | |
577 | plen += sizeof(*v2_ext) + sizeof(*smcd_v2_ext); | |
578 | if (ini->ism_offered_cnt) { | |
579 | for (i = 1; i <= ini->ism_offered_cnt; i++) { | |
580 | gidchids[i - 1].gid = | |
581 | htonll(ini->ism_dev[i]->local_gid); | |
582 | gidchids[i - 1].chid = | |
583 | htons(smc_ism_get_chid(ini->ism_dev[i])); | |
584 | } | |
585 | plen += ini->ism_offered_cnt * | |
586 | sizeof(struct smc_clc_smcd_gid_chid); | |
587 | } | |
c758dfdd | 588 | } |
6bb14e48 | 589 | pclc_base->hdr.length = htons(plen); |
6bb14e48 | 590 | memcpy(trl->eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); |
8c3dca34 UB |
591 | |
592 | /* send SMC Proposal CLC message */ | |
a046d57d | 593 | memset(&msg, 0, sizeof(msg)); |
1a26d020 | 594 | i = 0; |
6bb14e48 UB |
595 | vec[i].iov_base = pclc_base; |
596 | vec[i++].iov_len = sizeof(*pclc_base); | |
8c3dca34 UB |
597 | vec[i].iov_base = pclc_smcd; |
598 | vec[i++].iov_len = sizeof(*pclc_smcd); | |
599 | if (ini->smc_type_v1 != SMC_TYPE_N) { | |
600 | vec[i].iov_base = pclc_prfx; | |
601 | vec[i++].iov_len = sizeof(*pclc_prfx); | |
602 | if (pclc_prfx->ipv6_prefixes_cnt > 0) { | |
603 | vec[i].iov_base = ipv6_prfx; | |
604 | vec[i++].iov_len = pclc_prfx->ipv6_prefixes_cnt * | |
605 | sizeof(ipv6_prfx[0]); | |
606 | } | |
c758dfdd | 607 | } |
8c3dca34 UB |
608 | if (ini->smc_type_v2 != SMC_TYPE_N) { |
609 | vec[i].iov_base = v2_ext; | |
610 | vec[i++].iov_len = sizeof(*v2_ext); | |
611 | vec[i].iov_base = smcd_v2_ext; | |
612 | vec[i++].iov_len = sizeof(*smcd_v2_ext); | |
613 | if (ini->ism_offered_cnt) { | |
614 | vec[i].iov_base = gidchids; | |
615 | vec[i++].iov_len = ini->ism_offered_cnt * | |
616 | sizeof(struct smc_clc_smcd_gid_chid); | |
617 | } | |
1a26d020 | 618 | } |
6bb14e48 UB |
619 | vec[i].iov_base = trl; |
620 | vec[i++].iov_len = sizeof(*trl); | |
a046d57d | 621 | /* due to the few bytes needed for clc-handshake this cannot block */ |
1a26d020 | 622 | len = kernel_sendmsg(smc->clcsock, &msg, vec, i, plen); |
38189779 Y |
623 | if (len < 0) { |
624 | smc->sk.sk_err = smc->clcsock->sk->sk_err; | |
625 | reason_code = -smc->sk.sk_err; | |
6bb14e48 | 626 | } else if (len < ntohs(pclc_base->hdr.length)) { |
38189779 Y |
627 | reason_code = -ENETUNREACH; |
628 | smc->sk.sk_err = -reason_code; | |
a046d57d UB |
629 | } |
630 | ||
6bb14e48 | 631 | kfree(pclc); |
a046d57d UB |
632 | return reason_code; |
633 | } | |
634 | ||
3d9725a6 UB |
635 | /* build and send CLC CONFIRM / ACCEPT message */ |
636 | static int smc_clc_send_confirm_accept(struct smc_sock *smc, | |
a7c9c5f4 UB |
637 | struct smc_clc_msg_accept_confirm_v2 *clc_v2, |
638 | int first_contact, u8 version) | |
a046d57d | 639 | { |
0cfdd8f9 | 640 | struct smc_connection *conn = &smc->conn; |
a7c9c5f4 | 641 | struct smc_clc_msg_accept_confirm *clc; |
b81a5eb7 | 642 | struct smc_clc_first_contact_ext fce; |
e15c6c46 | 643 | struct smc_clc_msg_trail trl; |
b81a5eb7 | 644 | struct kvec vec[3]; |
a046d57d | 645 | struct msghdr msg; |
b81a5eb7 | 646 | int i, len; |
a046d57d UB |
647 | |
648 | /* send SMC Confirm CLC msg */ | |
a7c9c5f4 UB |
649 | clc = (struct smc_clc_msg_accept_confirm *)clc_v2; |
650 | clc->hdr.version = version; /* SMC version */ | |
3d9725a6 | 651 | if (first_contact) |
f1eb02f9 | 652 | clc->hdr.typev2 |= SMC_FIRST_CONTACT_MASK; |
5ac54d87 | 653 | if (conn->lgr->is_smcd) { |
c758dfdd | 654 | /* SMC-D specific settings */ |
3d9725a6 | 655 | memcpy(clc->hdr.eyecatcher, SMCD_EYECATCHER, |
c758dfdd | 656 | sizeof(SMCD_EYECATCHER)); |
f1eb02f9 | 657 | clc->hdr.typev1 = SMC_TYPE_D; |
3d9725a6 UB |
658 | clc->d0.gid = conn->lgr->smcd->local_gid; |
659 | clc->d0.token = conn->rmb_desc->token; | |
660 | clc->d0.dmbe_size = conn->rmbe_size_short; | |
661 | clc->d0.dmbe_idx = 0; | |
662 | memcpy(&clc->d0.linkid, conn->lgr->id, SMC_LGR_ID_SIZE); | |
a7c9c5f4 UB |
663 | if (version == SMC_V1) { |
664 | clc->hdr.length = htons(SMCD_CLC_ACCEPT_CONFIRM_LEN); | |
665 | } else { | |
666 | u8 *eid = NULL; | |
667 | ||
668 | clc_v2->chid = htons(smc_ism_get_chid(conn->lgr->smcd)); | |
669 | smc_ism_get_system_eid(conn->lgr->smcd, &eid); | |
670 | if (eid) | |
671 | memcpy(clc_v2->eid, eid, SMC_MAX_EID_LEN); | |
b81a5eb7 UB |
672 | len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2; |
673 | if (first_contact) | |
674 | smc_clc_fill_fce(&fce, &len); | |
675 | clc_v2->hdr.length = htons(len); | |
a7c9c5f4 | 676 | } |
e15c6c46 | 677 | memcpy(trl.eyecatcher, SMCD_EYECATCHER, |
c758dfdd HW |
678 | sizeof(SMCD_EYECATCHER)); |
679 | } else { | |
3d9725a6 UB |
680 | struct smc_link *link = conn->lnk; |
681 | ||
c758dfdd | 682 | /* SMC-R specific settings */ |
387707fd | 683 | link = conn->lnk; |
3d9725a6 | 684 | memcpy(clc->hdr.eyecatcher, SMC_EYECATCHER, |
c758dfdd | 685 | sizeof(SMC_EYECATCHER)); |
f1eb02f9 | 686 | clc->hdr.typev1 = SMC_TYPE_R; |
3d9725a6 UB |
687 | clc->hdr.length = htons(SMCR_CLC_ACCEPT_CONFIRM_LEN); |
688 | memcpy(clc->r0.lcl.id_for_peer, local_systemid, | |
c758dfdd | 689 | sizeof(local_systemid)); |
3d9725a6 UB |
690 | memcpy(&clc->r0.lcl.gid, link->gid, SMC_GID_SIZE); |
691 | memcpy(&clc->r0.lcl.mac, &link->smcibdev->mac[link->ibport - 1], | |
c758dfdd | 692 | ETH_ALEN); |
3d9725a6 UB |
693 | hton24(clc->r0.qpn, link->roce_qp->qp_num); |
694 | clc->r0.rmb_rkey = | |
387707fd | 695 | htonl(conn->rmb_desc->mr_rx[link->link_idx]->rkey); |
3d9725a6 UB |
696 | clc->r0.rmbe_idx = 1; /* for now: 1 RMB = 1 RMBE */ |
697 | clc->r0.rmbe_alert_token = htonl(conn->alert_token_local); | |
698 | switch (clc->hdr.type) { | |
699 | case SMC_CLC_ACCEPT: | |
700 | clc->r0.qp_mtu = link->path_mtu; | |
701 | break; | |
702 | case SMC_CLC_CONFIRM: | |
703 | clc->r0.qp_mtu = min(link->path_mtu, link->peer_mtu); | |
704 | break; | |
705 | } | |
706 | clc->r0.rmbe_size = conn->rmbe_size_short; | |
707 | clc->r0.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address | |
387707fd | 708 | (conn->rmb_desc->sgt[link->link_idx].sgl)); |
3d9725a6 | 709 | hton24(clc->r0.psn, link->psn_initial); |
e15c6c46 | 710 | memcpy(trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); |
c758dfdd | 711 | } |
a046d57d UB |
712 | |
713 | memset(&msg, 0, sizeof(msg)); | |
e15c6c46 | 714 | i = 0; |
a7c9c5f4 UB |
715 | vec[i].iov_base = clc_v2; |
716 | if (version > SMC_V1) | |
717 | vec[i++].iov_len = SMCD_CLC_ACCEPT_CONFIRM_LEN_V2 - sizeof(trl); | |
718 | else | |
719 | vec[i++].iov_len = (clc->hdr.typev1 == SMC_TYPE_D ? | |
720 | SMCD_CLC_ACCEPT_CONFIRM_LEN : | |
721 | SMCR_CLC_ACCEPT_CONFIRM_LEN) - | |
722 | sizeof(trl); | |
b81a5eb7 UB |
723 | if (version > SMC_V1 && first_contact) { |
724 | vec[i].iov_base = &fce; | |
725 | vec[i++].iov_len = sizeof(fce); | |
726 | } | |
e15c6c46 UB |
727 | vec[i].iov_base = &trl; |
728 | vec[i++].iov_len = sizeof(trl); | |
729 | return kernel_sendmsg(smc->clcsock, &msg, vec, 1, | |
3d9725a6 UB |
730 | ntohs(clc->hdr.length)); |
731 | } | |
732 | ||
733 | /* send CLC CONFIRM message across internal TCP socket */ | |
a7c9c5f4 UB |
734 | int smc_clc_send_confirm(struct smc_sock *smc, bool clnt_first_contact, |
735 | u8 version) | |
3d9725a6 | 736 | { |
a7c9c5f4 | 737 | struct smc_clc_msg_accept_confirm_v2 cclc_v2; |
3d9725a6 UB |
738 | int reason_code = 0; |
739 | int len; | |
740 | ||
741 | /* send SMC Confirm CLC msg */ | |
a7c9c5f4 UB |
742 | memset(&cclc_v2, 0, sizeof(cclc_v2)); |
743 | cclc_v2.hdr.type = SMC_CLC_CONFIRM; | |
744 | len = smc_clc_send_confirm_accept(smc, &cclc_v2, clnt_first_contact, | |
745 | version); | |
746 | if (len < ntohs(cclc_v2.hdr.length)) { | |
a046d57d UB |
747 | if (len >= 0) { |
748 | reason_code = -ENETUNREACH; | |
749 | smc->sk.sk_err = -reason_code; | |
750 | } else { | |
751 | smc->sk.sk_err = smc->clcsock->sk->sk_err; | |
752 | reason_code = -smc->sk.sk_err; | |
753 | } | |
754 | } | |
755 | return reason_code; | |
756 | } | |
757 | ||
758 | /* send CLC ACCEPT message across internal TCP socket */ | |
a7c9c5f4 UB |
759 | int smc_clc_send_accept(struct smc_sock *new_smc, bool srv_first_contact, |
760 | u8 version) | |
a046d57d | 761 | { |
a7c9c5f4 | 762 | struct smc_clc_msg_accept_confirm_v2 aclc_v2; |
a046d57d UB |
763 | int len; |
764 | ||
a7c9c5f4 UB |
765 | memset(&aclc_v2, 0, sizeof(aclc_v2)); |
766 | aclc_v2.hdr.type = SMC_CLC_ACCEPT; | |
767 | len = smc_clc_send_confirm_accept(new_smc, &aclc_v2, srv_first_contact, | |
768 | version); | |
769 | if (len < ntohs(aclc_v2.hdr.length)) | |
6ae36bff | 770 | len = len >= 0 ? -EPROTO : -new_smc->clcsock->sk->sk_err; |
a046d57d | 771 | |
6ae36bff | 772 | return len > 0 ? 0 : len; |
a046d57d | 773 | } |
b81a5eb7 | 774 | |
099b990b GG |
775 | void smc_clc_get_hostname(u8 **host) |
776 | { | |
777 | *host = &smc_hostname[0]; | |
778 | } | |
779 | ||
b81a5eb7 UB |
780 | void __init smc_clc_init(void) |
781 | { | |
782 | struct new_utsname *u; | |
783 | ||
784 | memset(smc_hostname, _S, sizeof(smc_hostname)); /* ASCII blanks */ | |
785 | u = utsname(); | |
786 | memcpy(smc_hostname, u->nodename, | |
787 | min_t(size_t, strlen(u->nodename), sizeof(smc_hostname))); | |
788 | } |