[{"id":3674518,"web_url":"http://patchwork.ozlabs.org/comment/3674518/","msgid":"<CAAVpQUCxCT-YGCu-JfwZNRvhgVOPfh+c9NBO+xMvSsphwJV_Zw@mail.gmail.com>","list_archive_url":null,"date":"2026-04-08T01:04:13","subject":"Re: [PATCH 6/8] smb: smbdirect: add in kernel only support for\n IPPROTO_SMBDIRECT","submitter":{"id":91297,"url":"http://patchwork.ozlabs.org/api/people/91297/","name":"Kuniyuki Iwashima","email":"kuniyu@google.com"},"content":"On Tue, Apr 7, 2026 at 7:47 AM Stefan Metzmacher <metze@samba.org> wrote:\n>\n> For userspace callers of socket() still get -EPROTONOSUPPORT,\n> so we are sure we'll only have in kernel callers, cifs.ko and\n> ksmbd.ko, for now. This makes it possible to relax the\n> constrains generic stream socket consumers would otherwise\n> assume.\n>\n> There's a prototype for userspace sockets on top of\n> this and there's working userspace code for Samba as\n> client and server, so this is just the first step,\n> but a very important one.\n>\n> The SMBDIRECT protocol is defined in [MS-SMBD] by Microsoft.\n> It is used as wrapper around RDMA in order to provide a transport for SMB3,\n> but Microsoft also uses it as transport for other protocols.\n>\n> SMBDIRECT works over Infiniband, RoCE and iWarp.\n> RoCEv2 is based on IP/UDP and iWarp is based on IP/TCP,\n> so these use IP addresses natively.\n> Infiniband and RoCEv1 require IPOIB in order to be used for\n> SMBDIRECT.\n>\n> So instead of adding a PF_SMBDIRECT, which would only use AF_INET[6],\n> we use IPPROTO_SMBDIRECT instead, this uses a number not\n> allocated from IANA, as it would not appear in an IP header.\n\nOverall I don't see the upside of reusing AF_INET6? infra.  It just\nadds an unnecessary sk->sk_prot layer, which can be simply\nimplemented as sock->ops.\n\nIt seems only inet_getname() is the valid user of inet_sock.\n\n\n>\n> This is similar to IPPROTO_SMC, IPPROTO_MPTCP and IPPROTO_QUIC,\n> which are linux specific values for the socket() syscall.\n\nSMBDIRECT seems more like SMC, rather than MPTCP and QUIC.\n\nSMC was implemented with AF_SMC first, and IPPROT_SMC\nwas added just to hook at userspace and silently convert TCP\nsockets to AF_SMC sockets easily.\n\nBut the is not the case of SMBDIRECT because the socket is\nonly created from kernel space.\n\n\n>\n>   socket(AF_INET, SOCK_STREAM, IPPROTO_SMBDIRECT);\n>   socket(AF_INET6, SOCK_STREAM, IPPROTO_SMBDIRECT);\n>\n> This will allow the existing smbdirect code used by\n> cifs.ko and ksmbd.ko to be moved behind the socket layer,\n\nReusing AF_INET6? is not related to this statement as long\nas the upper layer uses in-kernel socket API (sock_XXX()/kernel_XXX())\ninstead of calling sk->sk_prot->XXX() directly.\n\n\n\n> so that there's less special handling. Only sock_sendmsg()\n> sock_recvmsg() are used, so that the main stream handling\n> is done all the same for tcp, smbdirect and later also quic.\n>\n> The special RDMA read/write handling will be via direct\n> function calls as they are currently done for the in kernel\n> consumers.\n>\n> For now the core smbdirect code still supports both\n> modes, direct calls in indirect via the socket layer.\n> The core code uses if (sc->sk.sk_family) as indication\n> for the new socket mode. Once cifs.ko and ksmbd.ko\n> are converted we can remove the old mode slowly,\n> but I'll deferr that to a future patchset.\n>\n> There's still a way to go in order to make this\n> as generic as tcp and quic e.g. adding MSG_SPLICE_PAGES support or\n> splice_read/read_sock/read_skb.\n>\n> But it's a good start, which will make changes\n> much easier.\n>\n> Cc: Steve French <smfrench@gmail.com>\n> Cc: Tom Talpey <tom@talpey.com>\n> Cc: Long Li <longli@microsoft.com>\n> Cc: Namjae Jeon <linkinjeon@kernel.org>\n> Cc: David Howells <dhowells@redhat.com>\n> Cc: Henrique Carvalho <henrique.carvalho@suse.com>\n> Cc: linux-cifs@vger.kernel.org\n> Cc: samba-technical@lists.samba.org\n> Cc: David S. Miller <davem@davemloft.net>\n> Cc: Eric Dumazet <edumazet@google.com>\n> Cc: Jakub Kicinski <kuba@kernel.org>\n> Cc: Paolo Abeni <pabeni@redhat.com>\n> Cc: Simon Horman <horms@kernel.org>\n> Cc: Kuniyuki Iwashima <kuniyu@google.com>\n> Cc: Willem de Bruijn <willemb@google.com>\n> Cc: netdev@vger.kernel.org\n> Cc: Xin Long <lucien.xin@gmail.com>\n> Cc: quic@lists.linux.dev\n> Cc: linux-rdma@vger.kernel.org\n> Cc: linux-kernel@vger.kernel.org\n> Signed-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\n>\n> diff --git a/fs/smb/common/smbdirect/Makefile b/fs/smb/common/smbdirect/Makefile\n> index 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>         smbdirect_connection.o  \\\n>         smbdirect_mr.o          \\\n>         smbdirect_rw.o          \\\n> +       smbdirect_proto.o       \\\n>         smbdirect_debug.o       \\\n>         smbdirect_connect.o     \\\n>         smbdirect_listen.o      \\\n> diff --git a/fs/smb/common/smbdirect/smbdirect.h b/fs/smb/common/smbdirect/smbdirect.h\n> index 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>                 SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \\\n>                 SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW)\n>\n> +enum {\n> +       __SMBDIRECT_BUFFER_REMOTE_INVALIDATE = 0x20,\n> +};\n> +\n> +struct smbdirect_cmsg_buffer {\n> +       uint8_t msg_control[CMSG_SPACE(24)];\n> +};\n> +\n> +static __always_inline\n> +void __smbdirect_cmsg_prepare(struct msghdr *msg,\n> +                             struct smbdirect_cmsg_buffer *cbuffer,\n> +                             int cmsg_type,\n> +                             const void *payload,\n> +                             size_t payloadlen)\n> +{\n> +       size_t cmsg_space = CMSG_SPACE(payloadlen);\n> +       size_t cmsg_len = CMSG_LEN(payloadlen);\n> +       struct cmsghdr *cmsg = NULL;\n> +       void *dataptr = NULL;\n> +\n> +       BUILD_BUG_ON(cmsg_space > sizeof(cbuffer->msg_control));\n> +\n> +       memset(cbuffer, 0, sizeof(*cbuffer));\n> +\n> +       msg->msg_control = cbuffer->msg_control;\n> +       msg->msg_controllen = cmsg_space;\n> +\n> +       cmsg = CMSG_FIRSTHDR(msg);\n> +       cmsg->cmsg_level = SOL_SMBDIRECT;\n> +       cmsg->cmsg_type = cmsg_type;\n> +       cmsg->cmsg_len = cmsg_len;\n> +       dataptr = CMSG_DATA(cmsg);\n> +       memcpy(dataptr, payload, payloadlen);\n> +       msg->msg_controllen = cmsg->cmsg_len;\n> +}\n> +\n> +struct smbdirect_buffer_remote_invalidate_args {\n> +       __u32 remote_token;\n> +} __packed;\n> +#define SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE \\\n> +       _IOW('S', __SMBDIRECT_BUFFER_REMOTE_INVALIDATE, \\\n> +               struct smbdirect_buffer_remote_invalidate_args)\n> +\n> +static __always_inline\n> +void smbdirect_buffer_remote_invalidate_cmsg_prepare(struct msghdr *msg,\n> +                                                    struct smbdirect_cmsg_buffer *cbuffer,\n> +                                                    const __u32 *remote_token)\n> +{\n> +       if (remote_token) {\n> +               struct smbdirect_buffer_remote_invalidate_args args = {\n> +                       .remote_token = *remote_token,\n> +               };\n> +\n> +               __smbdirect_cmsg_prepare(msg, cbuffer,\n> +                                        SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE,\n> +                                        &args, sizeof(args));\n> +       }\n> +}\n> +\n>  #endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ */\n> diff --git a/fs/smb/common/smbdirect/smbdirect_accept.c b/fs/smb/common/smbdirect/smbdirect_accept.c\n> index 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>                 spin_lock_irqsave(&lsc->listen.lock, flags);\n>                 list_del(&sc->accept.list);\n>                 list_add_tail(&sc->accept.list, &lsc->listen.ready);\n> +               if (lsc->sk.sk_family) {\n> +                       struct sock *lsk = &lsc->sk;\n> +\n> +                       if (!sock_flag(lsk, SOCK_DEAD) && lsk->sk_socket)\n> +                               lsk->sk_data_ready(lsk);\n> +               }\n>                 wake_up(&lsc->listen.wait_queue);\n>                 spin_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>         long ret;\n>\n> +       smbdirect_socket_sk_unlock(lsc);\n>         ret = wait_event_interruptible_timeout(lsc->listen.wait_queue,\n>                                                !list_empty_careful(&lsc->listen.ready) ||\n>                                                lsc->status != SMBDIRECT_SOCKET_LISTENING ||\n>                                                lsc->first_error,\n>                                                timeo);\n> +       smbdirect_socket_sk_lock(lsc);\n>         if (lsc->status != SMBDIRECT_SOCKET_LISTENING)\n>                 return -EINVAL;\n>         if (lsc->first_error)\n> @@ -850,6 +857,11 @@ struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc,\n>          * order to grant credits to the peer.\n>          */\n>         nsc->status = SMBDIRECT_SOCKET_CONNECTED;\n> +       if (nsc->sk.sk_family) {\n> +               struct sock *nsk = &nsc->sk;\n> +\n> +               inet_sk_set_state(nsk, TCP_ESTABLISHED);\n> +       }\n>         smbdirect_accept_negotiate_finish(nsc, 0);\n>\n>         return nsc;\n> diff --git a/fs/smb/common/smbdirect/smbdirect_connection.c b/fs/smb/common/smbdirect/smbdirect_connection.c\n> index 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>         sc->rdma.cm_id->event_handler = smbdirect_connection_rdma_event_handler;\n>         sc->rdma.expected_event = RDMA_CM_EVENT_DISCONNECTED;\n> +\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               smbdirect_socket_sync_saddr_to_sk(sc, NULL);\n> +               smbdirect_socket_sync_daddr_to_sk(sc);\n> +\n> +               inet_sk_set_state(sk, TCP_SYN_RECV);\n> +       }\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>                   smbdirect_socket_status_string(sc->status),\n>                   SMBDIRECT_DEBUG_ERR_PTR(sc->first_error));\n>         sc->status = SMBDIRECT_SOCKET_CONNECTED;\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               inet_sk_set_state(sk, TCP_ESTABLISHED);\n> +               if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                       sk->sk_socket->state = SS_CONNECTED;\n> +       }\n>\n>         /*\n>          * 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>                 &sc->rdma.cm_id->route.addr.src_addr,\n>                 &sc->rdma.cm_id->route.addr.dst_addr);\n>\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                       sk->sk_state_change(sk);\n> +       }\n> +\n>         wake_up(&sc->status_wait);\n>  }\n>\n> @@ -739,10 +762,12 @@ int smbdirect_connection_wait_for_connected(struct smbdirect_socket *sc)\n>                 \"waiting for connection: device: %.*s local: %pISpsfc remote: %pISpsfc\\n\",\n>                 IB_DEVICE_NAME_MAX, devname, src, dst);\n>\n> +       smbdirect_socket_sk_unlock(sc);\n>         ret = wait_event_interruptible_timeout(sc->status_wait,\n>                                                sc->status == SMBDIRECT_SOCKET_CONNECTED ||\n>                                                sc->first_error,\n>                                                msecs_to_jiffies(sp->negotiate_timeout_msec));\n> +       smbdirect_socket_sk_lock(sc);\n>         if (sc->rdma.cm_id) {\n>                 /*\n>                  * 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>                 atomic_add(batch->credit, &sc->send_io.bcredits.count);\n>                 batch->credit = 0;\n>                 wake_up(&sc->send_io.bcredits.wait_queue);\n> +               if (sc->sk.sk_family) {\n> +                       struct sock *sk = &sc->sk;\n> +\n> +                       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                               sk->sk_write_space(sk);\n> +               }\n>         }\n>\n>         return ret;\n> @@ -1091,6 +1122,8 @@ int smbdirect_connection_send_single_iter(struct smbdirect_socket *sc,\n>         u32 data_length = 0;\n>         int ret;\n>\n> +       smbdirect_socket_sk_owned_by_me(sc);\n> +\n>         if (WARN_ON_ONCE(flags))\n>                 return -EINVAL; /* no flags support for now */\n>\n> @@ -1150,10 +1183,12 @@ int smbdirect_connection_send_single_iter(struct smbdirect_socket *sc,\n>                  * wait until either the refill work or the peer\n>                  * granted new credits\n>                  */\n> +               smbdirect_socket_sk_unlock(sc);\n>                 ret = wait_event_interruptible(sc->send_io.credits.wait_queue,\n>                                                atomic_read(&sc->send_io.credits.count) >= 1 ||\n>                                                atomic_read(&sc->recv_io.credits.available) >= 1 ||\n>                                                sc->status != SMBDIRECT_SOCKET_CONNECTED);\n> +               smbdirect_socket_sk_lock(sc);\n>                 if (sc->status != SMBDIRECT_SOCKET_CONNECTED)\n>                         ret = -ENOTCONN;\n>                 if (ret < 0)\n> @@ -1268,9 +1303,11 @@ int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc)\n>          * that means all the I/Os have been out and we are good to return\n>          */\n>\n> +       smbdirect_socket_sk_unlock(sc);\n>         wait_event(sc->send_io.pending.zero_wait_queue,\n>                    atomic_read(&sc->send_io.pending.count) == 0 ||\n>                    sc->status != SMBDIRECT_SOCKET_CONNECTED);\n> +       smbdirect_socket_sk_lock(sc);\n>         if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {\n>                 smbdirect_log_write(sc, SMBDIRECT_LOG_ERR,\n>                         \"status=%s first_error=%1pe => %1pe\\n\",\n> @@ -1297,6 +1334,8 @@ int smbdirect_connection_send_iter(struct smbdirect_socket *sc,\n>         int error = 0;\n>         __be32 hdr;\n>\n> +       smbdirect_socket_sk_owned_by_me(sc);\n> +\n>         if (WARN_ONCE(flags, \"unexpected flags=0x%x\\n\", flags))\n>                 return -EINVAL; /* no flags support for now */\n>\n> @@ -1448,7 +1487,9 @@ static void smbdirect_connection_send_immediate_work(struct work_struct *work)\n>         smbdirect_log_keep_alive(sc, SMBDIRECT_LOG_INFO,\n>                 \"send an empty message\\n\");\n>         sc->statistics.send_empty++;\n> +       smbdirect_socket_sk_lock(sc);\n>         ret = smbdirect_connection_send_single_iter(sc, NULL, NULL, 0, 0);\n> +       smbdirect_socket_sk_unlock(sc);\n>         if (ret < 0) {\n>                 smbdirect_log_write(sc, SMBDIRECT_LOG_ERR,\n>                         \"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>                  * If any sender is waiting for credits, unblock it\n>                  */\n>                 wake_up(&sc->send_io.credits.wait_queue);\n> +               if (sc->sk.sk_family) {\n> +                       struct sock *sk = &sc->sk;\n> +\n> +                       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                               sk->sk_write_space(sk);\n> +               }\n>         }\n>\n>         /* 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>                 smbdirect_connection_reassembly_append_recv_io(sc, recv_io, data_length);\n>                 wake_up(&sc->recv_io.reassembly.wait_queue);\n> +               if (sc->sk.sk_family) {\n> +                       struct sock *sk = &sc->sk;\n> +\n> +                       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                               sk->sk_data_ready(sk);\n> +               }\n>         } else\n>                 smbdirect_connection_put_recv_io(recv_io);\n>\n> @@ -1735,6 +1788,9 @@ int smbdirect_connection_recv_io_refill(struct smbdirect_socket *sc)\n>         /*\n>          * If the last send credit is waiting for credits\n>          * it can grant we need to wake it up\n> +        *\n> +        * This needs to wake up smbdirect_connection_send_single_iter()\n> +        * only, so we don't need sk->sk_write_space() here.\n>          */\n>         if (atomic_read(&sc->send_io.bcredits.count) == 0 &&\n>             atomic_read(&sc->send_io.credits.count) == 0)\n> @@ -1922,9 +1978,11 @@ int smbdirect_connection_recvmsg(struct smbdirect_socket *sc,\n>\n>         smbdirect_log_read(sc, SMBDIRECT_LOG_INFO,\n>                 \"wait_event on more data\\n\");\n> +       smbdirect_socket_sk_unlock(sc);\n>         ret = wait_event_interruptible(sc->recv_io.reassembly.wait_queue,\n>                                        sc->recv_io.reassembly.data_length >= size ||\n>                                        sc->status != SMBDIRECT_SOCKET_CONNECTED);\n> +       smbdirect_socket_sk_lock(sc);\n>         /* Don't return any data if interrupted */\n>         if (ret)\n>                 return ret;\n> diff --git a/fs/smb/common/smbdirect/smbdirect_devices.c b/fs/smb/common/smbdirect/smbdirect_devices.c\n> index 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>         return 0;\n>  }\n>\n> -__exit void smbdirect_devices_exit(void)\n> +__cold void smbdirect_devices_exit(void)\n>  {\n>         struct smbdirect_device *sdev, *tmp;\n>\n> diff --git a/fs/smb/common/smbdirect/smbdirect_internal.h b/fs/smb/common/smbdirect/smbdirect_internal.h\n> index 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>         struct mutex mutex;\n>\n> @@ -30,6 +28,8 @@ struct smbdirect_module_state {\n>                 rwlock_t lock;\n>                 struct list_head list;\n>         } devices;\n> +\n> +       struct smbdirect_socket_parameters default_parameters;\n>  };\n>\n>  extern struct smbdirect_module_state smbdirect_globals;\n> @@ -46,10 +46,58 @@ struct smbdirect_device {\n>         char 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> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               /* assert it is already locked */\n> +               sock_owned_by_me(sk);\n> +       }\n> +}\n> +\n> +static __always_inline void smbdirect_socket_sk_not_owned_by_me(struct smbdirect_socket *sc)\n> +{\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               /* assert it is not already locked */\n> +               sock_not_owned_by_me(sk);\n> +       }\n> +}\n> +\n> +static __always_inline void smbdirect_socket_sk_lock(struct smbdirect_socket *sc)\n> +{\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               /* assert it is not already locked */\n> +               sock_not_owned_by_me(sk);\n> +\n> +               lock_sock(sk);\n> +       }\n> +}\n> +\n> +static __always_inline void smbdirect_socket_sk_unlock(struct smbdirect_socket *sc)\n> +{\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               /* assert it is already locked */\n> +               sock_owned_by_me(sk);\n> +\n> +               release_sock(sk);\n> +       }\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>                                          const char *macro_name,\n>                                          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__ */\n> diff --git a/fs/smb/common/smbdirect/smbdirect_listen.c b/fs/smb/common/smbdirect/smbdirect_listen.c\n> index 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>          */\n>         sc->listen.backlog = backlog;\n>\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               inet_sk_set_state(sk, TCP_LISTEN);\n> +       }\n> +\n>         if (sc->rdma.cm_id->device)\n>                 smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n>                         \"listening on addr: %pISpsfc dev: %.*s\\n\",\n> @@ -209,6 +215,7 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,\n>                                             const struct rdma_cm_event *event)\n>  {\n>         const struct smbdirect_socket_parameters *lsp = &lsc->parameters;\n> +       struct sock *nsk = NULL;\n>         struct smbdirect_socket *nsc;\n>         unsigned long flags;\n>         size_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>                 return -EBUSY;\n>         }\n>\n> -       ret = smbdirect_socket_create_accepting(new_id, &nsc);\n> -       if (ret)\n> -               goto socket_init_failed;\n> +       if (lsc->sk.sk_family) {\n> +               struct sock *lsk = &lsc->sk;\n> +\n> +               ret = -ENOMEM;\n> +               nsk = sk_clone(lsk, lsk->sk_allocation, false);\n> +               if (!nsk)\n> +                       goto sk_clone_failed;\n> +               /* sk_clone_lock() increments refcnt to 2; drop the extra. */\n> +               __sock_put(nsk);\n> +               /* sk_clone() already called sk_sockets_allocated_inc(sk); */\n> +               sock_prot_inuse_add(sock_net(nsk), nsk->sk_prot, 1);\n> +\n> +               smbdirect_sk_reclassify(nsk);\n> +               inet_sk_set_state(nsk, TCP_SYN_RECV);\n> +               nsc = smbdirect_socket_from_sk(nsk);\n> +\n> +               ret = smbdirect_socket_init_accepting(new_id, nsc);\n> +               if (ret)\n> +                       goto socket_init_failed;\n> +\n> +               /*\n> +                * Note that smbdirect_sock_accept() will set\n> +                * SOCK_CUSTOM_SOCKOPT once [__]inet_accept()\n> +                * called sk_set_socket() via sock_graft().\n> +                */\n> +               WARN_ON_ONCE(nsc->orig_sk_destruct != lsc->orig_sk_destruct);\n> +               WARN_ON_ONCE(nsk->sk_destruct != lsk->sk_destruct);\n> +               WARN_ON_ONCE(nsk->sk_ipv6only != lsk->sk_ipv6only);\n> +       } else {\n> +               ret = smbdirect_socket_create_accepting(new_id, &nsc);\n> +               if (ret)\n> +                       goto socket_init_failed;\n> +       }\n>\n>         nsc->logging = lsc->logging;\n>         ret = smbdirect_socket_set_initial_parameters(nsc, &lsc->parameters);\n> @@ -302,7 +339,11 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,\n>          */\n>         nsc->ib.dev = NULL;\n>         nsc->rdma.cm_id = NULL;\n> -       smbdirect_socket_release(nsc);\n> +       if (!nsk)\n> +               smbdirect_socket_release(nsc);\n>  socket_init_failed:\n> +       if (nsk)\n> +               sk_free(nsk);\n> +sk_clone_failed:\n>         return ret;\n>  }\n> diff --git a/fs/smb/common/smbdirect/smbdirect_main.c b/fs/smb/common/smbdirect/smbdirect_main.c\n> index 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> +       struct smbdirect_socket_parameters *sp;\n>         int ret = -ENOMEM;\n>\n>         pr_notice(\"subsystem loading...\\n\");\n> @@ -73,10 +74,52 @@ static __init int smbdirect_module_init(void)\n>         if (ret)\n>                 goto devices_init_failed;\n>\n> +       /*\n> +        * Create the global default parameters\n> +        */\n> +       sp = &smbdirect_globals.default_parameters;\n> +       sp->resolve_addr_timeout_msec = 5 * 1000;\n> +       sp->resolve_route_timeout_msec = 5 * 1000;\n> +       sp->rdma_connect_timeout_msec = 5 * 1000;\n> +       sp->negotiate_timeout_msec = 120 * 1000;\n> +       sp->initiator_depth = 1; /* the server should change this */\n> +       sp->responder_resources = 1; /* the client should change this */\n> +       sp->recv_credit_max = 255;\n> +       sp->send_credit_target = 255;\n> +       sp->max_send_size = 1364;\n> +       /*\n> +        * The maximum fragmented upper-layer payload receive size supported\n> +        *\n> +        * Assume max_payload_per_credit is\n> +        * smbd_max_receive_size - 24 = 1340\n> +        *\n> +        * The maximum number would be\n> +        * smbd_receive_credit_max * max_payload_per_credit\n> +        *\n> +        *                       1340 * 255 = 341700 (0x536C4)\n> +        *\n> +        * The minimum value from the spec is 131072 (0x20000)\n> +        *\n> +        * For now we use the logic we used before:\n> +        *                 (1364 * 255) / 2 = 173910 (0x2A756)\n> +        */\n> +       sp->max_fragmented_recv_size = (1364 * 255) / 2;\n> +       sp->max_recv_size = 1364;\n> +       sp->max_read_write_size = 0; /* the server should change this */\n> +       sp->max_frmr_depth = 0; /* the client should change this */\n> +       sp->keepalive_interval_msec = 120 * 1000;\n> +       sp->keepalive_timeout_msec = 5 * 1000;\n> +\n> +       ret = smbdirect_proto_init();\n> +       if (ret)\n> +               goto proto_init_failed;\n> +\n>         mutex_unlock(&smbdirect_globals.mutex);\n>         pr_notice(\"subsystem loaded\\n\");\n>         return 0;\n>\n> +proto_init_failed:\n> +       smbdirect_devices_exit();\n>  devices_init_failed:\n>         destroy_workqueue(smbdirect_globals.workqueues.cleanup);\n>  alloc_cleanup_wq_failed:\n> @@ -101,6 +144,8 @@ static __exit void smbdirect_module_exit(void)\n>         pr_notice(\"subsystem unloading...\\n\");\n>         mutex_lock(&smbdirect_globals.mutex);\n>\n> +       smbdirect_proto_exit();\n> +\n>         smbdirect_devices_exit();\n>\n>         destroy_workqueue(smbdirect_globals.workqueues.accept);\n> diff --git a/fs/smb/common/smbdirect/smbdirect_mr.c b/fs/smb/common/smbdirect/smbdirect_mr.c\n> index 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>         int ret;\n>\n>  again:\n> +       smbdirect_socket_sk_unlock(sc);\n>         ret = wait_event_interruptible(sc->mr_io.ready.wait_queue,\n>                                        atomic_read(&sc->mr_io.ready.count) ||\n>                                        sc->status != SMBDIRECT_SOCKET_CONNECTED);\n> +       smbdirect_socket_sk_lock(sc);\n>         if (ret) {\n>                 smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,\n>                         \"wait_event_interruptible ret=%d (%1pe)\\n\",\n> @@ -281,7 +283,9 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc,\n>                 return NULL;\n>         }\n>\n> +       smbdirect_socket_sk_lock(sc);\n>         mr = smbdirect_connection_get_mr_io(sc);\n> +       smbdirect_socket_sk_unlock(sc);\n>         if (!mr) {\n>                 smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,\n>                         \"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>         if (mr->state == SMBDIRECT_MR_DISABLED)\n>                 goto put_kref;\n>\n> +       /*\n> +        * We are protected by mr->mutex\n> +        * without lock_sock().\n> +        */\n> +       smbdirect_socket_sk_not_owned_by_me(sc);\n> +\n>         if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {\n>                 smbdirect_mr_io_disable_locked(mr);\n>                 goto put_kref;\n> diff --git a/fs/smb/common/smbdirect/smbdirect_proto.c b/fs/smb/common/smbdirect/smbdirect_proto.c\n> new file mode 100644\n> index 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> +       struct smbdirect_socket *__sc = smbdirect_socket_from_sk(__sk); \\\n> +       __smbdirect_log_generic(__sc, SMBDIRECT_LOG_INFO, SMBDIRECT_LOG_SK, \\\n> +               __fmt \" sc=%p %s first_error=%1pe kern=%u locked=%u refs=%u dead=%u mrefs=%u\\n\", \\\n> +               ##__args, __sc, \\\n> +               smbdirect_socket_status_string(__sc->status), \\\n> +               SMBDIRECT_DEBUG_ERR_PTR(__sc->first_error), \\\n> +               (__sk)->sk_kern_sock, \\\n> +               sock_owned_by_user_nocheck(__sk), \\\n> +               refcount_read(&((__sk)->sk_refcnt)), \\\n> +               sock_flag(__sk, SOCK_DEAD), \\\n> +               module_refcount(THIS_MODULE)); \\\n> +} while (0)\n> +\n> +#define SMBDIRECT_FN_COMMENT(__sk, __comment) \\\n> +       SMBDIRECT_FN_GENERIC(__sk, \"%s with\", __comment)\n> +\n> +#define SMBDIRECT_FN_CALLED(__sk) \\\n> +       SMBDIRECT_FN_GENERIC(__sk, \"Called for\")\n> +\n> +#define SMBDIRECT_FN_RETURN_VOID(__sk) \\\n> +       SMBDIRECT_FN_GENERIC(__sk, \"Returning for\")\n> +\n> +#define SMBDIRECT_FN_RETURN_POLL(__sk, __mask) \\\n> +       SMBDIRECT_FN_GENERIC(__sk, \"Returning mask=0x%x for\", __mask)\n> +\n> +#define SMBDIRECT_FN_RETURN_INT(__sk, __ret) do { \\\n> +       bool __is_err = IS_ERR(SMBDIRECT_DEBUG_ERR_PTR(__ret)); \\\n> +       SMBDIRECT_FN_GENERIC(__sk, \"Returning ret=%d%s%s%s for\", \\\n> +               (__ret), \\\n> +               __is_err ? \" (\" : \"\", \\\n> +               __is_err ? errname(__ret) : \"\", \\\n> +               __is_err ? \")\" : \"\"); \\\n> +} while (0)\n> +\n> +static bool smbdirect_sk_logging_needed(struct smbdirect_socket *sc,\n> +                                       void *private_ptr,\n> +                                       unsigned int lvl,\n> +                                       unsigned int cls)\n> +{\n> +       /*\n> +        * Only errors by default.\n> +        */\n> +       if (lvl <= SMBDIRECT_LOG_ERR)\n> +               return true;\n> +       return false;\n> +}\n> +\n> +static void smbdirect_sk_logging_vaprintf(struct smbdirect_socket *sc,\n> +                                         const char *func,\n> +                                         unsigned int line,\n> +                                         void *private_ptr,\n> +                                         unsigned int lvl,\n> +                                         unsigned int cls,\n> +                                         struct va_format *vaf)\n> +{\n> +       if (lvl <= SMBDIRECT_LOG_ERR)\n> +               pr_err(\"%s:%u %pV\", func, line, vaf);\n> +       else\n> +               pr_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> +       static struct lock_class_key sk_key[2];\n> +       static struct lock_class_key slock_key[2];\n> +\n> +       if (WARN_ON_ONCE(!sock_allow_reclassification(sk)))\n> +               return;\n> +\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               /*\n> +                * Before we reset the owner we\n> +                * need to drop the reference of the\n> +                * existing module, this is only\n> +                * really relevant for AF_INET,\n> +                * as that is always builtin\n> +                * there's no potential leak\n> +                * of module references. We do it\n> +                * mainly in order to match the\n> +                * AF_INET6 case.\n> +                */\n> +               sk_owner_put(sk);\n> +               sk_owner_clear(sk);\n> +\n> +               sock_lock_init_class_and_name(sk,\n> +                                             \"slock-AF_INET-IPPROTO-SMBDIRECT\",\n> +                                             &slock_key[0],\n> +                                             \"sk_lock-AF_INET-IPPROTO-SMBDIRECT\",\n> +                                             &sk_key[0]);\n> +\n> +               /*\n> +                * Now that we reclassified the socket\n> +                * we're also the new sk_owner, but that's\n> +                * not needed as there's still a reference\n> +                * on sk->sk_prot->owner, which is dropped\n> +                * in sk_prot_free(). But in order to\n> +                * avoid module reference leaks to our\n> +                * own module we need to put and clear\n> +                * sk_owner, in order to allow callers\n> +                * to do their own reclassification.\n> +                */\n> +               sk_owner_put(sk);\n> +               sk_owner_clear(sk);\n> +               break;\n> +       case AF_INET6:\n> +               /*\n> +                * Before we reset the owner we\n> +                * need to drop the reference of the\n> +                * existing module.\n> +                *\n> +                * As we also use inet6_register_protosw()\n> +                * and other symbols from a possible\n> +                * ipv6.ko, we already have enough\n> +                * module references in order to avoid\n> +                * unloading of ipv6.ko, while smbdirect.ko\n> +                * is loaded.\n> +                *\n> +                * However when smbdirect.ko is unloaded\n> +                * we should not leak references in order\n> +                * to allow ipv6.ko to be unloaded as well.\n> +                */\n> +               sk_owner_put(sk);\n> +               sk_owner_clear(sk);\n> +\n> +               sock_lock_init_class_and_name(sk,\n> +                                             \"slock-AF_INET6-IPPROTO-SMBDIRECT\",\n> +                                             &slock_key[1],\n> +                                             \"sk_lock-AF_INET6-IPPROTO-SMBDIRECT\",\n> +                                             &sk_key[1]);\n> +\n> +               /*\n> +                * Now that we reclassified the socket\n> +                * we're also the new sk_owner, but that's\n> +                * not needed as there's still a reference\n> +                * on sk->sk_prot->owner, which is dropped\n> +                * in sk_prot_free(). But in order to\n> +                * avoid module reference leaks to our\n> +                * own module we need to put and clear\n> +                * sk_owner, in order to allow callers\n> +                * to do their own reclassification.\n> +                */\n> +               sk_owner_put(sk);\n> +               sk_owner_clear(sk);\n> +               break;\n> +       default:\n> +               WARN_ON_ONCE(1);\n> +       }\n> +#endif /* CONFIG_DEBUG_LOCK_ALLOC */\n> +}\n> +\n> +static void smbdirect_sk_destruct(struct sock *sk)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +\n> +       /*\n> +        * Called by sk_free()\n> +        */\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       if (WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_DESTROYED)) {\n> +               pr_err(\"Attempt to release SMBDIRECT socket in status %s sc %p\\n\",\n> +                      smbdirect_socket_status_string(sc->status), sc);\n> +               SMBDIRECT_FN_RETURN_VOID(sk);\n> +               return;\n> +       }\n> +\n> +       SMBDIRECT_FN_COMMENT(sk, \"calling orig_sk_destruct\");\n> +       smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n> +               \"sc[%p]->orig_sk_destruct[%ps]\\n\",\n> +               sc, sc->orig_sk_destruct);\n> +       sc->orig_sk_destruct(sk);\n> +       SMBDIRECT_FN_RETURN_VOID(sk);\n> +}\n> +\n> +static int smbdirect_sk_init(struct sock *sk)\n> +{\n> +       struct socket *sock = sk->sk_socket;\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       const struct smbdirect_socket_parameters *sp = &smbdirect_globals.default_parameters;\n> +       void (*orig_sk_destruct)(struct sock *sk);\n> +       int ret;\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       smbdirect_sk_reclassify(sk);\n> +\n> +       smbdirect_socket_init(sc);\n> +       smbdirect_socket_set_logging(sc,\n> +                                    NULL,\n> +                                    smbdirect_sk_logging_needed,\n> +                                    smbdirect_sk_logging_vaprintf);\n> +\n> +       smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n> +               \"Called for sk=%p family=%u protocol=%u type=%u\\n\",\n> +               sk, sk->sk_family, sk->sk_protocol, sk->sk_type);\n> +\n> +       sk_sockets_allocated_inc(sk);\n> +       sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);\n> +\n> +       orig_sk_destruct = sk->sk_destruct;\n> +       SMBDIRECT_FN_COMMENT(sk, \"remembered orig_sk_destruct\");\n> +       smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n> +               \"sc[%p]->orig_sk_destruct[%ps]\\n\",\n> +               sc, orig_sk_destruct);\n> +       sc->orig_sk_destruct = orig_sk_destruct;\n> +       sk->sk_destruct = smbdirect_sk_destruct;\n> +\n> +       /*\n> +        * We want to handle all sockopts explicitly\n> +        * and only support what we really support.\n> +        */\n> +       set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags);\n> +       /*\n> +        * There are no legacy callers, so we are strict\n> +        * regarding ipv4 vs. ipv6.\n> +        */\n> +       sk->sk_ipv6only = true;\n> +\n> +       /*\n> +        * No userspace sockets yet...\n> +        */\n> +       if (!sk->sk_kern_sock) {\n> +               sc->first_error = -EPROTONOSUPPORT;\n> +               SMBDIRECT_FN_COMMENT(sk, \"No userspace sockets\");\n> +               return -EPROTONOSUPPORT;\n> +       }\n> +\n> +       ret = smbdirect_socket_init_new(sock_net(sk), sc);\n> +       if (ret)\n> +               goto socket_init_failed;\n> +       /*\n> +        * smbdirect_socket_init_new() called smbdirect_socket_init() again,\n> +        * so we need to call smbdirect_socket_set_logging() again!\n> +        */\n> +       smbdirect_socket_set_logging(sc,\n> +                                    NULL,\n> +                                    smbdirect_sk_logging_needed,\n> +                                    smbdirect_sk_logging_vaprintf);\n> +\n> +       WARN_ON_ONCE(sc->orig_sk_destruct != orig_sk_destruct);\n> +       WARN_ON_ONCE(sk->sk_destruct != smbdirect_sk_destruct);\n> +\n> +       ret = smbdirect_socket_set_initial_parameters(sc, sp);\n> +       if (ret)\n> +               goto set_params_failed;\n> +\n> +       ret = smbdirect_socket_set_kernel_settings(sc, IB_POLL_SOFTIRQ, sk->sk_allocation);\n> +       if (ret)\n> +               goto set_settings_failed;\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, 0);\n> +       return 0;\n> +\n> +set_settings_failed:\n> +set_params_failed:\n> +socket_init_failed:\n> +       sc->first_error = ret;\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static void smbdirect_sk_destroy(struct sock *sk)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       /*\n> +        * For now do a sync disconnect/destroy\n> +        *\n> +        * SMBDIRECT_LOG_INFO is enough here\n> +        * as this is the typical case where\n> +        * we terminate the connection ourself.\n> +        */\n> +       smbdirect_socket_schedule_cleanup_lvl(sc,\n> +                                             SMBDIRECT_LOG_INFO,\n> +                                             -ESHUTDOWN);\n> +       smbdirect_socket_destroy_sync(sc);\n> +\n> +       sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);\n> +       sk_sockets_allocated_dec(sk);\n> +\n> +       SMBDIRECT_FN_RETURN_VOID(sk);\n> +}\n> +\n> +static int smbdirect_sk_hash(struct sock *sk)\n> +{\n> +       SMBDIRECT_FN_CALLED(sk);\n> +       return 0;\n> +}\n\nIt seems this was implemented just to fill all function\npointers of sk->sk_prot but looks unnecessary.\n\nSame for other NOP functions, unhash(), release_cb(), etc.\n\n\n> +\n> +static void smbdirect_sk_unhash(struct sock *sk)\n> +{\n> +       SMBDIRECT_FN_CALLED(sk);\n> +}\n> +\n> +static void smbdirect_sk_release_cb(struct sock *sk)\n> +{\n> +       /*\n> +        * Called from release_sock()\n> +        */\n> +       SMBDIRECT_FN_CALLED(sk);\n> +}\n> +\n> +static int smbdirect_sk_pre_bind(struct sock *sk,\n> +                                struct sockaddr_unsized *uaddr,\n> +                                int *addr_len,\n> +                                u32 *flags,\n> +                                u16 *port)\n> +{\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       if (*addr_len < sizeof(uaddr->sa_family))\n> +               return -EINVAL;\n> +\n> +       /* AF_UNSPEC is not allowed */\n> +       if (sk->sk_family != uaddr->sa_family)\n> +               return -EAFNOSUPPORT;\n> +\n> +       /*\n> +        * BPF prog is run before any checks are done so that if the prog\n> +        * changes context in a wrong way it will be caught.\n> +        */\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               if (*addr_len < sizeof(struct sockaddr_in))\n> +                       return -EINVAL;\n> +\n> +               *port = ntohs(((struct sockaddr_in *)uaddr)->sin_port);\n> +\n> +               return BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr_len,\n> +                                                         CGROUP_INET4_BIND,\n> +                                                         flags);\n\nDo you really need these bpf hooks ?\n\nIt seems the smb sockets can be created from kthread\nand tied to the root cgroup.\n\n\n> +       case AF_INET6:\n> +               /*\n> +                * We require a full struct sockaddr_in6 (28 bytes) instead of a\n> +                * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don't\n> +                * have any legacy callers in userspace and the\n> +                * rdma layer also expects that.\n> +                */\n> +               if (*addr_len < sizeof(struct sockaddr_in6))\n> +                       return -EINVAL;\n> +\n> +               *port = ntohs(((struct sockaddr_in6 *)uaddr)->sin6_port);\n> +\n> +               return BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr_len,\n> +                                                         CGROUP_INET6_BIND,\n> +                                                         flags);\n> +       }\n> +\n> +       return -EAFNOSUPPORT;\n> +}\n> +\n> +static int smbdirect_sk_do_bind(struct sock *sk,\n> +                               struct sockaddr_unsized *uaddr,\n> +                               const u32 flags,\n> +                               const u16 port)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       bool is_any_addr = true;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       if (flags & BIND_WITH_LOCK)\n> +               sock_owned_by_me(sk);\n> +       else\n> +               sock_not_owned_by_me(sk);\n> +\n> +       ret = smbdirect_socket_bind(sc, (struct sockaddr *)uaddr);\n> +       if (ret) {\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       ret = smbdirect_socket_sync_saddr_to_sk(sc, &is_any_addr);\n> +       if (ret) {\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       /* Make sure we are allowed to bind here. */\n> +       if (sk->sk_num && !(flags & BIND_FROM_BPF)) {\n> +               switch (sk->sk_family) {\n> +               case AF_INET:\n> +                       ret = BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk);\n> +                       if (ret) {\n> +                               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +                               return ret;\n> +                       }\n> +                       break;\n> +\n> +               case AF_INET6:\n> +                       ret = BPF_CGROUP_RUN_PROG_INET6_POST_BIND(sk);\n> +                       if (ret) {\n> +                               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +                               return ret;\n> +                       }\n> +                       break;\n> +               }\n> +       }\n> +\n> +       if (!is_any_addr)\n> +               sk->sk_userlocks |= SOCK_BINDADDR_LOCK;\n> +       if (port)\n> +               sk->sk_userlocks |= SOCK_BINDPORT_LOCK;\n\nCan this socket be passed to SOCK_BINDPORT_LOCK user,\ninet_bhash2_reset_saddr(), inet_sk_rebuild_header() ?\n\n\n> +\n> +       ret = 0;\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sk_bind(struct sock *sk, struct sockaddr_unsized *addr, int addr_len)\n> +{\n> +       struct net *net = sock_net(sk);\n> +       u32 flags = BIND_WITH_LOCK;\n> +       u16 port = 0;\n> +       u16 check_port = 0;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       ret = smbdirect_sk_pre_bind(sk, addr, &addr_len, &flags, &port);\n> +       if (ret) {\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       /*\n> +        * treat the iwarp tcp port for\n> +        * smb (5445) as the main smb port (445)\n> +        * and only allow the bind if 445\n> +        * would be allowed.\n> +        */\n> +       if (port == 5445)\n> +               check_port = 445;\n> +       else\n> +               check_port = port;\n> +\n> +       if (!(flags & BIND_NO_CAP_NET_BIND_SERVICE) &&\n> +           check_port && inet_port_requires_bind_service(net, check_port) &&\n> +           !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {\n> +               ret = -EACCES;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (flags & BIND_WITH_LOCK)\n> +               lock_sock(sk);\n\nIs connect() called without bind() and could a bpf prog\ncalls bpf_bind() for this socket ?\n\n\n> +\n> +       ret = smbdirect_sk_do_bind(sk, addr, flags, port);\n> +\n> +       if (flags & BIND_WITH_LOCK)\n> +               release_sock(sk);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static struct sock *smbdirect_sk_accept(struct sock *lsk, struct proto_accept_arg *arg)\n> +{\n> +       struct smbdirect_socket *lsc = smbdirect_socket_from_sk(lsk);\n> +       long timeo = sock_rcvtimeo(lsk, arg->flags & O_NONBLOCK);\n> +       struct smbdirect_socket *nsc;\n> +       struct sock *nsk;\n> +\n> +       SMBDIRECT_FN_CALLED(lsk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(lsk);\n> +\n> +       lock_sock(lsk);\n> +       nsc = smbdirect_socket_accept(lsc, timeo, arg);\n> +       release_sock(lsk);\n> +       if (!nsc) {\n> +               SMBDIRECT_FN_RETURN_INT(lsk, arg->err);\n> +               return NULL;\n> +       }\n> +       nsk = &nsc->sk;\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(nsk);\n\nLooks redundant.\n\n\n> +\n> +       SMBDIRECT_FN_RETURN_INT(lsk, 0);\n> +       return nsk;\n> +}\n> +\n> +static int smbdirect_sk_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\n> +{\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       if (addr_len < sizeof(uaddr->sa_family))\n> +               return -EINVAL;\n> +\n> +       if (sk->sk_family != uaddr->sa_family)\n> +               return -EAFNOSUPPORT;\n> +\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               if (addr_len < sizeof(struct sockaddr_in))\n> +                       return -EINVAL;\n> +\n> +               return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr, &addr_len);\n> +       case AF_INET6:\n> +               /*\n> +                * We require a full struct sockaddr_in6 (28 bytes) instead of a\n> +                * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don't\n> +                * have any legacy callers in userspace and the\n> +                * rdma layer also expects that.\n> +                */\n> +               if (addr_len < sizeof(struct sockaddr_in6))\n> +                       return -EINVAL;\n> +\n> +               return BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr, &addr_len);\n> +       }\n> +\n> +       return -EAFNOSUPPORT;\n> +}\n> +\n> +static int smbdirect_sk_connect(struct sock *sk, struct sockaddr_unsized *addr, int addr_len)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       ret = smbdirect_connect(sc, (struct sockaddr *)addr);\n\nWhy is this called via sk->sk_prot instead of being called\ndirectly from sock->ops->connect() ?\n\nSame for other sk->sk_prot functions.\n\n\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sk_setsockopt(struct sock *sk, int level, int optname,\n> +                                  sockptr_t optval, unsigned int optlen)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       switch (level) {\n> +       default:\n> +               SMBDIRECT_FN_COMMENT(sk, \"default\");\n> +               smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n> +                       \"level=%d optname=%d for sk=%p\\n\",\n> +                       level, optname, sk);\n> +               ret = -EOPNOTSUPP;\n> +               break;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sk_getsockopt(struct sock *sk, int level, int optname,\n> +                                  char __user *optval, int __user *optlen)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       switch (level) {\n> +       default:\n> +               SMBDIRECT_FN_COMMENT(sk, \"default\");\n> +               smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n> +                       \"level=%d optname=%d for sk=%p\\n\",\n> +                       level, optname, sk);\n> +               ret = -EOPNOTSUPP;\n> +               break;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sk_ioctl(struct sock *sk, int cmd, int *karg)\n\nIs there any in-kernel ioctl() user for this socket ?\n\n\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       switch (cmd) {\n> +       default:\n> +               SMBDIRECT_FN_COMMENT(sk, \"default\");\n> +               smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO,\n> +                       \"cmd=%d (0x%x) for sk=%p\\n\",\n> +                       cmd, cmd, sk);\n> +               ret = -ENOIOCTLCMD;\n> +               break;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static inline size_t smbdirect_cmsg_count(const struct msghdr *_msg,\n> +                                         int *first_sol_smbdirect_type)\n> +{\n> +       struct msghdr *msg = (struct msghdr *)(uintptr_t)(const void *)_msg;\n> +       struct cmsghdr *cmsg = NULL;\n> +       size_t count = 0;\n> +\n> +       if (first_sol_smbdirect_type != NULL)\n> +               *first_sol_smbdirect_type = -1;\n> +\n> +       for (cmsg = CMSG_FIRSTHDR(msg);\n> +            cmsg != NULL;\n> +            cmsg = CMSG_NXTHDR(msg, cmsg)) {\n> +               count++;\n> +               if (cmsg->cmsg_level != SOL_SMBDIRECT)\n> +                       continue;\n> +               if (first_sol_smbdirect_type != NULL) {\n> +                       *first_sol_smbdirect_type = cmsg->cmsg_type;\n> +                       first_sol_smbdirect_type = NULL;\n> +               }\n> +       }\n> +\n> +       return count;\n> +}\n> +\n> +static __always_inline\n> +ssize_t __smbdirect_cmsg_extract(const struct msghdr *_msg,\n> +                                int cmsg_type,\n> +                                void *_payload,\n> +                                size_t payloadmin,\n> +                                size_t payloadmax)\n> +{\n> +       struct msghdr *msg = (struct msghdr *)(uintptr_t)(const void *)_msg;\n> +       size_t cmsg_len_min = CMSG_LEN(payloadmin);\n> +       size_t cmsg_len_max = CMSG_LEN(payloadmax);\n> +       const size_t cmsg_len_hdr = CMSG_LEN(0);\n> +       uint8_t *payload = (uint8_t *)_payload;\n> +       struct cmsghdr *cmsg = NULL;\n> +       size_t payloadlen;\n> +\n> +       BUILD_BUG_ON(cmsg_len_min > cmsg_len_max);\n> +       if (WARN_ON_ONCE(cmsg_len_min > cmsg_len_max))\n> +               return -EBADMSG;\n> +\n> +       for (cmsg = CMSG_FIRSTHDR(msg);\n> +            cmsg != NULL;\n> +            cmsg = CMSG_NXTHDR(msg, cmsg)) {\n> +               if (cmsg->cmsg_level != SOL_SMBDIRECT)\n> +                       continue;\n> +\n> +               if (cmsg->cmsg_type != cmsg_type)\n> +                       continue;\n> +\n> +               if (cmsg->cmsg_len < cmsg_len_min)\n> +                       return -EBADMSG;\n> +\n> +               if (cmsg->cmsg_len > cmsg_len_max)\n> +                       return -EMSGSIZE;\n> +\n> +               payloadlen = cmsg->cmsg_len - cmsg_len_hdr;\n> +               if (payloadlen > 0)\n> +                       memcpy(payload, CMSG_DATA(cmsg), payloadlen);\n> +               if (payloadlen < payloadmax)\n> +                       memset(payload + payloadlen, 0, payloadmax - payloadlen);\n> +               return payloadlen;\n> +       }\n> +\n> +       return -ENOMSG;\n> +}\n> +\n> +static __always_inline\n> +int smbdirect_buffer_remote_invalidate_cmsg_extract(const struct msghdr *msg,\n> +                                                   u32 *remote_token)\n> +{\n> +       struct smbdirect_buffer_remote_invalidate_args args = {\n> +               .remote_token = 0,\n> +       };\n> +       ssize_t ret;\n> +\n> +       ret = __smbdirect_cmsg_extract(msg,\n> +                                      SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE,\n> +                                      &args, sizeof(args), sizeof(args));\n> +       if (ret < 0)\n> +               return ret;\n> +\n> +       *remote_token = args.remote_token;\n> +       return 0;\n> +}\n> +\n> +static int smbdirect_sk_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t msg_len)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       struct iov_iter *iter = &msg->msg_iter;\n> +       unsigned int flags = msg->msg_flags;\n> +       size_t cmsg_count = 0;\n> +       int cmsg_type = -1;\n> +       bool need_invalidate = false;\n> +       u32 remote_key = 0;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       cmsg_count = smbdirect_cmsg_count(msg, &cmsg_type);\n> +       if (cmsg_count > 1) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (cmsg_type == SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE) {\n> +               ret = smbdirect_buffer_remote_invalidate_cmsg_extract(msg, &remote_key);\n> +               if (!ret)\n> +                       need_invalidate = true; /* remote_key is valid */\n> +               else if (ret != -ENOMSG) {\n> +                       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +                       return ret;\n> +               }\n> +       } else if (cmsg_count) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (WARN_ON_ONCE(iov_iter_rw(iter) != ITER_SOURCE)) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (WARN_ON_ONCE(iov_iter_count(iter) != msg_len)) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (flags & MSG_DONTWAIT) {\n> +               if (!sc->first_error && msg_len && atomic_read(&sc->send_io.credits.count) == 0) {\n> +                       ret = -EAGAIN;\n> +                       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +                       return ret;\n> +               }\n> +       }\n> +       flags &= ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL);\n> +\n> +       ret = smbdirect_connection_send_iter(sc,\n> +                                            iter,\n> +                                            flags,\n> +                                            need_invalidate,\n> +                                            remote_key);\n> +       if (ret < 0)\n> +               /* Handle error and possibly send SIGPIPE. */\n> +               ret = sk_stream_error(sk, msg->msg_flags, ret);\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sk_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)\n> +{\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       lock_sock(sk);\n> +       ret = smbdirect_sk_sendmsg_locked(sk, msg, msg_len);\n> +       release_sock(sk);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sk_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       struct iov_iter *iter = &msg->msg_iter;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       if (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       if (WARN_ON_ONCE(iov_iter_rw(iter) != ITER_DEST)) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       /*\n> +        * For now smbdirect_connection_recvmsg() relies\n> +        * on this assertion and the current in kernel\n> +        * users are working that way.\n> +        */\n> +       if (WARN_ON_ONCE(iov_iter_count(iter) != len)) {\n> +               ret = -EINVAL;\n> +               SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +               return ret;\n> +       }\n> +\n> +       lock_sock(sk);\n> +       if (flags & MSG_DONTWAIT) {\n> +               if (!sc->first_error && len && sc->recv_io.reassembly.data_length == 0) {\n> +                       ret = -EAGAIN;\n> +                       release_sock(sk);\n> +                       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +                       return ret;\n> +               }\n> +       }\n> +       flags &= ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL);\n> +       ret = smbdirect_connection_recvmsg(sc, msg, flags);\n> +       if (msg->msg_get_inq && ret >= 0)\n> +               msg->msg_inq = sc->recv_io.reassembly.data_length;\n> +       release_sock(sk);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static void smbdirect_sk_shutdown(struct sock *sk, int how)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       smbdirect_socket_schedule_cleanup(sc, -ESHUTDOWN);\n> +\n> +       SMBDIRECT_FN_RETURN_VOID(sk);\n> +}\n> +\n> +static int smbdirect_sk_disconnect(struct sock *sk, int flags)\n> +{\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       smbdirect_socket_schedule_cleanup(sc, -ESHUTDOWN);\n> +\n> +       if (flags & O_NONBLOCK) {\n> +               if (sc->status >= SMBDIRECT_SOCKET_DISCONNECTED) {\n> +                       SMBDIRECT_FN_RETURN_INT(sk, 0);\n> +                       return 0;\n> +               }\n> +\n> +               /*\n> +                * This will cause SS_DISCONNECTING in\n> +                * smbdirect_sock_connect_locked().\n> +                */\n> +               SMBDIRECT_FN_RETURN_INT(sk, sc->first_error);\n> +               return sc->first_error;\n> +       }\n> +\n> +       smbdirect_socket_destroy_sync(sc);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, 0);\n> +       return 0;\n> +}\n> +\n> +static void smbdirect_sk_close(struct sock *sk, long timeout)\n> +{\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       /*\n> +        * We hold an additional reference so\n> +        * that the sock_put() in sk_common_release()\n> +        * doesn't call sk_free(), that is potentially\n> +        * deferred to our sock_put() after release_sock().\n> +        *\n> +        * Note that sk_common_release() calls\n> +        * smbdirect_sk_destroy() as the first thing.\n> +        */\n> +       sock_hold(sk);\n> +       lock_sock(sk);\n> +       sk_common_release(sk);\n> +       release_sock(sk);\n> +       SMBDIRECT_FN_COMMENT(sk, \"before sock_put()\");\n> +       sock_put(sk);\n> +}\n> +\n> +static struct percpu_counter smbdirect_sockets_allocated;\n> +\n> +static struct proto smbdirect_prot = {\n> +       .name                   = \"smbdirect\",\n> +       .owner                  = THIS_MODULE,\n> +       .obj_size               = sizeof(struct smbdirect_socket),\n> +       .ipv6_pinfo_offset      = offsetof(struct smbdirect_socket, inet6),\n> +       .init                   = smbdirect_sk_init,\n> +       .destroy                = smbdirect_sk_destroy,\n> +       .hash                   = smbdirect_sk_hash,\n> +       .unhash                 = smbdirect_sk_unhash,\n> +       .release_cb             = smbdirect_sk_release_cb,\n> +       .bind                   = smbdirect_sk_bind,\n> +       .accept                 = smbdirect_sk_accept,\n> +       .pre_connect            = smbdirect_sk_pre_connect,\n> +       .connect                = smbdirect_sk_connect,\n> +       .setsockopt             = smbdirect_sk_setsockopt,\n> +       .getsockopt             = smbdirect_sk_getsockopt,\n> +       .ioctl                  = smbdirect_sk_ioctl,\n> +       .sendmsg                = smbdirect_sk_sendmsg,\n> +       .recvmsg                = smbdirect_sk_recvmsg,\n> +       .shutdown               = smbdirect_sk_shutdown,\n> +       .disconnect             = smbdirect_sk_disconnect,\n> +       .close                  = smbdirect_sk_close,\n> +       .sockets_allocated      = &smbdirect_sockets_allocated,\n> +};\n> +\n> +static int smbdirect_sock_release(struct socket *sock)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not locked */\n> +       sock_not_owned_by_me(sk);\n> +       WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));\n> +\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               SMBDIRECT_FN_COMMENT(sk, \"calling inet_release()\");\n> +               ret = inet_release(sock);\n\nGiven setsockopt() is banned, smbdirect_sk_close() can be\ninlined here.\n\n\n> +               break;\n> +       case AF_INET6:\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +               SMBDIRECT_FN_COMMENT(sk, \"calling inet6_release()\");\n> +               ret = inet6_release(sock);\n> +#else\n> +               ret = -EAFNOSUPPORT;\n> +#endif\n> +               break;\n> +       default:\n> +               ret = -EAFNOSUPPORT;\n> +               break;\n> +       }\n> +\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_bind(struct socket *sock, struct sockaddr_unsized *saddr, int len)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               ret = inet_bind(sock, saddr, len);\n\ninet_bind() just calls sk->sk_prot->bind() if set.\nSo, the same question applies; why not inline\nsk->sk_prot->bind() here.\n\n\n> +               break;\n> +       case AF_INET6:\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +               ret = inet6_bind(sock, saddr, len);\n> +#else\n> +               ret = -EAFNOSUPPORT;\n> +#endif\n> +               break;\n> +       default:\n> +               ret = -EAFNOSUPPORT;\n> +               break;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_connect_locked(struct socket *sock,\n> +                                        struct sockaddr_unsized *uaddr,\n> +                                        int addr_len, int flags)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is already locked */\n> +       sock_owned_by_me(sk);\n> +\n> +       if (addr_len < sizeof(uaddr->sa_family))\n> +               return -EINVAL;\n> +\n> +       if (sk->sk_family != uaddr->sa_family)\n> +               return -EAFNOSUPPORT;\n> +\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               if (addr_len < sizeof(struct sockaddr_in))\n> +                       return -EINVAL;\n> +               break;\n> +       case AF_INET6:\n> +               /*\n> +                * We require a full struct sockaddr_in6 (28 bytes) instead of a\n> +                * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don't\n> +                * have any legacy callers in userspace and the\n> +                * rdma layer also expects that.\n> +                */\n> +               if (addr_len < sizeof(struct sockaddr_in6))\n> +                       return -EINVAL;\n> +               break;\n> +       default:\n> +               return -EAFNOSUPPORT;\n> +       }\n> +\n> +       switch (sock->state) {\n> +       case SS_CONNECTED:\n> +               return -EISCONN;\n> +       case SS_CONNECTING:\n> +               return -EALREADY;\n> +       case SS_UNCONNECTED:\n> +               break;\n> +       default:\n> +               return -EINVAL;\n> +       }\n> +\n> +       if (sc->status == SMBDIRECT_SOCKET_CONNECTED)\n> +               return -EISCONN;\n> +\n> +       if (sc->status != SMBDIRECT_SOCKET_CREATED)\n> +               return -EINVAL;\n> +\n> +       if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) {\n> +               ret = sk->sk_prot->pre_connect(sk, uaddr, addr_len);\n> +               if (ret)\n> +                       return ret;\n> +       }\n> +\n> +       ret = sk->sk_prot->connect(sk, uaddr, addr_len);\n> +       if (ret < 0)\n> +               return ret;\n> +\n> +       inet_sk_set_state(sk, TCP_SYN_SENT);\n> +       sock->state = SS_CONNECTING;\n> +\n> +       if (flags & O_NONBLOCK)\n> +               return -EINPROGRESS;\n> +\n> +       ret = smbdirect_connection_wait_for_connected(sc);\n> +       if (ret)\n> +               goto sock_error;\n> +\n> +       return 0;\n> +\n> +sock_error:\n> +       sock->state = SS_UNCONNECTED;\n> +       sk->sk_disconnects++;\n> +       if (sk->sk_prot->disconnect(sk, flags))\n> +               sock->state = SS_DISCONNECTING;\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_connect(struct socket *sock,\n> +                                 struct sockaddr_unsized *uaddr,\n> +                                 int addr_len, int flags)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       lock_sock(sk);\n> +       ret = smbdirect_sock_connect_locked(sock, uaddr, addr_len, flags);\n> +       release_sock(sk);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_listen(struct socket *sock, int backlog)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       lock_sock(sk);\n> +       ret = smbdirect_socket_listen(sc, backlog);\n> +       release_sock(sk);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_accept(struct socket *lsock, struct socket *nsock,\n> +                                struct proto_accept_arg *arg)\n> +{\n> +       struct sock *lsk = lsock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(lsk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(lsk);\n> +\n> +       ret = inet_accept(lsock, nsock, arg);\n\nCould this account socket memory to memcg twice ?\nsee 4a997d49d92a and 16942cf4d3e3\n\n\n> +       if (!ret)\n> +               /*\n> +                * We want to handle all sockopts explicitly\n> +                * and only support what we really support.\n> +                */\n> +               set_bit(SOCK_CUSTOM_SOCKOPT, &nsock->flags);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(lsk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_getname(struct socket *sock, struct sockaddr *uaddr, int peer)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       switch (sk->sk_family) {\n> +       case AF_INET:\n> +               ret = inet_getname(sock, uaddr, peer);\n> +               break;\n> +       case AF_INET6:\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +               ret = inet6_getname(sock, uaddr, peer);\n> +#else\n> +               ret = -EAFNOSUPPORT;\n> +#endif\n> +               break;\n> +       default:\n> +               ret = -EAFNOSUPPORT;\n> +               break;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static __poll_t smbdirect_sock_poll(struct file *file, struct socket *sock, poll_table *wait)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);\n> +       __poll_t mask = 0;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       sock_poll_wait(file, sock, wait);\n> +\n> +       if (sc->status == SMBDIRECT_SOCKET_LISTENING) {\n> +               if (!list_empty_careful(&sc->listen.ready))\n> +                       mask |= EPOLLIN | EPOLLRDNORM;\n> +               SMBDIRECT_FN_RETURN_POLL(sk, mask);\n> +               return mask;\n> +       }\n> +\n> +       if (sc->first_error) {\n> +               /*\n> +                * A broken connection should report almost everything in order to let\n> +                * applications to detect it reliable.\n> +                */\n> +               mask |= EPOLLHUP;\n> +               mask |= EPOLLERR;\n> +               mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;\n> +               mask |= EPOLLOUT | EPOLLWRNORM;\n> +               SMBDIRECT_FN_RETURN_POLL(sk, mask);\n> +               return mask;\n> +       }\n> +\n> +       if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {\n> +               /*\n> +                * A just created socket.\n> +                */\n> +               SMBDIRECT_FN_RETURN_POLL(sk, mask);\n> +               return mask;\n> +       }\n> +\n> +       if (sc->recv_io.reassembly.data_length > 0)\n> +               mask |= EPOLLIN | EPOLLRDNORM;\n> +\n> +       if (atomic_read(&sc->send_io.bcredits.count) > 0 &&\n> +           atomic_read(&sc->send_io.lcredits.count) > 0 &&\n> +           atomic_read(&sc->send_io.credits.count) > 0)\n> +               mask |= EPOLLOUT | EPOLLWRNORM;\n> +       else {\n> +               sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);\n> +               set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);\n> +\n> +               /*\n> +                * Race breaker. If space is freed after\n> +                * wspace test but before the flags are set,\n> +                * IO signal will be lost. Memory barrier\n> +                * pairs with the input side.\n> +                */\n> +               smp_mb__after_atomic();\n> +               if (atomic_read(&sc->send_io.bcredits.count) > 0 &&\n> +                   atomic_read(&sc->send_io.lcredits.count) > 0 &&\n> +                   atomic_read(&sc->send_io.credits.count) > 0)\n> +                       mask |= EPOLLOUT | EPOLLWRNORM;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_POLL(sk, mask);\n> +       return mask;\n> +}\n> +\n> +static int smbdirect_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       /*\n> +        * We may need to handle some here as\n> +        * smbirect_sk_ioctl() only gets a kernel\n> +        * int pointer as arg, but we may\n> +        * need to the whole struct\n> +        */\n> +       switch (cmd) {\n> +       default:\n> +               /*\n> +                * Note this has some special handling for\n> +                * sk->sk_type == SOCK_RAW, in case we ever\n> +                * implement SOCK_RAW...\n> +                *\n> +                * It calls smbdirect_sk_ioctl()...\n> +                */\n> +               ret = sk_ioctl(sk, cmd, (void __user *)arg);\n> +               break;\n> +       }\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_shutdown(struct socket *sock, int how)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret = 0;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       /*\n> +        * We have these from userspace:\n> +        * SHUT_RD = 0, SHUT_WR = 1 and SHUT_RDWR = 2\n> +        *\n> +        * And we map them to SHUTDOWN_MASK = 3\n> +        * RCV_SHUTDOWN = 1, SEND_SHUTDOWN = 2, BOTH = 3\n> +        */\n> +       how++;\n> +       if ((how & ~SHUTDOWN_MASK) || !how)     /* MAXINT->0 */\n> +               return -EINVAL;\n> +\n> +       lock_sock(sk);\n> +\n> +       switch (sk->sk_state) {\n> +       case TCP_CLOSE:\n> +               ret = -ENOTCONN;\n> +               fallthrough;\n> +       default:\n> +               WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | how);\n> +               sk->sk_prot->shutdown(sk, how);\n> +               break;\n> +\n> +       case TCP_SYN_SENT:\n> +       case TCP_SYN_RECV:\n> +               ret = sk->sk_prot->disconnect(sk, O_NONBLOCK);\n> +               break;\n> +       }\n> +\n> +       /* Wake up anyone sleeping in poll. */\n> +       sk->sk_state_change(sk);\n> +       release_sock(sk);\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_setsockopt(struct socket *sock, int level, int optname,\n> +                                    sockptr_t optval, unsigned int optlen)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       ret = sock_common_setsockopt(sock, level, optname, optval, optlen);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_getsockopt(struct socket *sock, int level, int optname,\n> +                                    char __user *optval, int __user *optlen)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       ret = sock_common_getsockopt(sock, level, optname, optval, optlen);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       ret = sk->sk_prot->sendmsg(sk, msg, len);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static int smbdirect_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,\n> +                                 int flags)\n> +{\n> +       struct sock *sk = sock->sk;\n> +       int ret;\n> +\n> +       SMBDIRECT_FN_CALLED(sk);\n> +\n> +       /* assert it is not already locked */\n> +       sock_not_owned_by_me(sk);\n> +\n> +       ret = sock_common_recvmsg(sock, msg, size, flags);\n> +\n> +       SMBDIRECT_FN_RETURN_INT(sk, ret);\n> +       return ret;\n> +}\n> +\n> +static const struct proto_ops smbdirect_inet_proto_ops = {\n> +       .family                 = PF_INET,\n> +       .owner                  = THIS_MODULE,\n> +       .release                = smbdirect_sock_release,\n> +       .bind                   = smbdirect_sock_bind,\n> +       .connect                = smbdirect_sock_connect,\n> +       .socketpair             = sock_no_socketpair,\n> +       .listen                 = smbdirect_sock_listen,\n> +       .accept                 = smbdirect_sock_accept,\n> +       .getname                = smbdirect_sock_getname,\n> +       .poll                   = smbdirect_sock_poll,\n> +       .ioctl                  = smbdirect_sock_ioctl,\n> +       .shutdown               = smbdirect_sock_shutdown,\n> +       .setsockopt             = smbdirect_sock_setsockopt,\n> +       .getsockopt             = smbdirect_sock_getsockopt,\n> +       .sendmsg                = smbdirect_sock_sendmsg,\n> +       .sendmsg_locked         = smbdirect_sk_sendmsg_locked,\n> +       .recvmsg                = smbdirect_sock_recvmsg,\n> +       .mmap                   = sock_no_mmap,\n> +};\n> +\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +static const struct proto_ops smbdirect_inet6_proto_ops = {\n> +       .family                 = PF_INET6,\n> +       .owner                  = THIS_MODULE,\n> +       .release                = smbdirect_sock_release,\n> +       .bind                   = smbdirect_sock_bind,\n> +       .connect                = smbdirect_sock_connect,\n> +       .socketpair             = sock_no_socketpair,\n> +       .listen                 = smbdirect_sock_listen,\n> +       .accept                 = smbdirect_sock_accept,\n> +       .getname                = smbdirect_sock_getname,\n> +       .poll                   = smbdirect_sock_poll,\n> +       .ioctl                  = smbdirect_sock_ioctl,\n> +       .shutdown               = smbdirect_sock_shutdown,\n> +       .setsockopt             = smbdirect_sock_setsockopt,\n> +       .getsockopt             = smbdirect_sock_getsockopt,\n> +       .sendmsg                = smbdirect_sock_sendmsg,\n> +       .sendmsg_locked         = smbdirect_sk_sendmsg_locked,\n> +       .recvmsg                = smbdirect_sock_recvmsg,\n> +       .mmap                   = sock_no_mmap,\n> +};\n> +#endif\n> +\n> +static struct inet_protosw smbdirect_inet_stream_protosw = {\n> +       .type           = SOCK_STREAM,\n> +       .protocol       = IPPROTO_SMBDIRECT,\n> +       .prot           = &smbdirect_prot,\n> +       .ops            = &smbdirect_inet_proto_ops,\n> +};\n> +\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +static struct inet_protosw smbdirect_inet6_stream_protosw = {\n> +       .type           = SOCK_STREAM,\n> +       .protocol       = IPPROTO_SMBDIRECT,\n> +       .prot           = &smbdirect_prot,\n> +       .ops            = &smbdirect_inet6_proto_ops,\n> +};\n> +#endif\n> +\n> +struct smbdirect_socket *smbdirect_socket_from_sock(const struct socket *sock)\n> +{\n> +       if (!sock ||\n> +           !sock->sk ||\n> +           sock->sk->sk_protocol != IPPROTO_SMBDIRECT)\n> +               return NULL;\n> +\n> +       if (WARN_ON_ONCE(sock->sk->sk_destruct != smbdirect_sk_destruct))\n> +               return NULL;\n> +\n> +       return 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> +       int err;\n> +\n> +       err = proto_register(&smbdirect_prot, 1);\n> +       if (err)\n> +               return err;\n> +\n> +       inet_register_protosw(&smbdirect_inet_stream_protosw);\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +       inet6_register_protosw(&smbdirect_inet6_stream_protosw);\n> +#endif\n> +\n> +       return 0;\n> +}\n> +\n> +static __exit void smbdirect_protosw_exit(void)\n> +{\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +       inet6_unregister_protosw(&smbdirect_inet6_stream_protosw);\n> +#endif\n> +       inet_unregister_protosw(&smbdirect_inet_stream_protosw);\n> +\n> +       proto_unregister(&smbdirect_prot);\n> +}\n> +\n> +__init int smbdirect_proto_init(void)\n> +{\n> +       int err;\n> +\n> +       err = percpu_counter_init(&smbdirect_sockets_allocated, 0, GFP_KERNEL);\n> +       if (err)\n> +               goto err_percpu_counter;\n> +\n> +       err = smbdirect_protosw_init();\n> +       if (err)\n> +               goto err_protosw;\n> +\n> +       return 0;\n> +\n> +err_protosw:\n> +       percpu_counter_destroy(&smbdirect_sockets_allocated);\n> +err_percpu_counter:\n> +       return err;\n> +}\n> +\n> +__exit void smbdirect_proto_exit(void)\n> +{\n> +       smbdirect_protosw_exit();\n> +       percpu_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);\n> diff --git a/fs/smb/common/smbdirect/smbdirect_public.h b/fs/smb/common/smbdirect/smbdirect_public.h\n> index 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                  0x100\n>  #define SMBDIRECT_LOG_RDMA_RW                  0x200\n>  #define SMBDIRECT_LOG_NEGOTIATE                        0x400\n> +#define SMBDIRECT_LOG_SK                       0x800\n>  void smbdirect_socket_set_logging(struct smbdirect_socket *sc,\n>                                   void *private_ptr,\n>                                   bool (*needed)(struct smbdirect_socket *sc,\n> @@ -145,4 +146,6 @@ void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc,\n>                                                  unsigned int rdma_readwrite_threshold,\n>                                                  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__ */\n> diff --git a/fs/smb/common/smbdirect/smbdirect_rw.c b/fs/smb/common/smbdirect/smbdirect_rw.c\n> index 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>         smbdirect_connection_rdma_rw_done(cq, wc, DMA_TO_DEVICE);\n>  }\n>\n> -int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n> -                                  void *buf, size_t buf_len,\n> -                                  struct smbdirect_buffer_descriptor_v1 *desc,\n> -                                  size_t desc_len,\n> -                                  bool is_read)\n> +static int smbdirect_connection_rdma_xmit_locked(struct smbdirect_socket *sc,\n> +                                                void *buf, size_t buf_len,\n> +                                                struct smbdirect_buffer_descriptor_v1 *desc,\n> +                                                size_t desc_len,\n> +                                                bool is_read)\n>  {\n>         const struct smbdirect_socket_parameters *sp = &sc->parameters;\n>         enum 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>         int credits_needed;\n>         size_t desc_buf_len, desc_num = 0;\n>\n> +       smbdirect_socket_sk_owned_by_me(sc);\n> +\n>         if (sc->status != SMBDIRECT_SOCKET_CONNECTED)\n>                 return -ENOTCONN;\n>\n> @@ -235,7 +237,9 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n>         }\n>\n>         msg = list_last_entry(&msg_list, struct smbdirect_rw_io, list);\n> +       smbdirect_socket_sk_unlock(sc);\n>         wait_for_completion(&completion);\n> +       smbdirect_socket_sk_lock(sc);\n>         ret = msg->error;\n>  out:\n>         list_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>         kfree(msg);\n>         goto out;\n>  }\n> +\n> +int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,\n> +                                  void *buf, size_t buf_len,\n> +                                  struct smbdirect_buffer_descriptor_v1 *desc,\n> +                                  size_t desc_len,\n> +                                  bool is_read)\n> +{\n> +       int ret;\n> +\n> +       smbdirect_socket_sk_lock(sc);\n> +       ret = smbdirect_connection_rdma_xmit_locked(sc, buf, buf_len, desc, desc_len, is_read);\n> +       smbdirect_socket_sk_unlock(sc);\n> +\n> +       return ret;\n> +}\n>  __SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit);\n> diff --git a/fs/smb/common/smbdirect/smbdirect_socket.c b/fs/smb/common/smbdirect/smbdirect_socket.c\n> index 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>         sc->send_io.mem.gfp_mask = gfp_mask;\n>         sc->recv_io.mem.gfp_mask = gfp_mask;\n>         sc->rw_io.mem.gfp_mask = gfp_mask;\n> +       sc->sk.sk_allocation = gfp_mask;\n>\n>         return 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> +       struct sock *sk = &sc->sk;\n> +       const struct sockaddr_storage *saddr;\n> +       const struct sockaddr_in *sin;\n> +       const struct sockaddr_in6 *sin6;\n> +       struct in_addr sin_addr = { .s_addr = htonl(INADDR_ANY), };\n> +       struct in6_addr sin6_addr = in6addr_any;\n> +       __be32 sin6_flowinfo = 0;\n> +       bool is_any_addr = true;\n> +       u16 sport = 0;\n> +       int ret;\n> +\n> +       saddr = &sc->rdma.cm_id->route.addr.src_addr;\n> +\n> +       if (WARN_ON_ONCE(saddr->ss_family != sk->sk_family)) {\n> +               ret = -EINVAL;\n> +               return ret;\n> +       }\n> +\n> +       switch (saddr->ss_family) {\n> +       case AF_INET:\n> +               sin = (struct sockaddr_in *)saddr;\n> +               sport = ntohs(sin->sin_port);\n> +               sin_addr = sin->sin_addr;\n> +               is_any_addr = (sin_addr.s_addr == htonl(INADDR_ANY));\n> +               break;\n> +\n> +       case AF_INET6:\n> +               sin6 = (struct sockaddr_in6 *)saddr;\n> +               sport = ntohs(sin6->sin6_port);\n> +               sin_addr.s_addr = LOOPBACK4_IPV6;\n> +               sin6_addr = sin6->sin6_addr;\n> +               is_any_addr = ipv6_addr_any(&sin6_addr);\n> +               sin6_flowinfo = sin6->sin6_flowinfo;\n> +               break;\n> +       }\n> +\n> +       sk->sk_bound_dev_if = sc->rdma.cm_id->route.addr.dev_addr.bound_dev_if;\n> +       sk->sk_rcv_saddr = sc->inet.inet_saddr = sin_addr.s_addr;\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +       sk->sk_v6_rcv_saddr = sc->inet6.saddr = sin6_addr;\n> +#else\n> +       sc->inet6.saddr = sin6_addr;\n> +#endif\n> +       sc->inet6.flow_label = sin6_flowinfo;\n> +       sk->sk_num = sport;\n> +       sc->inet.inet_sport = htons(sport);\n> +\n> +       if (_is_any_addr)\n> +               *_is_any_addr = is_any_addr;\n> +       return 0;\n> +}\n> +\n> +int smbdirect_socket_sync_daddr_to_sk(struct smbdirect_socket *sc)\n> +{\n> +       struct sock *sk = &sc->sk;\n> +       const struct sockaddr_storage *daddr;\n> +       const struct sockaddr_in *sin;\n> +       const struct sockaddr_in6 *sin6;\n> +       struct in_addr sin_addr = { .s_addr = htonl(INADDR_ANY), };\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +       struct in6_addr sin6_addr = in6addr_any;\n> +#endif\n> +       u16 dport = 0;\n> +       int ret;\n> +\n> +       daddr = &sc->rdma.cm_id->route.addr.dst_addr;\n> +\n> +       if (WARN_ON_ONCE(daddr->ss_family != sk->sk_family)) {\n> +               ret = -EINVAL;\n> +               return ret;\n> +       }\n> +\n> +       switch (daddr->ss_family) {\n> +       case AF_INET:\n> +               sin = (struct sockaddr_in *)daddr;\n> +               dport = ntohs(sin->sin_port);\n> +               sin_addr = sin->sin_addr;\n> +               break;\n> +\n> +       case AF_INET6:\n> +               sin6 = (struct sockaddr_in6 *)daddr;\n> +               dport = ntohs(sin6->sin6_port);\n> +               sin_addr.s_addr = LOOPBACK4_IPV6;\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +               sin6_addr = sin6->sin6_addr;\n> +#endif\n> +               break;\n> +       }\n> +\n> +       sk->sk_daddr = sc->inet.inet_daddr = sin_addr.s_addr;\n> +#if IS_ENABLED(CONFIG_IPV6)\n> +       sk->sk_v6_daddr = sin6_addr;\n> +#endif\n> +       sk->sk_dport = sc->inet.inet_dport = htons(dport);\n> +\n> +       return 0;\n> +}\n> +\n>  static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc)\n>  {\n>         /*\n> @@ -257,6 +359,38 @@ static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc)\n>         wake_up_all(&sc->recv_io.reassembly.wait_queue);\n>         wake_up_all(&sc->rw_io.credits.wait_queue);\n>         wake_up_all(&sc->mr_io.ready.wait_queue);\n> +\n> +       if (sc->sk.sk_family) {\n> +               struct sock *sk = &sc->sk;\n> +\n> +               WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK);\n> +\n> +               WARN_ON_ONCE(sc->first_error == 0);\n> +               if (sc->first_error < 0)\n> +                       WRITE_ONCE(sk->sk_err, -sc->first_error);\n> +               else\n> +                       WRITE_ONCE(sk->sk_err, sc->first_error);\n> +\n> +               if (sc->status >= SMBDIRECT_SOCKET_DISCONNECTED) {\n> +                       inet_sk_set_state(sk, TCP_CLOSE);\n> +                       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                               sk->sk_socket->state = SS_UNCONNECTED;\n> +               } else {\n> +                       inet_sk_set_state(sk, TCP_CLOSING);\n> +                       if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                               sk->sk_socket->state = SS_DISCONNECTING;\n> +               }\n> +\n> +               /*\n> +                * Note tcp_done_with_error() also calls both\n> +                * sk->sk_state_change(sk) via tcp_done()\n> +                * and sk_error_report() directly.\n> +                */\n> +               if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                       sk->sk_state_change(sk);\n> +               if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)\n> +                       sk_error_report(sk);\n> +       }\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>          */\n>         smbdirect_socket_wake_up_all(sc);\n>\n> +       smbdirect_socket_sk_unlock(sc);\n>         disable_work_sync(&sc->disconnect_work);\n>         disable_work_sync(&sc->connect.work);\n>         disable_work_sync(&sc->recv_io.posted.refill_work);\n>         disable_work_sync(&sc->idle.immediate_work);\n>         disable_delayed_work_sync(&sc->idle.timer_work);\n> +       smbdirect_socket_sk_lock(sc);\n>\n>         if (sc->rdma.cm_id)\n>                 rdma_lock_handler(sc->rdma.cm_id);\n> @@ -600,6 +736,8 @@ void smbdirect_socket_destroy_sync(struct smbdirect_socket *sc)\n>          */\n>         WARN_ON_ONCE(in_interrupt());\n>\n> +       smbdirect_socket_sk_owned_by_me(sc);\n> +\n>         /*\n>          * First we try to disable the work\n>          * without disable_work_sync() in a\n> @@ -625,7 +763,9 @@ void smbdirect_socket_destroy_sync(struct smbdirect_socket *sc)\n>\n>         smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n>                 \"cancelling and disable disconnect_work\\n\");\n> +       smbdirect_socket_sk_unlock(sc);\n>         disable_work_sync(&sc->disconnect_work);\n> +       smbdirect_socket_sk_lock(sc);\n>\n>         smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n>                 \"destroying rdma session\\n\");\n> @@ -634,7 +774,9 @@ void smbdirect_socket_destroy_sync(struct smbdirect_socket *sc)\n>         if (sc->status < SMBDIRECT_SOCKET_DISCONNECTED) {\n>                 smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n>                         \"wait for transport being disconnected\\n\");\n> +               smbdirect_socket_sk_unlock(sc);\n>                 wait_event(sc->status_wait, sc->status == SMBDIRECT_SOCKET_DISCONNECTED);\n> +               smbdirect_socket_sk_lock(sc);\n>                 smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO,\n>                         \"waited for transport being disconnected\\n\");\n>         }\n> @@ -723,6 +865,8 @@ int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc,\n>  {\n>         int ret;\n>\n> +       smbdirect_socket_sk_owned_by_me(sc);\n> +\n>         if (WARN_ON_ONCE(needed < 0))\n>                 return -EINVAL;\n>\n> @@ -731,9 +875,12 @@ int smbdirect_socket_wait_for_credits(struct smbdirect_socket *sc,\n>                         return 0;\n>\n>                 atomic_add(needed, total_credits);\n> +\n> +               smbdirect_socket_sk_unlock(sc);\n>                 ret = wait_event_interruptible(*waitq,\n>                                                atomic_read(total_credits) >= needed ||\n>                                                sc->status != expected_status);\n> +               smbdirect_socket_sk_lock(sc);\n>\n>                 if (sc->status != expected_status)\n>                         return unexpected_errno;\n> diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h\n> index 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> +       union {\n> +               struct sock sk;\n> +               struct inet_sock inet;\n> +       };\n> +       /* needed by inet6_create() */\n> +       struct ipv6_pinfo inet6;\n> +       void (*orig_sk_destruct)(struct sock *sk);\n> +\n> +       /*\n> +        * This is the first element that is\n> +        * initialized in smbdirect_socket_init()\n> +        */\n>         enum smbdirect_socket_status status;\n>         wait_queue_head_t status_wait;\n>         int first_error;\n> @@ -548,14 +560,18 @@ static void __smbdirect_log_printf(struct smbdirect_socket *sc,\n>                 __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RW, fmt, ##args)\n>  #define smbdirect_log_negotiate(sc, lvl, fmt, args...) \\\n>                 __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_NEGOTIATE, fmt, ##args)\n> +#define smbdirect_log_sk(sc, lvl, fmt, args...) \\\n> +               __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> +       const size_t status_offset = offsetof(struct smbdirect_socket, status);\n> +\n>         /*\n>          * This also sets status = SMBDIRECT_SOCKET_CREATED\n>          */\n>         BUILD_BUG_ON(SMBDIRECT_SOCKET_CREATED != 0);\n> -       memset(sc, 0, sizeof(*sc));\n> +       memset(((u8 *)sc)+status_offset, 0, sizeof(*sc)-status_offset);\n>\n>         init_waitqueue_head(&sc->status_wait);\n>\n> @@ -700,6 +716,14 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc)\n>         __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, \\\n>                 __SMBDIRECT_SOCKET_DISCONNECT(__sc);)\n>\n> +static __always_inline struct smbdirect_socket *\n> +smbdirect_socket_from_sk(const struct sock *sk)\n> +{\n> +       WARN_ON_ONCE(!sk);\n> +       BUILD_BUG_ON(offsetof(struct smbdirect_socket, sk) != 0);\n> +       return container_of(sk, struct smbdirect_socket, sk);\n> +}\n> +\n>  struct smbdirect_send_io {\n>         struct smbdirect_socket *socket;\n>         struct ib_cqe cqe;\n\n\n\n\n> --\n> 2.43.0\n>","headers":{"Return-Path":"\n <linux-cifs+bounces-10704-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 (2048-bit key;\n unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256\n header.s=20251104 header.b=LSjOz/zn;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c15:e001:75::12fc:5321; helo=sin.lore.kernel.org;\n envelope-from=linux-cifs+bounces-10704-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=google.com header.i=@google.com\n header.b=\"LSjOz/zn\"","smtp.subspace.kernel.org;\n arc=pass smtp.client-ip=74.125.82.49","smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=google.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=google.com"],"Received":["from sin.lore.kernel.org (sin.lore.kernel.org\n [IPv6:2600:3c15:e001:75::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fr4bS0qLGz1xv0\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 08 Apr 2026 11:04:36 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sin.lore.kernel.org (Postfix) with ESMTP id EE6723002510\n\tfor <incoming@patchwork.ozlabs.org>; Wed,  8 Apr 2026 01:04:31 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id BD48ECA45;\n\tWed,  8 Apr 2026 01:04:30 +0000 (UTC)","from mail-dl1-f49.google.com (mail-dl1-f49.google.com\n [74.125.82.49])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 81E0B2E7F38\n\tfor <linux-cifs@vger.kernel.org>; Wed,  8 Apr 2026 01:04:26 +0000 (UTC)","by mail-dl1-f49.google.com with SMTP id\n a92af1059eb24-12c20010f10so984656c88.0\n        for <linux-cifs@vger.kernel.org>;\n Tue, 07 Apr 2026 18:04:26 -0700 (PDT)"],"ARC-Seal":["i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775610270; cv=pass;\n b=k5dzrEeGpA/5GWoXXF8jl73kZW+t69m/MebrXKo7aKhCdCEFaB+dqieNW+1Y3AvxaV0KboSRDIl6+tESwBInCPKjhoh7ZcIoclk3Wnaet4WgGbsKWXKyRhncylzgm7a8K8Cwj78+h5ozvhcM6Q9hKF/EBZBW/p7pijzQoVXrhyY=","i=1; a=rsa-sha256; t=1775610266; cv=none;\n        d=google.com; s=arc-20240605;\n        b=ZolQDVKpQ3dXWAeWoGJcprTyblNezDDVIf/YdkeTR69C3kPW+JWHhzLypeDBleqXTV\n         DZy8hFk/MMVcWzivdSArjF6xTGQYX6BHQ1FCQ0x7JIkQXX1w9kue4kWzRXU36fUPe8YR\n         Q2Lysoc/x0mCD8q0p/ohA5ndSZiwjrNRnPQVDVoMljU1ymVOBCrkx93zlE9aVKO/gH+D\n         xtX9RYlPPuDm2ZACndd0gWBQF3VDHkI6ipOc+A5rw7Hu+gQk/ds2LMcBDXdzils/S0bG\n         VPJ0wWvIxnSoZ+hXrVYV5V35Z66O6rWgw3aV1GwhLcwp0xjVZmueY5gtVu3PgHZPzBNv\n         SusA=="],"ARC-Message-Signature":["i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775610270; c=relaxed/simple;\n\tbh=DsrkJoJKbLE4/nWKg9/Haqmmvt/pZbjYu/aEDx/h21w=;\n\th=MIME-Version:References:In-Reply-To:From:Date:Message-ID:Subject:\n\t To:Cc:Content-Type;\n b=Vbv4ni11V4a7+mdMZQ6744xrT+KJJpBafbCPe7/mpsYY/zpGudweCtOoLR4K78ReX0aga3TCKFBTJX5OHV3y1fxSxzg/yogd4wSKr65x/hzeSTC0JTseA0Nogo9J+6ud8tpiZ7WUZWhSXBnTMc8m1as2iHsbUNURbwtAYAXuG+c=","i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n        h=content-transfer-encoding:cc:to:subject:message-id:date:from\n         :in-reply-to:references:mime-version:dkim-signature;\n        bh=UNw0UpRczsMn4045koaAG+00kXBmM2zavhv7u2yZELQ=;\n        fh=Ie0TBOE4Q6hnIDbpZvaE6tbFUHjNxelP1tVo7z6wHKY=;\n        b=J0d+ONcl2m2c/xWRxslNJCSlVlPaydG9qR/ieeAfz1QN4YMdDjDifdjq7ZYgJVHK9F\n         ejUMcszV9FFdTz1dqp1BTgD6zpTtB5u2twYt1LsKN2ev52SNXhPASFcHN6vHT5z3R4dX\n         MEYBtVgZ3nSHIcV2dVgfZ2KpYelScH4+0cIWJ667ii7exMl5jNyXswyffHBPJSX6yaC3\n         53YuhLXbvh9RvNCs/zfcTqQmmKqnfHBCG//tukwyFHuphamLUoFp+pEfiKybmTmU1ebe\n         91ydSTSLni0FxTaMsMaMsRK4IGEq/iwVgmoq6ECUcjTA/xKpehjeCklxlh+5vOQAS/eG\n         Vvzg==;\n        darn=vger.kernel.org"],"ARC-Authentication-Results":["i=2; smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=google.com;\n spf=pass smtp.mailfrom=google.com;\n dkim=pass (2048-bit key) header.d=google.com header.i=@google.com\n header.b=LSjOz/zn; arc=pass smtp.client-ip=74.125.82.49","i=1; mx.google.com; arc=none"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=google.com; s=20251104; t=1775610266; x=1776215066;\n darn=vger.kernel.org;\n        h=content-transfer-encoding:cc:to:subject:message-id:date:from\n         :in-reply-to:references:mime-version:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=UNw0UpRczsMn4045koaAG+00kXBmM2zavhv7u2yZELQ=;\n        b=LSjOz/znIhcP/++NVhm3VnrtMPDuWsQWwEHvl/ONNaSptxhZ7dJK/1Oyck9QdkVaWm\n         UKdyDcyajXdvFvK3D/wa714ESrIL23xH/XztxCxBqXbjFX4CduiK9/0p8T51TiCNEdrU\n         AaoTRO42qMxzAiJV9cbI71QYjyz4DaC4Ktstf2kJ85BblPVgdRKOO6npH0nSDeBh1b2L\n         V9o+vQFGRc9u2vpfYMwl/oW/XxRLl3GI/NJhcuiSZMxGzeEABgLjOItlWOz03ej70xJr\n         KqluLXW7kxkPHS3jzpARF6rer9zgCTgs1EY/9g2qK5jXjXBHJizutNd1hqh0xV7u4LJh\n         A+yA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1775610266; x=1776215066;\n        h=content-transfer-encoding:cc:to:subject:message-id:date:from\n         :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from\n         :to:cc:subject:date:message-id:reply-to;\n        bh=UNw0UpRczsMn4045koaAG+00kXBmM2zavhv7u2yZELQ=;\n        b=jAYtdeLBMyN+t2PX6SvPSh6zgWLZcTkzn/Dt6P0npEwVtCJiTzjQYPwAcUwQpeUIKc\n         P9oAxXDlIDvXd1QlwEZmYzC7Y/RRB4dAPQPGiqBKNw6xYNuKgs1tbTBqdB1qcRpCxkOl\n         6w0gEXx+TfhzaB3rdwP25hhr7OP/aZVOyvSLBydsOx8KBOFoLS9Gc60A8ZhcQfI7CFXk\n         uUBoTcTteoPg+04V4JMpIiK/KEQdK4XYFyifgAzxOtdpxlJdgdqFgCPGExhOdLoSFma0\n         zM/s/94J/cOH4xwtgM+A9NaudhoFFbaNlT4cjnmGPwdpJlXkI5v72K49iIsxWFiHT1kV\n         QUww==","X-Gm-Message-State":"AOJu0YyeOlUc5aMxEW4KjbtZQzrfD8kpSeNUIoMKPjVZSv0zX3meJO8w\n\txNodqmLs/LzgtvbuZEyeeGG15JVo/o4y0OhZVTOP847jEbAw9kXNv945r0sQSr+OIqgHpcyLJRK\n\tTUUaZlNWmmXZ13KnQm8wM8CnVfv28HaOZTKcZvyvU","X-Gm-Gg":"AeBDieulp3HTzZJFNDQL6+MZAYhGqexTG+p1rA/hAx+hEB8kvdZewB5h8nxa/SFQiqT\n\tyGYiHk3yzyrhX6/lB65A1uAs0Gv7YecpFcaX26aJOvnDSt3iXi3I9oaL+SrxvhH5ZIOZ1BMvNB8\n\thTAshhz7ILlLj/jjcsSE4GLIoRn9qI2QU9szRHIaxym6hC+pmh4yKHmBpXhDIS7ohYj+1Gudtjd\n\timKfBCmlmDtEHdheyvjHk0+nwZSpiDFLZL3oFN3xO3372eqYQP1tiWt054F8DsBzDaiV57rJFgK\n\tvcpgwaVzObye8t1xNBAyoTPqfLVymYvFKA/oy8M2pZerIYlVgDXoemFGrI36SlwCxFyvi8a5zA=\n\t=","X-Received":"by 2002:a05:7022:1005:b0:11b:f056:a1b3 with SMTP id\n a92af1059eb24-12bfb6fb217mr10959444c88.11.1775610264696; Tue, 07 Apr 2026\n 18:04:24 -0700 (PDT)","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","References":"<cover.1775571957.git.metze@samba.org>\n <4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org>","In-Reply-To":"\n <4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org>","From":"Kuniyuki Iwashima <kuniyu@google.com>","Date":"Tue, 7 Apr 2026 18:04:13 -0700","X-Gm-Features":"AQROBzC6P2j3JD1_n7EXMMMgxpWjLinmA480IOKf-UAjWGecDYLti7r6_Lg7ekk","Message-ID":"\n <CAAVpQUCxCT-YGCu-JfwZNRvhgVOPfh+c9NBO+xMvSsphwJV_Zw@mail.gmail.com>","Subject":"Re: [PATCH 6/8] smb: smbdirect: add in kernel only support for\n IPPROTO_SMBDIRECT","To":"Stefan Metzmacher <metze@samba.org>","Cc":"linux-cifs@vger.kernel.org, samba-technical@lists.samba.org,\n\tSteve French <smfrench@gmail.com>, Tom Talpey <tom@talpey.com>,\n Long Li <longli@microsoft.com>,\n\tNamjae Jeon <linkinjeon@kernel.org>, David Howells <dhowells@redhat.com>,\n\tHenrique Carvalho <henrique.carvalho@suse.com>,\n \"David S . Miller\" <davem@davemloft.net>,\n\tEric Dumazet <edumazet@google.com>, Jakub Kicinski <kuba@kernel.org>,\n Paolo Abeni <pabeni@redhat.com>,\n\tSimon Horman <horms@kernel.org>, Willem de Bruijn <willemb@google.com>,\n netdev@vger.kernel.org,\n\tXin Long <lucien.xin@gmail.com>, quic@lists.linux.dev,\n linux-rdma@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org","Content-Type":"text/plain; charset=\"UTF-8\"","Content-Transfer-Encoding":"quoted-printable"}}]