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