xref: /openbmc/linux/net/smc/af_smc.c (revision 060f35a317ef09101b128f399dce7ed13d019461)
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