109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ac713874SUrsula Braun /*
3ac713874SUrsula Braun * Shared Memory Communications over RDMA (SMC-R) and RoCE
4ac713874SUrsula Braun *
5ac713874SUrsula Braun * AF_SMC protocol family socket handler keeping the AF_INET sock address type
6ac713874SUrsula Braun * applies to SOCK_STREAM sockets only
7ac713874SUrsula Braun * offers an alternative communication option for TCP-protocol sockets
8ac713874SUrsula Braun * applicable with RoCE-cards only
9ac713874SUrsula Braun *
10a046d57dSUrsula Braun * Initial restrictions:
11a046d57dSUrsula Braun * - support for alternate links postponed
12a046d57dSUrsula Braun *
13aaa4d33fSKarsten Graul * Copyright IBM Corp. 2016, 2018
14ac713874SUrsula Braun *
15ac713874SUrsula Braun * Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com>
16ac713874SUrsula Braun * based on prototype from Frank Blaschka
17ac713874SUrsula Braun */
18ac713874SUrsula Braun
19ac713874SUrsula Braun #define KMSG_COMPONENT "smc"
20ac713874SUrsula Braun #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
21ac713874SUrsula Braun
22ac713874SUrsula Braun #include <linux/module.h>
23ac713874SUrsula Braun #include <linux/socket.h>
24a046d57dSUrsula Braun #include <linux/workqueue.h>
255f08318fSUrsula Braun #include <linux/in.h>
26c3edc401SIngo Molnar #include <linux/sched/signal.h>
2741349844SHans Wippel #include <linux/if_vlan.h>
284ead9c96SUrsula Braun #include <linux/rcupdate_wait.h>
29b81a5eb7SUrsula Braun #include <linux/ctype.h>
30509f15b9SJakub Kicinski #include <linux/splice.h>
31c3edc401SIngo Molnar
32ac713874SUrsula Braun #include <net/sock.h>
33a046d57dSUrsula Braun #include <net/tcp.h>
34f16a7dd5SUrsula Braun #include <net/smc.h>
359b67e26fSUrsula Braun #include <asm/ioctls.h>
36ac713874SUrsula Braun
3764e28b52SHans Wippel #include <net/net_namespace.h>
3864e28b52SHans Wippel #include <net/netns/generic.h>
3964e28b52SHans Wippel #include "smc_netns.h"
4064e28b52SHans Wippel
41ac713874SUrsula Braun #include "smc.h"
42a046d57dSUrsula Braun #include "smc_clc.h"
439bf9abeaSUrsula Braun #include "smc_llc.h"
445f08318fSUrsula Braun #include "smc_cdc.h"
450cfdd8f9SUrsula Braun #include "smc_core.h"
46a4cf0443SUrsula Braun #include "smc_ib.h"
4741349844SHans Wippel #include "smc_ism.h"
486812baabSThomas Richter #include "smc_pnet.h"
49e8372d9dSGuvenc Gulce #include "smc_netlink.h"
50e6727f39SUrsula Braun #include "smc_tx.h"
51952310ccSUrsula Braun #include "smc_rx.h"
52b38d7324SUrsula Braun #include "smc_close.h"
53e0e4b8faSGuvenc Gulce #include "smc_stats.h"
5448262608STony Lu #include "smc_tracepoint.h"
55462791bbSDust Li #include "smc_sysctl.h"
56ac713874SUrsula Braun
5772a36a8aSHans Wippel static DEFINE_MUTEX(smc_server_lgr_pending); /* serialize link group
5872a36a8aSHans Wippel * creation on server
5972a36a8aSHans Wippel */
6072a36a8aSHans Wippel static DEFINE_MUTEX(smc_client_lgr_pending); /* serialize link group
6172a36a8aSHans Wippel * creation on client
620cfdd8f9SUrsula Braun */
630cfdd8f9SUrsula Braun
643079e342SD. Wythe static struct workqueue_struct *smc_tcp_ls_wq; /* wq for tcp listen work */
6522ef473dSKarsten Graul struct workqueue_struct *smc_hs_wq; /* wq for handshake work */
6622ef473dSKarsten Graul struct workqueue_struct *smc_close_wq; /* wq for close work */
6722ef473dSKarsten Graul
68a046d57dSUrsula Braun static void smc_tcp_listen_work(struct work_struct *);
6924ac3a08SUrsula Braun static void smc_connect_work(struct work_struct *);
70a046d57dSUrsula Braun
smc_nl_dump_hs_limitation(struct sk_buff * skb,struct netlink_callback * cb)71f9496b7cSD. Wythe int smc_nl_dump_hs_limitation(struct sk_buff *skb, struct netlink_callback *cb)
72f9496b7cSD. Wythe {
73f9496b7cSD. Wythe struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb);
74f9496b7cSD. Wythe void *hdr;
75f9496b7cSD. Wythe
76f9496b7cSD. Wythe if (cb_ctx->pos[0])
77f9496b7cSD. Wythe goto out;
78f9496b7cSD. Wythe
79f9496b7cSD. Wythe hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
80f9496b7cSD. Wythe &smc_gen_nl_family, NLM_F_MULTI,
81f9496b7cSD. Wythe SMC_NETLINK_DUMP_HS_LIMITATION);
82f9496b7cSD. Wythe if (!hdr)
83f9496b7cSD. Wythe return -ENOMEM;
84f9496b7cSD. Wythe
85f9496b7cSD. Wythe if (nla_put_u8(skb, SMC_NLA_HS_LIMITATION_ENABLED,
86f9496b7cSD. Wythe sock_net(skb->sk)->smc.limit_smc_hs))
87f9496b7cSD. Wythe goto err;
88f9496b7cSD. Wythe
89f9496b7cSD. Wythe genlmsg_end(skb, hdr);
90f9496b7cSD. Wythe cb_ctx->pos[0] = 1;
91f9496b7cSD. Wythe out:
92f9496b7cSD. Wythe return skb->len;
93f9496b7cSD. Wythe err:
94f9496b7cSD. Wythe genlmsg_cancel(skb, hdr);
95f9496b7cSD. Wythe return -EMSGSIZE;
96f9496b7cSD. Wythe }
97f9496b7cSD. Wythe
smc_nl_enable_hs_limitation(struct sk_buff * skb,struct genl_info * info)98f9496b7cSD. Wythe int smc_nl_enable_hs_limitation(struct sk_buff *skb, struct genl_info *info)
99f9496b7cSD. Wythe {
100f9496b7cSD. Wythe sock_net(skb->sk)->smc.limit_smc_hs = true;
101f9496b7cSD. Wythe return 0;
102f9496b7cSD. Wythe }
103f9496b7cSD. Wythe
smc_nl_disable_hs_limitation(struct sk_buff * skb,struct genl_info * info)104f9496b7cSD. Wythe int smc_nl_disable_hs_limitation(struct sk_buff *skb, struct genl_info *info)
105f9496b7cSD. Wythe {
106f9496b7cSD. Wythe sock_net(skb->sk)->smc.limit_smc_hs = false;
107f9496b7cSD. Wythe return 0;
108f9496b7cSD. Wythe }
109f9496b7cSD. Wythe
smc_set_keepalive(struct sock * sk,int val)110ac713874SUrsula Braun static void smc_set_keepalive(struct sock *sk, int val)
111ac713874SUrsula Braun {
112ac713874SUrsula Braun struct smc_sock *smc = smc_sk(sk);
113ac713874SUrsula Braun
114ac713874SUrsula Braun smc->clcsock->sk->sk_prot->keepalive(smc->clcsock->sk, val);
115ac713874SUrsula Braun }
116ac713874SUrsula Braun
smc_tcp_syn_recv_sock(const struct sock * sk,struct sk_buff * skb,struct request_sock * req,struct dst_entry * dst,struct request_sock * req_unhash,bool * own_req)1178270d9c2SD. Wythe static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
1188270d9c2SD. Wythe struct sk_buff *skb,
1198270d9c2SD. Wythe struct request_sock *req,
1208270d9c2SD. Wythe struct dst_entry *dst,
1218270d9c2SD. Wythe struct request_sock *req_unhash,
1228270d9c2SD. Wythe bool *own_req)
1238270d9c2SD. Wythe {
1248270d9c2SD. Wythe struct smc_sock *smc;
12549b7d376SKarsten Graul struct sock *child;
1268270d9c2SD. Wythe
1278270d9c2SD. Wythe smc = smc_clcsock_user_data(sk);
1288270d9c2SD. Wythe
1298270d9c2SD. Wythe if (READ_ONCE(sk->sk_ack_backlog) + atomic_read(&smc->queued_smc_hs) >
1308270d9c2SD. Wythe sk->sk_max_ack_backlog)
1318270d9c2SD. Wythe goto drop;
1328270d9c2SD. Wythe
1338270d9c2SD. Wythe if (sk_acceptq_is_full(&smc->sk)) {
1348270d9c2SD. Wythe NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
1358270d9c2SD. Wythe goto drop;
1368270d9c2SD. Wythe }
1378270d9c2SD. Wythe
1388270d9c2SD. Wythe /* passthrough to original syn recv sock fct */
13949b7d376SKarsten Graul child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash,
1408270d9c2SD. Wythe own_req);
14149b7d376SKarsten Graul /* child must not inherit smc or its ops */
14249b7d376SKarsten Graul if (child) {
14349b7d376SKarsten Graul rcu_assign_sk_user_data(child, NULL);
14449b7d376SKarsten Graul
14549b7d376SKarsten Graul /* v4-mapped sockets don't inherit parent ops. Don't restore. */
14649b7d376SKarsten Graul if (inet_csk(child)->icsk_af_ops == inet_csk(sk)->icsk_af_ops)
14749b7d376SKarsten Graul inet_csk(child)->icsk_af_ops = smc->ori_af_ops;
14849b7d376SKarsten Graul }
14949b7d376SKarsten Graul return child;
1508270d9c2SD. Wythe
1518270d9c2SD. Wythe drop:
1528270d9c2SD. Wythe dst_release(dst);
1538270d9c2SD. Wythe tcp_listendrop(sk);
1548270d9c2SD. Wythe return NULL;
1558270d9c2SD. Wythe }
1568270d9c2SD. Wythe
smc_hs_congested(const struct sock * sk)15748b6190aSD. Wythe static bool smc_hs_congested(const struct sock *sk)
15848b6190aSD. Wythe {
15948b6190aSD. Wythe const struct smc_sock *smc;
16048b6190aSD. Wythe
16148b6190aSD. Wythe smc = smc_clcsock_user_data(sk);
16248b6190aSD. Wythe
16348b6190aSD. Wythe if (!smc)
16448b6190aSD. Wythe return true;
16548b6190aSD. Wythe
16648b6190aSD. Wythe if (workqueue_congested(WORK_CPU_UNBOUND, smc_hs_wq))
16748b6190aSD. Wythe return true;
16848b6190aSD. Wythe
16948b6190aSD. Wythe return false;
17048b6190aSD. Wythe }
17148b6190aSD. Wythe
172f16a7dd5SUrsula Braun static struct smc_hashinfo smc_v4_hashinfo = {
173f16a7dd5SUrsula Braun .lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock),
174f16a7dd5SUrsula Braun };
175f16a7dd5SUrsula Braun
176aaa4d33fSKarsten Graul static struct smc_hashinfo smc_v6_hashinfo = {
177aaa4d33fSKarsten Graul .lock = __RW_LOCK_UNLOCKED(smc_v6_hashinfo.lock),
178aaa4d33fSKarsten Graul };
179aaa4d33fSKarsten Graul
smc_hash_sk(struct sock * sk)180f16a7dd5SUrsula Braun int smc_hash_sk(struct sock *sk)
181f16a7dd5SUrsula Braun {
182f16a7dd5SUrsula Braun struct smc_hashinfo *h = sk->sk_prot->h.smc_hash;
183f16a7dd5SUrsula Braun struct hlist_head *head;
184f16a7dd5SUrsula Braun
185f16a7dd5SUrsula Braun head = &h->ht;
186f16a7dd5SUrsula Braun
187f16a7dd5SUrsula Braun write_lock_bh(&h->lock);
188f16a7dd5SUrsula Braun sk_add_node(sk, head);
189f16a7dd5SUrsula Braun write_unlock_bh(&h->lock);
190b3cb764aSEric Dumazet sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
191f16a7dd5SUrsula Braun
192f16a7dd5SUrsula Braun return 0;
193f16a7dd5SUrsula Braun }
194f16a7dd5SUrsula Braun EXPORT_SYMBOL_GPL(smc_hash_sk);
195f16a7dd5SUrsula Braun
smc_unhash_sk(struct sock * sk)196f16a7dd5SUrsula Braun void smc_unhash_sk(struct sock *sk)
197f16a7dd5SUrsula Braun {
198f16a7dd5SUrsula Braun struct smc_hashinfo *h = sk->sk_prot->h.smc_hash;
199f16a7dd5SUrsula Braun
200f16a7dd5SUrsula Braun write_lock_bh(&h->lock);
201f16a7dd5SUrsula Braun if (sk_del_node_init(sk))
202f16a7dd5SUrsula Braun sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
203f16a7dd5SUrsula Braun write_unlock_bh(&h->lock);
204f16a7dd5SUrsula Braun }
205f16a7dd5SUrsula Braun EXPORT_SYMBOL_GPL(smc_unhash_sk);
206f16a7dd5SUrsula Braun
2076b88af83SDust Li /* This will be called before user really release sock_lock. So do the
2086b88af83SDust Li * work which we didn't do because of user hold the sock_lock in the
2096b88af83SDust Li * BH context
2106b88af83SDust Li */
smc_release_cb(struct sock * sk)2116b88af83SDust Li static void smc_release_cb(struct sock *sk)
2126b88af83SDust Li {
2136b88af83SDust Li struct smc_sock *smc = smc_sk(sk);
2146b88af83SDust Li
2156b88af83SDust Li if (smc->conn.tx_in_release_sock) {
2166b88af83SDust Li smc_tx_pending(&smc->conn);
2176b88af83SDust Li smc->conn.tx_in_release_sock = false;
2186b88af83SDust Li }
2196b88af83SDust Li }
2206b88af83SDust Li
221f16a7dd5SUrsula Braun struct proto smc_proto = {
222ac713874SUrsula Braun .name = "SMC",
223ac713874SUrsula Braun .owner = THIS_MODULE,
224ac713874SUrsula Braun .keepalive = smc_set_keepalive,
225f16a7dd5SUrsula Braun .hash = smc_hash_sk,
226f16a7dd5SUrsula Braun .unhash = smc_unhash_sk,
2276b88af83SDust Li .release_cb = smc_release_cb,
228ac713874SUrsula Braun .obj_size = sizeof(struct smc_sock),
229f16a7dd5SUrsula Braun .h.smc_hash = &smc_v4_hashinfo,
2305f0d5a3aSPaul E. McKenney .slab_flags = SLAB_TYPESAFE_BY_RCU,
231ac713874SUrsula Braun };
232f16a7dd5SUrsula Braun EXPORT_SYMBOL_GPL(smc_proto);
233ac713874SUrsula Braun
234aaa4d33fSKarsten Graul struct proto smc_proto6 = {
235aaa4d33fSKarsten Graul .name = "SMC6",
236aaa4d33fSKarsten Graul .owner = THIS_MODULE,
237aaa4d33fSKarsten Graul .keepalive = smc_set_keepalive,
238aaa4d33fSKarsten Graul .hash = smc_hash_sk,
239aaa4d33fSKarsten Graul .unhash = smc_unhash_sk,
2406b88af83SDust Li .release_cb = smc_release_cb,
241aaa4d33fSKarsten Graul .obj_size = sizeof(struct smc_sock),
242aaa4d33fSKarsten Graul .h.smc_hash = &smc_v6_hashinfo,
243aaa4d33fSKarsten Graul .slab_flags = SLAB_TYPESAFE_BY_RCU,
244aaa4d33fSKarsten Graul };
245aaa4d33fSKarsten Graul EXPORT_SYMBOL_GPL(smc_proto6);
246aaa4d33fSKarsten Graul
smc_fback_restore_callbacks(struct smc_sock * smc)2470558226cSWen Gu static void smc_fback_restore_callbacks(struct smc_sock *smc)
2480558226cSWen Gu {
2490558226cSWen Gu struct sock *clcsk = smc->clcsock->sk;
2500558226cSWen Gu
2510558226cSWen Gu write_lock_bh(&clcsk->sk_callback_lock);
2520558226cSWen Gu clcsk->sk_user_data = NULL;
2530558226cSWen Gu
2540558226cSWen Gu smc_clcsock_restore_cb(&clcsk->sk_state_change, &smc->clcsk_state_change);
2550558226cSWen Gu smc_clcsock_restore_cb(&clcsk->sk_data_ready, &smc->clcsk_data_ready);
2560558226cSWen Gu smc_clcsock_restore_cb(&clcsk->sk_write_space, &smc->clcsk_write_space);
2570558226cSWen Gu smc_clcsock_restore_cb(&clcsk->sk_error_report, &smc->clcsk_error_report);
2580558226cSWen Gu
2590558226cSWen Gu write_unlock_bh(&clcsk->sk_callback_lock);
2600558226cSWen Gu }
2610558226cSWen Gu
smc_restore_fallback_changes(struct smc_sock * smc)262f536dffcSUrsula Braun static void smc_restore_fallback_changes(struct smc_sock *smc)
263f536dffcSUrsula Braun {
2641ad24058SKarsten Graul if (smc->clcsock->file) { /* non-accepted sockets have no file yet */
265f536dffcSUrsula Braun smc->clcsock->file->private_data = smc->sk.sk_socket;
266f536dffcSUrsula Braun smc->clcsock->file = NULL;
2670558226cSWen Gu smc_fback_restore_callbacks(smc);
268f536dffcSUrsula Braun }
2691ad24058SKarsten Graul }
270f536dffcSUrsula Braun
__smc_release(struct smc_sock * smc)27139f41f36SUrsula Braun static int __smc_release(struct smc_sock *smc)
272ac713874SUrsula Braun {
27339f41f36SUrsula Braun struct sock *sk = &smc->sk;
274b38d7324SUrsula Braun int rc = 0;
275ac713874SUrsula Braun
27651f1de79SUrsula Braun if (!smc->use_fallback) {
277b38d7324SUrsula Braun rc = smc_close_active(smc);
278afc6a04fSD. Wythe smc_sock_set_flag(sk, SOCK_DEAD);
279b38d7324SUrsula Braun sk->sk_shutdown |= SHUTDOWN_MASK;
280b03faa1fSUrsula Braun } else {
281e5d5aadcSDust Li if (sk->sk_state != SMC_CLOSED) {
282e5d5aadcSDust Li if (sk->sk_state != SMC_LISTEN &&
283e5d5aadcSDust Li sk->sk_state != SMC_INIT)
284b03faa1fSUrsula Braun sock_put(sk); /* passive closing */
285b03faa1fSUrsula Braun if (sk->sk_state == SMC_LISTEN) {
286b03faa1fSUrsula Braun /* wake up clcsock accept */
287e5d5aadcSDust Li rc = kernel_sock_shutdown(smc->clcsock,
288e5d5aadcSDust Li SHUT_RDWR);
289b03faa1fSUrsula Braun }
290b03faa1fSUrsula Braun sk->sk_state = SMC_CLOSED;
291b03faa1fSUrsula Braun sk->sk_state_change(sk);
292e5d5aadcSDust Li }
293f536dffcSUrsula Braun smc_restore_fallback_changes(smc);
294b38d7324SUrsula Braun }
29526d92e95SCong Wang
29626d92e95SCong Wang sk->sk_prot->unhash(sk);
29726d92e95SCong Wang
298b03faa1fSUrsula Braun if (sk->sk_state == SMC_CLOSED) {
299ac713874SUrsula Braun if (smc->clcsock) {
300fd57770dSKarsten Graul release_sock(sk);
301fd57770dSKarsten Graul smc_clcsock_release(smc);
302fd57770dSKarsten Graul lock_sock(sk);
303ac713874SUrsula Braun }
304b03faa1fSUrsula Braun if (!smc->use_fallback)
305b03faa1fSUrsula Braun smc_conn_free(&smc->conn);
30651f1de79SUrsula Braun }
307ac713874SUrsula Braun
30839f41f36SUrsula Braun return rc;
30939f41f36SUrsula Braun }
31039f41f36SUrsula Braun
smc_release(struct socket * sock)31139f41f36SUrsula Braun static int smc_release(struct socket *sock)
31239f41f36SUrsula Braun {
31339f41f36SUrsula Braun struct sock *sk = sock->sk;
31439f41f36SUrsula Braun struct smc_sock *smc;
3159f1c50cfSD. Wythe int old_state, rc = 0;
31639f41f36SUrsula Braun
31739f41f36SUrsula Braun if (!sk)
31839f41f36SUrsula Braun goto out;
31939f41f36SUrsula Braun
32081cf4f47SUrsula Braun sock_hold(sk); /* sock_put below */
32139f41f36SUrsula Braun smc = smc_sk(sk);
32239f41f36SUrsula Braun
3239f1c50cfSD. Wythe old_state = sk->sk_state;
3249f1c50cfSD. Wythe
32539f41f36SUrsula Braun /* cleanup for a dangling non-blocking connect */
3269f1c50cfSD. Wythe if (smc->connect_nonblock && old_state == SMC_INIT)
32739f41f36SUrsula Braun tcp_abort(smc->clcsock->sk, ECONNABORTED);
3285c15b312SD. Wythe
3295c15b312SD. Wythe if (cancel_work_sync(&smc->connect_work))
3305c15b312SD. Wythe sock_put(&smc->sk); /* sock_hold in smc_connect for passive closing */
33139f41f36SUrsula Braun
33239f41f36SUrsula Braun if (sk->sk_state == SMC_LISTEN)
33339f41f36SUrsula Braun /* smc_close_non_accepted() is called and acquires
33439f41f36SUrsula Braun * sock lock for child sockets again
33539f41f36SUrsula Braun */
33639f41f36SUrsula Braun lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
33739f41f36SUrsula Braun else
33839f41f36SUrsula Braun lock_sock(sk);
33939f41f36SUrsula Braun
3409f1c50cfSD. Wythe if (old_state == SMC_INIT && sk->sk_state == SMC_ACTIVE &&
3419f1c50cfSD. Wythe !smc->use_fallback)
3429f1c50cfSD. Wythe smc_close_active_abort(smc);
3439f1c50cfSD. Wythe
34439f41f36SUrsula Braun rc = __smc_release(smc);
34539f41f36SUrsula Braun
346ac713874SUrsula Braun /* detach socket */
347ac713874SUrsula Braun sock_orphan(sk);
348ac713874SUrsula Braun sock->sk = NULL;
349ac713874SUrsula Braun release_sock(sk);
350ac713874SUrsula Braun
35181cf4f47SUrsula Braun sock_put(sk); /* sock_hold above */
35251f1de79SUrsula Braun sock_put(sk); /* final sock_put */
353ac713874SUrsula Braun out:
354b38d7324SUrsula Braun return rc;
355ac713874SUrsula Braun }
356ac713874SUrsula Braun
smc_destruct(struct sock * sk)357ac713874SUrsula Braun static void smc_destruct(struct sock *sk)
358ac713874SUrsula Braun {
359ac713874SUrsula Braun if (sk->sk_state != SMC_CLOSED)
360ac713874SUrsula Braun return;
361ac713874SUrsula Braun if (!sock_flag(sk, SOCK_DEAD))
362ac713874SUrsula Braun return;
363ac713874SUrsula Braun }
364ac713874SUrsula Braun
smc_sk_init(struct net * net,struct sock * sk,int protocol)365f59b799eSD. Wythe void smc_sk_init(struct net *net, struct sock *sk, int protocol)
366ac713874SUrsula Braun {
367f59b799eSD. Wythe struct smc_sock *smc = smc_sk(sk);
368ac713874SUrsula Braun
369ac713874SUrsula Braun sk->sk_state = SMC_INIT;
370ac713874SUrsula Braun sk->sk_destruct = smc_destruct;
371aaa4d33fSKarsten Graul sk->sk_protocol = protocol;
372833bac7eSGerd Bayer WRITE_ONCE(sk->sk_sndbuf, 2 * READ_ONCE(net->smc.sysctl_wmem));
373833bac7eSGerd Bayer WRITE_ONCE(sk->sk_rcvbuf, 2 * READ_ONCE(net->smc.sysctl_rmem));
374a046d57dSUrsula Braun INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work);
37524ac3a08SUrsula Braun INIT_WORK(&smc->connect_work, smc_connect_work);
376be7f3e59SEric Dumazet INIT_DELAYED_WORK(&smc->conn.tx_work, smc_tx_work);
377a046d57dSUrsula Braun INIT_LIST_HEAD(&smc->accept_q);
378a046d57dSUrsula Braun spin_lock_init(&smc->accept_q_lock);
379be7f3e59SEric Dumazet spin_lock_init(&smc->conn.send_lock);
380f16a7dd5SUrsula Braun sk->sk_prot->hash(sk);
38178abe3d0SMyungho Jung mutex_init(&smc->clcsock_release_lock);
38297b9af7aSWen Gu smc_init_saved_callbacks(smc);
383f59b799eSD. Wythe smc->limit_smc_hs = net->smc.limit_smc_hs;
384f59b799eSD. Wythe smc->use_fallback = false; /* assume rdma capability first */
385f59b799eSD. Wythe smc->fallback_rsn = 0;
386f0c37002SWen Gu smc_close_init(smc);
387f59b799eSD. Wythe }
388f59b799eSD. Wythe
smc_sock_alloc(struct net * net,struct socket * sock,int protocol)389f59b799eSD. Wythe static struct sock *smc_sock_alloc(struct net *net, struct socket *sock,
390f59b799eSD. Wythe int protocol)
391f59b799eSD. Wythe {
392f59b799eSD. Wythe struct proto *prot;
393f59b799eSD. Wythe struct sock *sk;
394f59b799eSD. Wythe
395f59b799eSD. Wythe prot = (protocol == SMCPROTO_SMC6) ? &smc_proto6 : &smc_proto;
396f59b799eSD. Wythe sk = sk_alloc(net, PF_SMC, GFP_KERNEL, prot, 0);
397f59b799eSD. Wythe if (!sk)
398f59b799eSD. Wythe return NULL;
399f59b799eSD. Wythe
400f59b799eSD. Wythe sock_init_data(sock, sk); /* sets sk_refcnt to 1 */
401f59b799eSD. Wythe smc_sk_init(net, sk, protocol);
402ac713874SUrsula Braun
403ac713874SUrsula Braun return sk;
404ac713874SUrsula Braun }
405ac713874SUrsula Braun
smc_bind(struct socket * sock,struct sockaddr * uaddr,int addr_len)406ac713874SUrsula Braun static int smc_bind(struct socket *sock, struct sockaddr *uaddr,
407ac713874SUrsula Braun int addr_len)
408ac713874SUrsula Braun {
409ac713874SUrsula Braun struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
410ac713874SUrsula Braun struct sock *sk = sock->sk;
411ac713874SUrsula Braun struct smc_sock *smc;
412ac713874SUrsula Braun int rc;
413ac713874SUrsula Braun
414ac713874SUrsula Braun smc = smc_sk(sk);
415ac713874SUrsula Braun
416ac713874SUrsula Braun /* replicate tests from inet_bind(), to be safe wrt. future changes */
417ac713874SUrsula Braun rc = -EINVAL;
418ac713874SUrsula Braun if (addr_len < sizeof(struct sockaddr_in))
419ac713874SUrsula Braun goto out;
420ac713874SUrsula Braun
421ac713874SUrsula Braun rc = -EAFNOSUPPORT;
422aaa4d33fSKarsten Graul if (addr->sin_family != AF_INET &&
423aaa4d33fSKarsten Graul addr->sin_family != AF_INET6 &&
424aaa4d33fSKarsten Graul addr->sin_family != AF_UNSPEC)
425aaa4d33fSKarsten Graul goto out;
426ac713874SUrsula Braun /* accept AF_UNSPEC (mapped to AF_INET) only if s_addr is INADDR_ANY */
427aaa4d33fSKarsten Graul if (addr->sin_family == AF_UNSPEC &&
428aaa4d33fSKarsten Graul addr->sin_addr.s_addr != htonl(INADDR_ANY))
429ac713874SUrsula Braun goto out;
430ac713874SUrsula Braun
431ac713874SUrsula Braun lock_sock(sk);
432ac713874SUrsula Braun
433ac713874SUrsula Braun /* Check if socket is already active */
434ac713874SUrsula Braun rc = -EINVAL;
435cd206360SUrsula Braun if (sk->sk_state != SMC_INIT || smc->connect_nonblock)
436ac713874SUrsula Braun goto out_rel;
437ac713874SUrsula Braun
438ac713874SUrsula Braun smc->clcsock->sk->sk_reuse = sk->sk_reuse;
4396627a207STony Lu smc->clcsock->sk->sk_reuseport = sk->sk_reuseport;
440ac713874SUrsula Braun rc = kernel_bind(smc->clcsock, uaddr, addr_len);
441ac713874SUrsula Braun
442ac713874SUrsula Braun out_rel:
443ac713874SUrsula Braun release_sock(sk);
444ac713874SUrsula Braun out:
445ac713874SUrsula Braun return rc;
446ac713874SUrsula Braun }
447ac713874SUrsula Braun
44830c3c4a4SGerd Bayer /* copy only relevant settings and flags of SOL_SOCKET level from smc to
44930c3c4a4SGerd Bayer * clc socket (since smc is not called for these options from net/core)
45030c3c4a4SGerd Bayer */
451ac713874SUrsula Braun
452ac713874SUrsula Braun #define SK_FLAGS_SMC_TO_CLC ((1UL << SOCK_URGINLINE) | \
453ac713874SUrsula Braun (1UL << SOCK_KEEPOPEN) | \
454ac713874SUrsula Braun (1UL << SOCK_LINGER) | \
455ac713874SUrsula Braun (1UL << SOCK_BROADCAST) | \
456ac713874SUrsula Braun (1UL << SOCK_TIMESTAMP) | \
457ac713874SUrsula Braun (1UL << SOCK_DBG) | \
458ac713874SUrsula Braun (1UL << SOCK_RCVTSTAMP) | \
459ac713874SUrsula Braun (1UL << SOCK_RCVTSTAMPNS) | \
460ac713874SUrsula Braun (1UL << SOCK_LOCALROUTE) | \
461ac713874SUrsula Braun (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE) | \
462ac713874SUrsula Braun (1UL << SOCK_RXQ_OVFL) | \
463ac713874SUrsula Braun (1UL << SOCK_WIFI_STATUS) | \
464ac713874SUrsula Braun (1UL << SOCK_NOFCS) | \
4659718475eSDeepa Dinamani (1UL << SOCK_FILTER_LOCKED) | \
4669718475eSDeepa Dinamani (1UL << SOCK_TSTAMP_NEW))
46730c3c4a4SGerd Bayer
46830c3c4a4SGerd Bayer /* if set, use value set by setsockopt() - else use IPv4 or SMC sysctl value */
smc_adjust_sock_bufsizes(struct sock * nsk,struct sock * osk,unsigned long mask)46930c3c4a4SGerd Bayer static void smc_adjust_sock_bufsizes(struct sock *nsk, struct sock *osk,
47030c3c4a4SGerd Bayer unsigned long mask)
47130c3c4a4SGerd Bayer {
47230c3c4a4SGerd Bayer nsk->sk_userlocks = osk->sk_userlocks;
473b0325529SWen Gu if (osk->sk_userlocks & SOCK_SNDBUF_LOCK)
47430c3c4a4SGerd Bayer nsk->sk_sndbuf = osk->sk_sndbuf;
475b0325529SWen Gu if (osk->sk_userlocks & SOCK_RCVBUF_LOCK)
47630c3c4a4SGerd Bayer nsk->sk_rcvbuf = osk->sk_rcvbuf;
47730c3c4a4SGerd Bayer }
47830c3c4a4SGerd Bayer
smc_copy_sock_settings(struct sock * nsk,struct sock * osk,unsigned long mask)47930c3c4a4SGerd Bayer static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk,
48030c3c4a4SGerd Bayer unsigned long mask)
48130c3c4a4SGerd Bayer {
48230c3c4a4SGerd Bayer /* options we don't get control via setsockopt for */
48330c3c4a4SGerd Bayer nsk->sk_type = osk->sk_type;
48430c3c4a4SGerd Bayer nsk->sk_sndtimeo = osk->sk_sndtimeo;
48530c3c4a4SGerd Bayer nsk->sk_rcvtimeo = osk->sk_rcvtimeo;
48630c3c4a4SGerd Bayer nsk->sk_mark = READ_ONCE(osk->sk_mark);
48730c3c4a4SGerd Bayer nsk->sk_priority = osk->sk_priority;
48830c3c4a4SGerd Bayer nsk->sk_rcvlowat = osk->sk_rcvlowat;
48930c3c4a4SGerd Bayer nsk->sk_bound_dev_if = osk->sk_bound_dev_if;
49030c3c4a4SGerd Bayer nsk->sk_err = osk->sk_err;
49130c3c4a4SGerd Bayer
49230c3c4a4SGerd Bayer nsk->sk_flags &= ~mask;
49330c3c4a4SGerd Bayer nsk->sk_flags |= osk->sk_flags & mask;
49430c3c4a4SGerd Bayer
49530c3c4a4SGerd Bayer smc_adjust_sock_bufsizes(nsk, osk, mask);
49630c3c4a4SGerd Bayer }
49730c3c4a4SGerd Bayer
smc_copy_sock_settings_to_clc(struct smc_sock * smc)498ac713874SUrsula Braun static void smc_copy_sock_settings_to_clc(struct smc_sock *smc)
499ac713874SUrsula Braun {
500ac713874SUrsula Braun smc_copy_sock_settings(smc->clcsock->sk, &smc->sk, SK_FLAGS_SMC_TO_CLC);
501ac713874SUrsula Braun }
502ac713874SUrsula Braun
503ac713874SUrsula Braun #define SK_FLAGS_CLC_TO_SMC ((1UL << SOCK_URGINLINE) | \
504ac713874SUrsula Braun (1UL << SOCK_KEEPOPEN) | \
505ac713874SUrsula Braun (1UL << SOCK_LINGER) | \
506ac713874SUrsula Braun (1UL << SOCK_DBG))
507ac713874SUrsula Braun /* copy only settings and flags relevant for smc from clc to smc socket */
smc_copy_sock_settings_to_smc(struct smc_sock * smc)508ac713874SUrsula Braun static void smc_copy_sock_settings_to_smc(struct smc_sock *smc)
509ac713874SUrsula Braun {
510ac713874SUrsula Braun smc_copy_sock_settings(&smc->sk, smc->clcsock->sk, SK_FLAGS_CLC_TO_SMC);
511ac713874SUrsula Braun }
512ac713874SUrsula Braun
513b8d19945SWen Gu /* register the new vzalloced sndbuf on all links */
smcr_lgr_reg_sndbufs(struct smc_link * link,struct smc_buf_desc * snd_desc)514b8d19945SWen Gu static int smcr_lgr_reg_sndbufs(struct smc_link *link,
515b8d19945SWen Gu struct smc_buf_desc *snd_desc)
516b8d19945SWen Gu {
517b8d19945SWen Gu struct smc_link_group *lgr = link->lgr;
518b8d19945SWen Gu int i, rc = 0;
519b8d19945SWen Gu
520b8d19945SWen Gu if (!snd_desc->is_vm)
521b8d19945SWen Gu return -EINVAL;
522b8d19945SWen Gu
523b8d19945SWen Gu /* protect against parallel smcr_link_reg_buf() */
524b5dd4d69SD. Wythe down_write(&lgr->llc_conf_mutex);
525b8d19945SWen Gu for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
526b8d19945SWen Gu if (!smc_link_active(&lgr->lnk[i]))
527b8d19945SWen Gu continue;
528b8d19945SWen Gu rc = smcr_link_reg_buf(&lgr->lnk[i], snd_desc);
529b8d19945SWen Gu if (rc)
530b8d19945SWen Gu break;
531b8d19945SWen Gu }
532b5dd4d69SD. Wythe up_write(&lgr->llc_conf_mutex);
533b8d19945SWen Gu return rc;
534b8d19945SWen Gu }
535b8d19945SWen Gu
536b9247544SKarsten Graul /* register the new rmb on all links */
smcr_lgr_reg_rmbs(struct smc_link * link,struct smc_buf_desc * rmb_desc)5377562a13dSKarsten Graul static int smcr_lgr_reg_rmbs(struct smc_link *link,
538b9247544SKarsten Graul struct smc_buf_desc *rmb_desc)
539b9247544SKarsten Graul {
5407562a13dSKarsten Graul struct smc_link_group *lgr = link->lgr;
5414da68744SD. Wythe bool do_slow = false;
5427562a13dSKarsten Graul int i, rc = 0;
543b9247544SKarsten Graul
544d5500667SKarsten Graul rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
545d5500667SKarsten Graul if (rc)
546d5500667SKarsten Graul return rc;
5474da68744SD. Wythe
5484da68744SD. Wythe down_read(&lgr->llc_conf_mutex);
5494da68744SD. Wythe for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
5504da68744SD. Wythe if (!smc_link_active(&lgr->lnk[i]))
5514da68744SD. Wythe continue;
5524da68744SD. Wythe if (!rmb_desc->is_reg_mr[link->link_idx]) {
5534da68744SD. Wythe up_read(&lgr->llc_conf_mutex);
5544da68744SD. Wythe goto slow_path;
5554da68744SD. Wythe }
5564da68744SD. Wythe }
5574da68744SD. Wythe /* mr register already */
5584da68744SD. Wythe goto fast_path;
5594da68744SD. Wythe slow_path:
5604da68744SD. Wythe do_slow = true;
561d5500667SKarsten Graul /* protect against parallel smc_llc_cli_rkey_exchange() and
562b8d19945SWen Gu * parallel smcr_link_reg_buf()
563d5500667SKarsten Graul */
564b5dd4d69SD. Wythe down_write(&lgr->llc_conf_mutex);
565b9247544SKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
566741a49a4SKarsten Graul if (!smc_link_active(&lgr->lnk[i]))
567b9247544SKarsten Graul continue;
568b8d19945SWen Gu rc = smcr_link_reg_buf(&lgr->lnk[i], rmb_desc);
569b9247544SKarsten Graul if (rc)
5707562a13dSKarsten Graul goto out;
571b9247544SKarsten Graul }
5724da68744SD. Wythe fast_path:
5737562a13dSKarsten Graul /* exchange confirm_rkey msg with peer */
5747562a13dSKarsten Graul rc = smc_llc_do_confirm_rkey(link, rmb_desc);
5757562a13dSKarsten Graul if (rc) {
5767562a13dSKarsten Graul rc = -EFAULT;
5777562a13dSKarsten Graul goto out;
5787562a13dSKarsten Graul }
5797562a13dSKarsten Graul rmb_desc->is_conf_rkey = true;
5807562a13dSKarsten Graul out:
5814da68744SD. Wythe do_slow ? up_write(&lgr->llc_conf_mutex) : up_read(&lgr->llc_conf_mutex);
582d5500667SKarsten Graul smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
5837562a13dSKarsten Graul return rc;
584b9247544SKarsten Graul }
585b9247544SKarsten Graul
smcr_clnt_conf_first_link(struct smc_sock * smc)586b9247544SKarsten Graul static int smcr_clnt_conf_first_link(struct smc_sock *smc)
5879bf9abeaSUrsula Braun {
588387707fdSKarsten Graul struct smc_link *link = smc->conn.lnk;
5890fb0b02bSKarsten Graul struct smc_llc_qentry *qentry;
5909bf9abeaSUrsula Braun int rc;
5919bf9abeaSUrsula Braun
5927234d2b5SD. Wythe /* Receive CONFIRM LINK request from server over RoCE fabric.
5937234d2b5SD. Wythe * Increasing the client's timeout by twice as much as the server's
5947234d2b5SD. Wythe * timeout by default can temporarily avoid decline messages of
5957234d2b5SD. Wythe * both sides crossing or colliding
5967234d2b5SD. Wythe */
5977234d2b5SD. Wythe qentry = smc_llc_wait(link->lgr, NULL, 2 * SMC_LLC_WAIT_TIME,
5980fb0b02bSKarsten Graul SMC_LLC_CONFIRM_LINK);
5990fb0b02bSKarsten Graul if (!qentry) {
6009bf9abeaSUrsula Braun struct smc_clc_msg_decline dclc;
6019bf9abeaSUrsula Braun
6029bf9abeaSUrsula Braun rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
6032b59f58eSUrsula Braun SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
6049ed28556SUrsula Braun return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
6059bf9abeaSUrsula Braun }
606649758ffSKarsten Graul smc_llc_save_peer_uid(qentry);
6070fb0b02bSKarsten Graul rc = smc_llc_eval_conf_link(qentry, SMC_LLC_REQ);
6080fb0b02bSKarsten Graul smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl);
6090fb0b02bSKarsten Graul if (rc)
61075d320d6SKarsten Graul return SMC_CLC_DECL_RMBE_EC;
61175d320d6SKarsten Graul
6129bf9abeaSUrsula Braun rc = smc_ib_modify_qp_rts(link);
6139bf9abeaSUrsula Braun if (rc)
614603cc149SKarsten Graul return SMC_CLC_DECL_ERR_RDYLNK;
6159bf9abeaSUrsula Braun
6169bf9abeaSUrsula Braun smc_wr_remember_qp_attr(link);
617652a1e41SUrsula Braun
618b8d19945SWen Gu /* reg the sndbuf if it was vzalloced */
619b8d19945SWen Gu if (smc->conn.sndbuf_desc->is_vm) {
620b8d19945SWen Gu if (smcr_link_reg_buf(link, smc->conn.sndbuf_desc))
621b8d19945SWen Gu return SMC_CLC_DECL_ERR_REGBUF;
622b8d19945SWen Gu }
623b8d19945SWen Gu
624b8d19945SWen Gu /* reg the rmb */
625b8d19945SWen Gu if (smcr_link_reg_buf(link, smc->conn.rmb_desc))
626b8d19945SWen Gu return SMC_CLC_DECL_ERR_REGBUF;
627652a1e41SUrsula Braun
6280fb0b02bSKarsten Graul /* confirm_rkey is implicit on 1st contact */
6290fb0b02bSKarsten Graul smc->conn.rmb_desc->is_conf_rkey = true;
6300fb0b02bSKarsten Graul
6319bf9abeaSUrsula Braun /* send CONFIRM LINK response over RoCE fabric */
632947541f3SUrsula Braun rc = smc_llc_send_confirm_link(link, SMC_LLC_RESP);
6339bf9abeaSUrsula Braun if (rc < 0)
634603cc149SKarsten Graul return SMC_CLC_DECL_TIMEOUT_CL;
6359bf9abeaSUrsula Braun
6360fb0b02bSKarsten Graul smc_llc_link_active(link);
6370a99be43SKarsten Graul smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE);
6380fb0b02bSKarsten Graul
63969b888e3SGuangguan Wang if (link->lgr->max_links > 1) {
6400fb0b02bSKarsten Graul /* optional 2nd link, receive ADD LINK request from server */
6410fb0b02bSKarsten Graul qentry = smc_llc_wait(link->lgr, NULL, SMC_LLC_WAIT_TIME,
6420fb0b02bSKarsten Graul SMC_LLC_ADD_LINK);
6430fb0b02bSKarsten Graul if (!qentry) {
64452bedf37SKarsten Graul struct smc_clc_msg_decline dclc;
64552bedf37SKarsten Graul
64652bedf37SKarsten Graul rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
6472b59f58eSUrsula Braun SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
6480fb0b02bSKarsten Graul if (rc == -EAGAIN)
6490fb0b02bSKarsten Graul rc = 0; /* no DECLINE received, go with one link */
6500fb0b02bSKarsten Graul return rc;
65152bedf37SKarsten Graul }
6520fb0b02bSKarsten Graul smc_llc_flow_qentry_clr(&link->lgr->llc_flow_lcl);
653b1570a87SKarsten Graul smc_llc_cli_add_link(link, qentry);
65469b888e3SGuangguan Wang }
65575d320d6SKarsten Graul return 0;
6569bf9abeaSUrsula Braun }
6579bf9abeaSUrsula Braun
smc_isascii(char * hostname)658e5c4744cSKarsten Graul static bool smc_isascii(char *hostname)
659e5c4744cSKarsten Graul {
660e5c4744cSKarsten Graul int i;
661e5c4744cSKarsten Graul
662e5c4744cSKarsten Graul for (i = 0; i < SMC_MAX_HOSTNAME_LEN; i++)
663e5c4744cSKarsten Graul if (!isascii(hostname[i]))
664e5c4744cSKarsten Graul return false;
665e5c4744cSKarsten Graul return true;
666e5c4744cSKarsten Graul }
667e5c4744cSKarsten Graul
smc_conn_save_peer_info_fce(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * clc)668e5c4744cSKarsten Graul static void smc_conn_save_peer_info_fce(struct smc_sock *smc,
669e5c4744cSKarsten Graul struct smc_clc_msg_accept_confirm *clc)
670e5c4744cSKarsten Graul {
671e5c4744cSKarsten Graul struct smc_clc_first_contact_ext *fce;
672e5c4744cSKarsten Graul int clc_v2_len;
673e5c4744cSKarsten Graul
674e5c4744cSKarsten Graul if (clc->hdr.version == SMC_V1 ||
675e5c4744cSKarsten Graul !(clc->hdr.typev2 & SMC_FIRST_CONTACT_MASK))
676e5c4744cSKarsten Graul return;
677e5c4744cSKarsten Graul
678e5c4744cSKarsten Graul if (smc->conn.lgr->is_smcd) {
679dd0ab991SWen Gu memcpy(smc->conn.lgr->negotiated_eid, clc->d1.eid,
680e5c4744cSKarsten Graul SMC_MAX_EID_LEN);
681dd0ab991SWen Gu clc_v2_len = offsetofend(struct smc_clc_msg_accept_confirm, d1);
682e5c4744cSKarsten Graul } else {
683dd0ab991SWen Gu memcpy(smc->conn.lgr->negotiated_eid, clc->r1.eid,
684e5c4744cSKarsten Graul SMC_MAX_EID_LEN);
685dd0ab991SWen Gu clc_v2_len = offsetofend(struct smc_clc_msg_accept_confirm, r1);
686e5c4744cSKarsten Graul }
687dd0ab991SWen Gu fce = (struct smc_clc_first_contact_ext *)(((u8 *)clc) + clc_v2_len);
688e5c4744cSKarsten Graul smc->conn.lgr->peer_os = fce->os_type;
689e5c4744cSKarsten Graul smc->conn.lgr->peer_smc_release = fce->release;
690e5c4744cSKarsten Graul if (smc_isascii(fce->hostname))
691e5c4744cSKarsten Graul memcpy(smc->conn.lgr->peer_hostname, fce->hostname,
692e5c4744cSKarsten Graul SMC_MAX_HOSTNAME_LEN);
693e5c4744cSKarsten Graul }
694e5c4744cSKarsten Graul
smcr_conn_save_peer_info(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * clc)69541349844SHans Wippel static void smcr_conn_save_peer_info(struct smc_sock *smc,
6960cfdd8f9SUrsula Braun struct smc_clc_msg_accept_confirm *clc)
6970cfdd8f9SUrsula Braun {
6983d9725a6SUrsula Braun int bufsize = smc_uncompress_bufsize(clc->r0.rmbe_size);
69995d8d263SHans Wippel
7003d9725a6SUrsula Braun smc->conn.peer_rmbe_idx = clc->r0.rmbe_idx;
7013d9725a6SUrsula Braun smc->conn.local_tx_ctrl.token = ntohl(clc->r0.rmbe_alert_token);
70295d8d263SHans Wippel smc->conn.peer_rmbe_size = bufsize;
703cd6851f3SUrsula Braun atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
70495d8d263SHans Wippel smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1);
7050cfdd8f9SUrsula Braun }
7060cfdd8f9SUrsula Braun
smcd_conn_save_peer_info(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * clc)70741349844SHans Wippel static void smcd_conn_save_peer_info(struct smc_sock *smc,
70841349844SHans Wippel struct smc_clc_msg_accept_confirm *clc)
70941349844SHans Wippel {
7103d9725a6SUrsula Braun int bufsize = smc_uncompress_bufsize(clc->d0.dmbe_size);
71141349844SHans Wippel
7123d9725a6SUrsula Braun smc->conn.peer_rmbe_idx = clc->d0.dmbe_idx;
713f9eef256SWen Gu smc->conn.peer_token = ntohll(clc->d0.token);
71441349844SHans Wippel /* msg header takes up space in the buffer */
71541349844SHans Wippel smc->conn.peer_rmbe_size = bufsize - sizeof(struct smcd_cdc_msg);
71641349844SHans Wippel atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
71741349844SHans Wippel smc->conn.tx_off = bufsize * smc->conn.peer_rmbe_idx;
71841349844SHans Wippel }
71941349844SHans Wippel
smc_conn_save_peer_info(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * clc)72041349844SHans Wippel static void smc_conn_save_peer_info(struct smc_sock *smc,
72141349844SHans Wippel struct smc_clc_msg_accept_confirm *clc)
72241349844SHans Wippel {
72341349844SHans Wippel if (smc->conn.lgr->is_smcd)
72441349844SHans Wippel smcd_conn_save_peer_info(smc, clc);
72541349844SHans Wippel else
72641349844SHans Wippel smcr_conn_save_peer_info(smc, clc);
727e5c4744cSKarsten Graul smc_conn_save_peer_info_fce(smc, clc);
72841349844SHans Wippel }
72941349844SHans Wippel
smc_link_save_peer_info(struct smc_link * link,struct smc_clc_msg_accept_confirm * clc,struct smc_init_info * ini)7300cfdd8f9SUrsula Braun static void smc_link_save_peer_info(struct smc_link *link,
731e5c4744cSKarsten Graul struct smc_clc_msg_accept_confirm *clc,
732e5c4744cSKarsten Graul struct smc_init_info *ini)
7330cfdd8f9SUrsula Braun {
7343d9725a6SUrsula Braun link->peer_qpn = ntoh24(clc->r0.qpn);
735e5c4744cSKarsten Graul memcpy(link->peer_gid, ini->peer_gid, SMC_GID_SIZE);
736e5c4744cSKarsten Graul memcpy(link->peer_mac, ini->peer_mac, sizeof(link->peer_mac));
7373d9725a6SUrsula Braun link->peer_psn = ntoh24(clc->r0.psn);
7383d9725a6SUrsula Braun link->peer_mtu = clc->r0.qp_mtu;
7390cfdd8f9SUrsula Braun }
7400cfdd8f9SUrsula Braun
smc_stat_inc_fback_rsn_cnt(struct smc_sock * smc,struct smc_stats_fback * fback_arr)741e0e4b8faSGuvenc Gulce static void smc_stat_inc_fback_rsn_cnt(struct smc_sock *smc,
742e0e4b8faSGuvenc Gulce struct smc_stats_fback *fback_arr)
743e0e4b8faSGuvenc Gulce {
744e0e4b8faSGuvenc Gulce int cnt;
745e0e4b8faSGuvenc Gulce
746e0e4b8faSGuvenc Gulce for (cnt = 0; cnt < SMC_MAX_FBACK_RSN_CNT; cnt++) {
747e0e4b8faSGuvenc Gulce if (fback_arr[cnt].fback_code == smc->fallback_rsn) {
748e0e4b8faSGuvenc Gulce fback_arr[cnt].count++;
749e0e4b8faSGuvenc Gulce break;
750e0e4b8faSGuvenc Gulce }
751e0e4b8faSGuvenc Gulce if (!fback_arr[cnt].fback_code) {
752e0e4b8faSGuvenc Gulce fback_arr[cnt].fback_code = smc->fallback_rsn;
753e0e4b8faSGuvenc Gulce fback_arr[cnt].count++;
754e0e4b8faSGuvenc Gulce break;
755e0e4b8faSGuvenc Gulce }
756e0e4b8faSGuvenc Gulce }
757e0e4b8faSGuvenc Gulce }
758e0e4b8faSGuvenc Gulce
smc_stat_fallback(struct smc_sock * smc)759e0e4b8faSGuvenc Gulce static void smc_stat_fallback(struct smc_sock *smc)
760e0e4b8faSGuvenc Gulce {
761194730a9SGuvenc Gulce struct net *net = sock_net(&smc->sk);
762194730a9SGuvenc Gulce
763194730a9SGuvenc Gulce mutex_lock(&net->smc.mutex_fback_rsn);
764e0e4b8faSGuvenc Gulce if (smc->listen_smc) {
765194730a9SGuvenc Gulce smc_stat_inc_fback_rsn_cnt(smc, net->smc.fback_rsn->srv);
766194730a9SGuvenc Gulce net->smc.fback_rsn->srv_fback_cnt++;
767e0e4b8faSGuvenc Gulce } else {
768194730a9SGuvenc Gulce smc_stat_inc_fback_rsn_cnt(smc, net->smc.fback_rsn->clnt);
769194730a9SGuvenc Gulce net->smc.fback_rsn->clnt_fback_cnt++;
770e0e4b8faSGuvenc Gulce }
771194730a9SGuvenc Gulce mutex_unlock(&net->smc.mutex_fback_rsn);
772e0e4b8faSGuvenc Gulce }
773e0e4b8faSGuvenc Gulce
774341adeecSWen Gu /* must be called under rcu read lock */
smc_fback_wakeup_waitqueue(struct smc_sock * smc,void * key)775341adeecSWen Gu static void smc_fback_wakeup_waitqueue(struct smc_sock *smc, void *key)
776341adeecSWen Gu {
777341adeecSWen Gu struct socket_wq *wq;
778341adeecSWen Gu __poll_t flags;
779341adeecSWen Gu
780341adeecSWen Gu wq = rcu_dereference(smc->sk.sk_wq);
781341adeecSWen Gu if (!skwq_has_sleeper(wq))
782341adeecSWen Gu return;
783341adeecSWen Gu
784341adeecSWen Gu /* wake up smc sk->sk_wq */
785341adeecSWen Gu if (!key) {
786341adeecSWen Gu /* sk_state_change */
787341adeecSWen Gu wake_up_interruptible_all(&wq->wait);
788341adeecSWen Gu } else {
789341adeecSWen Gu flags = key_to_poll(key);
790341adeecSWen Gu if (flags & (EPOLLIN | EPOLLOUT))
791341adeecSWen Gu /* sk_data_ready or sk_write_space */
792341adeecSWen Gu wake_up_interruptible_sync_poll(&wq->wait, flags);
793341adeecSWen Gu else if (flags & EPOLLERR)
794341adeecSWen Gu /* sk_error_report */
795341adeecSWen Gu wake_up_interruptible_poll(&wq->wait, flags);
796341adeecSWen Gu }
797341adeecSWen Gu }
798341adeecSWen Gu
smc_fback_mark_woken(wait_queue_entry_t * wait,unsigned int mode,int sync,void * key)799341adeecSWen Gu static int smc_fback_mark_woken(wait_queue_entry_t *wait,
800341adeecSWen Gu unsigned int mode, int sync, void *key)
801341adeecSWen Gu {
802341adeecSWen Gu struct smc_mark_woken *mark =
803341adeecSWen Gu container_of(wait, struct smc_mark_woken, wait_entry);
804341adeecSWen Gu
805341adeecSWen Gu mark->woken = true;
806341adeecSWen Gu mark->key = key;
807341adeecSWen Gu return 0;
808341adeecSWen Gu }
809341adeecSWen Gu
smc_fback_forward_wakeup(struct smc_sock * smc,struct sock * clcsk,void (* clcsock_callback)(struct sock * sk))810341adeecSWen Gu static void smc_fback_forward_wakeup(struct smc_sock *smc, struct sock *clcsk,
811341adeecSWen Gu void (*clcsock_callback)(struct sock *sk))
812341adeecSWen Gu {
813341adeecSWen Gu struct smc_mark_woken mark = { .woken = false };
814341adeecSWen Gu struct socket_wq *wq;
815341adeecSWen Gu
816341adeecSWen Gu init_waitqueue_func_entry(&mark.wait_entry,
817341adeecSWen Gu smc_fback_mark_woken);
818341adeecSWen Gu rcu_read_lock();
819341adeecSWen Gu wq = rcu_dereference(clcsk->sk_wq);
820341adeecSWen Gu if (!wq)
821341adeecSWen Gu goto out;
822341adeecSWen Gu add_wait_queue(sk_sleep(clcsk), &mark.wait_entry);
823341adeecSWen Gu clcsock_callback(clcsk);
824341adeecSWen Gu remove_wait_queue(sk_sleep(clcsk), &mark.wait_entry);
825341adeecSWen Gu
826341adeecSWen Gu if (mark.woken)
827341adeecSWen Gu smc_fback_wakeup_waitqueue(smc, mark.key);
828341adeecSWen Gu out:
829341adeecSWen Gu rcu_read_unlock();
830341adeecSWen Gu }
831341adeecSWen Gu
smc_fback_state_change(struct sock * clcsk)832341adeecSWen Gu static void smc_fback_state_change(struct sock *clcsk)
833341adeecSWen Gu {
8340558226cSWen Gu struct smc_sock *smc;
835341adeecSWen Gu
8360558226cSWen Gu read_lock_bh(&clcsk->sk_callback_lock);
8370558226cSWen Gu smc = smc_clcsock_user_data(clcsk);
8380558226cSWen Gu if (smc)
8390558226cSWen Gu smc_fback_forward_wakeup(smc, clcsk,
8400558226cSWen Gu smc->clcsk_state_change);
8410558226cSWen Gu read_unlock_bh(&clcsk->sk_callback_lock);
842341adeecSWen Gu }
843341adeecSWen Gu
smc_fback_data_ready(struct sock * clcsk)844341adeecSWen Gu static void smc_fback_data_ready(struct sock *clcsk)
845341adeecSWen Gu {
8460558226cSWen Gu struct smc_sock *smc;
847341adeecSWen Gu
8480558226cSWen Gu read_lock_bh(&clcsk->sk_callback_lock);
8490558226cSWen Gu smc = smc_clcsock_user_data(clcsk);
8500558226cSWen Gu if (smc)
8510558226cSWen Gu smc_fback_forward_wakeup(smc, clcsk,
8520558226cSWen Gu smc->clcsk_data_ready);
8530558226cSWen Gu read_unlock_bh(&clcsk->sk_callback_lock);
854341adeecSWen Gu }
855341adeecSWen Gu
smc_fback_write_space(struct sock * clcsk)856341adeecSWen Gu static void smc_fback_write_space(struct sock *clcsk)
857341adeecSWen Gu {
8580558226cSWen Gu struct smc_sock *smc;
859341adeecSWen Gu
8600558226cSWen Gu read_lock_bh(&clcsk->sk_callback_lock);
8610558226cSWen Gu smc = smc_clcsock_user_data(clcsk);
8620558226cSWen Gu if (smc)
8630558226cSWen Gu smc_fback_forward_wakeup(smc, clcsk,
8640558226cSWen Gu smc->clcsk_write_space);
8650558226cSWen Gu read_unlock_bh(&clcsk->sk_callback_lock);
866341adeecSWen Gu }
867341adeecSWen Gu
smc_fback_error_report(struct sock * clcsk)868341adeecSWen Gu static void smc_fback_error_report(struct sock *clcsk)
869341adeecSWen Gu {
8700558226cSWen Gu struct smc_sock *smc;
871341adeecSWen Gu
8720558226cSWen Gu read_lock_bh(&clcsk->sk_callback_lock);
8730558226cSWen Gu smc = smc_clcsock_user_data(clcsk);
8740558226cSWen Gu if (smc)
8750558226cSWen Gu smc_fback_forward_wakeup(smc, clcsk,
8760558226cSWen Gu smc->clcsk_error_report);
8770558226cSWen Gu read_unlock_bh(&clcsk->sk_callback_lock);
878341adeecSWen Gu }
879341adeecSWen Gu
smc_fback_replace_callbacks(struct smc_sock * smc)88097b9af7aSWen Gu static void smc_fback_replace_callbacks(struct smc_sock *smc)
88197b9af7aSWen Gu {
88297b9af7aSWen Gu struct sock *clcsk = smc->clcsock->sk;
88397b9af7aSWen Gu
8840558226cSWen Gu write_lock_bh(&clcsk->sk_callback_lock);
88597b9af7aSWen Gu clcsk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
88697b9af7aSWen Gu
88797b9af7aSWen Gu smc_clcsock_replace_cb(&clcsk->sk_state_change, smc_fback_state_change,
88897b9af7aSWen Gu &smc->clcsk_state_change);
88997b9af7aSWen Gu smc_clcsock_replace_cb(&clcsk->sk_data_ready, smc_fback_data_ready,
89097b9af7aSWen Gu &smc->clcsk_data_ready);
89197b9af7aSWen Gu smc_clcsock_replace_cb(&clcsk->sk_write_space, smc_fback_write_space,
89297b9af7aSWen Gu &smc->clcsk_write_space);
89397b9af7aSWen Gu smc_clcsock_replace_cb(&clcsk->sk_error_report, smc_fback_error_report,
89497b9af7aSWen Gu &smc->clcsk_error_report);
8950558226cSWen Gu
8960558226cSWen Gu write_unlock_bh(&clcsk->sk_callback_lock);
89797b9af7aSWen Gu }
89897b9af7aSWen Gu
smc_switch_to_fallback(struct smc_sock * smc,int reason_code)899c0bf3d8aSWen Gu static int smc_switch_to_fallback(struct smc_sock *smc, int reason_code)
90007603b23SUrsula Braun {
9011de9770dSWen Gu int rc = 0;
9022153bd1eSWen Gu
903c0bf3d8aSWen Gu mutex_lock(&smc->clcsock_release_lock);
904c0bf3d8aSWen Gu if (!smc->clcsock) {
9051de9770dSWen Gu rc = -EBADF;
9061de9770dSWen Gu goto out;
907c0bf3d8aSWen Gu }
908341adeecSWen Gu
90907603b23SUrsula Braun smc->use_fallback = true;
910e0e4b8faSGuvenc Gulce smc->fallback_rsn = reason_code;
911e0e4b8faSGuvenc Gulce smc_stat_fallback(smc);
91248262608STony Lu trace_smc_switch_to_fallback(smc, reason_code);
91307603b23SUrsula Braun if (smc->sk.sk_socket && smc->sk.sk_socket->file) {
91407603b23SUrsula Braun smc->clcsock->file = smc->sk.sk_socket->file;
91507603b23SUrsula Braun smc->clcsock->file->private_data = smc->clcsock;
91667f562e3SUrsula Braun smc->clcsock->wq.fasync_list =
91767f562e3SUrsula Braun smc->sk.sk_socket->wq.fasync_list;
9182153bd1eSWen Gu
919341adeecSWen Gu /* There might be some wait entries remaining
920341adeecSWen Gu * in smc sk->sk_wq and they should be woken up
921341adeecSWen Gu * as clcsock's wait queue is woken up.
9222153bd1eSWen Gu */
92397b9af7aSWen Gu smc_fback_replace_callbacks(smc);
92407603b23SUrsula Braun }
9251de9770dSWen Gu out:
926c0bf3d8aSWen Gu mutex_unlock(&smc->clcsock_release_lock);
9271de9770dSWen Gu return rc;
92807603b23SUrsula Braun }
92907603b23SUrsula Braun
9303b2dec26SHans Wippel /* fall back during connect */
smc_connect_fallback(struct smc_sock * smc,int reason_code)931603cc149SKarsten Graul static int smc_connect_fallback(struct smc_sock *smc, int reason_code)
9323b2dec26SHans Wippel {
933c0bf3d8aSWen Gu struct net *net = sock_net(&smc->sk);
934c0bf3d8aSWen Gu int rc = 0;
935c0bf3d8aSWen Gu
936c0bf3d8aSWen Gu rc = smc_switch_to_fallback(smc, reason_code);
937c0bf3d8aSWen Gu if (rc) { /* fallback fails */
938c0bf3d8aSWen Gu this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt);
939c0bf3d8aSWen Gu if (smc->sk.sk_state == SMC_INIT)
940c0bf3d8aSWen Gu sock_put(&smc->sk); /* passive closing */
941c0bf3d8aSWen Gu return rc;
942c0bf3d8aSWen Gu }
9433b2dec26SHans Wippel smc_copy_sock_settings_to_clc(smc);
94450717a37SUrsula Braun smc->connect_nonblock = 0;
9453b2dec26SHans Wippel if (smc->sk.sk_state == SMC_INIT)
9463b2dec26SHans Wippel smc->sk.sk_state = SMC_ACTIVE;
9473b2dec26SHans Wippel return 0;
9483b2dec26SHans Wippel }
9493b2dec26SHans Wippel
9503b2dec26SHans Wippel /* decline and fall back during connect */
smc_connect_decline_fallback(struct smc_sock * smc,int reason_code,u8 version)951e8d726c8SUrsula Braun static int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code,
952e8d726c8SUrsula Braun u8 version)
9533b2dec26SHans Wippel {
954194730a9SGuvenc Gulce struct net *net = sock_net(&smc->sk);
9553b2dec26SHans Wippel int rc;
9563b2dec26SHans Wippel
957e1bbdd57SUrsula Braun if (reason_code < 0) { /* error, fallback is not possible */
958194730a9SGuvenc Gulce this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt);
959e1bbdd57SUrsula Braun if (smc->sk.sk_state == SMC_INIT)
960e1bbdd57SUrsula Braun sock_put(&smc->sk); /* passive closing */
9613b2dec26SHans Wippel return reason_code;
962e1bbdd57SUrsula Braun }
963603cc149SKarsten Graul if (reason_code != SMC_CLC_DECL_PEERDECL) {
964e8d726c8SUrsula Braun rc = smc_clc_send_decline(smc, reason_code, version);
965e1bbdd57SUrsula Braun if (rc < 0) {
966194730a9SGuvenc Gulce this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt);
967e1bbdd57SUrsula Braun if (smc->sk.sk_state == SMC_INIT)
968e1bbdd57SUrsula Braun sock_put(&smc->sk); /* passive closing */
9693b2dec26SHans Wippel return rc;
9703b2dec26SHans Wippel }
971e1bbdd57SUrsula Braun }
972603cc149SKarsten Graul return smc_connect_fallback(smc, reason_code);
9733b2dec26SHans Wippel }
9743b2dec26SHans Wippel
smc_conn_abort(struct smc_sock * smc,int local_first)9758cf3f3e4SKarsten Graul static void smc_conn_abort(struct smc_sock *smc, int local_first)
9763b2dec26SHans Wippel {
97736595d8aSWen Gu struct smc_connection *conn = &smc->conn;
97836595d8aSWen Gu struct smc_link_group *lgr = conn->lgr;
979ea89c6c0SWen Gu bool lgr_valid = false;
980ea89c6c0SWen Gu
981ea89c6c0SWen Gu if (smc_conn_lgr_valid(conn))
982ea89c6c0SWen Gu lgr_valid = true;
98336595d8aSWen Gu
98436595d8aSWen Gu smc_conn_free(conn);
985ea89c6c0SWen Gu if (local_first && lgr_valid)
98636595d8aSWen Gu smc_lgr_cleanup_early(lgr);
9873b2dec26SHans Wippel }
9883b2dec26SHans Wippel
9893b2dec26SHans Wippel /* check if there is a rdma device available for this connection. */
9903b2dec26SHans Wippel /* called for connect and listen */
smc_find_rdma_device(struct smc_sock * smc,struct smc_init_info * ini)991228bae05SKarsten Graul static int smc_find_rdma_device(struct smc_sock *smc, struct smc_init_info *ini)
9923b2dec26SHans Wippel {
9933b2dec26SHans Wippel /* PNET table look up: search active ib_device and port
9943b2dec26SHans Wippel * within same PNETID that also contains the ethernet device
9953b2dec26SHans Wippel * used for the internal TCP socket
9963b2dec26SHans Wippel */
997bc36d2fcSKarsten Graul smc_pnet_find_roce_resource(smc->clcsock->sk, ini);
99842042dbbSKarsten Graul if (!ini->check_smcrv2 && !ini->ib_dev)
99942042dbbSKarsten Graul return SMC_CLC_DECL_NOSMCRDEV;
100042042dbbSKarsten Graul if (ini->check_smcrv2 && !ini->smcrv2.ib_dev_v2)
10019aa68d29SKarsten Graul return SMC_CLC_DECL_NOSMCRDEV;
1002bc36d2fcSKarsten Graul return 0;
10033b2dec26SHans Wippel }
10043b2dec26SHans Wippel
100541349844SHans Wippel /* check if there is an ISM device available for this connection. */
100641349844SHans Wippel /* called for connect and listen */
smc_find_ism_device(struct smc_sock * smc,struct smc_init_info * ini)1007228bae05SKarsten Graul static int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini)
100841349844SHans Wippel {
100941349844SHans Wippel /* Find ISM device with same PNETID as connecting interface */
1010bc36d2fcSKarsten Graul smc_pnet_find_ism_resource(smc->clcsock->sk, ini);
10113fc64937SUrsula Braun if (!ini->ism_dev[0])
10129aa68d29SKarsten Graul return SMC_CLC_DECL_NOSMCDDEV;
10138caaccf5SUrsula Braun else
10148caaccf5SUrsula Braun ini->ism_chid[0] = smc_ism_get_chid(ini->ism_dev[0]);
101541349844SHans Wippel return 0;
101641349844SHans Wippel }
101741349844SHans Wippel
1018839d696fSKarsten Graul /* is chid unique for the ism devices that are already determined? */
smc_find_ism_v2_is_unique_chid(u16 chid,struct smc_init_info * ini,int cnt)1019839d696fSKarsten Graul static bool smc_find_ism_v2_is_unique_chid(u16 chid, struct smc_init_info *ini,
1020839d696fSKarsten Graul int cnt)
1021839d696fSKarsten Graul {
1022839d696fSKarsten Graul int i = (!ini->ism_dev[0]) ? 1 : 0;
1023839d696fSKarsten Graul
1024839d696fSKarsten Graul for (; i < cnt; i++)
1025839d696fSKarsten Graul if (ini->ism_chid[i] == chid)
1026839d696fSKarsten Graul return false;
1027839d696fSKarsten Graul return true;
1028839d696fSKarsten Graul }
1029839d696fSKarsten Graul
1030d70bf4f7SUrsula Braun /* determine possible V2 ISM devices (either without PNETID or with PNETID plus
1031d70bf4f7SUrsula Braun * PNETID matching net_device)
1032d70bf4f7SUrsula Braun */
smc_find_ism_v2_device_clnt(struct smc_sock * smc,struct smc_init_info * ini)1033d70bf4f7SUrsula Braun static int smc_find_ism_v2_device_clnt(struct smc_sock *smc,
1034d70bf4f7SUrsula Braun struct smc_init_info *ini)
1035d70bf4f7SUrsula Braun {
1036d70bf4f7SUrsula Braun int rc = SMC_CLC_DECL_NOSMCDDEV;
1037d70bf4f7SUrsula Braun struct smcd_dev *smcd;
10387e5ef8ebSWen Gu int i = 1, entry = 1;
10397e5ef8ebSWen Gu bool is_virtual;
1040839d696fSKarsten Graul u16 chid;
1041d70bf4f7SUrsula Braun
1042d70bf4f7SUrsula Braun if (smcd_indicated(ini->smc_type_v1))
1043d70bf4f7SUrsula Braun rc = 0; /* already initialized for V1 */
1044d70bf4f7SUrsula Braun mutex_lock(&smcd_dev_list.mutex);
1045d70bf4f7SUrsula Braun list_for_each_entry(smcd, &smcd_dev_list.list, list) {
1046d70bf4f7SUrsula Braun if (smcd->going_away || smcd == ini->ism_dev[0])
1047d70bf4f7SUrsula Braun continue;
1048839d696fSKarsten Graul chid = smc_ism_get_chid(smcd);
1049839d696fSKarsten Graul if (!smc_find_ism_v2_is_unique_chid(chid, ini, i))
1050839d696fSKarsten Graul continue;
10517e5ef8ebSWen Gu is_virtual = __smc_ism_is_virtual(chid);
1052d70bf4f7SUrsula Braun if (!smc_pnet_is_pnetid_set(smcd->pnetid) ||
1053d70bf4f7SUrsula Braun smc_pnet_is_ndev_pnetid(sock_net(&smc->sk), smcd->pnetid)) {
10547e5ef8ebSWen Gu if (is_virtual && entry == SMCD_CLC_MAX_V2_GID_ENTRIES)
10557e5ef8ebSWen Gu /* It's the last GID-CHID entry left in CLC
10567e5ef8ebSWen Gu * Proposal SMC-Dv2 extension, but a virtual
10577e5ef8ebSWen Gu * ISM device will take two entries. So give
10587e5ef8ebSWen Gu * up it and try the next potential ISM device.
10597e5ef8ebSWen Gu */
10607e5ef8ebSWen Gu continue;
1061d70bf4f7SUrsula Braun ini->ism_dev[i] = smcd;
1062839d696fSKarsten Graul ini->ism_chid[i] = chid;
1063d70bf4f7SUrsula Braun ini->is_smcd = true;
1064d70bf4f7SUrsula Braun rc = 0;
1065d70bf4f7SUrsula Braun i++;
10667e5ef8ebSWen Gu entry = is_virtual ? entry + 2 : entry + 1;
10677e5ef8ebSWen Gu if (entry > SMCD_CLC_MAX_V2_GID_ENTRIES)
1068d70bf4f7SUrsula Braun break;
1069d70bf4f7SUrsula Braun }
1070d70bf4f7SUrsula Braun }
1071d70bf4f7SUrsula Braun mutex_unlock(&smcd_dev_list.mutex);
1072d70bf4f7SUrsula Braun ini->ism_offered_cnt = i - 1;
1073d70bf4f7SUrsula Braun if (!ini->ism_dev[0] && !ini->ism_dev[1])
1074d70bf4f7SUrsula Braun ini->smcd_version = 0;
1075d70bf4f7SUrsula Braun
1076d70bf4f7SUrsula Braun return rc;
1077d70bf4f7SUrsula Braun }
1078d70bf4f7SUrsula Braun
107941349844SHans Wippel /* Check for VLAN ID and register it on ISM device just for CLC handshake */
smc_connect_ism_vlan_setup(struct smc_sock * smc,struct smc_init_info * ini)108041349844SHans Wippel static int smc_connect_ism_vlan_setup(struct smc_sock *smc,
1081bc36d2fcSKarsten Graul struct smc_init_info *ini)
108241349844SHans Wippel {
10833fc64937SUrsula Braun if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev[0], ini->vlan_id))
10847a62725aSKarsten Graul return SMC_CLC_DECL_ISMVLANERR;
108541349844SHans Wippel return 0;
108641349844SHans Wippel }
108741349844SHans Wippel
smc_find_proposal_devices(struct smc_sock * smc,struct smc_init_info * ini)1088d70bf4f7SUrsula Braun static int smc_find_proposal_devices(struct smc_sock *smc,
1089d70bf4f7SUrsula Braun struct smc_init_info *ini)
1090d70bf4f7SUrsula Braun {
1091d70bf4f7SUrsula Braun int rc = 0;
1092d70bf4f7SUrsula Braun
1093d70bf4f7SUrsula Braun /* check if there is an ism device available */
109442042dbbSKarsten Graul if (!(ini->smcd_version & SMC_V1) ||
109542042dbbSKarsten Graul smc_find_ism_device(smc, ini) ||
109642042dbbSKarsten Graul smc_connect_ism_vlan_setup(smc, ini))
109742042dbbSKarsten Graul ini->smcd_version &= ~SMC_V1;
109842042dbbSKarsten Graul /* else ISM V1 is supported for this connection */
109942042dbbSKarsten Graul
110042042dbbSKarsten Graul /* check if there is an rdma device available */
110142042dbbSKarsten Graul if (!(ini->smcr_version & SMC_V1) ||
110242042dbbSKarsten Graul smc_find_rdma_device(smc, ini))
110342042dbbSKarsten Graul ini->smcr_version &= ~SMC_V1;
110442042dbbSKarsten Graul /* else RDMA is supported for this connection */
110542042dbbSKarsten Graul
110642042dbbSKarsten Graul ini->smc_type_v1 = smc_indicated_type(ini->smcd_version & SMC_V1,
110742042dbbSKarsten Graul ini->smcr_version & SMC_V1);
110842042dbbSKarsten Graul
110942042dbbSKarsten Graul /* check if there is an ism v2 device available */
111042042dbbSKarsten Graul if (!(ini->smcd_version & SMC_V2) ||
111142042dbbSKarsten Graul !smc_ism_is_v2_capable() ||
111242042dbbSKarsten Graul smc_find_ism_v2_device_clnt(smc, ini))
111342042dbbSKarsten Graul ini->smcd_version &= ~SMC_V2;
111442042dbbSKarsten Graul
111542042dbbSKarsten Graul /* check if there is an rdma v2 device available */
111642042dbbSKarsten Graul ini->check_smcrv2 = true;
111742042dbbSKarsten Graul ini->smcrv2.saddr = smc->clcsock->sk->sk_rcv_saddr;
111842042dbbSKarsten Graul if (!(ini->smcr_version & SMC_V2) ||
111942042dbbSKarsten Graul smc->clcsock->sk->sk_family != AF_INET ||
112042042dbbSKarsten Graul !smc_clc_ueid_count() ||
112142042dbbSKarsten Graul smc_find_rdma_device(smc, ini))
112242042dbbSKarsten Graul ini->smcr_version &= ~SMC_V2;
112342042dbbSKarsten Graul ini->check_smcrv2 = false;
112442042dbbSKarsten Graul
112542042dbbSKarsten Graul ini->smc_type_v2 = smc_indicated_type(ini->smcd_version & SMC_V2,
112642042dbbSKarsten Graul ini->smcr_version & SMC_V2);
1127d70bf4f7SUrsula Braun
1128d70bf4f7SUrsula Braun /* if neither ISM nor RDMA are supported, fallback */
112942042dbbSKarsten Graul if (ini->smc_type_v1 == SMC_TYPE_N && ini->smc_type_v2 == SMC_TYPE_N)
1130d70bf4f7SUrsula Braun rc = SMC_CLC_DECL_NOSMCDEV;
1131d70bf4f7SUrsula Braun
1132d70bf4f7SUrsula Braun return rc;
1133d70bf4f7SUrsula Braun }
1134d70bf4f7SUrsula Braun
113541349844SHans Wippel /* cleanup temporary VLAN ID registration used for CLC handshake. If ISM is
113641349844SHans Wippel * used, the VLAN ID will be registered again during the connection setup.
113741349844SHans Wippel */
smc_connect_ism_vlan_cleanup(struct smc_sock * smc,struct smc_init_info * ini)1138d70bf4f7SUrsula Braun static int smc_connect_ism_vlan_cleanup(struct smc_sock *smc,
1139bc36d2fcSKarsten Graul struct smc_init_info *ini)
114041349844SHans Wippel {
1141d70bf4f7SUrsula Braun if (!smcd_indicated(ini->smc_type_v1))
114241349844SHans Wippel return 0;
11433fc64937SUrsula Braun if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev[0], ini->vlan_id))
114441349844SHans Wippel return SMC_CLC_DECL_CNFERR;
114541349844SHans Wippel return 0;
114641349844SHans Wippel }
114741349844SHans Wippel
1148a7c9c5f4SUrsula Braun #define SMC_CLC_MAX_ACCEPT_LEN \
1149dd0ab991SWen Gu (sizeof(struct smc_clc_msg_accept_confirm) + \
11507290178aSGuangguan Wang sizeof(struct smc_clc_first_contact_ext_v2x) + \
1151a7c9c5f4SUrsula Braun sizeof(struct smc_clc_msg_trail))
1152a7c9c5f4SUrsula Braun
11533b2dec26SHans Wippel /* CLC handshake during connect */
smc_connect_clc(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * aclc,struct smc_init_info * ini)1154d70bf4f7SUrsula Braun static int smc_connect_clc(struct smc_sock *smc,
1155dd0ab991SWen Gu struct smc_clc_msg_accept_confirm *aclc,
1156bc36d2fcSKarsten Graul struct smc_init_info *ini)
11573b2dec26SHans Wippel {
11583b2dec26SHans Wippel int rc = 0;
11593b2dec26SHans Wippel
11603b2dec26SHans Wippel /* do inband token exchange */
1161d70bf4f7SUrsula Braun rc = smc_clc_send_proposal(smc, ini);
11623b2dec26SHans Wippel if (rc)
11633b2dec26SHans Wippel return rc;
11643b2dec26SHans Wippel /* receive SMC Accept CLC message */
1165dd0ab991SWen Gu return smc_clc_wait_msg(smc, aclc, SMC_CLC_MAX_ACCEPT_LEN,
1166a7c9c5f4SUrsula Braun SMC_CLC_ACCEPT, CLC_WAIT_TIME);
11673b2dec26SHans Wippel }
11683b2dec26SHans Wippel
smc_fill_gid_list(struct smc_link_group * lgr,struct smc_gidlist * gidlist,struct smc_ib_device * known_dev,u8 * known_gid)1169b4ba4652SKarsten Graul void smc_fill_gid_list(struct smc_link_group *lgr,
1170e5c4744cSKarsten Graul struct smc_gidlist *gidlist,
1171e5c4744cSKarsten Graul struct smc_ib_device *known_dev, u8 *known_gid)
1172e5c4744cSKarsten Graul {
1173e5c4744cSKarsten Graul struct smc_init_info *alt_ini = NULL;
1174e5c4744cSKarsten Graul
1175e5c4744cSKarsten Graul memset(gidlist, 0, sizeof(*gidlist));
1176e5c4744cSKarsten Graul memcpy(gidlist->list[gidlist->len++], known_gid, SMC_GID_SIZE);
1177e5c4744cSKarsten Graul
1178e5c4744cSKarsten Graul alt_ini = kzalloc(sizeof(*alt_ini), GFP_KERNEL);
1179e5c4744cSKarsten Graul if (!alt_ini)
1180e5c4744cSKarsten Graul goto out;
1181e5c4744cSKarsten Graul
1182e5c4744cSKarsten Graul alt_ini->vlan_id = lgr->vlan_id;
1183e5c4744cSKarsten Graul alt_ini->check_smcrv2 = true;
1184e5c4744cSKarsten Graul alt_ini->smcrv2.saddr = lgr->saddr;
1185e5c4744cSKarsten Graul smc_pnet_find_alt_roce(lgr, alt_ini, known_dev);
1186e5c4744cSKarsten Graul
1187e5c4744cSKarsten Graul if (!alt_ini->smcrv2.ib_dev_v2)
1188e5c4744cSKarsten Graul goto out;
1189e5c4744cSKarsten Graul
1190e5c4744cSKarsten Graul memcpy(gidlist->list[gidlist->len++], alt_ini->smcrv2.ib_gid_v2,
1191e5c4744cSKarsten Graul SMC_GID_SIZE);
1192e5c4744cSKarsten Graul
1193e5c4744cSKarsten Graul out:
1194e5c4744cSKarsten Graul kfree(alt_ini);
1195e5c4744cSKarsten Graul }
1196e5c4744cSKarsten Graul
smc_connect_rdma_v2_prepare(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * aclc,struct smc_init_info * ini)1197e5c4744cSKarsten Graul static int smc_connect_rdma_v2_prepare(struct smc_sock *smc,
1198e5c4744cSKarsten Graul struct smc_clc_msg_accept_confirm *aclc,
1199e5c4744cSKarsten Graul struct smc_init_info *ini)
1200e5c4744cSKarsten Graul {
1201e5c4744cSKarsten Graul struct smc_clc_first_contact_ext *fce =
1202dd0ab991SWen Gu smc_get_clc_first_contact_ext(aclc, false);
1203c68681aeSAlbert Huang struct net *net = sock_net(&smc->sk);
12046ac1e656SGuangguan Wang int rc;
1205e5c4744cSKarsten Graul
1206e5c4744cSKarsten Graul if (!ini->first_contact_peer || aclc->hdr.version == SMC_V1)
1207e5c4744cSKarsten Graul return 0;
1208e5c4744cSKarsten Graul
1209e5c4744cSKarsten Graul if (fce->v2_direct) {
1210e5c4744cSKarsten Graul memcpy(ini->smcrv2.nexthop_mac, &aclc->r0.lcl.mac, ETH_ALEN);
1211e5c4744cSKarsten Graul ini->smcrv2.uses_gateway = false;
1212e5c4744cSKarsten Graul } else {
1213c68681aeSAlbert Huang if (smc_ib_find_route(net, smc->clcsock->sk->sk_rcv_saddr,
1214e5c4744cSKarsten Graul smc_ib_gid_to_ipv4(aclc->r0.lcl.gid),
1215e5c4744cSKarsten Graul ini->smcrv2.nexthop_mac,
1216e5c4744cSKarsten Graul &ini->smcrv2.uses_gateway))
1217e5c4744cSKarsten Graul return SMC_CLC_DECL_NOROUTE;
1218e5c4744cSKarsten Graul if (!ini->smcrv2.uses_gateway) {
1219e5c4744cSKarsten Graul /* mismatch: peer claims indirect, but its direct */
1220e5c4744cSKarsten Graul return SMC_CLC_DECL_NOINDIRECT;
1221e5c4744cSKarsten Graul }
1222e5c4744cSKarsten Graul }
12231e700948SGuangguan Wang
12241e700948SGuangguan Wang ini->release_nr = fce->release;
12256ac1e656SGuangguan Wang rc = smc_clc_clnt_v2x_features_validate(fce, ini);
12266ac1e656SGuangguan Wang if (rc)
12276ac1e656SGuangguan Wang return rc;
12281e700948SGuangguan Wang
1229e5c4744cSKarsten Graul return 0;
1230e5c4744cSKarsten Graul }
1231e5c4744cSKarsten Graul
1232a046d57dSUrsula Braun /* setup for RDMA connection of client */
smc_connect_rdma(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * aclc,struct smc_init_info * ini)12333b2dec26SHans Wippel static int smc_connect_rdma(struct smc_sock *smc,
12343b2dec26SHans Wippel struct smc_clc_msg_accept_confirm *aclc,
1235bc36d2fcSKarsten Graul struct smc_init_info *ini)
12363b2dec26SHans Wippel {
12370fb0b02bSKarsten Graul int i, reason_code = 0;
12383b2dec26SHans Wippel struct smc_link *link;
1239e5c4744cSKarsten Graul u8 *eid = NULL;
12403b2dec26SHans Wippel
1241bc36d2fcSKarsten Graul ini->is_smcd = false;
12423d9725a6SUrsula Braun ini->ib_clcqpn = ntoh24(aclc->r0.qpn);
1243f1eb02f9SUrsula Braun ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
1244e5c4744cSKarsten Graul memcpy(ini->peer_systemid, aclc->r0.lcl.id_for_peer, SMC_SYSTEMID_LEN);
1245e5c4744cSKarsten Graul memcpy(ini->peer_gid, aclc->r0.lcl.gid, SMC_GID_SIZE);
1246e5c4744cSKarsten Graul memcpy(ini->peer_mac, aclc->r0.lcl.mac, ETH_ALEN);
12477f0620b9SGuangguan Wang ini->max_conns = SMC_CONN_PER_LGR_MAX;
124869b888e3SGuangguan Wang ini->max_links = SMC_LINKS_ADD_LNK_MAX;
1249e5c4744cSKarsten Graul
1250e5c4744cSKarsten Graul reason_code = smc_connect_rdma_v2_prepare(smc, aclc, ini);
1251e5c4744cSKarsten Graul if (reason_code)
1252e5c4744cSKarsten Graul return reason_code;
1253bc36d2fcSKarsten Graul
125472a36a8aSHans Wippel mutex_lock(&smc_client_lgr_pending);
12557a62725aSKarsten Graul reason_code = smc_conn_create(smc, ini);
12567a62725aSKarsten Graul if (reason_code) {
125772a36a8aSHans Wippel mutex_unlock(&smc_client_lgr_pending);
125872a36a8aSHans Wippel return reason_code;
12593b2dec26SHans Wippel }
12603b2dec26SHans Wippel
12613b2dec26SHans Wippel smc_conn_save_peer_info(smc, aclc);
12623b2dec26SHans Wippel
12635ac54d87SUrsula Braun if (ini->first_contact_local) {
12640fb0b02bSKarsten Graul link = smc->conn.lnk;
12650fb0b02bSKarsten Graul } else {
12660fb0b02bSKarsten Graul /* set link that was assigned by server */
12670fb0b02bSKarsten Graul link = NULL;
12680fb0b02bSKarsten Graul for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
12690fb0b02bSKarsten Graul struct smc_link *l = &smc->conn.lgr->lnk[i];
12700fb0b02bSKarsten Graul
12713d9725a6SUrsula Braun if (l->peer_qpn == ntoh24(aclc->r0.qpn) &&
12723d9725a6SUrsula Braun !memcmp(l->peer_gid, &aclc->r0.lcl.gid,
12733d9725a6SUrsula Braun SMC_GID_SIZE) &&
1274e5c4744cSKarsten Graul (aclc->hdr.version > SMC_V1 ||
12753d9725a6SUrsula Braun !memcmp(l->peer_mac, &aclc->r0.lcl.mac,
1276e5c4744cSKarsten Graul sizeof(l->peer_mac)))) {
12770fb0b02bSKarsten Graul link = l;
12780fb0b02bSKarsten Graul break;
12790fb0b02bSKarsten Graul }
12800fb0b02bSKarsten Graul }
1281c60a2cefSKarsten Graul if (!link) {
1282c60a2cefSKarsten Graul reason_code = SMC_CLC_DECL_NOSRVLINK;
1283c60a2cefSKarsten Graul goto connect_abort;
1284c60a2cefSKarsten Graul }
128564513d26SGuvenc Gulce smc_switch_link_and_count(&smc->conn, link);
12860fb0b02bSKarsten Graul }
12870fb0b02bSKarsten Graul
12883b2dec26SHans Wippel /* create send buffer and rmb */
1289c60a2cefSKarsten Graul if (smc_buf_create(smc, false)) {
1290c60a2cefSKarsten Graul reason_code = SMC_CLC_DECL_MEM;
1291c60a2cefSKarsten Graul goto connect_abort;
1292c60a2cefSKarsten Graul }
12933b2dec26SHans Wippel
12945ac54d87SUrsula Braun if (ini->first_contact_local)
1295e5c4744cSKarsten Graul smc_link_save_peer_info(link, aclc, ini);
12963b2dec26SHans Wippel
1297c60a2cefSKarsten Graul if (smc_rmb_rtoken_handling(&smc->conn, link, aclc)) {
1298c60a2cefSKarsten Graul reason_code = SMC_CLC_DECL_ERR_RTOK;
1299c60a2cefSKarsten Graul goto connect_abort;
1300c60a2cefSKarsten Graul }
13013b2dec26SHans Wippel
13023b2dec26SHans Wippel smc_rx_init(smc);
13033b2dec26SHans Wippel
13045ac54d87SUrsula Braun if (ini->first_contact_local) {
1305c60a2cefSKarsten Graul if (smc_ib_ready_link(link)) {
1306c60a2cefSKarsten Graul reason_code = SMC_CLC_DECL_ERR_RDYLNK;
1307c60a2cefSKarsten Graul goto connect_abort;
1308c60a2cefSKarsten Graul }
13093b2dec26SHans Wippel } else {
1310b8d19945SWen Gu /* reg sendbufs if they were vzalloced */
1311b8d19945SWen Gu if (smc->conn.sndbuf_desc->is_vm) {
1312b8d19945SWen Gu if (smcr_lgr_reg_sndbufs(link, smc->conn.sndbuf_desc)) {
1313b8d19945SWen Gu reason_code = SMC_CLC_DECL_ERR_REGBUF;
1314b8d19945SWen Gu goto connect_abort;
1315b8d19945SWen Gu }
1316b8d19945SWen Gu }
1317c60a2cefSKarsten Graul if (smcr_lgr_reg_rmbs(link, smc->conn.rmb_desc)) {
1318b8d19945SWen Gu reason_code = SMC_CLC_DECL_ERR_REGBUF;
1319c60a2cefSKarsten Graul goto connect_abort;
1320c60a2cefSKarsten Graul }
13213b2dec26SHans Wippel }
13223b2dec26SHans Wippel
1323e5c4744cSKarsten Graul if (aclc->hdr.version > SMC_V1) {
1324dd0ab991SWen Gu eid = aclc->r1.eid;
1325e5c4744cSKarsten Graul if (ini->first_contact_local)
1326e5c4744cSKarsten Graul smc_fill_gid_list(link->lgr, &ini->smcrv2.gidlist,
1327e5c4744cSKarsten Graul link->smcibdev, link->gid);
1328e5c4744cSKarsten Graul }
1329e5c4744cSKarsten Graul
1330a7c9c5f4SUrsula Braun reason_code = smc_clc_send_confirm(smc, ini->first_contact_local,
1331e5c4744cSKarsten Graul aclc->hdr.version, eid, ini);
13323b2dec26SHans Wippel if (reason_code)
1333c60a2cefSKarsten Graul goto connect_abort;
13343b2dec26SHans Wippel
13353b2dec26SHans Wippel smc_tx_init(smc);
13363b2dec26SHans Wippel
13375ac54d87SUrsula Braun if (ini->first_contact_local) {
13383b2dec26SHans Wippel /* QP confirmation over RoCE fabric */
13390fb0b02bSKarsten Graul smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
1340b9247544SKarsten Graul reason_code = smcr_clnt_conf_first_link(smc);
13410fb0b02bSKarsten Graul smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
13423b2dec26SHans Wippel if (reason_code)
1343c60a2cefSKarsten Graul goto connect_abort;
13443b2dec26SHans Wippel }
134572a36a8aSHans Wippel mutex_unlock(&smc_client_lgr_pending);
13463b2dec26SHans Wippel
13473b2dec26SHans Wippel smc_copy_sock_settings_to_clc(smc);
134850717a37SUrsula Braun smc->connect_nonblock = 0;
13493b2dec26SHans Wippel if (smc->sk.sk_state == SMC_INIT)
13503b2dec26SHans Wippel smc->sk.sk_state = SMC_ACTIVE;
13513b2dec26SHans Wippel
13523b2dec26SHans Wippel return 0;
1353c60a2cefSKarsten Graul connect_abort:
13548cf3f3e4SKarsten Graul smc_conn_abort(smc, ini->first_contact_local);
1355c60a2cefSKarsten Graul mutex_unlock(&smc_client_lgr_pending);
1356c60a2cefSKarsten Graul smc->connect_nonblock = 0;
1357c60a2cefSKarsten Graul
1358c60a2cefSKarsten Graul return reason_code;
13593b2dec26SHans Wippel }
13603b2dec26SHans Wippel
1361a7c9c5f4SUrsula Braun /* The server has chosen one of the proposed ISM devices for the communication.
1362a7c9c5f4SUrsula Braun * Determine from the CHID of the received CLC ACCEPT the ISM device chosen.
1363a7c9c5f4SUrsula Braun */
1364a7c9c5f4SUrsula Braun static int
smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm * aclc,struct smc_init_info * ini)1365dd0ab991SWen Gu smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc,
1366a7c9c5f4SUrsula Braun struct smc_init_info *ini)
1367a7c9c5f4SUrsula Braun {
1368a7c9c5f4SUrsula Braun int i;
1369a7c9c5f4SUrsula Braun
1370a7c9c5f4SUrsula Braun for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
1371e5c4744cSKarsten Graul if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) {
1372a7c9c5f4SUrsula Braun ini->ism_selected = i;
1373a7c9c5f4SUrsula Braun return 0;
1374a7c9c5f4SUrsula Braun }
1375a7c9c5f4SUrsula Braun }
1376a7c9c5f4SUrsula Braun
1377a7c9c5f4SUrsula Braun return -EPROTO;
1378a7c9c5f4SUrsula Braun }
1379a7c9c5f4SUrsula Braun
138041349844SHans Wippel /* setup for ISM connection of client */
smc_connect_ism(struct smc_sock * smc,struct smc_clc_msg_accept_confirm * aclc,struct smc_init_info * ini)138141349844SHans Wippel static int smc_connect_ism(struct smc_sock *smc,
138241349844SHans Wippel struct smc_clc_msg_accept_confirm *aclc,
1383bc36d2fcSKarsten Graul struct smc_init_info *ini)
138441349844SHans Wippel {
1385fa086662SKarsten Graul u8 *eid = NULL;
138641349844SHans Wippel int rc = 0;
138741349844SHans Wippel
1388bc36d2fcSKarsten Graul ini->is_smcd = true;
1389f1eb02f9SUrsula Braun ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
1390bc36d2fcSKarsten Graul
1391a7c9c5f4SUrsula Braun if (aclc->hdr.version == SMC_V2) {
13921e700948SGuangguan Wang if (ini->first_contact_peer) {
13931e700948SGuangguan Wang struct smc_clc_first_contact_ext *fce =
1394dd0ab991SWen Gu smc_get_clc_first_contact_ext(aclc, true);
13951e700948SGuangguan Wang
13961e700948SGuangguan Wang ini->release_nr = fce->release;
13976ac1e656SGuangguan Wang rc = smc_clc_clnt_v2x_features_validate(fce, ini);
13986ac1e656SGuangguan Wang if (rc)
13996ac1e656SGuangguan Wang return rc;
14001e700948SGuangguan Wang }
14011e700948SGuangguan Wang
1402dd0ab991SWen Gu rc = smc_v2_determine_accepted_chid(aclc, ini);
1403a7c9c5f4SUrsula Braun if (rc)
1404a7c9c5f4SUrsula Braun return rc;
14057e5ef8ebSWen Gu
14067e5ef8ebSWen Gu if (__smc_ism_is_virtual(ini->ism_chid[ini->ism_selected]))
14077e5ef8ebSWen Gu ini->ism_peer_gid[ini->ism_selected].gid_ext =
14087e5ef8ebSWen Gu ntohll(aclc->d1.gid_ext);
14097e5ef8ebSWen Gu /* for non-virtual ISM devices, peer gid_ext remains 0. */
1410a7c9c5f4SUrsula Braun }
14117e5ef8ebSWen Gu ini->ism_peer_gid[ini->ism_selected].gid = ntohll(aclc->d0.gid);
1412a7c9c5f4SUrsula Braun
141372a36a8aSHans Wippel /* there is only one lgr role for SMC-D; use server lock */
141472a36a8aSHans Wippel mutex_lock(&smc_server_lgr_pending);
14157a62725aSKarsten Graul rc = smc_conn_create(smc, ini);
14167a62725aSKarsten Graul if (rc) {
141772a36a8aSHans Wippel mutex_unlock(&smc_server_lgr_pending);
14187a62725aSKarsten Graul return rc;
141972a36a8aSHans Wippel }
142041349844SHans Wippel
142141349844SHans Wippel /* Create send and receive buffers */
142272b7f6c4SKarsten Graul rc = smc_buf_create(smc, true);
1423c60a2cefSKarsten Graul if (rc) {
1424c60a2cefSKarsten Graul rc = (rc == -ENOSPC) ? SMC_CLC_DECL_MAX_DMB : SMC_CLC_DECL_MEM;
1425c60a2cefSKarsten Graul goto connect_abort;
1426c60a2cefSKarsten Graul }
142741349844SHans Wippel
142841349844SHans Wippel smc_conn_save_peer_info(smc, aclc);
142921f6f41eSWen Gu
143021f6f41eSWen Gu if (smc_ism_support_dmb_nocopy(smc->conn.lgr->smcd)) {
143121f6f41eSWen Gu rc = smcd_buf_attach(smc);
143221f6f41eSWen Gu if (rc) {
143321f6f41eSWen Gu rc = SMC_CLC_DECL_MEM; /* try to fallback */
143421f6f41eSWen Gu goto connect_abort;
143521f6f41eSWen Gu }
143621f6f41eSWen Gu }
143741349844SHans Wippel smc_rx_init(smc);
143841349844SHans Wippel smc_tx_init(smc);
143941349844SHans Wippel
1440dd0ab991SWen Gu if (aclc->hdr.version > SMC_V1)
1441dd0ab991SWen Gu eid = aclc->d1.eid;
1442fa086662SKarsten Graul
1443a7c9c5f4SUrsula Braun rc = smc_clc_send_confirm(smc, ini->first_contact_local,
14441e700948SGuangguan Wang aclc->hdr.version, eid, ini);
144541349844SHans Wippel if (rc)
1446c60a2cefSKarsten Graul goto connect_abort;
144772a36a8aSHans Wippel mutex_unlock(&smc_server_lgr_pending);
144841349844SHans Wippel
144941349844SHans Wippel smc_copy_sock_settings_to_clc(smc);
145050717a37SUrsula Braun smc->connect_nonblock = 0;
145141349844SHans Wippel if (smc->sk.sk_state == SMC_INIT)
145241349844SHans Wippel smc->sk.sk_state = SMC_ACTIVE;
145341349844SHans Wippel
145441349844SHans Wippel return 0;
1455c60a2cefSKarsten Graul connect_abort:
14568cf3f3e4SKarsten Graul smc_conn_abort(smc, ini->first_contact_local);
1457c60a2cefSKarsten Graul mutex_unlock(&smc_server_lgr_pending);
1458c60a2cefSKarsten Graul smc->connect_nonblock = 0;
1459c60a2cefSKarsten Graul
1460c60a2cefSKarsten Graul return rc;
146141349844SHans Wippel }
146241349844SHans Wippel
1463d70bf4f7SUrsula Braun /* check if received accept type and version matches a proposed one */
smc_connect_check_aclc(struct smc_init_info * ini,struct smc_clc_msg_accept_confirm * aclc)1464d70bf4f7SUrsula Braun static int smc_connect_check_aclc(struct smc_init_info *ini,
1465d70bf4f7SUrsula Braun struct smc_clc_msg_accept_confirm *aclc)
1466d70bf4f7SUrsula Braun {
146742042dbbSKarsten Graul if (aclc->hdr.typev1 != SMC_TYPE_R &&
146842042dbbSKarsten Graul aclc->hdr.typev1 != SMC_TYPE_D)
146942042dbbSKarsten Graul return SMC_CLC_DECL_MODEUNSUPP;
147042042dbbSKarsten Graul
147142042dbbSKarsten Graul if (aclc->hdr.version >= SMC_V2) {
147242042dbbSKarsten Graul if ((aclc->hdr.typev1 == SMC_TYPE_R &&
147342042dbbSKarsten Graul !smcr_indicated(ini->smc_type_v2)) ||
147442042dbbSKarsten Graul (aclc->hdr.typev1 == SMC_TYPE_D &&
147542042dbbSKarsten Graul !smcd_indicated(ini->smc_type_v2)))
147642042dbbSKarsten Graul return SMC_CLC_DECL_MODEUNSUPP;
147742042dbbSKarsten Graul } else {
1478d70bf4f7SUrsula Braun if ((aclc->hdr.typev1 == SMC_TYPE_R &&
1479d70bf4f7SUrsula Braun !smcr_indicated(ini->smc_type_v1)) ||
1480d70bf4f7SUrsula Braun (aclc->hdr.typev1 == SMC_TYPE_D &&
148142042dbbSKarsten Graul !smcd_indicated(ini->smc_type_v1)))
1482d70bf4f7SUrsula Braun return SMC_CLC_DECL_MODEUNSUPP;
148342042dbbSKarsten Graul }
1484d70bf4f7SUrsula Braun
1485d70bf4f7SUrsula Braun return 0;
1486d70bf4f7SUrsula Braun }
1487d70bf4f7SUrsula Braun
14883b2dec26SHans Wippel /* perform steps before actually connecting */
__smc_connect(struct smc_sock * smc)14893b2dec26SHans Wippel static int __smc_connect(struct smc_sock *smc)
1490a046d57dSUrsula Braun {
149149407ae2SGuvenc Gulce u8 version = smc_ism_is_v2_capable() ? SMC_V2 : SMC_V1;
1492a7c9c5f4SUrsula Braun struct smc_clc_msg_accept_confirm *aclc;
14933fc64937SUrsula Braun struct smc_init_info *ini = NULL;
1494a7c9c5f4SUrsula Braun u8 *buf = NULL;
1495a046d57dSUrsula Braun int rc = 0;
1496a046d57dSUrsula Braun
1497ee9dfbefSUrsula Braun if (smc->use_fallback)
1498603cc149SKarsten Graul return smc_connect_fallback(smc, smc->fallback_rsn);
1499ee9dfbefSUrsula Braun
15003b2dec26SHans Wippel /* if peer has not signalled SMC-capability, fall back */
15013b2dec26SHans Wippel if (!tcp_sk(smc->clcsock->sk)->syn_smc)
1502603cc149SKarsten Graul return smc_connect_fallback(smc, SMC_CLC_DECL_PEERNOSMC);
1503c5c1cc9cSUrsula Braun
1504e8d726c8SUrsula Braun /* IPSec connections opt out of SMC optimizations */
15053b2dec26SHans Wippel if (using_ipsec(smc))
1506e8d726c8SUrsula Braun return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC,
1507e8d726c8SUrsula Braun version);
1508a046d57dSUrsula Braun
15093fc64937SUrsula Braun ini = kzalloc(sizeof(*ini), GFP_KERNEL);
15103fc64937SUrsula Braun if (!ini)
1511e8d726c8SUrsula Braun return smc_connect_decline_fallback(smc, SMC_CLC_DECL_MEM,
1512e8d726c8SUrsula Braun version);
15133fc64937SUrsula Braun
151442042dbbSKarsten Graul ini->smcd_version = SMC_V1 | SMC_V2;
151542042dbbSKarsten Graul ini->smcr_version = SMC_V1 | SMC_V2;
1516d70bf4f7SUrsula Braun ini->smc_type_v1 = SMC_TYPE_B;
151742042dbbSKarsten Graul ini->smc_type_v2 = SMC_TYPE_B;
1518d70bf4f7SUrsula Braun
1519fba7e8efSKarsten Graul /* get vlan id from IP device */
15203fc64937SUrsula Braun if (smc_vlan_by_tcpsk(smc->clcsock, ini)) {
1521d70bf4f7SUrsula Braun ini->smcd_version &= ~SMC_V1;
152242042dbbSKarsten Graul ini->smcr_version = 0;
1523d70bf4f7SUrsula Braun ini->smc_type_v1 = SMC_TYPE_N;
1524d70bf4f7SUrsula Braun if (!ini->smcd_version) {
1525d70bf4f7SUrsula Braun rc = SMC_CLC_DECL_GETVLANERR;
1526d70bf4f7SUrsula Braun goto fallback;
1527d70bf4f7SUrsula Braun }
15283fc64937SUrsula Braun }
152941349844SHans Wippel
1530d70bf4f7SUrsula Braun rc = smc_find_proposal_devices(smc, ini);
1531d70bf4f7SUrsula Braun if (rc)
1532d70bf4f7SUrsula Braun goto fallback;
1533a046d57dSUrsula Braun
1534a7c9c5f4SUrsula Braun buf = kzalloc(SMC_CLC_MAX_ACCEPT_LEN, GFP_KERNEL);
1535a7c9c5f4SUrsula Braun if (!buf) {
1536a7c9c5f4SUrsula Braun rc = SMC_CLC_DECL_MEM;
1537a7c9c5f4SUrsula Braun goto fallback;
1538a7c9c5f4SUrsula Braun }
1539dd0ab991SWen Gu aclc = (struct smc_clc_msg_accept_confirm *)buf;
1540a7c9c5f4SUrsula Braun
15413b2dec26SHans Wippel /* perform CLC handshake */
1542dd0ab991SWen Gu rc = smc_connect_clc(smc, aclc, ini);
15431ce22047SD. Wythe if (rc) {
15441ce22047SD. Wythe /* -EAGAIN on timeout, see tcp_recvmsg() */
15451ce22047SD. Wythe if (rc == -EAGAIN) {
15461ce22047SD. Wythe rc = -ETIMEDOUT;
15471ce22047SD. Wythe smc->sk.sk_err = ETIMEDOUT;
15481ce22047SD. Wythe }
1549d70bf4f7SUrsula Braun goto vlan_cleanup;
15501ce22047SD. Wythe }
1551d70bf4f7SUrsula Braun
1552d70bf4f7SUrsula Braun /* check if smc modes and versions of CLC proposal and accept match */
1553a7c9c5f4SUrsula Braun rc = smc_connect_check_aclc(ini, aclc);
15540530bd6eSKarsten Graul version = aclc->hdr.version == SMC_V1 ? SMC_V1 : SMC_V2;
1555d70bf4f7SUrsula Braun if (rc)
1556d70bf4f7SUrsula Braun goto vlan_cleanup;
1557a046d57dSUrsula Braun
155841349844SHans Wippel /* depending on previous steps, connect using rdma or ism */
155942042dbbSKarsten Graul if (aclc->hdr.typev1 == SMC_TYPE_R) {
156042042dbbSKarsten Graul ini->smcr_version = version;
1561a7c9c5f4SUrsula Braun rc = smc_connect_rdma(smc, aclc, ini);
156242042dbbSKarsten Graul } else if (aclc->hdr.typev1 == SMC_TYPE_D) {
156342042dbbSKarsten Graul ini->smcd_version = version;
1564a7c9c5f4SUrsula Braun rc = smc_connect_ism(smc, aclc, ini);
156542042dbbSKarsten Graul }
1566d70bf4f7SUrsula Braun if (rc)
1567d70bf4f7SUrsula Braun goto vlan_cleanup;
1568a046d57dSUrsula Braun
1569194730a9SGuvenc Gulce SMC_STAT_CLNT_SUCC_INC(sock_net(smc->clcsock->sk), aclc);
1570d70bf4f7SUrsula Braun smc_connect_ism_vlan_cleanup(smc, ini);
1571a7c9c5f4SUrsula Braun kfree(buf);
15723fc64937SUrsula Braun kfree(ini);
15733b2dec26SHans Wippel return 0;
1574d70bf4f7SUrsula Braun
1575d70bf4f7SUrsula Braun vlan_cleanup:
1576d70bf4f7SUrsula Braun smc_connect_ism_vlan_cleanup(smc, ini);
1577a7c9c5f4SUrsula Braun kfree(buf);
1578d70bf4f7SUrsula Braun fallback:
1579d70bf4f7SUrsula Braun kfree(ini);
1580e8d726c8SUrsula Braun return smc_connect_decline_fallback(smc, rc, version);
1581a046d57dSUrsula Braun }
1582a046d57dSUrsula Braun
smc_connect_work(struct work_struct * work)158324ac3a08SUrsula Braun static void smc_connect_work(struct work_struct *work)
158424ac3a08SUrsula Braun {
158524ac3a08SUrsula Braun struct smc_sock *smc = container_of(work, struct smc_sock,
158624ac3a08SUrsula Braun connect_work);
158750717a37SUrsula Braun long timeo = smc->sk.sk_sndtimeo;
158850717a37SUrsula Braun int rc = 0;
158924ac3a08SUrsula Braun
159050717a37SUrsula Braun if (!timeo)
159150717a37SUrsula Braun timeo = MAX_SCHEDULE_TIMEOUT;
159250717a37SUrsula Braun lock_sock(smc->clcsock->sk);
159324ac3a08SUrsula Braun if (smc->clcsock->sk->sk_err) {
159424ac3a08SUrsula Braun smc->sk.sk_err = smc->clcsock->sk->sk_err;
159550717a37SUrsula Braun } else if ((1 << smc->clcsock->sk->sk_state) &
1596f3a3a0feSWen Gu (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
159750717a37SUrsula Braun rc = sk_stream_wait_connect(smc->clcsock->sk, &timeo);
159850717a37SUrsula Braun if ((rc == -EPIPE) &&
159950717a37SUrsula Braun ((1 << smc->clcsock->sk->sk_state) &
160050717a37SUrsula Braun (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)))
160150717a37SUrsula Braun rc = 0;
160224ac3a08SUrsula Braun }
160350717a37SUrsula Braun release_sock(smc->clcsock->sk);
160450717a37SUrsula Braun lock_sock(&smc->sk);
160550717a37SUrsula Braun if (rc != 0 || smc->sk.sk_err) {
160650717a37SUrsula Braun smc->sk.sk_state = SMC_CLOSED;
160750717a37SUrsula Braun if (rc == -EPIPE || rc == -EAGAIN)
160850717a37SUrsula Braun smc->sk.sk_err = EPIPE;
16094e2e65e2Sliuyacan else if (rc == -ECONNREFUSED)
16104e2e65e2Sliuyacan smc->sk.sk_err = ECONNREFUSED;
161150717a37SUrsula Braun else if (signal_pending(current))
161250717a37SUrsula Braun smc->sk.sk_err = -sock_intr_errno(timeo);
16136d6dd528SUrsula Braun sock_put(&smc->sk); /* passive closing */
161424ac3a08SUrsula Braun goto out;
161524ac3a08SUrsula Braun }
161624ac3a08SUrsula Braun
161724ac3a08SUrsula Braun rc = __smc_connect(smc);
161824ac3a08SUrsula Braun if (rc < 0)
161924ac3a08SUrsula Braun smc->sk.sk_err = -rc;
162024ac3a08SUrsula Braun
162124ac3a08SUrsula Braun out:
162207603b23SUrsula Braun if (!sock_flag(&smc->sk, SOCK_DEAD)) {
162307603b23SUrsula Braun if (smc->sk.sk_err) {
162424ac3a08SUrsula Braun smc->sk.sk_state_change(&smc->sk);
162507603b23SUrsula Braun } else { /* allow polling before and after fallback decision */
162607603b23SUrsula Braun smc->clcsock->sk->sk_write_space(smc->clcsock->sk);
1627648a5a7aSUrsula Braun smc->sk.sk_write_space(&smc->sk);
162807603b23SUrsula Braun }
162907603b23SUrsula Braun }
163024ac3a08SUrsula Braun release_sock(&smc->sk);
163124ac3a08SUrsula Braun }
163224ac3a08SUrsula Braun
smc_connect(struct socket * sock,struct sockaddr * addr,int alen,int flags)1633ac713874SUrsula Braun static int smc_connect(struct socket *sock, struct sockaddr *addr,
1634ac713874SUrsula Braun int alen, int flags)
1635ac713874SUrsula Braun {
1636ac713874SUrsula Braun struct sock *sk = sock->sk;
1637ac713874SUrsula Braun struct smc_sock *smc;
1638ac713874SUrsula Braun int rc = -EINVAL;
1639ac713874SUrsula Braun
1640ac713874SUrsula Braun smc = smc_sk(sk);
1641ac713874SUrsula Braun
1642ac713874SUrsula Braun /* separate smc parameter checking to be safe */
1643ac713874SUrsula Braun if (alen < sizeof(addr->sa_family))
1644ac713874SUrsula Braun goto out_err;
1645aaa4d33fSKarsten Graul if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
1646ac713874SUrsula Braun goto out_err;
1647ac713874SUrsula Braun
1648ac713874SUrsula Braun lock_sock(sk);
16493aba1030SGuangguan Wang switch (sock->state) {
16503aba1030SGuangguan Wang default:
16513aba1030SGuangguan Wang rc = -EINVAL;
16523aba1030SGuangguan Wang goto out;
16533aba1030SGuangguan Wang case SS_CONNECTED:
16543aba1030SGuangguan Wang rc = sk->sk_state == SMC_ACTIVE ? -EISCONN : -EINVAL;
16553aba1030SGuangguan Wang goto out;
16563aba1030SGuangguan Wang case SS_CONNECTING:
16573aba1030SGuangguan Wang if (sk->sk_state == SMC_ACTIVE)
16583aba1030SGuangguan Wang goto connected;
16593aba1030SGuangguan Wang break;
16603aba1030SGuangguan Wang case SS_UNCONNECTED:
16613aba1030SGuangguan Wang sock->state = SS_CONNECTING;
16623aba1030SGuangguan Wang break;
16633aba1030SGuangguan Wang }
16643aba1030SGuangguan Wang
1665ac713874SUrsula Braun switch (sk->sk_state) {
1666ac713874SUrsula Braun default:
1667ac713874SUrsula Braun goto out;
16683aba1030SGuangguan Wang case SMC_CLOSED:
16693aba1030SGuangguan Wang rc = sock_error(sk) ? : -ECONNABORTED;
16703aba1030SGuangguan Wang sock->state = SS_UNCONNECTED;
16713aba1030SGuangguan Wang goto out;
1672ac713874SUrsula Braun case SMC_ACTIVE:
1673ac713874SUrsula Braun rc = -EISCONN;
1674ac713874SUrsula Braun goto out;
1675ac713874SUrsula Braun case SMC_INIT:
1676ac713874SUrsula Braun break;
1677ac713874SUrsula Braun }
1678ac713874SUrsula Braun
1679ac713874SUrsula Braun smc_copy_sock_settings_to_clc(smc);
1680c5c1cc9cSUrsula Braun tcp_sk(smc->clcsock->sk)->syn_smc = 1;
168150717a37SUrsula Braun if (smc->connect_nonblock) {
168224ac3a08SUrsula Braun rc = -EALREADY;
168324ac3a08SUrsula Braun goto out;
168424ac3a08SUrsula Braun }
168550717a37SUrsula Braun rc = kernel_connect(smc->clcsock, addr, alen, flags);
168650717a37SUrsula Braun if (rc && rc != -EINPROGRESS)
168724ac3a08SUrsula Braun goto out;
1688301428eaSUrsula Braun
16893aba1030SGuangguan Wang if (smc->use_fallback) {
16903aba1030SGuangguan Wang sock->state = rc ? SS_CONNECTING : SS_CONNECTED;
169186434744SUrsula Braun goto out;
16923aba1030SGuangguan Wang }
169375c1edf2Sliuyacan sock_hold(&smc->sk); /* sock put in passive closing */
169450717a37SUrsula Braun if (flags & O_NONBLOCK) {
169522ef473dSKarsten Graul if (queue_work(smc_hs_wq, &smc->connect_work))
169650717a37SUrsula Braun smc->connect_nonblock = 1;
169724ac3a08SUrsula Braun rc = -EINPROGRESS;
16983aba1030SGuangguan Wang goto out;
169924ac3a08SUrsula Braun } else {
17003b2dec26SHans Wippel rc = __smc_connect(smc);
1701a046d57dSUrsula Braun if (rc < 0)
1702a046d57dSUrsula Braun goto out;
170324ac3a08SUrsula Braun }
1704ac713874SUrsula Braun
17053aba1030SGuangguan Wang connected:
17063aba1030SGuangguan Wang rc = 0;
17073aba1030SGuangguan Wang sock->state = SS_CONNECTED;
1708ac713874SUrsula Braun out:
1709ac713874SUrsula Braun release_sock(sk);
1710ac713874SUrsula Braun out_err:
1711ac713874SUrsula Braun return rc;
1712ac713874SUrsula Braun }
1713ac713874SUrsula Braun
smc_clcsock_accept(struct smc_sock * lsmc,struct smc_sock ** new_smc)1714ac713874SUrsula Braun static int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
1715ac713874SUrsula Braun {
17163163c507SUrsula Braun struct socket *new_clcsock = NULL;
17173163c507SUrsula Braun struct sock *lsk = &lsmc->sk;
1718ac713874SUrsula Braun struct sock *new_sk;
171978abe3d0SMyungho Jung int rc = -EINVAL;
1720ac713874SUrsula Braun
17213163c507SUrsula Braun release_sock(lsk);
1722aaa4d33fSKarsten Graul new_sk = smc_sock_alloc(sock_net(lsk), NULL, lsk->sk_protocol);
1723ac713874SUrsula Braun if (!new_sk) {
1724ac713874SUrsula Braun rc = -ENOMEM;
17253163c507SUrsula Braun lsk->sk_err = ENOMEM;
1726ac713874SUrsula Braun *new_smc = NULL;
17273163c507SUrsula Braun lock_sock(lsk);
1728ac713874SUrsula Braun goto out;
1729ac713874SUrsula Braun }
1730ac713874SUrsula Braun *new_smc = smc_sk(new_sk);
1731ac713874SUrsula Braun
173278abe3d0SMyungho Jung mutex_lock(&lsmc->clcsock_release_lock);
173378abe3d0SMyungho Jung if (lsmc->clcsock)
1734a60a2b1eSUrsula Braun rc = kernel_accept(lsmc->clcsock, &new_clcsock, SOCK_NONBLOCK);
173578abe3d0SMyungho Jung mutex_unlock(&lsmc->clcsock_release_lock);
17363163c507SUrsula Braun lock_sock(lsk);
1737a60a2b1eSUrsula Braun if (rc < 0 && rc != -EAGAIN)
17383163c507SUrsula Braun lsk->sk_err = -rc;
173935a6b178SUrsula Braun if (rc < 0 || lsk->sk_state == SMC_CLOSED) {
1740f61bca58SUrsula Braun new_sk->sk_prot->unhash(new_sk);
1741a046d57dSUrsula Braun if (new_clcsock)
1742a046d57dSUrsula Braun sock_release(new_clcsock);
1743a046d57dSUrsula Braun new_sk->sk_state = SMC_CLOSED;
1744afc6a04fSD. Wythe smc_sock_set_flag(new_sk, SOCK_DEAD);
174551f1de79SUrsula Braun sock_put(new_sk); /* final */
1746ac713874SUrsula Braun *new_smc = NULL;
1747ac713874SUrsula Braun goto out;
1748ac713874SUrsula Braun }
1749ac713874SUrsula Braun
1750a60a2b1eSUrsula Braun /* new clcsock has inherited the smc listen-specific sk_data_ready
1751a60a2b1eSUrsula Braun * function; switch it back to the original sk_data_ready function
1752a60a2b1eSUrsula Braun */
1753a60a2b1eSUrsula Braun new_clcsock->sk->sk_data_ready = lsmc->clcsk_data_ready;
175497b9af7aSWen Gu
175597b9af7aSWen Gu /* if new clcsock has also inherited the fallback-specific callback
175697b9af7aSWen Gu * functions, switch them back to the original ones.
175797b9af7aSWen Gu */
175897b9af7aSWen Gu if (lsmc->use_fallback) {
175997b9af7aSWen Gu if (lsmc->clcsk_state_change)
176097b9af7aSWen Gu new_clcsock->sk->sk_state_change = lsmc->clcsk_state_change;
176197b9af7aSWen Gu if (lsmc->clcsk_write_space)
176297b9af7aSWen Gu new_clcsock->sk->sk_write_space = lsmc->clcsk_write_space;
176397b9af7aSWen Gu if (lsmc->clcsk_error_report)
176497b9af7aSWen Gu new_clcsock->sk->sk_error_report = lsmc->clcsk_error_report;
176597b9af7aSWen Gu }
176697b9af7aSWen Gu
1767ac713874SUrsula Braun (*new_smc)->clcsock = new_clcsock;
1768ac713874SUrsula Braun out:
1769ac713874SUrsula Braun return rc;
1770ac713874SUrsula Braun }
1771ac713874SUrsula Braun
1772a046d57dSUrsula Braun /* add a just created sock to the accept queue of the listen sock as
1773a046d57dSUrsula Braun * candidate for a following socket accept call from user space
1774a046d57dSUrsula Braun */
smc_accept_enqueue(struct sock * parent,struct sock * sk)1775a046d57dSUrsula Braun static void smc_accept_enqueue(struct sock *parent, struct sock *sk)
1776a046d57dSUrsula Braun {
1777a046d57dSUrsula Braun struct smc_sock *par = smc_sk(parent);
1778a046d57dSUrsula Braun
177951f1de79SUrsula Braun sock_hold(sk); /* sock_put in smc_accept_unlink () */
1780a046d57dSUrsula Braun spin_lock(&par->accept_q_lock);
1781a046d57dSUrsula Braun list_add_tail(&smc_sk(sk)->accept_q, &par->accept_q);
1782a046d57dSUrsula Braun spin_unlock(&par->accept_q_lock);
1783a046d57dSUrsula Braun sk_acceptq_added(parent);
1784a046d57dSUrsula Braun }
1785a046d57dSUrsula Braun
1786a046d57dSUrsula Braun /* remove a socket from the accept queue of its parental listening socket */
smc_accept_unlink(struct sock * sk)1787a046d57dSUrsula Braun static void smc_accept_unlink(struct sock *sk)
1788a046d57dSUrsula Braun {
1789a046d57dSUrsula Braun struct smc_sock *par = smc_sk(sk)->listen_smc;
1790a046d57dSUrsula Braun
1791a046d57dSUrsula Braun spin_lock(&par->accept_q_lock);
1792a046d57dSUrsula Braun list_del_init(&smc_sk(sk)->accept_q);
1793a046d57dSUrsula Braun spin_unlock(&par->accept_q_lock);
1794a046d57dSUrsula Braun sk_acceptq_removed(&smc_sk(sk)->listen_smc->sk);
179551f1de79SUrsula Braun sock_put(sk); /* sock_hold in smc_accept_enqueue */
1796a046d57dSUrsula Braun }
1797a046d57dSUrsula Braun
1798a046d57dSUrsula Braun /* remove a sock from the accept queue to bind it to a new socket created
1799a046d57dSUrsula Braun * for a socket accept call from user space
1800a046d57dSUrsula Braun */
smc_accept_dequeue(struct sock * parent,struct socket * new_sock)1801b38d7324SUrsula Braun struct sock *smc_accept_dequeue(struct sock *parent,
1802a046d57dSUrsula Braun struct socket *new_sock)
1803a046d57dSUrsula Braun {
1804a046d57dSUrsula Braun struct smc_sock *isk, *n;
1805a046d57dSUrsula Braun struct sock *new_sk;
1806a046d57dSUrsula Braun
1807a046d57dSUrsula Braun list_for_each_entry_safe(isk, n, &smc_sk(parent)->accept_q, accept_q) {
1808a046d57dSUrsula Braun new_sk = (struct sock *)isk;
1809a046d57dSUrsula Braun
1810a046d57dSUrsula Braun smc_accept_unlink(new_sk);
1811a046d57dSUrsula Braun if (new_sk->sk_state == SMC_CLOSED) {
1812f61bca58SUrsula Braun new_sk->sk_prot->unhash(new_sk);
1813127f4970SUrsula Braun if (isk->clcsock) {
1814127f4970SUrsula Braun sock_release(isk->clcsock);
1815127f4970SUrsula Braun isk->clcsock = NULL;
1816127f4970SUrsula Braun }
181751f1de79SUrsula Braun sock_put(new_sk); /* final */
1818a046d57dSUrsula Braun continue;
1819a046d57dSUrsula Braun }
182007603b23SUrsula Braun if (new_sock) {
1821a046d57dSUrsula Braun sock_graft(new_sk, new_sock);
18223aba1030SGuangguan Wang new_sock->state = SS_CONNECTED;
182307603b23SUrsula Braun if (isk->use_fallback) {
182407603b23SUrsula Braun smc_sk(new_sk)->clcsock->file = new_sock->file;
182507603b23SUrsula Braun isk->clcsock->file->private_data = isk->clcsock;
182607603b23SUrsula Braun }
182707603b23SUrsula Braun }
1828a046d57dSUrsula Braun return new_sk;
1829a046d57dSUrsula Braun }
1830a046d57dSUrsula Braun return NULL;
1831a046d57dSUrsula Braun }
1832a046d57dSUrsula Braun
1833a046d57dSUrsula Braun /* clean up for a created but never accepted sock */
smc_close_non_accepted(struct sock * sk)1834b38d7324SUrsula Braun void smc_close_non_accepted(struct sock *sk)
1835a046d57dSUrsula Braun {
1836a046d57dSUrsula Braun struct smc_sock *smc = smc_sk(sk);
1837a046d57dSUrsula Braun
183881cf4f47SUrsula Braun sock_hold(sk); /* sock_put below */
1839b38d7324SUrsula Braun lock_sock(sk);
1840b38d7324SUrsula Braun if (!sk->sk_lingertime)
1841b38d7324SUrsula Braun /* wait for peer closing */
1842bc1fb82aSEric Dumazet WRITE_ONCE(sk->sk_lingertime, SMC_MAX_STREAM_WAIT_TIMEOUT);
184339f41f36SUrsula Braun __smc_release(smc);
1844b38d7324SUrsula Braun release_sock(sk);
184581cf4f47SUrsula Braun sock_put(sk); /* sock_hold above */
184651f1de79SUrsula Braun sock_put(sk); /* final sock_put */
1847a046d57dSUrsula Braun }
1848a046d57dSUrsula Braun
smcr_serv_conf_first_link(struct smc_sock * smc)1849b9247544SKarsten Graul static int smcr_serv_conf_first_link(struct smc_sock *smc)
18509bf9abeaSUrsula Braun {
1851387707fdSKarsten Graul struct smc_link *link = smc->conn.lnk;
18524667bb4aSKarsten Graul struct smc_llc_qentry *qentry;
18539bf9abeaSUrsula Braun int rc;
18549bf9abeaSUrsula Braun
1855b8d19945SWen Gu /* reg the sndbuf if it was vzalloced*/
1856b8d19945SWen Gu if (smc->conn.sndbuf_desc->is_vm) {
1857b8d19945SWen Gu if (smcr_link_reg_buf(link, smc->conn.sndbuf_desc))
1858b8d19945SWen Gu return SMC_CLC_DECL_ERR_REGBUF;
1859b8d19945SWen Gu }
1860b8d19945SWen Gu
1861b8d19945SWen Gu /* reg the rmb */
1862b8d19945SWen Gu if (smcr_link_reg_buf(link, smc->conn.rmb_desc))
1863b8d19945SWen Gu return SMC_CLC_DECL_ERR_REGBUF;
1864652a1e41SUrsula Braun
18659bf9abeaSUrsula Braun /* send CONFIRM LINK request to client over the RoCE fabric */
1866947541f3SUrsula Braun rc = smc_llc_send_confirm_link(link, SMC_LLC_REQ);
18679bf9abeaSUrsula Braun if (rc < 0)
1868603cc149SKarsten Graul return SMC_CLC_DECL_TIMEOUT_CL;
18699bf9abeaSUrsula Braun
18709bf9abeaSUrsula Braun /* receive CONFIRM LINK response from client over the RoCE fabric */
18714667bb4aSKarsten Graul qentry = smc_llc_wait(link->lgr, link, SMC_LLC_WAIT_TIME,
18724667bb4aSKarsten Graul SMC_LLC_CONFIRM_LINK);
18734667bb4aSKarsten Graul if (!qentry) {
18749bf9abeaSUrsula Braun struct smc_clc_msg_decline dclc;
18759bf9abeaSUrsula Braun
18769bf9abeaSUrsula Braun rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
18772b59f58eSUrsula Braun SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
18789ed28556SUrsula Braun return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
18799bf9abeaSUrsula Braun }
1880649758ffSKarsten Graul smc_llc_save_peer_uid(qentry);
18814667bb4aSKarsten Graul rc = smc_llc_eval_conf_link(qentry, SMC_LLC_RESP);
18824667bb4aSKarsten Graul smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl);
18834667bb4aSKarsten Graul if (rc)
188475d320d6SKarsten Graul return SMC_CLC_DECL_RMBE_EC;
188575d320d6SKarsten Graul
18864667bb4aSKarsten Graul /* confirm_rkey is implicit on 1st contact */
18874667bb4aSKarsten Graul smc->conn.rmb_desc->is_conf_rkey = true;
188852bedf37SKarsten Graul
188900a049cfSKarsten Graul smc_llc_link_active(link);
18900a99be43SKarsten Graul smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE);
189152bedf37SKarsten Graul
189269b888e3SGuangguan Wang if (link->lgr->max_links > 1) {
1893d1fabc68SJakub Kicinski down_write(&link->lgr->llc_conf_mutex);
18944667bb4aSKarsten Graul /* initial contact - try to establish second link */
1895b4ba4652SKarsten Graul smc_llc_srv_add_link(link, NULL);
1896d1fabc68SJakub Kicinski up_write(&link->lgr->llc_conf_mutex);
189769b888e3SGuangguan Wang }
189875d320d6SKarsten Graul return 0;
18999bf9abeaSUrsula Braun }
19009bf9abeaSUrsula Braun
19013b2dec26SHans Wippel /* listen worker: finish */
smc_listen_out(struct smc_sock * new_smc)19023b2dec26SHans Wippel static void smc_listen_out(struct smc_sock *new_smc)
1903a046d57dSUrsula Braun {
1904a046d57dSUrsula Braun struct smc_sock *lsmc = new_smc->listen_smc;
1905a046d57dSUrsula Braun struct sock *newsmcsk = &new_smc->sk;
1906a046d57dSUrsula Braun
19078270d9c2SD. Wythe if (tcp_sk(new_smc->clcsock->sk)->syn_smc)
19088270d9c2SD. Wythe atomic_dec(&lsmc->queued_smc_hs);
19098270d9c2SD. Wythe
1910673d6066SWen Gu release_sock(newsmcsk); /* lock in smc_listen_work() */
1911a046d57dSUrsula Braun if (lsmc->sk.sk_state == SMC_LISTEN) {
1912fd57770dSKarsten Graul lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
1913a046d57dSUrsula Braun smc_accept_enqueue(&lsmc->sk, newsmcsk);
1914fd57770dSKarsten Graul release_sock(&lsmc->sk);
1915a046d57dSUrsula Braun } else { /* no longer listening */
1916a046d57dSUrsula Braun smc_close_non_accepted(newsmcsk);
1917a046d57dSUrsula Braun }
1918a046d57dSUrsula Braun
1919a046d57dSUrsula Braun /* Wake up accept */
1920a046d57dSUrsula Braun lsmc->sk.sk_data_ready(&lsmc->sk);
1921a046d57dSUrsula Braun sock_put(&lsmc->sk); /* sock_hold in smc_tcp_listen_work */
1922a046d57dSUrsula Braun }
1923a046d57dSUrsula Braun
19243b2dec26SHans Wippel /* listen worker: finish in state connected */
smc_listen_out_connected(struct smc_sock * new_smc)19253b2dec26SHans Wippel static void smc_listen_out_connected(struct smc_sock *new_smc)
19263b2dec26SHans Wippel {
19273b2dec26SHans Wippel struct sock *newsmcsk = &new_smc->sk;
19283b2dec26SHans Wippel
19293b2dec26SHans Wippel if (newsmcsk->sk_state == SMC_INIT)
19303b2dec26SHans Wippel newsmcsk->sk_state = SMC_ACTIVE;
19313b2dec26SHans Wippel
19323b2dec26SHans Wippel smc_listen_out(new_smc);
19333b2dec26SHans Wippel }
19343b2dec26SHans Wippel
19353b2dec26SHans Wippel /* listen worker: finish in error state */
smc_listen_out_err(struct smc_sock * new_smc)19363b2dec26SHans Wippel static void smc_listen_out_err(struct smc_sock *new_smc)
19373b2dec26SHans Wippel {
19383b2dec26SHans Wippel struct sock *newsmcsk = &new_smc->sk;
1939194730a9SGuvenc Gulce struct net *net = sock_net(newsmcsk);
19403b2dec26SHans Wippel
1941194730a9SGuvenc Gulce this_cpu_inc(net->smc.smc_stats->srv_hshake_err_cnt);
194251f1de79SUrsula Braun if (newsmcsk->sk_state == SMC_INIT)
194351f1de79SUrsula Braun sock_put(&new_smc->sk); /* passive closing */
1944a046d57dSUrsula Braun newsmcsk->sk_state = SMC_CLOSED;
19453b2dec26SHans Wippel
19463b2dec26SHans Wippel smc_listen_out(new_smc);
19473b2dec26SHans Wippel }
19483b2dec26SHans Wippel
19493b2dec26SHans Wippel /* listen worker: decline and fall back if possible */
smc_listen_decline(struct smc_sock * new_smc,int reason_code,int local_first,u8 version)19503b2dec26SHans Wippel static void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
19514a9baf45SKarsten Graul int local_first, u8 version)
19523b2dec26SHans Wippel {
19533b2dec26SHans Wippel /* RDMA setup failed, switch back to TCP */
19548cf3f3e4SKarsten Graul smc_conn_abort(new_smc, local_first);
1955c0bf3d8aSWen Gu if (reason_code < 0 ||
1956c0bf3d8aSWen Gu smc_switch_to_fallback(new_smc, reason_code)) {
1957c0bf3d8aSWen Gu /* error, no fallback possible */
19583b2dec26SHans Wippel smc_listen_out_err(new_smc);
19593b2dec26SHans Wippel return;
19603b2dec26SHans Wippel }
1961603cc149SKarsten Graul if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
1962e8d726c8SUrsula Braun if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {
19633b2dec26SHans Wippel smc_listen_out_err(new_smc);
19643b2dec26SHans Wippel return;
19653b2dec26SHans Wippel }
19663b2dec26SHans Wippel }
19673b2dec26SHans Wippel smc_listen_out_connected(new_smc);
19683b2dec26SHans Wippel }
19693b2dec26SHans Wippel
19705c21c4ccSUrsula Braun /* listen worker: version checking */
smc_listen_v2_check(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc,struct smc_init_info * ini)19715c21c4ccSUrsula Braun static int smc_listen_v2_check(struct smc_sock *new_smc,
19725c21c4ccSUrsula Braun struct smc_clc_msg_proposal *pclc,
19735c21c4ccSUrsula Braun struct smc_init_info *ini)
19745c21c4ccSUrsula Braun {
19755c21c4ccSUrsula Braun struct smc_clc_smcd_v2_extension *pclc_smcd_v2_ext;
19765c21c4ccSUrsula Braun struct smc_clc_v2_extension *pclc_v2_ext;
19773752404aSKarsten Graul int rc = SMC_CLC_DECL_PEERNOSMC;
19785c21c4ccSUrsula Braun
19795c21c4ccSUrsula Braun ini->smc_type_v1 = pclc->hdr.typev1;
19805c21c4ccSUrsula Braun ini->smc_type_v2 = pclc->hdr.typev2;
1981e49300a6SKarsten Graul ini->smcd_version = smcd_indicated(ini->smc_type_v1) ? SMC_V1 : 0;
1982e49300a6SKarsten Graul ini->smcr_version = smcr_indicated(ini->smc_type_v1) ? SMC_V1 : 0;
1983e49300a6SKarsten Graul if (pclc->hdr.version > SMC_V1) {
1984e49300a6SKarsten Graul if (smcd_indicated(ini->smc_type_v2))
1985e49300a6SKarsten Graul ini->smcd_version |= SMC_V2;
1986e49300a6SKarsten Graul if (smcr_indicated(ini->smc_type_v2))
1987e49300a6SKarsten Graul ini->smcr_version |= SMC_V2;
19883752404aSKarsten Graul }
1989e49300a6SKarsten Graul if (!(ini->smcd_version & SMC_V2) && !(ini->smcr_version & SMC_V2)) {
1990e49300a6SKarsten Graul rc = SMC_CLC_DECL_PEERNOSMC;
19915c21c4ccSUrsula Braun goto out;
19925c21c4ccSUrsula Braun }
19935c21c4ccSUrsula Braun pclc_v2_ext = smc_get_clc_v2_ext(pclc);
19945c21c4ccSUrsula Braun if (!pclc_v2_ext) {
19955c21c4ccSUrsula Braun ini->smcd_version &= ~SMC_V2;
1996e49300a6SKarsten Graul ini->smcr_version &= ~SMC_V2;
19973752404aSKarsten Graul rc = SMC_CLC_DECL_NOV2EXT;
19985c21c4ccSUrsula Braun goto out;
19995c21c4ccSUrsula Braun }
20005c21c4ccSUrsula Braun pclc_smcd_v2_ext = smc_get_clc_smcd_v2_ext(pclc_v2_ext);
2001e49300a6SKarsten Graul if (ini->smcd_version & SMC_V2) {
2002e49300a6SKarsten Graul if (!smc_ism_is_v2_capable()) {
2003e49300a6SKarsten Graul ini->smcd_version &= ~SMC_V2;
2004e49300a6SKarsten Graul rc = SMC_CLC_DECL_NOISM2SUPP;
2005e49300a6SKarsten Graul } else if (!pclc_smcd_v2_ext) {
20065c21c4ccSUrsula Braun ini->smcd_version &= ~SMC_V2;
20073752404aSKarsten Graul rc = SMC_CLC_DECL_NOV2DEXT;
2008e49300a6SKarsten Graul } else if (!pclc_v2_ext->hdr.eid_cnt &&
2009e49300a6SKarsten Graul !pclc_v2_ext->hdr.flag.seid) {
2010e49300a6SKarsten Graul ini->smcd_version &= ~SMC_V2;
2011e49300a6SKarsten Graul rc = SMC_CLC_DECL_NOUEID;
2012e49300a6SKarsten Graul }
2013e49300a6SKarsten Graul }
2014e49300a6SKarsten Graul if (ini->smcr_version & SMC_V2) {
2015e49300a6SKarsten Graul if (!pclc_v2_ext->hdr.eid_cnt) {
2016e49300a6SKarsten Graul ini->smcr_version &= ~SMC_V2;
2017e49300a6SKarsten Graul rc = SMC_CLC_DECL_NOUEID;
2018e49300a6SKarsten Graul }
20193752404aSKarsten Graul }
20205c21c4ccSUrsula Braun
20211e700948SGuangguan Wang ini->release_nr = pclc_v2_ext->hdr.flag.release;
20221e700948SGuangguan Wang if (pclc_v2_ext->hdr.flag.release > SMC_RELEASE)
20231e700948SGuangguan Wang ini->release_nr = SMC_RELEASE;
20241e700948SGuangguan Wang
20255c21c4ccSUrsula Braun out:
2026e49300a6SKarsten Graul if (!ini->smcd_version && !ini->smcr_version)
20273752404aSKarsten Graul return rc;
20285c21c4ccSUrsula Braun
20295c21c4ccSUrsula Braun return 0;
20305c21c4ccSUrsula Braun }
20315c21c4ccSUrsula Braun
20323b2dec26SHans Wippel /* listen worker: check prefixes */
smc_listen_prfx_check(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc)203359886697SKarsten Graul static int smc_listen_prfx_check(struct smc_sock *new_smc,
20343b2dec26SHans Wippel struct smc_clc_msg_proposal *pclc)
20353b2dec26SHans Wippel {
20363b2dec26SHans Wippel struct smc_clc_msg_proposal_prefix *pclc_prfx;
20373b2dec26SHans Wippel struct socket *newclcsock = new_smc->clcsock;
20383b2dec26SHans Wippel
20395c21c4ccSUrsula Braun if (pclc->hdr.typev1 == SMC_TYPE_N)
20405c21c4ccSUrsula Braun return 0;
20413b2dec26SHans Wippel pclc_prfx = smc_clc_proposal_get_prefix(pclc);
204291a7c27cSGuangguan Wang if (!pclc_prfx)
204391a7c27cSGuangguan Wang return -EPROTO;
20443b2dec26SHans Wippel if (smc_clc_prfx_match(newclcsock, pclc_prfx))
204559886697SKarsten Graul return SMC_CLC_DECL_DIFFPREFIX;
20463b2dec26SHans Wippel
20473b2dec26SHans Wippel return 0;
20483b2dec26SHans Wippel }
20493b2dec26SHans Wippel
20503b2dec26SHans Wippel /* listen worker: initialize connection and buffers */
smc_listen_rdma_init(struct smc_sock * new_smc,struct smc_init_info * ini)20513b2dec26SHans Wippel static int smc_listen_rdma_init(struct smc_sock *new_smc,
20527a62725aSKarsten Graul struct smc_init_info *ini)
20533b2dec26SHans Wippel {
20547a62725aSKarsten Graul int rc;
20557a62725aSKarsten Graul
20563b2dec26SHans Wippel /* allocate connection / link group */
20577a62725aSKarsten Graul rc = smc_conn_create(new_smc, ini);
20587a62725aSKarsten Graul if (rc)
20597a62725aSKarsten Graul return rc;
20603b2dec26SHans Wippel
20613b2dec26SHans Wippel /* create send buffer and rmb */
206235112271SWen Gu if (smc_buf_create(new_smc, false)) {
206335112271SWen Gu smc_conn_abort(new_smc, ini->first_contact_local);
20643b2dec26SHans Wippel return SMC_CLC_DECL_MEM;
206535112271SWen Gu }
20663b2dec26SHans Wippel
20673b2dec26SHans Wippel return 0;
20683b2dec26SHans Wippel }
20693b2dec26SHans Wippel
207041349844SHans Wippel /* listen worker: initialize connection and buffers for SMC-D */
smc_listen_ism_init(struct smc_sock * new_smc,struct smc_init_info * ini)207141349844SHans Wippel static int smc_listen_ism_init(struct smc_sock *new_smc,
20727a62725aSKarsten Graul struct smc_init_info *ini)
207341349844SHans Wippel {
20747a62725aSKarsten Graul int rc;
207541349844SHans Wippel
20767a62725aSKarsten Graul rc = smc_conn_create(new_smc, ini);
20777a62725aSKarsten Graul if (rc)
20787a62725aSKarsten Graul return rc;
207941349844SHans Wippel
208041349844SHans Wippel /* Create send and receive buffers */
208172b7f6c4SKarsten Graul rc = smc_buf_create(new_smc, true);
208272b7f6c4SKarsten Graul if (rc) {
20838cf3f3e4SKarsten Graul smc_conn_abort(new_smc, ini->first_contact_local);
208472b7f6c4SKarsten Graul return (rc == -ENOSPC) ? SMC_CLC_DECL_MAX_DMB :
208572b7f6c4SKarsten Graul SMC_CLC_DECL_MEM;
208641349844SHans Wippel }
208741349844SHans Wippel
208841349844SHans Wippel return 0;
208941349844SHans Wippel }
209041349844SHans Wippel
smc_is_already_selected(struct smcd_dev * smcd,struct smc_init_info * ini,int matches)20915c21c4ccSUrsula Braun static bool smc_is_already_selected(struct smcd_dev *smcd,
20925c21c4ccSUrsula Braun struct smc_init_info *ini,
20935c21c4ccSUrsula Braun int matches)
20945c21c4ccSUrsula Braun {
20955c21c4ccSUrsula Braun int i;
20965c21c4ccSUrsula Braun
20975c21c4ccSUrsula Braun for (i = 0; i < matches; i++)
20985c21c4ccSUrsula Braun if (smcd == ini->ism_dev[i])
20995c21c4ccSUrsula Braun return true;
21005c21c4ccSUrsula Braun
21015c21c4ccSUrsula Braun return false;
21025c21c4ccSUrsula Braun }
21035c21c4ccSUrsula Braun
21045c21c4ccSUrsula Braun /* check for ISM devices matching proposed ISM devices */
smc_check_ism_v2_match(struct smc_init_info * ini,u16 proposed_chid,struct smcd_gid * proposed_gid,unsigned int * matches)21055c21c4ccSUrsula Braun static void smc_check_ism_v2_match(struct smc_init_info *ini,
21067e5ef8ebSWen Gu u16 proposed_chid,
21077e5ef8ebSWen Gu struct smcd_gid *proposed_gid,
21085c21c4ccSUrsula Braun unsigned int *matches)
21095c21c4ccSUrsula Braun {
21105c21c4ccSUrsula Braun struct smcd_dev *smcd;
21115c21c4ccSUrsula Braun
21125c21c4ccSUrsula Braun list_for_each_entry(smcd, &smcd_dev_list.list, list) {
21135c21c4ccSUrsula Braun if (smcd->going_away)
21145c21c4ccSUrsula Braun continue;
21155c21c4ccSUrsula Braun if (smc_is_already_selected(smcd, ini, *matches))
21165c21c4ccSUrsula Braun continue;
21175c21c4ccSUrsula Braun if (smc_ism_get_chid(smcd) == proposed_chid &&
21185c21c4ccSUrsula Braun !smc_ism_cantalk(proposed_gid, ISM_RESERVED_VLANID, smcd)) {
21197e5ef8ebSWen Gu ini->ism_peer_gid[*matches].gid = proposed_gid->gid;
21207e5ef8ebSWen Gu if (__smc_ism_is_virtual(proposed_chid))
21217e5ef8ebSWen Gu ini->ism_peer_gid[*matches].gid_ext =
21227e5ef8ebSWen Gu proposed_gid->gid_ext;
21237e5ef8ebSWen Gu /* non-virtual ISM's peer gid_ext remains 0. */
21245c21c4ccSUrsula Braun ini->ism_dev[*matches] = smcd;
21255c21c4ccSUrsula Braun (*matches)++;
21265c21c4ccSUrsula Braun break;
21275c21c4ccSUrsula Braun }
21285c21c4ccSUrsula Braun }
21295c21c4ccSUrsula Braun }
21305c21c4ccSUrsula Braun
smc_find_ism_store_rc(u32 rc,struct smc_init_info * ini)21313752404aSKarsten Graul static void smc_find_ism_store_rc(u32 rc, struct smc_init_info *ini)
21323752404aSKarsten Graul {
21333752404aSKarsten Graul if (!ini->rc)
21343752404aSKarsten Graul ini->rc = rc;
21353752404aSKarsten Graul }
21363752404aSKarsten Graul
smc_find_ism_v2_device_serv(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc,struct smc_init_info * ini)21375c21c4ccSUrsula Braun static void smc_find_ism_v2_device_serv(struct smc_sock *new_smc,
21385c21c4ccSUrsula Braun struct smc_clc_msg_proposal *pclc,
21395c21c4ccSUrsula Braun struct smc_init_info *ini)
21405c21c4ccSUrsula Braun {
21415c21c4ccSUrsula Braun struct smc_clc_smcd_v2_extension *smcd_v2_ext;
21425c21c4ccSUrsula Braun struct smc_clc_v2_extension *smc_v2_ext;
21435c21c4ccSUrsula Braun struct smc_clc_msg_smcd *pclc_smcd;
21445c21c4ccSUrsula Braun unsigned int matches = 0;
21457e5ef8ebSWen Gu struct smcd_gid smcd_gid;
2146f29fa003SKarsten Graul u8 smcd_version;
21475c21c4ccSUrsula Braun u8 *eid = NULL;
21483752404aSKarsten Graul int i, rc;
21497e5ef8ebSWen Gu u16 chid;
21505c21c4ccSUrsula Braun
21515c21c4ccSUrsula Braun if (!(ini->smcd_version & SMC_V2) || !smcd_indicated(ini->smc_type_v2))
2152f29fa003SKarsten Graul goto not_found;
21535c21c4ccSUrsula Braun
21545c21c4ccSUrsula Braun pclc_smcd = smc_get_clc_msg_smcd(pclc);
21555c21c4ccSUrsula Braun smc_v2_ext = smc_get_clc_v2_ext(pclc);
21565c21c4ccSUrsula Braun smcd_v2_ext = smc_get_clc_smcd_v2_ext(smc_v2_ext);
2157935caf32SGuangguan Wang if (!pclc_smcd || !smc_v2_ext || !smcd_v2_ext)
2158935caf32SGuangguan Wang goto not_found;
21595c21c4ccSUrsula Braun
21605c21c4ccSUrsula Braun mutex_lock(&smcd_dev_list.mutex);
21617e5ef8ebSWen Gu if (pclc_smcd->ism.chid) {
21625c21c4ccSUrsula Braun /* check for ISM device matching proposed native ISM device */
21637e5ef8ebSWen Gu smcd_gid.gid = ntohll(pclc_smcd->ism.gid);
21647e5ef8ebSWen Gu smcd_gid.gid_ext = 0;
21655c21c4ccSUrsula Braun smc_check_ism_v2_match(ini, ntohs(pclc_smcd->ism.chid),
21667e5ef8ebSWen Gu &smcd_gid, &matches);
21677e5ef8ebSWen Gu }
21687e5ef8ebSWen Gu for (i = 0; i < smc_v2_ext->hdr.ism_gid_cnt; i++) {
21695c21c4ccSUrsula Braun /* check for ISM devices matching proposed non-native ISM
21705c21c4ccSUrsula Braun * devices
21715c21c4ccSUrsula Braun */
21727e5ef8ebSWen Gu smcd_gid.gid = ntohll(smcd_v2_ext->gidchid[i].gid);
21737e5ef8ebSWen Gu smcd_gid.gid_ext = 0;
21747e5ef8ebSWen Gu chid = ntohs(smcd_v2_ext->gidchid[i].chid);
21757e5ef8ebSWen Gu if (__smc_ism_is_virtual(chid)) {
21767e5ef8ebSWen Gu if ((i + 1) == smc_v2_ext->hdr.ism_gid_cnt ||
21777e5ef8ebSWen Gu chid != ntohs(smcd_v2_ext->gidchid[i + 1].chid))
21787e5ef8ebSWen Gu /* each virtual ISM device takes two GID-CHID
21797e5ef8ebSWen Gu * entries and CHID of the second entry repeats
21807e5ef8ebSWen Gu * that of the first entry.
21817e5ef8ebSWen Gu *
21827e5ef8ebSWen Gu * So check if the next GID-CHID entry exists
21837e5ef8ebSWen Gu * and both two entries' CHIDs are the same.
21847e5ef8ebSWen Gu */
21857e5ef8ebSWen Gu continue;
21867e5ef8ebSWen Gu smcd_gid.gid_ext =
21877e5ef8ebSWen Gu ntohll(smcd_v2_ext->gidchid[++i].gid);
21887e5ef8ebSWen Gu }
21897e5ef8ebSWen Gu smc_check_ism_v2_match(ini, chid, &smcd_gid, &matches);
21905c21c4ccSUrsula Braun }
21915c21c4ccSUrsula Braun mutex_unlock(&smcd_dev_list.mutex);
21925c21c4ccSUrsula Braun
2193e49300a6SKarsten Graul if (!ini->ism_dev[0]) {
2194e49300a6SKarsten Graul smc_find_ism_store_rc(SMC_CLC_DECL_NOSMCD2DEV, ini);
2195fa086662SKarsten Graul goto not_found;
2196e49300a6SKarsten Graul }
2197fa086662SKarsten Graul
219811a26c59SKarsten Graul smc_ism_get_system_eid(&eid);
2199fa086662SKarsten Graul if (!smc_clc_match_eid(ini->negotiated_eid, smc_v2_ext,
2200fa086662SKarsten Graul smcd_v2_ext->system_eid, eid))
22015c21c4ccSUrsula Braun goto not_found;
22025c21c4ccSUrsula Braun
22035c21c4ccSUrsula Braun /* separate - outside the smcd_dev_list.lock */
2204f29fa003SKarsten Graul smcd_version = ini->smcd_version;
22055c21c4ccSUrsula Braun for (i = 0; i < matches; i++) {
22065c21c4ccSUrsula Braun ini->smcd_version = SMC_V2;
22075c21c4ccSUrsula Braun ini->is_smcd = true;
22085c21c4ccSUrsula Braun ini->ism_selected = i;
22093752404aSKarsten Graul rc = smc_listen_ism_init(new_smc, ini);
22103752404aSKarsten Graul if (rc) {
22113752404aSKarsten Graul smc_find_ism_store_rc(rc, ini);
22125c21c4ccSUrsula Braun /* try next active ISM device */
22135c21c4ccSUrsula Braun continue;
22143752404aSKarsten Graul }
22155c21c4ccSUrsula Braun return; /* matching and usable V2 ISM device found */
22165c21c4ccSUrsula Braun }
2217f29fa003SKarsten Graul /* no V2 ISM device could be initialized */
2218f29fa003SKarsten Graul ini->smcd_version = smcd_version; /* restore original value */
2219fa086662SKarsten Graul ini->negotiated_eid[0] = 0;
22205c21c4ccSUrsula Braun
22215c21c4ccSUrsula Braun not_found:
22225c21c4ccSUrsula Braun ini->smcd_version &= ~SMC_V2;
22235c21c4ccSUrsula Braun ini->ism_dev[0] = NULL;
22245c21c4ccSUrsula Braun ini->is_smcd = false;
22255c21c4ccSUrsula Braun }
22265c21c4ccSUrsula Braun
smc_find_ism_v1_device_serv(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc,struct smc_init_info * ini)22275c21c4ccSUrsula Braun static void smc_find_ism_v1_device_serv(struct smc_sock *new_smc,
22287affc809SUrsula Braun struct smc_clc_msg_proposal *pclc,
22297affc809SUrsula Braun struct smc_init_info *ini)
22307affc809SUrsula Braun {
22317affc809SUrsula Braun struct smc_clc_msg_smcd *pclc_smcd = smc_get_clc_msg_smcd(pclc);
22323752404aSKarsten Graul int rc = 0;
22337affc809SUrsula Braun
22345c21c4ccSUrsula Braun /* check if ISM V1 is available */
223591a7c27cSGuangguan Wang if (!(ini->smcd_version & SMC_V1) ||
223691a7c27cSGuangguan Wang !smcd_indicated(ini->smc_type_v1) ||
223791a7c27cSGuangguan Wang !pclc_smcd)
22387affc809SUrsula Braun goto not_found;
22397affc809SUrsula Braun ini->is_smcd = true; /* prepare ISM check */
22407e5ef8ebSWen Gu ini->ism_peer_gid[0].gid = ntohll(pclc_smcd->ism.gid);
22417e5ef8ebSWen Gu ini->ism_peer_gid[0].gid_ext = 0;
22423752404aSKarsten Graul rc = smc_find_ism_device(new_smc, ini);
22433752404aSKarsten Graul if (rc)
22447affc809SUrsula Braun goto not_found;
22455c21c4ccSUrsula Braun ini->ism_selected = 0;
22463752404aSKarsten Graul rc = smc_listen_ism_init(new_smc, ini);
22473752404aSKarsten Graul if (!rc)
22485c21c4ccSUrsula Braun return; /* V1 ISM device found */
22497affc809SUrsula Braun
22507affc809SUrsula Braun not_found:
22513752404aSKarsten Graul smc_find_ism_store_rc(rc, ini);
2252e49300a6SKarsten Graul ini->smcd_version &= ~SMC_V1;
22533fc64937SUrsula Braun ini->ism_dev[0] = NULL;
22547affc809SUrsula Braun ini->is_smcd = false;
22557affc809SUrsula Braun }
22567affc809SUrsula Braun
22573b2dec26SHans Wippel /* listen worker: register buffers */
smc_listen_rdma_reg(struct smc_sock * new_smc,bool local_first)22585ac54d87SUrsula Braun static int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first)
22593b2dec26SHans Wippel {
2260b9247544SKarsten Graul struct smc_connection *conn = &new_smc->conn;
22613b2dec26SHans Wippel
22625ac54d87SUrsula Braun if (!local_first) {
2263b8d19945SWen Gu /* reg sendbufs if they were vzalloced */
2264b8d19945SWen Gu if (conn->sndbuf_desc->is_vm) {
2265b8d19945SWen Gu if (smcr_lgr_reg_sndbufs(conn->lnk,
2266b8d19945SWen Gu conn->sndbuf_desc))
2267b8d19945SWen Gu return SMC_CLC_DECL_ERR_REGBUF;
2268b8d19945SWen Gu }
22697562a13dSKarsten Graul if (smcr_lgr_reg_rmbs(conn->lnk, conn->rmb_desc))
2270b8d19945SWen Gu return SMC_CLC_DECL_ERR_REGBUF;
22713b2dec26SHans Wippel }
22723b2dec26SHans Wippel
22733b2dec26SHans Wippel return 0;
22743b2dec26SHans Wippel }
22753b2dec26SHans Wippel
smc_find_rdma_v2_device_serv(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc,struct smc_init_info * ini)22769029ac03Sliuyacan static void smc_find_rdma_v2_device_serv(struct smc_sock *new_smc,
2277e49300a6SKarsten Graul struct smc_clc_msg_proposal *pclc,
2278e49300a6SKarsten Graul struct smc_init_info *ini)
2279e49300a6SKarsten Graul {
2280e49300a6SKarsten Graul struct smc_clc_v2_extension *smc_v2_ext;
2281e49300a6SKarsten Graul u8 smcr_version;
22829029ac03Sliuyacan int rc;
2283e49300a6SKarsten Graul
2284e49300a6SKarsten Graul if (!(ini->smcr_version & SMC_V2) || !smcr_indicated(ini->smc_type_v2))
2285e49300a6SKarsten Graul goto not_found;
2286e49300a6SKarsten Graul
2287e49300a6SKarsten Graul smc_v2_ext = smc_get_clc_v2_ext(pclc);
2288295a92e3SGuangguan Wang if (!smc_v2_ext ||
2289295a92e3SGuangguan Wang !smc_clc_match_eid(ini->negotiated_eid, smc_v2_ext, NULL, NULL))
2290e49300a6SKarsten Graul goto not_found;
2291e49300a6SKarsten Graul
2292e49300a6SKarsten Graul /* prepare RDMA check */
2293e49300a6SKarsten Graul memcpy(ini->peer_systemid, pclc->lcl.id_for_peer, SMC_SYSTEMID_LEN);
2294e49300a6SKarsten Graul memcpy(ini->peer_gid, smc_v2_ext->roce, SMC_GID_SIZE);
2295e49300a6SKarsten Graul memcpy(ini->peer_mac, pclc->lcl.mac, ETH_ALEN);
2296e49300a6SKarsten Graul ini->check_smcrv2 = true;
2297e49300a6SKarsten Graul ini->smcrv2.clc_sk = new_smc->clcsock->sk;
2298e49300a6SKarsten Graul ini->smcrv2.saddr = new_smc->clcsock->sk->sk_rcv_saddr;
2299e49300a6SKarsten Graul ini->smcrv2.daddr = smc_ib_gid_to_ipv4(smc_v2_ext->roce);
2300e49300a6SKarsten Graul rc = smc_find_rdma_device(new_smc, ini);
23019029ac03Sliuyacan if (rc) {
23029029ac03Sliuyacan smc_find_ism_store_rc(rc, ini);
2303e49300a6SKarsten Graul goto not_found;
23049029ac03Sliuyacan }
2305e49300a6SKarsten Graul if (!ini->smcrv2.uses_gateway)
2306e49300a6SKarsten Graul memcpy(ini->smcrv2.nexthop_mac, pclc->lcl.mac, ETH_ALEN);
2307e49300a6SKarsten Graul
2308e49300a6SKarsten Graul smcr_version = ini->smcr_version;
2309e49300a6SKarsten Graul ini->smcr_version = SMC_V2;
2310e49300a6SKarsten Graul rc = smc_listen_rdma_init(new_smc, ini);
231135112271SWen Gu if (!rc) {
23128c3b8dc5Sliuyacan rc = smc_listen_rdma_reg(new_smc, ini->first_contact_local);
231335112271SWen Gu if (rc)
231435112271SWen Gu smc_conn_abort(new_smc, ini->first_contact_local);
231535112271SWen Gu }
23169029ac03Sliuyacan if (!rc)
23179029ac03Sliuyacan return;
23188c3b8dc5Sliuyacan ini->smcr_version = smcr_version;
23199029ac03Sliuyacan smc_find_ism_store_rc(rc, ini);
2320e49300a6SKarsten Graul
2321e49300a6SKarsten Graul not_found:
2322e49300a6SKarsten Graul ini->smcr_version &= ~SMC_V2;
2323b3b1a175Sliuyacan ini->smcrv2.ib_dev_v2 = NULL;
2324e49300a6SKarsten Graul ini->check_smcrv2 = false;
2325e49300a6SKarsten Graul }
2326e49300a6SKarsten Graul
smc_find_rdma_v1_device_serv(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc,struct smc_init_info * ini)23275c21c4ccSUrsula Braun static int smc_find_rdma_v1_device_serv(struct smc_sock *new_smc,
23287affc809SUrsula Braun struct smc_clc_msg_proposal *pclc,
23297affc809SUrsula Braun struct smc_init_info *ini)
23307affc809SUrsula Braun {
23317affc809SUrsula Braun int rc;
23327affc809SUrsula Braun
2333e49300a6SKarsten Graul if (!(ini->smcr_version & SMC_V1) || !smcr_indicated(ini->smc_type_v1))
23347affc809SUrsula Braun return SMC_CLC_DECL_NOSMCDEV;
23357affc809SUrsula Braun
23367affc809SUrsula Braun /* prepare RDMA check */
2337e49300a6SKarsten Graul memcpy(ini->peer_systemid, pclc->lcl.id_for_peer, SMC_SYSTEMID_LEN);
2338e49300a6SKarsten Graul memcpy(ini->peer_gid, pclc->lcl.gid, SMC_GID_SIZE);
2339e49300a6SKarsten Graul memcpy(ini->peer_mac, pclc->lcl.mac, ETH_ALEN);
23407affc809SUrsula Braun rc = smc_find_rdma_device(new_smc, ini);
23417affc809SUrsula Braun if (rc) {
23427affc809SUrsula Braun /* no RDMA device found */
2343e49300a6SKarsten Graul return SMC_CLC_DECL_NOSMCDEV;
23447affc809SUrsula Braun }
23457affc809SUrsula Braun rc = smc_listen_rdma_init(new_smc, ini);
23467affc809SUrsula Braun if (rc)
23477affc809SUrsula Braun return rc;
23487affc809SUrsula Braun return smc_listen_rdma_reg(new_smc, ini->first_contact_local);
23497affc809SUrsula Braun }
23507affc809SUrsula Braun
23517affc809SUrsula Braun /* determine the local device matching to proposal */
smc_listen_find_device(struct smc_sock * new_smc,struct smc_clc_msg_proposal * pclc,struct smc_init_info * ini)23527affc809SUrsula Braun static int smc_listen_find_device(struct smc_sock *new_smc,
23537affc809SUrsula Braun struct smc_clc_msg_proposal *pclc,
23547affc809SUrsula Braun struct smc_init_info *ini)
23557affc809SUrsula Braun {
2356e49300a6SKarsten Graul int prfx_rc;
23575c21c4ccSUrsula Braun
23585c21c4ccSUrsula Braun /* check for ISM device matching V2 proposed device */
23595c21c4ccSUrsula Braun smc_find_ism_v2_device_serv(new_smc, pclc, ini);
23605c21c4ccSUrsula Braun if (ini->ism_dev[0])
23617affc809SUrsula Braun return 0;
23625c21c4ccSUrsula Braun
2363e49300a6SKarsten Graul /* check for matching IP prefix and subnet length (V1) */
2364e49300a6SKarsten Graul prfx_rc = smc_listen_prfx_check(new_smc, pclc);
2365e49300a6SKarsten Graul if (prfx_rc)
2366e49300a6SKarsten Graul smc_find_ism_store_rc(prfx_rc, ini);
23675c21c4ccSUrsula Braun
23685c21c4ccSUrsula Braun /* get vlan id from IP device */
23695c21c4ccSUrsula Braun if (smc_vlan_by_tcpsk(new_smc->clcsock, ini))
23703752404aSKarsten Graul return ini->rc ?: SMC_CLC_DECL_GETVLANERR;
23715c21c4ccSUrsula Braun
23725c21c4ccSUrsula Braun /* check for ISM device matching V1 proposed device */
2373e49300a6SKarsten Graul if (!prfx_rc)
23745c21c4ccSUrsula Braun smc_find_ism_v1_device_serv(new_smc, pclc, ini);
23755c21c4ccSUrsula Braun if (ini->ism_dev[0])
23765c21c4ccSUrsula Braun return 0;
23775c21c4ccSUrsula Braun
2378e49300a6SKarsten Graul if (!smcr_indicated(pclc->hdr.typev1) &&
2379e49300a6SKarsten Graul !smcr_indicated(pclc->hdr.typev2))
23803752404aSKarsten Graul /* skip RDMA and decline */
23813752404aSKarsten Graul return ini->rc ?: SMC_CLC_DECL_NOSMCDDEV;
23827affc809SUrsula Braun
2383e49300a6SKarsten Graul /* check if RDMA V2 is available */
23849029ac03Sliuyacan smc_find_rdma_v2_device_serv(new_smc, pclc, ini);
23859029ac03Sliuyacan if (ini->smcrv2.ib_dev_v2)
2386e49300a6SKarsten Graul return 0;
2387e49300a6SKarsten Graul
2388e49300a6SKarsten Graul /* check if RDMA V1 is available */
2389e49300a6SKarsten Graul if (!prfx_rc) {
23909029ac03Sliuyacan int rc;
23919029ac03Sliuyacan
23923752404aSKarsten Graul rc = smc_find_rdma_v1_device_serv(new_smc, pclc, ini);
23933752404aSKarsten Graul smc_find_ism_store_rc(rc, ini);
23943752404aSKarsten Graul return (!rc) ? 0 : ini->rc;
23957affc809SUrsula Braun }
23964abbd2e3SDust Li return prfx_rc;
2397e49300a6SKarsten Graul }
23987affc809SUrsula Braun
23993b2dec26SHans Wippel /* listen worker: finish RDMA setup */
smc_listen_rdma_finish(struct smc_sock * new_smc,struct smc_clc_msg_accept_confirm * cclc,bool local_first,struct smc_init_info * ini)24001ca52fcfSUrsula Braun static int smc_listen_rdma_finish(struct smc_sock *new_smc,
24013b2dec26SHans Wippel struct smc_clc_msg_accept_confirm *cclc,
2402e49300a6SKarsten Graul bool local_first,
2403e49300a6SKarsten Graul struct smc_init_info *ini)
24043b2dec26SHans Wippel {
2405387707fdSKarsten Graul struct smc_link *link = new_smc->conn.lnk;
24063b2dec26SHans Wippel int reason_code = 0;
24073b2dec26SHans Wippel
24085ac54d87SUrsula Braun if (local_first)
2409e49300a6SKarsten Graul smc_link_save_peer_info(link, cclc, ini);
24103b2dec26SHans Wippel
24110c881adaSUrsula Braun if (smc_rmb_rtoken_handling(&new_smc->conn, link, cclc))
24120c881adaSUrsula Braun return SMC_CLC_DECL_ERR_RTOK;
24133b2dec26SHans Wippel
24145ac54d87SUrsula Braun if (local_first) {
24150c881adaSUrsula Braun if (smc_ib_ready_link(link))
24160c881adaSUrsula Braun return SMC_CLC_DECL_ERR_RDYLNK;
24173b2dec26SHans Wippel /* QP confirmation over RoCE fabric */
24184667bb4aSKarsten Graul smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
2419b9247544SKarsten Graul reason_code = smcr_serv_conf_first_link(new_smc);
24204667bb4aSKarsten Graul smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
24213b2dec26SHans Wippel }
24221ca52fcfSUrsula Braun return reason_code;
24233b2dec26SHans Wippel }
24243b2dec26SHans Wippel
24257affc809SUrsula Braun /* setup for connection of server */
smc_listen_work(struct work_struct * work)24263b2dec26SHans Wippel static void smc_listen_work(struct work_struct *work)
24273b2dec26SHans Wippel {
24283b2dec26SHans Wippel struct smc_sock *new_smc = container_of(work, struct smc_sock,
24293b2dec26SHans Wippel smc_listen_work);
24303b2dec26SHans Wippel struct socket *newclcsock = new_smc->clcsock;
2431a7c9c5f4SUrsula Braun struct smc_clc_msg_accept_confirm *cclc;
24326bb14e48SUrsula Braun struct smc_clc_msg_proposal_area *buf;
24333b2dec26SHans Wippel struct smc_clc_msg_proposal *pclc;
24343fc64937SUrsula Braun struct smc_init_info *ini = NULL;
2435e49300a6SKarsten Graul u8 proposal_version = SMC_V1;
2436e49300a6SKarsten Graul u8 accept_version;
24373b2dec26SHans Wippel int rc = 0;
24383b2dec26SHans Wippel
2439673d6066SWen Gu lock_sock(&new_smc->sk); /* release in smc_listen_out() */
2440fd57770dSKarsten Graul if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN)
2441fd57770dSKarsten Graul return smc_listen_out_err(new_smc);
2442fd57770dSKarsten Graul
24433b2dec26SHans Wippel if (new_smc->use_fallback) {
24443b2dec26SHans Wippel smc_listen_out_connected(new_smc);
24453b2dec26SHans Wippel return;
24463b2dec26SHans Wippel }
24473b2dec26SHans Wippel
24483b2dec26SHans Wippel /* check if peer is smc capable */
24493b2dec26SHans Wippel if (!tcp_sk(newclcsock->sk)->syn_smc) {
2450c0bf3d8aSWen Gu rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC);
2451c0bf3d8aSWen Gu if (rc)
2452c0bf3d8aSWen Gu smc_listen_out_err(new_smc);
2453c0bf3d8aSWen Gu else
24543b2dec26SHans Wippel smc_listen_out_connected(new_smc);
24553b2dec26SHans Wippel return;
24563b2dec26SHans Wippel }
24573b2dec26SHans Wippel
24583b2dec26SHans Wippel /* do inband token exchange -
24593b2dec26SHans Wippel * wait for and receive SMC Proposal CLC message
24603b2dec26SHans Wippel */
24616bb14e48SUrsula Braun buf = kzalloc(sizeof(*buf), GFP_KERNEL);
24626bb14e48SUrsula Braun if (!buf) {
24636bb14e48SUrsula Braun rc = SMC_CLC_DECL_MEM;
24646bb14e48SUrsula Braun goto out_decl;
24656bb14e48SUrsula Braun }
24666bb14e48SUrsula Braun pclc = (struct smc_clc_msg_proposal *)buf;
24676bb14e48SUrsula Braun rc = smc_clc_wait_msg(new_smc, pclc, sizeof(*buf),
24682b59f58eSUrsula Braun SMC_CLC_PROPOSAL, CLC_WAIT_TIME);
24699aa68d29SKarsten Graul if (rc)
24709aa68d29SKarsten Graul goto out_decl;
2471e49300a6SKarsten Graul
2472e49300a6SKarsten Graul if (pclc->hdr.version > SMC_V1)
2473e49300a6SKarsten Graul proposal_version = SMC_V2;
24743b2dec26SHans Wippel
2475e8d726c8SUrsula Braun /* IPSec connections opt out of SMC optimizations */
24763b2dec26SHans Wippel if (using_ipsec(new_smc)) {
24779aa68d29SKarsten Graul rc = SMC_CLC_DECL_IPSEC;
24789aa68d29SKarsten Graul goto out_decl;
24793b2dec26SHans Wippel }
24803b2dec26SHans Wippel
24813fc64937SUrsula Braun ini = kzalloc(sizeof(*ini), GFP_KERNEL);
24823fc64937SUrsula Braun if (!ini) {
24833fc64937SUrsula Braun rc = SMC_CLC_DECL_MEM;
24843fc64937SUrsula Braun goto out_decl;
24853fc64937SUrsula Braun }
24863fc64937SUrsula Braun
24875c21c4ccSUrsula Braun /* initial version checking */
24885c21c4ccSUrsula Braun rc = smc_listen_v2_check(new_smc, pclc, ini);
24895c21c4ccSUrsula Braun if (rc)
24909aa68d29SKarsten Graul goto out_decl;
2491fba7e8efSKarsten Graul
24926ac1e656SGuangguan Wang rc = smc_clc_srv_v2x_features_validate(pclc, ini);
24936ac1e656SGuangguan Wang if (rc)
24946ac1e656SGuangguan Wang goto out_decl;
24956ac1e656SGuangguan Wang
249672a36a8aSHans Wippel mutex_lock(&smc_server_lgr_pending);
24973b2dec26SHans Wippel smc_rx_init(new_smc);
24983b2dec26SHans Wippel smc_tx_init(new_smc);
24993b2dec26SHans Wippel
25007affc809SUrsula Braun /* determine ISM or RoCE device used for connection */
25013fc64937SUrsula Braun rc = smc_listen_find_device(new_smc, pclc, ini);
25029aa68d29SKarsten Graul if (rc)
25039aa68d29SKarsten Graul goto out_unlock;
25043b2dec26SHans Wippel
25053b2dec26SHans Wippel /* send SMC Accept CLC message */
2506e49300a6SKarsten Graul accept_version = ini->is_smcd ? ini->smcd_version : ini->smcr_version;
2507a7c9c5f4SUrsula Braun rc = smc_clc_send_accept(new_smc, ini->first_contact_local,
25081e700948SGuangguan Wang accept_version, ini->negotiated_eid, ini);
25099aa68d29SKarsten Graul if (rc)
25109aa68d29SKarsten Graul goto out_unlock;
25113b2dec26SHans Wippel
251262c7139fSHans Wippel /* SMC-D does not need this lock any more */
25133fc64937SUrsula Braun if (ini->is_smcd)
251472a36a8aSHans Wippel mutex_unlock(&smc_server_lgr_pending);
251562c7139fSHans Wippel
25163b2dec26SHans Wippel /* receive SMC Confirm CLC message */
25179047a617SKarsten Graul memset(buf, 0, sizeof(*buf));
25189047a617SKarsten Graul cclc = (struct smc_clc_msg_accept_confirm *)buf;
25199047a617SKarsten Graul rc = smc_clc_wait_msg(new_smc, cclc, sizeof(*buf),
25202b59f58eSUrsula Braun SMC_CLC_CONFIRM, CLC_WAIT_TIME);
2521228bae05SKarsten Graul if (rc) {
25223fc64937SUrsula Braun if (!ini->is_smcd)
25239aa68d29SKarsten Graul goto out_unlock;
25249aa68d29SKarsten Graul goto out_decl;
25253b2dec26SHans Wippel }
25263b2dec26SHans Wippel
25276ac1e656SGuangguan Wang rc = smc_clc_v2x_features_confirm_check(cclc, ini);
25286ac1e656SGuangguan Wang if (rc) {
25296ac1e656SGuangguan Wang if (!ini->is_smcd)
25306ac1e656SGuangguan Wang goto out_unlock;
25316ac1e656SGuangguan Wang goto out_decl;
25326ac1e656SGuangguan Wang }
25336ac1e656SGuangguan Wang
253469b888e3SGuangguan Wang /* fce smc release version is needed in smc_listen_rdma_finish,
253569b888e3SGuangguan Wang * so save fce info here.
253669b888e3SGuangguan Wang */
253769b888e3SGuangguan Wang smc_conn_save_peer_info_fce(new_smc, cclc);
253869b888e3SGuangguan Wang
25393b2dec26SHans Wippel /* finish worker */
25403fc64937SUrsula Braun if (!ini->is_smcd) {
2541a7c9c5f4SUrsula Braun rc = smc_listen_rdma_finish(new_smc, cclc,
2542e49300a6SKarsten Graul ini->first_contact_local, ini);
254362c7139fSHans Wippel if (rc)
25440c881adaSUrsula Braun goto out_unlock;
25450c881adaSUrsula Braun mutex_unlock(&smc_server_lgr_pending);
25461ca52fcfSUrsula Braun }
2547a7c9c5f4SUrsula Braun smc_conn_save_peer_info(new_smc, cclc);
254821f6f41eSWen Gu
254921f6f41eSWen Gu if (ini->is_smcd &&
255021f6f41eSWen Gu smc_ism_support_dmb_nocopy(new_smc->conn.lgr->smcd)) {
255121f6f41eSWen Gu rc = smcd_buf_attach(new_smc);
255221f6f41eSWen Gu if (rc)
255321f6f41eSWen Gu goto out_decl;
255421f6f41eSWen Gu }
255521f6f41eSWen Gu
25563b2dec26SHans Wippel smc_listen_out_connected(new_smc);
2557194730a9SGuvenc Gulce SMC_STAT_SERV_SUCC_INC(sock_net(newclcsock->sk), ini);
2558ac679364SUrsula Braun goto out_free;
25599aa68d29SKarsten Graul
25609aa68d29SKarsten Graul out_unlock:
25619aa68d29SKarsten Graul mutex_unlock(&smc_server_lgr_pending);
25629aa68d29SKarsten Graul out_decl:
25634a9baf45SKarsten Graul smc_listen_decline(new_smc, rc, ini ? ini->first_contact_local : 0,
2564e49300a6SKarsten Graul proposal_version);
2565ac679364SUrsula Braun out_free:
25663fc64937SUrsula Braun kfree(ini);
25676bb14e48SUrsula Braun kfree(buf);
2568a046d57dSUrsula Braun }
2569a046d57dSUrsula Braun
smc_tcp_listen_work(struct work_struct * work)2570a046d57dSUrsula Braun static void smc_tcp_listen_work(struct work_struct *work)
2571a046d57dSUrsula Braun {
2572a046d57dSUrsula Braun struct smc_sock *lsmc = container_of(work, struct smc_sock,
2573a046d57dSUrsula Braun tcp_listen_work);
25743163c507SUrsula Braun struct sock *lsk = &lsmc->sk;
2575a046d57dSUrsula Braun struct smc_sock *new_smc;
2576a046d57dSUrsula Braun int rc = 0;
2577a046d57dSUrsula Braun
25783163c507SUrsula Braun lock_sock(lsk);
25793163c507SUrsula Braun while (lsk->sk_state == SMC_LISTEN) {
2580a046d57dSUrsula Braun rc = smc_clcsock_accept(lsmc, &new_smc);
2581a60a2b1eSUrsula Braun if (rc) /* clcsock accept queue empty or error */
2582a046d57dSUrsula Braun goto out;
2583a046d57dSUrsula Braun if (!new_smc)
2584a046d57dSUrsula Braun continue;
2585a046d57dSUrsula Braun
25868270d9c2SD. Wythe if (tcp_sk(new_smc->clcsock->sk)->syn_smc)
25878270d9c2SD. Wythe atomic_inc(&lsmc->queued_smc_hs);
25888270d9c2SD. Wythe
2589a046d57dSUrsula Braun new_smc->listen_smc = lsmc;
2590ee9dfbefSUrsula Braun new_smc->use_fallback = lsmc->use_fallback;
2591603cc149SKarsten Graul new_smc->fallback_rsn = lsmc->fallback_rsn;
25923163c507SUrsula Braun sock_hold(lsk); /* sock_put in smc_listen_work */
2593a046d57dSUrsula Braun INIT_WORK(&new_smc->smc_listen_work, smc_listen_work);
2594a046d57dSUrsula Braun smc_copy_sock_settings_to_smc(new_smc);
259551f1de79SUrsula Braun sock_hold(&new_smc->sk); /* sock_put in passive closing */
259622ef473dSKarsten Graul if (!queue_work(smc_hs_wq, &new_smc->smc_listen_work))
259751f1de79SUrsula Braun sock_put(&new_smc->sk);
2598a046d57dSUrsula Braun }
2599a046d57dSUrsula Braun
2600a046d57dSUrsula Braun out:
26013163c507SUrsula Braun release_sock(lsk);
2602a60a2b1eSUrsula Braun sock_put(&lsmc->sk); /* sock_hold in smc_clcsock_data_ready() */
2603a60a2b1eSUrsula Braun }
2604a60a2b1eSUrsula Braun
smc_clcsock_data_ready(struct sock * listen_clcsock)2605a60a2b1eSUrsula Braun static void smc_clcsock_data_ready(struct sock *listen_clcsock)
2606a60a2b1eSUrsula Braun {
26070558226cSWen Gu struct smc_sock *lsmc;
2608a60a2b1eSUrsula Braun
26090558226cSWen Gu read_lock_bh(&listen_clcsock->sk_callback_lock);
26100558226cSWen Gu lsmc = smc_clcsock_user_data(listen_clcsock);
2611a60a2b1eSUrsula Braun if (!lsmc)
26120558226cSWen Gu goto out;
2613a60a2b1eSUrsula Braun lsmc->clcsk_data_ready(listen_clcsock);
2614a60a2b1eSUrsula Braun if (lsmc->sk.sk_state == SMC_LISTEN) {
2615a60a2b1eSUrsula Braun sock_hold(&lsmc->sk); /* sock_put in smc_tcp_listen_work() */
26163079e342SD. Wythe if (!queue_work(smc_tcp_ls_wq, &lsmc->tcp_listen_work))
2617a60a2b1eSUrsula Braun sock_put(&lsmc->sk);
2618a60a2b1eSUrsula Braun }
26190558226cSWen Gu out:
26200558226cSWen Gu read_unlock_bh(&listen_clcsock->sk_callback_lock);
2621a046d57dSUrsula Braun }
2622a046d57dSUrsula Braun
smc_listen(struct socket * sock,int backlog)2623ac713874SUrsula Braun static int smc_listen(struct socket *sock, int backlog)
2624ac713874SUrsula Braun {
2625ac713874SUrsula Braun struct sock *sk = sock->sk;
2626ac713874SUrsula Braun struct smc_sock *smc;
2627ac713874SUrsula Braun int rc;
2628ac713874SUrsula Braun
2629ac713874SUrsula Braun smc = smc_sk(sk);
2630ac713874SUrsula Braun lock_sock(sk);
2631ac713874SUrsula Braun
2632ac713874SUrsula Braun rc = -EINVAL;
2633cd206360SUrsula Braun if ((sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN) ||
26343aba1030SGuangguan Wang smc->connect_nonblock || sock->state != SS_UNCONNECTED)
2635ac713874SUrsula Braun goto out;
2636ac713874SUrsula Braun
2637ac713874SUrsula Braun rc = 0;
2638ac713874SUrsula Braun if (sk->sk_state == SMC_LISTEN) {
2639ac713874SUrsula Braun sk->sk_max_ack_backlog = backlog;
2640ac713874SUrsula Braun goto out;
2641ac713874SUrsula Braun }
2642ac713874SUrsula Braun /* some socket options are handled in core, so we could not apply
2643ac713874SUrsula Braun * them to the clc socket -- copy smc socket options to clc socket
2644ac713874SUrsula Braun */
2645ac713874SUrsula Braun smc_copy_sock_settings_to_clc(smc);
2646ee9dfbefSUrsula Braun if (!smc->use_fallback)
2647c5c1cc9cSUrsula Braun tcp_sk(smc->clcsock->sk)->syn_smc = 1;
2648ac713874SUrsula Braun
2649a60a2b1eSUrsula Braun /* save original sk_data_ready function and establish
2650a60a2b1eSUrsula Braun * smc-specific sk_data_ready function
2651a60a2b1eSUrsula Braun */
26520558226cSWen Gu write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
2653a60a2b1eSUrsula Braun smc->clcsock->sk->sk_user_data =
2654a60a2b1eSUrsula Braun (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
265597b9af7aSWen Gu smc_clcsock_replace_cb(&smc->clcsock->sk->sk_data_ready,
265697b9af7aSWen Gu smc_clcsock_data_ready, &smc->clcsk_data_ready);
26570558226cSWen Gu write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
26588270d9c2SD. Wythe
26598270d9c2SD. Wythe /* save original ops */
26608270d9c2SD. Wythe smc->ori_af_ops = inet_csk(smc->clcsock->sk)->icsk_af_ops;
26618270d9c2SD. Wythe
26628270d9c2SD. Wythe smc->af_ops = *smc->ori_af_ops;
26638270d9c2SD. Wythe smc->af_ops.syn_recv_sock = smc_tcp_syn_recv_sock;
26648270d9c2SD. Wythe
26658270d9c2SD. Wythe inet_csk(smc->clcsock->sk)->icsk_af_ops = &smc->af_ops;
26668270d9c2SD. Wythe
2667a6a6fe27SD. Wythe if (smc->limit_smc_hs)
266848b6190aSD. Wythe tcp_sk(smc->clcsock->sk)->smc_hs_congested = smc_hs_congested;
266948b6190aSD. Wythe
2670ac713874SUrsula Braun rc = kernel_listen(smc->clcsock, backlog);
26719ebb0c4bSGuo DaXing if (rc) {
26720558226cSWen Gu write_lock_bh(&smc->clcsock->sk->sk_callback_lock);
267397b9af7aSWen Gu smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready,
267497b9af7aSWen Gu &smc->clcsk_data_ready);
267597b9af7aSWen Gu smc->clcsock->sk->sk_user_data = NULL;
26760558226cSWen Gu write_unlock_bh(&smc->clcsock->sk->sk_callback_lock);
2677ac713874SUrsula Braun goto out;
26789ebb0c4bSGuo DaXing }
2679ac713874SUrsula Braun sk->sk_max_ack_backlog = backlog;
2680ac713874SUrsula Braun sk->sk_ack_backlog = 0;
2681ac713874SUrsula Braun sk->sk_state = SMC_LISTEN;
2682ac713874SUrsula Braun
2683ac713874SUrsula Braun out:
2684ac713874SUrsula Braun release_sock(sk);
2685ac713874SUrsula Braun return rc;
2686ac713874SUrsula Braun }
2687ac713874SUrsula Braun
smc_accept(struct socket * sock,struct socket * new_sock,int flags,bool kern)2688ac713874SUrsula Braun static int smc_accept(struct socket *sock, struct socket *new_sock,
2689cdfbabfbSDavid Howells int flags, bool kern)
2690ac713874SUrsula Braun {
2691a046d57dSUrsula Braun struct sock *sk = sock->sk, *nsk;
2692a046d57dSUrsula Braun DECLARE_WAITQUEUE(wait, current);
2693ac713874SUrsula Braun struct smc_sock *lsmc;
2694a046d57dSUrsula Braun long timeo;
2695a046d57dSUrsula Braun int rc = 0;
2696ac713874SUrsula Braun
2697ac713874SUrsula Braun lsmc = smc_sk(sk);
269851f1de79SUrsula Braun sock_hold(sk); /* sock_put below */
2699ac713874SUrsula Braun lock_sock(sk);
2700ac713874SUrsula Braun
2701ac713874SUrsula Braun if (lsmc->sk.sk_state != SMC_LISTEN) {
2702ac713874SUrsula Braun rc = -EINVAL;
2703abb190f1SUrsula Braun release_sock(sk);
2704ac713874SUrsula Braun goto out;
2705ac713874SUrsula Braun }
2706ac713874SUrsula Braun
2707a046d57dSUrsula Braun /* Wait for an incoming connection */
2708a046d57dSUrsula Braun timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
2709a046d57dSUrsula Braun add_wait_queue_exclusive(sk_sleep(sk), &wait);
2710a046d57dSUrsula Braun while (!(nsk = smc_accept_dequeue(sk, new_sock))) {
2711a046d57dSUrsula Braun set_current_state(TASK_INTERRUPTIBLE);
2712a046d57dSUrsula Braun if (!timeo) {
2713a046d57dSUrsula Braun rc = -EAGAIN;
2714a046d57dSUrsula Braun break;
2715a046d57dSUrsula Braun }
2716a046d57dSUrsula Braun release_sock(sk);
2717a046d57dSUrsula Braun timeo = schedule_timeout(timeo);
2718a046d57dSUrsula Braun /* wakeup by sk_data_ready in smc_listen_work() */
2719a046d57dSUrsula Braun sched_annotate_sleep();
2720a046d57dSUrsula Braun lock_sock(sk);
2721a046d57dSUrsula Braun if (signal_pending(current)) {
2722a046d57dSUrsula Braun rc = sock_intr_errno(timeo);
2723a046d57dSUrsula Braun break;
2724a046d57dSUrsula Braun }
2725a046d57dSUrsula Braun }
2726a046d57dSUrsula Braun set_current_state(TASK_RUNNING);
2727a046d57dSUrsula Braun remove_wait_queue(sk_sleep(sk), &wait);
2728ac713874SUrsula Braun
2729a046d57dSUrsula Braun if (!rc)
2730a046d57dSUrsula Braun rc = sock_error(nsk);
2731abb190f1SUrsula Braun release_sock(sk);
2732abb190f1SUrsula Braun if (rc)
2733abb190f1SUrsula Braun goto out;
2734abb190f1SUrsula Braun
2735abb190f1SUrsula Braun if (lsmc->sockopt_defer_accept && !(flags & O_NONBLOCK)) {
2736abb190f1SUrsula Braun /* wait till data arrives on the socket */
2737abb190f1SUrsula Braun timeo = msecs_to_jiffies(lsmc->sockopt_defer_accept *
2738abb190f1SUrsula Braun MSEC_PER_SEC);
2739abb190f1SUrsula Braun if (smc_sk(nsk)->use_fallback) {
2740abb190f1SUrsula Braun struct sock *clcsk = smc_sk(nsk)->clcsock->sk;
2741abb190f1SUrsula Braun
2742abb190f1SUrsula Braun lock_sock(clcsk);
2743abb190f1SUrsula Braun if (skb_queue_empty(&clcsk->sk_receive_queue))
2744abb190f1SUrsula Braun sk_wait_data(clcsk, &timeo, NULL);
2745abb190f1SUrsula Braun release_sock(clcsk);
2746abb190f1SUrsula Braun } else if (!atomic_read(&smc_sk(nsk)->conn.bytes_to_rcv)) {
2747abb190f1SUrsula Braun lock_sock(nsk);
2748*d433ccd9SGuangguan Wang smc_rx_wait(smc_sk(nsk), &timeo, 0, smc_rx_data_available);
2749abb190f1SUrsula Braun release_sock(nsk);
2750abb190f1SUrsula Braun }
2751abb190f1SUrsula Braun }
2752ac713874SUrsula Braun
2753ac713874SUrsula Braun out:
275451f1de79SUrsula Braun sock_put(sk); /* sock_hold above */
2755ac713874SUrsula Braun return rc;
2756ac713874SUrsula Braun }
2757ac713874SUrsula Braun
smc_getname(struct socket * sock,struct sockaddr * addr,int peer)2758ac713874SUrsula Braun static int smc_getname(struct socket *sock, struct sockaddr *addr,
27599b2c45d4SDenys Vlasenko int peer)
2760ac713874SUrsula Braun {
2761ac713874SUrsula Braun struct smc_sock *smc;
2762ac713874SUrsula Braun
2763b38d7324SUrsula Braun if (peer && (sock->sk->sk_state != SMC_ACTIVE) &&
2764b38d7324SUrsula Braun (sock->sk->sk_state != SMC_APPCLOSEWAIT1))
2765ac713874SUrsula Braun return -ENOTCONN;
2766ac713874SUrsula Braun
2767ac713874SUrsula Braun smc = smc_sk(sock->sk);
2768ac713874SUrsula Braun
27699b2c45d4SDenys Vlasenko return smc->clcsock->ops->getname(smc->clcsock, addr, peer);
2770ac713874SUrsula Braun }
2771ac713874SUrsula Braun
smc_sendmsg(struct socket * sock,struct msghdr * msg,size_t len)2772ac713874SUrsula Braun static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
2773ac713874SUrsula Braun {
2774ac713874SUrsula Braun struct sock *sk = sock->sk;
2775ac713874SUrsula Braun struct smc_sock *smc;
2776ce7ca794SD. Wythe int rc;
2777ac713874SUrsula Braun
2778ac713874SUrsula Braun smc = smc_sk(sk);
2779ac713874SUrsula Braun lock_sock(sk);
2780ee9dfbefSUrsula Braun
2781ce7ca794SD. Wythe /* SMC does not support connect with fastopen */
2782ee9dfbefSUrsula Braun if (msg->msg_flags & MSG_FASTOPEN) {
2783ce7ca794SD. Wythe /* not connected yet, fallback */
2784cd206360SUrsula Braun if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
2785c0bf3d8aSWen Gu rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
2786c0bf3d8aSWen Gu if (rc)
2787c0bf3d8aSWen Gu goto out;
2788ee9dfbefSUrsula Braun } else {
2789ee9dfbefSUrsula Braun rc = -EINVAL;
2790ee9dfbefSUrsula Braun goto out;
2791ee9dfbefSUrsula Braun }
2792ce7ca794SD. Wythe } else if ((sk->sk_state != SMC_ACTIVE) &&
2793ce7ca794SD. Wythe (sk->sk_state != SMC_APPCLOSEWAIT1) &&
2794ce7ca794SD. Wythe (sk->sk_state != SMC_INIT)) {
2795ce7ca794SD. Wythe rc = -EPIPE;
2796ce7ca794SD. Wythe goto out;
2797ee9dfbefSUrsula Braun }
2798ee9dfbefSUrsula Braun
2799e0e4b8faSGuvenc Gulce if (smc->use_fallback) {
2800ac713874SUrsula Braun rc = smc->clcsock->ops->sendmsg(smc->clcsock, msg, len);
2801e0e4b8faSGuvenc Gulce } else {
2802e6727f39SUrsula Braun rc = smc_tx_sendmsg(smc, msg, len);
2803e0e4b8faSGuvenc Gulce SMC_STAT_TX_PAYLOAD(smc, len, rc);
2804e0e4b8faSGuvenc Gulce }
2805ac713874SUrsula Braun out:
2806ac713874SUrsula Braun release_sock(sk);
2807ac713874SUrsula Braun return rc;
2808ac713874SUrsula Braun }
2809ac713874SUrsula Braun
smc_recvmsg(struct socket * sock,struct msghdr * msg,size_t len,int flags)2810ac713874SUrsula Braun static int smc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
2811ac713874SUrsula Braun int flags)
2812ac713874SUrsula Braun {
2813ac713874SUrsula Braun struct sock *sk = sock->sk;
2814ac713874SUrsula Braun struct smc_sock *smc;
2815ac713874SUrsula Braun int rc = -ENOTCONN;
2816ac713874SUrsula Braun
2817ac713874SUrsula Braun smc = smc_sk(sk);
2818ac713874SUrsula Braun lock_sock(sk);
281951c5aba3SKarsten Graul if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) {
282051c5aba3SKarsten Graul /* socket was connected before, no more data to read */
282151c5aba3SKarsten Graul rc = 0;
282251c5aba3SKarsten Graul goto out;
282351c5aba3SKarsten Graul }
2824b38d7324SUrsula Braun if ((sk->sk_state == SMC_INIT) ||
2825b38d7324SUrsula Braun (sk->sk_state == SMC_LISTEN) ||
2826b38d7324SUrsula Braun (sk->sk_state == SMC_CLOSED))
2827ac713874SUrsula Braun goto out;
2828ac713874SUrsula Braun
2829b38d7324SUrsula Braun if (sk->sk_state == SMC_PEERFINCLOSEWAIT) {
2830b38d7324SUrsula Braun rc = 0;
2831b38d7324SUrsula Braun goto out;
2832b38d7324SUrsula Braun }
2833b38d7324SUrsula Braun
28349014db20SStefan Raspl if (smc->use_fallback) {
2835ac713874SUrsula Braun rc = smc->clcsock->ops->recvmsg(smc->clcsock, msg, len, flags);
28369014db20SStefan Raspl } else {
28379014db20SStefan Raspl msg->msg_namelen = 0;
28389014db20SStefan Raspl rc = smc_rx_recvmsg(smc, msg, NULL, len, flags);
2839e0e4b8faSGuvenc Gulce SMC_STAT_RX_PAYLOAD(smc, rc, rc);
28409014db20SStefan Raspl }
2841b38d7324SUrsula Braun
2842ac713874SUrsula Braun out:
2843ac713874SUrsula Braun release_sock(sk);
2844ac713874SUrsula Braun return rc;
2845ac713874SUrsula Braun }
2846ac713874SUrsula Braun
smc_accept_poll(struct sock * parent)2847ade994f4SAl Viro static __poll_t smc_accept_poll(struct sock *parent)
2848a046d57dSUrsula Braun {
28498dce2786SUrsula Braun struct smc_sock *isk = smc_sk(parent);
285063e2480cSAl Viro __poll_t mask = 0;
2851a046d57dSUrsula Braun
28528dce2786SUrsula Braun spin_lock(&isk->accept_q_lock);
28538dce2786SUrsula Braun if (!list_empty(&isk->accept_q))
2854a9a08845SLinus Torvalds mask = EPOLLIN | EPOLLRDNORM;
28558dce2786SUrsula Braun spin_unlock(&isk->accept_q_lock);
2856a046d57dSUrsula Braun
28578dce2786SUrsula Braun return mask;
2858a046d57dSUrsula Braun }
2859a046d57dSUrsula Braun
smc_poll(struct file * file,struct socket * sock,poll_table * wait)2860a11e1d43SLinus Torvalds static __poll_t smc_poll(struct file *file, struct socket *sock,
2861a11e1d43SLinus Torvalds poll_table *wait)
2862ac713874SUrsula Braun {
2863ac713874SUrsula Braun struct sock *sk = sock->sk;
2864ac713874SUrsula Braun struct smc_sock *smc;
286550717a37SUrsula Braun __poll_t mask = 0;
2866ac713874SUrsula Braun
28678dce2786SUrsula Braun if (!sk)
2868a9a08845SLinus Torvalds return EPOLLNVAL;
28698dce2786SUrsula Braun
2870ac713874SUrsula Braun smc = smc_sk(sock->sk);
2871648a5a7aSUrsula Braun if (smc->use_fallback) {
2872a046d57dSUrsula Braun /* delegate to CLC child sock */
2873a11e1d43SLinus Torvalds mask = smc->clcsock->ops->poll(file, smc->clcsock, wait);
2874a046d57dSUrsula Braun sk->sk_err = smc->clcsock->sk->sk_err;
2875a046d57dSUrsula Braun } else {
2876410da1e1SLinus Torvalds if (sk->sk_state != SMC_CLOSED)
287789ab066dSKarsten Graul sock_poll_wait(file, sock, wait);
2878a046d57dSUrsula Braun if (sk->sk_err)
2879a9a08845SLinus Torvalds mask |= EPOLLERR;
28808dce2786SUrsula Braun if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
28818dce2786SUrsula Braun (sk->sk_state == SMC_CLOSED))
2882a9a08845SLinus Torvalds mask |= EPOLLHUP;
28838dce2786SUrsula Braun if (sk->sk_state == SMC_LISTEN) {
28848dce2786SUrsula Braun /* woken up by sk_data_ready in smc_listen_work() */
288550717a37SUrsula Braun mask |= smc_accept_poll(sk);
288650717a37SUrsula Braun } else if (smc->use_fallback) { /* as result of connect_work()*/
288750717a37SUrsula Braun mask |= smc->clcsock->ops->poll(file, smc->clcsock,
288850717a37SUrsula Braun wait);
288950717a37SUrsula Braun sk->sk_err = smc->clcsock->sk->sk_err;
28908dce2786SUrsula Braun } else {
289150717a37SUrsula Braun if ((sk->sk_state != SMC_INIT &&
289250717a37SUrsula Braun atomic_read(&smc->conn.sndbuf_space)) ||
28938dce2786SUrsula Braun sk->sk_shutdown & SEND_SHUTDOWN) {
2894a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM;
2895e6727f39SUrsula Braun } else {
2896e6727f39SUrsula Braun sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
2897e6727f39SUrsula Braun set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
28982e0786b3SGuangguan Wang
28992e0786b3SGuangguan Wang if (sk->sk_state != SMC_INIT) {
29002e0786b3SGuangguan Wang /* Race breaker the same way as tcp_poll(). */
29012e0786b3SGuangguan Wang smp_mb__after_atomic();
29022e0786b3SGuangguan Wang if (atomic_read(&smc->conn.sndbuf_space))
29032e0786b3SGuangguan Wang mask |= EPOLLOUT | EPOLLWRNORM;
29042e0786b3SGuangguan Wang }
2905e6727f39SUrsula Braun }
2906952310ccSUrsula Braun if (atomic_read(&smc->conn.bytes_to_rcv))
2907a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
2908b38d7324SUrsula Braun if (sk->sk_shutdown & RCV_SHUTDOWN)
2909a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
2910b38d7324SUrsula Braun if (sk->sk_state == SMC_APPCLOSEWAIT1)
2911a9a08845SLinus Torvalds mask |= EPOLLIN;
2912de8474ebSStefan Raspl if (smc->conn.urg_state == SMC_URG_VALID)
2913de8474ebSStefan Raspl mask |= EPOLLPRI;
2914ac713874SUrsula Braun }
291571d117f5SKarsten Graul }
2916ac713874SUrsula Braun
2917ac713874SUrsula Braun return mask;
2918ac713874SUrsula Braun }
2919ac713874SUrsula Braun
smc_shutdown(struct socket * sock,int how)2920ac713874SUrsula Braun static int smc_shutdown(struct socket *sock, int how)
2921ac713874SUrsula Braun {
2922ac713874SUrsula Braun struct sock *sk = sock->sk;
2923bacb6c1eSTony Lu bool do_shutdown = true;
2924ac713874SUrsula Braun struct smc_sock *smc;
2925ac713874SUrsula Braun int rc = -EINVAL;
2926bacb6c1eSTony Lu int old_state;
2927b38d7324SUrsula Braun int rc1 = 0;
2928ac713874SUrsula Braun
2929ac713874SUrsula Braun smc = smc_sk(sk);
2930ac713874SUrsula Braun
2931ac713874SUrsula Braun if ((how < SHUT_RD) || (how > SHUT_RDWR))
2932b38d7324SUrsula Braun return rc;
2933ac713874SUrsula Braun
2934ac713874SUrsula Braun lock_sock(sk);
2935ac713874SUrsula Braun
29363aba1030SGuangguan Wang if (sock->state == SS_CONNECTING) {
29373aba1030SGuangguan Wang if (sk->sk_state == SMC_ACTIVE)
29383aba1030SGuangguan Wang sock->state = SS_CONNECTED;
29393aba1030SGuangguan Wang else if (sk->sk_state == SMC_PEERCLOSEWAIT1 ||
29403aba1030SGuangguan Wang sk->sk_state == SMC_PEERCLOSEWAIT2 ||
29413aba1030SGuangguan Wang sk->sk_state == SMC_APPCLOSEWAIT1 ||
29423aba1030SGuangguan Wang sk->sk_state == SMC_APPCLOSEWAIT2 ||
29433aba1030SGuangguan Wang sk->sk_state == SMC_APPFINCLOSEWAIT)
29443aba1030SGuangguan Wang sock->state = SS_DISCONNECTING;
29453aba1030SGuangguan Wang }
29463aba1030SGuangguan Wang
2947ac713874SUrsula Braun rc = -ENOTCONN;
2948caa21e19SUrsula Braun if ((sk->sk_state != SMC_ACTIVE) &&
2949b38d7324SUrsula Braun (sk->sk_state != SMC_PEERCLOSEWAIT1) &&
2950b38d7324SUrsula Braun (sk->sk_state != SMC_PEERCLOSEWAIT2) &&
2951b38d7324SUrsula Braun (sk->sk_state != SMC_APPCLOSEWAIT1) &&
2952b38d7324SUrsula Braun (sk->sk_state != SMC_APPCLOSEWAIT2) &&
2953b38d7324SUrsula Braun (sk->sk_state != SMC_APPFINCLOSEWAIT))
2954ac713874SUrsula Braun goto out;
2955ac713874SUrsula Braun if (smc->use_fallback) {
2956ac713874SUrsula Braun rc = kernel_sock_shutdown(smc->clcsock, how);
2957ac713874SUrsula Braun sk->sk_shutdown = smc->clcsock->sk->sk_shutdown;
29581a74e993STony Lu if (sk->sk_shutdown == SHUTDOWN_MASK) {
2959ac713874SUrsula Braun sk->sk_state = SMC_CLOSED;
29603aba1030SGuangguan Wang sk->sk_socket->state = SS_UNCONNECTED;
29611a74e993STony Lu sock_put(sk);
29621a74e993STony Lu }
2963b38d7324SUrsula Braun goto out;
2964ac713874SUrsula Braun }
2965b38d7324SUrsula Braun switch (how) {
2966b38d7324SUrsula Braun case SHUT_RDWR: /* shutdown in both directions */
2967bacb6c1eSTony Lu old_state = sk->sk_state;
2968b38d7324SUrsula Braun rc = smc_close_active(smc);
2969bacb6c1eSTony Lu if (old_state == SMC_ACTIVE &&
2970bacb6c1eSTony Lu sk->sk_state == SMC_PEERCLOSEWAIT1)
2971bacb6c1eSTony Lu do_shutdown = false;
2972b38d7324SUrsula Braun break;
2973b38d7324SUrsula Braun case SHUT_WR:
2974b38d7324SUrsula Braun rc = smc_close_shutdown_write(smc);
2975b38d7324SUrsula Braun break;
2976b38d7324SUrsula Braun case SHUT_RD:
2977b38d7324SUrsula Braun rc = 0;
2978b38d7324SUrsula Braun /* nothing more to do because peer is not involved */
2979b38d7324SUrsula Braun break;
2980b38d7324SUrsula Braun }
2981bacb6c1eSTony Lu if (do_shutdown && smc->clcsock)
2982b38d7324SUrsula Braun rc1 = kernel_sock_shutdown(smc->clcsock, how);
2983b38d7324SUrsula Braun /* map sock_shutdown_cmd constants to sk_shutdown value range */
2984b38d7324SUrsula Braun sk->sk_shutdown |= how + 1;
2985ac713874SUrsula Braun
29863aba1030SGuangguan Wang if (sk->sk_state == SMC_CLOSED)
29873aba1030SGuangguan Wang sock->state = SS_UNCONNECTED;
29883aba1030SGuangguan Wang else
29893aba1030SGuangguan Wang sock->state = SS_DISCONNECTING;
2990ac713874SUrsula Braun out:
2991ac713874SUrsula Braun release_sock(sk);
2992b38d7324SUrsula Braun return rc ? rc : rc1;
2993ac713874SUrsula Braun }
2994ac713874SUrsula Braun
__smc_getsockopt(struct socket * sock,int level,int optname,char __user * optval,int __user * optlen)2995a6a6fe27SD. Wythe static int __smc_getsockopt(struct socket *sock, int level, int optname,
2996a6a6fe27SD. Wythe char __user *optval, int __user *optlen)
2997a6a6fe27SD. Wythe {
2998a6a6fe27SD. Wythe struct smc_sock *smc;
2999a6a6fe27SD. Wythe int val, len;
3000a6a6fe27SD. Wythe
3001a6a6fe27SD. Wythe smc = smc_sk(sock->sk);
3002a6a6fe27SD. Wythe
3003a6a6fe27SD. Wythe if (get_user(len, optlen))
3004a6a6fe27SD. Wythe return -EFAULT;
3005a6a6fe27SD. Wythe
3006a6a6fe27SD. Wythe len = min_t(int, len, sizeof(int));
3007a6a6fe27SD. Wythe
3008a6a6fe27SD. Wythe if (len < 0)
3009a6a6fe27SD. Wythe return -EINVAL;
3010a6a6fe27SD. Wythe
3011a6a6fe27SD. Wythe switch (optname) {
3012a6a6fe27SD. Wythe case SMC_LIMIT_HS:
3013a6a6fe27SD. Wythe val = smc->limit_smc_hs;
3014a6a6fe27SD. Wythe break;
3015a6a6fe27SD. Wythe default:
3016a6a6fe27SD. Wythe return -EOPNOTSUPP;
3017a6a6fe27SD. Wythe }
3018a6a6fe27SD. Wythe
3019a6a6fe27SD. Wythe if (put_user(len, optlen))
3020a6a6fe27SD. Wythe return -EFAULT;
3021a6a6fe27SD. Wythe if (copy_to_user(optval, &val, len))
3022a6a6fe27SD. Wythe return -EFAULT;
3023a6a6fe27SD. Wythe
3024a6a6fe27SD. Wythe return 0;
3025a6a6fe27SD. Wythe }
3026a6a6fe27SD. Wythe
__smc_setsockopt(struct socket * sock,int level,int optname,sockptr_t optval,unsigned int optlen)3027a6a6fe27SD. Wythe static int __smc_setsockopt(struct socket *sock, int level, int optname,
3028a6a6fe27SD. Wythe sockptr_t optval, unsigned int optlen)
3029a6a6fe27SD. Wythe {
3030a6a6fe27SD. Wythe struct sock *sk = sock->sk;
3031a6a6fe27SD. Wythe struct smc_sock *smc;
3032a6a6fe27SD. Wythe int val, rc;
3033a6a6fe27SD. Wythe
3034a6a6fe27SD. Wythe smc = smc_sk(sk);
3035a6a6fe27SD. Wythe
3036a6a6fe27SD. Wythe lock_sock(sk);
3037a6a6fe27SD. Wythe switch (optname) {
3038a6a6fe27SD. Wythe case SMC_LIMIT_HS:
30397a11455fSDan Carpenter if (optlen < sizeof(int)) {
30407a11455fSDan Carpenter rc = -EINVAL;
30417a11455fSDan Carpenter break;
30427a11455fSDan Carpenter }
30437a11455fSDan Carpenter if (copy_from_sockptr(&val, optval, sizeof(int))) {
30447a11455fSDan Carpenter rc = -EFAULT;
30457a11455fSDan Carpenter break;
30467a11455fSDan Carpenter }
3047a6a6fe27SD. Wythe
3048a6a6fe27SD. Wythe smc->limit_smc_hs = !!val;
3049a6a6fe27SD. Wythe rc = 0;
3050a6a6fe27SD. Wythe break;
3051a6a6fe27SD. Wythe default:
3052a6a6fe27SD. Wythe rc = -EOPNOTSUPP;
3053a6a6fe27SD. Wythe break;
3054a6a6fe27SD. Wythe }
3055a6a6fe27SD. Wythe release_sock(sk);
3056a6a6fe27SD. Wythe
3057a6a6fe27SD. Wythe return rc;
3058a6a6fe27SD. Wythe }
3059a6a6fe27SD. Wythe
smc_setsockopt(struct socket * sock,int level,int optname,sockptr_t optval,unsigned int optlen)3060ac713874SUrsula Braun static int smc_setsockopt(struct socket *sock, int level, int optname,
3061a7b75c5aSChristoph Hellwig sockptr_t optval, unsigned int optlen)
3062ac713874SUrsula Braun {
3063ac713874SUrsula Braun struct sock *sk = sock->sk;
3064ac713874SUrsula Braun struct smc_sock *smc;
306501d2f7e2SUrsula Braun int val, rc;
3066ac713874SUrsula Braun
306786214366SCong Wang if (level == SOL_TCP && optname == TCP_ULP)
306886214366SCong Wang return -EOPNOTSUPP;
3069a6a6fe27SD. Wythe else if (level == SOL_SMC)
3070a6a6fe27SD. Wythe return __smc_setsockopt(sock, level, optname, optval, optlen);
307186214366SCong Wang
3072ac713874SUrsula Braun smc = smc_sk(sk);
3073ac713874SUrsula Braun
3074ac713874SUrsula Braun /* generic setsockopts reaching us here always apply to the
3075ac713874SUrsula Braun * CLC socket
3076ac713874SUrsula Braun */
3077c0bf3d8aSWen Gu mutex_lock(&smc->clcsock_release_lock);
3078c0bf3d8aSWen Gu if (!smc->clcsock) {
3079c0bf3d8aSWen Gu mutex_unlock(&smc->clcsock_release_lock);
3080c0bf3d8aSWen Gu return -EBADF;
3081c0bf3d8aSWen Gu }
3082a44d9e72SChristoph Hellwig if (unlikely(!smc->clcsock->ops->setsockopt))
3083a44d9e72SChristoph Hellwig rc = -EOPNOTSUPP;
3084a44d9e72SChristoph Hellwig else
3085ee9dfbefSUrsula Braun rc = smc->clcsock->ops->setsockopt(smc->clcsock, level, optname,
3086ac713874SUrsula Braun optval, optlen);
3087ee9dfbefSUrsula Braun if (smc->clcsock->sk->sk_err) {
3088ee9dfbefSUrsula Braun sk->sk_err = smc->clcsock->sk->sk_err;
3089e3ae2365SAlexander Aring sk_error_report(sk);
3090ee9dfbefSUrsula Braun }
3091c0bf3d8aSWen Gu mutex_unlock(&smc->clcsock_release_lock);
3092ee9dfbefSUrsula Braun
309301d2f7e2SUrsula Braun if (optlen < sizeof(int))
30943dc9f558SWei Yongjun return -EINVAL;
3095a7b75c5aSChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(int)))
3096ac0107edSUrsula Braun return -EFAULT;
309701d2f7e2SUrsula Braun
3098ee9dfbefSUrsula Braun lock_sock(sk);
309986434744SUrsula Braun if (rc || smc->use_fallback)
310086434744SUrsula Braun goto out;
3101ee9dfbefSUrsula Braun switch (optname) {
3102ee9dfbefSUrsula Braun case TCP_FASTOPEN:
3103ee9dfbefSUrsula Braun case TCP_FASTOPEN_CONNECT:
3104ee9dfbefSUrsula Braun case TCP_FASTOPEN_KEY:
3105ee9dfbefSUrsula Braun case TCP_FASTOPEN_NO_COOKIE:
3106ee9dfbefSUrsula Braun /* option not supported by SMC */
31078204df72SUrsula Braun if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
3108c0bf3d8aSWen Gu rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
3109ee9dfbefSUrsula Braun } else {
3110ee9dfbefSUrsula Braun rc = -EINVAL;
3111ee9dfbefSUrsula Braun }
3112ee9dfbefSUrsula Braun break;
311301d2f7e2SUrsula Braun case TCP_NODELAY:
3114f9cedf1aSUrsula Braun if (sk->sk_state != SMC_INIT &&
3115f9cedf1aSUrsula Braun sk->sk_state != SMC_LISTEN &&
3116f9cedf1aSUrsula Braun sk->sk_state != SMC_CLOSED) {
3117e0e4b8faSGuvenc Gulce if (val) {
3118194730a9SGuvenc Gulce SMC_STAT_INC(smc, ndly_cnt);
3119b70a5cc0SDust Li smc_tx_pending(&smc->conn);
3120b70a5cc0SDust Li cancel_delayed_work(&smc->conn.tx_work);
312101d2f7e2SUrsula Braun }
3122e0e4b8faSGuvenc Gulce }
312301d2f7e2SUrsula Braun break;
312401d2f7e2SUrsula Braun case TCP_CORK:
3125f9cedf1aSUrsula Braun if (sk->sk_state != SMC_INIT &&
3126f9cedf1aSUrsula Braun sk->sk_state != SMC_LISTEN &&
3127f9cedf1aSUrsula Braun sk->sk_state != SMC_CLOSED) {
3128e0e4b8faSGuvenc Gulce if (!val) {
3129194730a9SGuvenc Gulce SMC_STAT_INC(smc, cork_cnt);
3130ea785a1aSTony Lu smc_tx_pending(&smc->conn);
3131ea785a1aSTony Lu cancel_delayed_work(&smc->conn.tx_work);
313201d2f7e2SUrsula Braun }
3133e0e4b8faSGuvenc Gulce }
313401d2f7e2SUrsula Braun break;
3135abb190f1SUrsula Braun case TCP_DEFER_ACCEPT:
3136abb190f1SUrsula Braun smc->sockopt_defer_accept = val;
3137abb190f1SUrsula Braun break;
3138ee9dfbefSUrsula Braun default:
3139ee9dfbefSUrsula Braun break;
3140ee9dfbefSUrsula Braun }
314186434744SUrsula Braun out:
3142ee9dfbefSUrsula Braun release_sock(sk);
3143ee9dfbefSUrsula Braun
3144ee9dfbefSUrsula Braun return rc;
3145ac713874SUrsula Braun }
3146ac713874SUrsula Braun
smc_getsockopt(struct socket * sock,int level,int optname,char __user * optval,int __user * optlen)3147ac713874SUrsula Braun static int smc_getsockopt(struct socket *sock, int level, int optname,
3148ac713874SUrsula Braun char __user *optval, int __user *optlen)
3149ac713874SUrsula Braun {
3150ac713874SUrsula Braun struct smc_sock *smc;
3151c0bf3d8aSWen Gu int rc;
3152ac713874SUrsula Braun
3153a6a6fe27SD. Wythe if (level == SOL_SMC)
3154a6a6fe27SD. Wythe return __smc_getsockopt(sock, level, optname, optval, optlen);
3155a6a6fe27SD. Wythe
3156ac713874SUrsula Braun smc = smc_sk(sock->sk);
3157c0bf3d8aSWen Gu mutex_lock(&smc->clcsock_release_lock);
3158c0bf3d8aSWen Gu if (!smc->clcsock) {
3159c0bf3d8aSWen Gu mutex_unlock(&smc->clcsock_release_lock);
3160c0bf3d8aSWen Gu return -EBADF;
3161c0bf3d8aSWen Gu }
3162ac713874SUrsula Braun /* socket options apply to the CLC socket */
3163c0bf3d8aSWen Gu if (unlikely(!smc->clcsock->ops->getsockopt)) {
3164c0bf3d8aSWen Gu mutex_unlock(&smc->clcsock_release_lock);
3165a44d9e72SChristoph Hellwig return -EOPNOTSUPP;
3166c0bf3d8aSWen Gu }
3167c0bf3d8aSWen Gu rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
3168ac713874SUrsula Braun optval, optlen);
3169c0bf3d8aSWen Gu mutex_unlock(&smc->clcsock_release_lock);
3170c0bf3d8aSWen Gu return rc;
3171ac713874SUrsula Braun }
3172ac713874SUrsula Braun
smc_ioctl(struct socket * sock,unsigned int cmd,unsigned long arg)3173ac713874SUrsula Braun static int smc_ioctl(struct socket *sock, unsigned int cmd,
3174ac713874SUrsula Braun unsigned long arg)
3175ac713874SUrsula Braun {
3176de8474ebSStefan Raspl union smc_host_cursor cons, urg;
3177de8474ebSStefan Raspl struct smc_connection *conn;
3178ac713874SUrsula Braun struct smc_sock *smc;
31799b67e26fSUrsula Braun int answ;
3180ac713874SUrsula Braun
3181ac713874SUrsula Braun smc = smc_sk(sock->sk);
3182de8474ebSStefan Raspl conn = &smc->conn;
31831992d998SUrsula Braun lock_sock(&smc->sk);
31847311d665SUrsula Braun if (smc->use_fallback) {
31857311d665SUrsula Braun if (!smc->clcsock) {
31867311d665SUrsula Braun release_sock(&smc->sk);
31877311d665SUrsula Braun return -EBADF;
31887311d665SUrsula Braun }
31897311d665SUrsula Braun answ = smc->clcsock->ops->ioctl(smc->clcsock, cmd, arg);
31907311d665SUrsula Braun release_sock(&smc->sk);
31917311d665SUrsula Braun return answ;
31927311d665SUrsula Braun }
31939b67e26fSUrsula Braun switch (cmd) {
31949b67e26fSUrsula Braun case SIOCINQ: /* same as FIONREAD */
31951992d998SUrsula Braun if (smc->sk.sk_state == SMC_LISTEN) {
31961992d998SUrsula Braun release_sock(&smc->sk);
31979b67e26fSUrsula Braun return -EINVAL;
31981992d998SUrsula Braun }
31992351abe6SUrsula Braun if (smc->sk.sk_state == SMC_INIT ||
32002351abe6SUrsula Braun smc->sk.sk_state == SMC_CLOSED)
32012351abe6SUrsula Braun answ = 0;
32022351abe6SUrsula Braun else
32039b67e26fSUrsula Braun answ = atomic_read(&smc->conn.bytes_to_rcv);
32049b67e26fSUrsula Braun break;
32059b67e26fSUrsula Braun case SIOCOUTQ:
32069b67e26fSUrsula Braun /* output queue size (not send + not acked) */
32071992d998SUrsula Braun if (smc->sk.sk_state == SMC_LISTEN) {
32081992d998SUrsula Braun release_sock(&smc->sk);
32099b67e26fSUrsula Braun return -EINVAL;
32101992d998SUrsula Braun }
32112351abe6SUrsula Braun if (smc->sk.sk_state == SMC_INIT ||
32122351abe6SUrsula Braun smc->sk.sk_state == SMC_CLOSED)
32132351abe6SUrsula Braun answ = 0;
32142351abe6SUrsula Braun else
321569cb7dc0SHans Wippel answ = smc->conn.sndbuf_desc->len -
32169b67e26fSUrsula Braun atomic_read(&smc->conn.sndbuf_space);
32179b67e26fSUrsula Braun break;
32189b67e26fSUrsula Braun case SIOCOUTQNSD:
32199b67e26fSUrsula Braun /* output queue size (not send only) */
32201992d998SUrsula Braun if (smc->sk.sk_state == SMC_LISTEN) {
32211992d998SUrsula Braun release_sock(&smc->sk);
32229b67e26fSUrsula Braun return -EINVAL;
32231992d998SUrsula Braun }
32242351abe6SUrsula Braun if (smc->sk.sk_state == SMC_INIT ||
32252351abe6SUrsula Braun smc->sk.sk_state == SMC_CLOSED)
32262351abe6SUrsula Braun answ = 0;
32272351abe6SUrsula Braun else
32289b67e26fSUrsula Braun answ = smc_tx_prepared_sends(&smc->conn);
32299b67e26fSUrsula Braun break;
3230de8474ebSStefan Raspl case SIOCATMARK:
32311992d998SUrsula Braun if (smc->sk.sk_state == SMC_LISTEN) {
32321992d998SUrsula Braun release_sock(&smc->sk);
3233de8474ebSStefan Raspl return -EINVAL;
32341992d998SUrsula Braun }
3235de8474ebSStefan Raspl if (smc->sk.sk_state == SMC_INIT ||
3236de8474ebSStefan Raspl smc->sk.sk_state == SMC_CLOSED) {
3237de8474ebSStefan Raspl answ = 0;
3238de8474ebSStefan Raspl } else {
3239bac6de7bSStefan Raspl smc_curs_copy(&cons, &conn->local_tx_ctrl.cons, conn);
3240bac6de7bSStefan Raspl smc_curs_copy(&urg, &conn->urg_curs, conn);
3241de8474ebSStefan Raspl answ = smc_curs_diff(conn->rmb_desc->len,
3242de8474ebSStefan Raspl &cons, &urg) == 1;
3243de8474ebSStefan Raspl }
3244de8474ebSStefan Raspl break;
32459b67e26fSUrsula Braun default:
32461992d998SUrsula Braun release_sock(&smc->sk);
32479b67e26fSUrsula Braun return -ENOIOCTLCMD;
32489b67e26fSUrsula Braun }
32491992d998SUrsula Braun release_sock(&smc->sk);
32509b67e26fSUrsula Braun
32519b67e26fSUrsula Braun return put_user(answ, (int __user *)arg);
3252ac713874SUrsula Braun }
3253ac713874SUrsula Braun
32549014db20SStefan Raspl /* Map the affected portions of the rmbe into an spd, note the number of bytes
32559014db20SStefan Raspl * to splice in conn->splice_pending, and press 'go'. Delays consumer cursor
32569014db20SStefan Raspl * updates till whenever a respective page has been fully processed.
32579014db20SStefan Raspl * Note that subsequent recv() calls have to wait till all splice() processing
32589014db20SStefan Raspl * completed.
32599014db20SStefan Raspl */
smc_splice_read(struct socket * sock,loff_t * ppos,struct pipe_inode_info * pipe,size_t len,unsigned int flags)3260ac713874SUrsula Braun static ssize_t smc_splice_read(struct socket *sock, loff_t *ppos,
3261ac713874SUrsula Braun struct pipe_inode_info *pipe, size_t len,
3262ac713874SUrsula Braun unsigned int flags)
3263ac713874SUrsula Braun {
3264ac713874SUrsula Braun struct sock *sk = sock->sk;
3265ac713874SUrsula Braun struct smc_sock *smc;
3266ac713874SUrsula Braun int rc = -ENOTCONN;
3267ac713874SUrsula Braun
3268ac713874SUrsula Braun smc = smc_sk(sk);
3269ac713874SUrsula Braun lock_sock(sk);
327051c5aba3SKarsten Graul if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) {
327151c5aba3SKarsten Graul /* socket was connected before, no more data to read */
327251c5aba3SKarsten Graul rc = 0;
327351c5aba3SKarsten Graul goto out;
327451c5aba3SKarsten Graul }
32759014db20SStefan Raspl if (sk->sk_state == SMC_INIT ||
32769014db20SStefan Raspl sk->sk_state == SMC_LISTEN ||
32779014db20SStefan Raspl sk->sk_state == SMC_CLOSED)
3278ac713874SUrsula Braun goto out;
32799014db20SStefan Raspl
32809014db20SStefan Raspl if (sk->sk_state == SMC_PEERFINCLOSEWAIT) {
32819014db20SStefan Raspl rc = 0;
32829014db20SStefan Raspl goto out;
32839014db20SStefan Raspl }
32849014db20SStefan Raspl
3285ac713874SUrsula Braun if (smc->use_fallback) {
3286ac713874SUrsula Braun rc = smc->clcsock->ops->splice_read(smc->clcsock, ppos,
3287ac713874SUrsula Braun pipe, len, flags);
3288ac713874SUrsula Braun } else {
32899014db20SStefan Raspl if (*ppos) {
32909014db20SStefan Raspl rc = -ESPIPE;
32919014db20SStefan Raspl goto out;
32929014db20SStefan Raspl }
32939014db20SStefan Raspl if (flags & SPLICE_F_NONBLOCK)
32949014db20SStefan Raspl flags = MSG_DONTWAIT;
32959014db20SStefan Raspl else
32969014db20SStefan Raspl flags = 0;
3297194730a9SGuvenc Gulce SMC_STAT_INC(smc, splice_cnt);
32989014db20SStefan Raspl rc = smc_rx_recvmsg(smc, NULL, pipe, len, flags);
3299ac713874SUrsula Braun }
3300ac713874SUrsula Braun out:
3301ac713874SUrsula Braun release_sock(sk);
33029014db20SStefan Raspl
3303ac713874SUrsula Braun return rc;
3304ac713874SUrsula Braun }
3305ac713874SUrsula Braun
3306ac713874SUrsula Braun /* must look like tcp */
3307ac713874SUrsula Braun static const struct proto_ops smc_sock_ops = {
3308ac713874SUrsula Braun .family = PF_SMC,
3309ac713874SUrsula Braun .owner = THIS_MODULE,
3310ac713874SUrsula Braun .release = smc_release,
3311ac713874SUrsula Braun .bind = smc_bind,
3312ac713874SUrsula Braun .connect = smc_connect,
3313ac713874SUrsula Braun .socketpair = sock_no_socketpair,
3314ac713874SUrsula Braun .accept = smc_accept,
3315ac713874SUrsula Braun .getname = smc_getname,
3316a11e1d43SLinus Torvalds .poll = smc_poll,
3317ac713874SUrsula Braun .ioctl = smc_ioctl,
3318ac713874SUrsula Braun .listen = smc_listen,
3319ac713874SUrsula Braun .shutdown = smc_shutdown,
3320ac713874SUrsula Braun .setsockopt = smc_setsockopt,
3321ac713874SUrsula Braun .getsockopt = smc_getsockopt,
3322ac713874SUrsula Braun .sendmsg = smc_sendmsg,
3323ac713874SUrsula Braun .recvmsg = smc_recvmsg,
3324ac713874SUrsula Braun .mmap = sock_no_mmap,
3325ac713874SUrsula Braun .splice_read = smc_splice_read,
3326ac713874SUrsula Braun };
3327ac713874SUrsula Braun
smc_create_clcsk(struct net * net,struct sock * sk,int family)3328f59b799eSD. Wythe int smc_create_clcsk(struct net *net, struct sock *sk, int family)
3329f59b799eSD. Wythe {
3330f59b799eSD. Wythe struct smc_sock *smc = smc_sk(sk);
3331f59b799eSD. Wythe int rc;
3332f59b799eSD. Wythe
3333f59b799eSD. Wythe rc = sock_create_kern(net, family, SOCK_STREAM, IPPROTO_TCP,
3334f59b799eSD. Wythe &smc->clcsock);
3335f59b799eSD. Wythe if (rc) {
3336f59b799eSD. Wythe sk_common_release(sk);
3337f59b799eSD. Wythe return rc;
3338f59b799eSD. Wythe }
3339f59b799eSD. Wythe
3340f59b799eSD. Wythe /* smc_clcsock_release() does not wait smc->clcsock->sk's
3341f59b799eSD. Wythe * destruction; its sk_state might not be TCP_CLOSE after
3342f59b799eSD. Wythe * smc->sk is close()d, and TCP timers can be fired later,
3343f59b799eSD. Wythe * which need net ref.
3344f59b799eSD. Wythe */
3345f59b799eSD. Wythe sk = smc->clcsock->sk;
3346f59b799eSD. Wythe __netns_tracker_free(net, &sk->ns_tracker, false);
3347f59b799eSD. Wythe sk->sk_net_refcnt = 1;
3348f59b799eSD. Wythe get_net_track(net, &sk->ns_tracker, GFP_KERNEL);
3349f59b799eSD. Wythe sock_inuse_add(net, 1);
3350f59b799eSD. Wythe return 0;
3351f59b799eSD. Wythe }
3352f59b799eSD. Wythe
__smc_create(struct net * net,struct socket * sock,int protocol,int kern,struct socket * clcsock)3353d7cd421dSTony Lu static int __smc_create(struct net *net, struct socket *sock, int protocol,
3354d7cd421dSTony Lu int kern, struct socket *clcsock)
3355ac713874SUrsula Braun {
3356aaa4d33fSKarsten Graul int family = (protocol == SMCPROTO_SMC6) ? PF_INET6 : PF_INET;
3357ac713874SUrsula Braun struct smc_sock *smc;
3358ac713874SUrsula Braun struct sock *sk;
3359ac713874SUrsula Braun int rc;
3360ac713874SUrsula Braun
3361ac713874SUrsula Braun rc = -ESOCKTNOSUPPORT;
3362ac713874SUrsula Braun if (sock->type != SOCK_STREAM)
3363ac713874SUrsula Braun goto out;
3364ac713874SUrsula Braun
3365ac713874SUrsula Braun rc = -EPROTONOSUPPORT;
3366aaa4d33fSKarsten Graul if (protocol != SMCPROTO_SMC && protocol != SMCPROTO_SMC6)
3367ac713874SUrsula Braun goto out;
3368ac713874SUrsula Braun
3369ac713874SUrsula Braun rc = -ENOBUFS;
3370ac713874SUrsula Braun sock->ops = &smc_sock_ops;
33713aba1030SGuangguan Wang sock->state = SS_UNCONNECTED;
3372aaa4d33fSKarsten Graul sk = smc_sock_alloc(net, sock, protocol);
3373ac713874SUrsula Braun if (!sk)
3374ac713874SUrsula Braun goto out;
3375ac713874SUrsula Braun
3376ac713874SUrsula Braun /* create internal TCP socket for CLC handshake and fallback */
3377ac713874SUrsula Braun smc = smc_sk(sk);
3378f9496b7cSD. Wythe
3379d7cd421dSTony Lu rc = 0;
3380f59b799eSD. Wythe if (clcsock)
3381d7cd421dSTony Lu smc->clcsock = clcsock;
3382f59b799eSD. Wythe else
3383f59b799eSD. Wythe rc = smc_create_clcsk(net, sk, family);
3384ac713874SUrsula Braun out:
3385ac713874SUrsula Braun return rc;
3386ac713874SUrsula Braun }
3387ac713874SUrsula Braun
smc_create(struct net * net,struct socket * sock,int protocol,int kern)3388d7cd421dSTony Lu static int smc_create(struct net *net, struct socket *sock, int protocol,
3389d7cd421dSTony Lu int kern)
3390d7cd421dSTony Lu {
3391d7cd421dSTony Lu return __smc_create(net, sock, protocol, kern, NULL);
3392d7cd421dSTony Lu }
3393d7cd421dSTony Lu
3394ac713874SUrsula Braun static const struct net_proto_family smc_sock_family_ops = {
3395ac713874SUrsula Braun .family = PF_SMC,
3396ac713874SUrsula Braun .owner = THIS_MODULE,
3397ac713874SUrsula Braun .create = smc_create,
3398ac713874SUrsula Braun };
3399ac713874SUrsula Braun
smc_ulp_init(struct sock * sk)3400d7cd421dSTony Lu static int smc_ulp_init(struct sock *sk)
3401d7cd421dSTony Lu {
3402d7cd421dSTony Lu struct socket *tcp = sk->sk_socket;
3403d7cd421dSTony Lu struct net *net = sock_net(sk);
3404d7cd421dSTony Lu struct socket *smcsock;
3405d7cd421dSTony Lu int protocol, ret;
3406d7cd421dSTony Lu
3407d7cd421dSTony Lu /* only TCP can be replaced */
3408d7cd421dSTony Lu if (tcp->type != SOCK_STREAM || sk->sk_protocol != IPPROTO_TCP ||
3409d7cd421dSTony Lu (sk->sk_family != AF_INET && sk->sk_family != AF_INET6))
3410d7cd421dSTony Lu return -ESOCKTNOSUPPORT;
3411d7cd421dSTony Lu /* don't handle wq now */
3412d7cd421dSTony Lu if (tcp->state != SS_UNCONNECTED || !tcp->file || tcp->wq.fasync_list)
3413d7cd421dSTony Lu return -ENOTCONN;
3414d7cd421dSTony Lu
3415d7cd421dSTony Lu if (sk->sk_family == AF_INET)
3416d7cd421dSTony Lu protocol = SMCPROTO_SMC;
3417d7cd421dSTony Lu else
3418d7cd421dSTony Lu protocol = SMCPROTO_SMC6;
3419d7cd421dSTony Lu
3420d7cd421dSTony Lu smcsock = sock_alloc();
3421d7cd421dSTony Lu if (!smcsock)
3422d7cd421dSTony Lu return -ENFILE;
3423d7cd421dSTony Lu
3424d7cd421dSTony Lu smcsock->type = SOCK_STREAM;
3425d7cd421dSTony Lu __module_get(THIS_MODULE); /* tried in __tcp_ulp_find_autoload */
3426d7cd421dSTony Lu ret = __smc_create(net, smcsock, protocol, 1, tcp);
3427d7cd421dSTony Lu if (ret) {
3428d7cd421dSTony Lu sock_release(smcsock); /* module_put() which ops won't be NULL */
3429d7cd421dSTony Lu return ret;
3430d7cd421dSTony Lu }
3431d7cd421dSTony Lu
3432d7cd421dSTony Lu /* replace tcp socket to smc */
3433d7cd421dSTony Lu smcsock->file = tcp->file;
3434d7cd421dSTony Lu smcsock->file->private_data = smcsock;
3435d7cd421dSTony Lu smcsock->file->f_inode = SOCK_INODE(smcsock); /* replace inode when sock_close */
3436d7cd421dSTony Lu smcsock->file->f_path.dentry->d_inode = SOCK_INODE(smcsock); /* dput() in __fput */
3437d7cd421dSTony Lu tcp->file = NULL;
3438d7cd421dSTony Lu
3439d7cd421dSTony Lu return ret;
3440d7cd421dSTony Lu }
3441d7cd421dSTony Lu
smc_ulp_clone(const struct request_sock * req,struct sock * newsk,const gfp_t priority)3442d7cd421dSTony Lu static void smc_ulp_clone(const struct request_sock *req, struct sock *newsk,
3443d7cd421dSTony Lu const gfp_t priority)
3444d7cd421dSTony Lu {
3445d7cd421dSTony Lu struct inet_connection_sock *icsk = inet_csk(newsk);
3446d7cd421dSTony Lu
3447d7cd421dSTony Lu /* don't inherit ulp ops to child when listen */
3448d7cd421dSTony Lu icsk->icsk_ulp_ops = NULL;
3449d7cd421dSTony Lu }
3450d7cd421dSTony Lu
3451d7cd421dSTony Lu static struct tcp_ulp_ops smc_ulp_ops __read_mostly = {
3452d7cd421dSTony Lu .name = "smc",
3453d7cd421dSTony Lu .owner = THIS_MODULE,
3454d7cd421dSTony Lu .init = smc_ulp_init,
3455d7cd421dSTony Lu .clone = smc_ulp_clone,
3456d7cd421dSTony Lu };
3457d7cd421dSTony Lu
345864e28b52SHans Wippel unsigned int smc_net_id;
345964e28b52SHans Wippel
smc_net_init(struct net * net)346064e28b52SHans Wippel static __net_init int smc_net_init(struct net *net)
346164e28b52SHans Wippel {
34627de8eb0dSDust Li int rc;
34637de8eb0dSDust Li
34647de8eb0dSDust Li rc = smc_sysctl_net_init(net);
34657de8eb0dSDust Li if (rc)
34667de8eb0dSDust Li return rc;
346764e28b52SHans Wippel return smc_pnet_net_init(net);
346864e28b52SHans Wippel }
346964e28b52SHans Wippel
smc_net_exit(struct net * net)347064e28b52SHans Wippel static void __net_exit smc_net_exit(struct net *net)
347164e28b52SHans Wippel {
34727de8eb0dSDust Li smc_sysctl_net_exit(net);
347364e28b52SHans Wippel smc_pnet_net_exit(net);
347464e28b52SHans Wippel }
347564e28b52SHans Wippel
smc_net_stat_init(struct net * net)3476194730a9SGuvenc Gulce static __net_init int smc_net_stat_init(struct net *net)
3477194730a9SGuvenc Gulce {
3478194730a9SGuvenc Gulce return smc_stats_init(net);
3479194730a9SGuvenc Gulce }
3480194730a9SGuvenc Gulce
smc_net_stat_exit(struct net * net)3481194730a9SGuvenc Gulce static void __net_exit smc_net_stat_exit(struct net *net)
3482194730a9SGuvenc Gulce {
3483194730a9SGuvenc Gulce smc_stats_exit(net);
3484194730a9SGuvenc Gulce }
3485194730a9SGuvenc Gulce
348664e28b52SHans Wippel static struct pernet_operations smc_net_ops = {
348764e28b52SHans Wippel .init = smc_net_init,
348864e28b52SHans Wippel .exit = smc_net_exit,
348964e28b52SHans Wippel .id = &smc_net_id,
349064e28b52SHans Wippel .size = sizeof(struct smc_net),
349164e28b52SHans Wippel };
349264e28b52SHans Wippel
3493194730a9SGuvenc Gulce static struct pernet_operations smc_net_stat_ops = {
3494194730a9SGuvenc Gulce .init = smc_net_stat_init,
3495194730a9SGuvenc Gulce .exit = smc_net_stat_exit,
3496194730a9SGuvenc Gulce };
3497194730a9SGuvenc Gulce
smc_init(void)3498ac713874SUrsula Braun static int __init smc_init(void)
3499ac713874SUrsula Braun {
3500ac713874SUrsula Braun int rc;
3501ac713874SUrsula Braun
350264e28b52SHans Wippel rc = register_pernet_subsys(&smc_net_ops);
350364e28b52SHans Wippel if (rc)
350464e28b52SHans Wippel return rc;
350564e28b52SHans Wippel
3506194730a9SGuvenc Gulce rc = register_pernet_subsys(&smc_net_stat_ops);
3507194730a9SGuvenc Gulce if (rc)
350862ff373dSChen Zhongjin goto out_pernet_subsys;
3509194730a9SGuvenc Gulce
35108747716fSStefan Raspl rc = smc_ism_init();
35118747716fSStefan Raspl if (rc)
35128747716fSStefan Raspl goto out_pernet_subsys_stat;
3513b81a5eb7SUrsula Braun smc_clc_init();
3514201091ebSUrsula Braun
3515e8372d9dSGuvenc Gulce rc = smc_nl_init();
35166812baabSThomas Richter if (rc)
35178747716fSStefan Raspl goto out_ism;
35186812baabSThomas Richter
3519e8372d9dSGuvenc Gulce rc = smc_pnet_init();
3520e8372d9dSGuvenc Gulce if (rc)
3521e8372d9dSGuvenc Gulce goto out_nl;
3522e8372d9dSGuvenc Gulce
352322ef473dSKarsten Graul rc = -ENOMEM;
35243079e342SD. Wythe
35253079e342SD. Wythe smc_tcp_ls_wq = alloc_workqueue("smc_tcp_ls_wq", 0, 0);
35263079e342SD. Wythe if (!smc_tcp_ls_wq)
35273079e342SD. Wythe goto out_pnet;
35283079e342SD. Wythe
352922ef473dSKarsten Graul smc_hs_wq = alloc_workqueue("smc_hs_wq", 0, 0);
353022ef473dSKarsten Graul if (!smc_hs_wq)
35313079e342SD. Wythe goto out_alloc_tcp_ls_wq;
353222ef473dSKarsten Graul
353322ef473dSKarsten Graul smc_close_wq = alloc_workqueue("smc_close_wq", 0, 0);
353422ef473dSKarsten Graul if (!smc_close_wq)
353522ef473dSKarsten Graul goto out_alloc_hs_wq;
353622ef473dSKarsten Graul
35376dabd405SUrsula Braun rc = smc_core_init();
35386dabd405SUrsula Braun if (rc) {
35396dabd405SUrsula Braun pr_err("%s: smc_core_init fails with %d\n", __func__, rc);
3540194730a9SGuvenc Gulce goto out_alloc_wqs;
35416dabd405SUrsula Braun }
35426dabd405SUrsula Braun
35439bf9abeaSUrsula Braun rc = smc_llc_init();
35449bf9abeaSUrsula Braun if (rc) {
35459bf9abeaSUrsula Braun pr_err("%s: smc_llc_init fails with %d\n", __func__, rc);
35466dabd405SUrsula Braun goto out_core;
35479bf9abeaSUrsula Braun }
35489bf9abeaSUrsula Braun
35495f08318fSUrsula Braun rc = smc_cdc_init();
35505f08318fSUrsula Braun if (rc) {
35515f08318fSUrsula Braun pr_err("%s: smc_cdc_init fails with %d\n", __func__, rc);
35526dabd405SUrsula Braun goto out_core;
35535f08318fSUrsula Braun }
35545f08318fSUrsula Braun
3555ac713874SUrsula Braun rc = proto_register(&smc_proto, 1);
3556ac713874SUrsula Braun if (rc) {
3557aaa4d33fSKarsten Graul pr_err("%s: proto_register(v4) fails with %d\n", __func__, rc);
35586dabd405SUrsula Braun goto out_core;
3559ac713874SUrsula Braun }
3560ac713874SUrsula Braun
3561aaa4d33fSKarsten Graul rc = proto_register(&smc_proto6, 1);
3562aaa4d33fSKarsten Graul if (rc) {
3563aaa4d33fSKarsten Graul pr_err("%s: proto_register(v6) fails with %d\n", __func__, rc);
3564aaa4d33fSKarsten Graul goto out_proto;
3565aaa4d33fSKarsten Graul }
3566aaa4d33fSKarsten Graul
3567ac713874SUrsula Braun rc = sock_register(&smc_sock_family_ops);
3568ac713874SUrsula Braun if (rc) {
3569ac713874SUrsula Braun pr_err("%s: sock_register fails with %d\n", __func__, rc);
3570aaa4d33fSKarsten Graul goto out_proto6;
3571ac713874SUrsula Braun }
3572f16a7dd5SUrsula Braun INIT_HLIST_HEAD(&smc_v4_hashinfo.ht);
3573aaa4d33fSKarsten Graul INIT_HLIST_HEAD(&smc_v6_hashinfo.ht);
3574ac713874SUrsula Braun
3575a4cf0443SUrsula Braun rc = smc_ib_register_client();
3576a4cf0443SUrsula Braun if (rc) {
3577a4cf0443SUrsula Braun pr_err("%s: ib_register fails with %d\n", __func__, rc);
3578a4cf0443SUrsula Braun goto out_sock;
3579a4cf0443SUrsula Braun }
3580a4cf0443SUrsula Braun
3581d7cd421dSTony Lu rc = tcp_register_ulp(&smc_ulp_ops);
3582d7cd421dSTony Lu if (rc) {
3583d7cd421dSTony Lu pr_err("%s: tcp_ulp_register fails with %d\n", __func__, rc);
35844d08b7b5STony Lu goto out_ib;
3585d7cd421dSTony Lu }
3586d7cd421dSTony Lu
3587c5c1cc9cSUrsula Braun static_branch_enable(&tcp_have_smc);
3588ac713874SUrsula Braun return 0;
3589ac713874SUrsula Braun
35904d08b7b5STony Lu out_ib:
35914d08b7b5STony Lu smc_ib_unregister_client();
3592a4cf0443SUrsula Braun out_sock:
3593a4cf0443SUrsula Braun sock_unregister(PF_SMC);
3594aaa4d33fSKarsten Graul out_proto6:
3595aaa4d33fSKarsten Graul proto_unregister(&smc_proto6);
3596ac713874SUrsula Braun out_proto:
3597ac713874SUrsula Braun proto_unregister(&smc_proto);
35986dabd405SUrsula Braun out_core:
35996dabd405SUrsula Braun smc_core_exit();
360022ef473dSKarsten Graul out_alloc_wqs:
360122ef473dSKarsten Graul destroy_workqueue(smc_close_wq);
360222ef473dSKarsten Graul out_alloc_hs_wq:
360322ef473dSKarsten Graul destroy_workqueue(smc_hs_wq);
36043079e342SD. Wythe out_alloc_tcp_ls_wq:
36053079e342SD. Wythe destroy_workqueue(smc_tcp_ls_wq);
36066812baabSThomas Richter out_pnet:
36076812baabSThomas Richter smc_pnet_exit();
3608e8372d9dSGuvenc Gulce out_nl:
3609e8372d9dSGuvenc Gulce smc_nl_exit();
36108747716fSStefan Raspl out_ism:
36119d876d3eSStefan Raspl smc_clc_exit();
36128747716fSStefan Raspl smc_ism_exit();
361362ff373dSChen Zhongjin out_pernet_subsys_stat:
361462ff373dSChen Zhongjin unregister_pernet_subsys(&smc_net_stat_ops);
36158c33bf1bSYueHaibing out_pernet_subsys:
36168c33bf1bSYueHaibing unregister_pernet_subsys(&smc_net_ops);
36178c33bf1bSYueHaibing
3618ac713874SUrsula Braun return rc;
3619ac713874SUrsula Braun }
3620ac713874SUrsula Braun
smc_exit(void)3621ac713874SUrsula Braun static void __exit smc_exit(void)
3622ac713874SUrsula Braun {
3623c5c1cc9cSUrsula Braun static_branch_disable(&tcp_have_smc);
3624d7cd421dSTony Lu tcp_unregister_ulp(&smc_ulp_ops);
3625ac713874SUrsula Braun sock_unregister(PF_SMC);
36266dabd405SUrsula Braun smc_core_exit();
36276dabd405SUrsula Braun smc_ib_unregister_client();
36288c81ba20SStefan Raspl smc_ism_exit();
362922ef473dSKarsten Graul destroy_workqueue(smc_close_wq);
36303079e342SD. Wythe destroy_workqueue(smc_tcp_ls_wq);
363122ef473dSKarsten Graul destroy_workqueue(smc_hs_wq);
3632aaa4d33fSKarsten Graul proto_unregister(&smc_proto6);
3633ac713874SUrsula Braun proto_unregister(&smc_proto);
36346812baabSThomas Richter smc_pnet_exit();
3635e8372d9dSGuvenc Gulce smc_nl_exit();
3636fa086662SKarsten Graul smc_clc_exit();
3637194730a9SGuvenc Gulce unregister_pernet_subsys(&smc_net_stat_ops);
363864e28b52SHans Wippel unregister_pernet_subsys(&smc_net_ops);
36394ead9c96SUrsula Braun rcu_barrier();
3640ac713874SUrsula Braun }
3641ac713874SUrsula Braun
3642ac713874SUrsula Braun module_init(smc_init);
3643ac713874SUrsula Braun module_exit(smc_exit);
3644ac713874SUrsula Braun
3645ac713874SUrsula Braun MODULE_AUTHOR("Ursula Braun <ubraun@linux.vnet.ibm.com>");
3646ac713874SUrsula Braun MODULE_DESCRIPTION("smc socket address family");
3647ac713874SUrsula Braun MODULE_LICENSE("GPL");
3648ac713874SUrsula Braun MODULE_ALIAS_NETPROTO(PF_SMC);
3649d7cd421dSTony Lu MODULE_ALIAS_TCP_ULP("smc");
365028ec53f3SStefan Raspl MODULE_ALIAS_GENL_FAMILY(SMC_GENL_FAMILY_NAME);
3651