18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Shared Memory Communications over RDMA (SMC-R) and RoCE
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  AF_SMC protocol family socket handler keeping the AF_INET sock address type
68c2ecf20Sopenharmony_ci *  applies to SOCK_STREAM sockets only
78c2ecf20Sopenharmony_ci *  offers an alternative communication option for TCP-protocol sockets
88c2ecf20Sopenharmony_ci *  applicable with RoCE-cards only
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  Initial restrictions:
118c2ecf20Sopenharmony_ci *    - support for alternate links postponed
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2016, 2018
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *  Author(s):  Ursula Braun <ubraun@linux.vnet.ibm.com>
168c2ecf20Sopenharmony_ci *              based on prototype from Frank Blaschka
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "smc"
208c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/socket.h>
248c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
258c2ecf20Sopenharmony_ci#include <linux/in.h>
268c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
278c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
288c2ecf20Sopenharmony_ci#include <linux/rcupdate_wait.h>
298c2ecf20Sopenharmony_ci#include <linux/ctype.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <net/sock.h>
328c2ecf20Sopenharmony_ci#include <net/tcp.h>
338c2ecf20Sopenharmony_ci#include <net/smc.h>
348c2ecf20Sopenharmony_ci#include <asm/ioctls.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
378c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
388c2ecf20Sopenharmony_ci#include "smc_netns.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "smc.h"
418c2ecf20Sopenharmony_ci#include "smc_clc.h"
428c2ecf20Sopenharmony_ci#include "smc_llc.h"
438c2ecf20Sopenharmony_ci#include "smc_cdc.h"
448c2ecf20Sopenharmony_ci#include "smc_core.h"
458c2ecf20Sopenharmony_ci#include "smc_ib.h"
468c2ecf20Sopenharmony_ci#include "smc_ism.h"
478c2ecf20Sopenharmony_ci#include "smc_pnet.h"
488c2ecf20Sopenharmony_ci#include "smc_tx.h"
498c2ecf20Sopenharmony_ci#include "smc_rx.h"
508c2ecf20Sopenharmony_ci#include "smc_close.h"
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(smc_server_lgr_pending);	/* serialize link group
538c2ecf20Sopenharmony_ci						 * creation on server
548c2ecf20Sopenharmony_ci						 */
558c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(smc_client_lgr_pending);	/* serialize link group
568c2ecf20Sopenharmony_ci						 * creation on client
578c2ecf20Sopenharmony_ci						 */
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct workqueue_struct	*smc_hs_wq;	/* wq for handshake work */
608c2ecf20Sopenharmony_cistruct workqueue_struct	*smc_close_wq;	/* wq for close work */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void smc_tcp_listen_work(struct work_struct *);
638c2ecf20Sopenharmony_cistatic void smc_connect_work(struct work_struct *);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void smc_set_keepalive(struct sock *sk, int val)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct smc_sock *smc = smc_sk(sk);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	smc->clcsock->sk->sk_prot->keepalive(smc->clcsock->sk, val);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic struct smc_hashinfo smc_v4_hashinfo = {
738c2ecf20Sopenharmony_ci	.lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock),
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic struct smc_hashinfo smc_v6_hashinfo = {
778c2ecf20Sopenharmony_ci	.lock = __RW_LOCK_UNLOCKED(smc_v6_hashinfo.lock),
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciint smc_hash_sk(struct sock *sk)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct smc_hashinfo *h = sk->sk_prot->h.smc_hash;
838c2ecf20Sopenharmony_ci	struct hlist_head *head;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	head = &h->ht;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	write_lock_bh(&h->lock);
888c2ecf20Sopenharmony_ci	sk_add_node(sk, head);
898c2ecf20Sopenharmony_ci	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
908c2ecf20Sopenharmony_ci	write_unlock_bh(&h->lock);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_hash_sk);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_civoid smc_unhash_sk(struct sock *sk)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct smc_hashinfo *h = sk->sk_prot->h.smc_hash;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	write_lock_bh(&h->lock);
1018c2ecf20Sopenharmony_ci	if (sk_del_node_init(sk))
1028c2ecf20Sopenharmony_ci		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
1038c2ecf20Sopenharmony_ci	write_unlock_bh(&h->lock);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_unhash_sk);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistruct proto smc_proto = {
1088c2ecf20Sopenharmony_ci	.name		= "SMC",
1098c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1108c2ecf20Sopenharmony_ci	.keepalive	= smc_set_keepalive,
1118c2ecf20Sopenharmony_ci	.hash		= smc_hash_sk,
1128c2ecf20Sopenharmony_ci	.unhash		= smc_unhash_sk,
1138c2ecf20Sopenharmony_ci	.obj_size	= sizeof(struct smc_sock),
1148c2ecf20Sopenharmony_ci	.h.smc_hash	= &smc_v4_hashinfo,
1158c2ecf20Sopenharmony_ci	.slab_flags	= SLAB_TYPESAFE_BY_RCU,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_proto);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct proto smc_proto6 = {
1208c2ecf20Sopenharmony_ci	.name		= "SMC6",
1218c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1228c2ecf20Sopenharmony_ci	.keepalive	= smc_set_keepalive,
1238c2ecf20Sopenharmony_ci	.hash		= smc_hash_sk,
1248c2ecf20Sopenharmony_ci	.unhash		= smc_unhash_sk,
1258c2ecf20Sopenharmony_ci	.obj_size	= sizeof(struct smc_sock),
1268c2ecf20Sopenharmony_ci	.h.smc_hash	= &smc_v6_hashinfo,
1278c2ecf20Sopenharmony_ci	.slab_flags	= SLAB_TYPESAFE_BY_RCU,
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_proto6);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic void smc_restore_fallback_changes(struct smc_sock *smc)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	if (smc->clcsock->file) { /* non-accepted sockets have no file yet */
1348c2ecf20Sopenharmony_ci		smc->clcsock->file->private_data = smc->sk.sk_socket;
1358c2ecf20Sopenharmony_ci		smc->clcsock->file = NULL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int __smc_release(struct smc_sock *smc)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct sock *sk = &smc->sk;
1428c2ecf20Sopenharmony_ci	int rc = 0;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!smc->use_fallback) {
1458c2ecf20Sopenharmony_ci		rc = smc_close_active(smc);
1468c2ecf20Sopenharmony_ci		smc_sock_set_flag(sk, SOCK_DEAD);
1478c2ecf20Sopenharmony_ci		sk->sk_shutdown |= SHUTDOWN_MASK;
1488c2ecf20Sopenharmony_ci	} else {
1498c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_CLOSED) {
1508c2ecf20Sopenharmony_ci			if (sk->sk_state != SMC_LISTEN &&
1518c2ecf20Sopenharmony_ci			    sk->sk_state != SMC_INIT)
1528c2ecf20Sopenharmony_ci				sock_put(sk); /* passive closing */
1538c2ecf20Sopenharmony_ci			if (sk->sk_state == SMC_LISTEN) {
1548c2ecf20Sopenharmony_ci				/* wake up clcsock accept */
1558c2ecf20Sopenharmony_ci				rc = kernel_sock_shutdown(smc->clcsock,
1568c2ecf20Sopenharmony_ci							  SHUT_RDWR);
1578c2ecf20Sopenharmony_ci			}
1588c2ecf20Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
1598c2ecf20Sopenharmony_ci			sk->sk_state_change(sk);
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci		smc_restore_fallback_changes(smc);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	sk->sk_prot->unhash(sk);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_CLOSED) {
1678c2ecf20Sopenharmony_ci		if (smc->clcsock) {
1688c2ecf20Sopenharmony_ci			release_sock(sk);
1698c2ecf20Sopenharmony_ci			smc_clcsock_release(smc);
1708c2ecf20Sopenharmony_ci			lock_sock(sk);
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		if (!smc->use_fallback)
1738c2ecf20Sopenharmony_ci			smc_conn_free(&smc->conn);
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return rc;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int smc_release(struct socket *sock)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
1828c2ecf20Sopenharmony_ci	struct smc_sock *smc;
1838c2ecf20Sopenharmony_ci	int old_state, rc = 0;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (!sk)
1868c2ecf20Sopenharmony_ci		goto out;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	sock_hold(sk); /* sock_put below */
1898c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	old_state = sk->sk_state;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* cleanup for a dangling non-blocking connect */
1948c2ecf20Sopenharmony_ci	if (smc->connect_nonblock && old_state == SMC_INIT)
1958c2ecf20Sopenharmony_ci		tcp_abort(smc->clcsock->sk, ECONNABORTED);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (cancel_work_sync(&smc->connect_work))
1988c2ecf20Sopenharmony_ci		sock_put(&smc->sk); /* sock_hold in smc_connect for passive closing */
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_LISTEN)
2018c2ecf20Sopenharmony_ci		/* smc_close_non_accepted() is called and acquires
2028c2ecf20Sopenharmony_ci		 * sock lock for child sockets again
2038c2ecf20Sopenharmony_ci		 */
2048c2ecf20Sopenharmony_ci		lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
2058c2ecf20Sopenharmony_ci	else
2068c2ecf20Sopenharmony_ci		lock_sock(sk);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (old_state == SMC_INIT && sk->sk_state == SMC_ACTIVE &&
2098c2ecf20Sopenharmony_ci	    !smc->use_fallback)
2108c2ecf20Sopenharmony_ci		smc_close_active_abort(smc);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	rc = __smc_release(smc);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* detach socket */
2158c2ecf20Sopenharmony_ci	sock_orphan(sk);
2168c2ecf20Sopenharmony_ci	sock->sk = NULL;
2178c2ecf20Sopenharmony_ci	release_sock(sk);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	sock_put(sk); /* sock_hold above */
2208c2ecf20Sopenharmony_ci	sock_put(sk); /* final sock_put */
2218c2ecf20Sopenharmony_ciout:
2228c2ecf20Sopenharmony_ci	return rc;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void smc_destruct(struct sock *sk)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (sk->sk_state != SMC_CLOSED)
2288c2ecf20Sopenharmony_ci		return;
2298c2ecf20Sopenharmony_ci	if (!sock_flag(sk, SOCK_DEAD))
2308c2ecf20Sopenharmony_ci		return;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	sk_refcnt_debug_dec(sk);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic struct sock *smc_sock_alloc(struct net *net, struct socket *sock,
2368c2ecf20Sopenharmony_ci				   int protocol)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct smc_sock *smc;
2398c2ecf20Sopenharmony_ci	struct proto *prot;
2408c2ecf20Sopenharmony_ci	struct sock *sk;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	prot = (protocol == SMCPROTO_SMC6) ? &smc_proto6 : &smc_proto;
2438c2ecf20Sopenharmony_ci	sk = sk_alloc(net, PF_SMC, GFP_KERNEL, prot, 0);
2448c2ecf20Sopenharmony_ci	if (!sk)
2458c2ecf20Sopenharmony_ci		return NULL;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	sock_init_data(sock, sk); /* sets sk_refcnt to 1 */
2488c2ecf20Sopenharmony_ci	sk->sk_state = SMC_INIT;
2498c2ecf20Sopenharmony_ci	sk->sk_destruct = smc_destruct;
2508c2ecf20Sopenharmony_ci	sk->sk_protocol = protocol;
2518c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
2528c2ecf20Sopenharmony_ci	INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work);
2538c2ecf20Sopenharmony_ci	INIT_WORK(&smc->connect_work, smc_connect_work);
2548c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&smc->conn.tx_work, smc_tx_work);
2558c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&smc->accept_q);
2568c2ecf20Sopenharmony_ci	spin_lock_init(&smc->accept_q_lock);
2578c2ecf20Sopenharmony_ci	spin_lock_init(&smc->conn.send_lock);
2588c2ecf20Sopenharmony_ci	sk->sk_prot->hash(sk);
2598c2ecf20Sopenharmony_ci	sk_refcnt_debug_inc(sk);
2608c2ecf20Sopenharmony_ci	mutex_init(&smc->clcsock_release_lock);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return sk;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int smc_bind(struct socket *sock, struct sockaddr *uaddr,
2668c2ecf20Sopenharmony_ci		    int addr_len)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
2698c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
2708c2ecf20Sopenharmony_ci	struct smc_sock *smc;
2718c2ecf20Sopenharmony_ci	int rc;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* replicate tests from inet_bind(), to be safe wrt. future changes */
2768c2ecf20Sopenharmony_ci	rc = -EINVAL;
2778c2ecf20Sopenharmony_ci	if (addr_len < sizeof(struct sockaddr_in))
2788c2ecf20Sopenharmony_ci		goto out;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	rc = -EAFNOSUPPORT;
2818c2ecf20Sopenharmony_ci	if (addr->sin_family != AF_INET &&
2828c2ecf20Sopenharmony_ci	    addr->sin_family != AF_INET6 &&
2838c2ecf20Sopenharmony_ci	    addr->sin_family != AF_UNSPEC)
2848c2ecf20Sopenharmony_ci		goto out;
2858c2ecf20Sopenharmony_ci	/* accept AF_UNSPEC (mapped to AF_INET) only if s_addr is INADDR_ANY */
2868c2ecf20Sopenharmony_ci	if (addr->sin_family == AF_UNSPEC &&
2878c2ecf20Sopenharmony_ci	    addr->sin_addr.s_addr != htonl(INADDR_ANY))
2888c2ecf20Sopenharmony_ci		goto out;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	lock_sock(sk);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/* Check if socket is already active */
2938c2ecf20Sopenharmony_ci	rc = -EINVAL;
2948c2ecf20Sopenharmony_ci	if (sk->sk_state != SMC_INIT || smc->connect_nonblock)
2958c2ecf20Sopenharmony_ci		goto out_rel;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	smc->clcsock->sk->sk_reuse = sk->sk_reuse;
2988c2ecf20Sopenharmony_ci	rc = kernel_bind(smc->clcsock, uaddr, addr_len);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ciout_rel:
3018c2ecf20Sopenharmony_ci	release_sock(sk);
3028c2ecf20Sopenharmony_ciout:
3038c2ecf20Sopenharmony_ci	return rc;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void smc_copy_sock_settings(struct sock *nsk, struct sock *osk,
3078c2ecf20Sopenharmony_ci				   unsigned long mask)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	/* options we don't get control via setsockopt for */
3108c2ecf20Sopenharmony_ci	nsk->sk_type = osk->sk_type;
3118c2ecf20Sopenharmony_ci	nsk->sk_sndbuf = osk->sk_sndbuf;
3128c2ecf20Sopenharmony_ci	nsk->sk_rcvbuf = osk->sk_rcvbuf;
3138c2ecf20Sopenharmony_ci	nsk->sk_sndtimeo = osk->sk_sndtimeo;
3148c2ecf20Sopenharmony_ci	nsk->sk_rcvtimeo = osk->sk_rcvtimeo;
3158c2ecf20Sopenharmony_ci	nsk->sk_mark = osk->sk_mark;
3168c2ecf20Sopenharmony_ci	nsk->sk_priority = osk->sk_priority;
3178c2ecf20Sopenharmony_ci	nsk->sk_rcvlowat = osk->sk_rcvlowat;
3188c2ecf20Sopenharmony_ci	nsk->sk_bound_dev_if = osk->sk_bound_dev_if;
3198c2ecf20Sopenharmony_ci	nsk->sk_err = osk->sk_err;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	nsk->sk_flags &= ~mask;
3228c2ecf20Sopenharmony_ci	nsk->sk_flags |= osk->sk_flags & mask;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci#define SK_FLAGS_SMC_TO_CLC ((1UL << SOCK_URGINLINE) | \
3268c2ecf20Sopenharmony_ci			     (1UL << SOCK_KEEPOPEN) | \
3278c2ecf20Sopenharmony_ci			     (1UL << SOCK_LINGER) | \
3288c2ecf20Sopenharmony_ci			     (1UL << SOCK_BROADCAST) | \
3298c2ecf20Sopenharmony_ci			     (1UL << SOCK_TIMESTAMP) | \
3308c2ecf20Sopenharmony_ci			     (1UL << SOCK_DBG) | \
3318c2ecf20Sopenharmony_ci			     (1UL << SOCK_RCVTSTAMP) | \
3328c2ecf20Sopenharmony_ci			     (1UL << SOCK_RCVTSTAMPNS) | \
3338c2ecf20Sopenharmony_ci			     (1UL << SOCK_LOCALROUTE) | \
3348c2ecf20Sopenharmony_ci			     (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE) | \
3358c2ecf20Sopenharmony_ci			     (1UL << SOCK_RXQ_OVFL) | \
3368c2ecf20Sopenharmony_ci			     (1UL << SOCK_WIFI_STATUS) | \
3378c2ecf20Sopenharmony_ci			     (1UL << SOCK_NOFCS) | \
3388c2ecf20Sopenharmony_ci			     (1UL << SOCK_FILTER_LOCKED) | \
3398c2ecf20Sopenharmony_ci			     (1UL << SOCK_TSTAMP_NEW))
3408c2ecf20Sopenharmony_ci/* copy only relevant settings and flags of SOL_SOCKET level from smc to
3418c2ecf20Sopenharmony_ci * clc socket (since smc is not called for these options from net/core)
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_cistatic void smc_copy_sock_settings_to_clc(struct smc_sock *smc)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	smc_copy_sock_settings(smc->clcsock->sk, &smc->sk, SK_FLAGS_SMC_TO_CLC);
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci#define SK_FLAGS_CLC_TO_SMC ((1UL << SOCK_URGINLINE) | \
3498c2ecf20Sopenharmony_ci			     (1UL << SOCK_KEEPOPEN) | \
3508c2ecf20Sopenharmony_ci			     (1UL << SOCK_LINGER) | \
3518c2ecf20Sopenharmony_ci			     (1UL << SOCK_DBG))
3528c2ecf20Sopenharmony_ci/* copy only settings and flags relevant for smc from clc to smc socket */
3538c2ecf20Sopenharmony_cistatic void smc_copy_sock_settings_to_smc(struct smc_sock *smc)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	smc_copy_sock_settings(&smc->sk, smc->clcsock->sk, SK_FLAGS_CLC_TO_SMC);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/* register the new rmb on all links */
3598c2ecf20Sopenharmony_cistatic int smcr_lgr_reg_rmbs(struct smc_link *link,
3608c2ecf20Sopenharmony_ci			     struct smc_buf_desc *rmb_desc)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct smc_link_group *lgr = link->lgr;
3638c2ecf20Sopenharmony_ci	int i, rc = 0;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
3668c2ecf20Sopenharmony_ci	if (rc)
3678c2ecf20Sopenharmony_ci		return rc;
3688c2ecf20Sopenharmony_ci	/* protect against parallel smc_llc_cli_rkey_exchange() and
3698c2ecf20Sopenharmony_ci	 * parallel smcr_link_reg_rmb()
3708c2ecf20Sopenharmony_ci	 */
3718c2ecf20Sopenharmony_ci	mutex_lock(&lgr->llc_conf_mutex);
3728c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
3738c2ecf20Sopenharmony_ci		if (!smc_link_active(&lgr->lnk[i]))
3748c2ecf20Sopenharmony_ci			continue;
3758c2ecf20Sopenharmony_ci		rc = smcr_link_reg_rmb(&lgr->lnk[i], rmb_desc);
3768c2ecf20Sopenharmony_ci		if (rc)
3778c2ecf20Sopenharmony_ci			goto out;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* exchange confirm_rkey msg with peer */
3818c2ecf20Sopenharmony_ci	rc = smc_llc_do_confirm_rkey(link, rmb_desc);
3828c2ecf20Sopenharmony_ci	if (rc) {
3838c2ecf20Sopenharmony_ci		rc = -EFAULT;
3848c2ecf20Sopenharmony_ci		goto out;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci	rmb_desc->is_conf_rkey = true;
3878c2ecf20Sopenharmony_ciout:
3888c2ecf20Sopenharmony_ci	mutex_unlock(&lgr->llc_conf_mutex);
3898c2ecf20Sopenharmony_ci	smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
3908c2ecf20Sopenharmony_ci	return rc;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int smcr_clnt_conf_first_link(struct smc_sock *smc)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct smc_link *link = smc->conn.lnk;
3968c2ecf20Sopenharmony_ci	struct smc_llc_qentry *qentry;
3978c2ecf20Sopenharmony_ci	int rc;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Receive CONFIRM LINK request from server over RoCE fabric.
4008c2ecf20Sopenharmony_ci	 * Increasing the client's timeout by twice as much as the server's
4018c2ecf20Sopenharmony_ci	 * timeout by default can temporarily avoid decline messages of
4028c2ecf20Sopenharmony_ci	 * both sides crossing or colliding
4038c2ecf20Sopenharmony_ci	 */
4048c2ecf20Sopenharmony_ci	qentry = smc_llc_wait(link->lgr, NULL, 2 * SMC_LLC_WAIT_TIME,
4058c2ecf20Sopenharmony_ci			      SMC_LLC_CONFIRM_LINK);
4068c2ecf20Sopenharmony_ci	if (!qentry) {
4078c2ecf20Sopenharmony_ci		struct smc_clc_msg_decline dclc;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
4108c2ecf20Sopenharmony_ci				      SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
4118c2ecf20Sopenharmony_ci		return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci	smc_llc_save_peer_uid(qentry);
4148c2ecf20Sopenharmony_ci	rc = smc_llc_eval_conf_link(qentry, SMC_LLC_REQ);
4158c2ecf20Sopenharmony_ci	smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl);
4168c2ecf20Sopenharmony_ci	if (rc)
4178c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_RMBE_EC;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	rc = smc_ib_modify_qp_rts(link);
4208c2ecf20Sopenharmony_ci	if (rc)
4218c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_ERR_RDYLNK;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	smc_wr_remember_qp_attr(link);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (smcr_link_reg_rmb(link, smc->conn.rmb_desc))
4268c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_ERR_REGRMB;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* confirm_rkey is implicit on 1st contact */
4298c2ecf20Sopenharmony_ci	smc->conn.rmb_desc->is_conf_rkey = true;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* send CONFIRM LINK response over RoCE fabric */
4328c2ecf20Sopenharmony_ci	rc = smc_llc_send_confirm_link(link, SMC_LLC_RESP);
4338c2ecf20Sopenharmony_ci	if (rc < 0)
4348c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_TIMEOUT_CL;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	smc_llc_link_active(link);
4378c2ecf20Sopenharmony_ci	smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* optional 2nd link, receive ADD LINK request from server */
4408c2ecf20Sopenharmony_ci	qentry = smc_llc_wait(link->lgr, NULL, SMC_LLC_WAIT_TIME,
4418c2ecf20Sopenharmony_ci			      SMC_LLC_ADD_LINK);
4428c2ecf20Sopenharmony_ci	if (!qentry) {
4438c2ecf20Sopenharmony_ci		struct smc_clc_msg_decline dclc;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
4468c2ecf20Sopenharmony_ci				      SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
4478c2ecf20Sopenharmony_ci		if (rc == -EAGAIN)
4488c2ecf20Sopenharmony_ci			rc = 0; /* no DECLINE received, go with one link */
4498c2ecf20Sopenharmony_ci		return rc;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	smc_llc_flow_qentry_clr(&link->lgr->llc_flow_lcl);
4528c2ecf20Sopenharmony_ci	smc_llc_cli_add_link(link, qentry);
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void smcr_conn_save_peer_info(struct smc_sock *smc,
4578c2ecf20Sopenharmony_ci				     struct smc_clc_msg_accept_confirm *clc)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	int bufsize = smc_uncompress_bufsize(clc->r0.rmbe_size);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	smc->conn.peer_rmbe_idx = clc->r0.rmbe_idx;
4628c2ecf20Sopenharmony_ci	smc->conn.local_tx_ctrl.token = ntohl(clc->r0.rmbe_alert_token);
4638c2ecf20Sopenharmony_ci	smc->conn.peer_rmbe_size = bufsize;
4648c2ecf20Sopenharmony_ci	atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
4658c2ecf20Sopenharmony_ci	smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1);
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic bool smc_isascii(char *hostname)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	int i;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	for (i = 0; i < SMC_MAX_HOSTNAME_LEN; i++)
4738c2ecf20Sopenharmony_ci		if (!isascii(hostname[i]))
4748c2ecf20Sopenharmony_ci			return false;
4758c2ecf20Sopenharmony_ci	return true;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic void smcd_conn_save_peer_info(struct smc_sock *smc,
4798c2ecf20Sopenharmony_ci				     struct smc_clc_msg_accept_confirm *clc)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	int bufsize = smc_uncompress_bufsize(clc->d0.dmbe_size);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	smc->conn.peer_rmbe_idx = clc->d0.dmbe_idx;
4848c2ecf20Sopenharmony_ci	smc->conn.peer_token = clc->d0.token;
4858c2ecf20Sopenharmony_ci	/* msg header takes up space in the buffer */
4868c2ecf20Sopenharmony_ci	smc->conn.peer_rmbe_size = bufsize - sizeof(struct smcd_cdc_msg);
4878c2ecf20Sopenharmony_ci	atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size);
4888c2ecf20Sopenharmony_ci	smc->conn.tx_off = bufsize * smc->conn.peer_rmbe_idx;
4898c2ecf20Sopenharmony_ci	if (clc->hdr.version > SMC_V1 &&
4908c2ecf20Sopenharmony_ci	    (clc->hdr.typev2 & SMC_FIRST_CONTACT_MASK)) {
4918c2ecf20Sopenharmony_ci		struct smc_clc_msg_accept_confirm_v2 *clc_v2 =
4928c2ecf20Sopenharmony_ci			(struct smc_clc_msg_accept_confirm_v2 *)clc;
4938c2ecf20Sopenharmony_ci		struct smc_clc_first_contact_ext *fce =
4948c2ecf20Sopenharmony_ci			(struct smc_clc_first_contact_ext *)
4958c2ecf20Sopenharmony_ci				(((u8 *)clc_v2) + sizeof(*clc_v2));
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		memcpy(smc->conn.lgr->negotiated_eid, clc_v2->eid,
4988c2ecf20Sopenharmony_ci		       SMC_MAX_EID_LEN);
4998c2ecf20Sopenharmony_ci		smc->conn.lgr->peer_os = fce->os_type;
5008c2ecf20Sopenharmony_ci		smc->conn.lgr->peer_smc_release = fce->release;
5018c2ecf20Sopenharmony_ci		if (smc_isascii(fce->hostname))
5028c2ecf20Sopenharmony_ci			memcpy(smc->conn.lgr->peer_hostname, fce->hostname,
5038c2ecf20Sopenharmony_ci			       SMC_MAX_HOSTNAME_LEN);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void smc_conn_save_peer_info(struct smc_sock *smc,
5088c2ecf20Sopenharmony_ci				    struct smc_clc_msg_accept_confirm *clc)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	if (smc->conn.lgr->is_smcd)
5118c2ecf20Sopenharmony_ci		smcd_conn_save_peer_info(smc, clc);
5128c2ecf20Sopenharmony_ci	else
5138c2ecf20Sopenharmony_ci		smcr_conn_save_peer_info(smc, clc);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void smc_link_save_peer_info(struct smc_link *link,
5178c2ecf20Sopenharmony_ci				    struct smc_clc_msg_accept_confirm *clc)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	link->peer_qpn = ntoh24(clc->r0.qpn);
5208c2ecf20Sopenharmony_ci	memcpy(link->peer_gid, clc->r0.lcl.gid, SMC_GID_SIZE);
5218c2ecf20Sopenharmony_ci	memcpy(link->peer_mac, clc->r0.lcl.mac, sizeof(link->peer_mac));
5228c2ecf20Sopenharmony_ci	link->peer_psn = ntoh24(clc->r0.psn);
5238c2ecf20Sopenharmony_ci	link->peer_mtu = clc->r0.qp_mtu;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic void smc_switch_to_fallback(struct smc_sock *smc)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	wait_queue_head_t *smc_wait = sk_sleep(&smc->sk);
5298c2ecf20Sopenharmony_ci	wait_queue_head_t *clc_wait = sk_sleep(smc->clcsock->sk);
5308c2ecf20Sopenharmony_ci	unsigned long flags;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	smc->use_fallback = true;
5338c2ecf20Sopenharmony_ci	if (smc->sk.sk_socket && smc->sk.sk_socket->file) {
5348c2ecf20Sopenharmony_ci		smc->clcsock->file = smc->sk.sk_socket->file;
5358c2ecf20Sopenharmony_ci		smc->clcsock->file->private_data = smc->clcsock;
5368c2ecf20Sopenharmony_ci		smc->clcsock->wq.fasync_list =
5378c2ecf20Sopenharmony_ci			smc->sk.sk_socket->wq.fasync_list;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		/* There may be some entries remaining in
5408c2ecf20Sopenharmony_ci		 * smc socket->wq, which should be removed
5418c2ecf20Sopenharmony_ci		 * to clcsocket->wq during the fallback.
5428c2ecf20Sopenharmony_ci		 */
5438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&smc_wait->lock, flags);
5448c2ecf20Sopenharmony_ci		spin_lock_nested(&clc_wait->lock, SINGLE_DEPTH_NESTING);
5458c2ecf20Sopenharmony_ci		list_splice_init(&smc_wait->head, &clc_wait->head);
5468c2ecf20Sopenharmony_ci		spin_unlock(&clc_wait->lock);
5478c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&smc_wait->lock, flags);
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci/* fall back during connect */
5528c2ecf20Sopenharmony_cistatic int smc_connect_fallback(struct smc_sock *smc, int reason_code)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	smc_switch_to_fallback(smc);
5558c2ecf20Sopenharmony_ci	smc->fallback_rsn = reason_code;
5568c2ecf20Sopenharmony_ci	smc_copy_sock_settings_to_clc(smc);
5578c2ecf20Sopenharmony_ci	smc->connect_nonblock = 0;
5588c2ecf20Sopenharmony_ci	if (smc->sk.sk_state == SMC_INIT)
5598c2ecf20Sopenharmony_ci		smc->sk.sk_state = SMC_ACTIVE;
5608c2ecf20Sopenharmony_ci	return 0;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci/* decline and fall back during connect */
5648c2ecf20Sopenharmony_cistatic int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code,
5658c2ecf20Sopenharmony_ci					u8 version)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	int rc;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (reason_code < 0) { /* error, fallback is not possible */
5708c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_INIT)
5718c2ecf20Sopenharmony_ci			sock_put(&smc->sk); /* passive closing */
5728c2ecf20Sopenharmony_ci		return reason_code;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	if (reason_code != SMC_CLC_DECL_PEERDECL) {
5758c2ecf20Sopenharmony_ci		rc = smc_clc_send_decline(smc, reason_code, version);
5768c2ecf20Sopenharmony_ci		if (rc < 0) {
5778c2ecf20Sopenharmony_ci			if (smc->sk.sk_state == SMC_INIT)
5788c2ecf20Sopenharmony_ci				sock_put(&smc->sk); /* passive closing */
5798c2ecf20Sopenharmony_ci			return rc;
5808c2ecf20Sopenharmony_ci		}
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	return smc_connect_fallback(smc, reason_code);
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci/* abort connecting */
5868c2ecf20Sopenharmony_cistatic void smc_connect_abort(struct smc_sock *smc, int local_first)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	if (local_first)
5898c2ecf20Sopenharmony_ci		smc_lgr_cleanup_early(&smc->conn);
5908c2ecf20Sopenharmony_ci	else
5918c2ecf20Sopenharmony_ci		smc_conn_free(&smc->conn);
5928c2ecf20Sopenharmony_ci}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci/* check if there is a rdma device available for this connection. */
5958c2ecf20Sopenharmony_ci/* called for connect and listen */
5968c2ecf20Sopenharmony_cistatic int smc_find_rdma_device(struct smc_sock *smc, struct smc_init_info *ini)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	/* PNET table look up: search active ib_device and port
5998c2ecf20Sopenharmony_ci	 * within same PNETID that also contains the ethernet device
6008c2ecf20Sopenharmony_ci	 * used for the internal TCP socket
6018c2ecf20Sopenharmony_ci	 */
6028c2ecf20Sopenharmony_ci	smc_pnet_find_roce_resource(smc->clcsock->sk, ini);
6038c2ecf20Sopenharmony_ci	if (!ini->ib_dev)
6048c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOSMCRDEV;
6058c2ecf20Sopenharmony_ci	return 0;
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci/* check if there is an ISM device available for this connection. */
6098c2ecf20Sopenharmony_ci/* called for connect and listen */
6108c2ecf20Sopenharmony_cistatic int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	/* Find ISM device with same PNETID as connecting interface  */
6138c2ecf20Sopenharmony_ci	smc_pnet_find_ism_resource(smc->clcsock->sk, ini);
6148c2ecf20Sopenharmony_ci	if (!ini->ism_dev[0])
6158c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOSMCDDEV;
6168c2ecf20Sopenharmony_ci	else
6178c2ecf20Sopenharmony_ci		ini->ism_chid[0] = smc_ism_get_chid(ini->ism_dev[0]);
6188c2ecf20Sopenharmony_ci	return 0;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/* is chid unique for the ism devices that are already determined? */
6228c2ecf20Sopenharmony_cistatic bool smc_find_ism_v2_is_unique_chid(u16 chid, struct smc_init_info *ini,
6238c2ecf20Sopenharmony_ci					   int cnt)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	int i = (!ini->ism_dev[0]) ? 1 : 0;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	for (; i < cnt; i++)
6288c2ecf20Sopenharmony_ci		if (ini->ism_chid[i] == chid)
6298c2ecf20Sopenharmony_ci			return false;
6308c2ecf20Sopenharmony_ci	return true;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/* determine possible V2 ISM devices (either without PNETID or with PNETID plus
6348c2ecf20Sopenharmony_ci * PNETID matching net_device)
6358c2ecf20Sopenharmony_ci */
6368c2ecf20Sopenharmony_cistatic int smc_find_ism_v2_device_clnt(struct smc_sock *smc,
6378c2ecf20Sopenharmony_ci				       struct smc_init_info *ini)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	int rc = SMC_CLC_DECL_NOSMCDDEV;
6408c2ecf20Sopenharmony_ci	struct smcd_dev *smcd;
6418c2ecf20Sopenharmony_ci	int i = 1;
6428c2ecf20Sopenharmony_ci	u16 chid;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (smcd_indicated(ini->smc_type_v1))
6458c2ecf20Sopenharmony_ci		rc = 0;		/* already initialized for V1 */
6468c2ecf20Sopenharmony_ci	mutex_lock(&smcd_dev_list.mutex);
6478c2ecf20Sopenharmony_ci	list_for_each_entry(smcd, &smcd_dev_list.list, list) {
6488c2ecf20Sopenharmony_ci		if (smcd->going_away || smcd == ini->ism_dev[0])
6498c2ecf20Sopenharmony_ci			continue;
6508c2ecf20Sopenharmony_ci		chid = smc_ism_get_chid(smcd);
6518c2ecf20Sopenharmony_ci		if (!smc_find_ism_v2_is_unique_chid(chid, ini, i))
6528c2ecf20Sopenharmony_ci			continue;
6538c2ecf20Sopenharmony_ci		if (!smc_pnet_is_pnetid_set(smcd->pnetid) ||
6548c2ecf20Sopenharmony_ci		    smc_pnet_is_ndev_pnetid(sock_net(&smc->sk), smcd->pnetid)) {
6558c2ecf20Sopenharmony_ci			ini->ism_dev[i] = smcd;
6568c2ecf20Sopenharmony_ci			ini->ism_chid[i] = chid;
6578c2ecf20Sopenharmony_ci			ini->is_smcd = true;
6588c2ecf20Sopenharmony_ci			rc = 0;
6598c2ecf20Sopenharmony_ci			i++;
6608c2ecf20Sopenharmony_ci			if (i > SMC_MAX_ISM_DEVS)
6618c2ecf20Sopenharmony_ci				break;
6628c2ecf20Sopenharmony_ci		}
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci	mutex_unlock(&smcd_dev_list.mutex);
6658c2ecf20Sopenharmony_ci	ini->ism_offered_cnt = i - 1;
6668c2ecf20Sopenharmony_ci	if (!ini->ism_dev[0] && !ini->ism_dev[1])
6678c2ecf20Sopenharmony_ci		ini->smcd_version = 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	return rc;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci/* Check for VLAN ID and register it on ISM device just for CLC handshake */
6738c2ecf20Sopenharmony_cistatic int smc_connect_ism_vlan_setup(struct smc_sock *smc,
6748c2ecf20Sopenharmony_ci				      struct smc_init_info *ini)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev[0], ini->vlan_id))
6778c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_ISMVLANERR;
6788c2ecf20Sopenharmony_ci	return 0;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cistatic int smc_find_proposal_devices(struct smc_sock *smc,
6828c2ecf20Sopenharmony_ci				     struct smc_init_info *ini)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	int rc = 0;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* check if there is an ism device available */
6878c2ecf20Sopenharmony_ci	if (ini->smcd_version & SMC_V1) {
6888c2ecf20Sopenharmony_ci		if (smc_find_ism_device(smc, ini) ||
6898c2ecf20Sopenharmony_ci		    smc_connect_ism_vlan_setup(smc, ini)) {
6908c2ecf20Sopenharmony_ci			if (ini->smc_type_v1 == SMC_TYPE_B)
6918c2ecf20Sopenharmony_ci				ini->smc_type_v1 = SMC_TYPE_R;
6928c2ecf20Sopenharmony_ci			else
6938c2ecf20Sopenharmony_ci				ini->smc_type_v1 = SMC_TYPE_N;
6948c2ecf20Sopenharmony_ci		} /* else ISM V1 is supported for this connection */
6958c2ecf20Sopenharmony_ci		if (smc_find_rdma_device(smc, ini)) {
6968c2ecf20Sopenharmony_ci			if (ini->smc_type_v1 == SMC_TYPE_B)
6978c2ecf20Sopenharmony_ci				ini->smc_type_v1 = SMC_TYPE_D;
6988c2ecf20Sopenharmony_ci			else
6998c2ecf20Sopenharmony_ci				ini->smc_type_v1 = SMC_TYPE_N;
7008c2ecf20Sopenharmony_ci		} /* else RDMA is supported for this connection */
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	if (smc_ism_v2_capable && smc_find_ism_v2_device_clnt(smc, ini))
7038c2ecf20Sopenharmony_ci		ini->smc_type_v2 = SMC_TYPE_N;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* if neither ISM nor RDMA are supported, fallback */
7068c2ecf20Sopenharmony_ci	if (!smcr_indicated(ini->smc_type_v1) &&
7078c2ecf20Sopenharmony_ci	    ini->smc_type_v1 == SMC_TYPE_N && ini->smc_type_v2 == SMC_TYPE_N)
7088c2ecf20Sopenharmony_ci		rc = SMC_CLC_DECL_NOSMCDEV;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return rc;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci/* cleanup temporary VLAN ID registration used for CLC handshake. If ISM is
7148c2ecf20Sopenharmony_ci * used, the VLAN ID will be registered again during the connection setup.
7158c2ecf20Sopenharmony_ci */
7168c2ecf20Sopenharmony_cistatic int smc_connect_ism_vlan_cleanup(struct smc_sock *smc,
7178c2ecf20Sopenharmony_ci					struct smc_init_info *ini)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	if (!smcd_indicated(ini->smc_type_v1))
7208c2ecf20Sopenharmony_ci		return 0;
7218c2ecf20Sopenharmony_ci	if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev[0], ini->vlan_id))
7228c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_CNFERR;
7238c2ecf20Sopenharmony_ci	return 0;
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci#define SMC_CLC_MAX_ACCEPT_LEN \
7278c2ecf20Sopenharmony_ci	(sizeof(struct smc_clc_msg_accept_confirm_v2) + \
7288c2ecf20Sopenharmony_ci	 sizeof(struct smc_clc_first_contact_ext) + \
7298c2ecf20Sopenharmony_ci	 sizeof(struct smc_clc_msg_trail))
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci/* CLC handshake during connect */
7328c2ecf20Sopenharmony_cistatic int smc_connect_clc(struct smc_sock *smc,
7338c2ecf20Sopenharmony_ci			   struct smc_clc_msg_accept_confirm_v2 *aclc2,
7348c2ecf20Sopenharmony_ci			   struct smc_init_info *ini)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	int rc = 0;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* do inband token exchange */
7398c2ecf20Sopenharmony_ci	rc = smc_clc_send_proposal(smc, ini);
7408c2ecf20Sopenharmony_ci	if (rc)
7418c2ecf20Sopenharmony_ci		return rc;
7428c2ecf20Sopenharmony_ci	/* receive SMC Accept CLC message */
7438c2ecf20Sopenharmony_ci	return smc_clc_wait_msg(smc, aclc2, SMC_CLC_MAX_ACCEPT_LEN,
7448c2ecf20Sopenharmony_ci				SMC_CLC_ACCEPT, CLC_WAIT_TIME);
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci/* setup for RDMA connection of client */
7488c2ecf20Sopenharmony_cistatic int smc_connect_rdma(struct smc_sock *smc,
7498c2ecf20Sopenharmony_ci			    struct smc_clc_msg_accept_confirm *aclc,
7508c2ecf20Sopenharmony_ci			    struct smc_init_info *ini)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	int i, reason_code = 0;
7538c2ecf20Sopenharmony_ci	struct smc_link *link;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	ini->is_smcd = false;
7568c2ecf20Sopenharmony_ci	ini->ib_lcl = &aclc->r0.lcl;
7578c2ecf20Sopenharmony_ci	ini->ib_clcqpn = ntoh24(aclc->r0.qpn);
7588c2ecf20Sopenharmony_ci	ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	mutex_lock(&smc_client_lgr_pending);
7618c2ecf20Sopenharmony_ci	reason_code = smc_conn_create(smc, ini);
7628c2ecf20Sopenharmony_ci	if (reason_code) {
7638c2ecf20Sopenharmony_ci		mutex_unlock(&smc_client_lgr_pending);
7648c2ecf20Sopenharmony_ci		return reason_code;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	smc_conn_save_peer_info(smc, aclc);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (ini->first_contact_local) {
7708c2ecf20Sopenharmony_ci		link = smc->conn.lnk;
7718c2ecf20Sopenharmony_ci	} else {
7728c2ecf20Sopenharmony_ci		/* set link that was assigned by server */
7738c2ecf20Sopenharmony_ci		link = NULL;
7748c2ecf20Sopenharmony_ci		for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
7758c2ecf20Sopenharmony_ci			struct smc_link *l = &smc->conn.lgr->lnk[i];
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci			if (l->peer_qpn == ntoh24(aclc->r0.qpn) &&
7788c2ecf20Sopenharmony_ci			    !memcmp(l->peer_gid, &aclc->r0.lcl.gid,
7798c2ecf20Sopenharmony_ci				    SMC_GID_SIZE) &&
7808c2ecf20Sopenharmony_ci			    !memcmp(l->peer_mac, &aclc->r0.lcl.mac,
7818c2ecf20Sopenharmony_ci				    sizeof(l->peer_mac))) {
7828c2ecf20Sopenharmony_ci				link = l;
7838c2ecf20Sopenharmony_ci				break;
7848c2ecf20Sopenharmony_ci			}
7858c2ecf20Sopenharmony_ci		}
7868c2ecf20Sopenharmony_ci		if (!link) {
7878c2ecf20Sopenharmony_ci			reason_code = SMC_CLC_DECL_NOSRVLINK;
7888c2ecf20Sopenharmony_ci			goto connect_abort;
7898c2ecf20Sopenharmony_ci		}
7908c2ecf20Sopenharmony_ci		smc->conn.lnk = link;
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* create send buffer and rmb */
7948c2ecf20Sopenharmony_ci	if (smc_buf_create(smc, false)) {
7958c2ecf20Sopenharmony_ci		reason_code = SMC_CLC_DECL_MEM;
7968c2ecf20Sopenharmony_ci		goto connect_abort;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (ini->first_contact_local)
8008c2ecf20Sopenharmony_ci		smc_link_save_peer_info(link, aclc);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (smc_rmb_rtoken_handling(&smc->conn, link, aclc)) {
8038c2ecf20Sopenharmony_ci		reason_code = SMC_CLC_DECL_ERR_RTOK;
8048c2ecf20Sopenharmony_ci		goto connect_abort;
8058c2ecf20Sopenharmony_ci	}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	smc_close_init(smc);
8088c2ecf20Sopenharmony_ci	smc_rx_init(smc);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (ini->first_contact_local) {
8118c2ecf20Sopenharmony_ci		if (smc_ib_ready_link(link)) {
8128c2ecf20Sopenharmony_ci			reason_code = SMC_CLC_DECL_ERR_RDYLNK;
8138c2ecf20Sopenharmony_ci			goto connect_abort;
8148c2ecf20Sopenharmony_ci		}
8158c2ecf20Sopenharmony_ci	} else {
8168c2ecf20Sopenharmony_ci		if (smcr_lgr_reg_rmbs(link, smc->conn.rmb_desc)) {
8178c2ecf20Sopenharmony_ci			reason_code = SMC_CLC_DECL_ERR_REGRMB;
8188c2ecf20Sopenharmony_ci			goto connect_abort;
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci	smc_rmb_sync_sg_for_device(&smc->conn);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	reason_code = smc_clc_send_confirm(smc, ini->first_contact_local,
8248c2ecf20Sopenharmony_ci					   SMC_V1);
8258c2ecf20Sopenharmony_ci	if (reason_code)
8268c2ecf20Sopenharmony_ci		goto connect_abort;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	smc_tx_init(smc);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (ini->first_contact_local) {
8318c2ecf20Sopenharmony_ci		/* QP confirmation over RoCE fabric */
8328c2ecf20Sopenharmony_ci		smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
8338c2ecf20Sopenharmony_ci		reason_code = smcr_clnt_conf_first_link(smc);
8348c2ecf20Sopenharmony_ci		smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
8358c2ecf20Sopenharmony_ci		if (reason_code)
8368c2ecf20Sopenharmony_ci			goto connect_abort;
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci	mutex_unlock(&smc_client_lgr_pending);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	smc_copy_sock_settings_to_clc(smc);
8418c2ecf20Sopenharmony_ci	smc->connect_nonblock = 0;
8428c2ecf20Sopenharmony_ci	if (smc->sk.sk_state == SMC_INIT)
8438c2ecf20Sopenharmony_ci		smc->sk.sk_state = SMC_ACTIVE;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	return 0;
8468c2ecf20Sopenharmony_ciconnect_abort:
8478c2ecf20Sopenharmony_ci	smc_connect_abort(smc, ini->first_contact_local);
8488c2ecf20Sopenharmony_ci	mutex_unlock(&smc_client_lgr_pending);
8498c2ecf20Sopenharmony_ci	smc->connect_nonblock = 0;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	return reason_code;
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci/* The server has chosen one of the proposed ISM devices for the communication.
8558c2ecf20Sopenharmony_ci * Determine from the CHID of the received CLC ACCEPT the ISM device chosen.
8568c2ecf20Sopenharmony_ci */
8578c2ecf20Sopenharmony_cistatic int
8588c2ecf20Sopenharmony_cismc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm_v2 *aclc,
8598c2ecf20Sopenharmony_ci			       struct smc_init_info *ini)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	int i;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	for (i = 0; i < ini->ism_offered_cnt + 1; i++) {
8648c2ecf20Sopenharmony_ci		if (ini->ism_chid[i] == ntohs(aclc->chid)) {
8658c2ecf20Sopenharmony_ci			ini->ism_selected = i;
8668c2ecf20Sopenharmony_ci			return 0;
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	return -EPROTO;
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci/* setup for ISM connection of client */
8748c2ecf20Sopenharmony_cistatic int smc_connect_ism(struct smc_sock *smc,
8758c2ecf20Sopenharmony_ci			   struct smc_clc_msg_accept_confirm *aclc,
8768c2ecf20Sopenharmony_ci			   struct smc_init_info *ini)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	int rc = 0;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	ini->is_smcd = true;
8818c2ecf20Sopenharmony_ci	ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (aclc->hdr.version == SMC_V2) {
8848c2ecf20Sopenharmony_ci		struct smc_clc_msg_accept_confirm_v2 *aclc_v2 =
8858c2ecf20Sopenharmony_ci			(struct smc_clc_msg_accept_confirm_v2 *)aclc;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		rc = smc_v2_determine_accepted_chid(aclc_v2, ini);
8888c2ecf20Sopenharmony_ci		if (rc)
8898c2ecf20Sopenharmony_ci			return rc;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	ini->ism_peer_gid[ini->ism_selected] = aclc->d0.gid;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	/* there is only one lgr role for SMC-D; use server lock */
8948c2ecf20Sopenharmony_ci	mutex_lock(&smc_server_lgr_pending);
8958c2ecf20Sopenharmony_ci	rc = smc_conn_create(smc, ini);
8968c2ecf20Sopenharmony_ci	if (rc) {
8978c2ecf20Sopenharmony_ci		mutex_unlock(&smc_server_lgr_pending);
8988c2ecf20Sopenharmony_ci		return rc;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	/* Create send and receive buffers */
9028c2ecf20Sopenharmony_ci	rc = smc_buf_create(smc, true);
9038c2ecf20Sopenharmony_ci	if (rc) {
9048c2ecf20Sopenharmony_ci		rc = (rc == -ENOSPC) ? SMC_CLC_DECL_MAX_DMB : SMC_CLC_DECL_MEM;
9058c2ecf20Sopenharmony_ci		goto connect_abort;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	smc_conn_save_peer_info(smc, aclc);
9098c2ecf20Sopenharmony_ci	smc_close_init(smc);
9108c2ecf20Sopenharmony_ci	smc_rx_init(smc);
9118c2ecf20Sopenharmony_ci	smc_tx_init(smc);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	rc = smc_clc_send_confirm(smc, ini->first_contact_local,
9148c2ecf20Sopenharmony_ci				  aclc->hdr.version);
9158c2ecf20Sopenharmony_ci	if (rc)
9168c2ecf20Sopenharmony_ci		goto connect_abort;
9178c2ecf20Sopenharmony_ci	mutex_unlock(&smc_server_lgr_pending);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	smc_copy_sock_settings_to_clc(smc);
9208c2ecf20Sopenharmony_ci	smc->connect_nonblock = 0;
9218c2ecf20Sopenharmony_ci	if (smc->sk.sk_state == SMC_INIT)
9228c2ecf20Sopenharmony_ci		smc->sk.sk_state = SMC_ACTIVE;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	return 0;
9258c2ecf20Sopenharmony_ciconnect_abort:
9268c2ecf20Sopenharmony_ci	smc_connect_abort(smc, ini->first_contact_local);
9278c2ecf20Sopenharmony_ci	mutex_unlock(&smc_server_lgr_pending);
9288c2ecf20Sopenharmony_ci	smc->connect_nonblock = 0;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	return rc;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/* check if received accept type and version matches a proposed one */
9348c2ecf20Sopenharmony_cistatic int smc_connect_check_aclc(struct smc_init_info *ini,
9358c2ecf20Sopenharmony_ci				  struct smc_clc_msg_accept_confirm *aclc)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	if ((aclc->hdr.typev1 == SMC_TYPE_R &&
9388c2ecf20Sopenharmony_ci	     !smcr_indicated(ini->smc_type_v1)) ||
9398c2ecf20Sopenharmony_ci	    (aclc->hdr.typev1 == SMC_TYPE_D &&
9408c2ecf20Sopenharmony_ci	     ((!smcd_indicated(ini->smc_type_v1) &&
9418c2ecf20Sopenharmony_ci	       !smcd_indicated(ini->smc_type_v2)) ||
9428c2ecf20Sopenharmony_ci	      (aclc->hdr.version == SMC_V1 &&
9438c2ecf20Sopenharmony_ci	       !smcd_indicated(ini->smc_type_v1)) ||
9448c2ecf20Sopenharmony_ci	      (aclc->hdr.version == SMC_V2 &&
9458c2ecf20Sopenharmony_ci	       !smcd_indicated(ini->smc_type_v2)))))
9468c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_MODEUNSUPP;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	return 0;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci/* perform steps before actually connecting */
9528c2ecf20Sopenharmony_cistatic int __smc_connect(struct smc_sock *smc)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	u8 version = smc_ism_v2_capable ? SMC_V2 : SMC_V1;
9558c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm_v2 *aclc2;
9568c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm *aclc;
9578c2ecf20Sopenharmony_ci	struct smc_init_info *ini = NULL;
9588c2ecf20Sopenharmony_ci	u8 *buf = NULL;
9598c2ecf20Sopenharmony_ci	int rc = 0;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	if (smc->use_fallback)
9628c2ecf20Sopenharmony_ci		return smc_connect_fallback(smc, smc->fallback_rsn);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	/* if peer has not signalled SMC-capability, fall back */
9658c2ecf20Sopenharmony_ci	if (!tcp_sk(smc->clcsock->sk)->syn_smc)
9668c2ecf20Sopenharmony_ci		return smc_connect_fallback(smc, SMC_CLC_DECL_PEERNOSMC);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	/* IPSec connections opt out of SMC optimizations */
9698c2ecf20Sopenharmony_ci	if (using_ipsec(smc))
9708c2ecf20Sopenharmony_ci		return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC,
9718c2ecf20Sopenharmony_ci						    version);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	ini = kzalloc(sizeof(*ini), GFP_KERNEL);
9748c2ecf20Sopenharmony_ci	if (!ini)
9758c2ecf20Sopenharmony_ci		return smc_connect_decline_fallback(smc, SMC_CLC_DECL_MEM,
9768c2ecf20Sopenharmony_ci						    version);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	ini->smcd_version = SMC_V1;
9798c2ecf20Sopenharmony_ci	ini->smcd_version |= smc_ism_v2_capable ? SMC_V2 : 0;
9808c2ecf20Sopenharmony_ci	ini->smc_type_v1 = SMC_TYPE_B;
9818c2ecf20Sopenharmony_ci	ini->smc_type_v2 = smc_ism_v2_capable ? SMC_TYPE_D : SMC_TYPE_N;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* get vlan id from IP device */
9848c2ecf20Sopenharmony_ci	if (smc_vlan_by_tcpsk(smc->clcsock, ini)) {
9858c2ecf20Sopenharmony_ci		ini->smcd_version &= ~SMC_V1;
9868c2ecf20Sopenharmony_ci		ini->smc_type_v1 = SMC_TYPE_N;
9878c2ecf20Sopenharmony_ci		if (!ini->smcd_version) {
9888c2ecf20Sopenharmony_ci			rc = SMC_CLC_DECL_GETVLANERR;
9898c2ecf20Sopenharmony_ci			goto fallback;
9908c2ecf20Sopenharmony_ci		}
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	rc = smc_find_proposal_devices(smc, ini);
9948c2ecf20Sopenharmony_ci	if (rc)
9958c2ecf20Sopenharmony_ci		goto fallback;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	buf = kzalloc(SMC_CLC_MAX_ACCEPT_LEN, GFP_KERNEL);
9988c2ecf20Sopenharmony_ci	if (!buf) {
9998c2ecf20Sopenharmony_ci		rc = SMC_CLC_DECL_MEM;
10008c2ecf20Sopenharmony_ci		goto fallback;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci	aclc2 = (struct smc_clc_msg_accept_confirm_v2 *)buf;
10038c2ecf20Sopenharmony_ci	aclc = (struct smc_clc_msg_accept_confirm *)aclc2;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	/* perform CLC handshake */
10068c2ecf20Sopenharmony_ci	rc = smc_connect_clc(smc, aclc2, ini);
10078c2ecf20Sopenharmony_ci	if (rc)
10088c2ecf20Sopenharmony_ci		goto vlan_cleanup;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* check if smc modes and versions of CLC proposal and accept match */
10118c2ecf20Sopenharmony_ci	rc = smc_connect_check_aclc(ini, aclc);
10128c2ecf20Sopenharmony_ci	version = aclc->hdr.version == SMC_V1 ? SMC_V1 : SMC_V2;
10138c2ecf20Sopenharmony_ci	ini->smcd_version = version;
10148c2ecf20Sopenharmony_ci	if (rc)
10158c2ecf20Sopenharmony_ci		goto vlan_cleanup;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* depending on previous steps, connect using rdma or ism */
10188c2ecf20Sopenharmony_ci	if (aclc->hdr.typev1 == SMC_TYPE_R)
10198c2ecf20Sopenharmony_ci		rc = smc_connect_rdma(smc, aclc, ini);
10208c2ecf20Sopenharmony_ci	else if (aclc->hdr.typev1 == SMC_TYPE_D)
10218c2ecf20Sopenharmony_ci		rc = smc_connect_ism(smc, aclc, ini);
10228c2ecf20Sopenharmony_ci	if (rc)
10238c2ecf20Sopenharmony_ci		goto vlan_cleanup;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	smc_connect_ism_vlan_cleanup(smc, ini);
10268c2ecf20Sopenharmony_ci	kfree(buf);
10278c2ecf20Sopenharmony_ci	kfree(ini);
10288c2ecf20Sopenharmony_ci	return 0;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_civlan_cleanup:
10318c2ecf20Sopenharmony_ci	smc_connect_ism_vlan_cleanup(smc, ini);
10328c2ecf20Sopenharmony_ci	kfree(buf);
10338c2ecf20Sopenharmony_cifallback:
10348c2ecf20Sopenharmony_ci	kfree(ini);
10358c2ecf20Sopenharmony_ci	return smc_connect_decline_fallback(smc, rc, version);
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cistatic void smc_connect_work(struct work_struct *work)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	struct smc_sock *smc = container_of(work, struct smc_sock,
10418c2ecf20Sopenharmony_ci					    connect_work);
10428c2ecf20Sopenharmony_ci	long timeo = smc->sk.sk_sndtimeo;
10438c2ecf20Sopenharmony_ci	int rc = 0;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	if (!timeo)
10468c2ecf20Sopenharmony_ci		timeo = MAX_SCHEDULE_TIMEOUT;
10478c2ecf20Sopenharmony_ci	lock_sock(smc->clcsock->sk);
10488c2ecf20Sopenharmony_ci	if (smc->clcsock->sk->sk_err) {
10498c2ecf20Sopenharmony_ci		smc->sk.sk_err = smc->clcsock->sk->sk_err;
10508c2ecf20Sopenharmony_ci	} else if ((1 << smc->clcsock->sk->sk_state) &
10518c2ecf20Sopenharmony_ci					(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
10528c2ecf20Sopenharmony_ci		rc = sk_stream_wait_connect(smc->clcsock->sk, &timeo);
10538c2ecf20Sopenharmony_ci		if ((rc == -EPIPE) &&
10548c2ecf20Sopenharmony_ci		    ((1 << smc->clcsock->sk->sk_state) &
10558c2ecf20Sopenharmony_ci					(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)))
10568c2ecf20Sopenharmony_ci			rc = 0;
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci	release_sock(smc->clcsock->sk);
10598c2ecf20Sopenharmony_ci	lock_sock(&smc->sk);
10608c2ecf20Sopenharmony_ci	if (rc != 0 || smc->sk.sk_err) {
10618c2ecf20Sopenharmony_ci		smc->sk.sk_state = SMC_CLOSED;
10628c2ecf20Sopenharmony_ci		if (rc == -EPIPE || rc == -EAGAIN)
10638c2ecf20Sopenharmony_ci			smc->sk.sk_err = EPIPE;
10648c2ecf20Sopenharmony_ci		else if (rc == -ECONNREFUSED)
10658c2ecf20Sopenharmony_ci			smc->sk.sk_err = ECONNREFUSED;
10668c2ecf20Sopenharmony_ci		else if (signal_pending(current))
10678c2ecf20Sopenharmony_ci			smc->sk.sk_err = -sock_intr_errno(timeo);
10688c2ecf20Sopenharmony_ci		sock_put(&smc->sk); /* passive closing */
10698c2ecf20Sopenharmony_ci		goto out;
10708c2ecf20Sopenharmony_ci	}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	rc = __smc_connect(smc);
10738c2ecf20Sopenharmony_ci	if (rc < 0)
10748c2ecf20Sopenharmony_ci		smc->sk.sk_err = -rc;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ciout:
10778c2ecf20Sopenharmony_ci	if (!sock_flag(&smc->sk, SOCK_DEAD)) {
10788c2ecf20Sopenharmony_ci		if (smc->sk.sk_err) {
10798c2ecf20Sopenharmony_ci			smc->sk.sk_state_change(&smc->sk);
10808c2ecf20Sopenharmony_ci		} else { /* allow polling before and after fallback decision */
10818c2ecf20Sopenharmony_ci			smc->clcsock->sk->sk_write_space(smc->clcsock->sk);
10828c2ecf20Sopenharmony_ci			smc->sk.sk_write_space(&smc->sk);
10838c2ecf20Sopenharmony_ci		}
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci	release_sock(&smc->sk);
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_cistatic int smc_connect(struct socket *sock, struct sockaddr *addr,
10898c2ecf20Sopenharmony_ci		       int alen, int flags)
10908c2ecf20Sopenharmony_ci{
10918c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
10928c2ecf20Sopenharmony_ci	struct smc_sock *smc;
10938c2ecf20Sopenharmony_ci	int rc = -EINVAL;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	/* separate smc parameter checking to be safe */
10988c2ecf20Sopenharmony_ci	if (alen < sizeof(addr->sa_family))
10998c2ecf20Sopenharmony_ci		goto out_err;
11008c2ecf20Sopenharmony_ci	if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
11018c2ecf20Sopenharmony_ci		goto out_err;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	lock_sock(sk);
11048c2ecf20Sopenharmony_ci	switch (sk->sk_state) {
11058c2ecf20Sopenharmony_ci	default:
11068c2ecf20Sopenharmony_ci		goto out;
11078c2ecf20Sopenharmony_ci	case SMC_ACTIVE:
11088c2ecf20Sopenharmony_ci		rc = -EISCONN;
11098c2ecf20Sopenharmony_ci		goto out;
11108c2ecf20Sopenharmony_ci	case SMC_INIT:
11118c2ecf20Sopenharmony_ci		rc = 0;
11128c2ecf20Sopenharmony_ci		break;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	smc_copy_sock_settings_to_clc(smc);
11168c2ecf20Sopenharmony_ci	tcp_sk(smc->clcsock->sk)->syn_smc = 1;
11178c2ecf20Sopenharmony_ci	if (smc->connect_nonblock) {
11188c2ecf20Sopenharmony_ci		rc = -EALREADY;
11198c2ecf20Sopenharmony_ci		goto out;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci	rc = kernel_connect(smc->clcsock, addr, alen, flags);
11228c2ecf20Sopenharmony_ci	if (rc && rc != -EINPROGRESS)
11238c2ecf20Sopenharmony_ci		goto out;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (smc->use_fallback)
11268c2ecf20Sopenharmony_ci		goto out;
11278c2ecf20Sopenharmony_ci	sock_hold(&smc->sk); /* sock put in passive closing */
11288c2ecf20Sopenharmony_ci	if (flags & O_NONBLOCK) {
11298c2ecf20Sopenharmony_ci		if (queue_work(smc_hs_wq, &smc->connect_work))
11308c2ecf20Sopenharmony_ci			smc->connect_nonblock = 1;
11318c2ecf20Sopenharmony_ci		rc = -EINPROGRESS;
11328c2ecf20Sopenharmony_ci	} else {
11338c2ecf20Sopenharmony_ci		rc = __smc_connect(smc);
11348c2ecf20Sopenharmony_ci		if (rc < 0)
11358c2ecf20Sopenharmony_ci			goto out;
11368c2ecf20Sopenharmony_ci		else
11378c2ecf20Sopenharmony_ci			rc = 0; /* success cases including fallback */
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ciout:
11418c2ecf20Sopenharmony_ci	release_sock(sk);
11428c2ecf20Sopenharmony_ciout_err:
11438c2ecf20Sopenharmony_ci	return rc;
11448c2ecf20Sopenharmony_ci}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_cistatic int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc)
11478c2ecf20Sopenharmony_ci{
11488c2ecf20Sopenharmony_ci	struct socket *new_clcsock = NULL;
11498c2ecf20Sopenharmony_ci	struct sock *lsk = &lsmc->sk;
11508c2ecf20Sopenharmony_ci	struct sock *new_sk;
11518c2ecf20Sopenharmony_ci	int rc = -EINVAL;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	release_sock(lsk);
11548c2ecf20Sopenharmony_ci	new_sk = smc_sock_alloc(sock_net(lsk), NULL, lsk->sk_protocol);
11558c2ecf20Sopenharmony_ci	if (!new_sk) {
11568c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11578c2ecf20Sopenharmony_ci		lsk->sk_err = ENOMEM;
11588c2ecf20Sopenharmony_ci		*new_smc = NULL;
11598c2ecf20Sopenharmony_ci		lock_sock(lsk);
11608c2ecf20Sopenharmony_ci		goto out;
11618c2ecf20Sopenharmony_ci	}
11628c2ecf20Sopenharmony_ci	*new_smc = smc_sk(new_sk);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	mutex_lock(&lsmc->clcsock_release_lock);
11658c2ecf20Sopenharmony_ci	if (lsmc->clcsock)
11668c2ecf20Sopenharmony_ci		rc = kernel_accept(lsmc->clcsock, &new_clcsock, SOCK_NONBLOCK);
11678c2ecf20Sopenharmony_ci	mutex_unlock(&lsmc->clcsock_release_lock);
11688c2ecf20Sopenharmony_ci	lock_sock(lsk);
11698c2ecf20Sopenharmony_ci	if  (rc < 0 && rc != -EAGAIN)
11708c2ecf20Sopenharmony_ci		lsk->sk_err = -rc;
11718c2ecf20Sopenharmony_ci	if (rc < 0 || lsk->sk_state == SMC_CLOSED) {
11728c2ecf20Sopenharmony_ci		new_sk->sk_prot->unhash(new_sk);
11738c2ecf20Sopenharmony_ci		if (new_clcsock)
11748c2ecf20Sopenharmony_ci			sock_release(new_clcsock);
11758c2ecf20Sopenharmony_ci		new_sk->sk_state = SMC_CLOSED;
11768c2ecf20Sopenharmony_ci		smc_sock_set_flag(new_sk, SOCK_DEAD);
11778c2ecf20Sopenharmony_ci		sock_put(new_sk); /* final */
11788c2ecf20Sopenharmony_ci		*new_smc = NULL;
11798c2ecf20Sopenharmony_ci		goto out;
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	/* new clcsock has inherited the smc listen-specific sk_data_ready
11838c2ecf20Sopenharmony_ci	 * function; switch it back to the original sk_data_ready function
11848c2ecf20Sopenharmony_ci	 */
11858c2ecf20Sopenharmony_ci	new_clcsock->sk->sk_data_ready = lsmc->clcsk_data_ready;
11868c2ecf20Sopenharmony_ci	(*new_smc)->clcsock = new_clcsock;
11878c2ecf20Sopenharmony_ciout:
11888c2ecf20Sopenharmony_ci	return rc;
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci/* add a just created sock to the accept queue of the listen sock as
11928c2ecf20Sopenharmony_ci * candidate for a following socket accept call from user space
11938c2ecf20Sopenharmony_ci */
11948c2ecf20Sopenharmony_cistatic void smc_accept_enqueue(struct sock *parent, struct sock *sk)
11958c2ecf20Sopenharmony_ci{
11968c2ecf20Sopenharmony_ci	struct smc_sock *par = smc_sk(parent);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	sock_hold(sk); /* sock_put in smc_accept_unlink () */
11998c2ecf20Sopenharmony_ci	spin_lock(&par->accept_q_lock);
12008c2ecf20Sopenharmony_ci	list_add_tail(&smc_sk(sk)->accept_q, &par->accept_q);
12018c2ecf20Sopenharmony_ci	spin_unlock(&par->accept_q_lock);
12028c2ecf20Sopenharmony_ci	sk_acceptq_added(parent);
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci/* remove a socket from the accept queue of its parental listening socket */
12068c2ecf20Sopenharmony_cistatic void smc_accept_unlink(struct sock *sk)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	struct smc_sock *par = smc_sk(sk)->listen_smc;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	spin_lock(&par->accept_q_lock);
12118c2ecf20Sopenharmony_ci	list_del_init(&smc_sk(sk)->accept_q);
12128c2ecf20Sopenharmony_ci	spin_unlock(&par->accept_q_lock);
12138c2ecf20Sopenharmony_ci	sk_acceptq_removed(&smc_sk(sk)->listen_smc->sk);
12148c2ecf20Sopenharmony_ci	sock_put(sk); /* sock_hold in smc_accept_enqueue */
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci/* remove a sock from the accept queue to bind it to a new socket created
12188c2ecf20Sopenharmony_ci * for a socket accept call from user space
12198c2ecf20Sopenharmony_ci */
12208c2ecf20Sopenharmony_cistruct sock *smc_accept_dequeue(struct sock *parent,
12218c2ecf20Sopenharmony_ci				struct socket *new_sock)
12228c2ecf20Sopenharmony_ci{
12238c2ecf20Sopenharmony_ci	struct smc_sock *isk, *n;
12248c2ecf20Sopenharmony_ci	struct sock *new_sk;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(isk, n, &smc_sk(parent)->accept_q, accept_q) {
12278c2ecf20Sopenharmony_ci		new_sk = (struct sock *)isk;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci		smc_accept_unlink(new_sk);
12308c2ecf20Sopenharmony_ci		if (new_sk->sk_state == SMC_CLOSED) {
12318c2ecf20Sopenharmony_ci			new_sk->sk_prot->unhash(new_sk);
12328c2ecf20Sopenharmony_ci			if (isk->clcsock) {
12338c2ecf20Sopenharmony_ci				sock_release(isk->clcsock);
12348c2ecf20Sopenharmony_ci				isk->clcsock = NULL;
12358c2ecf20Sopenharmony_ci			}
12368c2ecf20Sopenharmony_ci			sock_put(new_sk); /* final */
12378c2ecf20Sopenharmony_ci			continue;
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci		if (new_sock) {
12408c2ecf20Sopenharmony_ci			sock_graft(new_sk, new_sock);
12418c2ecf20Sopenharmony_ci			if (isk->use_fallback) {
12428c2ecf20Sopenharmony_ci				smc_sk(new_sk)->clcsock->file = new_sock->file;
12438c2ecf20Sopenharmony_ci				isk->clcsock->file->private_data = isk->clcsock;
12448c2ecf20Sopenharmony_ci			}
12458c2ecf20Sopenharmony_ci		}
12468c2ecf20Sopenharmony_ci		return new_sk;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci	return NULL;
12498c2ecf20Sopenharmony_ci}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci/* clean up for a created but never accepted sock */
12528c2ecf20Sopenharmony_civoid smc_close_non_accepted(struct sock *sk)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	struct smc_sock *smc = smc_sk(sk);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	sock_hold(sk); /* sock_put below */
12578c2ecf20Sopenharmony_ci	lock_sock(sk);
12588c2ecf20Sopenharmony_ci	if (!sk->sk_lingertime)
12598c2ecf20Sopenharmony_ci		/* wait for peer closing */
12608c2ecf20Sopenharmony_ci		sk->sk_lingertime = SMC_MAX_STREAM_WAIT_TIMEOUT;
12618c2ecf20Sopenharmony_ci	__smc_release(smc);
12628c2ecf20Sopenharmony_ci	release_sock(sk);
12638c2ecf20Sopenharmony_ci	sock_put(sk); /* sock_hold above */
12648c2ecf20Sopenharmony_ci	sock_put(sk); /* final sock_put */
12658c2ecf20Sopenharmony_ci}
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_cistatic int smcr_serv_conf_first_link(struct smc_sock *smc)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	struct smc_link *link = smc->conn.lnk;
12708c2ecf20Sopenharmony_ci	struct smc_llc_qentry *qentry;
12718c2ecf20Sopenharmony_ci	int rc;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	if (smcr_link_reg_rmb(link, smc->conn.rmb_desc))
12748c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_ERR_REGRMB;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* send CONFIRM LINK request to client over the RoCE fabric */
12778c2ecf20Sopenharmony_ci	rc = smc_llc_send_confirm_link(link, SMC_LLC_REQ);
12788c2ecf20Sopenharmony_ci	if (rc < 0)
12798c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_TIMEOUT_CL;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	/* receive CONFIRM LINK response from client over the RoCE fabric */
12828c2ecf20Sopenharmony_ci	qentry = smc_llc_wait(link->lgr, link, SMC_LLC_WAIT_TIME,
12838c2ecf20Sopenharmony_ci			      SMC_LLC_CONFIRM_LINK);
12848c2ecf20Sopenharmony_ci	if (!qentry) {
12858c2ecf20Sopenharmony_ci		struct smc_clc_msg_decline dclc;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci		rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
12888c2ecf20Sopenharmony_ci				      SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
12898c2ecf20Sopenharmony_ci		return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci	smc_llc_save_peer_uid(qentry);
12928c2ecf20Sopenharmony_ci	rc = smc_llc_eval_conf_link(qentry, SMC_LLC_RESP);
12938c2ecf20Sopenharmony_ci	smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl);
12948c2ecf20Sopenharmony_ci	if (rc)
12958c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_RMBE_EC;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	/* confirm_rkey is implicit on 1st contact */
12988c2ecf20Sopenharmony_ci	smc->conn.rmb_desc->is_conf_rkey = true;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	smc_llc_link_active(link);
13018c2ecf20Sopenharmony_ci	smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	/* initial contact - try to establish second link */
13048c2ecf20Sopenharmony_ci	smc_llc_srv_add_link(link);
13058c2ecf20Sopenharmony_ci	return 0;
13068c2ecf20Sopenharmony_ci}
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci/* listen worker: finish */
13098c2ecf20Sopenharmony_cistatic void smc_listen_out(struct smc_sock *new_smc)
13108c2ecf20Sopenharmony_ci{
13118c2ecf20Sopenharmony_ci	struct smc_sock *lsmc = new_smc->listen_smc;
13128c2ecf20Sopenharmony_ci	struct sock *newsmcsk = &new_smc->sk;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	if (lsmc->sk.sk_state == SMC_LISTEN) {
13158c2ecf20Sopenharmony_ci		lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING);
13168c2ecf20Sopenharmony_ci		smc_accept_enqueue(&lsmc->sk, newsmcsk);
13178c2ecf20Sopenharmony_ci		release_sock(&lsmc->sk);
13188c2ecf20Sopenharmony_ci	} else { /* no longer listening */
13198c2ecf20Sopenharmony_ci		smc_close_non_accepted(newsmcsk);
13208c2ecf20Sopenharmony_ci	}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	/* Wake up accept */
13238c2ecf20Sopenharmony_ci	lsmc->sk.sk_data_ready(&lsmc->sk);
13248c2ecf20Sopenharmony_ci	sock_put(&lsmc->sk); /* sock_hold in smc_tcp_listen_work */
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci/* listen worker: finish in state connected */
13288c2ecf20Sopenharmony_cistatic void smc_listen_out_connected(struct smc_sock *new_smc)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	struct sock *newsmcsk = &new_smc->sk;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	if (newsmcsk->sk_state == SMC_INIT)
13338c2ecf20Sopenharmony_ci		newsmcsk->sk_state = SMC_ACTIVE;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	smc_listen_out(new_smc);
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci/* listen worker: finish in error state */
13398c2ecf20Sopenharmony_cistatic void smc_listen_out_err(struct smc_sock *new_smc)
13408c2ecf20Sopenharmony_ci{
13418c2ecf20Sopenharmony_ci	struct sock *newsmcsk = &new_smc->sk;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	if (newsmcsk->sk_state == SMC_INIT)
13448c2ecf20Sopenharmony_ci		sock_put(&new_smc->sk); /* passive closing */
13458c2ecf20Sopenharmony_ci	newsmcsk->sk_state = SMC_CLOSED;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	smc_listen_out(new_smc);
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci/* listen worker: decline and fall back if possible */
13518c2ecf20Sopenharmony_cistatic void smc_listen_decline(struct smc_sock *new_smc, int reason_code,
13528c2ecf20Sopenharmony_ci			       int local_first, u8 version)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	/* RDMA setup failed, switch back to TCP */
13558c2ecf20Sopenharmony_ci	if (local_first)
13568c2ecf20Sopenharmony_ci		smc_lgr_cleanup_early(&new_smc->conn);
13578c2ecf20Sopenharmony_ci	else
13588c2ecf20Sopenharmony_ci		smc_conn_free(&new_smc->conn);
13598c2ecf20Sopenharmony_ci	if (reason_code < 0) { /* error, no fallback possible */
13608c2ecf20Sopenharmony_ci		smc_listen_out_err(new_smc);
13618c2ecf20Sopenharmony_ci		return;
13628c2ecf20Sopenharmony_ci	}
13638c2ecf20Sopenharmony_ci	smc_switch_to_fallback(new_smc);
13648c2ecf20Sopenharmony_ci	new_smc->fallback_rsn = reason_code;
13658c2ecf20Sopenharmony_ci	if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) {
13668c2ecf20Sopenharmony_ci		if (smc_clc_send_decline(new_smc, reason_code, version) < 0) {
13678c2ecf20Sopenharmony_ci			smc_listen_out_err(new_smc);
13688c2ecf20Sopenharmony_ci			return;
13698c2ecf20Sopenharmony_ci		}
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci	smc_listen_out_connected(new_smc);
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci/* listen worker: version checking */
13758c2ecf20Sopenharmony_cistatic int smc_listen_v2_check(struct smc_sock *new_smc,
13768c2ecf20Sopenharmony_ci			       struct smc_clc_msg_proposal *pclc,
13778c2ecf20Sopenharmony_ci			       struct smc_init_info *ini)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	struct smc_clc_smcd_v2_extension *pclc_smcd_v2_ext;
13808c2ecf20Sopenharmony_ci	struct smc_clc_v2_extension *pclc_v2_ext;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	ini->smc_type_v1 = pclc->hdr.typev1;
13838c2ecf20Sopenharmony_ci	ini->smc_type_v2 = pclc->hdr.typev2;
13848c2ecf20Sopenharmony_ci	ini->smcd_version = ini->smc_type_v1 != SMC_TYPE_N ? SMC_V1 : 0;
13858c2ecf20Sopenharmony_ci	if (pclc->hdr.version > SMC_V1)
13868c2ecf20Sopenharmony_ci		ini->smcd_version |=
13878c2ecf20Sopenharmony_ci				ini->smc_type_v2 != SMC_TYPE_N ? SMC_V2 : 0;
13888c2ecf20Sopenharmony_ci	if (!smc_ism_v2_capable) {
13898c2ecf20Sopenharmony_ci		ini->smcd_version &= ~SMC_V2;
13908c2ecf20Sopenharmony_ci		goto out;
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci	pclc_v2_ext = smc_get_clc_v2_ext(pclc);
13938c2ecf20Sopenharmony_ci	if (!pclc_v2_ext) {
13948c2ecf20Sopenharmony_ci		ini->smcd_version &= ~SMC_V2;
13958c2ecf20Sopenharmony_ci		goto out;
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci	pclc_smcd_v2_ext = smc_get_clc_smcd_v2_ext(pclc_v2_ext);
13988c2ecf20Sopenharmony_ci	if (!pclc_smcd_v2_ext)
13998c2ecf20Sopenharmony_ci		ini->smcd_version &= ~SMC_V2;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ciout:
14028c2ecf20Sopenharmony_ci	if (!ini->smcd_version) {
14038c2ecf20Sopenharmony_ci		if (pclc->hdr.typev1 == SMC_TYPE_B ||
14048c2ecf20Sopenharmony_ci		    pclc->hdr.typev2 == SMC_TYPE_B)
14058c2ecf20Sopenharmony_ci			return SMC_CLC_DECL_NOSMCDEV;
14068c2ecf20Sopenharmony_ci		if (pclc->hdr.typev1 == SMC_TYPE_D ||
14078c2ecf20Sopenharmony_ci		    pclc->hdr.typev2 == SMC_TYPE_D)
14088c2ecf20Sopenharmony_ci			return SMC_CLC_DECL_NOSMCDDEV;
14098c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOSMCRDEV;
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	return 0;
14138c2ecf20Sopenharmony_ci}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci/* listen worker: check prefixes */
14168c2ecf20Sopenharmony_cistatic int smc_listen_prfx_check(struct smc_sock *new_smc,
14178c2ecf20Sopenharmony_ci				 struct smc_clc_msg_proposal *pclc)
14188c2ecf20Sopenharmony_ci{
14198c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal_prefix *pclc_prfx;
14208c2ecf20Sopenharmony_ci	struct socket *newclcsock = new_smc->clcsock;
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	if (pclc->hdr.typev1 == SMC_TYPE_N)
14238c2ecf20Sopenharmony_ci		return 0;
14248c2ecf20Sopenharmony_ci	pclc_prfx = smc_clc_proposal_get_prefix(pclc);
14258c2ecf20Sopenharmony_ci	if (smc_clc_prfx_match(newclcsock, pclc_prfx))
14268c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_DIFFPREFIX;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	return 0;
14298c2ecf20Sopenharmony_ci}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci/* listen worker: initialize connection and buffers */
14328c2ecf20Sopenharmony_cistatic int smc_listen_rdma_init(struct smc_sock *new_smc,
14338c2ecf20Sopenharmony_ci				struct smc_init_info *ini)
14348c2ecf20Sopenharmony_ci{
14358c2ecf20Sopenharmony_ci	int rc;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	/* allocate connection / link group */
14388c2ecf20Sopenharmony_ci	rc = smc_conn_create(new_smc, ini);
14398c2ecf20Sopenharmony_ci	if (rc)
14408c2ecf20Sopenharmony_ci		return rc;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* create send buffer and rmb */
14438c2ecf20Sopenharmony_ci	if (smc_buf_create(new_smc, false))
14448c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_MEM;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	return 0;
14478c2ecf20Sopenharmony_ci}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci/* listen worker: initialize connection and buffers for SMC-D */
14508c2ecf20Sopenharmony_cistatic int smc_listen_ism_init(struct smc_sock *new_smc,
14518c2ecf20Sopenharmony_ci			       struct smc_init_info *ini)
14528c2ecf20Sopenharmony_ci{
14538c2ecf20Sopenharmony_ci	int rc;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	rc = smc_conn_create(new_smc, ini);
14568c2ecf20Sopenharmony_ci	if (rc)
14578c2ecf20Sopenharmony_ci		return rc;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	/* Create send and receive buffers */
14608c2ecf20Sopenharmony_ci	rc = smc_buf_create(new_smc, true);
14618c2ecf20Sopenharmony_ci	if (rc) {
14628c2ecf20Sopenharmony_ci		if (ini->first_contact_local)
14638c2ecf20Sopenharmony_ci			smc_lgr_cleanup_early(&new_smc->conn);
14648c2ecf20Sopenharmony_ci		else
14658c2ecf20Sopenharmony_ci			smc_conn_free(&new_smc->conn);
14668c2ecf20Sopenharmony_ci		return (rc == -ENOSPC) ? SMC_CLC_DECL_MAX_DMB :
14678c2ecf20Sopenharmony_ci					 SMC_CLC_DECL_MEM;
14688c2ecf20Sopenharmony_ci	}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	return 0;
14718c2ecf20Sopenharmony_ci}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_cistatic bool smc_is_already_selected(struct smcd_dev *smcd,
14748c2ecf20Sopenharmony_ci				    struct smc_init_info *ini,
14758c2ecf20Sopenharmony_ci				    int matches)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	int i;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	for (i = 0; i < matches; i++)
14808c2ecf20Sopenharmony_ci		if (smcd == ini->ism_dev[i])
14818c2ecf20Sopenharmony_ci			return true;
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	return false;
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci/* check for ISM devices matching proposed ISM devices */
14878c2ecf20Sopenharmony_cistatic void smc_check_ism_v2_match(struct smc_init_info *ini,
14888c2ecf20Sopenharmony_ci				   u16 proposed_chid, u64 proposed_gid,
14898c2ecf20Sopenharmony_ci				   unsigned int *matches)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct smcd_dev *smcd;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	list_for_each_entry(smcd, &smcd_dev_list.list, list) {
14948c2ecf20Sopenharmony_ci		if (smcd->going_away)
14958c2ecf20Sopenharmony_ci			continue;
14968c2ecf20Sopenharmony_ci		if (smc_is_already_selected(smcd, ini, *matches))
14978c2ecf20Sopenharmony_ci			continue;
14988c2ecf20Sopenharmony_ci		if (smc_ism_get_chid(smcd) == proposed_chid &&
14998c2ecf20Sopenharmony_ci		    !smc_ism_cantalk(proposed_gid, ISM_RESERVED_VLANID, smcd)) {
15008c2ecf20Sopenharmony_ci			ini->ism_peer_gid[*matches] = proposed_gid;
15018c2ecf20Sopenharmony_ci			ini->ism_dev[*matches] = smcd;
15028c2ecf20Sopenharmony_ci			(*matches)++;
15038c2ecf20Sopenharmony_ci			break;
15048c2ecf20Sopenharmony_ci		}
15058c2ecf20Sopenharmony_ci	}
15068c2ecf20Sopenharmony_ci}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_cistatic void smc_find_ism_v2_device_serv(struct smc_sock *new_smc,
15098c2ecf20Sopenharmony_ci					struct smc_clc_msg_proposal *pclc,
15108c2ecf20Sopenharmony_ci					struct smc_init_info *ini)
15118c2ecf20Sopenharmony_ci{
15128c2ecf20Sopenharmony_ci	struct smc_clc_smcd_v2_extension *smcd_v2_ext;
15138c2ecf20Sopenharmony_ci	struct smc_clc_v2_extension *smc_v2_ext;
15148c2ecf20Sopenharmony_ci	struct smc_clc_msg_smcd *pclc_smcd;
15158c2ecf20Sopenharmony_ci	unsigned int matches = 0;
15168c2ecf20Sopenharmony_ci	u8 smcd_version;
15178c2ecf20Sopenharmony_ci	u8 *eid = NULL;
15188c2ecf20Sopenharmony_ci	int i;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	if (!(ini->smcd_version & SMC_V2) || !smcd_indicated(ini->smc_type_v2))
15218c2ecf20Sopenharmony_ci		goto not_found;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	pclc_smcd = smc_get_clc_msg_smcd(pclc);
15248c2ecf20Sopenharmony_ci	smc_v2_ext = smc_get_clc_v2_ext(pclc);
15258c2ecf20Sopenharmony_ci	smcd_v2_ext = smc_get_clc_smcd_v2_ext(smc_v2_ext);
15268c2ecf20Sopenharmony_ci	if (!smcd_v2_ext ||
15278c2ecf20Sopenharmony_ci	    !smc_v2_ext->hdr.flag.seid) /* no system EID support for SMCD */
15288c2ecf20Sopenharmony_ci		goto not_found;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	mutex_lock(&smcd_dev_list.mutex);
15318c2ecf20Sopenharmony_ci	if (pclc_smcd->ism.chid)
15328c2ecf20Sopenharmony_ci		/* check for ISM device matching proposed native ISM device */
15338c2ecf20Sopenharmony_ci		smc_check_ism_v2_match(ini, ntohs(pclc_smcd->ism.chid),
15348c2ecf20Sopenharmony_ci				       ntohll(pclc_smcd->ism.gid), &matches);
15358c2ecf20Sopenharmony_ci	for (i = 1; i <= smc_v2_ext->hdr.ism_gid_cnt; i++) {
15368c2ecf20Sopenharmony_ci		/* check for ISM devices matching proposed non-native ISM
15378c2ecf20Sopenharmony_ci		 * devices
15388c2ecf20Sopenharmony_ci		 */
15398c2ecf20Sopenharmony_ci		smc_check_ism_v2_match(ini,
15408c2ecf20Sopenharmony_ci				       ntohs(smcd_v2_ext->gidchid[i - 1].chid),
15418c2ecf20Sopenharmony_ci				       ntohll(smcd_v2_ext->gidchid[i - 1].gid),
15428c2ecf20Sopenharmony_ci				       &matches);
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci	mutex_unlock(&smcd_dev_list.mutex);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	if (ini->ism_dev[0]) {
15478c2ecf20Sopenharmony_ci		smc_ism_get_system_eid(ini->ism_dev[0], &eid);
15488c2ecf20Sopenharmony_ci		if (memcmp(eid, smcd_v2_ext->system_eid, SMC_MAX_EID_LEN))
15498c2ecf20Sopenharmony_ci			goto not_found;
15508c2ecf20Sopenharmony_ci	} else {
15518c2ecf20Sopenharmony_ci		goto not_found;
15528c2ecf20Sopenharmony_ci	}
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/* separate - outside the smcd_dev_list.lock */
15558c2ecf20Sopenharmony_ci	smcd_version = ini->smcd_version;
15568c2ecf20Sopenharmony_ci	for (i = 0; i < matches; i++) {
15578c2ecf20Sopenharmony_ci		ini->smcd_version = SMC_V2;
15588c2ecf20Sopenharmony_ci		ini->is_smcd = true;
15598c2ecf20Sopenharmony_ci		ini->ism_selected = i;
15608c2ecf20Sopenharmony_ci		if (smc_listen_ism_init(new_smc, ini))
15618c2ecf20Sopenharmony_ci			/* try next active ISM device */
15628c2ecf20Sopenharmony_ci			continue;
15638c2ecf20Sopenharmony_ci		return; /* matching and usable V2 ISM device found */
15648c2ecf20Sopenharmony_ci	}
15658c2ecf20Sopenharmony_ci	/* no V2 ISM device could be initialized */
15668c2ecf20Sopenharmony_ci	ini->smcd_version = smcd_version;	/* restore original value */
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_cinot_found:
15698c2ecf20Sopenharmony_ci	ini->smcd_version &= ~SMC_V2;
15708c2ecf20Sopenharmony_ci	ini->ism_dev[0] = NULL;
15718c2ecf20Sopenharmony_ci	ini->is_smcd = false;
15728c2ecf20Sopenharmony_ci}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_cistatic void smc_find_ism_v1_device_serv(struct smc_sock *new_smc,
15758c2ecf20Sopenharmony_ci					struct smc_clc_msg_proposal *pclc,
15768c2ecf20Sopenharmony_ci					struct smc_init_info *ini)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	struct smc_clc_msg_smcd *pclc_smcd = smc_get_clc_msg_smcd(pclc);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	/* check if ISM V1 is available */
15818c2ecf20Sopenharmony_ci	if (!(ini->smcd_version & SMC_V1) || !smcd_indicated(ini->smc_type_v1))
15828c2ecf20Sopenharmony_ci		goto not_found;
15838c2ecf20Sopenharmony_ci	ini->is_smcd = true; /* prepare ISM check */
15848c2ecf20Sopenharmony_ci	ini->ism_peer_gid[0] = ntohll(pclc_smcd->ism.gid);
15858c2ecf20Sopenharmony_ci	if (smc_find_ism_device(new_smc, ini))
15868c2ecf20Sopenharmony_ci		goto not_found;
15878c2ecf20Sopenharmony_ci	ini->ism_selected = 0;
15888c2ecf20Sopenharmony_ci	if (!smc_listen_ism_init(new_smc, ini))
15898c2ecf20Sopenharmony_ci		return;		/* V1 ISM device found */
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_cinot_found:
15928c2ecf20Sopenharmony_ci	ini->ism_dev[0] = NULL;
15938c2ecf20Sopenharmony_ci	ini->is_smcd = false;
15948c2ecf20Sopenharmony_ci}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci/* listen worker: register buffers */
15978c2ecf20Sopenharmony_cistatic int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first)
15988c2ecf20Sopenharmony_ci{
15998c2ecf20Sopenharmony_ci	struct smc_connection *conn = &new_smc->conn;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci	if (!local_first) {
16028c2ecf20Sopenharmony_ci		if (smcr_lgr_reg_rmbs(conn->lnk, conn->rmb_desc))
16038c2ecf20Sopenharmony_ci			return SMC_CLC_DECL_ERR_REGRMB;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci	smc_rmb_sync_sg_for_device(&new_smc->conn);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	return 0;
16088c2ecf20Sopenharmony_ci}
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_cistatic int smc_find_rdma_v1_device_serv(struct smc_sock *new_smc,
16118c2ecf20Sopenharmony_ci					struct smc_clc_msg_proposal *pclc,
16128c2ecf20Sopenharmony_ci					struct smc_init_info *ini)
16138c2ecf20Sopenharmony_ci{
16148c2ecf20Sopenharmony_ci	int rc;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	if (!smcr_indicated(ini->smc_type_v1))
16178c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOSMCDEV;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	/* prepare RDMA check */
16208c2ecf20Sopenharmony_ci	ini->ib_lcl = &pclc->lcl;
16218c2ecf20Sopenharmony_ci	rc = smc_find_rdma_device(new_smc, ini);
16228c2ecf20Sopenharmony_ci	if (rc) {
16238c2ecf20Sopenharmony_ci		/* no RDMA device found */
16248c2ecf20Sopenharmony_ci		if (ini->smc_type_v1 == SMC_TYPE_B)
16258c2ecf20Sopenharmony_ci			/* neither ISM nor RDMA device found */
16268c2ecf20Sopenharmony_ci			rc = SMC_CLC_DECL_NOSMCDEV;
16278c2ecf20Sopenharmony_ci		return rc;
16288c2ecf20Sopenharmony_ci	}
16298c2ecf20Sopenharmony_ci	rc = smc_listen_rdma_init(new_smc, ini);
16308c2ecf20Sopenharmony_ci	if (rc)
16318c2ecf20Sopenharmony_ci		return rc;
16328c2ecf20Sopenharmony_ci	return smc_listen_rdma_reg(new_smc, ini->first_contact_local);
16338c2ecf20Sopenharmony_ci}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci/* determine the local device matching to proposal */
16368c2ecf20Sopenharmony_cistatic int smc_listen_find_device(struct smc_sock *new_smc,
16378c2ecf20Sopenharmony_ci				  struct smc_clc_msg_proposal *pclc,
16388c2ecf20Sopenharmony_ci				  struct smc_init_info *ini)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	int rc;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	/* check for ISM device matching V2 proposed device */
16438c2ecf20Sopenharmony_ci	smc_find_ism_v2_device_serv(new_smc, pclc, ini);
16448c2ecf20Sopenharmony_ci	if (ini->ism_dev[0])
16458c2ecf20Sopenharmony_ci		return 0;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	if (!(ini->smcd_version & SMC_V1))
16488c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOSMCDEV;
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	/* check for matching IP prefix and subnet length */
16518c2ecf20Sopenharmony_ci	rc = smc_listen_prfx_check(new_smc, pclc);
16528c2ecf20Sopenharmony_ci	if (rc)
16538c2ecf20Sopenharmony_ci		return rc;
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci	/* get vlan id from IP device */
16568c2ecf20Sopenharmony_ci	if (smc_vlan_by_tcpsk(new_smc->clcsock, ini))
16578c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_GETVLANERR;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	/* check for ISM device matching V1 proposed device */
16608c2ecf20Sopenharmony_ci	smc_find_ism_v1_device_serv(new_smc, pclc, ini);
16618c2ecf20Sopenharmony_ci	if (ini->ism_dev[0])
16628c2ecf20Sopenharmony_ci		return 0;
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	if (pclc->hdr.typev1 == SMC_TYPE_D)
16658c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_NOSMCDDEV; /* skip RDMA and decline */
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	/* check if RDMA is available */
16688c2ecf20Sopenharmony_ci	return smc_find_rdma_v1_device_serv(new_smc, pclc, ini);
16698c2ecf20Sopenharmony_ci}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci/* listen worker: finish RDMA setup */
16728c2ecf20Sopenharmony_cistatic int smc_listen_rdma_finish(struct smc_sock *new_smc,
16738c2ecf20Sopenharmony_ci				  struct smc_clc_msg_accept_confirm *cclc,
16748c2ecf20Sopenharmony_ci				  bool local_first)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	struct smc_link *link = new_smc->conn.lnk;
16778c2ecf20Sopenharmony_ci	int reason_code = 0;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (local_first)
16808c2ecf20Sopenharmony_ci		smc_link_save_peer_info(link, cclc);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	if (smc_rmb_rtoken_handling(&new_smc->conn, link, cclc))
16838c2ecf20Sopenharmony_ci		return SMC_CLC_DECL_ERR_RTOK;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	if (local_first) {
16868c2ecf20Sopenharmony_ci		if (smc_ib_ready_link(link))
16878c2ecf20Sopenharmony_ci			return SMC_CLC_DECL_ERR_RDYLNK;
16888c2ecf20Sopenharmony_ci		/* QP confirmation over RoCE fabric */
16898c2ecf20Sopenharmony_ci		smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
16908c2ecf20Sopenharmony_ci		reason_code = smcr_serv_conf_first_link(new_smc);
16918c2ecf20Sopenharmony_ci		smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
16928c2ecf20Sopenharmony_ci	}
16938c2ecf20Sopenharmony_ci	return reason_code;
16948c2ecf20Sopenharmony_ci}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci/* setup for connection of server */
16978c2ecf20Sopenharmony_cistatic void smc_listen_work(struct work_struct *work)
16988c2ecf20Sopenharmony_ci{
16998c2ecf20Sopenharmony_ci	struct smc_sock *new_smc = container_of(work, struct smc_sock,
17008c2ecf20Sopenharmony_ci						smc_listen_work);
17018c2ecf20Sopenharmony_ci	u8 version = smc_ism_v2_capable ? SMC_V2 : SMC_V1;
17028c2ecf20Sopenharmony_ci	struct socket *newclcsock = new_smc->clcsock;
17038c2ecf20Sopenharmony_ci	struct smc_clc_msg_accept_confirm *cclc;
17048c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal_area *buf;
17058c2ecf20Sopenharmony_ci	struct smc_clc_msg_proposal *pclc;
17068c2ecf20Sopenharmony_ci	struct smc_init_info *ini = NULL;
17078c2ecf20Sopenharmony_ci	int rc = 0;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN)
17108c2ecf20Sopenharmony_ci		return smc_listen_out_err(new_smc);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	if (new_smc->use_fallback) {
17138c2ecf20Sopenharmony_ci		smc_listen_out_connected(new_smc);
17148c2ecf20Sopenharmony_ci		return;
17158c2ecf20Sopenharmony_ci	}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	/* check if peer is smc capable */
17188c2ecf20Sopenharmony_ci	if (!tcp_sk(newclcsock->sk)->syn_smc) {
17198c2ecf20Sopenharmony_ci		smc_switch_to_fallback(new_smc);
17208c2ecf20Sopenharmony_ci		new_smc->fallback_rsn = SMC_CLC_DECL_PEERNOSMC;
17218c2ecf20Sopenharmony_ci		smc_listen_out_connected(new_smc);
17228c2ecf20Sopenharmony_ci		return;
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	/* do inband token exchange -
17268c2ecf20Sopenharmony_ci	 * wait for and receive SMC Proposal CLC message
17278c2ecf20Sopenharmony_ci	 */
17288c2ecf20Sopenharmony_ci	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
17298c2ecf20Sopenharmony_ci	if (!buf) {
17308c2ecf20Sopenharmony_ci		rc = SMC_CLC_DECL_MEM;
17318c2ecf20Sopenharmony_ci		goto out_decl;
17328c2ecf20Sopenharmony_ci	}
17338c2ecf20Sopenharmony_ci	pclc = (struct smc_clc_msg_proposal *)buf;
17348c2ecf20Sopenharmony_ci	rc = smc_clc_wait_msg(new_smc, pclc, sizeof(*buf),
17358c2ecf20Sopenharmony_ci			      SMC_CLC_PROPOSAL, CLC_WAIT_TIME);
17368c2ecf20Sopenharmony_ci	if (rc)
17378c2ecf20Sopenharmony_ci		goto out_decl;
17388c2ecf20Sopenharmony_ci	version = pclc->hdr.version == SMC_V1 ? SMC_V1 : version;
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	/* IPSec connections opt out of SMC optimizations */
17418c2ecf20Sopenharmony_ci	if (using_ipsec(new_smc)) {
17428c2ecf20Sopenharmony_ci		rc = SMC_CLC_DECL_IPSEC;
17438c2ecf20Sopenharmony_ci		goto out_decl;
17448c2ecf20Sopenharmony_ci	}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	ini = kzalloc(sizeof(*ini), GFP_KERNEL);
17478c2ecf20Sopenharmony_ci	if (!ini) {
17488c2ecf20Sopenharmony_ci		rc = SMC_CLC_DECL_MEM;
17498c2ecf20Sopenharmony_ci		goto out_decl;
17508c2ecf20Sopenharmony_ci	}
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	/* initial version checking */
17538c2ecf20Sopenharmony_ci	rc = smc_listen_v2_check(new_smc, pclc, ini);
17548c2ecf20Sopenharmony_ci	if (rc)
17558c2ecf20Sopenharmony_ci		goto out_decl;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci	mutex_lock(&smc_server_lgr_pending);
17588c2ecf20Sopenharmony_ci	smc_close_init(new_smc);
17598c2ecf20Sopenharmony_ci	smc_rx_init(new_smc);
17608c2ecf20Sopenharmony_ci	smc_tx_init(new_smc);
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	/* determine ISM or RoCE device used for connection */
17638c2ecf20Sopenharmony_ci	rc = smc_listen_find_device(new_smc, pclc, ini);
17648c2ecf20Sopenharmony_ci	if (rc)
17658c2ecf20Sopenharmony_ci		goto out_unlock;
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	/* send SMC Accept CLC message */
17688c2ecf20Sopenharmony_ci	rc = smc_clc_send_accept(new_smc, ini->first_contact_local,
17698c2ecf20Sopenharmony_ci				 ini->smcd_version == SMC_V2 ? SMC_V2 : SMC_V1);
17708c2ecf20Sopenharmony_ci	if (rc)
17718c2ecf20Sopenharmony_ci		goto out_unlock;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	/* SMC-D does not need this lock any more */
17748c2ecf20Sopenharmony_ci	if (ini->is_smcd)
17758c2ecf20Sopenharmony_ci		mutex_unlock(&smc_server_lgr_pending);
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	/* receive SMC Confirm CLC message */
17788c2ecf20Sopenharmony_ci	memset(buf, 0, sizeof(*buf));
17798c2ecf20Sopenharmony_ci	cclc = (struct smc_clc_msg_accept_confirm *)buf;
17808c2ecf20Sopenharmony_ci	rc = smc_clc_wait_msg(new_smc, cclc, sizeof(*buf),
17818c2ecf20Sopenharmony_ci			      SMC_CLC_CONFIRM, CLC_WAIT_TIME);
17828c2ecf20Sopenharmony_ci	if (rc) {
17838c2ecf20Sopenharmony_ci		if (!ini->is_smcd)
17848c2ecf20Sopenharmony_ci			goto out_unlock;
17858c2ecf20Sopenharmony_ci		goto out_decl;
17868c2ecf20Sopenharmony_ci	}
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	/* finish worker */
17898c2ecf20Sopenharmony_ci	if (!ini->is_smcd) {
17908c2ecf20Sopenharmony_ci		rc = smc_listen_rdma_finish(new_smc, cclc,
17918c2ecf20Sopenharmony_ci					    ini->first_contact_local);
17928c2ecf20Sopenharmony_ci		if (rc)
17938c2ecf20Sopenharmony_ci			goto out_unlock;
17948c2ecf20Sopenharmony_ci		mutex_unlock(&smc_server_lgr_pending);
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci	smc_conn_save_peer_info(new_smc, cclc);
17978c2ecf20Sopenharmony_ci	smc_listen_out_connected(new_smc);
17988c2ecf20Sopenharmony_ci	goto out_free;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ciout_unlock:
18018c2ecf20Sopenharmony_ci	mutex_unlock(&smc_server_lgr_pending);
18028c2ecf20Sopenharmony_ciout_decl:
18038c2ecf20Sopenharmony_ci	smc_listen_decline(new_smc, rc, ini ? ini->first_contact_local : 0,
18048c2ecf20Sopenharmony_ci			   version);
18058c2ecf20Sopenharmony_ciout_free:
18068c2ecf20Sopenharmony_ci	kfree(ini);
18078c2ecf20Sopenharmony_ci	kfree(buf);
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_cistatic void smc_tcp_listen_work(struct work_struct *work)
18118c2ecf20Sopenharmony_ci{
18128c2ecf20Sopenharmony_ci	struct smc_sock *lsmc = container_of(work, struct smc_sock,
18138c2ecf20Sopenharmony_ci					     tcp_listen_work);
18148c2ecf20Sopenharmony_ci	struct sock *lsk = &lsmc->sk;
18158c2ecf20Sopenharmony_ci	struct smc_sock *new_smc;
18168c2ecf20Sopenharmony_ci	int rc = 0;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	lock_sock(lsk);
18198c2ecf20Sopenharmony_ci	while (lsk->sk_state == SMC_LISTEN) {
18208c2ecf20Sopenharmony_ci		rc = smc_clcsock_accept(lsmc, &new_smc);
18218c2ecf20Sopenharmony_ci		if (rc) /* clcsock accept queue empty or error */
18228c2ecf20Sopenharmony_ci			goto out;
18238c2ecf20Sopenharmony_ci		if (!new_smc)
18248c2ecf20Sopenharmony_ci			continue;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci		new_smc->listen_smc = lsmc;
18278c2ecf20Sopenharmony_ci		new_smc->use_fallback = lsmc->use_fallback;
18288c2ecf20Sopenharmony_ci		new_smc->fallback_rsn = lsmc->fallback_rsn;
18298c2ecf20Sopenharmony_ci		sock_hold(lsk); /* sock_put in smc_listen_work */
18308c2ecf20Sopenharmony_ci		INIT_WORK(&new_smc->smc_listen_work, smc_listen_work);
18318c2ecf20Sopenharmony_ci		smc_copy_sock_settings_to_smc(new_smc);
18328c2ecf20Sopenharmony_ci		new_smc->sk.sk_sndbuf = lsmc->sk.sk_sndbuf;
18338c2ecf20Sopenharmony_ci		new_smc->sk.sk_rcvbuf = lsmc->sk.sk_rcvbuf;
18348c2ecf20Sopenharmony_ci		sock_hold(&new_smc->sk); /* sock_put in passive closing */
18358c2ecf20Sopenharmony_ci		if (!queue_work(smc_hs_wq, &new_smc->smc_listen_work))
18368c2ecf20Sopenharmony_ci			sock_put(&new_smc->sk);
18378c2ecf20Sopenharmony_ci	}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ciout:
18408c2ecf20Sopenharmony_ci	release_sock(lsk);
18418c2ecf20Sopenharmony_ci	sock_put(&lsmc->sk); /* sock_hold in smc_clcsock_data_ready() */
18428c2ecf20Sopenharmony_ci}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_cistatic void smc_clcsock_data_ready(struct sock *listen_clcsock)
18458c2ecf20Sopenharmony_ci{
18468c2ecf20Sopenharmony_ci	struct smc_sock *lsmc;
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	lsmc = (struct smc_sock *)
18498c2ecf20Sopenharmony_ci	       ((uintptr_t)listen_clcsock->sk_user_data & ~SK_USER_DATA_NOCOPY);
18508c2ecf20Sopenharmony_ci	if (!lsmc)
18518c2ecf20Sopenharmony_ci		return;
18528c2ecf20Sopenharmony_ci	lsmc->clcsk_data_ready(listen_clcsock);
18538c2ecf20Sopenharmony_ci	if (lsmc->sk.sk_state == SMC_LISTEN) {
18548c2ecf20Sopenharmony_ci		sock_hold(&lsmc->sk); /* sock_put in smc_tcp_listen_work() */
18558c2ecf20Sopenharmony_ci		if (!queue_work(smc_hs_wq, &lsmc->tcp_listen_work))
18568c2ecf20Sopenharmony_ci			sock_put(&lsmc->sk);
18578c2ecf20Sopenharmony_ci	}
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_cistatic int smc_listen(struct socket *sock, int backlog)
18618c2ecf20Sopenharmony_ci{
18628c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
18638c2ecf20Sopenharmony_ci	struct smc_sock *smc;
18648c2ecf20Sopenharmony_ci	int rc;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
18678c2ecf20Sopenharmony_ci	lock_sock(sk);
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	rc = -EINVAL;
18708c2ecf20Sopenharmony_ci	if ((sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN) ||
18718c2ecf20Sopenharmony_ci	    smc->connect_nonblock)
18728c2ecf20Sopenharmony_ci		goto out;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	rc = 0;
18758c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_LISTEN) {
18768c2ecf20Sopenharmony_ci		sk->sk_max_ack_backlog = backlog;
18778c2ecf20Sopenharmony_ci		goto out;
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci	/* some socket options are handled in core, so we could not apply
18808c2ecf20Sopenharmony_ci	 * them to the clc socket -- copy smc socket options to clc socket
18818c2ecf20Sopenharmony_ci	 */
18828c2ecf20Sopenharmony_ci	smc_copy_sock_settings_to_clc(smc);
18838c2ecf20Sopenharmony_ci	if (!smc->use_fallback)
18848c2ecf20Sopenharmony_ci		tcp_sk(smc->clcsock->sk)->syn_smc = 1;
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	/* save original sk_data_ready function and establish
18878c2ecf20Sopenharmony_ci	 * smc-specific sk_data_ready function
18888c2ecf20Sopenharmony_ci	 */
18898c2ecf20Sopenharmony_ci	smc->clcsk_data_ready = smc->clcsock->sk->sk_data_ready;
18908c2ecf20Sopenharmony_ci	smc->clcsock->sk->sk_data_ready = smc_clcsock_data_ready;
18918c2ecf20Sopenharmony_ci	smc->clcsock->sk->sk_user_data =
18928c2ecf20Sopenharmony_ci		(void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY);
18938c2ecf20Sopenharmony_ci	rc = kernel_listen(smc->clcsock, backlog);
18948c2ecf20Sopenharmony_ci	if (rc) {
18958c2ecf20Sopenharmony_ci		smc->clcsock->sk->sk_data_ready = smc->clcsk_data_ready;
18968c2ecf20Sopenharmony_ci		goto out;
18978c2ecf20Sopenharmony_ci	}
18988c2ecf20Sopenharmony_ci	sk->sk_max_ack_backlog = backlog;
18998c2ecf20Sopenharmony_ci	sk->sk_ack_backlog = 0;
19008c2ecf20Sopenharmony_ci	sk->sk_state = SMC_LISTEN;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ciout:
19038c2ecf20Sopenharmony_ci	release_sock(sk);
19048c2ecf20Sopenharmony_ci	return rc;
19058c2ecf20Sopenharmony_ci}
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_cistatic int smc_accept(struct socket *sock, struct socket *new_sock,
19088c2ecf20Sopenharmony_ci		      int flags, bool kern)
19098c2ecf20Sopenharmony_ci{
19108c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk, *nsk;
19118c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
19128c2ecf20Sopenharmony_ci	struct smc_sock *lsmc;
19138c2ecf20Sopenharmony_ci	long timeo;
19148c2ecf20Sopenharmony_ci	int rc = 0;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	lsmc = smc_sk(sk);
19178c2ecf20Sopenharmony_ci	sock_hold(sk); /* sock_put below */
19188c2ecf20Sopenharmony_ci	lock_sock(sk);
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	if (lsmc->sk.sk_state != SMC_LISTEN) {
19218c2ecf20Sopenharmony_ci		rc = -EINVAL;
19228c2ecf20Sopenharmony_ci		release_sock(sk);
19238c2ecf20Sopenharmony_ci		goto out;
19248c2ecf20Sopenharmony_ci	}
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	/* Wait for an incoming connection */
19278c2ecf20Sopenharmony_ci	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
19288c2ecf20Sopenharmony_ci	add_wait_queue_exclusive(sk_sleep(sk), &wait);
19298c2ecf20Sopenharmony_ci	while (!(nsk = smc_accept_dequeue(sk, new_sock))) {
19308c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
19318c2ecf20Sopenharmony_ci		if (!timeo) {
19328c2ecf20Sopenharmony_ci			rc = -EAGAIN;
19338c2ecf20Sopenharmony_ci			break;
19348c2ecf20Sopenharmony_ci		}
19358c2ecf20Sopenharmony_ci		release_sock(sk);
19368c2ecf20Sopenharmony_ci		timeo = schedule_timeout(timeo);
19378c2ecf20Sopenharmony_ci		/* wakeup by sk_data_ready in smc_listen_work() */
19388c2ecf20Sopenharmony_ci		sched_annotate_sleep();
19398c2ecf20Sopenharmony_ci		lock_sock(sk);
19408c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
19418c2ecf20Sopenharmony_ci			rc = sock_intr_errno(timeo);
19428c2ecf20Sopenharmony_ci			break;
19438c2ecf20Sopenharmony_ci		}
19448c2ecf20Sopenharmony_ci	}
19458c2ecf20Sopenharmony_ci	set_current_state(TASK_RUNNING);
19468c2ecf20Sopenharmony_ci	remove_wait_queue(sk_sleep(sk), &wait);
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_ci	if (!rc)
19498c2ecf20Sopenharmony_ci		rc = sock_error(nsk);
19508c2ecf20Sopenharmony_ci	release_sock(sk);
19518c2ecf20Sopenharmony_ci	if (rc)
19528c2ecf20Sopenharmony_ci		goto out;
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	if (lsmc->sockopt_defer_accept && !(flags & O_NONBLOCK)) {
19558c2ecf20Sopenharmony_ci		/* wait till data arrives on the socket */
19568c2ecf20Sopenharmony_ci		timeo = msecs_to_jiffies(lsmc->sockopt_defer_accept *
19578c2ecf20Sopenharmony_ci								MSEC_PER_SEC);
19588c2ecf20Sopenharmony_ci		if (smc_sk(nsk)->use_fallback) {
19598c2ecf20Sopenharmony_ci			struct sock *clcsk = smc_sk(nsk)->clcsock->sk;
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci			lock_sock(clcsk);
19628c2ecf20Sopenharmony_ci			if (skb_queue_empty(&clcsk->sk_receive_queue))
19638c2ecf20Sopenharmony_ci				sk_wait_data(clcsk, &timeo, NULL);
19648c2ecf20Sopenharmony_ci			release_sock(clcsk);
19658c2ecf20Sopenharmony_ci		} else if (!atomic_read(&smc_sk(nsk)->conn.bytes_to_rcv)) {
19668c2ecf20Sopenharmony_ci			lock_sock(nsk);
19678c2ecf20Sopenharmony_ci			smc_rx_wait(smc_sk(nsk), &timeo, smc_rx_data_available);
19688c2ecf20Sopenharmony_ci			release_sock(nsk);
19698c2ecf20Sopenharmony_ci		}
19708c2ecf20Sopenharmony_ci	}
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ciout:
19738c2ecf20Sopenharmony_ci	sock_put(sk); /* sock_hold above */
19748c2ecf20Sopenharmony_ci	return rc;
19758c2ecf20Sopenharmony_ci}
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_cistatic int smc_getname(struct socket *sock, struct sockaddr *addr,
19788c2ecf20Sopenharmony_ci		       int peer)
19798c2ecf20Sopenharmony_ci{
19808c2ecf20Sopenharmony_ci	struct smc_sock *smc;
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	if (peer && (sock->sk->sk_state != SMC_ACTIVE) &&
19838c2ecf20Sopenharmony_ci	    (sock->sk->sk_state != SMC_APPCLOSEWAIT1))
19848c2ecf20Sopenharmony_ci		return -ENOTCONN;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	smc = smc_sk(sock->sk);
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	return smc->clcsock->ops->getname(smc->clcsock, addr, peer);
19898c2ecf20Sopenharmony_ci}
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_cistatic int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
19928c2ecf20Sopenharmony_ci{
19938c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
19948c2ecf20Sopenharmony_ci	struct smc_sock *smc;
19958c2ecf20Sopenharmony_ci	int rc;
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
19988c2ecf20Sopenharmony_ci	lock_sock(sk);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	/* SMC does not support connect with fastopen */
20018c2ecf20Sopenharmony_ci	if (msg->msg_flags & MSG_FASTOPEN) {
20028c2ecf20Sopenharmony_ci		/* not connected yet, fallback */
20038c2ecf20Sopenharmony_ci		if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
20048c2ecf20Sopenharmony_ci			smc_switch_to_fallback(smc);
20058c2ecf20Sopenharmony_ci			smc->fallback_rsn = SMC_CLC_DECL_OPTUNSUPP;
20068c2ecf20Sopenharmony_ci		} else {
20078c2ecf20Sopenharmony_ci			rc = -EINVAL;
20088c2ecf20Sopenharmony_ci			goto out;
20098c2ecf20Sopenharmony_ci		}
20108c2ecf20Sopenharmony_ci	} else if ((sk->sk_state != SMC_ACTIVE) &&
20118c2ecf20Sopenharmony_ci		   (sk->sk_state != SMC_APPCLOSEWAIT1) &&
20128c2ecf20Sopenharmony_ci		   (sk->sk_state != SMC_INIT)) {
20138c2ecf20Sopenharmony_ci		rc = -EPIPE;
20148c2ecf20Sopenharmony_ci		goto out;
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	if (smc->use_fallback)
20188c2ecf20Sopenharmony_ci		rc = smc->clcsock->ops->sendmsg(smc->clcsock, msg, len);
20198c2ecf20Sopenharmony_ci	else
20208c2ecf20Sopenharmony_ci		rc = smc_tx_sendmsg(smc, msg, len);
20218c2ecf20Sopenharmony_ciout:
20228c2ecf20Sopenharmony_ci	release_sock(sk);
20238c2ecf20Sopenharmony_ci	return rc;
20248c2ecf20Sopenharmony_ci}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_cistatic int smc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
20278c2ecf20Sopenharmony_ci		       int flags)
20288c2ecf20Sopenharmony_ci{
20298c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
20308c2ecf20Sopenharmony_ci	struct smc_sock *smc;
20318c2ecf20Sopenharmony_ci	int rc = -ENOTCONN;
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
20348c2ecf20Sopenharmony_ci	lock_sock(sk);
20358c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) {
20368c2ecf20Sopenharmony_ci		/* socket was connected before, no more data to read */
20378c2ecf20Sopenharmony_ci		rc = 0;
20388c2ecf20Sopenharmony_ci		goto out;
20398c2ecf20Sopenharmony_ci	}
20408c2ecf20Sopenharmony_ci	if ((sk->sk_state == SMC_INIT) ||
20418c2ecf20Sopenharmony_ci	    (sk->sk_state == SMC_LISTEN) ||
20428c2ecf20Sopenharmony_ci	    (sk->sk_state == SMC_CLOSED))
20438c2ecf20Sopenharmony_ci		goto out;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_PEERFINCLOSEWAIT) {
20468c2ecf20Sopenharmony_ci		rc = 0;
20478c2ecf20Sopenharmony_ci		goto out;
20488c2ecf20Sopenharmony_ci	}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	if (smc->use_fallback) {
20518c2ecf20Sopenharmony_ci		rc = smc->clcsock->ops->recvmsg(smc->clcsock, msg, len, flags);
20528c2ecf20Sopenharmony_ci	} else {
20538c2ecf20Sopenharmony_ci		msg->msg_namelen = 0;
20548c2ecf20Sopenharmony_ci		rc = smc_rx_recvmsg(smc, msg, NULL, len, flags);
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ciout:
20588c2ecf20Sopenharmony_ci	release_sock(sk);
20598c2ecf20Sopenharmony_ci	return rc;
20608c2ecf20Sopenharmony_ci}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_cistatic __poll_t smc_accept_poll(struct sock *parent)
20638c2ecf20Sopenharmony_ci{
20648c2ecf20Sopenharmony_ci	struct smc_sock *isk = smc_sk(parent);
20658c2ecf20Sopenharmony_ci	__poll_t mask = 0;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	spin_lock(&isk->accept_q_lock);
20688c2ecf20Sopenharmony_ci	if (!list_empty(&isk->accept_q))
20698c2ecf20Sopenharmony_ci		mask = EPOLLIN | EPOLLRDNORM;
20708c2ecf20Sopenharmony_ci	spin_unlock(&isk->accept_q_lock);
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	return mask;
20738c2ecf20Sopenharmony_ci}
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_cistatic __poll_t smc_poll(struct file *file, struct socket *sock,
20768c2ecf20Sopenharmony_ci			     poll_table *wait)
20778c2ecf20Sopenharmony_ci{
20788c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
20798c2ecf20Sopenharmony_ci	struct smc_sock *smc;
20808c2ecf20Sopenharmony_ci	__poll_t mask = 0;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	if (!sk)
20838c2ecf20Sopenharmony_ci		return EPOLLNVAL;
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ci	smc = smc_sk(sock->sk);
20868c2ecf20Sopenharmony_ci	if (smc->use_fallback) {
20878c2ecf20Sopenharmony_ci		/* delegate to CLC child sock */
20888c2ecf20Sopenharmony_ci		mask = smc->clcsock->ops->poll(file, smc->clcsock, wait);
20898c2ecf20Sopenharmony_ci		sk->sk_err = smc->clcsock->sk->sk_err;
20908c2ecf20Sopenharmony_ci	} else {
20918c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_CLOSED)
20928c2ecf20Sopenharmony_ci			sock_poll_wait(file, sock, wait);
20938c2ecf20Sopenharmony_ci		if (sk->sk_err)
20948c2ecf20Sopenharmony_ci			mask |= EPOLLERR;
20958c2ecf20Sopenharmony_ci		if ((sk->sk_shutdown == SHUTDOWN_MASK) ||
20968c2ecf20Sopenharmony_ci		    (sk->sk_state == SMC_CLOSED))
20978c2ecf20Sopenharmony_ci			mask |= EPOLLHUP;
20988c2ecf20Sopenharmony_ci		if (sk->sk_state == SMC_LISTEN) {
20998c2ecf20Sopenharmony_ci			/* woken up by sk_data_ready in smc_listen_work() */
21008c2ecf20Sopenharmony_ci			mask |= smc_accept_poll(sk);
21018c2ecf20Sopenharmony_ci		} else if (smc->use_fallback) { /* as result of connect_work()*/
21028c2ecf20Sopenharmony_ci			mask |= smc->clcsock->ops->poll(file, smc->clcsock,
21038c2ecf20Sopenharmony_ci							   wait);
21048c2ecf20Sopenharmony_ci			sk->sk_err = smc->clcsock->sk->sk_err;
21058c2ecf20Sopenharmony_ci		} else {
21068c2ecf20Sopenharmony_ci			if ((sk->sk_state != SMC_INIT &&
21078c2ecf20Sopenharmony_ci			     atomic_read(&smc->conn.sndbuf_space)) ||
21088c2ecf20Sopenharmony_ci			    sk->sk_shutdown & SEND_SHUTDOWN) {
21098c2ecf20Sopenharmony_ci				mask |= EPOLLOUT | EPOLLWRNORM;
21108c2ecf20Sopenharmony_ci			} else {
21118c2ecf20Sopenharmony_ci				sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
21128c2ecf20Sopenharmony_ci				set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
21138c2ecf20Sopenharmony_ci			}
21148c2ecf20Sopenharmony_ci			if (atomic_read(&smc->conn.bytes_to_rcv))
21158c2ecf20Sopenharmony_ci				mask |= EPOLLIN | EPOLLRDNORM;
21168c2ecf20Sopenharmony_ci			if (sk->sk_shutdown & RCV_SHUTDOWN)
21178c2ecf20Sopenharmony_ci				mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
21188c2ecf20Sopenharmony_ci			if (sk->sk_state == SMC_APPCLOSEWAIT1)
21198c2ecf20Sopenharmony_ci				mask |= EPOLLIN;
21208c2ecf20Sopenharmony_ci			if (smc->conn.urg_state == SMC_URG_VALID)
21218c2ecf20Sopenharmony_ci				mask |= EPOLLPRI;
21228c2ecf20Sopenharmony_ci		}
21238c2ecf20Sopenharmony_ci	}
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	return mask;
21268c2ecf20Sopenharmony_ci}
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_cistatic int smc_shutdown(struct socket *sock, int how)
21298c2ecf20Sopenharmony_ci{
21308c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
21318c2ecf20Sopenharmony_ci	bool do_shutdown = true;
21328c2ecf20Sopenharmony_ci	struct smc_sock *smc;
21338c2ecf20Sopenharmony_ci	int rc = -EINVAL;
21348c2ecf20Sopenharmony_ci	int old_state;
21358c2ecf20Sopenharmony_ci	int rc1 = 0;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	if ((how < SHUT_RD) || (how > SHUT_RDWR))
21408c2ecf20Sopenharmony_ci		return rc;
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	lock_sock(sk);
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	rc = -ENOTCONN;
21458c2ecf20Sopenharmony_ci	if ((sk->sk_state != SMC_ACTIVE) &&
21468c2ecf20Sopenharmony_ci	    (sk->sk_state != SMC_PEERCLOSEWAIT1) &&
21478c2ecf20Sopenharmony_ci	    (sk->sk_state != SMC_PEERCLOSEWAIT2) &&
21488c2ecf20Sopenharmony_ci	    (sk->sk_state != SMC_APPCLOSEWAIT1) &&
21498c2ecf20Sopenharmony_ci	    (sk->sk_state != SMC_APPCLOSEWAIT2) &&
21508c2ecf20Sopenharmony_ci	    (sk->sk_state != SMC_APPFINCLOSEWAIT))
21518c2ecf20Sopenharmony_ci		goto out;
21528c2ecf20Sopenharmony_ci	if (smc->use_fallback) {
21538c2ecf20Sopenharmony_ci		rc = kernel_sock_shutdown(smc->clcsock, how);
21548c2ecf20Sopenharmony_ci		sk->sk_shutdown = smc->clcsock->sk->sk_shutdown;
21558c2ecf20Sopenharmony_ci		if (sk->sk_shutdown == SHUTDOWN_MASK) {
21568c2ecf20Sopenharmony_ci			sk->sk_state = SMC_CLOSED;
21578c2ecf20Sopenharmony_ci			sock_put(sk);
21588c2ecf20Sopenharmony_ci		}
21598c2ecf20Sopenharmony_ci		goto out;
21608c2ecf20Sopenharmony_ci	}
21618c2ecf20Sopenharmony_ci	switch (how) {
21628c2ecf20Sopenharmony_ci	case SHUT_RDWR:		/* shutdown in both directions */
21638c2ecf20Sopenharmony_ci		old_state = sk->sk_state;
21648c2ecf20Sopenharmony_ci		rc = smc_close_active(smc);
21658c2ecf20Sopenharmony_ci		if (old_state == SMC_ACTIVE &&
21668c2ecf20Sopenharmony_ci		    sk->sk_state == SMC_PEERCLOSEWAIT1)
21678c2ecf20Sopenharmony_ci			do_shutdown = false;
21688c2ecf20Sopenharmony_ci		break;
21698c2ecf20Sopenharmony_ci	case SHUT_WR:
21708c2ecf20Sopenharmony_ci		rc = smc_close_shutdown_write(smc);
21718c2ecf20Sopenharmony_ci		break;
21728c2ecf20Sopenharmony_ci	case SHUT_RD:
21738c2ecf20Sopenharmony_ci		rc = 0;
21748c2ecf20Sopenharmony_ci		/* nothing more to do because peer is not involved */
21758c2ecf20Sopenharmony_ci		break;
21768c2ecf20Sopenharmony_ci	}
21778c2ecf20Sopenharmony_ci	if (do_shutdown && smc->clcsock)
21788c2ecf20Sopenharmony_ci		rc1 = kernel_sock_shutdown(smc->clcsock, how);
21798c2ecf20Sopenharmony_ci	/* map sock_shutdown_cmd constants to sk_shutdown value range */
21808c2ecf20Sopenharmony_ci	sk->sk_shutdown |= how + 1;
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ciout:
21838c2ecf20Sopenharmony_ci	release_sock(sk);
21848c2ecf20Sopenharmony_ci	return rc ? rc : rc1;
21858c2ecf20Sopenharmony_ci}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_cistatic int smc_setsockopt(struct socket *sock, int level, int optname,
21888c2ecf20Sopenharmony_ci			  sockptr_t optval, unsigned int optlen)
21898c2ecf20Sopenharmony_ci{
21908c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
21918c2ecf20Sopenharmony_ci	struct smc_sock *smc;
21928c2ecf20Sopenharmony_ci	int val, rc;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (level == SOL_TCP && optname == TCP_ULP)
21958c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci	/* generic setsockopts reaching us here always apply to the
22008c2ecf20Sopenharmony_ci	 * CLC socket
22018c2ecf20Sopenharmony_ci	 */
22028c2ecf20Sopenharmony_ci	if (unlikely(!smc->clcsock->ops->setsockopt))
22038c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
22048c2ecf20Sopenharmony_ci	else
22058c2ecf20Sopenharmony_ci		rc = smc->clcsock->ops->setsockopt(smc->clcsock, level, optname,
22068c2ecf20Sopenharmony_ci						   optval, optlen);
22078c2ecf20Sopenharmony_ci	if (smc->clcsock->sk->sk_err) {
22088c2ecf20Sopenharmony_ci		sk->sk_err = smc->clcsock->sk->sk_err;
22098c2ecf20Sopenharmony_ci		sk->sk_error_report(sk);
22108c2ecf20Sopenharmony_ci	}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
22138c2ecf20Sopenharmony_ci		return -EINVAL;
22148c2ecf20Sopenharmony_ci	if (copy_from_sockptr(&val, optval, sizeof(int)))
22158c2ecf20Sopenharmony_ci		return -EFAULT;
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	lock_sock(sk);
22188c2ecf20Sopenharmony_ci	if (rc || smc->use_fallback)
22198c2ecf20Sopenharmony_ci		goto out;
22208c2ecf20Sopenharmony_ci	switch (optname) {
22218c2ecf20Sopenharmony_ci	case TCP_FASTOPEN:
22228c2ecf20Sopenharmony_ci	case TCP_FASTOPEN_CONNECT:
22238c2ecf20Sopenharmony_ci	case TCP_FASTOPEN_KEY:
22248c2ecf20Sopenharmony_ci	case TCP_FASTOPEN_NO_COOKIE:
22258c2ecf20Sopenharmony_ci		/* option not supported by SMC */
22268c2ecf20Sopenharmony_ci		if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
22278c2ecf20Sopenharmony_ci			smc_switch_to_fallback(smc);
22288c2ecf20Sopenharmony_ci			smc->fallback_rsn = SMC_CLC_DECL_OPTUNSUPP;
22298c2ecf20Sopenharmony_ci		} else {
22308c2ecf20Sopenharmony_ci			rc = -EINVAL;
22318c2ecf20Sopenharmony_ci		}
22328c2ecf20Sopenharmony_ci		break;
22338c2ecf20Sopenharmony_ci	case TCP_NODELAY:
22348c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_INIT &&
22358c2ecf20Sopenharmony_ci		    sk->sk_state != SMC_LISTEN &&
22368c2ecf20Sopenharmony_ci		    sk->sk_state != SMC_CLOSED) {
22378c2ecf20Sopenharmony_ci			if (val)
22388c2ecf20Sopenharmony_ci				mod_delayed_work(smc->conn.lgr->tx_wq,
22398c2ecf20Sopenharmony_ci						 &smc->conn.tx_work, 0);
22408c2ecf20Sopenharmony_ci		}
22418c2ecf20Sopenharmony_ci		break;
22428c2ecf20Sopenharmony_ci	case TCP_CORK:
22438c2ecf20Sopenharmony_ci		if (sk->sk_state != SMC_INIT &&
22448c2ecf20Sopenharmony_ci		    sk->sk_state != SMC_LISTEN &&
22458c2ecf20Sopenharmony_ci		    sk->sk_state != SMC_CLOSED) {
22468c2ecf20Sopenharmony_ci			if (!val)
22478c2ecf20Sopenharmony_ci				mod_delayed_work(smc->conn.lgr->tx_wq,
22488c2ecf20Sopenharmony_ci						 &smc->conn.tx_work, 0);
22498c2ecf20Sopenharmony_ci		}
22508c2ecf20Sopenharmony_ci		break;
22518c2ecf20Sopenharmony_ci	case TCP_DEFER_ACCEPT:
22528c2ecf20Sopenharmony_ci		smc->sockopt_defer_accept = val;
22538c2ecf20Sopenharmony_ci		break;
22548c2ecf20Sopenharmony_ci	default:
22558c2ecf20Sopenharmony_ci		break;
22568c2ecf20Sopenharmony_ci	}
22578c2ecf20Sopenharmony_ciout:
22588c2ecf20Sopenharmony_ci	release_sock(sk);
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci	return rc;
22618c2ecf20Sopenharmony_ci}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_cistatic int smc_getsockopt(struct socket *sock, int level, int optname,
22648c2ecf20Sopenharmony_ci			  char __user *optval, int __user *optlen)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	struct smc_sock *smc;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	smc = smc_sk(sock->sk);
22698c2ecf20Sopenharmony_ci	/* socket options apply to the CLC socket */
22708c2ecf20Sopenharmony_ci	if (unlikely(!smc->clcsock->ops->getsockopt))
22718c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
22728c2ecf20Sopenharmony_ci	return smc->clcsock->ops->getsockopt(smc->clcsock, level, optname,
22738c2ecf20Sopenharmony_ci					     optval, optlen);
22748c2ecf20Sopenharmony_ci}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_cistatic int smc_ioctl(struct socket *sock, unsigned int cmd,
22778c2ecf20Sopenharmony_ci		     unsigned long arg)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	union smc_host_cursor cons, urg;
22808c2ecf20Sopenharmony_ci	struct smc_connection *conn;
22818c2ecf20Sopenharmony_ci	struct smc_sock *smc;
22828c2ecf20Sopenharmony_ci	int answ;
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	smc = smc_sk(sock->sk);
22858c2ecf20Sopenharmony_ci	conn = &smc->conn;
22868c2ecf20Sopenharmony_ci	lock_sock(&smc->sk);
22878c2ecf20Sopenharmony_ci	if (smc->use_fallback) {
22888c2ecf20Sopenharmony_ci		if (!smc->clcsock) {
22898c2ecf20Sopenharmony_ci			release_sock(&smc->sk);
22908c2ecf20Sopenharmony_ci			return -EBADF;
22918c2ecf20Sopenharmony_ci		}
22928c2ecf20Sopenharmony_ci		answ = smc->clcsock->ops->ioctl(smc->clcsock, cmd, arg);
22938c2ecf20Sopenharmony_ci		release_sock(&smc->sk);
22948c2ecf20Sopenharmony_ci		return answ;
22958c2ecf20Sopenharmony_ci	}
22968c2ecf20Sopenharmony_ci	switch (cmd) {
22978c2ecf20Sopenharmony_ci	case SIOCINQ: /* same as FIONREAD */
22988c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_LISTEN) {
22998c2ecf20Sopenharmony_ci			release_sock(&smc->sk);
23008c2ecf20Sopenharmony_ci			return -EINVAL;
23018c2ecf20Sopenharmony_ci		}
23028c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_INIT ||
23038c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_CLOSED)
23048c2ecf20Sopenharmony_ci			answ = 0;
23058c2ecf20Sopenharmony_ci		else
23068c2ecf20Sopenharmony_ci			answ = atomic_read(&smc->conn.bytes_to_rcv);
23078c2ecf20Sopenharmony_ci		break;
23088c2ecf20Sopenharmony_ci	case SIOCOUTQ:
23098c2ecf20Sopenharmony_ci		/* output queue size (not send + not acked) */
23108c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_LISTEN) {
23118c2ecf20Sopenharmony_ci			release_sock(&smc->sk);
23128c2ecf20Sopenharmony_ci			return -EINVAL;
23138c2ecf20Sopenharmony_ci		}
23148c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_INIT ||
23158c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_CLOSED)
23168c2ecf20Sopenharmony_ci			answ = 0;
23178c2ecf20Sopenharmony_ci		else
23188c2ecf20Sopenharmony_ci			answ = smc->conn.sndbuf_desc->len -
23198c2ecf20Sopenharmony_ci					atomic_read(&smc->conn.sndbuf_space);
23208c2ecf20Sopenharmony_ci		break;
23218c2ecf20Sopenharmony_ci	case SIOCOUTQNSD:
23228c2ecf20Sopenharmony_ci		/* output queue size (not send only) */
23238c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_LISTEN) {
23248c2ecf20Sopenharmony_ci			release_sock(&smc->sk);
23258c2ecf20Sopenharmony_ci			return -EINVAL;
23268c2ecf20Sopenharmony_ci		}
23278c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_INIT ||
23288c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_CLOSED)
23298c2ecf20Sopenharmony_ci			answ = 0;
23308c2ecf20Sopenharmony_ci		else
23318c2ecf20Sopenharmony_ci			answ = smc_tx_prepared_sends(&smc->conn);
23328c2ecf20Sopenharmony_ci		break;
23338c2ecf20Sopenharmony_ci	case SIOCATMARK:
23348c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_LISTEN) {
23358c2ecf20Sopenharmony_ci			release_sock(&smc->sk);
23368c2ecf20Sopenharmony_ci			return -EINVAL;
23378c2ecf20Sopenharmony_ci		}
23388c2ecf20Sopenharmony_ci		if (smc->sk.sk_state == SMC_INIT ||
23398c2ecf20Sopenharmony_ci		    smc->sk.sk_state == SMC_CLOSED) {
23408c2ecf20Sopenharmony_ci			answ = 0;
23418c2ecf20Sopenharmony_ci		} else {
23428c2ecf20Sopenharmony_ci			smc_curs_copy(&cons, &conn->local_tx_ctrl.cons, conn);
23438c2ecf20Sopenharmony_ci			smc_curs_copy(&urg, &conn->urg_curs, conn);
23448c2ecf20Sopenharmony_ci			answ = smc_curs_diff(conn->rmb_desc->len,
23458c2ecf20Sopenharmony_ci					     &cons, &urg) == 1;
23468c2ecf20Sopenharmony_ci		}
23478c2ecf20Sopenharmony_ci		break;
23488c2ecf20Sopenharmony_ci	default:
23498c2ecf20Sopenharmony_ci		release_sock(&smc->sk);
23508c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
23518c2ecf20Sopenharmony_ci	}
23528c2ecf20Sopenharmony_ci	release_sock(&smc->sk);
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	return put_user(answ, (int __user *)arg);
23558c2ecf20Sopenharmony_ci}
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_cistatic ssize_t smc_sendpage(struct socket *sock, struct page *page,
23588c2ecf20Sopenharmony_ci			    int offset, size_t size, int flags)
23598c2ecf20Sopenharmony_ci{
23608c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
23618c2ecf20Sopenharmony_ci	struct smc_sock *smc;
23628c2ecf20Sopenharmony_ci	int rc = -EPIPE;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
23658c2ecf20Sopenharmony_ci	lock_sock(sk);
23668c2ecf20Sopenharmony_ci	if (sk->sk_state != SMC_ACTIVE) {
23678c2ecf20Sopenharmony_ci		release_sock(sk);
23688c2ecf20Sopenharmony_ci		goto out;
23698c2ecf20Sopenharmony_ci	}
23708c2ecf20Sopenharmony_ci	release_sock(sk);
23718c2ecf20Sopenharmony_ci	if (smc->use_fallback)
23728c2ecf20Sopenharmony_ci		rc = kernel_sendpage(smc->clcsock, page, offset,
23738c2ecf20Sopenharmony_ci				     size, flags);
23748c2ecf20Sopenharmony_ci	else
23758c2ecf20Sopenharmony_ci		rc = sock_no_sendpage(sock, page, offset, size, flags);
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ciout:
23788c2ecf20Sopenharmony_ci	return rc;
23798c2ecf20Sopenharmony_ci}
23808c2ecf20Sopenharmony_ci
23818c2ecf20Sopenharmony_ci/* Map the affected portions of the rmbe into an spd, note the number of bytes
23828c2ecf20Sopenharmony_ci * to splice in conn->splice_pending, and press 'go'. Delays consumer cursor
23838c2ecf20Sopenharmony_ci * updates till whenever a respective page has been fully processed.
23848c2ecf20Sopenharmony_ci * Note that subsequent recv() calls have to wait till all splice() processing
23858c2ecf20Sopenharmony_ci * completed.
23868c2ecf20Sopenharmony_ci */
23878c2ecf20Sopenharmony_cistatic ssize_t smc_splice_read(struct socket *sock, loff_t *ppos,
23888c2ecf20Sopenharmony_ci			       struct pipe_inode_info *pipe, size_t len,
23898c2ecf20Sopenharmony_ci			       unsigned int flags)
23908c2ecf20Sopenharmony_ci{
23918c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
23928c2ecf20Sopenharmony_ci	struct smc_sock *smc;
23938c2ecf20Sopenharmony_ci	int rc = -ENOTCONN;
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
23968c2ecf20Sopenharmony_ci	lock_sock(sk);
23978c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) {
23988c2ecf20Sopenharmony_ci		/* socket was connected before, no more data to read */
23998c2ecf20Sopenharmony_ci		rc = 0;
24008c2ecf20Sopenharmony_ci		goto out;
24018c2ecf20Sopenharmony_ci	}
24028c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_INIT ||
24038c2ecf20Sopenharmony_ci	    sk->sk_state == SMC_LISTEN ||
24048c2ecf20Sopenharmony_ci	    sk->sk_state == SMC_CLOSED)
24058c2ecf20Sopenharmony_ci		goto out;
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	if (sk->sk_state == SMC_PEERFINCLOSEWAIT) {
24088c2ecf20Sopenharmony_ci		rc = 0;
24098c2ecf20Sopenharmony_ci		goto out;
24108c2ecf20Sopenharmony_ci	}
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	if (smc->use_fallback) {
24138c2ecf20Sopenharmony_ci		rc = smc->clcsock->ops->splice_read(smc->clcsock, ppos,
24148c2ecf20Sopenharmony_ci						    pipe, len, flags);
24158c2ecf20Sopenharmony_ci	} else {
24168c2ecf20Sopenharmony_ci		if (*ppos) {
24178c2ecf20Sopenharmony_ci			rc = -ESPIPE;
24188c2ecf20Sopenharmony_ci			goto out;
24198c2ecf20Sopenharmony_ci		}
24208c2ecf20Sopenharmony_ci		if (flags & SPLICE_F_NONBLOCK)
24218c2ecf20Sopenharmony_ci			flags = MSG_DONTWAIT;
24228c2ecf20Sopenharmony_ci		else
24238c2ecf20Sopenharmony_ci			flags = 0;
24248c2ecf20Sopenharmony_ci		rc = smc_rx_recvmsg(smc, NULL, pipe, len, flags);
24258c2ecf20Sopenharmony_ci	}
24268c2ecf20Sopenharmony_ciout:
24278c2ecf20Sopenharmony_ci	release_sock(sk);
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	return rc;
24308c2ecf20Sopenharmony_ci}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci/* must look like tcp */
24338c2ecf20Sopenharmony_cistatic const struct proto_ops smc_sock_ops = {
24348c2ecf20Sopenharmony_ci	.family		= PF_SMC,
24358c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
24368c2ecf20Sopenharmony_ci	.release	= smc_release,
24378c2ecf20Sopenharmony_ci	.bind		= smc_bind,
24388c2ecf20Sopenharmony_ci	.connect	= smc_connect,
24398c2ecf20Sopenharmony_ci	.socketpair	= sock_no_socketpair,
24408c2ecf20Sopenharmony_ci	.accept		= smc_accept,
24418c2ecf20Sopenharmony_ci	.getname	= smc_getname,
24428c2ecf20Sopenharmony_ci	.poll		= smc_poll,
24438c2ecf20Sopenharmony_ci	.ioctl		= smc_ioctl,
24448c2ecf20Sopenharmony_ci	.listen		= smc_listen,
24458c2ecf20Sopenharmony_ci	.shutdown	= smc_shutdown,
24468c2ecf20Sopenharmony_ci	.setsockopt	= smc_setsockopt,
24478c2ecf20Sopenharmony_ci	.getsockopt	= smc_getsockopt,
24488c2ecf20Sopenharmony_ci	.sendmsg	= smc_sendmsg,
24498c2ecf20Sopenharmony_ci	.recvmsg	= smc_recvmsg,
24508c2ecf20Sopenharmony_ci	.mmap		= sock_no_mmap,
24518c2ecf20Sopenharmony_ci	.sendpage	= smc_sendpage,
24528c2ecf20Sopenharmony_ci	.splice_read	= smc_splice_read,
24538c2ecf20Sopenharmony_ci};
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_cistatic int smc_create(struct net *net, struct socket *sock, int protocol,
24568c2ecf20Sopenharmony_ci		      int kern)
24578c2ecf20Sopenharmony_ci{
24588c2ecf20Sopenharmony_ci	int family = (protocol == SMCPROTO_SMC6) ? PF_INET6 : PF_INET;
24598c2ecf20Sopenharmony_ci	struct smc_sock *smc;
24608c2ecf20Sopenharmony_ci	struct sock *sk;
24618c2ecf20Sopenharmony_ci	int rc;
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	rc = -ESOCKTNOSUPPORT;
24648c2ecf20Sopenharmony_ci	if (sock->type != SOCK_STREAM)
24658c2ecf20Sopenharmony_ci		goto out;
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	rc = -EPROTONOSUPPORT;
24688c2ecf20Sopenharmony_ci	if (protocol != SMCPROTO_SMC && protocol != SMCPROTO_SMC6)
24698c2ecf20Sopenharmony_ci		goto out;
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	rc = -ENOBUFS;
24728c2ecf20Sopenharmony_ci	sock->ops = &smc_sock_ops;
24738c2ecf20Sopenharmony_ci	sk = smc_sock_alloc(net, sock, protocol);
24748c2ecf20Sopenharmony_ci	if (!sk)
24758c2ecf20Sopenharmony_ci		goto out;
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci	/* create internal TCP socket for CLC handshake and fallback */
24788c2ecf20Sopenharmony_ci	smc = smc_sk(sk);
24798c2ecf20Sopenharmony_ci	smc->use_fallback = false; /* assume rdma capability first */
24808c2ecf20Sopenharmony_ci	smc->fallback_rsn = 0;
24818c2ecf20Sopenharmony_ci	rc = sock_create_kern(net, family, SOCK_STREAM, IPPROTO_TCP,
24828c2ecf20Sopenharmony_ci			      &smc->clcsock);
24838c2ecf20Sopenharmony_ci	if (rc) {
24848c2ecf20Sopenharmony_ci		sk_common_release(sk);
24858c2ecf20Sopenharmony_ci		goto out;
24868c2ecf20Sopenharmony_ci	}
24878c2ecf20Sopenharmony_ci	smc->sk.sk_sndbuf = max(smc->clcsock->sk->sk_sndbuf, SMC_BUF_MIN_SIZE);
24888c2ecf20Sopenharmony_ci	smc->sk.sk_rcvbuf = max(smc->clcsock->sk->sk_rcvbuf, SMC_BUF_MIN_SIZE);
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ciout:
24918c2ecf20Sopenharmony_ci	return rc;
24928c2ecf20Sopenharmony_ci}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_cistatic const struct net_proto_family smc_sock_family_ops = {
24958c2ecf20Sopenharmony_ci	.family	= PF_SMC,
24968c2ecf20Sopenharmony_ci	.owner	= THIS_MODULE,
24978c2ecf20Sopenharmony_ci	.create	= smc_create,
24988c2ecf20Sopenharmony_ci};
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_ciunsigned int smc_net_id;
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_cistatic __net_init int smc_net_init(struct net *net)
25038c2ecf20Sopenharmony_ci{
25048c2ecf20Sopenharmony_ci	return smc_pnet_net_init(net);
25058c2ecf20Sopenharmony_ci}
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_cistatic void __net_exit smc_net_exit(struct net *net)
25088c2ecf20Sopenharmony_ci{
25098c2ecf20Sopenharmony_ci	smc_pnet_net_exit(net);
25108c2ecf20Sopenharmony_ci}
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_cistatic struct pernet_operations smc_net_ops = {
25138c2ecf20Sopenharmony_ci	.init = smc_net_init,
25148c2ecf20Sopenharmony_ci	.exit = smc_net_exit,
25158c2ecf20Sopenharmony_ci	.id   = &smc_net_id,
25168c2ecf20Sopenharmony_ci	.size = sizeof(struct smc_net),
25178c2ecf20Sopenharmony_ci};
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_cistatic int __init smc_init(void)
25208c2ecf20Sopenharmony_ci{
25218c2ecf20Sopenharmony_ci	int rc;
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	rc = register_pernet_subsys(&smc_net_ops);
25248c2ecf20Sopenharmony_ci	if (rc)
25258c2ecf20Sopenharmony_ci		return rc;
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci	smc_ism_init();
25288c2ecf20Sopenharmony_ci	smc_clc_init();
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_ci	rc = smc_pnet_init();
25318c2ecf20Sopenharmony_ci	if (rc)
25328c2ecf20Sopenharmony_ci		goto out_pernet_subsys;
25338c2ecf20Sopenharmony_ci
25348c2ecf20Sopenharmony_ci	rc = -ENOMEM;
25358c2ecf20Sopenharmony_ci	smc_hs_wq = alloc_workqueue("smc_hs_wq", 0, 0);
25368c2ecf20Sopenharmony_ci	if (!smc_hs_wq)
25378c2ecf20Sopenharmony_ci		goto out_pnet;
25388c2ecf20Sopenharmony_ci
25398c2ecf20Sopenharmony_ci	smc_close_wq = alloc_workqueue("smc_close_wq", 0, 0);
25408c2ecf20Sopenharmony_ci	if (!smc_close_wq)
25418c2ecf20Sopenharmony_ci		goto out_alloc_hs_wq;
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	rc = smc_core_init();
25448c2ecf20Sopenharmony_ci	if (rc) {
25458c2ecf20Sopenharmony_ci		pr_err("%s: smc_core_init fails with %d\n", __func__, rc);
25468c2ecf20Sopenharmony_ci		goto out_alloc_wqs;
25478c2ecf20Sopenharmony_ci	}
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci	rc = smc_llc_init();
25508c2ecf20Sopenharmony_ci	if (rc) {
25518c2ecf20Sopenharmony_ci		pr_err("%s: smc_llc_init fails with %d\n", __func__, rc);
25528c2ecf20Sopenharmony_ci		goto out_core;
25538c2ecf20Sopenharmony_ci	}
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_ci	rc = smc_cdc_init();
25568c2ecf20Sopenharmony_ci	if (rc) {
25578c2ecf20Sopenharmony_ci		pr_err("%s: smc_cdc_init fails with %d\n", __func__, rc);
25588c2ecf20Sopenharmony_ci		goto out_core;
25598c2ecf20Sopenharmony_ci	}
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	rc = proto_register(&smc_proto, 1);
25628c2ecf20Sopenharmony_ci	if (rc) {
25638c2ecf20Sopenharmony_ci		pr_err("%s: proto_register(v4) fails with %d\n", __func__, rc);
25648c2ecf20Sopenharmony_ci		goto out_core;
25658c2ecf20Sopenharmony_ci	}
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_ci	rc = proto_register(&smc_proto6, 1);
25688c2ecf20Sopenharmony_ci	if (rc) {
25698c2ecf20Sopenharmony_ci		pr_err("%s: proto_register(v6) fails with %d\n", __func__, rc);
25708c2ecf20Sopenharmony_ci		goto out_proto;
25718c2ecf20Sopenharmony_ci	}
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	rc = sock_register(&smc_sock_family_ops);
25748c2ecf20Sopenharmony_ci	if (rc) {
25758c2ecf20Sopenharmony_ci		pr_err("%s: sock_register fails with %d\n", __func__, rc);
25768c2ecf20Sopenharmony_ci		goto out_proto6;
25778c2ecf20Sopenharmony_ci	}
25788c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&smc_v4_hashinfo.ht);
25798c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&smc_v6_hashinfo.ht);
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	rc = smc_ib_register_client();
25828c2ecf20Sopenharmony_ci	if (rc) {
25838c2ecf20Sopenharmony_ci		pr_err("%s: ib_register fails with %d\n", __func__, rc);
25848c2ecf20Sopenharmony_ci		goto out_sock;
25858c2ecf20Sopenharmony_ci	}
25868c2ecf20Sopenharmony_ci
25878c2ecf20Sopenharmony_ci	static_branch_enable(&tcp_have_smc);
25888c2ecf20Sopenharmony_ci	return 0;
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ciout_sock:
25918c2ecf20Sopenharmony_ci	sock_unregister(PF_SMC);
25928c2ecf20Sopenharmony_ciout_proto6:
25938c2ecf20Sopenharmony_ci	proto_unregister(&smc_proto6);
25948c2ecf20Sopenharmony_ciout_proto:
25958c2ecf20Sopenharmony_ci	proto_unregister(&smc_proto);
25968c2ecf20Sopenharmony_ciout_core:
25978c2ecf20Sopenharmony_ci	smc_core_exit();
25988c2ecf20Sopenharmony_ciout_alloc_wqs:
25998c2ecf20Sopenharmony_ci	destroy_workqueue(smc_close_wq);
26008c2ecf20Sopenharmony_ciout_alloc_hs_wq:
26018c2ecf20Sopenharmony_ci	destroy_workqueue(smc_hs_wq);
26028c2ecf20Sopenharmony_ciout_pnet:
26038c2ecf20Sopenharmony_ci	smc_pnet_exit();
26048c2ecf20Sopenharmony_ciout_pernet_subsys:
26058c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&smc_net_ops);
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci	return rc;
26088c2ecf20Sopenharmony_ci}
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_cistatic void __exit smc_exit(void)
26118c2ecf20Sopenharmony_ci{
26128c2ecf20Sopenharmony_ci	static_branch_disable(&tcp_have_smc);
26138c2ecf20Sopenharmony_ci	sock_unregister(PF_SMC);
26148c2ecf20Sopenharmony_ci	smc_core_exit();
26158c2ecf20Sopenharmony_ci	smc_ib_unregister_client();
26168c2ecf20Sopenharmony_ci	destroy_workqueue(smc_close_wq);
26178c2ecf20Sopenharmony_ci	destroy_workqueue(smc_hs_wq);
26188c2ecf20Sopenharmony_ci	proto_unregister(&smc_proto6);
26198c2ecf20Sopenharmony_ci	proto_unregister(&smc_proto);
26208c2ecf20Sopenharmony_ci	smc_pnet_exit();
26218c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&smc_net_ops);
26228c2ecf20Sopenharmony_ci	rcu_barrier();
26238c2ecf20Sopenharmony_ci}
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_cimodule_init(smc_init);
26268c2ecf20Sopenharmony_cimodule_exit(smc_exit);
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ursula Braun <ubraun@linux.vnet.ibm.com>");
26298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("smc socket address family");
26308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
26318c2ecf20Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_SMC);
2632