|  | // SPDX-License-Identifier: GPL-2.0-only | 
|  | /* | 
|  | *  Shared Memory Communications over RDMA (SMC-R) and RoCE | 
|  | * | 
|  | *  Definitions for the IPPROTO_SMC (socket related) | 
|  | * | 
|  | *  Copyright IBM Corp. 2016, 2018 | 
|  | *  Copyright (c) 2024, Alibaba Inc. | 
|  | * | 
|  | *  Author: D. Wythe <alibuda@linux.alibaba.com> | 
|  | */ | 
|  |  | 
|  | #include <net/protocol.h> | 
|  | #include <net/sock.h> | 
|  |  | 
|  | #include "smc_inet.h" | 
|  | #include "smc.h" | 
|  |  | 
|  | static int smc_inet_init_sock(struct sock *sk); | 
|  |  | 
|  | static struct proto smc_inet_prot = { | 
|  | .name		= "INET_SMC", | 
|  | .owner		= THIS_MODULE, | 
|  | .init		= smc_inet_init_sock, | 
|  | .hash		= smc_hash_sk, | 
|  | .unhash		= smc_unhash_sk, | 
|  | .release_cb	= smc_release_cb, | 
|  | .obj_size	= sizeof(struct smc_sock), | 
|  | .h.smc_hash	= &smc_v4_hashinfo, | 
|  | .slab_flags	= SLAB_TYPESAFE_BY_RCU, | 
|  | }; | 
|  |  | 
|  | static const struct proto_ops smc_inet_stream_ops = { | 
|  | .family		= PF_INET, | 
|  | .owner		= THIS_MODULE, | 
|  | .release	= smc_release, | 
|  | .bind		= smc_bind, | 
|  | .connect	= smc_connect, | 
|  | .socketpair	= sock_no_socketpair, | 
|  | .accept		= smc_accept, | 
|  | .getname	= smc_getname, | 
|  | .poll		= smc_poll, | 
|  | .ioctl		= smc_ioctl, | 
|  | .listen		= smc_listen, | 
|  | .shutdown	= smc_shutdown, | 
|  | .setsockopt	= smc_setsockopt, | 
|  | .getsockopt	= smc_getsockopt, | 
|  | .sendmsg	= smc_sendmsg, | 
|  | .recvmsg	= smc_recvmsg, | 
|  | .mmap		= sock_no_mmap, | 
|  | .splice_read	= smc_splice_read, | 
|  | }; | 
|  |  | 
|  | static struct inet_protosw smc_inet_protosw = { | 
|  | .type		= SOCK_STREAM, | 
|  | .protocol	= IPPROTO_SMC, | 
|  | .prot		= &smc_inet_prot, | 
|  | .ops		= &smc_inet_stream_ops, | 
|  | .flags		= INET_PROTOSW_ICSK, | 
|  | }; | 
|  |  | 
|  | #if IS_ENABLED(CONFIG_IPV6) | 
|  | struct smc6_sock { | 
|  | struct smc_sock		smc; | 
|  | struct ipv6_pinfo	inet6; | 
|  | }; | 
|  |  | 
|  | static struct proto smc_inet6_prot = { | 
|  | .name		= "INET6_SMC", | 
|  | .owner		= THIS_MODULE, | 
|  | .init		= smc_inet_init_sock, | 
|  | .hash		= smc_hash_sk, | 
|  | .unhash		= smc_unhash_sk, | 
|  | .release_cb	= smc_release_cb, | 
|  | .obj_size	= sizeof(struct smc6_sock), | 
|  | .h.smc_hash	= &smc_v6_hashinfo, | 
|  | .slab_flags	= SLAB_TYPESAFE_BY_RCU, | 
|  | .ipv6_pinfo_offset	= offsetof(struct smc6_sock, inet6), | 
|  | }; | 
|  |  | 
|  | static const struct proto_ops smc_inet6_stream_ops = { | 
|  | .family		= PF_INET6, | 
|  | .owner		= THIS_MODULE, | 
|  | .release	= smc_release, | 
|  | .bind		= smc_bind, | 
|  | .connect	= smc_connect, | 
|  | .socketpair	= sock_no_socketpair, | 
|  | .accept		= smc_accept, | 
|  | .getname	= smc_getname, | 
|  | .poll		= smc_poll, | 
|  | .ioctl		= smc_ioctl, | 
|  | .listen		= smc_listen, | 
|  | .shutdown	= smc_shutdown, | 
|  | .setsockopt	= smc_setsockopt, | 
|  | .getsockopt	= smc_getsockopt, | 
|  | .sendmsg	= smc_sendmsg, | 
|  | .recvmsg	= smc_recvmsg, | 
|  | .mmap		= sock_no_mmap, | 
|  | .splice_read	= smc_splice_read, | 
|  | }; | 
|  |  | 
|  | static struct inet_protosw smc_inet6_protosw = { | 
|  | .type		= SOCK_STREAM, | 
|  | .protocol	= IPPROTO_SMC, | 
|  | .prot		= &smc_inet6_prot, | 
|  | .ops		= &smc_inet6_stream_ops, | 
|  | .flags		= INET_PROTOSW_ICSK, | 
|  | }; | 
|  | #endif /* CONFIG_IPV6 */ | 
|  |  | 
|  | static unsigned int smc_sync_mss(struct sock *sk, u32 pmtu) | 
|  | { | 
|  | /* No need pass it through to clcsock, mss can always be set by | 
|  | * sock_create_kern or smc_setsockopt. | 
|  | */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int smc_inet_init_sock(struct sock *sk) | 
|  | { | 
|  | struct net *net = sock_net(sk); | 
|  |  | 
|  | /* init common smc sock */ | 
|  | smc_sk_init(net, sk, IPPROTO_SMC); | 
|  |  | 
|  | inet_csk(sk)->icsk_sync_mss = smc_sync_mss; | 
|  |  | 
|  | /* create clcsock */ | 
|  | return smc_create_clcsk(net, sk, sk->sk_family); | 
|  | } | 
|  |  | 
|  | int __init smc_inet_init(void) | 
|  | { | 
|  | int rc; | 
|  |  | 
|  | rc = proto_register(&smc_inet_prot, 1); | 
|  | if (rc) { | 
|  | pr_err("%s: proto_register smc_inet_prot fails with %d\n", | 
|  | __func__, rc); | 
|  | return rc; | 
|  | } | 
|  | /* no return value */ | 
|  | inet_register_protosw(&smc_inet_protosw); | 
|  |  | 
|  | #if IS_ENABLED(CONFIG_IPV6) | 
|  | rc = proto_register(&smc_inet6_prot, 1); | 
|  | if (rc) { | 
|  | pr_err("%s: proto_register smc_inet6_prot fails with %d\n", | 
|  | __func__, rc); | 
|  | goto out_inet6_prot; | 
|  | } | 
|  | rc = inet6_register_protosw(&smc_inet6_protosw); | 
|  | if (rc) { | 
|  | pr_err("%s: inet6_register_protosw smc_inet6_protosw fails with %d\n", | 
|  | __func__, rc); | 
|  | goto out_inet6_protosw; | 
|  | } | 
|  | return rc; | 
|  | out_inet6_protosw: | 
|  | proto_unregister(&smc_inet6_prot); | 
|  | out_inet6_prot: | 
|  | inet_unregister_protosw(&smc_inet_protosw); | 
|  | proto_unregister(&smc_inet_prot); | 
|  | #endif /* CONFIG_IPV6 */ | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | void smc_inet_exit(void) | 
|  | { | 
|  | #if IS_ENABLED(CONFIG_IPV6) | 
|  | inet6_unregister_protosw(&smc_inet6_protosw); | 
|  | proto_unregister(&smc_inet6_prot); | 
|  | #endif /* CONFIG_IPV6 */ | 
|  | inet_unregister_protosw(&smc_inet_protosw); | 
|  | proto_unregister(&smc_inet_prot); | 
|  | } |