162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Shared Memory Communications over RDMA (SMC-R) and RoCE 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * AF_SMC protocol family socket handler keeping the AF_INET sock address type 662306a36Sopenharmony_ci * applies to SOCK_STREAM sockets only 762306a36Sopenharmony_ci * offers an alternative communication option for TCP-protocol sockets 862306a36Sopenharmony_ci * applicable with RoCE-cards only 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Initial restrictions: 1162306a36Sopenharmony_ci * - support for alternate links postponed 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Copyright IBM Corp. 2016, 2018 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Author(s): Ursula Braun <ubraun@linux.vnet.ibm.com> 1662306a36Sopenharmony_ci * based on prototype from Frank Blaschka 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define KMSG_COMPONENT "smc" 2062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/socket.h> 2462306a36Sopenharmony_ci#include <linux/workqueue.h> 2562306a36Sopenharmony_ci#include <linux/in.h> 2662306a36Sopenharmony_ci#include <linux/sched/signal.h> 2762306a36Sopenharmony_ci#include <linux/if_vlan.h> 2862306a36Sopenharmony_ci#include <linux/rcupdate_wait.h> 2962306a36Sopenharmony_ci#include <linux/ctype.h> 3062306a36Sopenharmony_ci#include <linux/splice.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <net/sock.h> 3362306a36Sopenharmony_ci#include <net/tcp.h> 3462306a36Sopenharmony_ci#include <net/smc.h> 3562306a36Sopenharmony_ci#include <asm/ioctls.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <net/net_namespace.h> 3862306a36Sopenharmony_ci#include <net/netns/generic.h> 3962306a36Sopenharmony_ci#include "smc_netns.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "smc.h" 4262306a36Sopenharmony_ci#include "smc_clc.h" 4362306a36Sopenharmony_ci#include "smc_llc.h" 4462306a36Sopenharmony_ci#include "smc_cdc.h" 4562306a36Sopenharmony_ci#include "smc_core.h" 4662306a36Sopenharmony_ci#include "smc_ib.h" 4762306a36Sopenharmony_ci#include "smc_ism.h" 4862306a36Sopenharmony_ci#include "smc_pnet.h" 4962306a36Sopenharmony_ci#include "smc_netlink.h" 5062306a36Sopenharmony_ci#include "smc_tx.h" 5162306a36Sopenharmony_ci#include "smc_rx.h" 5262306a36Sopenharmony_ci#include "smc_close.h" 5362306a36Sopenharmony_ci#include "smc_stats.h" 5462306a36Sopenharmony_ci#include "smc_tracepoint.h" 5562306a36Sopenharmony_ci#include "smc_sysctl.h" 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic DEFINE_MUTEX(smc_server_lgr_pending); /* serialize link group 5862306a36Sopenharmony_ci * creation on server 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistatic DEFINE_MUTEX(smc_client_lgr_pending); /* serialize link group 6162306a36Sopenharmony_ci * creation on client 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct workqueue_struct *smc_tcp_ls_wq; /* wq for tcp listen work */ 6562306a36Sopenharmony_cistruct workqueue_struct *smc_hs_wq; /* wq for handshake work */ 6662306a36Sopenharmony_cistruct workqueue_struct *smc_close_wq; /* wq for close work */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void smc_tcp_listen_work(struct work_struct *); 6962306a36Sopenharmony_cistatic void smc_connect_work(struct work_struct *); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint smc_nl_dump_hs_limitation(struct sk_buff *skb, struct netlink_callback *cb) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct smc_nl_dmp_ctx *cb_ctx = smc_nl_dmp_ctx(cb); 7462306a36Sopenharmony_ci void *hdr; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (cb_ctx->pos[0]) 7762306a36Sopenharmony_ci goto out; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 8062306a36Sopenharmony_ci &smc_gen_nl_family, NLM_F_MULTI, 8162306a36Sopenharmony_ci SMC_NETLINK_DUMP_HS_LIMITATION); 8262306a36Sopenharmony_ci if (!hdr) 8362306a36Sopenharmony_ci return -ENOMEM; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (nla_put_u8(skb, SMC_NLA_HS_LIMITATION_ENABLED, 8662306a36Sopenharmony_ci sock_net(skb->sk)->smc.limit_smc_hs)) 8762306a36Sopenharmony_ci goto err; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci genlmsg_end(skb, hdr); 9062306a36Sopenharmony_ci cb_ctx->pos[0] = 1; 9162306a36Sopenharmony_ciout: 9262306a36Sopenharmony_ci return skb->len; 9362306a36Sopenharmony_cierr: 9462306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 9562306a36Sopenharmony_ci return -EMSGSIZE; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint smc_nl_enable_hs_limitation(struct sk_buff *skb, struct genl_info *info) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci sock_net(skb->sk)->smc.limit_smc_hs = true; 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint smc_nl_disable_hs_limitation(struct sk_buff *skb, struct genl_info *info) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci sock_net(skb->sk)->smc.limit_smc_hs = false; 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void smc_set_keepalive(struct sock *sk, int val) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct smc_sock *smc = smc_sk(sk); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci smc->clcsock->sk->sk_prot->keepalive(smc->clcsock->sk, val); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct sock *smc_tcp_syn_recv_sock(const struct sock *sk, 11862306a36Sopenharmony_ci struct sk_buff *skb, 11962306a36Sopenharmony_ci struct request_sock *req, 12062306a36Sopenharmony_ci struct dst_entry *dst, 12162306a36Sopenharmony_ci struct request_sock *req_unhash, 12262306a36Sopenharmony_ci bool *own_req) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct smc_sock *smc; 12562306a36Sopenharmony_ci struct sock *child; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci smc = smc_clcsock_user_data(sk); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (READ_ONCE(sk->sk_ack_backlog) + atomic_read(&smc->queued_smc_hs) > 13062306a36Sopenharmony_ci sk->sk_max_ack_backlog) 13162306a36Sopenharmony_ci goto drop; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (sk_acceptq_is_full(&smc->sk)) { 13462306a36Sopenharmony_ci NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); 13562306a36Sopenharmony_ci goto drop; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* passthrough to original syn recv sock fct */ 13962306a36Sopenharmony_ci child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash, 14062306a36Sopenharmony_ci own_req); 14162306a36Sopenharmony_ci /* child must not inherit smc or its ops */ 14262306a36Sopenharmony_ci if (child) { 14362306a36Sopenharmony_ci rcu_assign_sk_user_data(child, NULL); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* v4-mapped sockets don't inherit parent ops. Don't restore. */ 14662306a36Sopenharmony_ci if (inet_csk(child)->icsk_af_ops == inet_csk(sk)->icsk_af_ops) 14762306a36Sopenharmony_ci inet_csk(child)->icsk_af_ops = smc->ori_af_ops; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci return child; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cidrop: 15262306a36Sopenharmony_ci dst_release(dst); 15362306a36Sopenharmony_ci tcp_listendrop(sk); 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic bool smc_hs_congested(const struct sock *sk) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci const struct smc_sock *smc; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci smc = smc_clcsock_user_data(sk); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (!smc) 16462306a36Sopenharmony_ci return true; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (workqueue_congested(WORK_CPU_UNBOUND, smc_hs_wq)) 16762306a36Sopenharmony_ci return true; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return false; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic struct smc_hashinfo smc_v4_hashinfo = { 17362306a36Sopenharmony_ci .lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock), 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct smc_hashinfo smc_v6_hashinfo = { 17762306a36Sopenharmony_ci .lock = __RW_LOCK_UNLOCKED(smc_v6_hashinfo.lock), 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciint smc_hash_sk(struct sock *sk) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct smc_hashinfo *h = sk->sk_prot->h.smc_hash; 18362306a36Sopenharmony_ci struct hlist_head *head; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci head = &h->ht; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci write_lock_bh(&h->lock); 18862306a36Sopenharmony_ci sk_add_node(sk, head); 18962306a36Sopenharmony_ci write_unlock_bh(&h->lock); 19062306a36Sopenharmony_ci sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_hash_sk); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid smc_unhash_sk(struct sock *sk) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct smc_hashinfo *h = sk->sk_prot->h.smc_hash; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci write_lock_bh(&h->lock); 20162306a36Sopenharmony_ci if (sk_del_node_init(sk)) 20262306a36Sopenharmony_ci sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); 20362306a36Sopenharmony_ci write_unlock_bh(&h->lock); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_unhash_sk); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* This will be called before user really release sock_lock. So do the 20862306a36Sopenharmony_ci * work which we didn't do because of user hold the sock_lock in the 20962306a36Sopenharmony_ci * BH context 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic void smc_release_cb(struct sock *sk) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct smc_sock *smc = smc_sk(sk); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (smc->conn.tx_in_release_sock) { 21662306a36Sopenharmony_ci smc_tx_pending(&smc->conn); 21762306a36Sopenharmony_ci smc->conn.tx_in_release_sock = false; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistruct proto smc_proto = { 22262306a36Sopenharmony_ci .name = "SMC", 22362306a36Sopenharmony_ci .owner = THIS_MODULE, 22462306a36Sopenharmony_ci .keepalive = smc_set_keepalive, 22562306a36Sopenharmony_ci .hash = smc_hash_sk, 22662306a36Sopenharmony_ci .unhash = smc_unhash_sk, 22762306a36Sopenharmony_ci .release_cb = smc_release_cb, 22862306a36Sopenharmony_ci .obj_size = sizeof(struct smc_sock), 22962306a36Sopenharmony_ci .h.smc_hash = &smc_v4_hashinfo, 23062306a36Sopenharmony_ci .slab_flags = SLAB_TYPESAFE_BY_RCU, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_proto); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistruct proto smc_proto6 = { 23562306a36Sopenharmony_ci .name = "SMC6", 23662306a36Sopenharmony_ci .owner = THIS_MODULE, 23762306a36Sopenharmony_ci .keepalive = smc_set_keepalive, 23862306a36Sopenharmony_ci .hash = smc_hash_sk, 23962306a36Sopenharmony_ci .unhash = smc_unhash_sk, 24062306a36Sopenharmony_ci .release_cb = smc_release_cb, 24162306a36Sopenharmony_ci .obj_size = sizeof(struct smc_sock), 24262306a36Sopenharmony_ci .h.smc_hash = &smc_v6_hashinfo, 24362306a36Sopenharmony_ci .slab_flags = SLAB_TYPESAFE_BY_RCU, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(smc_proto6); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic void smc_fback_restore_callbacks(struct smc_sock *smc) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct sock *clcsk = smc->clcsock->sk; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci write_lock_bh(&clcsk->sk_callback_lock); 25262306a36Sopenharmony_ci clcsk->sk_user_data = NULL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci smc_clcsock_restore_cb(&clcsk->sk_state_change, &smc->clcsk_state_change); 25562306a36Sopenharmony_ci smc_clcsock_restore_cb(&clcsk->sk_data_ready, &smc->clcsk_data_ready); 25662306a36Sopenharmony_ci smc_clcsock_restore_cb(&clcsk->sk_write_space, &smc->clcsk_write_space); 25762306a36Sopenharmony_ci smc_clcsock_restore_cb(&clcsk->sk_error_report, &smc->clcsk_error_report); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci write_unlock_bh(&clcsk->sk_callback_lock); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void smc_restore_fallback_changes(struct smc_sock *smc) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci if (smc->clcsock->file) { /* non-accepted sockets have no file yet */ 26562306a36Sopenharmony_ci smc->clcsock->file->private_data = smc->sk.sk_socket; 26662306a36Sopenharmony_ci smc->clcsock->file = NULL; 26762306a36Sopenharmony_ci smc_fback_restore_callbacks(smc); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int __smc_release(struct smc_sock *smc) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct sock *sk = &smc->sk; 27462306a36Sopenharmony_ci int rc = 0; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!smc->use_fallback) { 27762306a36Sopenharmony_ci rc = smc_close_active(smc); 27862306a36Sopenharmony_ci smc_sock_set_flag(sk, SOCK_DEAD); 27962306a36Sopenharmony_ci sk->sk_shutdown |= SHUTDOWN_MASK; 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci if (sk->sk_state != SMC_CLOSED) { 28262306a36Sopenharmony_ci if (sk->sk_state != SMC_LISTEN && 28362306a36Sopenharmony_ci sk->sk_state != SMC_INIT) 28462306a36Sopenharmony_ci sock_put(sk); /* passive closing */ 28562306a36Sopenharmony_ci if (sk->sk_state == SMC_LISTEN) { 28662306a36Sopenharmony_ci /* wake up clcsock accept */ 28762306a36Sopenharmony_ci rc = kernel_sock_shutdown(smc->clcsock, 28862306a36Sopenharmony_ci SHUT_RDWR); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci sk->sk_state = SMC_CLOSED; 29162306a36Sopenharmony_ci sk->sk_state_change(sk); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci smc_restore_fallback_changes(smc); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci sk->sk_prot->unhash(sk); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (sk->sk_state == SMC_CLOSED) { 29962306a36Sopenharmony_ci if (smc->clcsock) { 30062306a36Sopenharmony_ci release_sock(sk); 30162306a36Sopenharmony_ci smc_clcsock_release(smc); 30262306a36Sopenharmony_ci lock_sock(sk); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci if (!smc->use_fallback) 30562306a36Sopenharmony_ci smc_conn_free(&smc->conn); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return rc; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int smc_release(struct socket *sock) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct sock *sk = sock->sk; 31462306a36Sopenharmony_ci struct smc_sock *smc; 31562306a36Sopenharmony_ci int old_state, rc = 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!sk) 31862306a36Sopenharmony_ci goto out; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci sock_hold(sk); /* sock_put below */ 32162306a36Sopenharmony_ci smc = smc_sk(sk); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci old_state = sk->sk_state; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* cleanup for a dangling non-blocking connect */ 32662306a36Sopenharmony_ci if (smc->connect_nonblock && old_state == SMC_INIT) 32762306a36Sopenharmony_ci tcp_abort(smc->clcsock->sk, ECONNABORTED); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (cancel_work_sync(&smc->connect_work)) 33062306a36Sopenharmony_ci sock_put(&smc->sk); /* sock_hold in smc_connect for passive closing */ 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (sk->sk_state == SMC_LISTEN) 33362306a36Sopenharmony_ci /* smc_close_non_accepted() is called and acquires 33462306a36Sopenharmony_ci * sock lock for child sockets again 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci lock_sock_nested(sk, SINGLE_DEPTH_NESTING); 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci lock_sock(sk); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (old_state == SMC_INIT && sk->sk_state == SMC_ACTIVE && 34162306a36Sopenharmony_ci !smc->use_fallback) 34262306a36Sopenharmony_ci smc_close_active_abort(smc); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci rc = __smc_release(smc); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* detach socket */ 34762306a36Sopenharmony_ci sock_orphan(sk); 34862306a36Sopenharmony_ci sock->sk = NULL; 34962306a36Sopenharmony_ci release_sock(sk); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci sock_put(sk); /* sock_hold above */ 35262306a36Sopenharmony_ci sock_put(sk); /* final sock_put */ 35362306a36Sopenharmony_ciout: 35462306a36Sopenharmony_ci return rc; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void smc_destruct(struct sock *sk) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci if (sk->sk_state != SMC_CLOSED) 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci if (!sock_flag(sk, SOCK_DEAD)) 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic struct sock *smc_sock_alloc(struct net *net, struct socket *sock, 36662306a36Sopenharmony_ci int protocol) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct smc_sock *smc; 36962306a36Sopenharmony_ci struct proto *prot; 37062306a36Sopenharmony_ci struct sock *sk; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci prot = (protocol == SMCPROTO_SMC6) ? &smc_proto6 : &smc_proto; 37362306a36Sopenharmony_ci sk = sk_alloc(net, PF_SMC, GFP_KERNEL, prot, 0); 37462306a36Sopenharmony_ci if (!sk) 37562306a36Sopenharmony_ci return NULL; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci sock_init_data(sock, sk); /* sets sk_refcnt to 1 */ 37862306a36Sopenharmony_ci sk->sk_state = SMC_INIT; 37962306a36Sopenharmony_ci sk->sk_destruct = smc_destruct; 38062306a36Sopenharmony_ci sk->sk_protocol = protocol; 38162306a36Sopenharmony_ci WRITE_ONCE(sk->sk_sndbuf, 2 * READ_ONCE(net->smc.sysctl_wmem)); 38262306a36Sopenharmony_ci WRITE_ONCE(sk->sk_rcvbuf, 2 * READ_ONCE(net->smc.sysctl_rmem)); 38362306a36Sopenharmony_ci smc = smc_sk(sk); 38462306a36Sopenharmony_ci INIT_WORK(&smc->tcp_listen_work, smc_tcp_listen_work); 38562306a36Sopenharmony_ci INIT_WORK(&smc->connect_work, smc_connect_work); 38662306a36Sopenharmony_ci INIT_DELAYED_WORK(&smc->conn.tx_work, smc_tx_work); 38762306a36Sopenharmony_ci INIT_LIST_HEAD(&smc->accept_q); 38862306a36Sopenharmony_ci spin_lock_init(&smc->accept_q_lock); 38962306a36Sopenharmony_ci spin_lock_init(&smc->conn.send_lock); 39062306a36Sopenharmony_ci sk->sk_prot->hash(sk); 39162306a36Sopenharmony_ci mutex_init(&smc->clcsock_release_lock); 39262306a36Sopenharmony_ci smc_init_saved_callbacks(smc); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return sk; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int smc_bind(struct socket *sock, struct sockaddr *uaddr, 39862306a36Sopenharmony_ci int addr_len) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; 40162306a36Sopenharmony_ci struct sock *sk = sock->sk; 40262306a36Sopenharmony_ci struct smc_sock *smc; 40362306a36Sopenharmony_ci int rc; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci smc = smc_sk(sk); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* replicate tests from inet_bind(), to be safe wrt. future changes */ 40862306a36Sopenharmony_ci rc = -EINVAL; 40962306a36Sopenharmony_ci if (addr_len < sizeof(struct sockaddr_in)) 41062306a36Sopenharmony_ci goto out; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci rc = -EAFNOSUPPORT; 41362306a36Sopenharmony_ci if (addr->sin_family != AF_INET && 41462306a36Sopenharmony_ci addr->sin_family != AF_INET6 && 41562306a36Sopenharmony_ci addr->sin_family != AF_UNSPEC) 41662306a36Sopenharmony_ci goto out; 41762306a36Sopenharmony_ci /* accept AF_UNSPEC (mapped to AF_INET) only if s_addr is INADDR_ANY */ 41862306a36Sopenharmony_ci if (addr->sin_family == AF_UNSPEC && 41962306a36Sopenharmony_ci addr->sin_addr.s_addr != htonl(INADDR_ANY)) 42062306a36Sopenharmony_ci goto out; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci lock_sock(sk); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Check if socket is already active */ 42562306a36Sopenharmony_ci rc = -EINVAL; 42662306a36Sopenharmony_ci if (sk->sk_state != SMC_INIT || smc->connect_nonblock) 42762306a36Sopenharmony_ci goto out_rel; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci smc->clcsock->sk->sk_reuse = sk->sk_reuse; 43062306a36Sopenharmony_ci smc->clcsock->sk->sk_reuseport = sk->sk_reuseport; 43162306a36Sopenharmony_ci rc = kernel_bind(smc->clcsock, uaddr, addr_len); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciout_rel: 43462306a36Sopenharmony_ci release_sock(sk); 43562306a36Sopenharmony_ciout: 43662306a36Sopenharmony_ci return rc; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* copy only relevant settings and flags of SOL_SOCKET level from smc to 44062306a36Sopenharmony_ci * clc socket (since smc is not called for these options from net/core) 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci#define SK_FLAGS_SMC_TO_CLC ((1UL << SOCK_URGINLINE) | \ 44462306a36Sopenharmony_ci (1UL << SOCK_KEEPOPEN) | \ 44562306a36Sopenharmony_ci (1UL << SOCK_LINGER) | \ 44662306a36Sopenharmony_ci (1UL << SOCK_BROADCAST) | \ 44762306a36Sopenharmony_ci (1UL << SOCK_TIMESTAMP) | \ 44862306a36Sopenharmony_ci (1UL << SOCK_DBG) | \ 44962306a36Sopenharmony_ci (1UL << SOCK_RCVTSTAMP) | \ 45062306a36Sopenharmony_ci (1UL << SOCK_RCVTSTAMPNS) | \ 45162306a36Sopenharmony_ci (1UL << SOCK_LOCALROUTE) | \ 45262306a36Sopenharmony_ci (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE) | \ 45362306a36Sopenharmony_ci (1UL << SOCK_RXQ_OVFL) | \ 45462306a36Sopenharmony_ci (1UL << SOCK_WIFI_STATUS) | \ 45562306a36Sopenharmony_ci (1UL << SOCK_NOFCS) | \ 45662306a36Sopenharmony_ci (1UL << SOCK_FILTER_LOCKED) | \ 45762306a36Sopenharmony_ci (1UL << SOCK_TSTAMP_NEW)) 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* if set, use value set by setsockopt() - else use IPv4 or SMC sysctl value */ 46062306a36Sopenharmony_cistatic void smc_adjust_sock_bufsizes(struct sock *nsk, struct sock *osk, 46162306a36Sopenharmony_ci unsigned long mask) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct net *nnet = sock_net(nsk); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci nsk->sk_userlocks = osk->sk_userlocks; 46662306a36Sopenharmony_ci if (osk->sk_userlocks & SOCK_SNDBUF_LOCK) { 46762306a36Sopenharmony_ci nsk->sk_sndbuf = osk->sk_sndbuf; 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci if (mask == SK_FLAGS_SMC_TO_CLC) 47062306a36Sopenharmony_ci WRITE_ONCE(nsk->sk_sndbuf, 47162306a36Sopenharmony_ci READ_ONCE(nnet->ipv4.sysctl_tcp_wmem[1])); 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci WRITE_ONCE(nsk->sk_sndbuf, 47462306a36Sopenharmony_ci 2 * READ_ONCE(nnet->smc.sysctl_wmem)); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci if (osk->sk_userlocks & SOCK_RCVBUF_LOCK) { 47762306a36Sopenharmony_ci nsk->sk_rcvbuf = osk->sk_rcvbuf; 47862306a36Sopenharmony_ci } else { 47962306a36Sopenharmony_ci if (mask == SK_FLAGS_SMC_TO_CLC) 48062306a36Sopenharmony_ci WRITE_ONCE(nsk->sk_rcvbuf, 48162306a36Sopenharmony_ci READ_ONCE(nnet->ipv4.sysctl_tcp_rmem[1])); 48262306a36Sopenharmony_ci else 48362306a36Sopenharmony_ci WRITE_ONCE(nsk->sk_rcvbuf, 48462306a36Sopenharmony_ci 2 * READ_ONCE(nnet->smc.sysctl_rmem)); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, 48962306a36Sopenharmony_ci unsigned long mask) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci /* options we don't get control via setsockopt for */ 49262306a36Sopenharmony_ci nsk->sk_type = osk->sk_type; 49362306a36Sopenharmony_ci nsk->sk_sndtimeo = osk->sk_sndtimeo; 49462306a36Sopenharmony_ci nsk->sk_rcvtimeo = osk->sk_rcvtimeo; 49562306a36Sopenharmony_ci nsk->sk_mark = READ_ONCE(osk->sk_mark); 49662306a36Sopenharmony_ci nsk->sk_priority = osk->sk_priority; 49762306a36Sopenharmony_ci nsk->sk_rcvlowat = osk->sk_rcvlowat; 49862306a36Sopenharmony_ci nsk->sk_bound_dev_if = osk->sk_bound_dev_if; 49962306a36Sopenharmony_ci nsk->sk_err = osk->sk_err; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci nsk->sk_flags &= ~mask; 50262306a36Sopenharmony_ci nsk->sk_flags |= osk->sk_flags & mask; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci smc_adjust_sock_bufsizes(nsk, osk, mask); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic void smc_copy_sock_settings_to_clc(struct smc_sock *smc) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci smc_copy_sock_settings(smc->clcsock->sk, &smc->sk, SK_FLAGS_SMC_TO_CLC); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci#define SK_FLAGS_CLC_TO_SMC ((1UL << SOCK_URGINLINE) | \ 51362306a36Sopenharmony_ci (1UL << SOCK_KEEPOPEN) | \ 51462306a36Sopenharmony_ci (1UL << SOCK_LINGER) | \ 51562306a36Sopenharmony_ci (1UL << SOCK_DBG)) 51662306a36Sopenharmony_ci/* copy only settings and flags relevant for smc from clc to smc socket */ 51762306a36Sopenharmony_cistatic void smc_copy_sock_settings_to_smc(struct smc_sock *smc) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci smc_copy_sock_settings(&smc->sk, smc->clcsock->sk, SK_FLAGS_CLC_TO_SMC); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* register the new vzalloced sndbuf on all links */ 52362306a36Sopenharmony_cistatic int smcr_lgr_reg_sndbufs(struct smc_link *link, 52462306a36Sopenharmony_ci struct smc_buf_desc *snd_desc) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 52762306a36Sopenharmony_ci int i, rc = 0; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!snd_desc->is_vm) 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* protect against parallel smcr_link_reg_buf() */ 53362306a36Sopenharmony_ci down_write(&lgr->llc_conf_mutex); 53462306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 53562306a36Sopenharmony_ci if (!smc_link_active(&lgr->lnk[i])) 53662306a36Sopenharmony_ci continue; 53762306a36Sopenharmony_ci rc = smcr_link_reg_buf(&lgr->lnk[i], snd_desc); 53862306a36Sopenharmony_ci if (rc) 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci up_write(&lgr->llc_conf_mutex); 54262306a36Sopenharmony_ci return rc; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/* register the new rmb on all links */ 54662306a36Sopenharmony_cistatic int smcr_lgr_reg_rmbs(struct smc_link *link, 54762306a36Sopenharmony_ci struct smc_buf_desc *rmb_desc) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct smc_link_group *lgr = link->lgr; 55062306a36Sopenharmony_ci bool do_slow = false; 55162306a36Sopenharmony_ci int i, rc = 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY); 55462306a36Sopenharmony_ci if (rc) 55562306a36Sopenharmony_ci return rc; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci down_read(&lgr->llc_conf_mutex); 55862306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 55962306a36Sopenharmony_ci if (!smc_link_active(&lgr->lnk[i])) 56062306a36Sopenharmony_ci continue; 56162306a36Sopenharmony_ci if (!rmb_desc->is_reg_mr[link->link_idx]) { 56262306a36Sopenharmony_ci up_read(&lgr->llc_conf_mutex); 56362306a36Sopenharmony_ci goto slow_path; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci /* mr register already */ 56762306a36Sopenharmony_ci goto fast_path; 56862306a36Sopenharmony_cislow_path: 56962306a36Sopenharmony_ci do_slow = true; 57062306a36Sopenharmony_ci /* protect against parallel smc_llc_cli_rkey_exchange() and 57162306a36Sopenharmony_ci * parallel smcr_link_reg_buf() 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci down_write(&lgr->llc_conf_mutex); 57462306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 57562306a36Sopenharmony_ci if (!smc_link_active(&lgr->lnk[i])) 57662306a36Sopenharmony_ci continue; 57762306a36Sopenharmony_ci rc = smcr_link_reg_buf(&lgr->lnk[i], rmb_desc); 57862306a36Sopenharmony_ci if (rc) 57962306a36Sopenharmony_ci goto out; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_cifast_path: 58262306a36Sopenharmony_ci /* exchange confirm_rkey msg with peer */ 58362306a36Sopenharmony_ci rc = smc_llc_do_confirm_rkey(link, rmb_desc); 58462306a36Sopenharmony_ci if (rc) { 58562306a36Sopenharmony_ci rc = -EFAULT; 58662306a36Sopenharmony_ci goto out; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci rmb_desc->is_conf_rkey = true; 58962306a36Sopenharmony_ciout: 59062306a36Sopenharmony_ci do_slow ? up_write(&lgr->llc_conf_mutex) : up_read(&lgr->llc_conf_mutex); 59162306a36Sopenharmony_ci smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl); 59262306a36Sopenharmony_ci return rc; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int smcr_clnt_conf_first_link(struct smc_sock *smc) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct smc_link *link = smc->conn.lnk; 59862306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 59962306a36Sopenharmony_ci int rc; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Receive CONFIRM LINK request from server over RoCE fabric. 60262306a36Sopenharmony_ci * Increasing the client's timeout by twice as much as the server's 60362306a36Sopenharmony_ci * timeout by default can temporarily avoid decline messages of 60462306a36Sopenharmony_ci * both sides crossing or colliding 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci qentry = smc_llc_wait(link->lgr, NULL, 2 * SMC_LLC_WAIT_TIME, 60762306a36Sopenharmony_ci SMC_LLC_CONFIRM_LINK); 60862306a36Sopenharmony_ci if (!qentry) { 60962306a36Sopenharmony_ci struct smc_clc_msg_decline dclc; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc), 61262306a36Sopenharmony_ci SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT); 61362306a36Sopenharmony_ci return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci smc_llc_save_peer_uid(qentry); 61662306a36Sopenharmony_ci rc = smc_llc_eval_conf_link(qentry, SMC_LLC_REQ); 61762306a36Sopenharmony_ci smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl); 61862306a36Sopenharmony_ci if (rc) 61962306a36Sopenharmony_ci return SMC_CLC_DECL_RMBE_EC; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci rc = smc_ib_modify_qp_rts(link); 62262306a36Sopenharmony_ci if (rc) 62362306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_RDYLNK; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci smc_wr_remember_qp_attr(link); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* reg the sndbuf if it was vzalloced */ 62862306a36Sopenharmony_ci if (smc->conn.sndbuf_desc->is_vm) { 62962306a36Sopenharmony_ci if (smcr_link_reg_buf(link, smc->conn.sndbuf_desc)) 63062306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_REGBUF; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* reg the rmb */ 63462306a36Sopenharmony_ci if (smcr_link_reg_buf(link, smc->conn.rmb_desc)) 63562306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_REGBUF; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* confirm_rkey is implicit on 1st contact */ 63862306a36Sopenharmony_ci smc->conn.rmb_desc->is_conf_rkey = true; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* send CONFIRM LINK response over RoCE fabric */ 64162306a36Sopenharmony_ci rc = smc_llc_send_confirm_link(link, SMC_LLC_RESP); 64262306a36Sopenharmony_ci if (rc < 0) 64362306a36Sopenharmony_ci return SMC_CLC_DECL_TIMEOUT_CL; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci smc_llc_link_active(link); 64662306a36Sopenharmony_ci smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (link->lgr->max_links > 1) { 64962306a36Sopenharmony_ci /* optional 2nd link, receive ADD LINK request from server */ 65062306a36Sopenharmony_ci qentry = smc_llc_wait(link->lgr, NULL, SMC_LLC_WAIT_TIME, 65162306a36Sopenharmony_ci SMC_LLC_ADD_LINK); 65262306a36Sopenharmony_ci if (!qentry) { 65362306a36Sopenharmony_ci struct smc_clc_msg_decline dclc; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc), 65662306a36Sopenharmony_ci SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT); 65762306a36Sopenharmony_ci if (rc == -EAGAIN) 65862306a36Sopenharmony_ci rc = 0; /* no DECLINE received, go with one link */ 65962306a36Sopenharmony_ci return rc; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci smc_llc_flow_qentry_clr(&link->lgr->llc_flow_lcl); 66262306a36Sopenharmony_ci smc_llc_cli_add_link(link, qentry); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic bool smc_isascii(char *hostname) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci int i; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for (i = 0; i < SMC_MAX_HOSTNAME_LEN; i++) 67262306a36Sopenharmony_ci if (!isascii(hostname[i])) 67362306a36Sopenharmony_ci return false; 67462306a36Sopenharmony_ci return true; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic void smc_conn_save_peer_info_fce(struct smc_sock *smc, 67862306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *clc) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *clc_v2 = 68162306a36Sopenharmony_ci (struct smc_clc_msg_accept_confirm_v2 *)clc; 68262306a36Sopenharmony_ci struct smc_clc_first_contact_ext *fce; 68362306a36Sopenharmony_ci int clc_v2_len; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (clc->hdr.version == SMC_V1 || 68662306a36Sopenharmony_ci !(clc->hdr.typev2 & SMC_FIRST_CONTACT_MASK)) 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (smc->conn.lgr->is_smcd) { 69062306a36Sopenharmony_ci memcpy(smc->conn.lgr->negotiated_eid, clc_v2->d1.eid, 69162306a36Sopenharmony_ci SMC_MAX_EID_LEN); 69262306a36Sopenharmony_ci clc_v2_len = offsetofend(struct smc_clc_msg_accept_confirm_v2, 69362306a36Sopenharmony_ci d1); 69462306a36Sopenharmony_ci } else { 69562306a36Sopenharmony_ci memcpy(smc->conn.lgr->negotiated_eid, clc_v2->r1.eid, 69662306a36Sopenharmony_ci SMC_MAX_EID_LEN); 69762306a36Sopenharmony_ci clc_v2_len = offsetofend(struct smc_clc_msg_accept_confirm_v2, 69862306a36Sopenharmony_ci r1); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci fce = (struct smc_clc_first_contact_ext *)(((u8 *)clc_v2) + clc_v2_len); 70162306a36Sopenharmony_ci smc->conn.lgr->peer_os = fce->os_type; 70262306a36Sopenharmony_ci smc->conn.lgr->peer_smc_release = fce->release; 70362306a36Sopenharmony_ci if (smc_isascii(fce->hostname)) 70462306a36Sopenharmony_ci memcpy(smc->conn.lgr->peer_hostname, fce->hostname, 70562306a36Sopenharmony_ci SMC_MAX_HOSTNAME_LEN); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic void smcr_conn_save_peer_info(struct smc_sock *smc, 70962306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *clc) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci int bufsize = smc_uncompress_bufsize(clc->r0.rmbe_size); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci smc->conn.peer_rmbe_idx = clc->r0.rmbe_idx; 71462306a36Sopenharmony_ci smc->conn.local_tx_ctrl.token = ntohl(clc->r0.rmbe_alert_token); 71562306a36Sopenharmony_ci smc->conn.peer_rmbe_size = bufsize; 71662306a36Sopenharmony_ci atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size); 71762306a36Sopenharmony_ci smc->conn.tx_off = bufsize * (smc->conn.peer_rmbe_idx - 1); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic void smcd_conn_save_peer_info(struct smc_sock *smc, 72162306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *clc) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci int bufsize = smc_uncompress_bufsize(clc->d0.dmbe_size); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci smc->conn.peer_rmbe_idx = clc->d0.dmbe_idx; 72662306a36Sopenharmony_ci smc->conn.peer_token = ntohll(clc->d0.token); 72762306a36Sopenharmony_ci /* msg header takes up space in the buffer */ 72862306a36Sopenharmony_ci smc->conn.peer_rmbe_size = bufsize - sizeof(struct smcd_cdc_msg); 72962306a36Sopenharmony_ci atomic_set(&smc->conn.peer_rmbe_space, smc->conn.peer_rmbe_size); 73062306a36Sopenharmony_ci smc->conn.tx_off = bufsize * smc->conn.peer_rmbe_idx; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void smc_conn_save_peer_info(struct smc_sock *smc, 73462306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *clc) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci if (smc->conn.lgr->is_smcd) 73762306a36Sopenharmony_ci smcd_conn_save_peer_info(smc, clc); 73862306a36Sopenharmony_ci else 73962306a36Sopenharmony_ci smcr_conn_save_peer_info(smc, clc); 74062306a36Sopenharmony_ci smc_conn_save_peer_info_fce(smc, clc); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic void smc_link_save_peer_info(struct smc_link *link, 74462306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *clc, 74562306a36Sopenharmony_ci struct smc_init_info *ini) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci link->peer_qpn = ntoh24(clc->r0.qpn); 74862306a36Sopenharmony_ci memcpy(link->peer_gid, ini->peer_gid, SMC_GID_SIZE); 74962306a36Sopenharmony_ci memcpy(link->peer_mac, ini->peer_mac, sizeof(link->peer_mac)); 75062306a36Sopenharmony_ci link->peer_psn = ntoh24(clc->r0.psn); 75162306a36Sopenharmony_ci link->peer_mtu = clc->r0.qp_mtu; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void smc_stat_inc_fback_rsn_cnt(struct smc_sock *smc, 75562306a36Sopenharmony_ci struct smc_stats_fback *fback_arr) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci int cnt; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci for (cnt = 0; cnt < SMC_MAX_FBACK_RSN_CNT; cnt++) { 76062306a36Sopenharmony_ci if (fback_arr[cnt].fback_code == smc->fallback_rsn) { 76162306a36Sopenharmony_ci fback_arr[cnt].count++; 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci if (!fback_arr[cnt].fback_code) { 76562306a36Sopenharmony_ci fback_arr[cnt].fback_code = smc->fallback_rsn; 76662306a36Sopenharmony_ci fback_arr[cnt].count++; 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic void smc_stat_fallback(struct smc_sock *smc) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct net *net = sock_net(&smc->sk); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci mutex_lock(&net->smc.mutex_fback_rsn); 77762306a36Sopenharmony_ci if (smc->listen_smc) { 77862306a36Sopenharmony_ci smc_stat_inc_fback_rsn_cnt(smc, net->smc.fback_rsn->srv); 77962306a36Sopenharmony_ci net->smc.fback_rsn->srv_fback_cnt++; 78062306a36Sopenharmony_ci } else { 78162306a36Sopenharmony_ci smc_stat_inc_fback_rsn_cnt(smc, net->smc.fback_rsn->clnt); 78262306a36Sopenharmony_ci net->smc.fback_rsn->clnt_fback_cnt++; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci mutex_unlock(&net->smc.mutex_fback_rsn); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci/* must be called under rcu read lock */ 78862306a36Sopenharmony_cistatic void smc_fback_wakeup_waitqueue(struct smc_sock *smc, void *key) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct socket_wq *wq; 79162306a36Sopenharmony_ci __poll_t flags; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci wq = rcu_dereference(smc->sk.sk_wq); 79462306a36Sopenharmony_ci if (!skwq_has_sleeper(wq)) 79562306a36Sopenharmony_ci return; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* wake up smc sk->sk_wq */ 79862306a36Sopenharmony_ci if (!key) { 79962306a36Sopenharmony_ci /* sk_state_change */ 80062306a36Sopenharmony_ci wake_up_interruptible_all(&wq->wait); 80162306a36Sopenharmony_ci } else { 80262306a36Sopenharmony_ci flags = key_to_poll(key); 80362306a36Sopenharmony_ci if (flags & (EPOLLIN | EPOLLOUT)) 80462306a36Sopenharmony_ci /* sk_data_ready or sk_write_space */ 80562306a36Sopenharmony_ci wake_up_interruptible_sync_poll(&wq->wait, flags); 80662306a36Sopenharmony_ci else if (flags & EPOLLERR) 80762306a36Sopenharmony_ci /* sk_error_report */ 80862306a36Sopenharmony_ci wake_up_interruptible_poll(&wq->wait, flags); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int smc_fback_mark_woken(wait_queue_entry_t *wait, 81362306a36Sopenharmony_ci unsigned int mode, int sync, void *key) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct smc_mark_woken *mark = 81662306a36Sopenharmony_ci container_of(wait, struct smc_mark_woken, wait_entry); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci mark->woken = true; 81962306a36Sopenharmony_ci mark->key = key; 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void smc_fback_forward_wakeup(struct smc_sock *smc, struct sock *clcsk, 82462306a36Sopenharmony_ci void (*clcsock_callback)(struct sock *sk)) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct smc_mark_woken mark = { .woken = false }; 82762306a36Sopenharmony_ci struct socket_wq *wq; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci init_waitqueue_func_entry(&mark.wait_entry, 83062306a36Sopenharmony_ci smc_fback_mark_woken); 83162306a36Sopenharmony_ci rcu_read_lock(); 83262306a36Sopenharmony_ci wq = rcu_dereference(clcsk->sk_wq); 83362306a36Sopenharmony_ci if (!wq) 83462306a36Sopenharmony_ci goto out; 83562306a36Sopenharmony_ci add_wait_queue(sk_sleep(clcsk), &mark.wait_entry); 83662306a36Sopenharmony_ci clcsock_callback(clcsk); 83762306a36Sopenharmony_ci remove_wait_queue(sk_sleep(clcsk), &mark.wait_entry); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (mark.woken) 84062306a36Sopenharmony_ci smc_fback_wakeup_waitqueue(smc, mark.key); 84162306a36Sopenharmony_ciout: 84262306a36Sopenharmony_ci rcu_read_unlock(); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic void smc_fback_state_change(struct sock *clcsk) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct smc_sock *smc; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci read_lock_bh(&clcsk->sk_callback_lock); 85062306a36Sopenharmony_ci smc = smc_clcsock_user_data(clcsk); 85162306a36Sopenharmony_ci if (smc) 85262306a36Sopenharmony_ci smc_fback_forward_wakeup(smc, clcsk, 85362306a36Sopenharmony_ci smc->clcsk_state_change); 85462306a36Sopenharmony_ci read_unlock_bh(&clcsk->sk_callback_lock); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic void smc_fback_data_ready(struct sock *clcsk) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct smc_sock *smc; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci read_lock_bh(&clcsk->sk_callback_lock); 86262306a36Sopenharmony_ci smc = smc_clcsock_user_data(clcsk); 86362306a36Sopenharmony_ci if (smc) 86462306a36Sopenharmony_ci smc_fback_forward_wakeup(smc, clcsk, 86562306a36Sopenharmony_ci smc->clcsk_data_ready); 86662306a36Sopenharmony_ci read_unlock_bh(&clcsk->sk_callback_lock); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void smc_fback_write_space(struct sock *clcsk) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct smc_sock *smc; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci read_lock_bh(&clcsk->sk_callback_lock); 87462306a36Sopenharmony_ci smc = smc_clcsock_user_data(clcsk); 87562306a36Sopenharmony_ci if (smc) 87662306a36Sopenharmony_ci smc_fback_forward_wakeup(smc, clcsk, 87762306a36Sopenharmony_ci smc->clcsk_write_space); 87862306a36Sopenharmony_ci read_unlock_bh(&clcsk->sk_callback_lock); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic void smc_fback_error_report(struct sock *clcsk) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci struct smc_sock *smc; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci read_lock_bh(&clcsk->sk_callback_lock); 88662306a36Sopenharmony_ci smc = smc_clcsock_user_data(clcsk); 88762306a36Sopenharmony_ci if (smc) 88862306a36Sopenharmony_ci smc_fback_forward_wakeup(smc, clcsk, 88962306a36Sopenharmony_ci smc->clcsk_error_report); 89062306a36Sopenharmony_ci read_unlock_bh(&clcsk->sk_callback_lock); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic void smc_fback_replace_callbacks(struct smc_sock *smc) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct sock *clcsk = smc->clcsock->sk; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci write_lock_bh(&clcsk->sk_callback_lock); 89862306a36Sopenharmony_ci clcsk->sk_user_data = (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci smc_clcsock_replace_cb(&clcsk->sk_state_change, smc_fback_state_change, 90162306a36Sopenharmony_ci &smc->clcsk_state_change); 90262306a36Sopenharmony_ci smc_clcsock_replace_cb(&clcsk->sk_data_ready, smc_fback_data_ready, 90362306a36Sopenharmony_ci &smc->clcsk_data_ready); 90462306a36Sopenharmony_ci smc_clcsock_replace_cb(&clcsk->sk_write_space, smc_fback_write_space, 90562306a36Sopenharmony_ci &smc->clcsk_write_space); 90662306a36Sopenharmony_ci smc_clcsock_replace_cb(&clcsk->sk_error_report, smc_fback_error_report, 90762306a36Sopenharmony_ci &smc->clcsk_error_report); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci write_unlock_bh(&clcsk->sk_callback_lock); 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic int smc_switch_to_fallback(struct smc_sock *smc, int reason_code) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci int rc = 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci mutex_lock(&smc->clcsock_release_lock); 91762306a36Sopenharmony_ci if (!smc->clcsock) { 91862306a36Sopenharmony_ci rc = -EBADF; 91962306a36Sopenharmony_ci goto out; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci smc->use_fallback = true; 92362306a36Sopenharmony_ci smc->fallback_rsn = reason_code; 92462306a36Sopenharmony_ci smc_stat_fallback(smc); 92562306a36Sopenharmony_ci trace_smc_switch_to_fallback(smc, reason_code); 92662306a36Sopenharmony_ci if (smc->sk.sk_socket && smc->sk.sk_socket->file) { 92762306a36Sopenharmony_ci smc->clcsock->file = smc->sk.sk_socket->file; 92862306a36Sopenharmony_ci smc->clcsock->file->private_data = smc->clcsock; 92962306a36Sopenharmony_ci smc->clcsock->wq.fasync_list = 93062306a36Sopenharmony_ci smc->sk.sk_socket->wq.fasync_list; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* There might be some wait entries remaining 93362306a36Sopenharmony_ci * in smc sk->sk_wq and they should be woken up 93462306a36Sopenharmony_ci * as clcsock's wait queue is woken up. 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci smc_fback_replace_callbacks(smc); 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ciout: 93962306a36Sopenharmony_ci mutex_unlock(&smc->clcsock_release_lock); 94062306a36Sopenharmony_ci return rc; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci/* fall back during connect */ 94462306a36Sopenharmony_cistatic int smc_connect_fallback(struct smc_sock *smc, int reason_code) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct net *net = sock_net(&smc->sk); 94762306a36Sopenharmony_ci int rc = 0; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci rc = smc_switch_to_fallback(smc, reason_code); 95062306a36Sopenharmony_ci if (rc) { /* fallback fails */ 95162306a36Sopenharmony_ci this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt); 95262306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT) 95362306a36Sopenharmony_ci sock_put(&smc->sk); /* passive closing */ 95462306a36Sopenharmony_ci return rc; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci smc_copy_sock_settings_to_clc(smc); 95762306a36Sopenharmony_ci smc->connect_nonblock = 0; 95862306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT) 95962306a36Sopenharmony_ci smc->sk.sk_state = SMC_ACTIVE; 96062306a36Sopenharmony_ci return 0; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci/* decline and fall back during connect */ 96462306a36Sopenharmony_cistatic int smc_connect_decline_fallback(struct smc_sock *smc, int reason_code, 96562306a36Sopenharmony_ci u8 version) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct net *net = sock_net(&smc->sk); 96862306a36Sopenharmony_ci int rc; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (reason_code < 0) { /* error, fallback is not possible */ 97162306a36Sopenharmony_ci this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt); 97262306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT) 97362306a36Sopenharmony_ci sock_put(&smc->sk); /* passive closing */ 97462306a36Sopenharmony_ci return reason_code; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci if (reason_code != SMC_CLC_DECL_PEERDECL) { 97762306a36Sopenharmony_ci rc = smc_clc_send_decline(smc, reason_code, version); 97862306a36Sopenharmony_ci if (rc < 0) { 97962306a36Sopenharmony_ci this_cpu_inc(net->smc.smc_stats->clnt_hshake_err_cnt); 98062306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT) 98162306a36Sopenharmony_ci sock_put(&smc->sk); /* passive closing */ 98262306a36Sopenharmony_ci return rc; 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci return smc_connect_fallback(smc, reason_code); 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic void smc_conn_abort(struct smc_sock *smc, int local_first) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci struct smc_connection *conn = &smc->conn; 99162306a36Sopenharmony_ci struct smc_link_group *lgr = conn->lgr; 99262306a36Sopenharmony_ci bool lgr_valid = false; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (smc_conn_lgr_valid(conn)) 99562306a36Sopenharmony_ci lgr_valid = true; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci smc_conn_free(conn); 99862306a36Sopenharmony_ci if (local_first && lgr_valid) 99962306a36Sopenharmony_ci smc_lgr_cleanup_early(lgr); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci/* check if there is a rdma device available for this connection. */ 100362306a36Sopenharmony_ci/* called for connect and listen */ 100462306a36Sopenharmony_cistatic int smc_find_rdma_device(struct smc_sock *smc, struct smc_init_info *ini) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci /* PNET table look up: search active ib_device and port 100762306a36Sopenharmony_ci * within same PNETID that also contains the ethernet device 100862306a36Sopenharmony_ci * used for the internal TCP socket 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ci smc_pnet_find_roce_resource(smc->clcsock->sk, ini); 101162306a36Sopenharmony_ci if (!ini->check_smcrv2 && !ini->ib_dev) 101262306a36Sopenharmony_ci return SMC_CLC_DECL_NOSMCRDEV; 101362306a36Sopenharmony_ci if (ini->check_smcrv2 && !ini->smcrv2.ib_dev_v2) 101462306a36Sopenharmony_ci return SMC_CLC_DECL_NOSMCRDEV; 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/* check if there is an ISM device available for this connection. */ 101962306a36Sopenharmony_ci/* called for connect and listen */ 102062306a36Sopenharmony_cistatic int smc_find_ism_device(struct smc_sock *smc, struct smc_init_info *ini) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci /* Find ISM device with same PNETID as connecting interface */ 102362306a36Sopenharmony_ci smc_pnet_find_ism_resource(smc->clcsock->sk, ini); 102462306a36Sopenharmony_ci if (!ini->ism_dev[0]) 102562306a36Sopenharmony_ci return SMC_CLC_DECL_NOSMCDDEV; 102662306a36Sopenharmony_ci else 102762306a36Sopenharmony_ci ini->ism_chid[0] = smc_ism_get_chid(ini->ism_dev[0]); 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/* is chid unique for the ism devices that are already determined? */ 103262306a36Sopenharmony_cistatic bool smc_find_ism_v2_is_unique_chid(u16 chid, struct smc_init_info *ini, 103362306a36Sopenharmony_ci int cnt) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci int i = (!ini->ism_dev[0]) ? 1 : 0; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci for (; i < cnt; i++) 103862306a36Sopenharmony_ci if (ini->ism_chid[i] == chid) 103962306a36Sopenharmony_ci return false; 104062306a36Sopenharmony_ci return true; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci/* determine possible V2 ISM devices (either without PNETID or with PNETID plus 104462306a36Sopenharmony_ci * PNETID matching net_device) 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_cistatic int smc_find_ism_v2_device_clnt(struct smc_sock *smc, 104762306a36Sopenharmony_ci struct smc_init_info *ini) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci int rc = SMC_CLC_DECL_NOSMCDDEV; 105062306a36Sopenharmony_ci struct smcd_dev *smcd; 105162306a36Sopenharmony_ci int i = 1; 105262306a36Sopenharmony_ci u16 chid; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (smcd_indicated(ini->smc_type_v1)) 105562306a36Sopenharmony_ci rc = 0; /* already initialized for V1 */ 105662306a36Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 105762306a36Sopenharmony_ci list_for_each_entry(smcd, &smcd_dev_list.list, list) { 105862306a36Sopenharmony_ci if (smcd->going_away || smcd == ini->ism_dev[0]) 105962306a36Sopenharmony_ci continue; 106062306a36Sopenharmony_ci chid = smc_ism_get_chid(smcd); 106162306a36Sopenharmony_ci if (!smc_find_ism_v2_is_unique_chid(chid, ini, i)) 106262306a36Sopenharmony_ci continue; 106362306a36Sopenharmony_ci if (!smc_pnet_is_pnetid_set(smcd->pnetid) || 106462306a36Sopenharmony_ci smc_pnet_is_ndev_pnetid(sock_net(&smc->sk), smcd->pnetid)) { 106562306a36Sopenharmony_ci ini->ism_dev[i] = smcd; 106662306a36Sopenharmony_ci ini->ism_chid[i] = chid; 106762306a36Sopenharmony_ci ini->is_smcd = true; 106862306a36Sopenharmony_ci rc = 0; 106962306a36Sopenharmony_ci i++; 107062306a36Sopenharmony_ci if (i > SMC_MAX_ISM_DEVS) 107162306a36Sopenharmony_ci break; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 107562306a36Sopenharmony_ci ini->ism_offered_cnt = i - 1; 107662306a36Sopenharmony_ci if (!ini->ism_dev[0] && !ini->ism_dev[1]) 107762306a36Sopenharmony_ci ini->smcd_version = 0; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci return rc; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci/* Check for VLAN ID and register it on ISM device just for CLC handshake */ 108362306a36Sopenharmony_cistatic int smc_connect_ism_vlan_setup(struct smc_sock *smc, 108462306a36Sopenharmony_ci struct smc_init_info *ini) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci if (ini->vlan_id && smc_ism_get_vlan(ini->ism_dev[0], ini->vlan_id)) 108762306a36Sopenharmony_ci return SMC_CLC_DECL_ISMVLANERR; 108862306a36Sopenharmony_ci return 0; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic int smc_find_proposal_devices(struct smc_sock *smc, 109262306a36Sopenharmony_ci struct smc_init_info *ini) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci int rc = 0; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* check if there is an ism device available */ 109762306a36Sopenharmony_ci if (!(ini->smcd_version & SMC_V1) || 109862306a36Sopenharmony_ci smc_find_ism_device(smc, ini) || 109962306a36Sopenharmony_ci smc_connect_ism_vlan_setup(smc, ini)) 110062306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V1; 110162306a36Sopenharmony_ci /* else ISM V1 is supported for this connection */ 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* check if there is an rdma device available */ 110462306a36Sopenharmony_ci if (!(ini->smcr_version & SMC_V1) || 110562306a36Sopenharmony_ci smc_find_rdma_device(smc, ini)) 110662306a36Sopenharmony_ci ini->smcr_version &= ~SMC_V1; 110762306a36Sopenharmony_ci /* else RDMA is supported for this connection */ 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci ini->smc_type_v1 = smc_indicated_type(ini->smcd_version & SMC_V1, 111062306a36Sopenharmony_ci ini->smcr_version & SMC_V1); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci /* check if there is an ism v2 device available */ 111362306a36Sopenharmony_ci if (!(ini->smcd_version & SMC_V2) || 111462306a36Sopenharmony_ci !smc_ism_is_v2_capable() || 111562306a36Sopenharmony_ci smc_find_ism_v2_device_clnt(smc, ini)) 111662306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V2; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* check if there is an rdma v2 device available */ 111962306a36Sopenharmony_ci ini->check_smcrv2 = true; 112062306a36Sopenharmony_ci ini->smcrv2.saddr = smc->clcsock->sk->sk_rcv_saddr; 112162306a36Sopenharmony_ci if (!(ini->smcr_version & SMC_V2) || 112262306a36Sopenharmony_ci smc->clcsock->sk->sk_family != AF_INET || 112362306a36Sopenharmony_ci !smc_clc_ueid_count() || 112462306a36Sopenharmony_ci smc_find_rdma_device(smc, ini)) 112562306a36Sopenharmony_ci ini->smcr_version &= ~SMC_V2; 112662306a36Sopenharmony_ci ini->check_smcrv2 = false; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ini->smc_type_v2 = smc_indicated_type(ini->smcd_version & SMC_V2, 112962306a36Sopenharmony_ci ini->smcr_version & SMC_V2); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* if neither ISM nor RDMA are supported, fallback */ 113262306a36Sopenharmony_ci if (ini->smc_type_v1 == SMC_TYPE_N && ini->smc_type_v2 == SMC_TYPE_N) 113362306a36Sopenharmony_ci rc = SMC_CLC_DECL_NOSMCDEV; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci return rc; 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci/* cleanup temporary VLAN ID registration used for CLC handshake. If ISM is 113962306a36Sopenharmony_ci * used, the VLAN ID will be registered again during the connection setup. 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_cistatic int smc_connect_ism_vlan_cleanup(struct smc_sock *smc, 114262306a36Sopenharmony_ci struct smc_init_info *ini) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci if (!smcd_indicated(ini->smc_type_v1)) 114562306a36Sopenharmony_ci return 0; 114662306a36Sopenharmony_ci if (ini->vlan_id && smc_ism_put_vlan(ini->ism_dev[0], ini->vlan_id)) 114762306a36Sopenharmony_ci return SMC_CLC_DECL_CNFERR; 114862306a36Sopenharmony_ci return 0; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci#define SMC_CLC_MAX_ACCEPT_LEN \ 115262306a36Sopenharmony_ci (sizeof(struct smc_clc_msg_accept_confirm_v2) + \ 115362306a36Sopenharmony_ci sizeof(struct smc_clc_first_contact_ext_v2x) + \ 115462306a36Sopenharmony_ci sizeof(struct smc_clc_msg_trail)) 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci/* CLC handshake during connect */ 115762306a36Sopenharmony_cistatic int smc_connect_clc(struct smc_sock *smc, 115862306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *aclc2, 115962306a36Sopenharmony_ci struct smc_init_info *ini) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci int rc = 0; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* do inband token exchange */ 116462306a36Sopenharmony_ci rc = smc_clc_send_proposal(smc, ini); 116562306a36Sopenharmony_ci if (rc) 116662306a36Sopenharmony_ci return rc; 116762306a36Sopenharmony_ci /* receive SMC Accept CLC message */ 116862306a36Sopenharmony_ci return smc_clc_wait_msg(smc, aclc2, SMC_CLC_MAX_ACCEPT_LEN, 116962306a36Sopenharmony_ci SMC_CLC_ACCEPT, CLC_WAIT_TIME); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_civoid smc_fill_gid_list(struct smc_link_group *lgr, 117362306a36Sopenharmony_ci struct smc_gidlist *gidlist, 117462306a36Sopenharmony_ci struct smc_ib_device *known_dev, u8 *known_gid) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci struct smc_init_info *alt_ini = NULL; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci memset(gidlist, 0, sizeof(*gidlist)); 117962306a36Sopenharmony_ci memcpy(gidlist->list[gidlist->len++], known_gid, SMC_GID_SIZE); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci alt_ini = kzalloc(sizeof(*alt_ini), GFP_KERNEL); 118262306a36Sopenharmony_ci if (!alt_ini) 118362306a36Sopenharmony_ci goto out; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci alt_ini->vlan_id = lgr->vlan_id; 118662306a36Sopenharmony_ci alt_ini->check_smcrv2 = true; 118762306a36Sopenharmony_ci alt_ini->smcrv2.saddr = lgr->saddr; 118862306a36Sopenharmony_ci smc_pnet_find_alt_roce(lgr, alt_ini, known_dev); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (!alt_ini->smcrv2.ib_dev_v2) 119162306a36Sopenharmony_ci goto out; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci memcpy(gidlist->list[gidlist->len++], alt_ini->smcrv2.ib_gid_v2, 119462306a36Sopenharmony_ci SMC_GID_SIZE); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ciout: 119762306a36Sopenharmony_ci kfree(alt_ini); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic int smc_connect_rdma_v2_prepare(struct smc_sock *smc, 120162306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *aclc, 120262306a36Sopenharmony_ci struct smc_init_info *ini) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *clc_v2 = 120562306a36Sopenharmony_ci (struct smc_clc_msg_accept_confirm_v2 *)aclc; 120662306a36Sopenharmony_ci struct smc_clc_first_contact_ext *fce = 120762306a36Sopenharmony_ci smc_get_clc_first_contact_ext(clc_v2, false); 120862306a36Sopenharmony_ci struct net *net = sock_net(&smc->sk); 120962306a36Sopenharmony_ci int rc; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (!ini->first_contact_peer || aclc->hdr.version == SMC_V1) 121262306a36Sopenharmony_ci return 0; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (fce->v2_direct) { 121562306a36Sopenharmony_ci memcpy(ini->smcrv2.nexthop_mac, &aclc->r0.lcl.mac, ETH_ALEN); 121662306a36Sopenharmony_ci ini->smcrv2.uses_gateway = false; 121762306a36Sopenharmony_ci } else { 121862306a36Sopenharmony_ci if (smc_ib_find_route(net, smc->clcsock->sk->sk_rcv_saddr, 121962306a36Sopenharmony_ci smc_ib_gid_to_ipv4(aclc->r0.lcl.gid), 122062306a36Sopenharmony_ci ini->smcrv2.nexthop_mac, 122162306a36Sopenharmony_ci &ini->smcrv2.uses_gateway)) 122262306a36Sopenharmony_ci return SMC_CLC_DECL_NOROUTE; 122362306a36Sopenharmony_ci if (!ini->smcrv2.uses_gateway) { 122462306a36Sopenharmony_ci /* mismatch: peer claims indirect, but its direct */ 122562306a36Sopenharmony_ci return SMC_CLC_DECL_NOINDIRECT; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci ini->release_nr = fce->release; 123062306a36Sopenharmony_ci rc = smc_clc_clnt_v2x_features_validate(fce, ini); 123162306a36Sopenharmony_ci if (rc) 123262306a36Sopenharmony_ci return rc; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci return 0; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci/* setup for RDMA connection of client */ 123862306a36Sopenharmony_cistatic int smc_connect_rdma(struct smc_sock *smc, 123962306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *aclc, 124062306a36Sopenharmony_ci struct smc_init_info *ini) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci int i, reason_code = 0; 124362306a36Sopenharmony_ci struct smc_link *link; 124462306a36Sopenharmony_ci u8 *eid = NULL; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci ini->is_smcd = false; 124762306a36Sopenharmony_ci ini->ib_clcqpn = ntoh24(aclc->r0.qpn); 124862306a36Sopenharmony_ci ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK; 124962306a36Sopenharmony_ci memcpy(ini->peer_systemid, aclc->r0.lcl.id_for_peer, SMC_SYSTEMID_LEN); 125062306a36Sopenharmony_ci memcpy(ini->peer_gid, aclc->r0.lcl.gid, SMC_GID_SIZE); 125162306a36Sopenharmony_ci memcpy(ini->peer_mac, aclc->r0.lcl.mac, ETH_ALEN); 125262306a36Sopenharmony_ci ini->max_conns = SMC_CONN_PER_LGR_MAX; 125362306a36Sopenharmony_ci ini->max_links = SMC_LINKS_ADD_LNK_MAX; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci reason_code = smc_connect_rdma_v2_prepare(smc, aclc, ini); 125662306a36Sopenharmony_ci if (reason_code) 125762306a36Sopenharmony_ci return reason_code; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci mutex_lock(&smc_client_lgr_pending); 126062306a36Sopenharmony_ci reason_code = smc_conn_create(smc, ini); 126162306a36Sopenharmony_ci if (reason_code) { 126262306a36Sopenharmony_ci mutex_unlock(&smc_client_lgr_pending); 126362306a36Sopenharmony_ci return reason_code; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci smc_conn_save_peer_info(smc, aclc); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (ini->first_contact_local) { 126962306a36Sopenharmony_ci link = smc->conn.lnk; 127062306a36Sopenharmony_ci } else { 127162306a36Sopenharmony_ci /* set link that was assigned by server */ 127262306a36Sopenharmony_ci link = NULL; 127362306a36Sopenharmony_ci for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) { 127462306a36Sopenharmony_ci struct smc_link *l = &smc->conn.lgr->lnk[i]; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (l->peer_qpn == ntoh24(aclc->r0.qpn) && 127762306a36Sopenharmony_ci !memcmp(l->peer_gid, &aclc->r0.lcl.gid, 127862306a36Sopenharmony_ci SMC_GID_SIZE) && 127962306a36Sopenharmony_ci (aclc->hdr.version > SMC_V1 || 128062306a36Sopenharmony_ci !memcmp(l->peer_mac, &aclc->r0.lcl.mac, 128162306a36Sopenharmony_ci sizeof(l->peer_mac)))) { 128262306a36Sopenharmony_ci link = l; 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci } 128662306a36Sopenharmony_ci if (!link) { 128762306a36Sopenharmony_ci reason_code = SMC_CLC_DECL_NOSRVLINK; 128862306a36Sopenharmony_ci goto connect_abort; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci smc_switch_link_and_count(&smc->conn, link); 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* create send buffer and rmb */ 129462306a36Sopenharmony_ci if (smc_buf_create(smc, false)) { 129562306a36Sopenharmony_ci reason_code = SMC_CLC_DECL_MEM; 129662306a36Sopenharmony_ci goto connect_abort; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (ini->first_contact_local) 130062306a36Sopenharmony_ci smc_link_save_peer_info(link, aclc, ini); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (smc_rmb_rtoken_handling(&smc->conn, link, aclc)) { 130362306a36Sopenharmony_ci reason_code = SMC_CLC_DECL_ERR_RTOK; 130462306a36Sopenharmony_ci goto connect_abort; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci smc_close_init(smc); 130862306a36Sopenharmony_ci smc_rx_init(smc); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (ini->first_contact_local) { 131162306a36Sopenharmony_ci if (smc_ib_ready_link(link)) { 131262306a36Sopenharmony_ci reason_code = SMC_CLC_DECL_ERR_RDYLNK; 131362306a36Sopenharmony_ci goto connect_abort; 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci } else { 131662306a36Sopenharmony_ci /* reg sendbufs if they were vzalloced */ 131762306a36Sopenharmony_ci if (smc->conn.sndbuf_desc->is_vm) { 131862306a36Sopenharmony_ci if (smcr_lgr_reg_sndbufs(link, smc->conn.sndbuf_desc)) { 131962306a36Sopenharmony_ci reason_code = SMC_CLC_DECL_ERR_REGBUF; 132062306a36Sopenharmony_ci goto connect_abort; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci if (smcr_lgr_reg_rmbs(link, smc->conn.rmb_desc)) { 132462306a36Sopenharmony_ci reason_code = SMC_CLC_DECL_ERR_REGBUF; 132562306a36Sopenharmony_ci goto connect_abort; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (aclc->hdr.version > SMC_V1) { 133062306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *clc_v2 = 133162306a36Sopenharmony_ci (struct smc_clc_msg_accept_confirm_v2 *)aclc; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci eid = clc_v2->r1.eid; 133462306a36Sopenharmony_ci if (ini->first_contact_local) 133562306a36Sopenharmony_ci smc_fill_gid_list(link->lgr, &ini->smcrv2.gidlist, 133662306a36Sopenharmony_ci link->smcibdev, link->gid); 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci reason_code = smc_clc_send_confirm(smc, ini->first_contact_local, 134062306a36Sopenharmony_ci aclc->hdr.version, eid, ini); 134162306a36Sopenharmony_ci if (reason_code) 134262306a36Sopenharmony_ci goto connect_abort; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci smc_tx_init(smc); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (ini->first_contact_local) { 134762306a36Sopenharmony_ci /* QP confirmation over RoCE fabric */ 134862306a36Sopenharmony_ci smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK); 134962306a36Sopenharmony_ci reason_code = smcr_clnt_conf_first_link(smc); 135062306a36Sopenharmony_ci smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl); 135162306a36Sopenharmony_ci if (reason_code) 135262306a36Sopenharmony_ci goto connect_abort; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci mutex_unlock(&smc_client_lgr_pending); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci smc_copy_sock_settings_to_clc(smc); 135762306a36Sopenharmony_ci smc->connect_nonblock = 0; 135862306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT) 135962306a36Sopenharmony_ci smc->sk.sk_state = SMC_ACTIVE; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci return 0; 136262306a36Sopenharmony_ciconnect_abort: 136362306a36Sopenharmony_ci smc_conn_abort(smc, ini->first_contact_local); 136462306a36Sopenharmony_ci mutex_unlock(&smc_client_lgr_pending); 136562306a36Sopenharmony_ci smc->connect_nonblock = 0; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci return reason_code; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci/* The server has chosen one of the proposed ISM devices for the communication. 137162306a36Sopenharmony_ci * Determine from the CHID of the received CLC ACCEPT the ISM device chosen. 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_cistatic int 137462306a36Sopenharmony_cismc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm_v2 *aclc, 137562306a36Sopenharmony_ci struct smc_init_info *ini) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci int i; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci for (i = 0; i < ini->ism_offered_cnt + 1; i++) { 138062306a36Sopenharmony_ci if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { 138162306a36Sopenharmony_ci ini->ism_selected = i; 138262306a36Sopenharmony_ci return 0; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci return -EPROTO; 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci/* setup for ISM connection of client */ 139062306a36Sopenharmony_cistatic int smc_connect_ism(struct smc_sock *smc, 139162306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *aclc, 139262306a36Sopenharmony_ci struct smc_init_info *ini) 139362306a36Sopenharmony_ci{ 139462306a36Sopenharmony_ci u8 *eid = NULL; 139562306a36Sopenharmony_ci int rc = 0; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci ini->is_smcd = true; 139862306a36Sopenharmony_ci ini->first_contact_peer = aclc->hdr.typev2 & SMC_FIRST_CONTACT_MASK; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci if (aclc->hdr.version == SMC_V2) { 140162306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *aclc_v2 = 140262306a36Sopenharmony_ci (struct smc_clc_msg_accept_confirm_v2 *)aclc; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (ini->first_contact_peer) { 140562306a36Sopenharmony_ci struct smc_clc_first_contact_ext *fce = 140662306a36Sopenharmony_ci smc_get_clc_first_contact_ext(aclc_v2, true); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci ini->release_nr = fce->release; 140962306a36Sopenharmony_ci rc = smc_clc_clnt_v2x_features_validate(fce, ini); 141062306a36Sopenharmony_ci if (rc) 141162306a36Sopenharmony_ci return rc; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci rc = smc_v2_determine_accepted_chid(aclc_v2, ini); 141562306a36Sopenharmony_ci if (rc) 141662306a36Sopenharmony_ci return rc; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci ini->ism_peer_gid[ini->ism_selected] = ntohll(aclc->d0.gid); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* there is only one lgr role for SMC-D; use server lock */ 142162306a36Sopenharmony_ci mutex_lock(&smc_server_lgr_pending); 142262306a36Sopenharmony_ci rc = smc_conn_create(smc, ini); 142362306a36Sopenharmony_ci if (rc) { 142462306a36Sopenharmony_ci mutex_unlock(&smc_server_lgr_pending); 142562306a36Sopenharmony_ci return rc; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci /* Create send and receive buffers */ 142962306a36Sopenharmony_ci rc = smc_buf_create(smc, true); 143062306a36Sopenharmony_ci if (rc) { 143162306a36Sopenharmony_ci rc = (rc == -ENOSPC) ? SMC_CLC_DECL_MAX_DMB : SMC_CLC_DECL_MEM; 143262306a36Sopenharmony_ci goto connect_abort; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci smc_conn_save_peer_info(smc, aclc); 143662306a36Sopenharmony_ci smc_close_init(smc); 143762306a36Sopenharmony_ci smc_rx_init(smc); 143862306a36Sopenharmony_ci smc_tx_init(smc); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci if (aclc->hdr.version > SMC_V1) { 144162306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *clc_v2 = 144262306a36Sopenharmony_ci (struct smc_clc_msg_accept_confirm_v2 *)aclc; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci eid = clc_v2->d1.eid; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci rc = smc_clc_send_confirm(smc, ini->first_contact_local, 144862306a36Sopenharmony_ci aclc->hdr.version, eid, ini); 144962306a36Sopenharmony_ci if (rc) 145062306a36Sopenharmony_ci goto connect_abort; 145162306a36Sopenharmony_ci mutex_unlock(&smc_server_lgr_pending); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci smc_copy_sock_settings_to_clc(smc); 145462306a36Sopenharmony_ci smc->connect_nonblock = 0; 145562306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT) 145662306a36Sopenharmony_ci smc->sk.sk_state = SMC_ACTIVE; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci return 0; 145962306a36Sopenharmony_ciconnect_abort: 146062306a36Sopenharmony_ci smc_conn_abort(smc, ini->first_contact_local); 146162306a36Sopenharmony_ci mutex_unlock(&smc_server_lgr_pending); 146262306a36Sopenharmony_ci smc->connect_nonblock = 0; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci return rc; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci/* check if received accept type and version matches a proposed one */ 146862306a36Sopenharmony_cistatic int smc_connect_check_aclc(struct smc_init_info *ini, 146962306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *aclc) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci if (aclc->hdr.typev1 != SMC_TYPE_R && 147262306a36Sopenharmony_ci aclc->hdr.typev1 != SMC_TYPE_D) 147362306a36Sopenharmony_ci return SMC_CLC_DECL_MODEUNSUPP; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci if (aclc->hdr.version >= SMC_V2) { 147662306a36Sopenharmony_ci if ((aclc->hdr.typev1 == SMC_TYPE_R && 147762306a36Sopenharmony_ci !smcr_indicated(ini->smc_type_v2)) || 147862306a36Sopenharmony_ci (aclc->hdr.typev1 == SMC_TYPE_D && 147962306a36Sopenharmony_ci !smcd_indicated(ini->smc_type_v2))) 148062306a36Sopenharmony_ci return SMC_CLC_DECL_MODEUNSUPP; 148162306a36Sopenharmony_ci } else { 148262306a36Sopenharmony_ci if ((aclc->hdr.typev1 == SMC_TYPE_R && 148362306a36Sopenharmony_ci !smcr_indicated(ini->smc_type_v1)) || 148462306a36Sopenharmony_ci (aclc->hdr.typev1 == SMC_TYPE_D && 148562306a36Sopenharmony_ci !smcd_indicated(ini->smc_type_v1))) 148662306a36Sopenharmony_ci return SMC_CLC_DECL_MODEUNSUPP; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci return 0; 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci/* perform steps before actually connecting */ 149362306a36Sopenharmony_cistatic int __smc_connect(struct smc_sock *smc) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci u8 version = smc_ism_is_v2_capable() ? SMC_V2 : SMC_V1; 149662306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm_v2 *aclc2; 149762306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *aclc; 149862306a36Sopenharmony_ci struct smc_init_info *ini = NULL; 149962306a36Sopenharmony_ci u8 *buf = NULL; 150062306a36Sopenharmony_ci int rc = 0; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (smc->use_fallback) 150362306a36Sopenharmony_ci return smc_connect_fallback(smc, smc->fallback_rsn); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci /* if peer has not signalled SMC-capability, fall back */ 150662306a36Sopenharmony_ci if (!tcp_sk(smc->clcsock->sk)->syn_smc) 150762306a36Sopenharmony_ci return smc_connect_fallback(smc, SMC_CLC_DECL_PEERNOSMC); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* IPSec connections opt out of SMC optimizations */ 151062306a36Sopenharmony_ci if (using_ipsec(smc)) 151162306a36Sopenharmony_ci return smc_connect_decline_fallback(smc, SMC_CLC_DECL_IPSEC, 151262306a36Sopenharmony_ci version); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci ini = kzalloc(sizeof(*ini), GFP_KERNEL); 151562306a36Sopenharmony_ci if (!ini) 151662306a36Sopenharmony_ci return smc_connect_decline_fallback(smc, SMC_CLC_DECL_MEM, 151762306a36Sopenharmony_ci version); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci ini->smcd_version = SMC_V1 | SMC_V2; 152062306a36Sopenharmony_ci ini->smcr_version = SMC_V1 | SMC_V2; 152162306a36Sopenharmony_ci ini->smc_type_v1 = SMC_TYPE_B; 152262306a36Sopenharmony_ci ini->smc_type_v2 = SMC_TYPE_B; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* get vlan id from IP device */ 152562306a36Sopenharmony_ci if (smc_vlan_by_tcpsk(smc->clcsock, ini)) { 152662306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V1; 152762306a36Sopenharmony_ci ini->smcr_version = 0; 152862306a36Sopenharmony_ci ini->smc_type_v1 = SMC_TYPE_N; 152962306a36Sopenharmony_ci if (!ini->smcd_version) { 153062306a36Sopenharmony_ci rc = SMC_CLC_DECL_GETVLANERR; 153162306a36Sopenharmony_ci goto fallback; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci rc = smc_find_proposal_devices(smc, ini); 153662306a36Sopenharmony_ci if (rc) 153762306a36Sopenharmony_ci goto fallback; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci buf = kzalloc(SMC_CLC_MAX_ACCEPT_LEN, GFP_KERNEL); 154062306a36Sopenharmony_ci if (!buf) { 154162306a36Sopenharmony_ci rc = SMC_CLC_DECL_MEM; 154262306a36Sopenharmony_ci goto fallback; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci aclc2 = (struct smc_clc_msg_accept_confirm_v2 *)buf; 154562306a36Sopenharmony_ci aclc = (struct smc_clc_msg_accept_confirm *)aclc2; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* perform CLC handshake */ 154862306a36Sopenharmony_ci rc = smc_connect_clc(smc, aclc2, ini); 154962306a36Sopenharmony_ci if (rc) { 155062306a36Sopenharmony_ci /* -EAGAIN on timeout, see tcp_recvmsg() */ 155162306a36Sopenharmony_ci if (rc == -EAGAIN) { 155262306a36Sopenharmony_ci rc = -ETIMEDOUT; 155362306a36Sopenharmony_ci smc->sk.sk_err = ETIMEDOUT; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci goto vlan_cleanup; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* check if smc modes and versions of CLC proposal and accept match */ 155962306a36Sopenharmony_ci rc = smc_connect_check_aclc(ini, aclc); 156062306a36Sopenharmony_ci version = aclc->hdr.version == SMC_V1 ? SMC_V1 : SMC_V2; 156162306a36Sopenharmony_ci if (rc) 156262306a36Sopenharmony_ci goto vlan_cleanup; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci /* depending on previous steps, connect using rdma or ism */ 156562306a36Sopenharmony_ci if (aclc->hdr.typev1 == SMC_TYPE_R) { 156662306a36Sopenharmony_ci ini->smcr_version = version; 156762306a36Sopenharmony_ci rc = smc_connect_rdma(smc, aclc, ini); 156862306a36Sopenharmony_ci } else if (aclc->hdr.typev1 == SMC_TYPE_D) { 156962306a36Sopenharmony_ci ini->smcd_version = version; 157062306a36Sopenharmony_ci rc = smc_connect_ism(smc, aclc, ini); 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci if (rc) 157362306a36Sopenharmony_ci goto vlan_cleanup; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci SMC_STAT_CLNT_SUCC_INC(sock_net(smc->clcsock->sk), aclc); 157662306a36Sopenharmony_ci smc_connect_ism_vlan_cleanup(smc, ini); 157762306a36Sopenharmony_ci kfree(buf); 157862306a36Sopenharmony_ci kfree(ini); 157962306a36Sopenharmony_ci return 0; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_civlan_cleanup: 158262306a36Sopenharmony_ci smc_connect_ism_vlan_cleanup(smc, ini); 158362306a36Sopenharmony_ci kfree(buf); 158462306a36Sopenharmony_cifallback: 158562306a36Sopenharmony_ci kfree(ini); 158662306a36Sopenharmony_ci return smc_connect_decline_fallback(smc, rc, version); 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic void smc_connect_work(struct work_struct *work) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct smc_sock *smc = container_of(work, struct smc_sock, 159262306a36Sopenharmony_ci connect_work); 159362306a36Sopenharmony_ci long timeo = smc->sk.sk_sndtimeo; 159462306a36Sopenharmony_ci int rc = 0; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci if (!timeo) 159762306a36Sopenharmony_ci timeo = MAX_SCHEDULE_TIMEOUT; 159862306a36Sopenharmony_ci lock_sock(smc->clcsock->sk); 159962306a36Sopenharmony_ci if (smc->clcsock->sk->sk_err) { 160062306a36Sopenharmony_ci smc->sk.sk_err = smc->clcsock->sk->sk_err; 160162306a36Sopenharmony_ci } else if ((1 << smc->clcsock->sk->sk_state) & 160262306a36Sopenharmony_ci (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 160362306a36Sopenharmony_ci rc = sk_stream_wait_connect(smc->clcsock->sk, &timeo); 160462306a36Sopenharmony_ci if ((rc == -EPIPE) && 160562306a36Sopenharmony_ci ((1 << smc->clcsock->sk->sk_state) & 160662306a36Sopenharmony_ci (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))) 160762306a36Sopenharmony_ci rc = 0; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci release_sock(smc->clcsock->sk); 161062306a36Sopenharmony_ci lock_sock(&smc->sk); 161162306a36Sopenharmony_ci if (rc != 0 || smc->sk.sk_err) { 161262306a36Sopenharmony_ci smc->sk.sk_state = SMC_CLOSED; 161362306a36Sopenharmony_ci if (rc == -EPIPE || rc == -EAGAIN) 161462306a36Sopenharmony_ci smc->sk.sk_err = EPIPE; 161562306a36Sopenharmony_ci else if (rc == -ECONNREFUSED) 161662306a36Sopenharmony_ci smc->sk.sk_err = ECONNREFUSED; 161762306a36Sopenharmony_ci else if (signal_pending(current)) 161862306a36Sopenharmony_ci smc->sk.sk_err = -sock_intr_errno(timeo); 161962306a36Sopenharmony_ci sock_put(&smc->sk); /* passive closing */ 162062306a36Sopenharmony_ci goto out; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci rc = __smc_connect(smc); 162462306a36Sopenharmony_ci if (rc < 0) 162562306a36Sopenharmony_ci smc->sk.sk_err = -rc; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ciout: 162862306a36Sopenharmony_ci if (!sock_flag(&smc->sk, SOCK_DEAD)) { 162962306a36Sopenharmony_ci if (smc->sk.sk_err) { 163062306a36Sopenharmony_ci smc->sk.sk_state_change(&smc->sk); 163162306a36Sopenharmony_ci } else { /* allow polling before and after fallback decision */ 163262306a36Sopenharmony_ci smc->clcsock->sk->sk_write_space(smc->clcsock->sk); 163362306a36Sopenharmony_ci smc->sk.sk_write_space(&smc->sk); 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci release_sock(&smc->sk); 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic int smc_connect(struct socket *sock, struct sockaddr *addr, 164062306a36Sopenharmony_ci int alen, int flags) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci struct sock *sk = sock->sk; 164362306a36Sopenharmony_ci struct smc_sock *smc; 164462306a36Sopenharmony_ci int rc = -EINVAL; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci smc = smc_sk(sk); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci /* separate smc parameter checking to be safe */ 164962306a36Sopenharmony_ci if (alen < sizeof(addr->sa_family)) 165062306a36Sopenharmony_ci goto out_err; 165162306a36Sopenharmony_ci if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6) 165262306a36Sopenharmony_ci goto out_err; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci lock_sock(sk); 165562306a36Sopenharmony_ci switch (sock->state) { 165662306a36Sopenharmony_ci default: 165762306a36Sopenharmony_ci rc = -EINVAL; 165862306a36Sopenharmony_ci goto out; 165962306a36Sopenharmony_ci case SS_CONNECTED: 166062306a36Sopenharmony_ci rc = sk->sk_state == SMC_ACTIVE ? -EISCONN : -EINVAL; 166162306a36Sopenharmony_ci goto out; 166262306a36Sopenharmony_ci case SS_CONNECTING: 166362306a36Sopenharmony_ci if (sk->sk_state == SMC_ACTIVE) 166462306a36Sopenharmony_ci goto connected; 166562306a36Sopenharmony_ci break; 166662306a36Sopenharmony_ci case SS_UNCONNECTED: 166762306a36Sopenharmony_ci sock->state = SS_CONNECTING; 166862306a36Sopenharmony_ci break; 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci switch (sk->sk_state) { 167262306a36Sopenharmony_ci default: 167362306a36Sopenharmony_ci goto out; 167462306a36Sopenharmony_ci case SMC_CLOSED: 167562306a36Sopenharmony_ci rc = sock_error(sk) ? : -ECONNABORTED; 167662306a36Sopenharmony_ci sock->state = SS_UNCONNECTED; 167762306a36Sopenharmony_ci goto out; 167862306a36Sopenharmony_ci case SMC_ACTIVE: 167962306a36Sopenharmony_ci rc = -EISCONN; 168062306a36Sopenharmony_ci goto out; 168162306a36Sopenharmony_ci case SMC_INIT: 168262306a36Sopenharmony_ci break; 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci smc_copy_sock_settings_to_clc(smc); 168662306a36Sopenharmony_ci tcp_sk(smc->clcsock->sk)->syn_smc = 1; 168762306a36Sopenharmony_ci if (smc->connect_nonblock) { 168862306a36Sopenharmony_ci rc = -EALREADY; 168962306a36Sopenharmony_ci goto out; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci rc = kernel_connect(smc->clcsock, addr, alen, flags); 169262306a36Sopenharmony_ci if (rc && rc != -EINPROGRESS) 169362306a36Sopenharmony_ci goto out; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (smc->use_fallback) { 169662306a36Sopenharmony_ci sock->state = rc ? SS_CONNECTING : SS_CONNECTED; 169762306a36Sopenharmony_ci goto out; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci sock_hold(&smc->sk); /* sock put in passive closing */ 170062306a36Sopenharmony_ci if (flags & O_NONBLOCK) { 170162306a36Sopenharmony_ci if (queue_work(smc_hs_wq, &smc->connect_work)) 170262306a36Sopenharmony_ci smc->connect_nonblock = 1; 170362306a36Sopenharmony_ci rc = -EINPROGRESS; 170462306a36Sopenharmony_ci goto out; 170562306a36Sopenharmony_ci } else { 170662306a36Sopenharmony_ci rc = __smc_connect(smc); 170762306a36Sopenharmony_ci if (rc < 0) 170862306a36Sopenharmony_ci goto out; 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ciconnected: 171262306a36Sopenharmony_ci rc = 0; 171362306a36Sopenharmony_ci sock->state = SS_CONNECTED; 171462306a36Sopenharmony_ciout: 171562306a36Sopenharmony_ci release_sock(sk); 171662306a36Sopenharmony_ciout_err: 171762306a36Sopenharmony_ci return rc; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int smc_clcsock_accept(struct smc_sock *lsmc, struct smc_sock **new_smc) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci struct socket *new_clcsock = NULL; 172362306a36Sopenharmony_ci struct sock *lsk = &lsmc->sk; 172462306a36Sopenharmony_ci struct sock *new_sk; 172562306a36Sopenharmony_ci int rc = -EINVAL; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci release_sock(lsk); 172862306a36Sopenharmony_ci new_sk = smc_sock_alloc(sock_net(lsk), NULL, lsk->sk_protocol); 172962306a36Sopenharmony_ci if (!new_sk) { 173062306a36Sopenharmony_ci rc = -ENOMEM; 173162306a36Sopenharmony_ci lsk->sk_err = ENOMEM; 173262306a36Sopenharmony_ci *new_smc = NULL; 173362306a36Sopenharmony_ci lock_sock(lsk); 173462306a36Sopenharmony_ci goto out; 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci *new_smc = smc_sk(new_sk); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci mutex_lock(&lsmc->clcsock_release_lock); 173962306a36Sopenharmony_ci if (lsmc->clcsock) 174062306a36Sopenharmony_ci rc = kernel_accept(lsmc->clcsock, &new_clcsock, SOCK_NONBLOCK); 174162306a36Sopenharmony_ci mutex_unlock(&lsmc->clcsock_release_lock); 174262306a36Sopenharmony_ci lock_sock(lsk); 174362306a36Sopenharmony_ci if (rc < 0 && rc != -EAGAIN) 174462306a36Sopenharmony_ci lsk->sk_err = -rc; 174562306a36Sopenharmony_ci if (rc < 0 || lsk->sk_state == SMC_CLOSED) { 174662306a36Sopenharmony_ci new_sk->sk_prot->unhash(new_sk); 174762306a36Sopenharmony_ci if (new_clcsock) 174862306a36Sopenharmony_ci sock_release(new_clcsock); 174962306a36Sopenharmony_ci new_sk->sk_state = SMC_CLOSED; 175062306a36Sopenharmony_ci smc_sock_set_flag(new_sk, SOCK_DEAD); 175162306a36Sopenharmony_ci sock_put(new_sk); /* final */ 175262306a36Sopenharmony_ci *new_smc = NULL; 175362306a36Sopenharmony_ci goto out; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* new clcsock has inherited the smc listen-specific sk_data_ready 175762306a36Sopenharmony_ci * function; switch it back to the original sk_data_ready function 175862306a36Sopenharmony_ci */ 175962306a36Sopenharmony_ci new_clcsock->sk->sk_data_ready = lsmc->clcsk_data_ready; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci /* if new clcsock has also inherited the fallback-specific callback 176262306a36Sopenharmony_ci * functions, switch them back to the original ones. 176362306a36Sopenharmony_ci */ 176462306a36Sopenharmony_ci if (lsmc->use_fallback) { 176562306a36Sopenharmony_ci if (lsmc->clcsk_state_change) 176662306a36Sopenharmony_ci new_clcsock->sk->sk_state_change = lsmc->clcsk_state_change; 176762306a36Sopenharmony_ci if (lsmc->clcsk_write_space) 176862306a36Sopenharmony_ci new_clcsock->sk->sk_write_space = lsmc->clcsk_write_space; 176962306a36Sopenharmony_ci if (lsmc->clcsk_error_report) 177062306a36Sopenharmony_ci new_clcsock->sk->sk_error_report = lsmc->clcsk_error_report; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci (*new_smc)->clcsock = new_clcsock; 177462306a36Sopenharmony_ciout: 177562306a36Sopenharmony_ci return rc; 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci/* add a just created sock to the accept queue of the listen sock as 177962306a36Sopenharmony_ci * candidate for a following socket accept call from user space 178062306a36Sopenharmony_ci */ 178162306a36Sopenharmony_cistatic void smc_accept_enqueue(struct sock *parent, struct sock *sk) 178262306a36Sopenharmony_ci{ 178362306a36Sopenharmony_ci struct smc_sock *par = smc_sk(parent); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci sock_hold(sk); /* sock_put in smc_accept_unlink () */ 178662306a36Sopenharmony_ci spin_lock(&par->accept_q_lock); 178762306a36Sopenharmony_ci list_add_tail(&smc_sk(sk)->accept_q, &par->accept_q); 178862306a36Sopenharmony_ci spin_unlock(&par->accept_q_lock); 178962306a36Sopenharmony_ci sk_acceptq_added(parent); 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci/* remove a socket from the accept queue of its parental listening socket */ 179362306a36Sopenharmony_cistatic void smc_accept_unlink(struct sock *sk) 179462306a36Sopenharmony_ci{ 179562306a36Sopenharmony_ci struct smc_sock *par = smc_sk(sk)->listen_smc; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci spin_lock(&par->accept_q_lock); 179862306a36Sopenharmony_ci list_del_init(&smc_sk(sk)->accept_q); 179962306a36Sopenharmony_ci spin_unlock(&par->accept_q_lock); 180062306a36Sopenharmony_ci sk_acceptq_removed(&smc_sk(sk)->listen_smc->sk); 180162306a36Sopenharmony_ci sock_put(sk); /* sock_hold in smc_accept_enqueue */ 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci/* remove a sock from the accept queue to bind it to a new socket created 180562306a36Sopenharmony_ci * for a socket accept call from user space 180662306a36Sopenharmony_ci */ 180762306a36Sopenharmony_cistruct sock *smc_accept_dequeue(struct sock *parent, 180862306a36Sopenharmony_ci struct socket *new_sock) 180962306a36Sopenharmony_ci{ 181062306a36Sopenharmony_ci struct smc_sock *isk, *n; 181162306a36Sopenharmony_ci struct sock *new_sk; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci list_for_each_entry_safe(isk, n, &smc_sk(parent)->accept_q, accept_q) { 181462306a36Sopenharmony_ci new_sk = (struct sock *)isk; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci smc_accept_unlink(new_sk); 181762306a36Sopenharmony_ci if (new_sk->sk_state == SMC_CLOSED) { 181862306a36Sopenharmony_ci new_sk->sk_prot->unhash(new_sk); 181962306a36Sopenharmony_ci if (isk->clcsock) { 182062306a36Sopenharmony_ci sock_release(isk->clcsock); 182162306a36Sopenharmony_ci isk->clcsock = NULL; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci sock_put(new_sk); /* final */ 182462306a36Sopenharmony_ci continue; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci if (new_sock) { 182762306a36Sopenharmony_ci sock_graft(new_sk, new_sock); 182862306a36Sopenharmony_ci new_sock->state = SS_CONNECTED; 182962306a36Sopenharmony_ci if (isk->use_fallback) { 183062306a36Sopenharmony_ci smc_sk(new_sk)->clcsock->file = new_sock->file; 183162306a36Sopenharmony_ci isk->clcsock->file->private_data = isk->clcsock; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci return new_sk; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci return NULL; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci/* clean up for a created but never accepted sock */ 184062306a36Sopenharmony_civoid smc_close_non_accepted(struct sock *sk) 184162306a36Sopenharmony_ci{ 184262306a36Sopenharmony_ci struct smc_sock *smc = smc_sk(sk); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci sock_hold(sk); /* sock_put below */ 184562306a36Sopenharmony_ci lock_sock(sk); 184662306a36Sopenharmony_ci if (!sk->sk_lingertime) 184762306a36Sopenharmony_ci /* wait for peer closing */ 184862306a36Sopenharmony_ci WRITE_ONCE(sk->sk_lingertime, SMC_MAX_STREAM_WAIT_TIMEOUT); 184962306a36Sopenharmony_ci __smc_release(smc); 185062306a36Sopenharmony_ci release_sock(sk); 185162306a36Sopenharmony_ci sock_put(sk); /* sock_hold above */ 185262306a36Sopenharmony_ci sock_put(sk); /* final sock_put */ 185362306a36Sopenharmony_ci} 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_cistatic int smcr_serv_conf_first_link(struct smc_sock *smc) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci struct smc_link *link = smc->conn.lnk; 185862306a36Sopenharmony_ci struct smc_llc_qentry *qentry; 185962306a36Sopenharmony_ci int rc; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci /* reg the sndbuf if it was vzalloced*/ 186262306a36Sopenharmony_ci if (smc->conn.sndbuf_desc->is_vm) { 186362306a36Sopenharmony_ci if (smcr_link_reg_buf(link, smc->conn.sndbuf_desc)) 186462306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_REGBUF; 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci /* reg the rmb */ 186862306a36Sopenharmony_ci if (smcr_link_reg_buf(link, smc->conn.rmb_desc)) 186962306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_REGBUF; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* send CONFIRM LINK request to client over the RoCE fabric */ 187262306a36Sopenharmony_ci rc = smc_llc_send_confirm_link(link, SMC_LLC_REQ); 187362306a36Sopenharmony_ci if (rc < 0) 187462306a36Sopenharmony_ci return SMC_CLC_DECL_TIMEOUT_CL; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci /* receive CONFIRM LINK response from client over the RoCE fabric */ 187762306a36Sopenharmony_ci qentry = smc_llc_wait(link->lgr, link, SMC_LLC_WAIT_TIME, 187862306a36Sopenharmony_ci SMC_LLC_CONFIRM_LINK); 187962306a36Sopenharmony_ci if (!qentry) { 188062306a36Sopenharmony_ci struct smc_clc_msg_decline dclc; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc), 188362306a36Sopenharmony_ci SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT); 188462306a36Sopenharmony_ci return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci smc_llc_save_peer_uid(qentry); 188762306a36Sopenharmony_ci rc = smc_llc_eval_conf_link(qentry, SMC_LLC_RESP); 188862306a36Sopenharmony_ci smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl); 188962306a36Sopenharmony_ci if (rc) 189062306a36Sopenharmony_ci return SMC_CLC_DECL_RMBE_EC; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci /* confirm_rkey is implicit on 1st contact */ 189362306a36Sopenharmony_ci smc->conn.rmb_desc->is_conf_rkey = true; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci smc_llc_link_active(link); 189662306a36Sopenharmony_ci smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (link->lgr->max_links > 1) { 189962306a36Sopenharmony_ci down_write(&link->lgr->llc_conf_mutex); 190062306a36Sopenharmony_ci /* initial contact - try to establish second link */ 190162306a36Sopenharmony_ci smc_llc_srv_add_link(link, NULL); 190262306a36Sopenharmony_ci up_write(&link->lgr->llc_conf_mutex); 190362306a36Sopenharmony_ci } 190462306a36Sopenharmony_ci return 0; 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci/* listen worker: finish */ 190862306a36Sopenharmony_cistatic void smc_listen_out(struct smc_sock *new_smc) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci struct smc_sock *lsmc = new_smc->listen_smc; 191162306a36Sopenharmony_ci struct sock *newsmcsk = &new_smc->sk; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci if (tcp_sk(new_smc->clcsock->sk)->syn_smc) 191462306a36Sopenharmony_ci atomic_dec(&lsmc->queued_smc_hs); 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci if (lsmc->sk.sk_state == SMC_LISTEN) { 191762306a36Sopenharmony_ci lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); 191862306a36Sopenharmony_ci smc_accept_enqueue(&lsmc->sk, newsmcsk); 191962306a36Sopenharmony_ci release_sock(&lsmc->sk); 192062306a36Sopenharmony_ci } else { /* no longer listening */ 192162306a36Sopenharmony_ci smc_close_non_accepted(newsmcsk); 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci /* Wake up accept */ 192562306a36Sopenharmony_ci lsmc->sk.sk_data_ready(&lsmc->sk); 192662306a36Sopenharmony_ci sock_put(&lsmc->sk); /* sock_hold in smc_tcp_listen_work */ 192762306a36Sopenharmony_ci} 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci/* listen worker: finish in state connected */ 193062306a36Sopenharmony_cistatic void smc_listen_out_connected(struct smc_sock *new_smc) 193162306a36Sopenharmony_ci{ 193262306a36Sopenharmony_ci struct sock *newsmcsk = &new_smc->sk; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci if (newsmcsk->sk_state == SMC_INIT) 193562306a36Sopenharmony_ci newsmcsk->sk_state = SMC_ACTIVE; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci smc_listen_out(new_smc); 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci/* listen worker: finish in error state */ 194162306a36Sopenharmony_cistatic void smc_listen_out_err(struct smc_sock *new_smc) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci struct sock *newsmcsk = &new_smc->sk; 194462306a36Sopenharmony_ci struct net *net = sock_net(newsmcsk); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci this_cpu_inc(net->smc.smc_stats->srv_hshake_err_cnt); 194762306a36Sopenharmony_ci if (newsmcsk->sk_state == SMC_INIT) 194862306a36Sopenharmony_ci sock_put(&new_smc->sk); /* passive closing */ 194962306a36Sopenharmony_ci newsmcsk->sk_state = SMC_CLOSED; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci smc_listen_out(new_smc); 195262306a36Sopenharmony_ci} 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci/* listen worker: decline and fall back if possible */ 195562306a36Sopenharmony_cistatic void smc_listen_decline(struct smc_sock *new_smc, int reason_code, 195662306a36Sopenharmony_ci int local_first, u8 version) 195762306a36Sopenharmony_ci{ 195862306a36Sopenharmony_ci /* RDMA setup failed, switch back to TCP */ 195962306a36Sopenharmony_ci smc_conn_abort(new_smc, local_first); 196062306a36Sopenharmony_ci if (reason_code < 0 || 196162306a36Sopenharmony_ci smc_switch_to_fallback(new_smc, reason_code)) { 196262306a36Sopenharmony_ci /* error, no fallback possible */ 196362306a36Sopenharmony_ci smc_listen_out_err(new_smc); 196462306a36Sopenharmony_ci return; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci if (reason_code && reason_code != SMC_CLC_DECL_PEERDECL) { 196762306a36Sopenharmony_ci if (smc_clc_send_decline(new_smc, reason_code, version) < 0) { 196862306a36Sopenharmony_ci smc_listen_out_err(new_smc); 196962306a36Sopenharmony_ci return; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci smc_listen_out_connected(new_smc); 197362306a36Sopenharmony_ci} 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci/* listen worker: version checking */ 197662306a36Sopenharmony_cistatic int smc_listen_v2_check(struct smc_sock *new_smc, 197762306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc, 197862306a36Sopenharmony_ci struct smc_init_info *ini) 197962306a36Sopenharmony_ci{ 198062306a36Sopenharmony_ci struct smc_clc_smcd_v2_extension *pclc_smcd_v2_ext; 198162306a36Sopenharmony_ci struct smc_clc_v2_extension *pclc_v2_ext; 198262306a36Sopenharmony_ci int rc = SMC_CLC_DECL_PEERNOSMC; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci ini->smc_type_v1 = pclc->hdr.typev1; 198562306a36Sopenharmony_ci ini->smc_type_v2 = pclc->hdr.typev2; 198662306a36Sopenharmony_ci ini->smcd_version = smcd_indicated(ini->smc_type_v1) ? SMC_V1 : 0; 198762306a36Sopenharmony_ci ini->smcr_version = smcr_indicated(ini->smc_type_v1) ? SMC_V1 : 0; 198862306a36Sopenharmony_ci if (pclc->hdr.version > SMC_V1) { 198962306a36Sopenharmony_ci if (smcd_indicated(ini->smc_type_v2)) 199062306a36Sopenharmony_ci ini->smcd_version |= SMC_V2; 199162306a36Sopenharmony_ci if (smcr_indicated(ini->smc_type_v2)) 199262306a36Sopenharmony_ci ini->smcr_version |= SMC_V2; 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci if (!(ini->smcd_version & SMC_V2) && !(ini->smcr_version & SMC_V2)) { 199562306a36Sopenharmony_ci rc = SMC_CLC_DECL_PEERNOSMC; 199662306a36Sopenharmony_ci goto out; 199762306a36Sopenharmony_ci } 199862306a36Sopenharmony_ci pclc_v2_ext = smc_get_clc_v2_ext(pclc); 199962306a36Sopenharmony_ci if (!pclc_v2_ext) { 200062306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V2; 200162306a36Sopenharmony_ci ini->smcr_version &= ~SMC_V2; 200262306a36Sopenharmony_ci rc = SMC_CLC_DECL_NOV2EXT; 200362306a36Sopenharmony_ci goto out; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci pclc_smcd_v2_ext = smc_get_clc_smcd_v2_ext(pclc_v2_ext); 200662306a36Sopenharmony_ci if (ini->smcd_version & SMC_V2) { 200762306a36Sopenharmony_ci if (!smc_ism_is_v2_capable()) { 200862306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V2; 200962306a36Sopenharmony_ci rc = SMC_CLC_DECL_NOISM2SUPP; 201062306a36Sopenharmony_ci } else if (!pclc_smcd_v2_ext) { 201162306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V2; 201262306a36Sopenharmony_ci rc = SMC_CLC_DECL_NOV2DEXT; 201362306a36Sopenharmony_ci } else if (!pclc_v2_ext->hdr.eid_cnt && 201462306a36Sopenharmony_ci !pclc_v2_ext->hdr.flag.seid) { 201562306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V2; 201662306a36Sopenharmony_ci rc = SMC_CLC_DECL_NOUEID; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci if (ini->smcr_version & SMC_V2) { 202062306a36Sopenharmony_ci if (!pclc_v2_ext->hdr.eid_cnt) { 202162306a36Sopenharmony_ci ini->smcr_version &= ~SMC_V2; 202262306a36Sopenharmony_ci rc = SMC_CLC_DECL_NOUEID; 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci ini->release_nr = pclc_v2_ext->hdr.flag.release; 202762306a36Sopenharmony_ci if (pclc_v2_ext->hdr.flag.release > SMC_RELEASE) 202862306a36Sopenharmony_ci ini->release_nr = SMC_RELEASE; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ciout: 203162306a36Sopenharmony_ci if (!ini->smcd_version && !ini->smcr_version) 203262306a36Sopenharmony_ci return rc; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci return 0; 203562306a36Sopenharmony_ci} 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci/* listen worker: check prefixes */ 203862306a36Sopenharmony_cistatic int smc_listen_prfx_check(struct smc_sock *new_smc, 203962306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc) 204062306a36Sopenharmony_ci{ 204162306a36Sopenharmony_ci struct smc_clc_msg_proposal_prefix *pclc_prfx; 204262306a36Sopenharmony_ci struct socket *newclcsock = new_smc->clcsock; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci if (pclc->hdr.typev1 == SMC_TYPE_N) 204562306a36Sopenharmony_ci return 0; 204662306a36Sopenharmony_ci pclc_prfx = smc_clc_proposal_get_prefix(pclc); 204762306a36Sopenharmony_ci if (smc_clc_prfx_match(newclcsock, pclc_prfx)) 204862306a36Sopenharmony_ci return SMC_CLC_DECL_DIFFPREFIX; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci return 0; 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci/* listen worker: initialize connection and buffers */ 205462306a36Sopenharmony_cistatic int smc_listen_rdma_init(struct smc_sock *new_smc, 205562306a36Sopenharmony_ci struct smc_init_info *ini) 205662306a36Sopenharmony_ci{ 205762306a36Sopenharmony_ci int rc; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci /* allocate connection / link group */ 206062306a36Sopenharmony_ci rc = smc_conn_create(new_smc, ini); 206162306a36Sopenharmony_ci if (rc) 206262306a36Sopenharmony_ci return rc; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci /* create send buffer and rmb */ 206562306a36Sopenharmony_ci if (smc_buf_create(new_smc, false)) { 206662306a36Sopenharmony_ci smc_conn_abort(new_smc, ini->first_contact_local); 206762306a36Sopenharmony_ci return SMC_CLC_DECL_MEM; 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci return 0; 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci/* listen worker: initialize connection and buffers for SMC-D */ 207462306a36Sopenharmony_cistatic int smc_listen_ism_init(struct smc_sock *new_smc, 207562306a36Sopenharmony_ci struct smc_init_info *ini) 207662306a36Sopenharmony_ci{ 207762306a36Sopenharmony_ci int rc; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci rc = smc_conn_create(new_smc, ini); 208062306a36Sopenharmony_ci if (rc) 208162306a36Sopenharmony_ci return rc; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* Create send and receive buffers */ 208462306a36Sopenharmony_ci rc = smc_buf_create(new_smc, true); 208562306a36Sopenharmony_ci if (rc) { 208662306a36Sopenharmony_ci smc_conn_abort(new_smc, ini->first_contact_local); 208762306a36Sopenharmony_ci return (rc == -ENOSPC) ? SMC_CLC_DECL_MAX_DMB : 208862306a36Sopenharmony_ci SMC_CLC_DECL_MEM; 208962306a36Sopenharmony_ci } 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci return 0; 209262306a36Sopenharmony_ci} 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_cistatic bool smc_is_already_selected(struct smcd_dev *smcd, 209562306a36Sopenharmony_ci struct smc_init_info *ini, 209662306a36Sopenharmony_ci int matches) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci int i; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci for (i = 0; i < matches; i++) 210162306a36Sopenharmony_ci if (smcd == ini->ism_dev[i]) 210262306a36Sopenharmony_ci return true; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci return false; 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci/* check for ISM devices matching proposed ISM devices */ 210862306a36Sopenharmony_cistatic void smc_check_ism_v2_match(struct smc_init_info *ini, 210962306a36Sopenharmony_ci u16 proposed_chid, u64 proposed_gid, 211062306a36Sopenharmony_ci unsigned int *matches) 211162306a36Sopenharmony_ci{ 211262306a36Sopenharmony_ci struct smcd_dev *smcd; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci list_for_each_entry(smcd, &smcd_dev_list.list, list) { 211562306a36Sopenharmony_ci if (smcd->going_away) 211662306a36Sopenharmony_ci continue; 211762306a36Sopenharmony_ci if (smc_is_already_selected(smcd, ini, *matches)) 211862306a36Sopenharmony_ci continue; 211962306a36Sopenharmony_ci if (smc_ism_get_chid(smcd) == proposed_chid && 212062306a36Sopenharmony_ci !smc_ism_cantalk(proposed_gid, ISM_RESERVED_VLANID, smcd)) { 212162306a36Sopenharmony_ci ini->ism_peer_gid[*matches] = proposed_gid; 212262306a36Sopenharmony_ci ini->ism_dev[*matches] = smcd; 212362306a36Sopenharmony_ci (*matches)++; 212462306a36Sopenharmony_ci break; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci} 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic void smc_find_ism_store_rc(u32 rc, struct smc_init_info *ini) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci if (!ini->rc) 213262306a36Sopenharmony_ci ini->rc = rc; 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cistatic void smc_find_ism_v2_device_serv(struct smc_sock *new_smc, 213662306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc, 213762306a36Sopenharmony_ci struct smc_init_info *ini) 213862306a36Sopenharmony_ci{ 213962306a36Sopenharmony_ci struct smc_clc_smcd_v2_extension *smcd_v2_ext; 214062306a36Sopenharmony_ci struct smc_clc_v2_extension *smc_v2_ext; 214162306a36Sopenharmony_ci struct smc_clc_msg_smcd *pclc_smcd; 214262306a36Sopenharmony_ci unsigned int matches = 0; 214362306a36Sopenharmony_ci u8 smcd_version; 214462306a36Sopenharmony_ci u8 *eid = NULL; 214562306a36Sopenharmony_ci int i, rc; 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci if (!(ini->smcd_version & SMC_V2) || !smcd_indicated(ini->smc_type_v2)) 214862306a36Sopenharmony_ci goto not_found; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci pclc_smcd = smc_get_clc_msg_smcd(pclc); 215162306a36Sopenharmony_ci smc_v2_ext = smc_get_clc_v2_ext(pclc); 215262306a36Sopenharmony_ci smcd_v2_ext = smc_get_clc_smcd_v2_ext(smc_v2_ext); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci mutex_lock(&smcd_dev_list.mutex); 215562306a36Sopenharmony_ci if (pclc_smcd->ism.chid) 215662306a36Sopenharmony_ci /* check for ISM device matching proposed native ISM device */ 215762306a36Sopenharmony_ci smc_check_ism_v2_match(ini, ntohs(pclc_smcd->ism.chid), 215862306a36Sopenharmony_ci ntohll(pclc_smcd->ism.gid), &matches); 215962306a36Sopenharmony_ci for (i = 1; i <= smc_v2_ext->hdr.ism_gid_cnt; i++) { 216062306a36Sopenharmony_ci /* check for ISM devices matching proposed non-native ISM 216162306a36Sopenharmony_ci * devices 216262306a36Sopenharmony_ci */ 216362306a36Sopenharmony_ci smc_check_ism_v2_match(ini, 216462306a36Sopenharmony_ci ntohs(smcd_v2_ext->gidchid[i - 1].chid), 216562306a36Sopenharmony_ci ntohll(smcd_v2_ext->gidchid[i - 1].gid), 216662306a36Sopenharmony_ci &matches); 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci mutex_unlock(&smcd_dev_list.mutex); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci if (!ini->ism_dev[0]) { 217162306a36Sopenharmony_ci smc_find_ism_store_rc(SMC_CLC_DECL_NOSMCD2DEV, ini); 217262306a36Sopenharmony_ci goto not_found; 217362306a36Sopenharmony_ci } 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci smc_ism_get_system_eid(&eid); 217662306a36Sopenharmony_ci if (!smc_clc_match_eid(ini->negotiated_eid, smc_v2_ext, 217762306a36Sopenharmony_ci smcd_v2_ext->system_eid, eid)) 217862306a36Sopenharmony_ci goto not_found; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci /* separate - outside the smcd_dev_list.lock */ 218162306a36Sopenharmony_ci smcd_version = ini->smcd_version; 218262306a36Sopenharmony_ci for (i = 0; i < matches; i++) { 218362306a36Sopenharmony_ci ini->smcd_version = SMC_V2; 218462306a36Sopenharmony_ci ini->is_smcd = true; 218562306a36Sopenharmony_ci ini->ism_selected = i; 218662306a36Sopenharmony_ci rc = smc_listen_ism_init(new_smc, ini); 218762306a36Sopenharmony_ci if (rc) { 218862306a36Sopenharmony_ci smc_find_ism_store_rc(rc, ini); 218962306a36Sopenharmony_ci /* try next active ISM device */ 219062306a36Sopenharmony_ci continue; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci return; /* matching and usable V2 ISM device found */ 219362306a36Sopenharmony_ci } 219462306a36Sopenharmony_ci /* no V2 ISM device could be initialized */ 219562306a36Sopenharmony_ci ini->smcd_version = smcd_version; /* restore original value */ 219662306a36Sopenharmony_ci ini->negotiated_eid[0] = 0; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_cinot_found: 219962306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V2; 220062306a36Sopenharmony_ci ini->ism_dev[0] = NULL; 220162306a36Sopenharmony_ci ini->is_smcd = false; 220262306a36Sopenharmony_ci} 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_cistatic void smc_find_ism_v1_device_serv(struct smc_sock *new_smc, 220562306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc, 220662306a36Sopenharmony_ci struct smc_init_info *ini) 220762306a36Sopenharmony_ci{ 220862306a36Sopenharmony_ci struct smc_clc_msg_smcd *pclc_smcd = smc_get_clc_msg_smcd(pclc); 220962306a36Sopenharmony_ci int rc = 0; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci /* check if ISM V1 is available */ 221262306a36Sopenharmony_ci if (!(ini->smcd_version & SMC_V1) || !smcd_indicated(ini->smc_type_v1)) 221362306a36Sopenharmony_ci goto not_found; 221462306a36Sopenharmony_ci ini->is_smcd = true; /* prepare ISM check */ 221562306a36Sopenharmony_ci ini->ism_peer_gid[0] = ntohll(pclc_smcd->ism.gid); 221662306a36Sopenharmony_ci rc = smc_find_ism_device(new_smc, ini); 221762306a36Sopenharmony_ci if (rc) 221862306a36Sopenharmony_ci goto not_found; 221962306a36Sopenharmony_ci ini->ism_selected = 0; 222062306a36Sopenharmony_ci rc = smc_listen_ism_init(new_smc, ini); 222162306a36Sopenharmony_ci if (!rc) 222262306a36Sopenharmony_ci return; /* V1 ISM device found */ 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_cinot_found: 222562306a36Sopenharmony_ci smc_find_ism_store_rc(rc, ini); 222662306a36Sopenharmony_ci ini->smcd_version &= ~SMC_V1; 222762306a36Sopenharmony_ci ini->ism_dev[0] = NULL; 222862306a36Sopenharmony_ci ini->is_smcd = false; 222962306a36Sopenharmony_ci} 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci/* listen worker: register buffers */ 223262306a36Sopenharmony_cistatic int smc_listen_rdma_reg(struct smc_sock *new_smc, bool local_first) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct smc_connection *conn = &new_smc->conn; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci if (!local_first) { 223762306a36Sopenharmony_ci /* reg sendbufs if they were vzalloced */ 223862306a36Sopenharmony_ci if (conn->sndbuf_desc->is_vm) { 223962306a36Sopenharmony_ci if (smcr_lgr_reg_sndbufs(conn->lnk, 224062306a36Sopenharmony_ci conn->sndbuf_desc)) 224162306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_REGBUF; 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci if (smcr_lgr_reg_rmbs(conn->lnk, conn->rmb_desc)) 224462306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_REGBUF; 224562306a36Sopenharmony_ci } 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci return 0; 224862306a36Sopenharmony_ci} 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_cistatic void smc_find_rdma_v2_device_serv(struct smc_sock *new_smc, 225162306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc, 225262306a36Sopenharmony_ci struct smc_init_info *ini) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci struct smc_clc_v2_extension *smc_v2_ext; 225562306a36Sopenharmony_ci u8 smcr_version; 225662306a36Sopenharmony_ci int rc; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (!(ini->smcr_version & SMC_V2) || !smcr_indicated(ini->smc_type_v2)) 225962306a36Sopenharmony_ci goto not_found; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci smc_v2_ext = smc_get_clc_v2_ext(pclc); 226262306a36Sopenharmony_ci if (!smc_clc_match_eid(ini->negotiated_eid, smc_v2_ext, NULL, NULL)) 226362306a36Sopenharmony_ci goto not_found; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci /* prepare RDMA check */ 226662306a36Sopenharmony_ci memcpy(ini->peer_systemid, pclc->lcl.id_for_peer, SMC_SYSTEMID_LEN); 226762306a36Sopenharmony_ci memcpy(ini->peer_gid, smc_v2_ext->roce, SMC_GID_SIZE); 226862306a36Sopenharmony_ci memcpy(ini->peer_mac, pclc->lcl.mac, ETH_ALEN); 226962306a36Sopenharmony_ci ini->check_smcrv2 = true; 227062306a36Sopenharmony_ci ini->smcrv2.clc_sk = new_smc->clcsock->sk; 227162306a36Sopenharmony_ci ini->smcrv2.saddr = new_smc->clcsock->sk->sk_rcv_saddr; 227262306a36Sopenharmony_ci ini->smcrv2.daddr = smc_ib_gid_to_ipv4(smc_v2_ext->roce); 227362306a36Sopenharmony_ci rc = smc_find_rdma_device(new_smc, ini); 227462306a36Sopenharmony_ci if (rc) { 227562306a36Sopenharmony_ci smc_find_ism_store_rc(rc, ini); 227662306a36Sopenharmony_ci goto not_found; 227762306a36Sopenharmony_ci } 227862306a36Sopenharmony_ci if (!ini->smcrv2.uses_gateway) 227962306a36Sopenharmony_ci memcpy(ini->smcrv2.nexthop_mac, pclc->lcl.mac, ETH_ALEN); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci smcr_version = ini->smcr_version; 228262306a36Sopenharmony_ci ini->smcr_version = SMC_V2; 228362306a36Sopenharmony_ci rc = smc_listen_rdma_init(new_smc, ini); 228462306a36Sopenharmony_ci if (!rc) { 228562306a36Sopenharmony_ci rc = smc_listen_rdma_reg(new_smc, ini->first_contact_local); 228662306a36Sopenharmony_ci if (rc) 228762306a36Sopenharmony_ci smc_conn_abort(new_smc, ini->first_contact_local); 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci if (!rc) 229062306a36Sopenharmony_ci return; 229162306a36Sopenharmony_ci ini->smcr_version = smcr_version; 229262306a36Sopenharmony_ci smc_find_ism_store_rc(rc, ini); 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_cinot_found: 229562306a36Sopenharmony_ci ini->smcr_version &= ~SMC_V2; 229662306a36Sopenharmony_ci ini->smcrv2.ib_dev_v2 = NULL; 229762306a36Sopenharmony_ci ini->check_smcrv2 = false; 229862306a36Sopenharmony_ci} 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_cistatic int smc_find_rdma_v1_device_serv(struct smc_sock *new_smc, 230162306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc, 230262306a36Sopenharmony_ci struct smc_init_info *ini) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci int rc; 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci if (!(ini->smcr_version & SMC_V1) || !smcr_indicated(ini->smc_type_v1)) 230762306a36Sopenharmony_ci return SMC_CLC_DECL_NOSMCDEV; 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci /* prepare RDMA check */ 231062306a36Sopenharmony_ci memcpy(ini->peer_systemid, pclc->lcl.id_for_peer, SMC_SYSTEMID_LEN); 231162306a36Sopenharmony_ci memcpy(ini->peer_gid, pclc->lcl.gid, SMC_GID_SIZE); 231262306a36Sopenharmony_ci memcpy(ini->peer_mac, pclc->lcl.mac, ETH_ALEN); 231362306a36Sopenharmony_ci rc = smc_find_rdma_device(new_smc, ini); 231462306a36Sopenharmony_ci if (rc) { 231562306a36Sopenharmony_ci /* no RDMA device found */ 231662306a36Sopenharmony_ci return SMC_CLC_DECL_NOSMCDEV; 231762306a36Sopenharmony_ci } 231862306a36Sopenharmony_ci rc = smc_listen_rdma_init(new_smc, ini); 231962306a36Sopenharmony_ci if (rc) 232062306a36Sopenharmony_ci return rc; 232162306a36Sopenharmony_ci return smc_listen_rdma_reg(new_smc, ini->first_contact_local); 232262306a36Sopenharmony_ci} 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci/* determine the local device matching to proposal */ 232562306a36Sopenharmony_cistatic int smc_listen_find_device(struct smc_sock *new_smc, 232662306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc, 232762306a36Sopenharmony_ci struct smc_init_info *ini) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci int prfx_rc; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci /* check for ISM device matching V2 proposed device */ 233262306a36Sopenharmony_ci smc_find_ism_v2_device_serv(new_smc, pclc, ini); 233362306a36Sopenharmony_ci if (ini->ism_dev[0]) 233462306a36Sopenharmony_ci return 0; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci /* check for matching IP prefix and subnet length (V1) */ 233762306a36Sopenharmony_ci prfx_rc = smc_listen_prfx_check(new_smc, pclc); 233862306a36Sopenharmony_ci if (prfx_rc) 233962306a36Sopenharmony_ci smc_find_ism_store_rc(prfx_rc, ini); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci /* get vlan id from IP device */ 234262306a36Sopenharmony_ci if (smc_vlan_by_tcpsk(new_smc->clcsock, ini)) 234362306a36Sopenharmony_ci return ini->rc ?: SMC_CLC_DECL_GETVLANERR; 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci /* check for ISM device matching V1 proposed device */ 234662306a36Sopenharmony_ci if (!prfx_rc) 234762306a36Sopenharmony_ci smc_find_ism_v1_device_serv(new_smc, pclc, ini); 234862306a36Sopenharmony_ci if (ini->ism_dev[0]) 234962306a36Sopenharmony_ci return 0; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci if (!smcr_indicated(pclc->hdr.typev1) && 235262306a36Sopenharmony_ci !smcr_indicated(pclc->hdr.typev2)) 235362306a36Sopenharmony_ci /* skip RDMA and decline */ 235462306a36Sopenharmony_ci return ini->rc ?: SMC_CLC_DECL_NOSMCDDEV; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci /* check if RDMA V2 is available */ 235762306a36Sopenharmony_ci smc_find_rdma_v2_device_serv(new_smc, pclc, ini); 235862306a36Sopenharmony_ci if (ini->smcrv2.ib_dev_v2) 235962306a36Sopenharmony_ci return 0; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci /* check if RDMA V1 is available */ 236262306a36Sopenharmony_ci if (!prfx_rc) { 236362306a36Sopenharmony_ci int rc; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci rc = smc_find_rdma_v1_device_serv(new_smc, pclc, ini); 236662306a36Sopenharmony_ci smc_find_ism_store_rc(rc, ini); 236762306a36Sopenharmony_ci return (!rc) ? 0 : ini->rc; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci return prfx_rc; 237062306a36Sopenharmony_ci} 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci/* listen worker: finish RDMA setup */ 237362306a36Sopenharmony_cistatic int smc_listen_rdma_finish(struct smc_sock *new_smc, 237462306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *cclc, 237562306a36Sopenharmony_ci bool local_first, 237662306a36Sopenharmony_ci struct smc_init_info *ini) 237762306a36Sopenharmony_ci{ 237862306a36Sopenharmony_ci struct smc_link *link = new_smc->conn.lnk; 237962306a36Sopenharmony_ci int reason_code = 0; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci if (local_first) 238262306a36Sopenharmony_ci smc_link_save_peer_info(link, cclc, ini); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci if (smc_rmb_rtoken_handling(&new_smc->conn, link, cclc)) 238562306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_RTOK; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci if (local_first) { 238862306a36Sopenharmony_ci if (smc_ib_ready_link(link)) 238962306a36Sopenharmony_ci return SMC_CLC_DECL_ERR_RDYLNK; 239062306a36Sopenharmony_ci /* QP confirmation over RoCE fabric */ 239162306a36Sopenharmony_ci smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK); 239262306a36Sopenharmony_ci reason_code = smcr_serv_conf_first_link(new_smc); 239362306a36Sopenharmony_ci smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl); 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ci return reason_code; 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci/* setup for connection of server */ 239962306a36Sopenharmony_cistatic void smc_listen_work(struct work_struct *work) 240062306a36Sopenharmony_ci{ 240162306a36Sopenharmony_ci struct smc_sock *new_smc = container_of(work, struct smc_sock, 240262306a36Sopenharmony_ci smc_listen_work); 240362306a36Sopenharmony_ci struct socket *newclcsock = new_smc->clcsock; 240462306a36Sopenharmony_ci struct smc_clc_msg_accept_confirm *cclc; 240562306a36Sopenharmony_ci struct smc_clc_msg_proposal_area *buf; 240662306a36Sopenharmony_ci struct smc_clc_msg_proposal *pclc; 240762306a36Sopenharmony_ci struct smc_init_info *ini = NULL; 240862306a36Sopenharmony_ci u8 proposal_version = SMC_V1; 240962306a36Sopenharmony_ci u8 accept_version; 241062306a36Sopenharmony_ci int rc = 0; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci if (new_smc->listen_smc->sk.sk_state != SMC_LISTEN) 241362306a36Sopenharmony_ci return smc_listen_out_err(new_smc); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci if (new_smc->use_fallback) { 241662306a36Sopenharmony_ci smc_listen_out_connected(new_smc); 241762306a36Sopenharmony_ci return; 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci /* check if peer is smc capable */ 242162306a36Sopenharmony_ci if (!tcp_sk(newclcsock->sk)->syn_smc) { 242262306a36Sopenharmony_ci rc = smc_switch_to_fallback(new_smc, SMC_CLC_DECL_PEERNOSMC); 242362306a36Sopenharmony_ci if (rc) 242462306a36Sopenharmony_ci smc_listen_out_err(new_smc); 242562306a36Sopenharmony_ci else 242662306a36Sopenharmony_ci smc_listen_out_connected(new_smc); 242762306a36Sopenharmony_ci return; 242862306a36Sopenharmony_ci } 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci /* do inband token exchange - 243162306a36Sopenharmony_ci * wait for and receive SMC Proposal CLC message 243262306a36Sopenharmony_ci */ 243362306a36Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 243462306a36Sopenharmony_ci if (!buf) { 243562306a36Sopenharmony_ci rc = SMC_CLC_DECL_MEM; 243662306a36Sopenharmony_ci goto out_decl; 243762306a36Sopenharmony_ci } 243862306a36Sopenharmony_ci pclc = (struct smc_clc_msg_proposal *)buf; 243962306a36Sopenharmony_ci rc = smc_clc_wait_msg(new_smc, pclc, sizeof(*buf), 244062306a36Sopenharmony_ci SMC_CLC_PROPOSAL, CLC_WAIT_TIME); 244162306a36Sopenharmony_ci if (rc) 244262306a36Sopenharmony_ci goto out_decl; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci if (pclc->hdr.version > SMC_V1) 244562306a36Sopenharmony_ci proposal_version = SMC_V2; 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci /* IPSec connections opt out of SMC optimizations */ 244862306a36Sopenharmony_ci if (using_ipsec(new_smc)) { 244962306a36Sopenharmony_ci rc = SMC_CLC_DECL_IPSEC; 245062306a36Sopenharmony_ci goto out_decl; 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci ini = kzalloc(sizeof(*ini), GFP_KERNEL); 245462306a36Sopenharmony_ci if (!ini) { 245562306a36Sopenharmony_ci rc = SMC_CLC_DECL_MEM; 245662306a36Sopenharmony_ci goto out_decl; 245762306a36Sopenharmony_ci } 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* initial version checking */ 246062306a36Sopenharmony_ci rc = smc_listen_v2_check(new_smc, pclc, ini); 246162306a36Sopenharmony_ci if (rc) 246262306a36Sopenharmony_ci goto out_decl; 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci rc = smc_clc_srv_v2x_features_validate(pclc, ini); 246562306a36Sopenharmony_ci if (rc) 246662306a36Sopenharmony_ci goto out_decl; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci mutex_lock(&smc_server_lgr_pending); 246962306a36Sopenharmony_ci smc_close_init(new_smc); 247062306a36Sopenharmony_ci smc_rx_init(new_smc); 247162306a36Sopenharmony_ci smc_tx_init(new_smc); 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci /* determine ISM or RoCE device used for connection */ 247462306a36Sopenharmony_ci rc = smc_listen_find_device(new_smc, pclc, ini); 247562306a36Sopenharmony_ci if (rc) 247662306a36Sopenharmony_ci goto out_unlock; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci /* send SMC Accept CLC message */ 247962306a36Sopenharmony_ci accept_version = ini->is_smcd ? ini->smcd_version : ini->smcr_version; 248062306a36Sopenharmony_ci rc = smc_clc_send_accept(new_smc, ini->first_contact_local, 248162306a36Sopenharmony_ci accept_version, ini->negotiated_eid, ini); 248262306a36Sopenharmony_ci if (rc) 248362306a36Sopenharmony_ci goto out_unlock; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci /* SMC-D does not need this lock any more */ 248662306a36Sopenharmony_ci if (ini->is_smcd) 248762306a36Sopenharmony_ci mutex_unlock(&smc_server_lgr_pending); 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci /* receive SMC Confirm CLC message */ 249062306a36Sopenharmony_ci memset(buf, 0, sizeof(*buf)); 249162306a36Sopenharmony_ci cclc = (struct smc_clc_msg_accept_confirm *)buf; 249262306a36Sopenharmony_ci rc = smc_clc_wait_msg(new_smc, cclc, sizeof(*buf), 249362306a36Sopenharmony_ci SMC_CLC_CONFIRM, CLC_WAIT_TIME); 249462306a36Sopenharmony_ci if (rc) { 249562306a36Sopenharmony_ci if (!ini->is_smcd) 249662306a36Sopenharmony_ci goto out_unlock; 249762306a36Sopenharmony_ci goto out_decl; 249862306a36Sopenharmony_ci } 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci rc = smc_clc_v2x_features_confirm_check(cclc, ini); 250162306a36Sopenharmony_ci if (rc) { 250262306a36Sopenharmony_ci if (!ini->is_smcd) 250362306a36Sopenharmony_ci goto out_unlock; 250462306a36Sopenharmony_ci goto out_decl; 250562306a36Sopenharmony_ci } 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci /* fce smc release version is needed in smc_listen_rdma_finish, 250862306a36Sopenharmony_ci * so save fce info here. 250962306a36Sopenharmony_ci */ 251062306a36Sopenharmony_ci smc_conn_save_peer_info_fce(new_smc, cclc); 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci /* finish worker */ 251362306a36Sopenharmony_ci if (!ini->is_smcd) { 251462306a36Sopenharmony_ci rc = smc_listen_rdma_finish(new_smc, cclc, 251562306a36Sopenharmony_ci ini->first_contact_local, ini); 251662306a36Sopenharmony_ci if (rc) 251762306a36Sopenharmony_ci goto out_unlock; 251862306a36Sopenharmony_ci mutex_unlock(&smc_server_lgr_pending); 251962306a36Sopenharmony_ci } 252062306a36Sopenharmony_ci smc_conn_save_peer_info(new_smc, cclc); 252162306a36Sopenharmony_ci smc_listen_out_connected(new_smc); 252262306a36Sopenharmony_ci SMC_STAT_SERV_SUCC_INC(sock_net(newclcsock->sk), ini); 252362306a36Sopenharmony_ci goto out_free; 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ciout_unlock: 252662306a36Sopenharmony_ci mutex_unlock(&smc_server_lgr_pending); 252762306a36Sopenharmony_ciout_decl: 252862306a36Sopenharmony_ci smc_listen_decline(new_smc, rc, ini ? ini->first_contact_local : 0, 252962306a36Sopenharmony_ci proposal_version); 253062306a36Sopenharmony_ciout_free: 253162306a36Sopenharmony_ci kfree(ini); 253262306a36Sopenharmony_ci kfree(buf); 253362306a36Sopenharmony_ci} 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_cistatic void smc_tcp_listen_work(struct work_struct *work) 253662306a36Sopenharmony_ci{ 253762306a36Sopenharmony_ci struct smc_sock *lsmc = container_of(work, struct smc_sock, 253862306a36Sopenharmony_ci tcp_listen_work); 253962306a36Sopenharmony_ci struct sock *lsk = &lsmc->sk; 254062306a36Sopenharmony_ci struct smc_sock *new_smc; 254162306a36Sopenharmony_ci int rc = 0; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci lock_sock(lsk); 254462306a36Sopenharmony_ci while (lsk->sk_state == SMC_LISTEN) { 254562306a36Sopenharmony_ci rc = smc_clcsock_accept(lsmc, &new_smc); 254662306a36Sopenharmony_ci if (rc) /* clcsock accept queue empty or error */ 254762306a36Sopenharmony_ci goto out; 254862306a36Sopenharmony_ci if (!new_smc) 254962306a36Sopenharmony_ci continue; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci if (tcp_sk(new_smc->clcsock->sk)->syn_smc) 255262306a36Sopenharmony_ci atomic_inc(&lsmc->queued_smc_hs); 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci new_smc->listen_smc = lsmc; 255562306a36Sopenharmony_ci new_smc->use_fallback = lsmc->use_fallback; 255662306a36Sopenharmony_ci new_smc->fallback_rsn = lsmc->fallback_rsn; 255762306a36Sopenharmony_ci sock_hold(lsk); /* sock_put in smc_listen_work */ 255862306a36Sopenharmony_ci INIT_WORK(&new_smc->smc_listen_work, smc_listen_work); 255962306a36Sopenharmony_ci smc_copy_sock_settings_to_smc(new_smc); 256062306a36Sopenharmony_ci sock_hold(&new_smc->sk); /* sock_put in passive closing */ 256162306a36Sopenharmony_ci if (!queue_work(smc_hs_wq, &new_smc->smc_listen_work)) 256262306a36Sopenharmony_ci sock_put(&new_smc->sk); 256362306a36Sopenharmony_ci } 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ciout: 256662306a36Sopenharmony_ci release_sock(lsk); 256762306a36Sopenharmony_ci sock_put(&lsmc->sk); /* sock_hold in smc_clcsock_data_ready() */ 256862306a36Sopenharmony_ci} 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_cistatic void smc_clcsock_data_ready(struct sock *listen_clcsock) 257162306a36Sopenharmony_ci{ 257262306a36Sopenharmony_ci struct smc_sock *lsmc; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci read_lock_bh(&listen_clcsock->sk_callback_lock); 257562306a36Sopenharmony_ci lsmc = smc_clcsock_user_data(listen_clcsock); 257662306a36Sopenharmony_ci if (!lsmc) 257762306a36Sopenharmony_ci goto out; 257862306a36Sopenharmony_ci lsmc->clcsk_data_ready(listen_clcsock); 257962306a36Sopenharmony_ci if (lsmc->sk.sk_state == SMC_LISTEN) { 258062306a36Sopenharmony_ci sock_hold(&lsmc->sk); /* sock_put in smc_tcp_listen_work() */ 258162306a36Sopenharmony_ci if (!queue_work(smc_tcp_ls_wq, &lsmc->tcp_listen_work)) 258262306a36Sopenharmony_ci sock_put(&lsmc->sk); 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ciout: 258562306a36Sopenharmony_ci read_unlock_bh(&listen_clcsock->sk_callback_lock); 258662306a36Sopenharmony_ci} 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_cistatic int smc_listen(struct socket *sock, int backlog) 258962306a36Sopenharmony_ci{ 259062306a36Sopenharmony_ci struct sock *sk = sock->sk; 259162306a36Sopenharmony_ci struct smc_sock *smc; 259262306a36Sopenharmony_ci int rc; 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci smc = smc_sk(sk); 259562306a36Sopenharmony_ci lock_sock(sk); 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci rc = -EINVAL; 259862306a36Sopenharmony_ci if ((sk->sk_state != SMC_INIT && sk->sk_state != SMC_LISTEN) || 259962306a36Sopenharmony_ci smc->connect_nonblock || sock->state != SS_UNCONNECTED) 260062306a36Sopenharmony_ci goto out; 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci rc = 0; 260362306a36Sopenharmony_ci if (sk->sk_state == SMC_LISTEN) { 260462306a36Sopenharmony_ci sk->sk_max_ack_backlog = backlog; 260562306a36Sopenharmony_ci goto out; 260662306a36Sopenharmony_ci } 260762306a36Sopenharmony_ci /* some socket options are handled in core, so we could not apply 260862306a36Sopenharmony_ci * them to the clc socket -- copy smc socket options to clc socket 260962306a36Sopenharmony_ci */ 261062306a36Sopenharmony_ci smc_copy_sock_settings_to_clc(smc); 261162306a36Sopenharmony_ci if (!smc->use_fallback) 261262306a36Sopenharmony_ci tcp_sk(smc->clcsock->sk)->syn_smc = 1; 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci /* save original sk_data_ready function and establish 261562306a36Sopenharmony_ci * smc-specific sk_data_ready function 261662306a36Sopenharmony_ci */ 261762306a36Sopenharmony_ci write_lock_bh(&smc->clcsock->sk->sk_callback_lock); 261862306a36Sopenharmony_ci smc->clcsock->sk->sk_user_data = 261962306a36Sopenharmony_ci (void *)((uintptr_t)smc | SK_USER_DATA_NOCOPY); 262062306a36Sopenharmony_ci smc_clcsock_replace_cb(&smc->clcsock->sk->sk_data_ready, 262162306a36Sopenharmony_ci smc_clcsock_data_ready, &smc->clcsk_data_ready); 262262306a36Sopenharmony_ci write_unlock_bh(&smc->clcsock->sk->sk_callback_lock); 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci /* save original ops */ 262562306a36Sopenharmony_ci smc->ori_af_ops = inet_csk(smc->clcsock->sk)->icsk_af_ops; 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci smc->af_ops = *smc->ori_af_ops; 262862306a36Sopenharmony_ci smc->af_ops.syn_recv_sock = smc_tcp_syn_recv_sock; 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci inet_csk(smc->clcsock->sk)->icsk_af_ops = &smc->af_ops; 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci if (smc->limit_smc_hs) 263362306a36Sopenharmony_ci tcp_sk(smc->clcsock->sk)->smc_hs_congested = smc_hs_congested; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci rc = kernel_listen(smc->clcsock, backlog); 263662306a36Sopenharmony_ci if (rc) { 263762306a36Sopenharmony_ci write_lock_bh(&smc->clcsock->sk->sk_callback_lock); 263862306a36Sopenharmony_ci smc_clcsock_restore_cb(&smc->clcsock->sk->sk_data_ready, 263962306a36Sopenharmony_ci &smc->clcsk_data_ready); 264062306a36Sopenharmony_ci smc->clcsock->sk->sk_user_data = NULL; 264162306a36Sopenharmony_ci write_unlock_bh(&smc->clcsock->sk->sk_callback_lock); 264262306a36Sopenharmony_ci goto out; 264362306a36Sopenharmony_ci } 264462306a36Sopenharmony_ci sk->sk_max_ack_backlog = backlog; 264562306a36Sopenharmony_ci sk->sk_ack_backlog = 0; 264662306a36Sopenharmony_ci sk->sk_state = SMC_LISTEN; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ciout: 264962306a36Sopenharmony_ci release_sock(sk); 265062306a36Sopenharmony_ci return rc; 265162306a36Sopenharmony_ci} 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_cistatic int smc_accept(struct socket *sock, struct socket *new_sock, 265462306a36Sopenharmony_ci int flags, bool kern) 265562306a36Sopenharmony_ci{ 265662306a36Sopenharmony_ci struct sock *sk = sock->sk, *nsk; 265762306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 265862306a36Sopenharmony_ci struct smc_sock *lsmc; 265962306a36Sopenharmony_ci long timeo; 266062306a36Sopenharmony_ci int rc = 0; 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci lsmc = smc_sk(sk); 266362306a36Sopenharmony_ci sock_hold(sk); /* sock_put below */ 266462306a36Sopenharmony_ci lock_sock(sk); 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci if (lsmc->sk.sk_state != SMC_LISTEN) { 266762306a36Sopenharmony_ci rc = -EINVAL; 266862306a36Sopenharmony_ci release_sock(sk); 266962306a36Sopenharmony_ci goto out; 267062306a36Sopenharmony_ci } 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci /* Wait for an incoming connection */ 267362306a36Sopenharmony_ci timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); 267462306a36Sopenharmony_ci add_wait_queue_exclusive(sk_sleep(sk), &wait); 267562306a36Sopenharmony_ci while (!(nsk = smc_accept_dequeue(sk, new_sock))) { 267662306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 267762306a36Sopenharmony_ci if (!timeo) { 267862306a36Sopenharmony_ci rc = -EAGAIN; 267962306a36Sopenharmony_ci break; 268062306a36Sopenharmony_ci } 268162306a36Sopenharmony_ci release_sock(sk); 268262306a36Sopenharmony_ci timeo = schedule_timeout(timeo); 268362306a36Sopenharmony_ci /* wakeup by sk_data_ready in smc_listen_work() */ 268462306a36Sopenharmony_ci sched_annotate_sleep(); 268562306a36Sopenharmony_ci lock_sock(sk); 268662306a36Sopenharmony_ci if (signal_pending(current)) { 268762306a36Sopenharmony_ci rc = sock_intr_errno(timeo); 268862306a36Sopenharmony_ci break; 268962306a36Sopenharmony_ci } 269062306a36Sopenharmony_ci } 269162306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 269262306a36Sopenharmony_ci remove_wait_queue(sk_sleep(sk), &wait); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci if (!rc) 269562306a36Sopenharmony_ci rc = sock_error(nsk); 269662306a36Sopenharmony_ci release_sock(sk); 269762306a36Sopenharmony_ci if (rc) 269862306a36Sopenharmony_ci goto out; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci if (lsmc->sockopt_defer_accept && !(flags & O_NONBLOCK)) { 270162306a36Sopenharmony_ci /* wait till data arrives on the socket */ 270262306a36Sopenharmony_ci timeo = msecs_to_jiffies(lsmc->sockopt_defer_accept * 270362306a36Sopenharmony_ci MSEC_PER_SEC); 270462306a36Sopenharmony_ci if (smc_sk(nsk)->use_fallback) { 270562306a36Sopenharmony_ci struct sock *clcsk = smc_sk(nsk)->clcsock->sk; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci lock_sock(clcsk); 270862306a36Sopenharmony_ci if (skb_queue_empty(&clcsk->sk_receive_queue)) 270962306a36Sopenharmony_ci sk_wait_data(clcsk, &timeo, NULL); 271062306a36Sopenharmony_ci release_sock(clcsk); 271162306a36Sopenharmony_ci } else if (!atomic_read(&smc_sk(nsk)->conn.bytes_to_rcv)) { 271262306a36Sopenharmony_ci lock_sock(nsk); 271362306a36Sopenharmony_ci smc_rx_wait(smc_sk(nsk), &timeo, smc_rx_data_available); 271462306a36Sopenharmony_ci release_sock(nsk); 271562306a36Sopenharmony_ci } 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ciout: 271962306a36Sopenharmony_ci sock_put(sk); /* sock_hold above */ 272062306a36Sopenharmony_ci return rc; 272162306a36Sopenharmony_ci} 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_cistatic int smc_getname(struct socket *sock, struct sockaddr *addr, 272462306a36Sopenharmony_ci int peer) 272562306a36Sopenharmony_ci{ 272662306a36Sopenharmony_ci struct smc_sock *smc; 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci if (peer && (sock->sk->sk_state != SMC_ACTIVE) && 272962306a36Sopenharmony_ci (sock->sk->sk_state != SMC_APPCLOSEWAIT1)) 273062306a36Sopenharmony_ci return -ENOTCONN; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci smc = smc_sk(sock->sk); 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci return smc->clcsock->ops->getname(smc->clcsock, addr, peer); 273562306a36Sopenharmony_ci} 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_cistatic int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) 273862306a36Sopenharmony_ci{ 273962306a36Sopenharmony_ci struct sock *sk = sock->sk; 274062306a36Sopenharmony_ci struct smc_sock *smc; 274162306a36Sopenharmony_ci int rc; 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci smc = smc_sk(sk); 274462306a36Sopenharmony_ci lock_sock(sk); 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci /* SMC does not support connect with fastopen */ 274762306a36Sopenharmony_ci if (msg->msg_flags & MSG_FASTOPEN) { 274862306a36Sopenharmony_ci /* not connected yet, fallback */ 274962306a36Sopenharmony_ci if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) { 275062306a36Sopenharmony_ci rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP); 275162306a36Sopenharmony_ci if (rc) 275262306a36Sopenharmony_ci goto out; 275362306a36Sopenharmony_ci } else { 275462306a36Sopenharmony_ci rc = -EINVAL; 275562306a36Sopenharmony_ci goto out; 275662306a36Sopenharmony_ci } 275762306a36Sopenharmony_ci } else if ((sk->sk_state != SMC_ACTIVE) && 275862306a36Sopenharmony_ci (sk->sk_state != SMC_APPCLOSEWAIT1) && 275962306a36Sopenharmony_ci (sk->sk_state != SMC_INIT)) { 276062306a36Sopenharmony_ci rc = -EPIPE; 276162306a36Sopenharmony_ci goto out; 276262306a36Sopenharmony_ci } 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci if (smc->use_fallback) { 276562306a36Sopenharmony_ci rc = smc->clcsock->ops->sendmsg(smc->clcsock, msg, len); 276662306a36Sopenharmony_ci } else { 276762306a36Sopenharmony_ci rc = smc_tx_sendmsg(smc, msg, len); 276862306a36Sopenharmony_ci SMC_STAT_TX_PAYLOAD(smc, len, rc); 276962306a36Sopenharmony_ci } 277062306a36Sopenharmony_ciout: 277162306a36Sopenharmony_ci release_sock(sk); 277262306a36Sopenharmony_ci return rc; 277362306a36Sopenharmony_ci} 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_cistatic int smc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, 277662306a36Sopenharmony_ci int flags) 277762306a36Sopenharmony_ci{ 277862306a36Sopenharmony_ci struct sock *sk = sock->sk; 277962306a36Sopenharmony_ci struct smc_sock *smc; 278062306a36Sopenharmony_ci int rc = -ENOTCONN; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci smc = smc_sk(sk); 278362306a36Sopenharmony_ci lock_sock(sk); 278462306a36Sopenharmony_ci if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) { 278562306a36Sopenharmony_ci /* socket was connected before, no more data to read */ 278662306a36Sopenharmony_ci rc = 0; 278762306a36Sopenharmony_ci goto out; 278862306a36Sopenharmony_ci } 278962306a36Sopenharmony_ci if ((sk->sk_state == SMC_INIT) || 279062306a36Sopenharmony_ci (sk->sk_state == SMC_LISTEN) || 279162306a36Sopenharmony_ci (sk->sk_state == SMC_CLOSED)) 279262306a36Sopenharmony_ci goto out; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci if (sk->sk_state == SMC_PEERFINCLOSEWAIT) { 279562306a36Sopenharmony_ci rc = 0; 279662306a36Sopenharmony_ci goto out; 279762306a36Sopenharmony_ci } 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci if (smc->use_fallback) { 280062306a36Sopenharmony_ci rc = smc->clcsock->ops->recvmsg(smc->clcsock, msg, len, flags); 280162306a36Sopenharmony_ci } else { 280262306a36Sopenharmony_ci msg->msg_namelen = 0; 280362306a36Sopenharmony_ci rc = smc_rx_recvmsg(smc, msg, NULL, len, flags); 280462306a36Sopenharmony_ci SMC_STAT_RX_PAYLOAD(smc, rc, rc); 280562306a36Sopenharmony_ci } 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ciout: 280862306a36Sopenharmony_ci release_sock(sk); 280962306a36Sopenharmony_ci return rc; 281062306a36Sopenharmony_ci} 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_cistatic __poll_t smc_accept_poll(struct sock *parent) 281362306a36Sopenharmony_ci{ 281462306a36Sopenharmony_ci struct smc_sock *isk = smc_sk(parent); 281562306a36Sopenharmony_ci __poll_t mask = 0; 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci spin_lock(&isk->accept_q_lock); 281862306a36Sopenharmony_ci if (!list_empty(&isk->accept_q)) 281962306a36Sopenharmony_ci mask = EPOLLIN | EPOLLRDNORM; 282062306a36Sopenharmony_ci spin_unlock(&isk->accept_q_lock); 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci return mask; 282362306a36Sopenharmony_ci} 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_cistatic __poll_t smc_poll(struct file *file, struct socket *sock, 282662306a36Sopenharmony_ci poll_table *wait) 282762306a36Sopenharmony_ci{ 282862306a36Sopenharmony_ci struct sock *sk = sock->sk; 282962306a36Sopenharmony_ci struct smc_sock *smc; 283062306a36Sopenharmony_ci __poll_t mask = 0; 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci if (!sk) 283362306a36Sopenharmony_ci return EPOLLNVAL; 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci smc = smc_sk(sock->sk); 283662306a36Sopenharmony_ci if (smc->use_fallback) { 283762306a36Sopenharmony_ci /* delegate to CLC child sock */ 283862306a36Sopenharmony_ci mask = smc->clcsock->ops->poll(file, smc->clcsock, wait); 283962306a36Sopenharmony_ci sk->sk_err = smc->clcsock->sk->sk_err; 284062306a36Sopenharmony_ci } else { 284162306a36Sopenharmony_ci if (sk->sk_state != SMC_CLOSED) 284262306a36Sopenharmony_ci sock_poll_wait(file, sock, wait); 284362306a36Sopenharmony_ci if (sk->sk_err) 284462306a36Sopenharmony_ci mask |= EPOLLERR; 284562306a36Sopenharmony_ci if ((sk->sk_shutdown == SHUTDOWN_MASK) || 284662306a36Sopenharmony_ci (sk->sk_state == SMC_CLOSED)) 284762306a36Sopenharmony_ci mask |= EPOLLHUP; 284862306a36Sopenharmony_ci if (sk->sk_state == SMC_LISTEN) { 284962306a36Sopenharmony_ci /* woken up by sk_data_ready in smc_listen_work() */ 285062306a36Sopenharmony_ci mask |= smc_accept_poll(sk); 285162306a36Sopenharmony_ci } else if (smc->use_fallback) { /* as result of connect_work()*/ 285262306a36Sopenharmony_ci mask |= smc->clcsock->ops->poll(file, smc->clcsock, 285362306a36Sopenharmony_ci wait); 285462306a36Sopenharmony_ci sk->sk_err = smc->clcsock->sk->sk_err; 285562306a36Sopenharmony_ci } else { 285662306a36Sopenharmony_ci if ((sk->sk_state != SMC_INIT && 285762306a36Sopenharmony_ci atomic_read(&smc->conn.sndbuf_space)) || 285862306a36Sopenharmony_ci sk->sk_shutdown & SEND_SHUTDOWN) { 285962306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 286062306a36Sopenharmony_ci } else { 286162306a36Sopenharmony_ci sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); 286262306a36Sopenharmony_ci set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); 286362306a36Sopenharmony_ci } 286462306a36Sopenharmony_ci if (atomic_read(&smc->conn.bytes_to_rcv)) 286562306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 286662306a36Sopenharmony_ci if (sk->sk_shutdown & RCV_SHUTDOWN) 286762306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; 286862306a36Sopenharmony_ci if (sk->sk_state == SMC_APPCLOSEWAIT1) 286962306a36Sopenharmony_ci mask |= EPOLLIN; 287062306a36Sopenharmony_ci if (smc->conn.urg_state == SMC_URG_VALID) 287162306a36Sopenharmony_ci mask |= EPOLLPRI; 287262306a36Sopenharmony_ci } 287362306a36Sopenharmony_ci } 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci return mask; 287662306a36Sopenharmony_ci} 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_cistatic int smc_shutdown(struct socket *sock, int how) 287962306a36Sopenharmony_ci{ 288062306a36Sopenharmony_ci struct sock *sk = sock->sk; 288162306a36Sopenharmony_ci bool do_shutdown = true; 288262306a36Sopenharmony_ci struct smc_sock *smc; 288362306a36Sopenharmony_ci int rc = -EINVAL; 288462306a36Sopenharmony_ci int old_state; 288562306a36Sopenharmony_ci int rc1 = 0; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci smc = smc_sk(sk); 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci if ((how < SHUT_RD) || (how > SHUT_RDWR)) 289062306a36Sopenharmony_ci return rc; 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci lock_sock(sk); 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci if (sock->state == SS_CONNECTING) { 289562306a36Sopenharmony_ci if (sk->sk_state == SMC_ACTIVE) 289662306a36Sopenharmony_ci sock->state = SS_CONNECTED; 289762306a36Sopenharmony_ci else if (sk->sk_state == SMC_PEERCLOSEWAIT1 || 289862306a36Sopenharmony_ci sk->sk_state == SMC_PEERCLOSEWAIT2 || 289962306a36Sopenharmony_ci sk->sk_state == SMC_APPCLOSEWAIT1 || 290062306a36Sopenharmony_ci sk->sk_state == SMC_APPCLOSEWAIT2 || 290162306a36Sopenharmony_ci sk->sk_state == SMC_APPFINCLOSEWAIT) 290262306a36Sopenharmony_ci sock->state = SS_DISCONNECTING; 290362306a36Sopenharmony_ci } 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci rc = -ENOTCONN; 290662306a36Sopenharmony_ci if ((sk->sk_state != SMC_ACTIVE) && 290762306a36Sopenharmony_ci (sk->sk_state != SMC_PEERCLOSEWAIT1) && 290862306a36Sopenharmony_ci (sk->sk_state != SMC_PEERCLOSEWAIT2) && 290962306a36Sopenharmony_ci (sk->sk_state != SMC_APPCLOSEWAIT1) && 291062306a36Sopenharmony_ci (sk->sk_state != SMC_APPCLOSEWAIT2) && 291162306a36Sopenharmony_ci (sk->sk_state != SMC_APPFINCLOSEWAIT)) 291262306a36Sopenharmony_ci goto out; 291362306a36Sopenharmony_ci if (smc->use_fallback) { 291462306a36Sopenharmony_ci rc = kernel_sock_shutdown(smc->clcsock, how); 291562306a36Sopenharmony_ci sk->sk_shutdown = smc->clcsock->sk->sk_shutdown; 291662306a36Sopenharmony_ci if (sk->sk_shutdown == SHUTDOWN_MASK) { 291762306a36Sopenharmony_ci sk->sk_state = SMC_CLOSED; 291862306a36Sopenharmony_ci sk->sk_socket->state = SS_UNCONNECTED; 291962306a36Sopenharmony_ci sock_put(sk); 292062306a36Sopenharmony_ci } 292162306a36Sopenharmony_ci goto out; 292262306a36Sopenharmony_ci } 292362306a36Sopenharmony_ci switch (how) { 292462306a36Sopenharmony_ci case SHUT_RDWR: /* shutdown in both directions */ 292562306a36Sopenharmony_ci old_state = sk->sk_state; 292662306a36Sopenharmony_ci rc = smc_close_active(smc); 292762306a36Sopenharmony_ci if (old_state == SMC_ACTIVE && 292862306a36Sopenharmony_ci sk->sk_state == SMC_PEERCLOSEWAIT1) 292962306a36Sopenharmony_ci do_shutdown = false; 293062306a36Sopenharmony_ci break; 293162306a36Sopenharmony_ci case SHUT_WR: 293262306a36Sopenharmony_ci rc = smc_close_shutdown_write(smc); 293362306a36Sopenharmony_ci break; 293462306a36Sopenharmony_ci case SHUT_RD: 293562306a36Sopenharmony_ci rc = 0; 293662306a36Sopenharmony_ci /* nothing more to do because peer is not involved */ 293762306a36Sopenharmony_ci break; 293862306a36Sopenharmony_ci } 293962306a36Sopenharmony_ci if (do_shutdown && smc->clcsock) 294062306a36Sopenharmony_ci rc1 = kernel_sock_shutdown(smc->clcsock, how); 294162306a36Sopenharmony_ci /* map sock_shutdown_cmd constants to sk_shutdown value range */ 294262306a36Sopenharmony_ci sk->sk_shutdown |= how + 1; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci if (sk->sk_state == SMC_CLOSED) 294562306a36Sopenharmony_ci sock->state = SS_UNCONNECTED; 294662306a36Sopenharmony_ci else 294762306a36Sopenharmony_ci sock->state = SS_DISCONNECTING; 294862306a36Sopenharmony_ciout: 294962306a36Sopenharmony_ci release_sock(sk); 295062306a36Sopenharmony_ci return rc ? rc : rc1; 295162306a36Sopenharmony_ci} 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_cistatic int __smc_getsockopt(struct socket *sock, int level, int optname, 295462306a36Sopenharmony_ci char __user *optval, int __user *optlen) 295562306a36Sopenharmony_ci{ 295662306a36Sopenharmony_ci struct smc_sock *smc; 295762306a36Sopenharmony_ci int val, len; 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci smc = smc_sk(sock->sk); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci if (get_user(len, optlen)) 296262306a36Sopenharmony_ci return -EFAULT; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci len = min_t(int, len, sizeof(int)); 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci if (len < 0) 296762306a36Sopenharmony_ci return -EINVAL; 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci switch (optname) { 297062306a36Sopenharmony_ci case SMC_LIMIT_HS: 297162306a36Sopenharmony_ci val = smc->limit_smc_hs; 297262306a36Sopenharmony_ci break; 297362306a36Sopenharmony_ci default: 297462306a36Sopenharmony_ci return -EOPNOTSUPP; 297562306a36Sopenharmony_ci } 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci if (put_user(len, optlen)) 297862306a36Sopenharmony_ci return -EFAULT; 297962306a36Sopenharmony_ci if (copy_to_user(optval, &val, len)) 298062306a36Sopenharmony_ci return -EFAULT; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci return 0; 298362306a36Sopenharmony_ci} 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_cistatic int __smc_setsockopt(struct socket *sock, int level, int optname, 298662306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 298762306a36Sopenharmony_ci{ 298862306a36Sopenharmony_ci struct sock *sk = sock->sk; 298962306a36Sopenharmony_ci struct smc_sock *smc; 299062306a36Sopenharmony_ci int val, rc; 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci smc = smc_sk(sk); 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci lock_sock(sk); 299562306a36Sopenharmony_ci switch (optname) { 299662306a36Sopenharmony_ci case SMC_LIMIT_HS: 299762306a36Sopenharmony_ci if (optlen < sizeof(int)) { 299862306a36Sopenharmony_ci rc = -EINVAL; 299962306a36Sopenharmony_ci break; 300062306a36Sopenharmony_ci } 300162306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(int))) { 300262306a36Sopenharmony_ci rc = -EFAULT; 300362306a36Sopenharmony_ci break; 300462306a36Sopenharmony_ci } 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci smc->limit_smc_hs = !!val; 300762306a36Sopenharmony_ci rc = 0; 300862306a36Sopenharmony_ci break; 300962306a36Sopenharmony_ci default: 301062306a36Sopenharmony_ci rc = -EOPNOTSUPP; 301162306a36Sopenharmony_ci break; 301262306a36Sopenharmony_ci } 301362306a36Sopenharmony_ci release_sock(sk); 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci return rc; 301662306a36Sopenharmony_ci} 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_cistatic int smc_setsockopt(struct socket *sock, int level, int optname, 301962306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 302062306a36Sopenharmony_ci{ 302162306a36Sopenharmony_ci struct sock *sk = sock->sk; 302262306a36Sopenharmony_ci struct smc_sock *smc; 302362306a36Sopenharmony_ci int val, rc; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci if (level == SOL_TCP && optname == TCP_ULP) 302662306a36Sopenharmony_ci return -EOPNOTSUPP; 302762306a36Sopenharmony_ci else if (level == SOL_SMC) 302862306a36Sopenharmony_ci return __smc_setsockopt(sock, level, optname, optval, optlen); 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci smc = smc_sk(sk); 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci /* generic setsockopts reaching us here always apply to the 303362306a36Sopenharmony_ci * CLC socket 303462306a36Sopenharmony_ci */ 303562306a36Sopenharmony_ci mutex_lock(&smc->clcsock_release_lock); 303662306a36Sopenharmony_ci if (!smc->clcsock) { 303762306a36Sopenharmony_ci mutex_unlock(&smc->clcsock_release_lock); 303862306a36Sopenharmony_ci return -EBADF; 303962306a36Sopenharmony_ci } 304062306a36Sopenharmony_ci if (unlikely(!smc->clcsock->ops->setsockopt)) 304162306a36Sopenharmony_ci rc = -EOPNOTSUPP; 304262306a36Sopenharmony_ci else 304362306a36Sopenharmony_ci rc = smc->clcsock->ops->setsockopt(smc->clcsock, level, optname, 304462306a36Sopenharmony_ci optval, optlen); 304562306a36Sopenharmony_ci if (smc->clcsock->sk->sk_err) { 304662306a36Sopenharmony_ci sk->sk_err = smc->clcsock->sk->sk_err; 304762306a36Sopenharmony_ci sk_error_report(sk); 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci mutex_unlock(&smc->clcsock_release_lock); 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci if (optlen < sizeof(int)) 305262306a36Sopenharmony_ci return -EINVAL; 305362306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(int))) 305462306a36Sopenharmony_ci return -EFAULT; 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci lock_sock(sk); 305762306a36Sopenharmony_ci if (rc || smc->use_fallback) 305862306a36Sopenharmony_ci goto out; 305962306a36Sopenharmony_ci switch (optname) { 306062306a36Sopenharmony_ci case TCP_FASTOPEN: 306162306a36Sopenharmony_ci case TCP_FASTOPEN_CONNECT: 306262306a36Sopenharmony_ci case TCP_FASTOPEN_KEY: 306362306a36Sopenharmony_ci case TCP_FASTOPEN_NO_COOKIE: 306462306a36Sopenharmony_ci /* option not supported by SMC */ 306562306a36Sopenharmony_ci if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) { 306662306a36Sopenharmony_ci rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP); 306762306a36Sopenharmony_ci } else { 306862306a36Sopenharmony_ci rc = -EINVAL; 306962306a36Sopenharmony_ci } 307062306a36Sopenharmony_ci break; 307162306a36Sopenharmony_ci case TCP_NODELAY: 307262306a36Sopenharmony_ci if (sk->sk_state != SMC_INIT && 307362306a36Sopenharmony_ci sk->sk_state != SMC_LISTEN && 307462306a36Sopenharmony_ci sk->sk_state != SMC_CLOSED) { 307562306a36Sopenharmony_ci if (val) { 307662306a36Sopenharmony_ci SMC_STAT_INC(smc, ndly_cnt); 307762306a36Sopenharmony_ci smc_tx_pending(&smc->conn); 307862306a36Sopenharmony_ci cancel_delayed_work(&smc->conn.tx_work); 307962306a36Sopenharmony_ci } 308062306a36Sopenharmony_ci } 308162306a36Sopenharmony_ci break; 308262306a36Sopenharmony_ci case TCP_CORK: 308362306a36Sopenharmony_ci if (sk->sk_state != SMC_INIT && 308462306a36Sopenharmony_ci sk->sk_state != SMC_LISTEN && 308562306a36Sopenharmony_ci sk->sk_state != SMC_CLOSED) { 308662306a36Sopenharmony_ci if (!val) { 308762306a36Sopenharmony_ci SMC_STAT_INC(smc, cork_cnt); 308862306a36Sopenharmony_ci smc_tx_pending(&smc->conn); 308962306a36Sopenharmony_ci cancel_delayed_work(&smc->conn.tx_work); 309062306a36Sopenharmony_ci } 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci break; 309362306a36Sopenharmony_ci case TCP_DEFER_ACCEPT: 309462306a36Sopenharmony_ci smc->sockopt_defer_accept = val; 309562306a36Sopenharmony_ci break; 309662306a36Sopenharmony_ci default: 309762306a36Sopenharmony_ci break; 309862306a36Sopenharmony_ci } 309962306a36Sopenharmony_ciout: 310062306a36Sopenharmony_ci release_sock(sk); 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci return rc; 310362306a36Sopenharmony_ci} 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_cistatic int smc_getsockopt(struct socket *sock, int level, int optname, 310662306a36Sopenharmony_ci char __user *optval, int __user *optlen) 310762306a36Sopenharmony_ci{ 310862306a36Sopenharmony_ci struct smc_sock *smc; 310962306a36Sopenharmony_ci int rc; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci if (level == SOL_SMC) 311262306a36Sopenharmony_ci return __smc_getsockopt(sock, level, optname, optval, optlen); 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci smc = smc_sk(sock->sk); 311562306a36Sopenharmony_ci mutex_lock(&smc->clcsock_release_lock); 311662306a36Sopenharmony_ci if (!smc->clcsock) { 311762306a36Sopenharmony_ci mutex_unlock(&smc->clcsock_release_lock); 311862306a36Sopenharmony_ci return -EBADF; 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci /* socket options apply to the CLC socket */ 312162306a36Sopenharmony_ci if (unlikely(!smc->clcsock->ops->getsockopt)) { 312262306a36Sopenharmony_ci mutex_unlock(&smc->clcsock_release_lock); 312362306a36Sopenharmony_ci return -EOPNOTSUPP; 312462306a36Sopenharmony_ci } 312562306a36Sopenharmony_ci rc = smc->clcsock->ops->getsockopt(smc->clcsock, level, optname, 312662306a36Sopenharmony_ci optval, optlen); 312762306a36Sopenharmony_ci mutex_unlock(&smc->clcsock_release_lock); 312862306a36Sopenharmony_ci return rc; 312962306a36Sopenharmony_ci} 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_cistatic int smc_ioctl(struct socket *sock, unsigned int cmd, 313262306a36Sopenharmony_ci unsigned long arg) 313362306a36Sopenharmony_ci{ 313462306a36Sopenharmony_ci union smc_host_cursor cons, urg; 313562306a36Sopenharmony_ci struct smc_connection *conn; 313662306a36Sopenharmony_ci struct smc_sock *smc; 313762306a36Sopenharmony_ci int answ; 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci smc = smc_sk(sock->sk); 314062306a36Sopenharmony_ci conn = &smc->conn; 314162306a36Sopenharmony_ci lock_sock(&smc->sk); 314262306a36Sopenharmony_ci if (smc->use_fallback) { 314362306a36Sopenharmony_ci if (!smc->clcsock) { 314462306a36Sopenharmony_ci release_sock(&smc->sk); 314562306a36Sopenharmony_ci return -EBADF; 314662306a36Sopenharmony_ci } 314762306a36Sopenharmony_ci answ = smc->clcsock->ops->ioctl(smc->clcsock, cmd, arg); 314862306a36Sopenharmony_ci release_sock(&smc->sk); 314962306a36Sopenharmony_ci return answ; 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci switch (cmd) { 315262306a36Sopenharmony_ci case SIOCINQ: /* same as FIONREAD */ 315362306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_LISTEN) { 315462306a36Sopenharmony_ci release_sock(&smc->sk); 315562306a36Sopenharmony_ci return -EINVAL; 315662306a36Sopenharmony_ci } 315762306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT || 315862306a36Sopenharmony_ci smc->sk.sk_state == SMC_CLOSED) 315962306a36Sopenharmony_ci answ = 0; 316062306a36Sopenharmony_ci else 316162306a36Sopenharmony_ci answ = atomic_read(&smc->conn.bytes_to_rcv); 316262306a36Sopenharmony_ci break; 316362306a36Sopenharmony_ci case SIOCOUTQ: 316462306a36Sopenharmony_ci /* output queue size (not send + not acked) */ 316562306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_LISTEN) { 316662306a36Sopenharmony_ci release_sock(&smc->sk); 316762306a36Sopenharmony_ci return -EINVAL; 316862306a36Sopenharmony_ci } 316962306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT || 317062306a36Sopenharmony_ci smc->sk.sk_state == SMC_CLOSED) 317162306a36Sopenharmony_ci answ = 0; 317262306a36Sopenharmony_ci else 317362306a36Sopenharmony_ci answ = smc->conn.sndbuf_desc->len - 317462306a36Sopenharmony_ci atomic_read(&smc->conn.sndbuf_space); 317562306a36Sopenharmony_ci break; 317662306a36Sopenharmony_ci case SIOCOUTQNSD: 317762306a36Sopenharmony_ci /* output queue size (not send only) */ 317862306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_LISTEN) { 317962306a36Sopenharmony_ci release_sock(&smc->sk); 318062306a36Sopenharmony_ci return -EINVAL; 318162306a36Sopenharmony_ci } 318262306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT || 318362306a36Sopenharmony_ci smc->sk.sk_state == SMC_CLOSED) 318462306a36Sopenharmony_ci answ = 0; 318562306a36Sopenharmony_ci else 318662306a36Sopenharmony_ci answ = smc_tx_prepared_sends(&smc->conn); 318762306a36Sopenharmony_ci break; 318862306a36Sopenharmony_ci case SIOCATMARK: 318962306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_LISTEN) { 319062306a36Sopenharmony_ci release_sock(&smc->sk); 319162306a36Sopenharmony_ci return -EINVAL; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci if (smc->sk.sk_state == SMC_INIT || 319462306a36Sopenharmony_ci smc->sk.sk_state == SMC_CLOSED) { 319562306a36Sopenharmony_ci answ = 0; 319662306a36Sopenharmony_ci } else { 319762306a36Sopenharmony_ci smc_curs_copy(&cons, &conn->local_tx_ctrl.cons, conn); 319862306a36Sopenharmony_ci smc_curs_copy(&urg, &conn->urg_curs, conn); 319962306a36Sopenharmony_ci answ = smc_curs_diff(conn->rmb_desc->len, 320062306a36Sopenharmony_ci &cons, &urg) == 1; 320162306a36Sopenharmony_ci } 320262306a36Sopenharmony_ci break; 320362306a36Sopenharmony_ci default: 320462306a36Sopenharmony_ci release_sock(&smc->sk); 320562306a36Sopenharmony_ci return -ENOIOCTLCMD; 320662306a36Sopenharmony_ci } 320762306a36Sopenharmony_ci release_sock(&smc->sk); 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ci return put_user(answ, (int __user *)arg); 321062306a36Sopenharmony_ci} 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci/* Map the affected portions of the rmbe into an spd, note the number of bytes 321362306a36Sopenharmony_ci * to splice in conn->splice_pending, and press 'go'. Delays consumer cursor 321462306a36Sopenharmony_ci * updates till whenever a respective page has been fully processed. 321562306a36Sopenharmony_ci * Note that subsequent recv() calls have to wait till all splice() processing 321662306a36Sopenharmony_ci * completed. 321762306a36Sopenharmony_ci */ 321862306a36Sopenharmony_cistatic ssize_t smc_splice_read(struct socket *sock, loff_t *ppos, 321962306a36Sopenharmony_ci struct pipe_inode_info *pipe, size_t len, 322062306a36Sopenharmony_ci unsigned int flags) 322162306a36Sopenharmony_ci{ 322262306a36Sopenharmony_ci struct sock *sk = sock->sk; 322362306a36Sopenharmony_ci struct smc_sock *smc; 322462306a36Sopenharmony_ci int rc = -ENOTCONN; 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci smc = smc_sk(sk); 322762306a36Sopenharmony_ci lock_sock(sk); 322862306a36Sopenharmony_ci if (sk->sk_state == SMC_CLOSED && (sk->sk_shutdown & RCV_SHUTDOWN)) { 322962306a36Sopenharmony_ci /* socket was connected before, no more data to read */ 323062306a36Sopenharmony_ci rc = 0; 323162306a36Sopenharmony_ci goto out; 323262306a36Sopenharmony_ci } 323362306a36Sopenharmony_ci if (sk->sk_state == SMC_INIT || 323462306a36Sopenharmony_ci sk->sk_state == SMC_LISTEN || 323562306a36Sopenharmony_ci sk->sk_state == SMC_CLOSED) 323662306a36Sopenharmony_ci goto out; 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci if (sk->sk_state == SMC_PEERFINCLOSEWAIT) { 323962306a36Sopenharmony_ci rc = 0; 324062306a36Sopenharmony_ci goto out; 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci if (smc->use_fallback) { 324462306a36Sopenharmony_ci rc = smc->clcsock->ops->splice_read(smc->clcsock, ppos, 324562306a36Sopenharmony_ci pipe, len, flags); 324662306a36Sopenharmony_ci } else { 324762306a36Sopenharmony_ci if (*ppos) { 324862306a36Sopenharmony_ci rc = -ESPIPE; 324962306a36Sopenharmony_ci goto out; 325062306a36Sopenharmony_ci } 325162306a36Sopenharmony_ci if (flags & SPLICE_F_NONBLOCK) 325262306a36Sopenharmony_ci flags = MSG_DONTWAIT; 325362306a36Sopenharmony_ci else 325462306a36Sopenharmony_ci flags = 0; 325562306a36Sopenharmony_ci SMC_STAT_INC(smc, splice_cnt); 325662306a36Sopenharmony_ci rc = smc_rx_recvmsg(smc, NULL, pipe, len, flags); 325762306a36Sopenharmony_ci } 325862306a36Sopenharmony_ciout: 325962306a36Sopenharmony_ci release_sock(sk); 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci return rc; 326262306a36Sopenharmony_ci} 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci/* must look like tcp */ 326562306a36Sopenharmony_cistatic const struct proto_ops smc_sock_ops = { 326662306a36Sopenharmony_ci .family = PF_SMC, 326762306a36Sopenharmony_ci .owner = THIS_MODULE, 326862306a36Sopenharmony_ci .release = smc_release, 326962306a36Sopenharmony_ci .bind = smc_bind, 327062306a36Sopenharmony_ci .connect = smc_connect, 327162306a36Sopenharmony_ci .socketpair = sock_no_socketpair, 327262306a36Sopenharmony_ci .accept = smc_accept, 327362306a36Sopenharmony_ci .getname = smc_getname, 327462306a36Sopenharmony_ci .poll = smc_poll, 327562306a36Sopenharmony_ci .ioctl = smc_ioctl, 327662306a36Sopenharmony_ci .listen = smc_listen, 327762306a36Sopenharmony_ci .shutdown = smc_shutdown, 327862306a36Sopenharmony_ci .setsockopt = smc_setsockopt, 327962306a36Sopenharmony_ci .getsockopt = smc_getsockopt, 328062306a36Sopenharmony_ci .sendmsg = smc_sendmsg, 328162306a36Sopenharmony_ci .recvmsg = smc_recvmsg, 328262306a36Sopenharmony_ci .mmap = sock_no_mmap, 328362306a36Sopenharmony_ci .splice_read = smc_splice_read, 328462306a36Sopenharmony_ci}; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_cistatic int __smc_create(struct net *net, struct socket *sock, int protocol, 328762306a36Sopenharmony_ci int kern, struct socket *clcsock) 328862306a36Sopenharmony_ci{ 328962306a36Sopenharmony_ci int family = (protocol == SMCPROTO_SMC6) ? PF_INET6 : PF_INET; 329062306a36Sopenharmony_ci struct smc_sock *smc; 329162306a36Sopenharmony_ci struct sock *sk; 329262306a36Sopenharmony_ci int rc; 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci rc = -ESOCKTNOSUPPORT; 329562306a36Sopenharmony_ci if (sock->type != SOCK_STREAM) 329662306a36Sopenharmony_ci goto out; 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci rc = -EPROTONOSUPPORT; 329962306a36Sopenharmony_ci if (protocol != SMCPROTO_SMC && protocol != SMCPROTO_SMC6) 330062306a36Sopenharmony_ci goto out; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci rc = -ENOBUFS; 330362306a36Sopenharmony_ci sock->ops = &smc_sock_ops; 330462306a36Sopenharmony_ci sock->state = SS_UNCONNECTED; 330562306a36Sopenharmony_ci sk = smc_sock_alloc(net, sock, protocol); 330662306a36Sopenharmony_ci if (!sk) 330762306a36Sopenharmony_ci goto out; 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci /* create internal TCP socket for CLC handshake and fallback */ 331062306a36Sopenharmony_ci smc = smc_sk(sk); 331162306a36Sopenharmony_ci smc->use_fallback = false; /* assume rdma capability first */ 331262306a36Sopenharmony_ci smc->fallback_rsn = 0; 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci /* default behavior from limit_smc_hs in every net namespace */ 331562306a36Sopenharmony_ci smc->limit_smc_hs = net->smc.limit_smc_hs; 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci rc = 0; 331862306a36Sopenharmony_ci if (!clcsock) { 331962306a36Sopenharmony_ci rc = sock_create_kern(net, family, SOCK_STREAM, IPPROTO_TCP, 332062306a36Sopenharmony_ci &smc->clcsock); 332162306a36Sopenharmony_ci if (rc) { 332262306a36Sopenharmony_ci sk_common_release(sk); 332362306a36Sopenharmony_ci goto out; 332462306a36Sopenharmony_ci } 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci /* smc_clcsock_release() does not wait smc->clcsock->sk's 332762306a36Sopenharmony_ci * destruction; its sk_state might not be TCP_CLOSE after 332862306a36Sopenharmony_ci * smc->sk is close()d, and TCP timers can be fired later, 332962306a36Sopenharmony_ci * which need net ref. 333062306a36Sopenharmony_ci */ 333162306a36Sopenharmony_ci sk = smc->clcsock->sk; 333262306a36Sopenharmony_ci __netns_tracker_free(net, &sk->ns_tracker, false); 333362306a36Sopenharmony_ci sk->sk_net_refcnt = 1; 333462306a36Sopenharmony_ci get_net_track(net, &sk->ns_tracker, GFP_KERNEL); 333562306a36Sopenharmony_ci sock_inuse_add(net, 1); 333662306a36Sopenharmony_ci } else { 333762306a36Sopenharmony_ci smc->clcsock = clcsock; 333862306a36Sopenharmony_ci } 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ciout: 334162306a36Sopenharmony_ci return rc; 334262306a36Sopenharmony_ci} 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_cistatic int smc_create(struct net *net, struct socket *sock, int protocol, 334562306a36Sopenharmony_ci int kern) 334662306a36Sopenharmony_ci{ 334762306a36Sopenharmony_ci return __smc_create(net, sock, protocol, kern, NULL); 334862306a36Sopenharmony_ci} 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_cistatic const struct net_proto_family smc_sock_family_ops = { 335162306a36Sopenharmony_ci .family = PF_SMC, 335262306a36Sopenharmony_ci .owner = THIS_MODULE, 335362306a36Sopenharmony_ci .create = smc_create, 335462306a36Sopenharmony_ci}; 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_cistatic int smc_ulp_init(struct sock *sk) 335762306a36Sopenharmony_ci{ 335862306a36Sopenharmony_ci struct socket *tcp = sk->sk_socket; 335962306a36Sopenharmony_ci struct net *net = sock_net(sk); 336062306a36Sopenharmony_ci struct socket *smcsock; 336162306a36Sopenharmony_ci int protocol, ret; 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci /* only TCP can be replaced */ 336462306a36Sopenharmony_ci if (tcp->type != SOCK_STREAM || sk->sk_protocol != IPPROTO_TCP || 336562306a36Sopenharmony_ci (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)) 336662306a36Sopenharmony_ci return -ESOCKTNOSUPPORT; 336762306a36Sopenharmony_ci /* don't handle wq now */ 336862306a36Sopenharmony_ci if (tcp->state != SS_UNCONNECTED || !tcp->file || tcp->wq.fasync_list) 336962306a36Sopenharmony_ci return -ENOTCONN; 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_ci if (sk->sk_family == AF_INET) 337262306a36Sopenharmony_ci protocol = SMCPROTO_SMC; 337362306a36Sopenharmony_ci else 337462306a36Sopenharmony_ci protocol = SMCPROTO_SMC6; 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci smcsock = sock_alloc(); 337762306a36Sopenharmony_ci if (!smcsock) 337862306a36Sopenharmony_ci return -ENFILE; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci smcsock->type = SOCK_STREAM; 338162306a36Sopenharmony_ci __module_get(THIS_MODULE); /* tried in __tcp_ulp_find_autoload */ 338262306a36Sopenharmony_ci ret = __smc_create(net, smcsock, protocol, 1, tcp); 338362306a36Sopenharmony_ci if (ret) { 338462306a36Sopenharmony_ci sock_release(smcsock); /* module_put() which ops won't be NULL */ 338562306a36Sopenharmony_ci return ret; 338662306a36Sopenharmony_ci } 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci /* replace tcp socket to smc */ 338962306a36Sopenharmony_ci smcsock->file = tcp->file; 339062306a36Sopenharmony_ci smcsock->file->private_data = smcsock; 339162306a36Sopenharmony_ci smcsock->file->f_inode = SOCK_INODE(smcsock); /* replace inode when sock_close */ 339262306a36Sopenharmony_ci smcsock->file->f_path.dentry->d_inode = SOCK_INODE(smcsock); /* dput() in __fput */ 339362306a36Sopenharmony_ci tcp->file = NULL; 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci return ret; 339662306a36Sopenharmony_ci} 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_cistatic void smc_ulp_clone(const struct request_sock *req, struct sock *newsk, 339962306a36Sopenharmony_ci const gfp_t priority) 340062306a36Sopenharmony_ci{ 340162306a36Sopenharmony_ci struct inet_connection_sock *icsk = inet_csk(newsk); 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci /* don't inherit ulp ops to child when listen */ 340462306a36Sopenharmony_ci icsk->icsk_ulp_ops = NULL; 340562306a36Sopenharmony_ci} 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_cistatic struct tcp_ulp_ops smc_ulp_ops __read_mostly = { 340862306a36Sopenharmony_ci .name = "smc", 340962306a36Sopenharmony_ci .owner = THIS_MODULE, 341062306a36Sopenharmony_ci .init = smc_ulp_init, 341162306a36Sopenharmony_ci .clone = smc_ulp_clone, 341262306a36Sopenharmony_ci}; 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ciunsigned int smc_net_id; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_cistatic __net_init int smc_net_init(struct net *net) 341762306a36Sopenharmony_ci{ 341862306a36Sopenharmony_ci int rc; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci rc = smc_sysctl_net_init(net); 342162306a36Sopenharmony_ci if (rc) 342262306a36Sopenharmony_ci return rc; 342362306a36Sopenharmony_ci return smc_pnet_net_init(net); 342462306a36Sopenharmony_ci} 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_cistatic void __net_exit smc_net_exit(struct net *net) 342762306a36Sopenharmony_ci{ 342862306a36Sopenharmony_ci smc_sysctl_net_exit(net); 342962306a36Sopenharmony_ci smc_pnet_net_exit(net); 343062306a36Sopenharmony_ci} 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_cistatic __net_init int smc_net_stat_init(struct net *net) 343362306a36Sopenharmony_ci{ 343462306a36Sopenharmony_ci return smc_stats_init(net); 343562306a36Sopenharmony_ci} 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_cistatic void __net_exit smc_net_stat_exit(struct net *net) 343862306a36Sopenharmony_ci{ 343962306a36Sopenharmony_ci smc_stats_exit(net); 344062306a36Sopenharmony_ci} 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_cistatic struct pernet_operations smc_net_ops = { 344362306a36Sopenharmony_ci .init = smc_net_init, 344462306a36Sopenharmony_ci .exit = smc_net_exit, 344562306a36Sopenharmony_ci .id = &smc_net_id, 344662306a36Sopenharmony_ci .size = sizeof(struct smc_net), 344762306a36Sopenharmony_ci}; 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_cistatic struct pernet_operations smc_net_stat_ops = { 345062306a36Sopenharmony_ci .init = smc_net_stat_init, 345162306a36Sopenharmony_ci .exit = smc_net_stat_exit, 345262306a36Sopenharmony_ci}; 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_cistatic int __init smc_init(void) 345562306a36Sopenharmony_ci{ 345662306a36Sopenharmony_ci int rc; 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci rc = register_pernet_subsys(&smc_net_ops); 345962306a36Sopenharmony_ci if (rc) 346062306a36Sopenharmony_ci return rc; 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci rc = register_pernet_subsys(&smc_net_stat_ops); 346362306a36Sopenharmony_ci if (rc) 346462306a36Sopenharmony_ci goto out_pernet_subsys; 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ci rc = smc_ism_init(); 346762306a36Sopenharmony_ci if (rc) 346862306a36Sopenharmony_ci goto out_pernet_subsys_stat; 346962306a36Sopenharmony_ci smc_clc_init(); 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci rc = smc_nl_init(); 347262306a36Sopenharmony_ci if (rc) 347362306a36Sopenharmony_ci goto out_ism; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci rc = smc_pnet_init(); 347662306a36Sopenharmony_ci if (rc) 347762306a36Sopenharmony_ci goto out_nl; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci rc = -ENOMEM; 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci smc_tcp_ls_wq = alloc_workqueue("smc_tcp_ls_wq", 0, 0); 348262306a36Sopenharmony_ci if (!smc_tcp_ls_wq) 348362306a36Sopenharmony_ci goto out_pnet; 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci smc_hs_wq = alloc_workqueue("smc_hs_wq", 0, 0); 348662306a36Sopenharmony_ci if (!smc_hs_wq) 348762306a36Sopenharmony_ci goto out_alloc_tcp_ls_wq; 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_ci smc_close_wq = alloc_workqueue("smc_close_wq", 0, 0); 349062306a36Sopenharmony_ci if (!smc_close_wq) 349162306a36Sopenharmony_ci goto out_alloc_hs_wq; 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci rc = smc_core_init(); 349462306a36Sopenharmony_ci if (rc) { 349562306a36Sopenharmony_ci pr_err("%s: smc_core_init fails with %d\n", __func__, rc); 349662306a36Sopenharmony_ci goto out_alloc_wqs; 349762306a36Sopenharmony_ci } 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci rc = smc_llc_init(); 350062306a36Sopenharmony_ci if (rc) { 350162306a36Sopenharmony_ci pr_err("%s: smc_llc_init fails with %d\n", __func__, rc); 350262306a36Sopenharmony_ci goto out_core; 350362306a36Sopenharmony_ci } 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci rc = smc_cdc_init(); 350662306a36Sopenharmony_ci if (rc) { 350762306a36Sopenharmony_ci pr_err("%s: smc_cdc_init fails with %d\n", __func__, rc); 350862306a36Sopenharmony_ci goto out_core; 350962306a36Sopenharmony_ci } 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_ci rc = proto_register(&smc_proto, 1); 351262306a36Sopenharmony_ci if (rc) { 351362306a36Sopenharmony_ci pr_err("%s: proto_register(v4) fails with %d\n", __func__, rc); 351462306a36Sopenharmony_ci goto out_core; 351562306a36Sopenharmony_ci } 351662306a36Sopenharmony_ci 351762306a36Sopenharmony_ci rc = proto_register(&smc_proto6, 1); 351862306a36Sopenharmony_ci if (rc) { 351962306a36Sopenharmony_ci pr_err("%s: proto_register(v6) fails with %d\n", __func__, rc); 352062306a36Sopenharmony_ci goto out_proto; 352162306a36Sopenharmony_ci } 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci rc = sock_register(&smc_sock_family_ops); 352462306a36Sopenharmony_ci if (rc) { 352562306a36Sopenharmony_ci pr_err("%s: sock_register fails with %d\n", __func__, rc); 352662306a36Sopenharmony_ci goto out_proto6; 352762306a36Sopenharmony_ci } 352862306a36Sopenharmony_ci INIT_HLIST_HEAD(&smc_v4_hashinfo.ht); 352962306a36Sopenharmony_ci INIT_HLIST_HEAD(&smc_v6_hashinfo.ht); 353062306a36Sopenharmony_ci 353162306a36Sopenharmony_ci rc = smc_ib_register_client(); 353262306a36Sopenharmony_ci if (rc) { 353362306a36Sopenharmony_ci pr_err("%s: ib_register fails with %d\n", __func__, rc); 353462306a36Sopenharmony_ci goto out_sock; 353562306a36Sopenharmony_ci } 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci rc = tcp_register_ulp(&smc_ulp_ops); 353862306a36Sopenharmony_ci if (rc) { 353962306a36Sopenharmony_ci pr_err("%s: tcp_ulp_register fails with %d\n", __func__, rc); 354062306a36Sopenharmony_ci goto out_ib; 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci static_branch_enable(&tcp_have_smc); 354462306a36Sopenharmony_ci return 0; 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ciout_ib: 354762306a36Sopenharmony_ci smc_ib_unregister_client(); 354862306a36Sopenharmony_ciout_sock: 354962306a36Sopenharmony_ci sock_unregister(PF_SMC); 355062306a36Sopenharmony_ciout_proto6: 355162306a36Sopenharmony_ci proto_unregister(&smc_proto6); 355262306a36Sopenharmony_ciout_proto: 355362306a36Sopenharmony_ci proto_unregister(&smc_proto); 355462306a36Sopenharmony_ciout_core: 355562306a36Sopenharmony_ci smc_core_exit(); 355662306a36Sopenharmony_ciout_alloc_wqs: 355762306a36Sopenharmony_ci destroy_workqueue(smc_close_wq); 355862306a36Sopenharmony_ciout_alloc_hs_wq: 355962306a36Sopenharmony_ci destroy_workqueue(smc_hs_wq); 356062306a36Sopenharmony_ciout_alloc_tcp_ls_wq: 356162306a36Sopenharmony_ci destroy_workqueue(smc_tcp_ls_wq); 356262306a36Sopenharmony_ciout_pnet: 356362306a36Sopenharmony_ci smc_pnet_exit(); 356462306a36Sopenharmony_ciout_nl: 356562306a36Sopenharmony_ci smc_nl_exit(); 356662306a36Sopenharmony_ciout_ism: 356762306a36Sopenharmony_ci smc_clc_exit(); 356862306a36Sopenharmony_ci smc_ism_exit(); 356962306a36Sopenharmony_ciout_pernet_subsys_stat: 357062306a36Sopenharmony_ci unregister_pernet_subsys(&smc_net_stat_ops); 357162306a36Sopenharmony_ciout_pernet_subsys: 357262306a36Sopenharmony_ci unregister_pernet_subsys(&smc_net_ops); 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci return rc; 357562306a36Sopenharmony_ci} 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_cistatic void __exit smc_exit(void) 357862306a36Sopenharmony_ci{ 357962306a36Sopenharmony_ci static_branch_disable(&tcp_have_smc); 358062306a36Sopenharmony_ci tcp_unregister_ulp(&smc_ulp_ops); 358162306a36Sopenharmony_ci sock_unregister(PF_SMC); 358262306a36Sopenharmony_ci smc_core_exit(); 358362306a36Sopenharmony_ci smc_ib_unregister_client(); 358462306a36Sopenharmony_ci smc_ism_exit(); 358562306a36Sopenharmony_ci destroy_workqueue(smc_close_wq); 358662306a36Sopenharmony_ci destroy_workqueue(smc_tcp_ls_wq); 358762306a36Sopenharmony_ci destroy_workqueue(smc_hs_wq); 358862306a36Sopenharmony_ci proto_unregister(&smc_proto6); 358962306a36Sopenharmony_ci proto_unregister(&smc_proto); 359062306a36Sopenharmony_ci smc_pnet_exit(); 359162306a36Sopenharmony_ci smc_nl_exit(); 359262306a36Sopenharmony_ci smc_clc_exit(); 359362306a36Sopenharmony_ci unregister_pernet_subsys(&smc_net_stat_ops); 359462306a36Sopenharmony_ci unregister_pernet_subsys(&smc_net_ops); 359562306a36Sopenharmony_ci rcu_barrier(); 359662306a36Sopenharmony_ci} 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_cimodule_init(smc_init); 359962306a36Sopenharmony_cimodule_exit(smc_exit); 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ciMODULE_AUTHOR("Ursula Braun <ubraun@linux.vnet.ibm.com>"); 360262306a36Sopenharmony_ciMODULE_DESCRIPTION("smc socket address family"); 360362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 360462306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_SMC); 360562306a36Sopenharmony_ciMODULE_ALIAS_TCP_ULP("smc"); 360662306a36Sopenharmony_ciMODULE_ALIAS_GENL_FAMILY(SMC_GENL_FAMILY_NAME); 3607