Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2220580/?format=api
{ "id": 2220580, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2220580/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org/", "project": { "id": 12, "url": "http://patchwork.ozlabs.org/api/1.1/projects/12/?format=api", "name": "Linux CIFS Client", "link_name": "linux-cifs-client", "list_id": "linux-cifs.vger.kernel.org", "list_email": "linux-cifs@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org>", "date": "2026-04-07T14:46:32", "name": "[6/8] smb: smbdirect: add in kernel only support for IPPROTO_SMBDIRECT", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "4577a49a395daae263790d4b4611b870ce095443", "submitter": { "id": 8149, "url": "http://patchwork.ozlabs.org/api/1.1/people/8149/?format=api", "name": "Stefan Metzmacher", "email": "metze@samba.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org/mbox/", "series": [ { "id": 498990, "url": "http://patchwork.ozlabs.org/api/1.1/series/498990/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/list/?series=498990", "date": "2026-04-07T14:46:28", "name": "smb: add kernel internal IPPROTO_SMBDIRECT", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/498990/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2220580/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2220580/checks/", "tags": {}, "headers": { "Return-Path": "\n <linux-cifs+bounces-10695-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-cifs@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (3072-bit key;\n secure) header.d=samba.org header.i=@samba.org header.a=rsa-sha256\n header.s=42 header.b=PdabcU2C;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=linux-cifs+bounces-10695-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (3072-bit key) header.d=samba.org header.i=@samba.org\n header.b=\"PdabcU2C\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=144.76.82.148", "smtp.subspace.kernel.org;\n dmarc=pass (p=quarantine dis=none) header.from=samba.org", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=samba.org" ], "Received": [ "from tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fqq2k2cMLz1yD3\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 08 Apr 2026 00:53:50 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id A99C530A379A\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 7 Apr 2026 14:48:01 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id D77F531E83D;\n\tTue, 7 Apr 2026 14:47:54 +0000 (UTC)", "from hr2.samba.org (hr2.samba.org [144.76.82.148])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 01AC830E829;\n\tTue, 7 Apr 2026 14:47:43 +0000 (UTC)", "from [127.0.0.2] (localhost [127.0.0.1])\n\tby hr2.samba.org with esmtpsa\n (TLS1.3:ECDHE_SECP256R1__ECDSA_SECP256R1_SHA256__CHACHA20_POLY1305:256)\n\t(Exim)\n\tid 1wA7iN-00000007WR1-3BJl;\n\tTue, 07 Apr 2026 14:47:40 +0000" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775573274; cv=none;\n b=hv6PR1Pzq6hYwS2sC0dPl/ytbwK9wVNcXBKpoC8C8AP5//KiWsr8CjuMi0yz0wy6gOAvBimtzWvtTGkfpF5/aeYi3ItdMdpuwfcVy19IVpEjMqxh8uyCKvm1td2T5PQ8g0Q8SMBNEMV4Q6MDT8FmaO2rktNMSlDrxpTMFByAFWM=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775573274; c=relaxed/simple;\n\tbh=xrvT2BDaA5SFez0aDTLDjj0BwuqdsyP0j0A2SL+Esl4=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=grK71iFNyyl08gDJJpFKXgRH0r0m0ziQbJCaX73YYM6GJrTMzrE/S/+kI4vbskpKFawhfZry3/mDojzABFvuxidetRgT+8gSASPMQA3sQ+ysrIzA2FNGr66vhYuntuM0sPGddPc6HX8ST+v0ahJe04/bL8/92bSqs6KAuk59+Hw=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=quarantine dis=none) header.from=samba.org;\n spf=pass smtp.mailfrom=samba.org;\n dkim=pass (3072-bit key) header.d=samba.org header.i=@samba.org\n header.b=PdabcU2C; arc=none smtp.client-ip=144.76.82.148", "DKIM-Signature": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=samba.org;\n\ts=42; h=Message-ID:Date:Cc:To:From;\n\tbh=/Qnvvf4nMiAoasVPPUTakHvzknwGpNRpuP5FHaL0NIA=; b=PdabcU2CFHHmEzMnsALWMLIY0f\n\t1E/y40SnNckW26a1js7wuKxoW9nQKYPYn5KBFPbPVi1IfQ6rJlcVlNDSN/HINEz+Ht+cnFMHnWOG2\n\tlCIbOrl0zlDbtJqrFTomITYjmyzXshDKxIZrODN+o+ypkre+g6OMoGLZArDktv0lOhjR22F0ZqEdf\n\t27AphlOtzNMwaA8ntuspixxNDaF42d3i9NxUNy25Y/V/mzwibYUTJWrFJstoifvehPFPNt9YbPshe\n\t34zgyuX5WPCGDazWW8M2cZ09hkkuoKFH4+NQOQv2um7gCirEvCBibf/OIFFz737Rky8OaD8mLOMRQ\n\tQNoBLJUxyIbVUOr5twXskPNlStUQhz4CNVO5vrEOY93BT3R/bAuVUvFOoazBOGztSgZfAGHMIfx4H\n\tjC2qipLuEbOcK90Jnpor9z+fzDkLqtCI9wGeos7DbuL+9+E4hUofyrfLYhcLXL7Gs6ual4z4cEfUP\n\t11MdfVPsTZ8fs73O/Bp1AZri;", "From": "Stefan Metzmacher <metze@samba.org>", "To": "linux-cifs@vger.kernel.org,\n\tsamba-technical@lists.samba.org", "Cc": "metze@samba.org,\n\tSteve French <smfrench@gmail.com>,\n\tTom Talpey <tom@talpey.com>,\n\tLong Li <longli@microsoft.com>,\n\tNamjae Jeon <linkinjeon@kernel.org>,\n\tDavid Howells <dhowells@redhat.com>,\n\tHenrique Carvalho <henrique.carvalho@suse.com>,\n\t\"David S . Miller\" <davem@davemloft.net>,\n\tEric Dumazet <edumazet@google.com>,\n\tJakub Kicinski <kuba@kernel.org>,\n\tPaolo Abeni <pabeni@redhat.com>,\n\tSimon Horman <horms@kernel.org>,\n\tKuniyuki Iwashima <kuniyu@google.com>,\n\tWillem de Bruijn <willemb@google.com>,\n\tnetdev@vger.kernel.org,\n\tXin Long <lucien.xin@gmail.com>,\n\tquic@lists.linux.dev,\n\tlinux-rdma@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org", "Subject": "[PATCH 6/8] smb: smbdirect: add in kernel only support for\n IPPROTO_SMBDIRECT", "Date": "Tue, 7 Apr 2026 16:46:32 +0200", "Message-ID": "\n <4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org>", "X-Mailer": "git-send-email 2.43.0", "In-Reply-To": "<cover.1775571957.git.metze@samba.org>", "References": "<cover.1775571957.git.metze@samba.org>", "Precedence": "bulk", "X-Mailing-List": "linux-cifs@vger.kernel.org", "List-Id": "<linux-cifs.vger.kernel.org>", "List-Subscribe": "<mailto:linux-cifs+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-cifs+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit" }, "content": "For userspace callers of socket() still get -EPROTONOSUPPORT,\nso we are sure we'll only have in kernel callers, cifs.ko and\nksmbd.ko, for now. This makes it possible to relax the\nconstrains generic stream socket consumers would otherwise\nassume.\n\nThere's a prototype for userspace sockets on top of\nthis and there's working userspace code for Samba as\nclient and server, so this is just the first step,\nbut a very important one.\n\nThe SMBDIRECT protocol is defined in [MS-SMBD] by Microsoft.\nIt is used as wrapper around RDMA in order to provide a transport for SMB3,\nbut Microsoft also uses it as transport for other protocols.\n\nSMBDIRECT works over Infiniband, RoCE and iWarp.\nRoCEv2 is based on IP/UDP and iWarp is based on IP/TCP,\nso these use IP addresses natively.\nInfiniband and RoCEv1 require IPOIB in order to be used for\nSMBDIRECT.\n\nSo instead of adding a PF_SMBDIRECT, which would only use AF_INET[6],\nwe use IPPROTO_SMBDIRECT instead, this uses a number not\nallocated from IANA, as it would not appear in an IP header.\n\nThis is similar to IPPROTO_SMC, IPPROTO_MPTCP and IPPROTO_QUIC,\nwhich are linux specific values for the socket() syscall.\n\n socket(AF_INET, SOCK_STREAM, IPPROTO_SMBDIRECT);\n socket(AF_INET6, SOCK_STREAM, IPPROTO_SMBDIRECT);\n\nThis will allow the existing smbdirect code used by\ncifs.ko and ksmbd.ko to be moved behind the socket layer,\nso that there's less special handling. Only sock_sendmsg()\nsock_recvmsg() are used, so that the main stream handling\nis done all the same for tcp, smbdirect and later also quic.\n\nThe special RDMA read/write handling will be via direct\nfunction calls as they are currently done for the in kernel\nconsumers.\n\nFor now the core smbdirect code still supports both\nmodes, direct calls in indirect via the socket layer.\nThe core code uses if (sc->sk.sk_family) as indication\nfor the new socket mode. Once cifs.ko and ksmbd.ko\nare converted we can remove the old mode slowly,\nbut I'll deferr that to a future patchset.\n\nThere's still a way to go in order to make this\nas generic as tcp and quic e.g. adding MSG_SPLICE_PAGES support or\nsplice_read/read_sock/read_skb.\n\nBut it's a good start, which will make changes\nmuch easier.\n\nCc: Steve French <smfrench@gmail.com>\nCc: Tom Talpey <tom@talpey.com>\nCc: Long Li <longli@microsoft.com>\nCc: Namjae Jeon <linkinjeon@kernel.org>\nCc: David Howells <dhowells@redhat.com>\nCc: Henrique Carvalho <henrique.carvalho@suse.com>\nCc: linux-cifs@vger.kernel.org\nCc: samba-technical@lists.samba.org\nCc: David S. Miller <davem@davemloft.net>\nCc: Eric Dumazet <edumazet@google.com>\nCc: Jakub Kicinski <kuba@kernel.org>\nCc: Paolo Abeni <pabeni@redhat.com>\nCc: Simon Horman <horms@kernel.org>\nCc: Kuniyuki Iwashima <kuniyu@google.com>\nCc: Willem de Bruijn <willemb@google.com>\nCc: netdev@vger.kernel.org\nCc: Xin Long <lucien.xin@gmail.com>\nCc: quic@lists.linux.dev\nCc: linux-rdma@vger.kernel.org\nCc: linux-kernel@vger.kernel.org\nSigned-off-by: Stefan Metzmacher <metze@samba.org>\n---\n fs/smb/common/smbdirect/Makefile | 1 +\n fs/smb/common/smbdirect/smbdirect.h | 62 +\n fs/smb/common/smbdirect/smbdirect_accept.c | 14 +-\n .../common/smbdirect/smbdirect_connection.c | 58 +\n fs/smb/common/smbdirect/smbdirect_devices.c | 2 +-\n fs/smb/common/smbdirect/smbdirect_internal.h | 59 +-\n fs/smb/common/smbdirect/smbdirect_listen.c | 49 +-\n fs/smb/common/smbdirect/smbdirect_main.c | 45 +\n fs/smb/common/smbdirect/smbdirect_mr.c | 10 +\n fs/smb/common/smbdirect/smbdirect_proto.c | 1549 +++++++++++++++++\n fs/smb/common/smbdirect/smbdirect_public.h | 3 +\n fs/smb/common/smbdirect/smbdirect_rw.c | 29 +-\n fs/smb/common/smbdirect/smbdirect_socket.c | 147 ++\n fs/smb/common/smbdirect/smbdirect_socket.h | 26 +-\n 14 files changed, 2039 insertions(+), 15 deletions(-)\n create mode 100644 fs/smb/common/smbdirect/smbdirect_proto.c", "diff": "diff --git a/fs/smb/common/smbdirect/Makefile b/fs/smb/common/smbdirect/Makefile\nindex 423f533e1002..fcff485d7c45 100644\n--- a/fs/smb/common/smbdirect/Makefile\n+++ b/fs/smb/common/smbdirect/Makefile\n@@ -10,6 +10,7 @@ smbdirect-y := \\\n \tsmbdirect_connection.o\t\\\n \tsmbdirect_mr.o\t\t\\\n \tsmbdirect_rw.o\t\t\\\n+\tsmbdirect_proto.o\t\\\n \tsmbdirect_debug.o\t\\\n \tsmbdirect_connect.o\t\\\n \tsmbdirect_listen.o\t\\\ndiff --git a/fs/smb/common/smbdirect/smbdirect.h b/fs/smb/common/smbdirect/smbdirect.h\nindex bbab5f7f7cc9..cf3d4957f94c 100644\n--- a/fs/smb/common/smbdirect/smbdirect.h\n+++ b/fs/smb/common/smbdirect/smbdirect.h\n@@ -6,7 +6,10 @@\n #ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__\n #define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__\n \n+#include <linux/stddef.h>\n #include <linux/types.h>\n+#include <linux/socket.h>\n+#include <asm/ioctls.h>\n \n /* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */\n struct smbdirect_buffer_descriptor_v1 {\n@@ -49,4 +52,63 @@ struct smbdirect_socket_parameters {\n \t\tSMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \\\n \t\tSMBDIRECT_FLAG_PORT_RANGE_ONLY_IW)\n \n+enum {\n+\t__SMBDIRECT_BUFFER_REMOTE_INVALIDATE = 0x20,\n+};\n+\n+struct smbdirect_cmsg_buffer {\n+\tuint8_t msg_control[CMSG_SPACE(24)];\n+};\n+\n+static __always_inline\n+void __smbdirect_cmsg_prepare(struct msghdr *msg,\n+\t\t\t struct smbdirect_cmsg_buffer *cbuffer,\n+\t\t\t int cmsg_type,\n+\t\t\t const void *payload,\n+\t\t\t size_t payloadlen)\n+{\n+\tsize_t cmsg_space = CMSG_SPACE(payloadlen);\n+\tsize_t cmsg_len = CMSG_LEN(payloadlen);\n+\tstruct cmsghdr *cmsg = NULL;\n+\tvoid *dataptr = NULL;\n+\n+\tBUILD_BUG_ON(cmsg_space > sizeof(cbuffer->msg_control));\n+\n+\tmemset(cbuffer, 0, sizeof(*cbuffer));\n+\n+\tmsg->msg_control = cbuffer->msg_control;\n+\tmsg->msg_controllen = cmsg_space;\n+\n+\tcmsg = CMSG_FIRSTHDR(msg);\n+\tcmsg->cmsg_level = SOL_SMBDIRECT;\n+\tcmsg->cmsg_type = cmsg_type;\n+\tcmsg->cmsg_len = cmsg_len;\n+\tdataptr = CMSG_DATA(cmsg);\n+\tmemcpy(dataptr, payload, payloadlen);\n+\tmsg->msg_controllen = cmsg->cmsg_len;\n+}\n+\n+struct smbdirect_buffer_remote_invalidate_args {\n+\t__u32 remote_token;\n+} __packed;\n+#define SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE \\\n+\t_IOW('S', __SMBDIRECT_BUFFER_REMOTE_INVALIDATE, \\\n+\t\tstruct smbdirect_buffer_remote_invalidate_args)\n+\n+static __always_inline\n+void smbdirect_buffer_remote_invalidate_cmsg_prepare(struct msghdr *msg,\n+\t\t\t\t\t\t struct smbdirect_cmsg_buffer *cbuffer,\n+\t\t\t\t\t\t const __u32 *remote_token)\n+{\n+\tif (remote_token) {\n+\t\tstruct smbdirect_buffer_remote_invalidate_args args = {\n+\t\t\t.remote_token = *remote_token,\n+\t\t};\n+\n+\t\t__smbdirect_cmsg_prepare(msg, cbuffer,\n+\t\t\t\t\t SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE,\n+\t\t\t\t\t &args, sizeof(args));\n+\t}\n+}\n+\n #endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ */\ndiff --git a/fs/smb/common/smbdirect/smbdirect_accept.c b/fs/smb/common/smbdirect/smbdirect_accept.c\nindex d6d5e6a3f5de..6d7d869cdbc3 100644\n--- a/fs/smb/common/smbdirect/smbdirect_accept.c\n+++ b/fs/smb/common/smbdirect/smbdirect_accept.c\n@@ -6,7 +6,6 @@\n */\n \n #include \"smbdirect_internal.h\"\n-#include <net/sock.h>\n #include \"../../common/smb2status.h\"\n \n static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id,\n@@ -460,6 +459,12 @@ static void smbdirect_accept_negotiate_recv_work(struct work_struct *work)\n \t\tspin_lock_irqsave(&lsc->listen.lock, flags);\n \t\tlist_del(&sc->accept.list);\n \t\tlist_add_tail(&sc->accept.list, &lsc->listen.ready);\n+\t\tif (lsc->sk.sk_family) {\n+\t\t\tstruct sock *lsk = &lsc->sk;\n+\n+\t\t\tif (!sock_flag(lsk, SOCK_DEAD) && lsk->sk_socket)\n+\t\t\t\tlsk->sk_data_ready(lsk);\n+\t\t}\n \t\twake_up(&lsc->listen.wait_queue);\n \t\tspin_unlock_irqrestore(&lsc->listen.lock, flags);\n \n@@ -774,11 +779,13 @@ static long smbdirect_socket_wait_for_accept(struct smbdirect_socket *lsc, long\n {\n \tlong ret;\n \n+\tsmbdirect_socket_sk_unlock(lsc);\n \tret = wait_event_interruptible_timeout(lsc->listen.wait_queue,\n \t\t\t\t\t !list_empty_careful(&lsc->listen.ready) ||\n \t\t\t\t\t lsc->status != SMBDIRECT_SOCKET_LISTENING ||\n \t\t\t\t\t lsc->first_error,\n \t\t\t\t\t timeo);\n+\tsmbdirect_socket_sk_lock(lsc);\n \tif (lsc->status != SMBDIRECT_SOCKET_LISTENING)\n \t\treturn -EINVAL;\n \tif (lsc->first_error)\n@@ -850,6 +857,11 @@ struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc,\n \t * order to grant credits to the peer.\n \t */\n \tnsc->status = SMBDIRECT_SOCKET_CONNECTED;\n+\tif (nsc->sk.sk_family) {\n+\t\tstruct sock *nsk = &nsc->sk;\n+\n+\t\tinet_sk_set_state(nsk, TCP_ESTABLISHED);\n+\t}\n \tsmbdirect_accept_negotiate_finish(nsc, 0);\n \n \treturn nsc;\ndiff --git a/fs/smb/common/smbdirect/smbdirect_connection.c b/fs/smb/common/smbdirect/smbdirect_connection.c\nindex 1e946f78e935..2c426aefd16d 100644\n--- a/fs/smb/common/smbdirect/smbdirect_connection.c\n+++ b/fs/smb/common/smbdirect/smbdirect_connection.c\n@@ -153,6 +153,15 @@ void smbdirect_connection_rdma_established(struct smbdirect_socket *sc)\n \n \tsc->rdma.cm_id->event_handler = smbdirect_connection_rdma_event_handler;\n \tsc->rdma.expected_event = RDMA_CM_EVENT_DISCONNECTED;\n+\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\tsmbdirect_socket_sync_saddr_to_sk(sc, NULL);\n+\t\tsmbdirect_socket_sync_daddr_to_sk(sc);\n+\n+\t\tinet_sk_set_state(sk, TCP_SYN_RECV);\n+\t}\n }\n \n void smbdirect_connection_negotiation_done(struct smbdirect_socket *sc)\n@@ -189,6 +198,13 @@ void smbdirect_connection_negotiation_done(struct smbdirect_socket *sc)\n \t\t smbdirect_socket_status_string(sc->status),\n \t\t SMBDIRECT_DEBUG_ERR_PTR(sc->first_error));\n \tsc->status = SMBDIRECT_SOCKET_CONNECTED;\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\tinet_sk_set_state(sk, TCP_ESTABLISHED);\n+\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\tsk->sk_socket->state = SS_CONNECTED;\n+\t}\n \n \t/*\n \t * We need to setup the refill and send immediate work\n@@ -203,6 +219,13 @@ void smbdirect_connection_negotiation_done(struct smbdirect_socket *sc)\n \t\t&sc->rdma.cm_id->route.addr.src_addr,\n \t\t&sc->rdma.cm_id->route.addr.dst_addr);\n \n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\tsk->sk_state_change(sk);\n+\t}\n+\n \twake_up(&sc->status_wait);\n }\n \n@@ -739,10 +762,12 @@ int smbdirect_connection_wait_for_connected(struct smbdirect_socket *sc)\n \t\t\"waiting for connection: device: %.*s local: %pISpsfc remote: %pISpsfc\\n\",\n \t\tIB_DEVICE_NAME_MAX, devname, src, dst);\n \n+\tsmbdirect_socket_sk_unlock(sc);\n \tret = wait_event_interruptible_timeout(sc->status_wait,\n \t\t\t\t\t sc->status == SMBDIRECT_SOCKET_CONNECTED ||\n \t\t\t\t\t sc->first_error,\n \t\t\t\t\t msecs_to_jiffies(sp->negotiate_timeout_msec));\n+\tsmbdirect_socket_sk_lock(sc);\n \tif (sc->rdma.cm_id) {\n \t\t/*\n \t\t * Maybe src and dev are updated in the meantime.\n@@ -954,6 +979,12 @@ int smbdirect_connection_send_batch_flush(struct smbdirect_socket *sc,\n \t\tatomic_add(batch->credit, &sc->send_io.bcredits.count);\n \t\tbatch->credit = 0;\n \t\twake_up(&sc->send_io.bcredits.wait_queue);\n+\t\tif (sc->sk.sk_family) {\n+\t\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\t\tsk->sk_write_space(sk);\n+\t\t}\n \t}\n \n \treturn ret;\n@@ -1091,6 +1122,8 @@ int smbdirect_connection_send_single_iter(struct smbdirect_socket *sc,\n \tu32 data_length = 0;\n \tint ret;\n \n+\tsmbdirect_socket_sk_owned_by_me(sc);\n+\n \tif (WARN_ON_ONCE(flags))\n \t\treturn -EINVAL; /* no flags support for now */\n \n@@ -1150,10 +1183,12 @@ int smbdirect_connection_send_single_iter(struct smbdirect_socket *sc,\n \t\t * wait until either the refill work or the peer\n \t\t * granted new credits\n \t\t */\n+\t\tsmbdirect_socket_sk_unlock(sc);\n \t\tret = wait_event_interruptible(sc->send_io.credits.wait_queue,\n \t\t\t\t\t atomic_read(&sc->send_io.credits.count) >= 1 ||\n \t\t\t\t\t atomic_read(&sc->recv_io.credits.available) >= 1 ||\n \t\t\t\t\t sc->status != SMBDIRECT_SOCKET_CONNECTED);\n+\t\tsmbdirect_socket_sk_lock(sc);\n \t\tif (sc->status != SMBDIRECT_SOCKET_CONNECTED)\n \t\t\tret = -ENOTCONN;\n \t\tif (ret < 0)\n@@ -1268,9 +1303,11 @@ int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc)\n \t * that means all the I/Os have been out and we are good to return\n \t */\n \n+\tsmbdirect_socket_sk_unlock(sc);\n \twait_event(sc->send_io.pending.zero_wait_queue,\n \t\t atomic_read(&sc->send_io.pending.count) == 0 ||\n \t\t sc->status != SMBDIRECT_SOCKET_CONNECTED);\n+\tsmbdirect_socket_sk_lock(sc);\n \tif (sc->status != SMBDIRECT_SOCKET_CONNECTED) {\n \t\tsmbdirect_log_write(sc, SMBDIRECT_LOG_ERR,\n \t\t\t\"status=%s first_error=%1pe => %1pe\\n\",\n@@ -1297,6 +1334,8 @@ int smbdirect_connection_send_iter(struct smbdirect_socket *sc,\n \tint error = 0;\n \t__be32 hdr;\n \n+\tsmbdirect_socket_sk_owned_by_me(sc);\n+\n \tif (WARN_ONCE(flags, \"unexpected flags=0x%x\\n\", flags))\n \t\treturn -EINVAL; /* no flags support for now */\n \n@@ -1448,7 +1487,9 @@ static void smbdirect_connection_send_immediate_work(struct work_struct *work)\n \tsmbdirect_log_keep_alive(sc, SMBDIRECT_LOG_INFO,\n \t\t\"send an empty message\\n\");\n \tsc->statistics.send_empty++;\n+\tsmbdirect_socket_sk_lock(sc);\n \tret = smbdirect_connection_send_single_iter(sc, NULL, NULL, 0, 0);\n+\tsmbdirect_socket_sk_unlock(sc);\n \tif (ret < 0) {\n \t\tsmbdirect_log_write(sc, SMBDIRECT_LOG_ERR,\n \t\t\t\"smbdirect_connection_send_single_iter ret=%1pe\\n\",\n@@ -1632,6 +1673,12 @@ void smbdirect_connection_recv_io_done(struct ib_cq *cq, struct ib_wc *wc)\n \t\t * If any sender is waiting for credits, unblock it\n \t\t */\n \t\twake_up(&sc->send_io.credits.wait_queue);\n+\t\tif (sc->sk.sk_family) {\n+\t\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\t\tsk->sk_write_space(sk);\n+\t\t}\n \t}\n \n \t/* Send an immediate response right away if requested */\n@@ -1652,6 +1699,12 @@ void smbdirect_connection_recv_io_done(struct ib_cq *cq, struct ib_wc *wc)\n \n \t\tsmbdirect_connection_reassembly_append_recv_io(sc, recv_io, data_length);\n \t\twake_up(&sc->recv_io.reassembly.wait_queue);\n+\t\tif (sc->sk.sk_family) {\n+\t\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\t\tsk->sk_data_ready(sk);\n+\t\t}\n \t} else\n \t\tsmbdirect_connection_put_recv_io(recv_io);\n \n@@ -1735,6 +1788,9 @@ int smbdirect_connection_recv_io_refill(struct smbdirect_socket *sc)\n \t/*\n \t * If the last send credit is waiting for credits\n \t * it can grant we need to wake it up\n+\t *\n+\t * This needs to wake up smbdirect_connection_send_single_iter()\n+\t * only, so we don't need sk->sk_write_space() here.\n \t */\n \tif (atomic_read(&sc->send_io.bcredits.count) == 0 &&\n \t atomic_read(&sc->send_io.credits.count) == 0)\n@@ -1922,9 +1978,11 @@ int smbdirect_connection_recvmsg(struct smbdirect_socket *sc,\n \n \tsmbdirect_log_read(sc, SMBDIRECT_LOG_INFO,\n \t\t\"wait_event on more data\\n\");\n+\tsmbdirect_socket_sk_unlock(sc);\n \tret = wait_event_interruptible(sc->recv_io.reassembly.wait_queue,\n \t\t\t\t sc->recv_io.reassembly.data_length >= size ||\n \t\t\t\t sc->status != SMBDIRECT_SOCKET_CONNECTED);\n+\tsmbdirect_socket_sk_lock(sc);\n \t/* Don't return any data if interrupted */\n \tif (ret)\n \t\treturn ret;\ndiff --git a/fs/smb/common/smbdirect/smbdirect_devices.c b/fs/smb/common/smbdirect/smbdirect_devices.c\nindex aaab99e9c045..da0edc104e48 100644\n--- a/fs/smb/common/smbdirect/smbdirect_devices.c\n+++ b/fs/smb/common/smbdirect/smbdirect_devices.c\n@@ -257,7 +257,7 @@ __init int smbdirect_devices_init(void)\n \treturn 0;\n }\n \n-__exit void smbdirect_devices_exit(void)\n+__cold void smbdirect_devices_exit(void)\n {\n \tstruct smbdirect_device *sdev, *tmp;\n \ndiff --git a/fs/smb/common/smbdirect/smbdirect_internal.h b/fs/smb/common/smbdirect/smbdirect_internal.h\nindex 30a1b8643657..517ff0533032 100644\n--- a/fs/smb/common/smbdirect/smbdirect_internal.h\n+++ b/fs/smb/common/smbdirect/smbdirect_internal.h\n@@ -12,8 +12,6 @@\n #include \"smbdirect_pdu.h\"\n #include \"smbdirect_public.h\"\n \n-#include <linux/mutex.h>\n-\n struct smbdirect_module_state {\n \tstruct mutex mutex;\n \n@@ -30,6 +28,8 @@ struct smbdirect_module_state {\n \t\trwlock_t lock;\n \t\tstruct list_head list;\n \t} devices;\n+\n+\tstruct smbdirect_socket_parameters default_parameters;\n };\n \n extern struct smbdirect_module_state smbdirect_globals;\n@@ -46,10 +46,58 @@ struct smbdirect_device {\n \tchar ib_name[IB_DEVICE_NAME_MAX];\n };\n \n+static __always_inline void smbdirect_socket_sk_owned_by_me(struct smbdirect_socket *sc)\n+{\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t/* assert it is already locked */\n+\t\tsock_owned_by_me(sk);\n+\t}\n+}\n+\n+static __always_inline void smbdirect_socket_sk_not_owned_by_me(struct smbdirect_socket *sc)\n+{\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t/* assert it is not already locked */\n+\t\tsock_not_owned_by_me(sk);\n+\t}\n+}\n+\n+static __always_inline void smbdirect_socket_sk_lock(struct smbdirect_socket *sc)\n+{\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t/* assert it is not already locked */\n+\t\tsock_not_owned_by_me(sk);\n+\n+\t\tlock_sock(sk);\n+\t}\n+}\n+\n+static __always_inline void smbdirect_socket_sk_unlock(struct smbdirect_socket *sc)\n+{\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\t/* assert it is already locked */\n+\t\tsock_owned_by_me(sk);\n+\n+\t\trelease_sock(sk);\n+\t}\n+}\n+\n int smbdirect_socket_init_new(struct net *net, struct smbdirect_socket *sc);\n \n int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdirect_socket *sc);\n \n+int smbdirect_socket_sync_saddr_to_sk(struct smbdirect_socket *sc, bool *_is_any_addr);\n+\n+int smbdirect_socket_sync_daddr_to_sk(struct smbdirect_socket *sc);\n+\n void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc,\n \t\t\t\t\t const char *macro_name,\n \t\t\t\t\t unsigned int lvl,\n@@ -135,7 +183,12 @@ int smbdirect_accept_connect_request(struct smbdirect_socket *sc,\n \n void smbdirect_accept_negotiate_finish(struct smbdirect_socket *sc, u32 ntstatus);\n \n+void smbdirect_sk_reclassify(struct sock *sk);\n+\n __init int smbdirect_devices_init(void);\n-__exit void smbdirect_devices_exit(void);\n+__cold void smbdirect_devices_exit(void);\n+\n+__init int smbdirect_proto_init(void);\n+__exit void smbdirect_proto_exit(void);\n \n #endif /* __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__ */\ndiff --git a/fs/smb/common/smbdirect/smbdirect_listen.c b/fs/smb/common/smbdirect/smbdirect_listen.c\nindex 05c7902e7020..a6e08d82dc73 100644\n--- a/fs/smb/common/smbdirect/smbdirect_listen.c\n+++ b/fs/smb/common/smbdirect/smbdirect_listen.c\n@@ -74,6 +74,12 @@ int smbdirect_socket_listen(struct smbdirect_socket *sc, int backlog)\n \t */\n \tsc->listen.backlog = backlog;\n \n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\tinet_sk_set_state(sk, TCP_LISTEN);\n+\t}\n+\n \tif (sc->rdma.cm_id->device)\n \t\tsmbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n \t\t\t\"listening on addr: %pISpsfc dev: %.*s\\n\",\n@@ -209,6 +215,7 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,\n \t\t\t\t\t const struct rdma_cm_event *event)\n {\n \tconst struct smbdirect_socket_parameters *lsp = &lsc->parameters;\n+\tstruct sock *nsk = NULL;\n \tstruct smbdirect_socket *nsc;\n \tunsigned long flags;\n \tsize_t backlog = max_t(size_t, 1, lsc->listen.backlog);\n@@ -265,9 +272,39 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,\n \t\treturn -EBUSY;\n \t}\n \n-\tret = smbdirect_socket_create_accepting(new_id, &nsc);\n-\tif (ret)\n-\t\tgoto socket_init_failed;\n+\tif (lsc->sk.sk_family) {\n+\t\tstruct sock *lsk = &lsc->sk;\n+\n+\t\tret = -ENOMEM;\n+\t\tnsk = sk_clone(lsk, lsk->sk_allocation, false);\n+\t\tif (!nsk)\n+\t\t\tgoto sk_clone_failed;\n+\t\t/* sk_clone_lock() increments refcnt to 2; drop the extra. */\n+\t\t__sock_put(nsk);\n+\t\t/* sk_clone() already called sk_sockets_allocated_inc(sk); */\n+\t\tsock_prot_inuse_add(sock_net(nsk), nsk->sk_prot, 1);\n+\n+\t\tsmbdirect_sk_reclassify(nsk);\n+\t\tinet_sk_set_state(nsk, TCP_SYN_RECV);\n+\t\tnsc = smbdirect_socket_from_sk(nsk);\n+\n+\t\tret = smbdirect_socket_init_accepting(new_id, nsc);\n+\t\tif (ret)\n+\t\t\tgoto socket_init_failed;\n+\n+\t\t/*\n+\t\t * Note that smbdirect_sock_accept() will set\n+\t\t * SOCK_CUSTOM_SOCKOPT once [__]inet_accept()\n+\t\t * called sk_set_socket() via sock_graft().\n+\t\t */\n+\t\tWARN_ON_ONCE(nsc->orig_sk_destruct != lsc->orig_sk_destruct);\n+\t\tWARN_ON_ONCE(nsk->sk_destruct != lsk->sk_destruct);\n+\t\tWARN_ON_ONCE(nsk->sk_ipv6only != lsk->sk_ipv6only);\n+\t} else {\n+\t\tret = smbdirect_socket_create_accepting(new_id, &nsc);\n+\t\tif (ret)\n+\t\t\tgoto socket_init_failed;\n+\t}\n \n \tnsc->logging = lsc->logging;\n \tret = smbdirect_socket_set_initial_parameters(nsc, &lsc->parameters);\n@@ -302,7 +339,11 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,\n \t */\n \tnsc->ib.dev = NULL;\n \tnsc->rdma.cm_id = NULL;\n-\tsmbdirect_socket_release(nsc);\n+\tif (!nsk)\n+\t\tsmbdirect_socket_release(nsc);\n socket_init_failed:\n+\tif (nsk)\n+\t\tsk_free(nsk);\n+sk_clone_failed:\n \treturn ret;\n }\ndiff --git a/fs/smb/common/smbdirect/smbdirect_main.c b/fs/smb/common/smbdirect/smbdirect_main.c\nindex fe6e8d93c34c..ccbe979332af 100644\n--- a/fs/smb/common/smbdirect/smbdirect_main.c\n+++ b/fs/smb/common/smbdirect/smbdirect_main.c\n@@ -12,6 +12,7 @@ struct smbdirect_module_state smbdirect_globals = {\n \n static __init int smbdirect_module_init(void)\n {\n+\tstruct smbdirect_socket_parameters *sp;\n \tint ret = -ENOMEM;\n \n \tpr_notice(\"subsystem loading...\\n\");\n@@ -73,10 +74,52 @@ static __init int smbdirect_module_init(void)\n \tif (ret)\n \t\tgoto devices_init_failed;\n \n+\t/*\n+\t * Create the global default parameters\n+\t */\n+\tsp = &smbdirect_globals.default_parameters;\n+\tsp->resolve_addr_timeout_msec = 5 * 1000;\n+\tsp->resolve_route_timeout_msec = 5 * 1000;\n+\tsp->rdma_connect_timeout_msec = 5 * 1000;\n+\tsp->negotiate_timeout_msec = 120 * 1000;\n+\tsp->initiator_depth = 1; /* the server should change this */\n+\tsp->responder_resources = 1; /* the client should change this */\n+\tsp->recv_credit_max = 255;\n+\tsp->send_credit_target = 255;\n+\tsp->max_send_size = 1364;\n+\t/*\n+\t * The maximum fragmented upper-layer payload receive size supported\n+\t *\n+\t * Assume max_payload_per_credit is\n+\t * smbd_max_receive_size - 24 = 1340\n+\t *\n+\t * The maximum number would be\n+\t * smbd_receive_credit_max * max_payload_per_credit\n+\t *\n+\t * 1340 * 255 = 341700 (0x536C4)\n+\t *\n+\t * The minimum value from the spec is 131072 (0x20000)\n+\t *\n+\t * For now we use the logic we used before:\n+\t * (1364 * 255) / 2 = 173910 (0x2A756)\n+\t */\n+\tsp->max_fragmented_recv_size = (1364 * 255) / 2;\n+\tsp->max_recv_size = 1364;\n+\tsp->max_read_write_size = 0; /* the server should change this */\n+\tsp->max_frmr_depth = 0; /* the client should change this */\n+\tsp->keepalive_interval_msec = 120 * 1000;\n+\tsp->keepalive_timeout_msec = 5 * 1000;\n+\n+\tret = smbdirect_proto_init();\n+\tif (ret)\n+\t\tgoto proto_init_failed;\n+\n \tmutex_unlock(&smbdirect_globals.mutex);\n \tpr_notice(\"subsystem loaded\\n\");\n \treturn 0;\n \n+proto_init_failed:\n+\tsmbdirect_devices_exit();\n devices_init_failed:\n \tdestroy_workqueue(smbdirect_globals.workqueues.cleanup);\n alloc_cleanup_wq_failed:\n@@ -101,6 +144,8 @@ static __exit void smbdirect_module_exit(void)\n \tpr_notice(\"subsystem unloading...\\n\");\n \tmutex_lock(&smbdirect_globals.mutex);\n \n+\tsmbdirect_proto_exit();\n+\n \tsmbdirect_devices_exit();\n \n \tdestroy_workqueue(smbdirect_globals.workqueues.accept);\ndiff --git a/fs/smb/common/smbdirect/smbdirect_mr.c b/fs/smb/common/smbdirect/smbdirect_mr.c\nindex fa9be8089925..86bb72ed10ae 100644\n--- a/fs/smb/common/smbdirect/smbdirect_mr.c\n+++ b/fs/smb/common/smbdirect/smbdirect_mr.c\n@@ -167,9 +167,11 @@ smbdirect_connection_get_mr_io(struct smbdirect_socket *sc)\n \tint ret;\n \n again:\n+\tsmbdirect_socket_sk_unlock(sc);\n \tret = wait_event_interruptible(sc->mr_io.ready.wait_queue,\n \t\t\t\t atomic_read(&sc->mr_io.ready.count) ||\n \t\t\t\t sc->status != SMBDIRECT_SOCKET_CONNECTED);\n+\tsmbdirect_socket_sk_lock(sc);\n \tif (ret) {\n \t\tsmbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,\n \t\t\t\"wait_event_interruptible ret=%d (%1pe)\\n\",\n@@ -281,7 +283,9 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc,\n \t\treturn NULL;\n \t}\n \n+\tsmbdirect_socket_sk_lock(sc);\n \tmr = smbdirect_connection_get_mr_io(sc);\n+\tsmbdirect_socket_sk_unlock(sc);\n \tif (!mr) {\n \t\tsmbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,\n \t\t\t\"smbdirect_connection_get_mr_io returning NULL\\n\");\n@@ -415,6 +419,12 @@ void smbdirect_connection_deregister_mr_io(struct smbdirect_mr_io *mr)\n \tif (mr->state == SMBDIRECT_MR_DISABLED)\n \t\tgoto put_kref;\n \n+\t/*\n+\t * We are protected by mr->mutex\n+\t * without lock_sock().\n+\t */\n+\tsmbdirect_socket_sk_not_owned_by_me(sc);\n+\n \tif (sc->status != SMBDIRECT_SOCKET_CONNECTED) {\n \t\tsmbdirect_mr_io_disable_locked(mr);\n \t\tgoto put_kref;\ndiff --git a/fs/smb/common/smbdirect/smbdirect_proto.c b/fs/smb/common/smbdirect/smbdirect_proto.c\nnew file mode 100644\nindex 000000000000..1a832d52eb89\n--- /dev/null\n+++ b/fs/smb/common/smbdirect/smbdirect_proto.c\n@@ -0,0 +1,1549 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * Copyright (c) 2025 Stefan Metzmacher\n+ */\n+\n+#include \"smbdirect_internal.h\"\n+#include <net/protocol.h>\n+#include <net/inet_common.h>\n+#include <linux/bpf-cgroup.h>\n+#include <linux/errname.h>\n+\n+#define SMBDIRECT_FN_GENERIC(__sk, __fmt, __args...) do { \\\n+\tstruct smbdirect_socket *__sc = smbdirect_socket_from_sk(__sk); \\\n+\t__smbdirect_log_generic(__sc, SMBDIRECT_LOG_INFO, SMBDIRECT_LOG_SK, \\\n+\t\t__fmt \" sc=%p %s first_error=%1pe kern=%u locked=%u refs=%u dead=%u mrefs=%u\\n\", \\\n+\t\t##__args, __sc, \\\n+\t\tsmbdirect_socket_status_string(__sc->status), \\\n+\t\tSMBDIRECT_DEBUG_ERR_PTR(__sc->first_error), \\\n+\t\t(__sk)->sk_kern_sock, \\\n+\t\tsock_owned_by_user_nocheck(__sk), \\\n+\t\trefcount_read(&((__sk)->sk_refcnt)), \\\n+\t\tsock_flag(__sk, SOCK_DEAD), \\\n+\t\tmodule_refcount(THIS_MODULE)); \\\n+} while (0)\n+\n+#define SMBDIRECT_FN_COMMENT(__sk, __comment) \\\n+\tSMBDIRECT_FN_GENERIC(__sk, \"%s with\", __comment)\n+\n+#define SMBDIRECT_FN_CALLED(__sk) \\\n+\tSMBDIRECT_FN_GENERIC(__sk, \"Called for\")\n+\n+#define SMBDIRECT_FN_RETURN_VOID(__sk) \\\n+\tSMBDIRECT_FN_GENERIC(__sk, \"Returning for\")\n+\n+#define SMBDIRECT_FN_RETURN_POLL(__sk, __mask) \\\n+\tSMBDIRECT_FN_GENERIC(__sk, \"Returning mask=0x%x for\", __mask)\n+\n+#define SMBDIRECT_FN_RETURN_INT(__sk, __ret) do { \\\n+\tbool __is_err = IS_ERR(SMBDIRECT_DEBUG_ERR_PTR(__ret)); \\\n+\tSMBDIRECT_FN_GENERIC(__sk, \"Returning ret=%d%s%s%s for\", \\\n+\t\t(__ret), \\\n+\t\t__is_err ? \" (\" : \"\", \\\n+\t\t__is_err ? errname(__ret) : \"\", \\\n+\t\t__is_err ? \")\" : \"\"); \\\n+} while (0)\n+\n+static bool smbdirect_sk_logging_needed(struct smbdirect_socket *sc,\n+\t\t\t\t\tvoid *private_ptr,\n+\t\t\t\t\tunsigned int lvl,\n+\t\t\t\t\tunsigned int cls)\n+{\n+\t/*\n+\t * Only errors by default.\n+\t */\n+\tif (lvl <= SMBDIRECT_LOG_ERR)\n+\t\treturn true;\n+\treturn false;\n+}\n+\n+static void smbdirect_sk_logging_vaprintf(struct smbdirect_socket *sc,\n+\t\t\t\t\t const char *func,\n+\t\t\t\t\t unsigned int line,\n+\t\t\t\t\t void *private_ptr,\n+\t\t\t\t\t unsigned int lvl,\n+\t\t\t\t\t unsigned int cls,\n+\t\t\t\t\t struct va_format *vaf)\n+{\n+\tif (lvl <= SMBDIRECT_LOG_ERR)\n+\t\tpr_err(\"%s:%u %pV\", func, line, vaf);\n+\telse\n+\t\tpr_info(\"%s:%u %pV\", func, line, vaf);\n+}\n+\n+void smbdirect_sk_reclassify(struct sock *sk)\n+{\n+#ifdef CONFIG_DEBUG_LOCK_ALLOC\n+\tstatic struct lock_class_key sk_key[2];\n+\tstatic struct lock_class_key slock_key[2];\n+\n+\tif (WARN_ON_ONCE(!sock_allow_reclassification(sk)))\n+\t\treturn;\n+\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\t/*\n+\t\t * Before we reset the owner we\n+\t\t * need to drop the reference of the\n+\t\t * existing module, this is only\n+\t\t * really relevant for AF_INET,\n+\t\t * as that is always builtin\n+\t\t * there's no potential leak\n+\t\t * of module references. We do it\n+\t\t * mainly in order to match the\n+\t\t * AF_INET6 case.\n+\t\t */\n+\t\tsk_owner_put(sk);\n+\t\tsk_owner_clear(sk);\n+\n+\t\tsock_lock_init_class_and_name(sk,\n+\t\t\t\t\t \"slock-AF_INET-IPPROTO-SMBDIRECT\",\n+\t\t\t\t\t &slock_key[0],\n+\t\t\t\t\t \"sk_lock-AF_INET-IPPROTO-SMBDIRECT\",\n+\t\t\t\t\t &sk_key[0]);\n+\n+\t\t/*\n+\t\t * Now that we reclassified the socket\n+\t\t * we're also the new sk_owner, but that's\n+\t\t * not needed as there's still a reference\n+\t\t * on sk->sk_prot->owner, which is dropped\n+\t\t * in sk_prot_free(). But in order to\n+\t\t * avoid module reference leaks to our\n+\t\t * own module we need to put and clear\n+\t\t * sk_owner, in order to allow callers\n+\t\t * to do their own reclassification.\n+\t\t */\n+\t\tsk_owner_put(sk);\n+\t\tsk_owner_clear(sk);\n+\t\tbreak;\n+\tcase AF_INET6:\n+\t\t/*\n+\t\t * Before we reset the owner we\n+\t\t * need to drop the reference of the\n+\t\t * existing module.\n+\t\t *\n+\t\t * As we also use inet6_register_protosw()\n+\t\t * and other symbols from a possible\n+\t\t * ipv6.ko, we already have enough\n+\t\t * module references in order to avoid\n+\t\t * unloading of ipv6.ko, while smbdirect.ko\n+\t\t * is loaded.\n+\t\t *\n+\t\t * However when smbdirect.ko is unloaded\n+\t\t * we should not leak references in order\n+\t\t * to allow ipv6.ko to be unloaded as well.\n+\t\t */\n+\t\tsk_owner_put(sk);\n+\t\tsk_owner_clear(sk);\n+\n+\t\tsock_lock_init_class_and_name(sk,\n+\t\t\t\t\t \"slock-AF_INET6-IPPROTO-SMBDIRECT\",\n+\t\t\t\t\t &slock_key[1],\n+\t\t\t\t\t \"sk_lock-AF_INET6-IPPROTO-SMBDIRECT\",\n+\t\t\t\t\t &sk_key[1]);\n+\n+\t\t/*\n+\t\t * Now that we reclassified the socket\n+\t\t * we're also the new sk_owner, but that's\n+\t\t * not needed as there's still a reference\n+\t\t * on sk->sk_prot->owner, which is dropped\n+\t\t * in sk_prot_free(). But in order to\n+\t\t * avoid module reference leaks to our\n+\t\t * own module we need to put and clear\n+\t\t * sk_owner, in order to allow callers\n+\t\t * to do their own reclassification.\n+\t\t */\n+\t\tsk_owner_put(sk);\n+\t\tsk_owner_clear(sk);\n+\t\tbreak;\n+\tdefault:\n+\t\tWARN_ON_ONCE(1);\n+\t}\n+#endif /* CONFIG_DEBUG_LOCK_ALLOC */\n+}\n+\n+static void smbdirect_sk_destruct(struct sock *sk)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\n+\t/*\n+\t * Called by sk_free()\n+\t */\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\tif (WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_DESTROYED)) {\n+\t\tpr_err(\"Attempt to release SMBDIRECT socket in status %s sc %p\\n\",\n+\t\t smbdirect_socket_status_string(sc->status), sc);\n+\t\tSMBDIRECT_FN_RETURN_VOID(sk);\n+\t\treturn;\n+\t}\n+\n+\tSMBDIRECT_FN_COMMENT(sk, \"calling orig_sk_destruct\");\n+\tsmbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n+\t\t\"sc[%p]->orig_sk_destruct[%ps]\\n\",\n+\t\tsc, sc->orig_sk_destruct);\n+\tsc->orig_sk_destruct(sk);\n+\tSMBDIRECT_FN_RETURN_VOID(sk);\n+}\n+\n+static int smbdirect_sk_init(struct sock *sk)\n+{\n+\tstruct socket *sock = sk->sk_socket;\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tconst struct smbdirect_socket_parameters *sp = &smbdirect_globals.default_parameters;\n+\tvoid (*orig_sk_destruct)(struct sock *sk);\n+\tint ret;\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tsmbdirect_sk_reclassify(sk);\n+\n+\tsmbdirect_socket_init(sc);\n+\tsmbdirect_socket_set_logging(sc,\n+\t\t\t\t NULL,\n+\t\t\t\t smbdirect_sk_logging_needed,\n+\t\t\t\t smbdirect_sk_logging_vaprintf);\n+\n+\tsmbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n+\t\t\"Called for sk=%p family=%u protocol=%u type=%u\\n\",\n+\t\tsk, sk->sk_family, sk->sk_protocol, sk->sk_type);\n+\n+\tsk_sockets_allocated_inc(sk);\n+\tsock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);\n+\n+\torig_sk_destruct = sk->sk_destruct;\n+\tSMBDIRECT_FN_COMMENT(sk, \"remembered orig_sk_destruct\");\n+\tsmbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n+\t\t\"sc[%p]->orig_sk_destruct[%ps]\\n\",\n+\t\tsc, orig_sk_destruct);\n+\tsc->orig_sk_destruct = orig_sk_destruct;\n+\tsk->sk_destruct = smbdirect_sk_destruct;\n+\n+\t/*\n+\t * We want to handle all sockopts explicitly\n+\t * and only support what we really support.\n+\t */\n+\tset_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags);\n+\t/*\n+\t * There are no legacy callers, so we are strict\n+\t * regarding ipv4 vs. ipv6.\n+\t */\n+\tsk->sk_ipv6only = true;\n+\n+\t/*\n+\t * No userspace sockets yet...\n+\t */\n+\tif (!sk->sk_kern_sock) {\n+\t\tsc->first_error = -EPROTONOSUPPORT;\n+\t\tSMBDIRECT_FN_COMMENT(sk, \"No userspace sockets\");\n+\t\treturn -EPROTONOSUPPORT;\n+\t}\n+\n+\tret = smbdirect_socket_init_new(sock_net(sk), sc);\n+\tif (ret)\n+\t\tgoto socket_init_failed;\n+\t/*\n+\t * smbdirect_socket_init_new() called smbdirect_socket_init() again,\n+\t * so we need to call smbdirect_socket_set_logging() again!\n+\t */\n+\tsmbdirect_socket_set_logging(sc,\n+\t\t\t\t NULL,\n+\t\t\t\t smbdirect_sk_logging_needed,\n+\t\t\t\t smbdirect_sk_logging_vaprintf);\n+\n+\tWARN_ON_ONCE(sc->orig_sk_destruct != orig_sk_destruct);\n+\tWARN_ON_ONCE(sk->sk_destruct != smbdirect_sk_destruct);\n+\n+\tret = smbdirect_socket_set_initial_parameters(sc, sp);\n+\tif (ret)\n+\t\tgoto set_params_failed;\n+\n+\tret = smbdirect_socket_set_kernel_settings(sc, IB_POLL_SOFTIRQ, sk->sk_allocation);\n+\tif (ret)\n+\t\tgoto set_settings_failed;\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, 0);\n+\treturn 0;\n+\n+set_settings_failed:\n+set_params_failed:\n+socket_init_failed:\n+\tsc->first_error = ret;\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static void smbdirect_sk_destroy(struct sock *sk)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\t/*\n+\t * For now do a sync disconnect/destroy\n+\t *\n+\t * SMBDIRECT_LOG_INFO is enough here\n+\t * as this is the typical case where\n+\t * we terminate the connection ourself.\n+\t */\n+\tsmbdirect_socket_schedule_cleanup_lvl(sc,\n+\t\t\t\t\t SMBDIRECT_LOG_INFO,\n+\t\t\t\t\t -ESHUTDOWN);\n+\tsmbdirect_socket_destroy_sync(sc);\n+\n+\tsock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);\n+\tsk_sockets_allocated_dec(sk);\n+\n+\tSMBDIRECT_FN_RETURN_VOID(sk);\n+}\n+\n+static int smbdirect_sk_hash(struct sock *sk)\n+{\n+\tSMBDIRECT_FN_CALLED(sk);\n+\treturn 0;\n+}\n+\n+static void smbdirect_sk_unhash(struct sock *sk)\n+{\n+\tSMBDIRECT_FN_CALLED(sk);\n+}\n+\n+static void smbdirect_sk_release_cb(struct sock *sk)\n+{\n+\t/*\n+\t * Called from release_sock()\n+\t */\n+\tSMBDIRECT_FN_CALLED(sk);\n+}\n+\n+static int smbdirect_sk_pre_bind(struct sock *sk,\n+\t\t\t\t struct sockaddr_unsized *uaddr,\n+\t\t\t\t int *addr_len,\n+\t\t\t\t u32 *flags,\n+\t\t\t\t u16 *port)\n+{\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tif (*addr_len < sizeof(uaddr->sa_family))\n+\t\treturn -EINVAL;\n+\n+\t/* AF_UNSPEC is not allowed */\n+\tif (sk->sk_family != uaddr->sa_family)\n+\t\treturn -EAFNOSUPPORT;\n+\n+\t/*\n+\t * BPF prog is run before any checks are done so that if the prog\n+\t * changes context in a wrong way it will be caught.\n+\t */\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\tif (*addr_len < sizeof(struct sockaddr_in))\n+\t\t\treturn -EINVAL;\n+\n+\t\t*port = ntohs(((struct sockaddr_in *)uaddr)->sin_port);\n+\n+\t\treturn BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr_len,\n+\t\t\t\t\t\t\t CGROUP_INET4_BIND,\n+\t\t\t\t\t\t\t flags);\n+\tcase AF_INET6:\n+\t\t/*\n+\t\t * We require a full struct sockaddr_in6 (28 bytes) instead of a\n+\t\t * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don't\n+\t\t * have any legacy callers in userspace and the\n+\t\t * rdma layer also expects that.\n+\t\t */\n+\t\tif (*addr_len < sizeof(struct sockaddr_in6))\n+\t\t\treturn -EINVAL;\n+\n+\t\t*port = ntohs(((struct sockaddr_in6 *)uaddr)->sin6_port);\n+\n+\t\treturn BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr_len,\n+\t\t\t\t\t\t\t CGROUP_INET6_BIND,\n+\t\t\t\t\t\t\t flags);\n+\t}\n+\n+\treturn -EAFNOSUPPORT;\n+}\n+\n+static int smbdirect_sk_do_bind(struct sock *sk,\n+\t\t\t\tstruct sockaddr_unsized *uaddr,\n+\t\t\t\tconst u32 flags,\n+\t\t\t\tconst u16 port)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tbool is_any_addr = true;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\tif (flags & BIND_WITH_LOCK)\n+\t\tsock_owned_by_me(sk);\n+\telse\n+\t\tsock_not_owned_by_me(sk);\n+\n+\tret = smbdirect_socket_bind(sc, (struct sockaddr *)uaddr);\n+\tif (ret) {\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tret = smbdirect_socket_sync_saddr_to_sk(sc, &is_any_addr);\n+\tif (ret) {\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\t/* Make sure we are allowed to bind here. */\n+\tif (sk->sk_num && !(flags & BIND_FROM_BPF)) {\n+\t\tswitch (sk->sk_family) {\n+\t\tcase AF_INET:\n+\t\t\tret = BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk);\n+\t\t\tif (ret) {\n+\t\t\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\t\t\treturn ret;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase AF_INET6:\n+\t\t\tret = BPF_CGROUP_RUN_PROG_INET6_POST_BIND(sk);\n+\t\t\tif (ret) {\n+\t\t\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\t\t\treturn ret;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (!is_any_addr)\n+\t\tsk->sk_userlocks |= SOCK_BINDADDR_LOCK;\n+\tif (port)\n+\t\tsk->sk_userlocks |= SOCK_BINDPORT_LOCK;\n+\n+\tret = 0;\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sk_bind(struct sock *sk, struct sockaddr_unsized *addr, int addr_len)\n+{\n+\tstruct net *net = sock_net(sk);\n+\tu32 flags = BIND_WITH_LOCK;\n+\tu16 port = 0;\n+\tu16 check_port = 0;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tret = smbdirect_sk_pre_bind(sk, addr, &addr_len, &flags, &port);\n+\tif (ret) {\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * treat the iwarp tcp port for\n+\t * smb (5445) as the main smb port (445)\n+\t * and only allow the bind if 445\n+\t * would be allowed.\n+\t */\n+\tif (port == 5445)\n+\t\tcheck_port = 445;\n+\telse\n+\t\tcheck_port = port;\n+\n+\tif (!(flags & BIND_NO_CAP_NET_BIND_SERVICE) &&\n+\t check_port && inet_port_requires_bind_service(net, check_port) &&\n+\t !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {\n+\t\tret = -EACCES;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (flags & BIND_WITH_LOCK)\n+\t\tlock_sock(sk);\n+\n+\tret = smbdirect_sk_do_bind(sk, addr, flags, port);\n+\n+\tif (flags & BIND_WITH_LOCK)\n+\t\trelease_sock(sk);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static struct sock *smbdirect_sk_accept(struct sock *lsk, struct proto_accept_arg *arg)\n+{\n+\tstruct smbdirect_socket *lsc = smbdirect_socket_from_sk(lsk);\n+\tlong timeo = sock_rcvtimeo(lsk, arg->flags & O_NONBLOCK);\n+\tstruct smbdirect_socket *nsc;\n+\tstruct sock *nsk;\n+\n+\tSMBDIRECT_FN_CALLED(lsk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(lsk);\n+\n+\tlock_sock(lsk);\n+\tnsc = smbdirect_socket_accept(lsc, timeo, arg);\n+\trelease_sock(lsk);\n+\tif (!nsc) {\n+\t\tSMBDIRECT_FN_RETURN_INT(lsk, arg->err);\n+\t\treturn NULL;\n+\t}\n+\tnsk = &nsc->sk;\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(nsk);\n+\n+\tSMBDIRECT_FN_RETURN_INT(lsk, 0);\n+\treturn nsk;\n+}\n+\n+static int smbdirect_sk_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\n+{\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\tif (addr_len < sizeof(uaddr->sa_family))\n+\t\treturn -EINVAL;\n+\n+\tif (sk->sk_family != uaddr->sa_family)\n+\t\treturn -EAFNOSUPPORT;\n+\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\tif (addr_len < sizeof(struct sockaddr_in))\n+\t\t\treturn -EINVAL;\n+\n+\t\treturn BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr, &addr_len);\n+\tcase AF_INET6:\n+\t\t/*\n+\t\t * We require a full struct sockaddr_in6 (28 bytes) instead of a\n+\t\t * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don't\n+\t\t * have any legacy callers in userspace and the\n+\t\t * rdma layer also expects that.\n+\t\t */\n+\t\tif (addr_len < sizeof(struct sockaddr_in6))\n+\t\t\treturn -EINVAL;\n+\n+\t\treturn BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr, &addr_len);\n+\t}\n+\n+\treturn -EAFNOSUPPORT;\n+}\n+\n+static int smbdirect_sk_connect(struct sock *sk, struct sockaddr_unsized *addr, int addr_len)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\tret = smbdirect_connect(sc, (struct sockaddr *)addr);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sk_setsockopt(struct sock *sk, int level, int optname,\n+\t\t\t\t sockptr_t optval, unsigned int optlen)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tswitch (level) {\n+\tdefault:\n+\t\tSMBDIRECT_FN_COMMENT(sk, \"default\");\n+\t\tsmbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n+\t\t\t\"level=%d optname=%d for sk=%p\\n\",\n+\t\t\tlevel, optname, sk);\n+\t\tret = -EOPNOTSUPP;\n+\t\tbreak;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sk_getsockopt(struct sock *sk, int level, int optname,\n+\t\t\t\t char __user *optval, int __user *optlen)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tswitch (level) {\n+\tdefault:\n+\t\tSMBDIRECT_FN_COMMENT(sk, \"default\");\n+\t\tsmbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n+\t\t\t\"level=%d optname=%d for sk=%p\\n\",\n+\t\t\tlevel, optname, sk);\n+\t\tret = -EOPNOTSUPP;\n+\t\tbreak;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sk_ioctl(struct sock *sk, int cmd, int *karg)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tswitch (cmd) {\n+\tdefault:\n+\t\tSMBDIRECT_FN_COMMENT(sk, \"default\");\n+\t\tsmbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n+\t\t\t\"cmd=%d (0x%x) for sk=%p\\n\",\n+\t\t\tcmd, cmd, sk);\n+\t\tret = -ENOIOCTLCMD;\n+\t\tbreak;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static inline size_t smbdirect_cmsg_count(const struct msghdr *_msg,\n+\t\t\t\t\t int *first_sol_smbdirect_type)\n+{\n+\tstruct msghdr *msg = (struct msghdr *)(uintptr_t)(const void *)_msg;\n+\tstruct cmsghdr *cmsg = NULL;\n+\tsize_t count = 0;\n+\n+\tif (first_sol_smbdirect_type != NULL)\n+\t\t*first_sol_smbdirect_type = -1;\n+\n+\tfor (cmsg = CMSG_FIRSTHDR(msg);\n+\t cmsg != NULL;\n+\t cmsg = CMSG_NXTHDR(msg, cmsg)) {\n+\t\tcount++;\n+\t\tif (cmsg->cmsg_level != SOL_SMBDIRECT)\n+\t\t\tcontinue;\n+\t\tif (first_sol_smbdirect_type != NULL) {\n+\t\t\t*first_sol_smbdirect_type = cmsg->cmsg_type;\n+\t\t\tfirst_sol_smbdirect_type = NULL;\n+\t\t}\n+\t}\n+\n+\treturn count;\n+}\n+\n+static __always_inline\n+ssize_t __smbdirect_cmsg_extract(const struct msghdr *_msg,\n+\t\t\t\t int cmsg_type,\n+\t\t\t\t void *_payload,\n+\t\t\t\t size_t payloadmin,\n+\t\t\t\t size_t payloadmax)\n+{\n+\tstruct msghdr *msg = (struct msghdr *)(uintptr_t)(const void *)_msg;\n+\tsize_t cmsg_len_min = CMSG_LEN(payloadmin);\n+\tsize_t cmsg_len_max = CMSG_LEN(payloadmax);\n+\tconst size_t cmsg_len_hdr = CMSG_LEN(0);\n+\tuint8_t *payload = (uint8_t *)_payload;\n+\tstruct cmsghdr *cmsg = NULL;\n+\tsize_t payloadlen;\n+\n+\tBUILD_BUG_ON(cmsg_len_min > cmsg_len_max);\n+\tif (WARN_ON_ONCE(cmsg_len_min > cmsg_len_max))\n+\t\treturn -EBADMSG;\n+\n+\tfor (cmsg = CMSG_FIRSTHDR(msg);\n+\t cmsg != NULL;\n+\t cmsg = CMSG_NXTHDR(msg, cmsg)) {\n+\t\tif (cmsg->cmsg_level != SOL_SMBDIRECT)\n+\t\t\tcontinue;\n+\n+\t\tif (cmsg->cmsg_type != cmsg_type)\n+\t\t\tcontinue;\n+\n+\t\tif (cmsg->cmsg_len < cmsg_len_min)\n+\t\t\treturn -EBADMSG;\n+\n+\t\tif (cmsg->cmsg_len > cmsg_len_max)\n+\t\t\treturn -EMSGSIZE;\n+\n+\t\tpayloadlen = cmsg->cmsg_len - cmsg_len_hdr;\n+\t\tif (payloadlen > 0)\n+\t\t\tmemcpy(payload, CMSG_DATA(cmsg), payloadlen);\n+\t\tif (payloadlen < payloadmax)\n+\t\t\tmemset(payload + payloadlen, 0, payloadmax - payloadlen);\n+\t\treturn payloadlen;\n+\t}\n+\n+\treturn -ENOMSG;\n+}\n+\n+static __always_inline\n+int smbdirect_buffer_remote_invalidate_cmsg_extract(const struct msghdr *msg,\n+\t\t\t\t\t\t u32 *remote_token)\n+{\n+\tstruct smbdirect_buffer_remote_invalidate_args args = {\n+\t\t.remote_token = 0,\n+\t};\n+\tssize_t ret;\n+\n+\tret = __smbdirect_cmsg_extract(msg,\n+\t\t\t\t SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE,\n+\t\t\t\t &args, sizeof(args), sizeof(args));\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\t*remote_token = args.remote_token;\n+\treturn 0;\n+}\n+\n+static int smbdirect_sk_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t msg_len)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tstruct iov_iter *iter = &msg->msg_iter;\n+\tunsigned int flags = msg->msg_flags;\n+\tsize_t cmsg_count = 0;\n+\tint cmsg_type = -1;\n+\tbool need_invalidate = false;\n+\tu32 remote_key = 0;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\tcmsg_count = smbdirect_cmsg_count(msg, &cmsg_type);\n+\tif (cmsg_count > 1) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (cmsg_type == SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE) {\n+\t\tret = smbdirect_buffer_remote_invalidate_cmsg_extract(msg, &remote_key);\n+\t\tif (!ret)\n+\t\t\tneed_invalidate = true; /* remote_key is valid */\n+\t\telse if (ret != -ENOMSG) {\n+\t\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t} else if (cmsg_count) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (WARN_ON_ONCE(iov_iter_rw(iter) != ITER_SOURCE)) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (WARN_ON_ONCE(iov_iter_count(iter) != msg_len)) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (flags & MSG_DONTWAIT) {\n+\t\tif (!sc->first_error && msg_len && atomic_read(&sc->send_io.credits.count) == 0) {\n+\t\t\tret = -EAGAIN;\n+\t\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tflags &= ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL);\n+\n+\tret = smbdirect_connection_send_iter(sc,\n+\t\t\t\t\t iter,\n+\t\t\t\t\t flags,\n+\t\t\t\t\t need_invalidate,\n+\t\t\t\t\t remote_key);\n+\tif (ret < 0)\n+\t\t/* Handle error and possibly send SIGPIPE. */\n+\t\tret = sk_stream_error(sk, msg->msg_flags, ret);\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sk_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)\n+{\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tlock_sock(sk);\n+\tret = smbdirect_sk_sendmsg_locked(sk, msg, msg_len);\n+\trelease_sock(sk);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sk_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tstruct iov_iter *iter = &msg->msg_iter;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tif (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tif (WARN_ON_ONCE(iov_iter_rw(iter) != ITER_DEST)) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * For now smbdirect_connection_recvmsg() relies\n+\t * on this assertion and the current in kernel\n+\t * users are working that way.\n+\t */\n+\tif (WARN_ON_ONCE(iov_iter_count(iter) != len)) {\n+\t\tret = -EINVAL;\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\treturn ret;\n+\t}\n+\n+\tlock_sock(sk);\n+\tif (flags & MSG_DONTWAIT) {\n+\t\tif (!sc->first_error && len && sc->recv_io.reassembly.data_length == 0) {\n+\t\t\tret = -EAGAIN;\n+\t\t\trelease_sock(sk);\n+\t\t\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\tflags &= ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL);\n+\tret = smbdirect_connection_recvmsg(sc, msg, flags);\n+\tif (msg->msg_get_inq && ret >= 0)\n+\t\tmsg->msg_inq = sc->recv_io.reassembly.data_length;\n+\trelease_sock(sk);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static void smbdirect_sk_shutdown(struct sock *sk, int how)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\tsmbdirect_socket_schedule_cleanup(sc, -ESHUTDOWN);\n+\n+\tSMBDIRECT_FN_RETURN_VOID(sk);\n+}\n+\n+static int smbdirect_sk_disconnect(struct sock *sk, int flags)\n+{\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\tsmbdirect_socket_schedule_cleanup(sc, -ESHUTDOWN);\n+\n+\tif (flags & O_NONBLOCK) {\n+\t\tif (sc->status >= SMBDIRECT_SOCKET_DISCONNECTED) {\n+\t\t\tSMBDIRECT_FN_RETURN_INT(sk, 0);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\t/*\n+\t\t * This will cause SS_DISCONNECTING in\n+\t\t * smbdirect_sock_connect_locked().\n+\t\t */\n+\t\tSMBDIRECT_FN_RETURN_INT(sk, sc->first_error);\n+\t\treturn sc->first_error;\n+\t}\n+\n+\tsmbdirect_socket_destroy_sync(sc);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, 0);\n+\treturn 0;\n+}\n+\n+static void smbdirect_sk_close(struct sock *sk, long timeout)\n+{\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\t/*\n+\t * We hold an additional reference so\n+\t * that the sock_put() in sk_common_release()\n+\t * doesn't call sk_free(), that is potentially\n+\t * deferred to our sock_put() after release_sock().\n+\t *\n+\t * Note that sk_common_release() calls\n+\t * smbdirect_sk_destroy() as the first thing.\n+\t */\n+\tsock_hold(sk);\n+\tlock_sock(sk);\n+\tsk_common_release(sk);\n+\trelease_sock(sk);\n+\tSMBDIRECT_FN_COMMENT(sk, \"before sock_put()\");\n+\tsock_put(sk);\n+}\n+\n+static struct percpu_counter smbdirect_sockets_allocated;\n+\n+static struct proto smbdirect_prot = {\n+\t.name\t\t\t= \"smbdirect\",\n+\t.owner\t\t\t= THIS_MODULE,\n+\t.obj_size\t\t= sizeof(struct smbdirect_socket),\n+\t.ipv6_pinfo_offset\t= offsetof(struct smbdirect_socket, inet6),\n+\t.init\t\t\t= smbdirect_sk_init,\n+\t.destroy\t\t= smbdirect_sk_destroy,\n+\t.hash\t\t\t= smbdirect_sk_hash,\n+\t.unhash\t\t\t= smbdirect_sk_unhash,\n+\t.release_cb\t\t= smbdirect_sk_release_cb,\n+\t.bind\t\t\t= smbdirect_sk_bind,\n+\t.accept\t\t\t= smbdirect_sk_accept,\n+\t.pre_connect\t\t= smbdirect_sk_pre_connect,\n+\t.connect\t\t= smbdirect_sk_connect,\n+\t.setsockopt\t\t= smbdirect_sk_setsockopt,\n+\t.getsockopt\t\t= smbdirect_sk_getsockopt,\n+\t.ioctl\t\t\t= smbdirect_sk_ioctl,\n+\t.sendmsg\t\t= smbdirect_sk_sendmsg,\n+\t.recvmsg\t\t= smbdirect_sk_recvmsg,\n+\t.shutdown\t\t= smbdirect_sk_shutdown,\n+\t.disconnect\t\t= smbdirect_sk_disconnect,\n+\t.close\t\t\t= smbdirect_sk_close,\n+\t.sockets_allocated\t= &smbdirect_sockets_allocated,\n+};\n+\n+static int smbdirect_sock_release(struct socket *sock)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not locked */\n+\tsock_not_owned_by_me(sk);\n+\tWARN_ON_ONCE(sock_owned_by_user_nocheck(sk));\n+\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\tSMBDIRECT_FN_COMMENT(sk, \"calling inet_release()\");\n+\t\tret = inet_release(sock);\n+\t\tbreak;\n+\tcase AF_INET6:\n+#if IS_ENABLED(CONFIG_IPV6)\n+\t\tSMBDIRECT_FN_COMMENT(sk, \"calling inet6_release()\");\n+\t\tret = inet6_release(sock);\n+#else\n+\t\tret = -EAFNOSUPPORT;\n+#endif\n+\t\tbreak;\n+\tdefault:\n+\t\tret = -EAFNOSUPPORT;\n+\t\tbreak;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_bind(struct socket *sock, struct sockaddr_unsized *saddr, int len)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\tret = inet_bind(sock, saddr, len);\n+\t\tbreak;\n+\tcase AF_INET6:\n+#if IS_ENABLED(CONFIG_IPV6)\n+\t\tret = inet6_bind(sock, saddr, len);\n+#else\n+\t\tret = -EAFNOSUPPORT;\n+#endif\n+\t\tbreak;\n+\tdefault:\n+\t\tret = -EAFNOSUPPORT;\n+\t\tbreak;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_connect_locked(struct socket *sock,\n+\t\t\t\t\t struct sockaddr_unsized *uaddr,\n+\t\t\t\t\t int addr_len, int flags)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is already locked */\n+\tsock_owned_by_me(sk);\n+\n+\tif (addr_len < sizeof(uaddr->sa_family))\n+\t\treturn -EINVAL;\n+\n+\tif (sk->sk_family != uaddr->sa_family)\n+\t\treturn -EAFNOSUPPORT;\n+\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\tif (addr_len < sizeof(struct sockaddr_in))\n+\t\t\treturn -EINVAL;\n+\t\tbreak;\n+\tcase AF_INET6:\n+\t\t/*\n+\t\t * We require a full struct sockaddr_in6 (28 bytes) instead of a\n+\t\t * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don't\n+\t\t * have any legacy callers in userspace and the\n+\t\t * rdma layer also expects that.\n+\t\t */\n+\t\tif (addr_len < sizeof(struct sockaddr_in6))\n+\t\t\treturn -EINVAL;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EAFNOSUPPORT;\n+\t}\n+\n+\tswitch (sock->state) {\n+\tcase SS_CONNECTED:\n+\t\treturn -EISCONN;\n+\tcase SS_CONNECTING:\n+\t\treturn -EALREADY;\n+\tcase SS_UNCONNECTED:\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (sc->status == SMBDIRECT_SOCKET_CONNECTED)\n+\t\treturn -EISCONN;\n+\n+\tif (sc->status != SMBDIRECT_SOCKET_CREATED)\n+\t\treturn -EINVAL;\n+\n+\tif (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) {\n+\t\tret = sk->sk_prot->pre_connect(sk, uaddr, addr_len);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\tret = sk->sk_prot->connect(sk, uaddr, addr_len);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tinet_sk_set_state(sk, TCP_SYN_SENT);\n+\tsock->state = SS_CONNECTING;\n+\n+\tif (flags & O_NONBLOCK)\n+\t\treturn -EINPROGRESS;\n+\n+\tret = smbdirect_connection_wait_for_connected(sc);\n+\tif (ret)\n+\t\tgoto sock_error;\n+\n+\treturn 0;\n+\n+sock_error:\n+\tsock->state = SS_UNCONNECTED;\n+\tsk->sk_disconnects++;\n+\tif (sk->sk_prot->disconnect(sk, flags))\n+\t\tsock->state = SS_DISCONNECTING;\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_connect(struct socket *sock,\n+\t\t\t\t struct sockaddr_unsized *uaddr,\n+\t\t\t\t int addr_len, int flags)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tlock_sock(sk);\n+\tret = smbdirect_sock_connect_locked(sock, uaddr, addr_len, flags);\n+\trelease_sock(sk);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_listen(struct socket *sock, int backlog)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tlock_sock(sk);\n+\tret = smbdirect_socket_listen(sc, backlog);\n+\trelease_sock(sk);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_accept(struct socket *lsock, struct socket *nsock,\n+\t\t\t\t struct proto_accept_arg *arg)\n+{\n+\tstruct sock *lsk = lsock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(lsk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(lsk);\n+\n+\tret = inet_accept(lsock, nsock, arg);\n+\tif (!ret)\n+\t\t/*\n+\t\t * We want to handle all sockopts explicitly\n+\t\t * and only support what we really support.\n+\t\t */\n+\t\tset_bit(SOCK_CUSTOM_SOCKOPT, &nsock->flags);\n+\n+\tSMBDIRECT_FN_RETURN_INT(lsk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_getname(struct socket *sock, struct sockaddr *uaddr, int peer)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tswitch (sk->sk_family) {\n+\tcase AF_INET:\n+\t\tret = inet_getname(sock, uaddr, peer);\n+\t\tbreak;\n+\tcase AF_INET6:\n+#if IS_ENABLED(CONFIG_IPV6)\n+\t\tret = inet6_getname(sock, uaddr, peer);\n+#else\n+\t\tret = -EAFNOSUPPORT;\n+#endif\n+\t\tbreak;\n+\tdefault:\n+\t\tret = -EAFNOSUPPORT;\n+\t\tbreak;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static __poll_t smbdirect_sock_poll(struct file *file, struct socket *sock, poll_table *wait)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tstruct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n+\t__poll_t mask = 0;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tsock_poll_wait(file, sock, wait);\n+\n+\tif (sc->status == SMBDIRECT_SOCKET_LISTENING) {\n+\t\tif (!list_empty_careful(&sc->listen.ready))\n+\t\t\tmask |= EPOLLIN | EPOLLRDNORM;\n+\t\tSMBDIRECT_FN_RETURN_POLL(sk, mask);\n+\t\treturn mask;\n+\t}\n+\n+\tif (sc->first_error) {\n+\t\t/*\n+\t\t * A broken connection should report almost everything in order to let\n+\t\t * applications to detect it reliable.\n+\t\t */\n+\t\tmask |= EPOLLHUP;\n+\t\tmask |= EPOLLERR;\n+\t\tmask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;\n+\t\tmask |= EPOLLOUT | EPOLLWRNORM;\n+\t\tSMBDIRECT_FN_RETURN_POLL(sk, mask);\n+\t\treturn mask;\n+\t}\n+\n+\tif (sc->status != SMBDIRECT_SOCKET_CONNECTED) {\n+\t\t/*\n+\t\t * A just created socket.\n+\t\t */\n+\t\tSMBDIRECT_FN_RETURN_POLL(sk, mask);\n+\t\treturn mask;\n+\t}\n+\n+\tif (sc->recv_io.reassembly.data_length > 0)\n+\t\tmask |= EPOLLIN | EPOLLRDNORM;\n+\n+\tif (atomic_read(&sc->send_io.bcredits.count) > 0 &&\n+\t atomic_read(&sc->send_io.lcredits.count) > 0 &&\n+\t atomic_read(&sc->send_io.credits.count) > 0)\n+\t\tmask |= EPOLLOUT | EPOLLWRNORM;\n+\telse {\n+\t\tsk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);\n+\t\tset_bit(SOCK_NOSPACE, &sk->sk_socket->flags);\n+\n+\t\t/*\n+\t\t * Race breaker. If space is freed after\n+\t\t * wspace test but before the flags are set,\n+\t\t * IO signal will be lost. Memory barrier\n+\t\t * pairs with the input side.\n+\t\t */\n+\t\tsmp_mb__after_atomic();\n+\t\tif (atomic_read(&sc->send_io.bcredits.count) > 0 &&\n+\t\t atomic_read(&sc->send_io.lcredits.count) > 0 &&\n+\t\t atomic_read(&sc->send_io.credits.count) > 0)\n+\t\t\tmask |= EPOLLOUT | EPOLLWRNORM;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_POLL(sk, mask);\n+\treturn mask;\n+}\n+\n+static int smbdirect_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\t/*\n+\t * We may need to handle some here as\n+\t * smbirect_sk_ioctl() only gets a kernel\n+\t * int pointer as arg, but we may\n+\t * need to the whole struct\n+\t */\n+\tswitch (cmd) {\n+\tdefault:\n+\t\t/*\n+\t\t * Note this has some special handling for\n+\t\t * sk->sk_type == SOCK_RAW, in case we ever\n+\t\t * implement SOCK_RAW...\n+\t\t *\n+\t\t * It calls smbdirect_sk_ioctl()...\n+\t\t */\n+\t\tret = sk_ioctl(sk, cmd, (void __user *)arg);\n+\t\tbreak;\n+\t}\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_shutdown(struct socket *sock, int how)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret = 0;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\t/*\n+\t * We have these from userspace:\n+\t * SHUT_RD = 0, SHUT_WR = 1 and SHUT_RDWR = 2\n+\t *\n+\t * And we map them to SHUTDOWN_MASK = 3\n+\t * RCV_SHUTDOWN = 1, SEND_SHUTDOWN = 2, BOTH = 3\n+\t */\n+\thow++;\n+\tif ((how & ~SHUTDOWN_MASK) || !how)\t/* MAXINT->0 */\n+\t\treturn -EINVAL;\n+\n+\tlock_sock(sk);\n+\n+\tswitch (sk->sk_state) {\n+\tcase TCP_CLOSE:\n+\t\tret = -ENOTCONN;\n+\t\tfallthrough;\n+\tdefault:\n+\t\tWRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | how);\n+\t\tsk->sk_prot->shutdown(sk, how);\n+\t\tbreak;\n+\n+\tcase TCP_SYN_SENT:\n+\tcase TCP_SYN_RECV:\n+\t\tret = sk->sk_prot->disconnect(sk, O_NONBLOCK);\n+\t\tbreak;\n+\t}\n+\n+\t/* Wake up anyone sleeping in poll. */\n+\tsk->sk_state_change(sk);\n+\trelease_sock(sk);\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_setsockopt(struct socket *sock, int level, int optname,\n+\t\t\t\t sockptr_t optval, unsigned int optlen)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tret = sock_common_setsockopt(sock, level, optname, optval, optlen);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_getsockopt(struct socket *sock, int level, int optname,\n+\t\t\t\t char __user *optval, int __user *optlen)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tret = sock_common_getsockopt(sock, level, optname, optval, optlen);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tret = sk->sk_prot->sendmsg(sk, msg, len);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static int smbdirect_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,\n+\t\t\t\t int flags)\n+{\n+\tstruct sock *sk = sock->sk;\n+\tint ret;\n+\n+\tSMBDIRECT_FN_CALLED(sk);\n+\n+\t/* assert it is not already locked */\n+\tsock_not_owned_by_me(sk);\n+\n+\tret = sock_common_recvmsg(sock, msg, size, flags);\n+\n+\tSMBDIRECT_FN_RETURN_INT(sk, ret);\n+\treturn ret;\n+}\n+\n+static const struct proto_ops smbdirect_inet_proto_ops = {\n+\t.family\t\t\t= PF_INET,\n+\t.owner\t\t\t= THIS_MODULE,\n+\t.release\t\t= smbdirect_sock_release,\n+\t.bind\t\t\t= smbdirect_sock_bind,\n+\t.connect\t\t= smbdirect_sock_connect,\n+\t.socketpair\t\t= sock_no_socketpair,\n+\t.listen\t\t\t= smbdirect_sock_listen,\n+\t.accept\t\t\t= smbdirect_sock_accept,\n+\t.getname\t\t= smbdirect_sock_getname,\n+\t.poll\t\t\t= smbdirect_sock_poll,\n+\t.ioctl\t\t\t= smbdirect_sock_ioctl,\n+\t.shutdown\t\t= smbdirect_sock_shutdown,\n+\t.setsockopt\t\t= smbdirect_sock_setsockopt,\n+\t.getsockopt\t\t= smbdirect_sock_getsockopt,\n+\t.sendmsg\t\t= smbdirect_sock_sendmsg,\n+\t.sendmsg_locked\t\t= smbdirect_sk_sendmsg_locked,\n+\t.recvmsg\t\t= smbdirect_sock_recvmsg,\n+\t.mmap\t\t\t= sock_no_mmap,\n+};\n+\n+#if IS_ENABLED(CONFIG_IPV6)\n+static const struct proto_ops smbdirect_inet6_proto_ops = {\n+\t.family\t\t\t= PF_INET6,\n+\t.owner\t\t\t= THIS_MODULE,\n+\t.release\t\t= smbdirect_sock_release,\n+\t.bind\t\t\t= smbdirect_sock_bind,\n+\t.connect\t\t= smbdirect_sock_connect,\n+\t.socketpair\t\t= sock_no_socketpair,\n+\t.listen\t\t\t= smbdirect_sock_listen,\n+\t.accept\t\t\t= smbdirect_sock_accept,\n+\t.getname\t\t= smbdirect_sock_getname,\n+\t.poll\t\t\t= smbdirect_sock_poll,\n+\t.ioctl\t\t\t= smbdirect_sock_ioctl,\n+\t.shutdown\t\t= smbdirect_sock_shutdown,\n+\t.setsockopt\t\t= smbdirect_sock_setsockopt,\n+\t.getsockopt\t\t= smbdirect_sock_getsockopt,\n+\t.sendmsg\t\t= smbdirect_sock_sendmsg,\n+\t.sendmsg_locked\t\t= smbdirect_sk_sendmsg_locked,\n+\t.recvmsg\t\t= smbdirect_sock_recvmsg,\n+\t.mmap\t\t\t= sock_no_mmap,\n+};\n+#endif\n+\n+static struct inet_protosw smbdirect_inet_stream_protosw = {\n+\t.type\t\t= SOCK_STREAM,\n+\t.protocol\t= IPPROTO_SMBDIRECT,\n+\t.prot\t\t= &smbdirect_prot,\n+\t.ops\t\t= &smbdirect_inet_proto_ops,\n+};\n+\n+#if IS_ENABLED(CONFIG_IPV6)\n+static struct inet_protosw smbdirect_inet6_stream_protosw = {\n+\t.type\t\t= SOCK_STREAM,\n+\t.protocol\t= IPPROTO_SMBDIRECT,\n+\t.prot\t\t= &smbdirect_prot,\n+\t.ops\t\t= &smbdirect_inet6_proto_ops,\n+};\n+#endif\n+\n+struct smbdirect_socket *smbdirect_socket_from_sock(const struct socket *sock)\n+{\n+\tif (!sock ||\n+\t !sock->sk ||\n+\t sock->sk->sk_protocol != IPPROTO_SMBDIRECT)\n+\t\treturn NULL;\n+\n+\tif (WARN_ON_ONCE(sock->sk->sk_destruct != smbdirect_sk_destruct))\n+\t\treturn NULL;\n+\n+\treturn smbdirect_socket_from_sk(sock->sk);\n+}\n+__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_from_sock);\n+\n+static __init int smbdirect_protosw_init(void)\n+{\n+\tint err;\n+\n+\terr = proto_register(&smbdirect_prot, 1);\n+\tif (err)\n+\t\treturn err;\n+\n+\tinet_register_protosw(&smbdirect_inet_stream_protosw);\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tinet6_register_protosw(&smbdirect_inet6_stream_protosw);\n+#endif\n+\n+\treturn 0;\n+}\n+\n+static __exit void smbdirect_protosw_exit(void)\n+{\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tinet6_unregister_protosw(&smbdirect_inet6_stream_protosw);\n+#endif\n+\tinet_unregister_protosw(&smbdirect_inet_stream_protosw);\n+\n+\tproto_unregister(&smbdirect_prot);\n+}\n+\n+__init int smbdirect_proto_init(void)\n+{\n+\tint err;\n+\n+\terr = percpu_counter_init(&smbdirect_sockets_allocated, 0, GFP_KERNEL);\n+\tif (err)\n+\t\tgoto err_percpu_counter;\n+\n+\terr = smbdirect_protosw_init();\n+\tif (err)\n+\t\tgoto err_protosw;\n+\n+\treturn 0;\n+\n+err_protosw:\n+\tpercpu_counter_destroy(&smbdirect_sockets_allocated);\n+err_percpu_counter:\n+\treturn err;\n+}\n+\n+__exit void smbdirect_proto_exit(void)\n+{\n+\tsmbdirect_protosw_exit();\n+\tpercpu_counter_destroy(&smbdirect_sockets_allocated);\n+}\n+\n+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 257 /* IPPROTO_SMBDIRECT */, SOCK_STREAM);\n+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 257 /* IPPROTO_SMBDIRECT */, SOCK_STREAM);\ndiff --git a/fs/smb/common/smbdirect/smbdirect_public.h b/fs/smb/common/smbdirect/smbdirect_public.h\nindex 50088155e7c3..9f96c66bbe32 100644\n--- a/fs/smb/common/smbdirect/smbdirect_public.h\n+++ b/fs/smb/common/smbdirect/smbdirect_public.h\n@@ -49,6 +49,7 @@ int smbdirect_socket_set_kernel_settings(struct smbdirect_socket *sc,\n #define SMBDIRECT_LOG_RDMA_MR\t\t\t0x100\n #define SMBDIRECT_LOG_RDMA_RW\t\t\t0x200\n #define SMBDIRECT_LOG_NEGOTIATE\t\t\t0x400\n+#define SMBDIRECT_LOG_SK\t\t\t0x800\n void smbdirect_socket_set_logging(struct smbdirect_socket *sc,\n \t\t\t\t void *private_ptr,\n \t\t\t\t bool (*needed)(struct smbdirect_socket *sc,\n@@ -145,4 +146,6 @@ void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc,\n \t\t\t\t\t\t unsigned int rdma_readwrite_threshold,\n \t\t\t\t\t\t struct seq_file *m);\n \n+struct smbdirect_socket *smbdirect_socket_from_sock(const struct socket *sock);\n+\n #endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__ */\ndiff --git a/fs/smb/common/smbdirect/smbdirect_rw.c b/fs/smb/common/smbdirect/smbdirect_rw.c\nindex 3b2eb8c48efc..154339955617 100644\n--- a/fs/smb/common/smbdirect/smbdirect_rw.c\n+++ b/fs/smb/common/smbdirect/smbdirect_rw.c\n@@ -105,11 +105,11 @@ static void smbdirect_connection_rdma_write_done(struct ib_cq *cq, struct ib_wc\n \tsmbdirect_connection_rdma_rw_done(cq, wc, DMA_TO_DEVICE);\n }\n \n-int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n-\t\t\t\t void *buf, size_t buf_len,\n-\t\t\t\t struct smbdirect_buffer_descriptor_v1 *desc,\n-\t\t\t\t size_t desc_len,\n-\t\t\t\t bool is_read)\n+static int smbdirect_connection_rdma_xmit_locked(struct smbdirect_socket *sc,\n+\t\t\t\t\t\t void *buf, size_t buf_len,\n+\t\t\t\t\t\t struct smbdirect_buffer_descriptor_v1 *desc,\n+\t\t\t\t\t\t size_t desc_len,\n+\t\t\t\t\t\t bool is_read)\n {\n \tconst struct smbdirect_socket_parameters *sp = &sc->parameters;\n \tenum dma_data_direction direction = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;\n@@ -123,6 +123,8 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n \tint credits_needed;\n \tsize_t desc_buf_len, desc_num = 0;\n \n+\tsmbdirect_socket_sk_owned_by_me(sc);\n+\n \tif (sc->status != SMBDIRECT_SOCKET_CONNECTED)\n \t\treturn -ENOTCONN;\n \n@@ -235,7 +237,9 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n \t}\n \n \tmsg = list_last_entry(&msg_list, struct smbdirect_rw_io, list);\n+\tsmbdirect_socket_sk_unlock(sc);\n \twait_for_completion(&completion);\n+\tsmbdirect_socket_sk_lock(sc);\n \tret = msg->error;\n out:\n \tlist_for_each_entry_safe(msg, next_msg, &msg_list, list) {\n@@ -252,4 +256,19 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n \tkfree(msg);\n \tgoto out;\n }\n+\n+int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n+\t\t\t\t void *buf, size_t buf_len,\n+\t\t\t\t struct smbdirect_buffer_descriptor_v1 *desc,\n+\t\t\t\t size_t desc_len,\n+\t\t\t\t bool is_read)\n+{\n+\tint ret;\n+\n+\tsmbdirect_socket_sk_lock(sc);\n+\tret = smbdirect_connection_rdma_xmit_locked(sc, buf, buf_len, desc, desc_len, is_read);\n+\tsmbdirect_socket_sk_unlock(sc);\n+\n+\treturn ret;\n+}\n __SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit);\ndiff --git a/fs/smb/common/smbdirect/smbdirect_socket.c b/fs/smb/common/smbdirect/smbdirect_socket.c\nindex 9153e1dbf53d..76e406999588 100644\n--- a/fs/smb/common/smbdirect/smbdirect_socket.c\n+++ b/fs/smb/common/smbdirect/smbdirect_socket.c\n@@ -5,6 +5,7 @@\n */\n \n #include \"smbdirect_internal.h\"\n+#include <net/transp_v6.h>\n \n bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs)\n {\n@@ -217,6 +218,7 @@ int smbdirect_socket_set_kernel_settings(struct smbdirect_socket *sc,\n \tsc->send_io.mem.gfp_mask = gfp_mask;\n \tsc->recv_io.mem.gfp_mask = gfp_mask;\n \tsc->rw_io.mem.gfp_mask = gfp_mask;\n+\tsc->sk.sk_allocation = gfp_mask;\n \n \treturn 0;\n }\n@@ -242,6 +244,106 @@ void smbdirect_socket_set_logging(struct smbdirect_socket *sc,\n }\n __SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_logging);\n \n+int smbdirect_socket_sync_saddr_to_sk(struct smbdirect_socket *sc, bool *_is_any_addr)\n+{\n+\tstruct sock *sk = &sc->sk;\n+\tconst struct sockaddr_storage *saddr;\n+\tconst struct sockaddr_in *sin;\n+\tconst struct sockaddr_in6 *sin6;\n+\tstruct in_addr sin_addr = { .s_addr = htonl(INADDR_ANY), };\n+\tstruct in6_addr sin6_addr = in6addr_any;\n+\t__be32 sin6_flowinfo = 0;\n+\tbool is_any_addr = true;\n+\tu16 sport = 0;\n+\tint ret;\n+\n+\tsaddr = &sc->rdma.cm_id->route.addr.src_addr;\n+\n+\tif (WARN_ON_ONCE(saddr->ss_family != sk->sk_family)) {\n+\t\tret = -EINVAL;\n+\t\treturn ret;\n+\t}\n+\n+\tswitch (saddr->ss_family) {\n+\tcase AF_INET:\n+\t\tsin = (struct sockaddr_in *)saddr;\n+\t\tsport = ntohs(sin->sin_port);\n+\t\tsin_addr = sin->sin_addr;\n+\t\tis_any_addr = (sin_addr.s_addr == htonl(INADDR_ANY));\n+\t\tbreak;\n+\n+\tcase AF_INET6:\n+\t\tsin6 = (struct sockaddr_in6 *)saddr;\n+\t\tsport = ntohs(sin6->sin6_port);\n+\t\tsin_addr.s_addr = LOOPBACK4_IPV6;\n+\t\tsin6_addr = sin6->sin6_addr;\n+\t\tis_any_addr = ipv6_addr_any(&sin6_addr);\n+\t\tsin6_flowinfo = sin6->sin6_flowinfo;\n+\t\tbreak;\n+\t}\n+\n+\tsk->sk_bound_dev_if = sc->rdma.cm_id->route.addr.dev_addr.bound_dev_if;\n+\tsk->sk_rcv_saddr = sc->inet.inet_saddr = sin_addr.s_addr;\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tsk->sk_v6_rcv_saddr = sc->inet6.saddr = sin6_addr;\n+#else\n+\tsc->inet6.saddr = sin6_addr;\n+#endif\n+\tsc->inet6.flow_label = sin6_flowinfo;\n+\tsk->sk_num = sport;\n+\tsc->inet.inet_sport = htons(sport);\n+\n+\tif (_is_any_addr)\n+\t\t*_is_any_addr = is_any_addr;\n+\treturn 0;\n+}\n+\n+int smbdirect_socket_sync_daddr_to_sk(struct smbdirect_socket *sc)\n+{\n+\tstruct sock *sk = &sc->sk;\n+\tconst struct sockaddr_storage *daddr;\n+\tconst struct sockaddr_in *sin;\n+\tconst struct sockaddr_in6 *sin6;\n+\tstruct in_addr sin_addr = { .s_addr = htonl(INADDR_ANY), };\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tstruct in6_addr sin6_addr = in6addr_any;\n+#endif\n+\tu16 dport = 0;\n+\tint ret;\n+\n+\tdaddr = &sc->rdma.cm_id->route.addr.dst_addr;\n+\n+\tif (WARN_ON_ONCE(daddr->ss_family != sk->sk_family)) {\n+\t\tret = -EINVAL;\n+\t\treturn ret;\n+\t}\n+\n+\tswitch (daddr->ss_family) {\n+\tcase AF_INET:\n+\t\tsin = (struct sockaddr_in *)daddr;\n+\t\tdport = ntohs(sin->sin_port);\n+\t\tsin_addr = sin->sin_addr;\n+\t\tbreak;\n+\n+\tcase AF_INET6:\n+\t\tsin6 = (struct sockaddr_in6 *)daddr;\n+\t\tdport = ntohs(sin6->sin6_port);\n+\t\tsin_addr.s_addr = LOOPBACK4_IPV6;\n+#if IS_ENABLED(CONFIG_IPV6)\n+\t\tsin6_addr = sin6->sin6_addr;\n+#endif\n+\t\tbreak;\n+\t}\n+\n+\tsk->sk_daddr = sc->inet.inet_daddr = sin_addr.s_addr;\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tsk->sk_v6_daddr = sin6_addr;\n+#endif\n+\tsk->sk_dport = sc->inet.inet_dport = htons(dport);\n+\n+\treturn 0;\n+}\n+\n static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc)\n {\n \t/*\n@@ -257,6 +359,38 @@ static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc)\n \twake_up_all(&sc->recv_io.reassembly.wait_queue);\n \twake_up_all(&sc->rw_io.credits.wait_queue);\n \twake_up_all(&sc->mr_io.ready.wait_queue);\n+\n+\tif (sc->sk.sk_family) {\n+\t\tstruct sock *sk = &sc->sk;\n+\n+\t\tWRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK);\n+\n+\t\tWARN_ON_ONCE(sc->first_error == 0);\n+\t\tif (sc->first_error < 0)\n+\t\t\tWRITE_ONCE(sk->sk_err, -sc->first_error);\n+\t\telse\n+\t\t\tWRITE_ONCE(sk->sk_err, sc->first_error);\n+\n+\t\tif (sc->status >= SMBDIRECT_SOCKET_DISCONNECTED) {\n+\t\t\tinet_sk_set_state(sk, TCP_CLOSE);\n+\t\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\t\tsk->sk_socket->state = SS_UNCONNECTED;\n+\t\t} else {\n+\t\t\tinet_sk_set_state(sk, TCP_CLOSING);\n+\t\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\t\tsk->sk_socket->state = SS_DISCONNECTING;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Note tcp_done_with_error() also calls both\n+\t\t * sk->sk_state_change(sk) via tcp_done()\n+\t\t * and sk_error_report() directly.\n+\t\t */\n+\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\tsk->sk_state_change(sk);\n+\t\tif (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n+\t\t\tsk_error_report(sk);\n+\t}\n }\n \n void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc,\n@@ -510,11 +644,13 @@ static void smbdirect_socket_destroy(struct smbdirect_socket *sc)\n \t */\n \tsmbdirect_socket_wake_up_all(sc);\n \n+\tsmbdirect_socket_sk_unlock(sc);\n \tdisable_work_sync(&sc->disconnect_work);\n \tdisable_work_sync(&sc->connect.work);\n \tdisable_work_sync(&sc->recv_io.posted.refill_work);\n \tdisable_work_sync(&sc->idle.immediate_work);\n \tdisable_delayed_work_sync(&sc->idle.timer_work);\n+\tsmbdirect_socket_sk_lock(sc);\n \n \tif (sc->rdma.cm_id)\n \t\trdma_lock_handler(sc->rdma.cm_id);\n@@ -600,6 +736,8 @@ void smbdirect_socket_destroy_sync(struct smbdirect_socket *sc)\n \t */\n \tWARN_ON_ONCE(in_interrupt());\n \n+\tsmbdirect_socket_sk_owned_by_me(sc);\n+\n \t/*\n \t * First we try to disable the work\n \t * without disable_work_sync() in a\n@@ -625,7 +763,9 @@ void smbdirect_socket_destroy_sync(struct smbdirect_socket *sc)\n \n \tsmbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n \t\t\"cancelling and disable disconnect_work\\n\");\n+\tsmbdirect_socket_sk_unlock(sc);\n \tdisable_work_sync(&sc->disconnect_work);\n+\tsmbdirect_socket_sk_lock(sc);\n \n \tsmbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n \t\t\"destroying rdma session\\n\");\n@@ -634,7 +774,9 @@ void smbdirect_socket_destroy_sync(struct smbdirect_socket *sc)\n \tif (sc->status < SMBDIRECT_SOCKET_DISCONNECTED) {\n \t\tsmbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n \t\t\t\"wait for transport being disconnected\\n\");\n+\t\tsmbdirect_socket_sk_unlock(sc);\n \t\twait_event(sc->status_wait, sc->status == SMBDIRECT_SOCKET_DISCONNECTED);\n+\t\tsmbdirect_socket_sk_lock(sc);\n \t\tsmbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n \t\t\t\"waited for transport being disconnected\\n\");\n \t}\n@@ -723,6 +865,8 @@ int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc,\n {\n \tint ret;\n \n+\tsmbdirect_socket_sk_owned_by_me(sc);\n+\n \tif (WARN_ON_ONCE(needed < 0))\n \t\treturn -EINVAL;\n \n@@ -731,9 +875,12 @@ int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc,\n \t\t\treturn 0;\n \n \t\tatomic_add(needed, total_credits);\n+\n+\t\tsmbdirect_socket_sk_unlock(sc);\n \t\tret = wait_event_interruptible(*waitq,\n \t\t\t\t\t atomic_read(total_credits) >= needed ||\n \t\t\t\t\t sc->status != expected_status);\n+\t\tsmbdirect_socket_sk_lock(sc);\n \n \t\tif (sc->status != expected_status)\n \t\t\treturn unexpected_errno;\ndiff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h\nindex c09eddd8ad16..6bb201683259 100644\n--- a/fs/smb/common/smbdirect/smbdirect_socket.h\n+++ b/fs/smb/common/smbdirect/smbdirect_socket.h\n@@ -104,6 +104,18 @@ enum smbdirect_keepalive_status {\n };\n \n struct smbdirect_socket {\n+\tunion {\n+\t\tstruct sock sk;\n+\t\tstruct inet_sock inet;\n+\t};\n+\t/* needed by inet6_create() */\n+\tstruct ipv6_pinfo inet6;\n+\tvoid (*orig_sk_destruct)(struct sock *sk);\n+\n+\t/*\n+\t * This is the first element that is\n+\t * initialized in smbdirect_socket_init()\n+\t */\n \tenum smbdirect_socket_status status;\n \twait_queue_head_t status_wait;\n \tint first_error;\n@@ -548,14 +560,18 @@ static void __smbdirect_log_printf(struct smbdirect_socket *sc,\n \t\t__smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RW, fmt, ##args)\n #define smbdirect_log_negotiate(sc, lvl, fmt, args...) \\\n \t\t__smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_NEGOTIATE, fmt, ##args)\n+#define smbdirect_log_sk(sc, lvl, fmt, args...) \\\n+\t\t__smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_SK, fmt, ##args)\n \n static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)\n {\n+\tconst size_t status_offset = offsetof(struct smbdirect_socket, status);\n+\n \t/*\n \t * This also sets status = SMBDIRECT_SOCKET_CREATED\n \t */\n \tBUILD_BUG_ON(SMBDIRECT_SOCKET_CREATED != 0);\n-\tmemset(sc, 0, sizeof(*sc));\n+\tmemset(((u8 *)sc)+status_offset, 0, sizeof(*sc)-status_offset);\n \n \tinit_waitqueue_head(&sc->status_wait);\n \n@@ -700,6 +716,14 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)\n \t__SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, \\\n \t\t__SMBDIRECT_SOCKET_DISCONNECT(__sc);)\n \n+static __always_inline struct smbdirect_socket *\n+smbdirect_socket_from_sk(const struct sock *sk)\n+{\n+\tWARN_ON_ONCE(!sk);\n+\tBUILD_BUG_ON(offsetof(struct smbdirect_socket, sk) != 0);\n+\treturn container_of(sk, struct smbdirect_socket, sk);\n+}\n+\n struct smbdirect_send_io {\n \tstruct smbdirect_socket *socket;\n \tstruct ib_cqe cqe;\n", "prefixes": [ "6/8" ] }