Re: [PATCH 6/8] smb: smbdirect: add in kernel only support for IPPROTO_SMBDIRECT
Kuniyuki Iwashima <[email protected]> Tue, 7 Apr 2026 18:04:13 -0700
| Newsgroups | dev.linux.lists.quic,org.kernel.vger.linux-cifs,org.kernel.vger.linux-kernel,org.kernel.vger.linux-rdma,org.kernel.vger.netdev |
|---|---|
| Message-ID | <CAAVpQUCxCT-YGCu-JfwZNRvhgVOPfh+c9NBO+xMvSsphwJV_Zw@mail.gmail.com> |
On Tue, Apr 7, 2026 at 7:47=E2=80=AFAM Stefan Metzmacher <[email protected]> = wrote: > > For userspace callers of socket() still get -EPROTONOSUPPORT, > so we are sure we'll only have in kernel callers, cifs.ko and > ksmbd.ko, for now. This makes it possible to relax the > constrains generic stream socket consumers would otherwise > assume. > > There's a prototype for userspace sockets on top of > this and there's working userspace code for Samba as > client and server, so this is just the first step, > but a very important one. > > The SMBDIRECT protocol is defined in [MS-SMBD] by Microsoft. > It is used as wrapper around RDMA in order to provide a transport for SMB= 3, > but Microsoft also uses it as transport for other protocols. > > SMBDIRECT works over Infiniband, RoCE and iWarp. > RoCEv2 is based on IP/UDP and iWarp is based on IP/TCP, > so these use IP addresses natively. > Infiniband and RoCEv1 require IPOIB in order to be used for > SMBDIRECT. > > So instead of adding a PF_SMBDIRECT, which would only use AF_INET[6], > we use IPPROTO_SMBDIRECT instead, this uses a number not > allocated from IANA, as it would not appear in an IP header. Overall I don't see the upside of reusing AF_INET6? infra. It just adds an unnecessary sk->sk_prot layer, which can be simply implemented as sock->ops. It seems only inet_getname() is the valid user of inet_sock. > > This is similar to IPPROTO_SMC, IPPROTO_MPTCP and IPPROTO_QUIC, > which are linux specific values for the socket() syscall. SMBDIRECT seems more like SMC, rather than MPTCP and QUIC. SMC was implemented with AF_SMC first, and IPPROT_SMC was added just to hook at userspace and silently convert TCP sockets to AF_SMC sockets easily. But the is not the case of SMBDIRECT because the socket is only created from kernel space. > > socket(AF_INET, SOCK_STREAM, IPPROTO_SMBDIRECT); > socket(AF_INET6, SOCK_STREAM, IPPROTO_SMBDIRECT); > > This will allow the existing smbdirect code used by > cifs.ko and ksmbd.ko to be moved behind the socket layer, Reusing AF_INET6? is not related to this statement as long as the upper layer uses in-kernel socket API (sock_XXX()/kernel_XXX()) instead of calling sk->sk_prot->XXX() directly. > so that there's less special handling. Only sock_sendmsg() > sock_recvmsg() are used, so that the main stream handling > is done all the same for tcp, smbdirect and later also quic. > > The special RDMA read/write handling will be via direct > function calls as they are currently done for the in kernel > consumers. > > For now the core smbdirect code still supports both > modes, direct calls in indirect via the socket layer. > The core code uses if (sc->sk.sk_family) as indication > for the new socket mode. Once cifs.ko and ksmbd.ko > are converted we can remove the old mode slowly, > but I'll deferr that to a future patchset. > > There's still a way to go in order to make this > as generic as tcp and quic e.g. adding MSG_SPLICE_PAGES support or > splice_read/read_sock/read_skb. > > But it's a good start, which will make changes > much easier. > > Cc: Steve French <[email protected]> > Cc: Tom Talpey <[email protected]> > Cc: Long Li <[email protected]> > Cc: Namjae Jeon <[email protected]> > Cc: David Howells <[email protected]> > Cc: Henrique Carvalho <[email protected]> > Cc: [email protected] > Cc: [email protected] > Cc: David S. Miller <[email protected]> > Cc: Eric Dumazet <[email protected]> > Cc: Jakub Kicinski <[email protected]> > Cc: Paolo Abeni <[email protected]> > Cc: Simon Horman <[email protected]> > Cc: Kuniyuki Iwashima <[email protected]> > Cc: Willem de Bruijn <[email protected]> > Cc: [email protected] > Cc: Xin Long <[email protected]> > Cc: [email protected] > Cc: [email protected] > Cc: [email protected] > Signed-off-by: Stefan Metzmacher <[email protected]> > --- > fs/smb/common/smbdirect/Makefile | 1 + > fs/smb/common/smbdirect/smbdirect.h | 62 + > fs/smb/common/smbdirect/smbdirect_accept.c | 14 +- > .../common/smbdirect/smbdirect_connection.c | 58 + > fs/smb/common/smbdirect/smbdirect_devices.c | 2 +- > fs/smb/common/smbdirect/smbdirect_internal.h | 59 +- > fs/smb/common/smbdirect/smbdirect_listen.c | 49 +- > fs/smb/common/smbdirect/smbdirect_main.c | 45 + > fs/smb/common/smbdirect/smbdirect_mr.c | 10 + > fs/smb/common/smbdirect/smbdirect_proto.c | 1549 +++++++++++++++++ > fs/smb/common/smbdirect/smbdirect_public.h | 3 + > fs/smb/common/smbdirect/smbdirect_rw.c | 29 +- > fs/smb/common/smbdirect/smbdirect_socket.c | 147 ++ > fs/smb/common/smbdirect/smbdirect_socket.h | 26 +- > 14 files changed, 2039 insertions(+), 15 deletions(-) > create mode 100644 fs/smb/common/smbdirect/smbdirect_proto.c > > diff --git a/fs/smb/common/smbdirect/Makefile b/fs/smb/common/smbdirect/M= akefile > index 423f533e1002..fcff485d7c45 100644 > --- a/fs/smb/common/smbdirect/Makefile > +++ b/fs/smb/common/smbdirect/Makefile > @@ -10,6 +10,7 @@ smbdirect-y :=3D \ > smbdirect_connection.o \ > smbdirect_mr.o \ > smbdirect_rw.o \ > + smbdirect_proto.o \ > smbdirect_debug.o \ > smbdirect_connect.o \ > smbdirect_listen.o \ > diff --git a/fs/smb/common/smbdirect/smbdirect.h b/fs/smb/common/smbdirec= t/smbdirect.h > index bbab5f7f7cc9..cf3d4957f94c 100644 > --- a/fs/smb/common/smbdirect/smbdirect.h > +++ b/fs/smb/common/smbdirect/smbdirect.h > @@ -6,7 +6,10 @@ > #ifndef __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ > #define __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ > > +#include <linux/stddef.h> > #include <linux/types.h> > +#include <linux/socket.h> > +#include <asm/ioctls.h> > > /* SMB-DIRECT buffer descriptor V1 structure [MS-SMBD] 2.2.3.1 */ > struct smbdirect_buffer_descriptor_v1 { > @@ -49,4 +52,63 @@ struct smbdirect_socket_parameters { > SMBDIRECT_FLAG_PORT_RANGE_ONLY_IB | \ > SMBDIRECT_FLAG_PORT_RANGE_ONLY_IW) > > +enum { > + __SMBDIRECT_BUFFER_REMOTE_INVALIDATE =3D 0x20, > +}; > + > +struct smbdirect_cmsg_buffer { > + uint8_t msg_control[CMSG_SPACE(24)]; > +}; > + > +static __always_inline > +void __smbdirect_cmsg_prepare(struct msghdr *msg, > + struct smbdirect_cmsg_buffer *cbuffer, > + int cmsg_type, > + const void *payload, > + size_t payloadlen) > +{ > + size_t cmsg_space =3D CMSG_SPACE(payloadlen); > + size_t cmsg_len =3D CMSG_LEN(payloadlen); > + struct cmsghdr *cmsg =3D NULL; > + void *dataptr =3D NULL; > + > + BUILD_BUG_ON(cmsg_space > sizeof(cbuffer->msg_control)); > + > + memset(cbuffer, 0, sizeof(*cbuffer)); > + > + msg->msg_control =3D cbuffer->msg_control; > + msg->msg_controllen =3D cmsg_space; > + > + cmsg =3D CMSG_FIRSTHDR(msg); > + cmsg->cmsg_level =3D SOL_SMBDIRECT; > + cmsg->cmsg_type =3D cmsg_type; > + cmsg->cmsg_len =3D cmsg_len; > + dataptr =3D CMSG_DATA(cmsg); > + memcpy(dataptr, payload, payloadlen); > + msg->msg_controllen =3D cmsg->cmsg_len; > +} > + > +struct smbdirect_buffer_remote_invalidate_args { > + __u32 remote_token; > +} __packed; > +#define SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE \ > + _IOW('S', __SMBDIRECT_BUFFER_REMOTE_INVALIDATE, \ > + struct smbdirect_buffer_remote_invalidate_args) > + > +static __always_inline > +void smbdirect_buffer_remote_invalidate_cmsg_prepare(struct msghdr *msg, > + struct smbdirect_cms= g_buffer *cbuffer, > + const __u32 *remote_= token) > +{ > + if (remote_token) { > + struct smbdirect_buffer_remote_invalidate_args args =3D { > + .remote_token =3D *remote_token, > + }; > + > + __smbdirect_cmsg_prepare(msg, cbuffer, > + SMBDIRECT_BUFFER_REMOTE_INVALIDA= TE_CMSG_TYPE, > + &args, sizeof(args)); > + } > +} > + > #endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_H__ */ > diff --git a/fs/smb/common/smbdirect/smbdirect_accept.c b/fs/smb/common/s= mbdirect/smbdirect_accept.c > index d6d5e6a3f5de..6d7d869cdbc3 100644 > --- a/fs/smb/common/smbdirect/smbdirect_accept.c > +++ b/fs/smb/common/smbdirect/smbdirect_accept.c > @@ -6,7 +6,6 @@ > */ > > #include "smbdirect_internal.h" > -#include <net/sock.h> > #include "../../common/smb2status.h" > > static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id, > @@ -460,6 +459,12 @@ static void smbdirect_accept_negotiate_recv_work(str= uct work_struct *work) > spin_lock_irqsave(&lsc->listen.lock, flags); > list_del(&sc->accept.list); > list_add_tail(&sc->accept.list, &lsc->listen.ready); > + if (lsc->sk.sk_family) { > + struct sock *lsk =3D &lsc->sk; > + > + if (!sock_flag(lsk, SOCK_DEAD) && lsk->sk_socket) > + lsk->sk_data_ready(lsk); > + } > wake_up(&lsc->listen.wait_queue); > spin_unlock_irqrestore(&lsc->listen.lock, flags); > > @@ -774,11 +779,13 @@ static long smbdirect_socket_wait_for_accept(struct= smbdirect_socket *lsc, long > { > long ret; > > + smbdirect_socket_sk_unlock(lsc); > ret =3D wait_event_interruptible_timeout(lsc->listen.wait_queue, > !list_empty_careful(&lsc->= listen.ready) || > lsc->status !=3D SMBDIRECT= _SOCKET_LISTENING || > lsc->first_error, > timeo); > + smbdirect_socket_sk_lock(lsc); > if (lsc->status !=3D SMBDIRECT_SOCKET_LISTENING) > return -EINVAL; > if (lsc->first_error) > @@ -850,6 +857,11 @@ struct smbdirect_socket *smbdirect_socket_accept(str= uct smbdirect_socket *lsc, > * order to grant credits to the peer. > */ > nsc->status =3D SMBDIRECT_SOCKET_CONNECTED; > + if (nsc->sk.sk_family) { > + struct sock *nsk =3D &nsc->sk; > + > + inet_sk_set_state(nsk, TCP_ESTABLISHED); > + } > smbdirect_accept_negotiate_finish(nsc, 0); > > return nsc; > diff --git a/fs/smb/common/smbdirect/smbdirect_connection.c b/fs/smb/comm= on/smbdirect/smbdirect_connection.c > index 1e946f78e935..2c426aefd16d 100644 > --- a/fs/smb/common/smbdirect/smbdirect_connection.c > +++ b/fs/smb/common/smbdirect/smbdirect_connection.c > @@ -153,6 +153,15 @@ void smbdirect_connection_rdma_established(struct sm= bdirect_socket *sc) > > sc->rdma.cm_id->event_handler =3D smbdirect_connection_rdma_event= _handler; > sc->rdma.expected_event =3D RDMA_CM_EVENT_DISCONNECTED; > + > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + smbdirect_socket_sync_saddr_to_sk(sc, NULL); > + smbdirect_socket_sync_daddr_to_sk(sc); > + > + inet_sk_set_state(sk, TCP_SYN_RECV); > + } > } > > void smbdirect_connection_negotiation_done(struct smbdirect_socket *sc) > @@ -189,6 +198,13 @@ void smbdirect_connection_negotiation_done(struct sm= bdirect_socket *sc) > smbdirect_socket_status_string(sc->status), > SMBDIRECT_DEBUG_ERR_PTR(sc->first_error)); > sc->status =3D SMBDIRECT_SOCKET_CONNECTED; > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + inet_sk_set_state(sk, TCP_ESTABLISHED); > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_socket->state =3D SS_CONNECTED; > + } > > /* > * We need to setup the refill and send immediate work > @@ -203,6 +219,13 @@ void smbdirect_connection_negotiation_done(struct sm= bdirect_socket *sc) > &sc->rdma.cm_id->route.addr.src_addr, > &sc->rdma.cm_id->route.addr.dst_addr); > > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_state_change(sk); > + } > + > wake_up(&sc->status_wait); > } > > @@ -739,10 +762,12 @@ int smbdirect_connection_wait_for_connected(struct = smbdirect_socket *sc) > "waiting for connection: device: %.*s local: %pISpsfc rem= ote: %pISpsfc\n", > IB_DEVICE_NAME_MAX, devname, src, dst); > > + smbdirect_socket_sk_unlock(sc); > ret =3D wait_event_interruptible_timeout(sc->status_wait, > sc->status =3D=3D SMBDIREC= T_SOCKET_CONNECTED || > sc->first_error, > msecs_to_jiffies(sp->negot= iate_timeout_msec)); > + smbdirect_socket_sk_lock(sc); > if (sc->rdma.cm_id) { > /* > * Maybe src and dev are updated in the meantime. > @@ -954,6 +979,12 @@ int smbdirect_connection_send_batch_flush(struct smb= direct_socket *sc, > atomic_add(batch->credit, &sc->send_io.bcredits.count); > batch->credit =3D 0; > wake_up(&sc->send_io.bcredits.wait_queue); > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_write_space(sk); > + } > } > > return ret; > @@ -1091,6 +1122,8 @@ int smbdirect_connection_send_single_iter(struct sm= bdirect_socket *sc, > u32 data_length =3D 0; > int ret; > > + smbdirect_socket_sk_owned_by_me(sc); > + > if (WARN_ON_ONCE(flags)) > return -EINVAL; /* no flags support for now */ > > @@ -1150,10 +1183,12 @@ int smbdirect_connection_send_single_iter(struct = smbdirect_socket *sc, > * wait until either the refill work or the peer > * granted new credits > */ > + smbdirect_socket_sk_unlock(sc); > ret =3D wait_event_interruptible(sc->send_io.credits.wait= _queue, > atomic_read(&sc->send_io.c= redits.count) >=3D 1 || > atomic_read(&sc->recv_io.c= redits.available) >=3D 1 || > sc->status !=3D SMBDIRECT_= SOCKET_CONNECTED); > + smbdirect_socket_sk_lock(sc); > if (sc->status !=3D SMBDIRECT_SOCKET_CONNECTED) > ret =3D -ENOTCONN; > if (ret < 0) > @@ -1268,9 +1303,11 @@ int smbdirect_connection_send_wait_zero_pending(st= ruct smbdirect_socket *sc) > * that means all the I/Os have been out and we are good to retur= n > */ > > + smbdirect_socket_sk_unlock(sc); > wait_event(sc->send_io.pending.zero_wait_queue, > atomic_read(&sc->send_io.pending.count) =3D=3D 0 || > sc->status !=3D SMBDIRECT_SOCKET_CONNECTED); > + smbdirect_socket_sk_lock(sc); > if (sc->status !=3D SMBDIRECT_SOCKET_CONNECTED) { > smbdirect_log_write(sc, SMBDIRECT_LOG_ERR, > "status=3D%s first_error=3D%1pe =3D> %1pe\n", > @@ -1297,6 +1334,8 @@ int smbdirect_connection_send_iter(struct smbdirect= _socket *sc, > int error =3D 0; > __be32 hdr; > > + smbdirect_socket_sk_owned_by_me(sc); > + > if (WARN_ONCE(flags, "unexpected flags=3D0x%x\n", flags)) > return -EINVAL; /* no flags support for now */ > > @@ -1448,7 +1487,9 @@ static void smbdirect_connection_send_immediate_wor= k(struct work_struct *work) > smbdirect_log_keep_alive(sc, SMBDIRECT_LOG_INFO, > "send an empty message\n"); > sc->statistics.send_empty++; > + smbdirect_socket_sk_lock(sc); > ret =3D smbdirect_connection_send_single_iter(sc, NULL, NULL, 0, = 0); > + smbdirect_socket_sk_unlock(sc); > if (ret < 0) { > smbdirect_log_write(sc, SMBDIRECT_LOG_ERR, > "smbdirect_connection_send_single_iter ret=3D%1pe= \n", > @@ -1632,6 +1673,12 @@ void smbdirect_connection_recv_io_done(struct ib_c= q *cq, struct ib_wc *wc) > * If any sender is waiting for credits, unblock it > */ > wake_up(&sc->send_io.credits.wait_queue); > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_write_space(sk); > + } > } > > /* Send an immediate response right away if requested */ > @@ -1652,6 +1699,12 @@ void smbdirect_connection_recv_io_done(struct ib_c= q *cq, struct ib_wc *wc) > > smbdirect_connection_reassembly_append_recv_io(sc, recv_i= o, data_length); > wake_up(&sc->recv_io.reassembly.wait_queue); > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_data_ready(sk); > + } > } else > smbdirect_connection_put_recv_io(recv_io); > > @@ -1735,6 +1788,9 @@ int smbdirect_connection_recv_io_refill(struct smbd= irect_socket *sc) > /* > * If the last send credit is waiting for credits > * it can grant we need to wake it up > + * > + * This needs to wake up smbdirect_connection_send_single_iter() > + * only, so we don't need sk->sk_write_space() here. > */ > if (atomic_read(&sc->send_io.bcredits.count) =3D=3D 0 && > atomic_read(&sc->send_io.credits.count) =3D=3D 0) > @@ -1922,9 +1978,11 @@ int smbdirect_connection_recvmsg(struct smbdirect_= socket *sc, > > smbdirect_log_read(sc, SMBDIRECT_LOG_INFO, > "wait_event on more data\n"); > + smbdirect_socket_sk_unlock(sc); > ret =3D wait_event_interruptible(sc->recv_io.reassembly.wait_queu= e, > sc->recv_io.reassembly.data_length= >=3D size || > sc->status !=3D SMBDIRECT_SOCKET_C= ONNECTED); > + smbdirect_socket_sk_lock(sc); > /* Don't return any data if interrupted */ > if (ret) > return ret; > diff --git a/fs/smb/common/smbdirect/smbdirect_devices.c b/fs/smb/common/= smbdirect/smbdirect_devices.c > index aaab99e9c045..da0edc104e48 100644 > --- a/fs/smb/common/smbdirect/smbdirect_devices.c > +++ b/fs/smb/common/smbdirect/smbdirect_devices.c > @@ -257,7 +257,7 @@ __init int smbdirect_devices_init(void) > return 0; > } > > -__exit void smbdirect_devices_exit(void) > +__cold void smbdirect_devices_exit(void) > { > struct smbdirect_device *sdev, *tmp; > > diff --git a/fs/smb/common/smbdirect/smbdirect_internal.h b/fs/smb/common= /smbdirect/smbdirect_internal.h > index 30a1b8643657..517ff0533032 100644 > --- a/fs/smb/common/smbdirect/smbdirect_internal.h > +++ b/fs/smb/common/smbdirect/smbdirect_internal.h > @@ -12,8 +12,6 @@ > #include "smbdirect_pdu.h" > #include "smbdirect_public.h" > > -#include <linux/mutex.h> > - > struct smbdirect_module_state { > struct mutex mutex; > > @@ -30,6 +28,8 @@ struct smbdirect_module_state { > rwlock_t lock; > struct list_head list; > } devices; > + > + struct smbdirect_socket_parameters default_parameters; > }; > > extern struct smbdirect_module_state smbdirect_globals; > @@ -46,10 +46,58 @@ struct smbdirect_device { > char ib_name[IB_DEVICE_NAME_MAX]; > }; > > +static __always_inline void smbdirect_socket_sk_owned_by_me(struct smbdi= rect_socket *sc) > +{ > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + } > +} > + > +static __always_inline void smbdirect_socket_sk_not_owned_by_me(struct s= mbdirect_socket *sc) > +{ > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + } > +} > + > +static __always_inline void smbdirect_socket_sk_lock(struct smbdirect_so= cket *sc) > +{ > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + lock_sock(sk); > + } > +} > + > +static __always_inline void smbdirect_socket_sk_unlock(struct smbdirect_= socket *sc) > +{ > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + release_sock(sk); > + } > +} > + > int smbdirect_socket_init_new(struct net *net, struct smbdirect_socket *= sc); > > int smbdirect_socket_init_accepting(struct rdma_cm_id *id, struct smbdir= ect_socket *sc); > > +int smbdirect_socket_sync_saddr_to_sk(struct smbdirect_socket *sc, bool = *_is_any_addr); > + > +int smbdirect_socket_sync_daddr_to_sk(struct smbdirect_socket *sc); > + > void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc, > const char *macro_name, > unsigned int lvl, > @@ -135,7 +183,12 @@ int smbdirect_accept_connect_request(struct smbdirec= t_socket *sc, > > void smbdirect_accept_negotiate_finish(struct smbdirect_socket *sc, u32 = ntstatus); > > +void smbdirect_sk_reclassify(struct sock *sk); > + > __init int smbdirect_devices_init(void); > -__exit void smbdirect_devices_exit(void); > +__cold void smbdirect_devices_exit(void); > + > +__init int smbdirect_proto_init(void); > +__exit void smbdirect_proto_exit(void); > > #endif /* __FS_SMB_COMMON_SMBDIRECT_INTERNAL_H__ */ > diff --git a/fs/smb/common/smbdirect/smbdirect_listen.c b/fs/smb/common/s= mbdirect/smbdirect_listen.c > index 05c7902e7020..a6e08d82dc73 100644 > --- a/fs/smb/common/smbdirect/smbdirect_listen.c > +++ b/fs/smb/common/smbdirect/smbdirect_listen.c > @@ -74,6 +74,12 @@ int smbdirect_socket_listen(struct smbdirect_socket *s= c, int backlog) > */ > sc->listen.backlog =3D backlog; > > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + inet_sk_set_state(sk, TCP_LISTEN); > + } > + > if (sc->rdma.cm_id->device) > smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, > "listening on addr: %pISpsfc dev: %.*s\n", > @@ -209,6 +215,7 @@ static int smbdirect_listen_connect_request(struct sm= bdirect_socket *lsc, > const struct rdma_cm_event *e= vent) > { > const struct smbdirect_socket_parameters *lsp =3D &lsc->parameter= s; > + struct sock *nsk =3D NULL; > struct smbdirect_socket *nsc; > unsigned long flags; > size_t backlog =3D max_t(size_t, 1, lsc->listen.backlog); > @@ -265,9 +272,39 @@ static int smbdirect_listen_connect_request(struct s= mbdirect_socket *lsc, > return -EBUSY; > } > > - ret =3D smbdirect_socket_create_accepting(new_id, &nsc); > - if (ret) > - goto socket_init_failed; > + if (lsc->sk.sk_family) { > + struct sock *lsk =3D &lsc->sk; > + > + ret =3D -ENOMEM; > + nsk =3D sk_clone(lsk, lsk->sk_allocation, false); > + if (!nsk) > + goto sk_clone_failed; > + /* sk_clone_lock() increments refcnt to 2; drop the extra= . */ > + __sock_put(nsk); > + /* sk_clone() already called sk_sockets_allocated_inc(sk)= ; */ > + sock_prot_inuse_add(sock_net(nsk), nsk->sk_prot, 1); > + > + smbdirect_sk_reclassify(nsk); > + inet_sk_set_state(nsk, TCP_SYN_RECV); > + nsc =3D smbdirect_socket_from_sk(nsk); > + > + ret =3D smbdirect_socket_init_accepting(new_id, nsc); > + if (ret) > + goto socket_init_failed; > + > + /* > + * Note that smbdirect_sock_accept() will set > + * SOCK_CUSTOM_SOCKOPT once [__]inet_accept() > + * called sk_set_socket() via sock_graft(). > + */ > + WARN_ON_ONCE(nsc->orig_sk_destruct !=3D lsc->orig_sk_dest= ruct); > + WARN_ON_ONCE(nsk->sk_destruct !=3D lsk->sk_destruct); > + WARN_ON_ONCE(nsk->sk_ipv6only !=3D lsk->sk_ipv6only); > + } else { > + ret =3D smbdirect_socket_create_accepting(new_id, &nsc); > + if (ret) > + goto socket_init_failed; > + } > > nsc->logging =3D lsc->logging; > ret =3D smbdirect_socket_set_initial_parameters(nsc, &lsc->parame= ters); > @@ -302,7 +339,11 @@ static int smbdirect_listen_connect_request(struct s= mbdirect_socket *lsc, > */ > nsc->ib.dev =3D NULL; > nsc->rdma.cm_id =3D NULL; > - smbdirect_socket_release(nsc); > + if (!nsk) > + smbdirect_socket_release(nsc); > socket_init_failed: > + if (nsk) > + sk_free(nsk); > +sk_clone_failed: > return ret; > } > diff --git a/fs/smb/common/smbdirect/smbdirect_main.c b/fs/smb/common/smb= direct/smbdirect_main.c > index fe6e8d93c34c..ccbe979332af 100644 > --- a/fs/smb/common/smbdirect/smbdirect_main.c > +++ b/fs/smb/common/smbdirect/smbdirect_main.c > @@ -12,6 +12,7 @@ struct smbdirect_module_state smbdirect_globals =3D { > > static __init int smbdirect_module_init(void) > { > + struct smbdirect_socket_parameters *sp; > int ret =3D -ENOMEM; > > pr_notice("subsystem loading...\n"); > @@ -73,10 +74,52 @@ static __init int smbdirect_module_init(void) > if (ret) > goto devices_init_failed; > > + /* > + * Create the global default parameters > + */ > + sp =3D &smbdirect_globals.default_parameters; > + sp->resolve_addr_timeout_msec =3D 5 * 1000; > + sp->resolve_route_timeout_msec =3D 5 * 1000; > + sp->rdma_connect_timeout_msec =3D 5 * 1000; > + sp->negotiate_timeout_msec =3D 120 * 1000; > + sp->initiator_depth =3D 1; /* the server should change this */ > + sp->responder_resources =3D 1; /* the client should change this *= / > + sp->recv_credit_max =3D 255; > + sp->send_credit_target =3D 255; > + sp->max_send_size =3D 1364; > + /* > + * The maximum fragmented upper-layer payload receive size suppor= ted > + * > + * Assume max_payload_per_credit is > + * smbd_max_receive_size - 24 =3D 1340 > + * > + * The maximum number would be > + * smbd_receive_credit_max * max_payload_per_credit > + * > + * 1340 * 255 =3D 341700 (0x536C4) > + * > + * The minimum value from the spec is 131072 (0x20000) > + * > + * For now we use the logic we used before: > + * (1364 * 255) / 2 =3D 173910 (0x2A756) > + */ > + sp->max_fragmented_recv_size =3D (1364 * 255) / 2; > + sp->max_recv_size =3D 1364; > + sp->max_read_write_size =3D 0; /* the server should change this *= / > + sp->max_frmr_depth =3D 0; /* the client should change this */ > + sp->keepalive_interval_msec =3D 120 * 1000; > + sp->keepalive_timeout_msec =3D 5 * 1000; > + > + ret =3D smbdirect_proto_init(); > + if (ret) > + goto proto_init_failed; > + > mutex_unlock(&smbdirect_globals.mutex); > pr_notice("subsystem loaded\n"); > return 0; > > +proto_init_failed: > + smbdirect_devices_exit(); > devices_init_failed: > destroy_workqueue(smbdirect_globals.workqueues.cleanup); > alloc_cleanup_wq_failed: > @@ -101,6 +144,8 @@ static __exit void smbdirect_module_exit(void) > pr_notice("subsystem unloading...\n"); > mutex_lock(&smbdirect_globals.mutex); > > + smbdirect_proto_exit(); > + > smbdirect_devices_exit(); > > destroy_workqueue(smbdirect_globals.workqueues.accept); > diff --git a/fs/smb/common/smbdirect/smbdirect_mr.c b/fs/smb/common/smbdi= rect/smbdirect_mr.c > index fa9be8089925..86bb72ed10ae 100644 > --- a/fs/smb/common/smbdirect/smbdirect_mr.c > +++ b/fs/smb/common/smbdirect/smbdirect_mr.c > @@ -167,9 +167,11 @@ smbdirect_connection_get_mr_io(struct smbdirect_sock= et *sc) > int ret; > > again: > + smbdirect_socket_sk_unlock(sc); > ret =3D wait_event_interruptible(sc->mr_io.ready.wait_queue, > atomic_read(&sc->mr_io.ready.count= ) || > sc->status !=3D SMBDIRECT_SOCKET_C= ONNECTED); > + smbdirect_socket_sk_lock(sc); > if (ret) { > smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR, > "wait_event_interruptible ret=3D%d (%1pe)\n", > @@ -281,7 +283,9 @@ smbdirect_connection_register_mr_io(struct smbdirect_= socket *sc, > return NULL; > } > > + smbdirect_socket_sk_lock(sc); > mr =3D smbdirect_connection_get_mr_io(sc); > + smbdirect_socket_sk_unlock(sc); > if (!mr) { > smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR, > "smbdirect_connection_get_mr_io returning NULL\n"= ); > @@ -415,6 +419,12 @@ void smbdirect_connection_deregister_mr_io(struct sm= bdirect_mr_io *mr) > if (mr->state =3D=3D SMBDIRECT_MR_DISABLED) > goto put_kref; > > + /* > + * We are protected by mr->mutex > + * without lock_sock(). > + */ > + smbdirect_socket_sk_not_owned_by_me(sc); > + > if (sc->status !=3D SMBDIRECT_SOCKET_CONNECTED) { > smbdirect_mr_io_disable_locked(mr); > goto put_kref; > diff --git a/fs/smb/common/smbdirect/smbdirect_proto.c b/fs/smb/common/sm= bdirect/smbdirect_proto.c > new file mode 100644 > index 000000000000..1a832d52eb89 > --- /dev/null > +++ b/fs/smb/common/smbdirect/smbdirect_proto.c > @@ -0,0 +1,1549 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (c) 2025 Stefan Metzmacher > + */ > + > +#include "smbdirect_internal.h" > +#include <net/protocol.h> > +#include <net/inet_common.h> > +#include <linux/bpf-cgroup.h> > +#include <linux/errname.h> > + > +#define SMBDIRECT_FN_GENERIC(__sk, __fmt, __args...) do { \ > + struct smbdirect_socket *__sc =3D smbdirect_socket_from_sk(__sk);= \ > + __smbdirect_log_generic(__sc, SMBDIRECT_LOG_INFO, SMBDIRECT_LOG_S= K, \ > + __fmt " sc=3D%p %s first_error=3D%1pe kern=3D%u locked=3D= %u refs=3D%u dead=3D%u mrefs=3D%u\n", \ > + ##__args, __sc, \ > + smbdirect_socket_status_string(__sc->status), \ > + SMBDIRECT_DEBUG_ERR_PTR(__sc->first_error), \ > + (__sk)->sk_kern_sock, \ > + sock_owned_by_user_nocheck(__sk), \ > + refcount_read(&((__sk)->sk_refcnt)), \ > + sock_flag(__sk, SOCK_DEAD), \ > + module_refcount(THIS_MODULE)); \ > +} while (0) > + > +#define SMBDIRECT_FN_COMMENT(__sk, __comment) \ > + SMBDIRECT_FN_GENERIC(__sk, "%s with", __comment) > + > +#define SMBDIRECT_FN_CALLED(__sk) \ > + SMBDIRECT_FN_GENERIC(__sk, "Called for") > + > +#define SMBDIRECT_FN_RETURN_VOID(__sk) \ > + SMBDIRECT_FN_GENERIC(__sk, "Returning for") > + > +#define SMBDIRECT_FN_RETURN_POLL(__sk, __mask) \ > + SMBDIRECT_FN_GENERIC(__sk, "Returning mask=3D0x%x for", __mask) > + > +#define SMBDIRECT_FN_RETURN_INT(__sk, __ret) do { \ > + bool __is_err =3D IS_ERR(SMBDIRECT_DEBUG_ERR_PTR(__ret)); \ > + SMBDIRECT_FN_GENERIC(__sk, "Returning ret=3D%d%s%s%s for", \ > + (__ret), \ > + __is_err ? " (" : "", \ > + __is_err ? errname(__ret) : "", \ > + __is_err ? ")" : ""); \ > +} while (0) > + > +static bool smbdirect_sk_logging_needed(struct smbdirect_socket *sc, > + void *private_ptr, > + unsigned int lvl, > + unsigned int cls) > +{ > + /* > + * Only errors by default. > + */ > + if (lvl <=3D SMBDIRECT_LOG_ERR) > + return true; > + return false; > +} > + > +static void smbdirect_sk_logging_vaprintf(struct smbdirect_socket *sc, > + const char *func, > + unsigned int line, > + void *private_ptr, > + unsigned int lvl, > + unsigned int cls, > + struct va_format *vaf) > +{ > + if (lvl <=3D SMBDIRECT_LOG_ERR) > + pr_err("%s:%u %pV", func, line, vaf); > + else > + pr_info("%s:%u %pV", func, line, vaf); > +} > + > +void smbdirect_sk_reclassify(struct sock *sk) > +{ > +#ifdef CONFIG_DEBUG_LOCK_ALLOC > + static struct lock_class_key sk_key[2]; > + static struct lock_class_key slock_key[2]; > + > + if (WARN_ON_ONCE(!sock_allow_reclassification(sk))) > + return; > + > + switch (sk->sk_family) { > + case AF_INET: > + /* > + * Before we reset the owner we > + * need to drop the reference of the > + * existing module, this is only > + * really relevant for AF_INET, > + * as that is always builtin > + * there's no potential leak > + * of module references. We do it > + * mainly in order to match the > + * AF_INET6 case. > + */ > + sk_owner_put(sk); > + sk_owner_clear(sk); > + > + sock_lock_init_class_and_name(sk, > + "slock-AF_INET-IPPROTO-SMBD= IRECT", > + &slock_key[0], > + "sk_lock-AF_INET-IPPROTO-SM= BDIRECT", > + &sk_key[0]); > + > + /* > + * Now that we reclassified the socket > + * we're also the new sk_owner, but that's > + * not needed as there's still a reference > + * on sk->sk_prot->owner, which is dropped > + * in sk_prot_free(). But in order to > + * avoid module reference leaks to our > + * own module we need to put and clear > + * sk_owner, in order to allow callers > + * to do their own reclassification. > + */ > + sk_owner_put(sk); > + sk_owner_clear(sk); > + break; > + case AF_INET6: > + /* > + * Before we reset the owner we > + * need to drop the reference of the > + * existing module. > + * > + * As we also use inet6_register_protosw() > + * and other symbols from a possible > + * ipv6.ko, we already have enough > + * module references in order to avoid > + * unloading of ipv6.ko, while smbdirect.ko > + * is loaded. > + * > + * However when smbdirect.ko is unloaded > + * we should not leak references in order > + * to allow ipv6.ko to be unloaded as well. > + */ > + sk_owner_put(sk); > + sk_owner_clear(sk); > + > + sock_lock_init_class_and_name(sk, > + "slock-AF_INET6-IPPROTO-SMB= DIRECT", > + &slock_key[1], > + "sk_lock-AF_INET6-IPPROTO-S= MBDIRECT", > + &sk_key[1]); > + > + /* > + * Now that we reclassified the socket > + * we're also the new sk_owner, but that's > + * not needed as there's still a reference > + * on sk->sk_prot->owner, which is dropped > + * in sk_prot_free(). But in order to > + * avoid module reference leaks to our > + * own module we need to put and clear > + * sk_owner, in order to allow callers > + * to do their own reclassification. > + */ > + sk_owner_put(sk); > + sk_owner_clear(sk); > + break; > + default: > + WARN_ON_ONCE(1); > + } > +#endif /* CONFIG_DEBUG_LOCK_ALLOC */ > +} > + > +static void smbdirect_sk_destruct(struct sock *sk) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + > + /* > + * Called by sk_free() > + */ > + > + SMBDIRECT_FN_CALLED(sk); > + > + if (WARN_ON_ONCE(sc->status !=3D SMBDIRECT_SOCKET_DESTROYED)) { > + pr_err("Attempt to release SMBDIRECT socket in status %s = sc %p\n", > + smbdirect_socket_status_string(sc->status), sc); > + SMBDIRECT_FN_RETURN_VOID(sk); > + return; > + } > + > + SMBDIRECT_FN_COMMENT(sk, "calling orig_sk_destruct"); > + smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO, > + "sc[%p]->orig_sk_destruct[%ps]\n", > + sc, sc->orig_sk_destruct); > + sc->orig_sk_destruct(sk); > + SMBDIRECT_FN_RETURN_VOID(sk); > +} > + > +static int smbdirect_sk_init(struct sock *sk) > +{ > + struct socket *sock =3D sk->sk_socket; > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + const struct smbdirect_socket_parameters *sp =3D &smbdirect_globa= ls.default_parameters; > + void (*orig_sk_destruct)(struct sock *sk); > + int ret; > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + smbdirect_sk_reclassify(sk); > + > + smbdirect_socket_init(sc); > + smbdirect_socket_set_logging(sc, > + NULL, > + smbdirect_sk_logging_needed, > + smbdirect_sk_logging_vaprintf); > + > + smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO, > + "Called for sk=3D%p family=3D%u protocol=3D%u type=3D%u\n= ", > + sk, sk->sk_family, sk->sk_protocol, sk->sk_type); > + > + sk_sockets_allocated_inc(sk); > + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); > + > + orig_sk_destruct =3D sk->sk_destruct; > + SMBDIRECT_FN_COMMENT(sk, "remembered orig_sk_destruct"); > + smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO, > + "sc[%p]->orig_sk_destruct[%ps]\n", > + sc, orig_sk_destruct); > + sc->orig_sk_destruct =3D orig_sk_destruct; > + sk->sk_destruct =3D smbdirect_sk_destruct; > + > + /* > + * We want to handle all sockopts explicitly > + * and only support what we really support. > + */ > + set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags); > + /* > + * There are no legacy callers, so we are strict > + * regarding ipv4 vs. ipv6. > + */ > + sk->sk_ipv6only =3D true; > + > + /* > + * No userspace sockets yet... > + */ > + if (!sk->sk_kern_sock) { > + sc->first_error =3D -EPROTONOSUPPORT; > + SMBDIRECT_FN_COMMENT(sk, "No userspace sockets"); > + return -EPROTONOSUPPORT; > + } > + > + ret =3D smbdirect_socket_init_new(sock_net(sk), sc); > + if (ret) > + goto socket_init_failed; > + /* > + * smbdirect_socket_init_new() called smbdirect_socket_init() aga= in, > + * so we need to call smbdirect_socket_set_logging() again! > + */ > + smbdirect_socket_set_logging(sc, > + NULL, > + smbdirect_sk_logging_needed, > + smbdirect_sk_logging_vaprintf); > + > + WARN_ON_ONCE(sc->orig_sk_destruct !=3D orig_sk_destruct); > + WARN_ON_ONCE(sk->sk_destruct !=3D smbdirect_sk_destruct); > + > + ret =3D smbdirect_socket_set_initial_parameters(sc, sp); > + if (ret) > + goto set_params_failed; > + > + ret =3D smbdirect_socket_set_kernel_settings(sc, IB_POLL_SOFTIRQ,= sk->sk_allocation); > + if (ret) > + goto set_settings_failed; > + > + SMBDIRECT_FN_RETURN_INT(sk, 0); > + return 0; > + > +set_settings_failed: > +set_params_failed: > +socket_init_failed: > + sc->first_error =3D ret; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static void smbdirect_sk_destroy(struct sock *sk) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + /* > + * For now do a sync disconnect/destroy > + * > + * SMBDIRECT_LOG_INFO is enough here > + * as this is the typical case where > + * we terminate the connection ourself. > + */ > + smbdirect_socket_schedule_cleanup_lvl(sc, > + SMBDIRECT_LOG_INFO, > + -ESHUTDOWN); > + smbdirect_socket_destroy_sync(sc); > + > + sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); > + sk_sockets_allocated_dec(sk); > + > + SMBDIRECT_FN_RETURN_VOID(sk); > +} > + > +static int smbdirect_sk_hash(struct sock *sk) > +{ > + SMBDIRECT_FN_CALLED(sk); > + return 0; > +} It seems this was implemented just to fill all function pointers of sk->sk_prot but looks unnecessary. Same for other NOP functions, unhash(), release_cb(), etc. > + > +static void smbdirect_sk_unhash(struct sock *sk) > +{ > + SMBDIRECT_FN_CALLED(sk); > +} > + > +static void smbdirect_sk_release_cb(struct sock *sk) > +{ > + /* > + * Called from release_sock() > + */ > + SMBDIRECT_FN_CALLED(sk); > +} > + > +static int smbdirect_sk_pre_bind(struct sock *sk, > + struct sockaddr_unsized *uaddr, > + int *addr_len, > + u32 *flags, > + u16 *port) > +{ > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + if (*addr_len < sizeof(uaddr->sa_family)) > + return -EINVAL; > + > + /* AF_UNSPEC is not allowed */ > + if (sk->sk_family !=3D uaddr->sa_family) > + return -EAFNOSUPPORT; > + > + /* > + * BPF prog is run before any checks are done so that if the prog > + * changes context in a wrong way it will be caught. > + */ > + switch (sk->sk_family) { > + case AF_INET: > + if (*addr_len < sizeof(struct sockaddr_in)) > + return -EINVAL; > + > + *port =3D ntohs(((struct sockaddr_in *)uaddr)->sin_port); > + > + return BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr= _len, > + CGROUP_INET4_BI= ND, > + flags); Do you really need these bpf hooks ? It seems the smb sockets can be created from kthread and tied to the root cgroup. > + case AF_INET6: > + /* > + * We require a full struct sockaddr_in6 (28 bytes) inste= ad of a > + * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don= 't > + * have any legacy callers in userspace and the > + * rdma layer also expects that. > + */ > + if (*addr_len < sizeof(struct sockaddr_in6)) > + return -EINVAL; > + > + *port =3D ntohs(((struct sockaddr_in6 *)uaddr)->sin6_port= ); > + > + return BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr= _len, > + CGROUP_INET6_BI= ND, > + flags); > + } > + > + return -EAFNOSUPPORT; > +} > + > +static int smbdirect_sk_do_bind(struct sock *sk, > + struct sockaddr_unsized *uaddr, > + const u32 flags, > + const u16 port) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + bool is_any_addr =3D true; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + if (flags & BIND_WITH_LOCK) > + sock_owned_by_me(sk); > + else > + sock_not_owned_by_me(sk); > + > + ret =3D smbdirect_socket_bind(sc, (struct sockaddr *)uaddr); > + if (ret) { > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + ret =3D smbdirect_socket_sync_saddr_to_sk(sc, &is_any_addr); > + if (ret) { > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + /* Make sure we are allowed to bind here. */ > + if (sk->sk_num && !(flags & BIND_FROM_BPF)) { > + switch (sk->sk_family) { > + case AF_INET: > + ret =3D BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk); > + if (ret) { > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + break; > + > + case AF_INET6: > + ret =3D BPF_CGROUP_RUN_PROG_INET6_POST_BIND(sk); > + if (ret) { > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + break; > + } > + } > + > + if (!is_any_addr) > + sk->sk_userlocks |=3D SOCK_BINDADDR_LOCK; > + if (port) > + sk->sk_userlocks |=3D SOCK_BINDPORT_LOCK; Can this socket be passed to SOCK_BINDPORT_LOCK user, inet_bhash2_reset_saddr(), inet_sk_rebuild_header() ? > + > + ret =3D 0; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sk_bind(struct sock *sk, struct sockaddr_unsized *a= ddr, int addr_len) > +{ > + struct net *net =3D sock_net(sk); > + u32 flags =3D BIND_WITH_LOCK; > + u16 port =3D 0; > + u16 check_port =3D 0; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + ret =3D smbdirect_sk_pre_bind(sk, addr, &addr_len, &flags, &port)= ; > + if (ret) { > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + /* > + * treat the iwarp tcp port for > + * smb (5445) as the main smb port (445) > + * and only allow the bind if 445 > + * would be allowed. > + */ > + if (port =3D=3D 5445) > + check_port =3D 445; > + else > + check_port =3D port; > + > + if (!(flags & BIND_NO_CAP_NET_BIND_SERVICE) && > + check_port && inet_port_requires_bind_service(net, check_port= ) && > + !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) { > + ret =3D -EACCES; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (flags & BIND_WITH_LOCK) > + lock_sock(sk); Is connect() called without bind() and could a bpf prog calls bpf_bind() for this socket ? > + > + ret =3D smbdirect_sk_do_bind(sk, addr, flags, port); > + > + if (flags & BIND_WITH_LOCK) > + release_sock(sk); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static struct sock *smbdirect_sk_accept(struct sock *lsk, struct proto_a= ccept_arg *arg) > +{ > + struct smbdirect_socket *lsc =3D smbdirect_socket_from_sk(lsk); > + long timeo =3D sock_rcvtimeo(lsk, arg->flags & O_NONBLOCK); > + struct smbdirect_socket *nsc; > + struct sock *nsk; > + > + SMBDIRECT_FN_CALLED(lsk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(lsk); > + > + lock_sock(lsk); > + nsc =3D smbdirect_socket_accept(lsc, timeo, arg); > + release_sock(lsk); > + if (!nsc) { > + SMBDIRECT_FN_RETURN_INT(lsk, arg->err); > + return NULL; > + } > + nsk =3D &nsc->sk; > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(nsk); Looks redundant. > + > + SMBDIRECT_FN_RETURN_INT(lsk, 0); > + return nsk; > +} > + > +static int smbdirect_sk_pre_connect(struct sock *sk, struct sockaddr_uns= ized *uaddr, int addr_len) > +{ > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + if (addr_len < sizeof(uaddr->sa_family)) > + return -EINVAL; > + > + if (sk->sk_family !=3D uaddr->sa_family) > + return -EAFNOSUPPORT; > + > + switch (sk->sk_family) { > + case AF_INET: > + if (addr_len < sizeof(struct sockaddr_in)) > + return -EINVAL; > + > + return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr, &addr= _len); > + case AF_INET6: > + /* > + * We require a full struct sockaddr_in6 (28 bytes) inste= ad of a > + * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don= 't > + * have any legacy callers in userspace and the > + * rdma layer also expects that. > + */ > + if (addr_len < sizeof(struct sockaddr_in6)) > + return -EINVAL; > + > + return BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr, &addr= _len); > + } > + > + return -EAFNOSUPPORT; > +} > + > +static int smbdirect_sk_connect(struct sock *sk, struct sockaddr_unsized= *addr, int addr_len) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + ret =3D smbdirect_connect(sc, (struct sockaddr *)addr); Why is this called via sk->sk_prot instead of being called directly from sock->ops->connect() ? Same for other sk->sk_prot functions. > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sk_setsockopt(struct sock *sk, int level, int optna= me, > + sockptr_t optval, unsigned int optlen) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + switch (level) { > + default: > + SMBDIRECT_FN_COMMENT(sk, "default"); > + smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO, > + "level=3D%d optname=3D%d for sk=3D%p\n", > + level, optname, sk); > + ret =3D -EOPNOTSUPP; > + break; > + } > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sk_getsockopt(struct sock *sk, int level, int optna= me, > + char __user *optval, int __user *optle= n) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + switch (level) { > + default: > + SMBDIRECT_FN_COMMENT(sk, "default"); > + smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO, > + "level=3D%d optname=3D%d for sk=3D%p\n", > + level, optname, sk); > + ret =3D -EOPNOTSUPP; > + break; > + } > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sk_ioctl(struct sock *sk, int cmd, int *karg) Is there any in-kernel ioctl() user for this socket ? > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + switch (cmd) { > + default: > + SMBDIRECT_FN_COMMENT(sk, "default"); > + smbdirect_log_sk(sc, SMBDIRECT_LOG_INFO, > + "cmd=3D%d (0x%x) for sk=3D%p\n", > + cmd, cmd, sk); > + ret =3D -ENOIOCTLCMD; > + break; > + } > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static inline size_t smbdirect_cmsg_count(const struct msghdr *_msg, > + int *first_sol_smbdirect_type) > +{ > + struct msghdr *msg =3D (struct msghdr *)(uintptr_t)(const void *)= _msg; > + struct cmsghdr *cmsg =3D NULL; > + size_t count =3D 0; > + > + if (first_sol_smbdirect_type !=3D NULL) > + *first_sol_smbdirect_type =3D -1; > + > + for (cmsg =3D CMSG_FIRSTHDR(msg); > + cmsg !=3D NULL; > + cmsg =3D CMSG_NXTHDR(msg, cmsg)) { > + count++; > + if (cmsg->cmsg_level !=3D SOL_SMBDIRECT) > + continue; > + if (first_sol_smbdirect_type !=3D NULL) { > + *first_sol_smbdirect_type =3D cmsg->cmsg_type; > + first_sol_smbdirect_type =3D NULL; > + } > + } > + > + return count; > +} > + > +static __always_inline > +ssize_t __smbdirect_cmsg_extract(const struct msghdr *_msg, > + int cmsg_type, > + void *_payload, > + size_t payloadmin, > + size_t payloadmax) > +{ > + struct msghdr *msg =3D (struct msghdr *)(uintptr_t)(const void *)= _msg; > + size_t cmsg_len_min =3D CMSG_LEN(payloadmin); > + size_t cmsg_len_max =3D CMSG_LEN(payloadmax); > + const size_t cmsg_len_hdr =3D CMSG_LEN(0); > + uint8_t *payload =3D (uint8_t *)_payload; > + struct cmsghdr *cmsg =3D NULL; > + size_t payloadlen; > + > + BUILD_BUG_ON(cmsg_len_min > cmsg_len_max); > + if (WARN_ON_ONCE(cmsg_len_min > cmsg_len_max)) > + return -EBADMSG; > + > + for (cmsg =3D CMSG_FIRSTHDR(msg); > + cmsg !=3D NULL; > + cmsg =3D CMSG_NXTHDR(msg, cmsg)) { > + if (cmsg->cmsg_level !=3D SOL_SMBDIRECT) > + continue; > + > + if (cmsg->cmsg_type !=3D cmsg_type) > + continue; > + > + if (cmsg->cmsg_len < cmsg_len_min) > + return -EBADMSG; > + > + if (cmsg->cmsg_len > cmsg_len_max) > + return -EMSGSIZE; > + > + payloadlen =3D cmsg->cmsg_len - cmsg_len_hdr; > + if (payloadlen > 0) > + memcpy(payload, CMSG_DATA(cmsg), payloadlen); > + if (payloadlen < payloadmax) > + memset(payload + payloadlen, 0, payloadmax - payl= oadlen); > + return payloadlen; > + } > + > + return -ENOMSG; > +} > + > +static __always_inline > +int smbdirect_buffer_remote_invalidate_cmsg_extract(const struct msghdr = *msg, > + u32 *remote_token) > +{ > + struct smbdirect_buffer_remote_invalidate_args args =3D { > + .remote_token =3D 0, > + }; > + ssize_t ret; > + > + ret =3D __smbdirect_cmsg_extract(msg, > + SMBDIRECT_BUFFER_REMOTE_INVALIDATE= _CMSG_TYPE, > + &args, sizeof(args), sizeof(args))= ; > + if (ret < 0) > + return ret; > + > + *remote_token =3D args.remote_token; > + return 0; > +} > + > +static int smbdirect_sk_sendmsg_locked(struct sock *sk, struct msghdr *m= sg, size_t msg_len) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + struct iov_iter *iter =3D &msg->msg_iter; > + unsigned int flags =3D msg->msg_flags; > + size_t cmsg_count =3D 0; > + int cmsg_type =3D -1; > + bool need_invalidate =3D false; > + u32 remote_key =3D 0; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + cmsg_count =3D smbdirect_cmsg_count(msg, &cmsg_type); > + if (cmsg_count > 1) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (cmsg_type =3D=3D SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE= ) { > + ret =3D smbdirect_buffer_remote_invalidate_cmsg_extract(m= sg, &remote_key); > + if (!ret) > + need_invalidate =3D true; /* remote_key is valid = */ > + else if (ret !=3D -ENOMSG) { > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + } else if (cmsg_count) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (WARN_ON_ONCE(iov_iter_rw(iter) !=3D ITER_SOURCE)) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (WARN_ON_ONCE(iov_iter_count(iter) !=3D msg_len)) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (flags & MSG_DONTWAIT) { > + if (!sc->first_error && msg_len && atomic_read(&sc->send_= io.credits.count) =3D=3D 0) { > + ret =3D -EAGAIN; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + } > + flags &=3D ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL); > + > + ret =3D smbdirect_connection_send_iter(sc, > + iter, > + flags, > + need_invalidate, > + remote_key); > + if (ret < 0) > + /* Handle error and possibly send SIGPIPE. */ > + ret =3D sk_stream_error(sk, msg->msg_flags, ret); > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sk_sendmsg(struct sock *sk, struct msghdr *msg, siz= e_t msg_len) > +{ > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + lock_sock(sk); > + ret =3D smbdirect_sk_sendmsg_locked(sk, msg, msg_len); > + release_sock(sk); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sk_recvmsg(struct sock *sk, struct msghdr *msg, siz= e_t len, int flags) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + struct iov_iter *iter =3D &msg->msg_iter; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + if (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + if (WARN_ON_ONCE(iov_iter_rw(iter) !=3D ITER_DEST)) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + /* > + * For now smbdirect_connection_recvmsg() relies > + * on this assertion and the current in kernel > + * users are working that way. > + */ > + if (WARN_ON_ONCE(iov_iter_count(iter) !=3D len)) { > + ret =3D -EINVAL; > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + > + lock_sock(sk); > + if (flags & MSG_DONTWAIT) { > + if (!sc->first_error && len && sc->recv_io.reassembly.dat= a_length =3D=3D 0) { > + ret =3D -EAGAIN; > + release_sock(sk); > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > + } > + } > + flags &=3D ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL); > + ret =3D smbdirect_connection_recvmsg(sc, msg, flags); > + if (msg->msg_get_inq && ret >=3D 0) > + msg->msg_inq =3D sc->recv_io.reassembly.data_length; > + release_sock(sk); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static void smbdirect_sk_shutdown(struct sock *sk, int how) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + smbdirect_socket_schedule_cleanup(sc, -ESHUTDOWN); > + > + SMBDIRECT_FN_RETURN_VOID(sk); > +} > + > +static int smbdirect_sk_disconnect(struct sock *sk, int flags) > +{ > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + smbdirect_socket_schedule_cleanup(sc, -ESHUTDOWN); > + > + if (flags & O_NONBLOCK) { > + if (sc->status >=3D SMBDIRECT_SOCKET_DISCONNECTED) { > + SMBDIRECT_FN_RETURN_INT(sk, 0); > + return 0; > + } > + > + /* > + * This will cause SS_DISCONNECTING in > + * smbdirect_sock_connect_locked(). > + */ > + SMBDIRECT_FN_RETURN_INT(sk, sc->first_error); > + return sc->first_error; > + } > + > + smbdirect_socket_destroy_sync(sc); > + > + SMBDIRECT_FN_RETURN_INT(sk, 0); > + return 0; > +} > + > +static void smbdirect_sk_close(struct sock *sk, long timeout) > +{ > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + /* > + * We hold an additional reference so > + * that the sock_put() in sk_common_release() > + * doesn't call sk_free(), that is potentially > + * deferred to our sock_put() after release_sock(). > + * > + * Note that sk_common_release() calls > + * smbdirect_sk_destroy() as the first thing. > + */ > + sock_hold(sk); > + lock_sock(sk); > + sk_common_release(sk); > + release_sock(sk); > + SMBDIRECT_FN_COMMENT(sk, "before sock_put()"); > + sock_put(sk); > +} > + > +static struct percpu_counter smbdirect_sockets_allocated; > + > +static struct proto smbdirect_prot =3D { > + .name =3D "smbdirect", > + .owner =3D THIS_MODULE, > + .obj_size =3D sizeof(struct smbdirect_socket), > + .ipv6_pinfo_offset =3D offsetof(struct smbdirect_socket, ine= t6), > + .init =3D smbdirect_sk_init, > + .destroy =3D smbdirect_sk_destroy, > + .hash =3D smbdirect_sk_hash, > + .unhash =3D smbdirect_sk_unhash, > + .release_cb =3D smbdirect_sk_release_cb, > + .bind =3D smbdirect_sk_bind, > + .accept =3D smbdirect_sk_accept, > + .pre_connect =3D smbdirect_sk_pre_connect, > + .connect =3D smbdirect_sk_connect, > + .setsockopt =3D smbdirect_sk_setsockopt, > + .getsockopt =3D smbdirect_sk_getsockopt, > + .ioctl =3D smbdirect_sk_ioctl, > + .sendmsg =3D smbdirect_sk_sendmsg, > + .recvmsg =3D smbdirect_sk_recvmsg, > + .shutdown =3D smbdirect_sk_shutdown, > + .disconnect =3D smbdirect_sk_disconnect, > + .close =3D smbdirect_sk_close, > + .sockets_allocated =3D &smbdirect_sockets_allocated, > +}; > + > +static int smbdirect_sock_release(struct socket *sock) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not locked */ > + sock_not_owned_by_me(sk); > + WARN_ON_ONCE(sock_owned_by_user_nocheck(sk)); > + > + switch (sk->sk_family) { > + case AF_INET: > + SMBDIRECT_FN_COMMENT(sk, "calling inet_release()"); > + ret =3D inet_release(sock); Given setsockopt() is banned, smbdirect_sk_close() can be inlined here. > + break; > + case AF_INET6: > +#if IS_ENABLED(CONFIG_IPV6) > + SMBDIRECT_FN_COMMENT(sk, "calling inet6_release()"); > + ret =3D inet6_release(sock); > +#else > + ret =3D -EAFNOSUPPORT; > +#endif > + break; > + default: > + ret =3D -EAFNOSUPPORT; > + break; > + } > + > + return ret; > +} > + > +static int smbdirect_sock_bind(struct socket *sock, struct sockaddr_unsi= zed *saddr, int len) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + switch (sk->sk_family) { > + case AF_INET: > + ret =3D inet_bind(sock, saddr, len); inet_bind() just calls sk->sk_prot->bind() if set. So, the same question applies; why not inline sk->sk_prot->bind() here. > + break; > + case AF_INET6: > +#if IS_ENABLED(CONFIG_IPV6) > + ret =3D inet6_bind(sock, saddr, len); > +#else > + ret =3D -EAFNOSUPPORT; > +#endif > + break; > + default: > + ret =3D -EAFNOSUPPORT; > + break; > + } > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_connect_locked(struct socket *sock, > + struct sockaddr_unsized *uaddr, > + int addr_len, int flags) > +{ > + struct sock *sk =3D sock->sk; > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is already locked */ > + sock_owned_by_me(sk); > + > + if (addr_len < sizeof(uaddr->sa_family)) > + return -EINVAL; > + > + if (sk->sk_family !=3D uaddr->sa_family) > + return -EAFNOSUPPORT; > + > + switch (sk->sk_family) { > + case AF_INET: > + if (addr_len < sizeof(struct sockaddr_in)) > + return -EINVAL; > + break; > + case AF_INET6: > + /* > + * We require a full struct sockaddr_in6 (28 bytes) inste= ad of a > + * minimal size of SIN6_LEN_RFC2133 (24 bytes), as we don= 't > + * have any legacy callers in userspace and the > + * rdma layer also expects that. > + */ > + if (addr_len < sizeof(struct sockaddr_in6)) > + return -EINVAL; > + break; > + default: > + return -EAFNOSUPPORT; > + } > + > + switch (sock->state) { > + case SS_CONNECTED: > + return -EISCONN; > + case SS_CONNECTING: > + return -EALREADY; > + case SS_UNCONNECTED: > + break; > + default: > + return -EINVAL; > + } > + > + if (sc->status =3D=3D SMBDIRECT_SOCKET_CONNECTED) > + return -EISCONN; > + > + if (sc->status !=3D SMBDIRECT_SOCKET_CREATED) > + return -EINVAL; > + > + if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) { > + ret =3D sk->sk_prot->pre_connect(sk, uaddr, addr_len); > + if (ret) > + return ret; > + } > + > + ret =3D sk->sk_prot->connect(sk, uaddr, addr_len); > + if (ret < 0) > + return ret; > + > + inet_sk_set_state(sk, TCP_SYN_SENT); > + sock->state =3D SS_CONNECTING; > + > + if (flags & O_NONBLOCK) > + return -EINPROGRESS; > + > + ret =3D smbdirect_connection_wait_for_connected(sc); > + if (ret) > + goto sock_error; > + > + return 0; > + > +sock_error: > + sock->state =3D SS_UNCONNECTED; > + sk->sk_disconnects++; > + if (sk->sk_prot->disconnect(sk, flags)) > + sock->state =3D SS_DISCONNECTING; > + return ret; > +} > + > +static int smbdirect_sock_connect(struct socket *sock, > + struct sockaddr_unsized *uaddr, > + int addr_len, int flags) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + lock_sock(sk); > + ret =3D smbdirect_sock_connect_locked(sock, uaddr, addr_len, flag= s); > + release_sock(sk); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_listen(struct socket *sock, int backlog) > +{ > + struct sock *sk =3D sock->sk; > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + lock_sock(sk); > + ret =3D smbdirect_socket_listen(sc, backlog); > + release_sock(sk); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_accept(struct socket *lsock, struct socket *ns= ock, > + struct proto_accept_arg *arg) > +{ > + struct sock *lsk =3D lsock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(lsk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(lsk); > + > + ret =3D inet_accept(lsock, nsock, arg); Could this account socket memory to memcg twice ? see 4a997d49d92a and 16942cf4d3e3 > + if (!ret) > + /* > + * We want to handle all sockopts explicitly > + * and only support what we really support. > + */ > + set_bit(SOCK_CUSTOM_SOCKOPT, &nsock->flags); > + > + SMBDIRECT_FN_RETURN_INT(lsk, ret); > + return ret; > +} > + > +static int smbdirect_sock_getname(struct socket *sock, struct sockaddr *= uaddr, int peer) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + switch (sk->sk_family) { > + case AF_INET: > + ret =3D inet_getname(sock, uaddr, peer); > + break; > + case AF_INET6: > +#if IS_ENABLED(CONFIG_IPV6) > + ret =3D inet6_getname(sock, uaddr, peer); > +#else > + ret =3D -EAFNOSUPPORT; > +#endif > + break; > + default: > + ret =3D -EAFNOSUPPORT; > + break; > + } > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static __poll_t smbdirect_sock_poll(struct file *file, struct socket *so= ck, poll_table *wait) > +{ > + struct sock *sk =3D sock->sk; > + struct smbdirect_socket *sc =3D smbdirect_socket_from_sk(sk); > + __poll_t mask =3D 0; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + sock_poll_wait(file, sock, wait); > + > + if (sc->status =3D=3D SMBDIRECT_SOCKET_LISTENING) { > + if (!list_empty_careful(&sc->listen.ready)) > + mask |=3D EPOLLIN | EPOLLRDNORM; > + SMBDIRECT_FN_RETURN_POLL(sk, mask); > + return mask; > + } > + > + if (sc->first_error) { > + /* > + * A broken connection should report almost everything in= order to let > + * applications to detect it reliable. > + */ > + mask |=3D EPOLLHUP; > + mask |=3D EPOLLERR; > + mask |=3D EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; > + mask |=3D EPOLLOUT | EPOLLWRNORM; > + SMBDIRECT_FN_RETURN_POLL(sk, mask); > + return mask; > + } > + > + if (sc->status !=3D SMBDIRECT_SOCKET_CONNECTED) { > + /* > + * A just created socket. > + */ > + SMBDIRECT_FN_RETURN_POLL(sk, mask); > + return mask; > + } > + > + if (sc->recv_io.reassembly.data_length > 0) > + mask |=3D EPOLLIN | EPOLLRDNORM; > + > + if (atomic_read(&sc->send_io.bcredits.count) > 0 && > + atomic_read(&sc->send_io.lcredits.count) > 0 && > + atomic_read(&sc->send_io.credits.count) > 0) > + mask |=3D EPOLLOUT | EPOLLWRNORM; > + else { > + sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); > + set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); > + > + /* > + * Race breaker. If space is freed after > + * wspace test but before the flags are set, > + * IO signal will be lost. Memory barrier > + * pairs with the input side. > + */ > + smp_mb__after_atomic(); > + if (atomic_read(&sc->send_io.bcredits.count) > 0 && > + atomic_read(&sc->send_io.lcredits.count) > 0 && > + atomic_read(&sc->send_io.credits.count) > 0) > + mask |=3D EPOLLOUT | EPOLLWRNORM; > + } > + > + SMBDIRECT_FN_RETURN_POLL(sk, mask); > + return mask; > +} > + > +static int smbdirect_sock_ioctl(struct socket *sock, unsigned int cmd, u= nsigned long arg) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + /* > + * We may need to handle some here as > + * smbirect_sk_ioctl() only gets a kernel > + * int pointer as arg, but we may > + * need to the whole struct > + */ > + switch (cmd) { > + default: > + /* > + * Note this has some special handling for > + * sk->sk_type =3D=3D SOCK_RAW, in case we ever > + * implement SOCK_RAW... > + * > + * It calls smbdirect_sk_ioctl()... > + */ > + ret =3D sk_ioctl(sk, cmd, (void __user *)arg); > + break; > + } > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_shutdown(struct socket *sock, int how) > +{ > + struct sock *sk =3D sock->sk; > + int ret =3D 0; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + /* > + * We have these from userspace: > + * SHUT_RD =3D 0, SHUT_WR =3D 1 and SHUT_RDWR =3D 2 > + * > + * And we map them to SHUTDOWN_MASK =3D 3 > + * RCV_SHUTDOWN =3D 1, SEND_SHUTDOWN =3D 2, BOTH =3D 3 > + */ > + how++; > + if ((how & ~SHUTDOWN_MASK) || !how) /* MAXINT->0 */ > + return -EINVAL; > + > + lock_sock(sk); > + > + switch (sk->sk_state) { > + case TCP_CLOSE: > + ret =3D -ENOTCONN; > + fallthrough; > + default: > + WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | how); > + sk->sk_prot->shutdown(sk, how); > + break; > + > + case TCP_SYN_SENT: > + case TCP_SYN_RECV: > + ret =3D sk->sk_prot->disconnect(sk, O_NONBLOCK); > + break; > + } > + > + /* Wake up anyone sleeping in poll. */ > + sk->sk_state_change(sk); > + release_sock(sk); > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_setsockopt(struct socket *sock, int level, int= optname, > + sockptr_t optval, unsigned int optle= n) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + ret =3D sock_common_setsockopt(sock, level, optname, optval, optl= en); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_getsockopt(struct socket *sock, int level, int= optname, > + char __user *optval, int __user *opt= len) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + ret =3D sock_common_getsockopt(sock, level, optname, optval, optl= en); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_sendmsg(struct socket *sock, struct msghdr *ms= g, size_t len) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + ret =3D sk->sk_prot->sendmsg(sk, msg, len); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static int smbdirect_sock_recvmsg(struct socket *sock, struct msghdr *ms= g, size_t size, > + int flags) > +{ > + struct sock *sk =3D sock->sk; > + int ret; > + > + SMBDIRECT_FN_CALLED(sk); > + > + /* assert it is not already locked */ > + sock_not_owned_by_me(sk); > + > + ret =3D sock_common_recvmsg(sock, msg, size, flags); > + > + SMBDIRECT_FN_RETURN_INT(sk, ret); > + return ret; > +} > + > +static const struct proto_ops smbdirect_inet_proto_ops =3D { > + .family =3D PF_INET, > + .owner =3D THIS_MODULE, > + .release =3D smbdirect_sock_release, > + .bind =3D smbdirect_sock_bind, > + .connect =3D smbdirect_sock_connect, > + .socketpair =3D sock_no_socketpair, > + .listen =3D smbdirect_sock_listen, > + .accept =3D smbdirect_sock_accept, > + .getname =3D smbdirect_sock_getname, > + .poll =3D smbdirect_sock_poll, > + .ioctl =3D smbdirect_sock_ioctl, > + .shutdown =3D smbdirect_sock_shutdown, > + .setsockopt =3D smbdirect_sock_setsockopt, > + .getsockopt =3D smbdirect_sock_getsockopt, > + .sendmsg =3D smbdirect_sock_sendmsg, > + .sendmsg_locked =3D smbdirect_sk_sendmsg_locked, > + .recvmsg =3D smbdirect_sock_recvmsg, > + .mmap =3D sock_no_mmap, > +}; > + > +#if IS_ENABLED(CONFIG_IPV6) > +static const struct proto_ops smbdirect_inet6_proto_ops =3D { > + .family =3D PF_INET6, > + .owner =3D THIS_MODULE, > + .release =3D smbdirect_sock_release, > + .bind =3D smbdirect_sock_bind, > + .connect =3D smbdirect_sock_connect, > + .socketpair =3D sock_no_socketpair, > + .listen =3D smbdirect_sock_listen, > + .accept =3D smbdirect_sock_accept, > + .getname =3D smbdirect_sock_getname, > + .poll =3D smbdirect_sock_poll, > + .ioctl =3D smbdirect_sock_ioctl, > + .shutdown =3D smbdirect_sock_shutdown, > + .setsockopt =3D smbdirect_sock_setsockopt, > + .getsockopt =3D smbdirect_sock_getsockopt, > + .sendmsg =3D smbdirect_sock_sendmsg, > + .sendmsg_locked =3D smbdirect_sk_sendmsg_locked, > + .recvmsg =3D smbdirect_sock_recvmsg, > + .mmap =3D sock_no_mmap, > +}; > +#endif > + > +static struct inet_protosw smbdirect_inet_stream_protosw =3D { > + .type =3D SOCK_STREAM, > + .protocol =3D IPPROTO_SMBDIRECT, > + .prot =3D &smbdirect_prot, > + .ops =3D &smbdirect_inet_proto_ops, > +}; > + > +#if IS_ENABLED(CONFIG_IPV6) > +static struct inet_protosw smbdirect_inet6_stream_protosw =3D { > + .type =3D SOCK_STREAM, > + .protocol =3D IPPROTO_SMBDIRECT, > + .prot =3D &smbdirect_prot, > + .ops =3D &smbdirect_inet6_proto_ops, > +}; > +#endif > + > +struct smbdirect_socket *smbdirect_socket_from_sock(const struct socket = *sock) > +{ > + if (!sock || > + !sock->sk || > + sock->sk->sk_protocol !=3D IPPROTO_SMBDIRECT) > + return NULL; > + > + if (WARN_ON_ONCE(sock->sk->sk_destruct !=3D smbdirect_sk_destruct= )) > + return NULL; > + > + return smbdirect_socket_from_sk(sock->sk); > +} > +__SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_from_sock); > + > +static __init int smbdirect_protosw_init(void) > +{ > + int err; > + > + err =3D proto_register(&smbdirect_prot, 1); > + if (err) > + return err; > + > + inet_register_protosw(&smbdirect_inet_stream_protosw); > +#if IS_ENABLED(CONFIG_IPV6) > + inet6_register_protosw(&smbdirect_inet6_stream_protosw); > +#endif > + > + return 0; > +} > + > +static __exit void smbdirect_protosw_exit(void) > +{ > +#if IS_ENABLED(CONFIG_IPV6) > + inet6_unregister_protosw(&smbdirect_inet6_stream_protosw); > +#endif > + inet_unregister_protosw(&smbdirect_inet_stream_protosw); > + > + proto_unregister(&smbdirect_prot); > +} > + > +__init int smbdirect_proto_init(void) > +{ > + int err; > + > + err =3D percpu_counter_init(&smbdirect_sockets_allocated, 0, GFP_= KERNEL); > + if (err) > + goto err_percpu_counter; > + > + err =3D smbdirect_protosw_init(); > + if (err) > + goto err_protosw; > + > + return 0; > + > +err_protosw: > + percpu_counter_destroy(&smbdirect_sockets_allocated); > +err_percpu_counter: > + return err; > +} > + > +__exit void smbdirect_proto_exit(void) > +{ > + smbdirect_protosw_exit(); > + percpu_counter_destroy(&smbdirect_sockets_allocated); > +} > + > +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 257 /* IPPROTO_SMBDIRECT */, SOC= K_STREAM); > +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 257 /* IPPROTO_SMBDIRECT */, SO= CK_STREAM); > diff --git a/fs/smb/common/smbdirect/smbdirect_public.h b/fs/smb/common/s= mbdirect/smbdirect_public.h > index 50088155e7c3..9f96c66bbe32 100644 > --- a/fs/smb/common/smbdirect/smbdirect_public.h > +++ b/fs/smb/common/smbdirect/smbdirect_public.h > @@ -49,6 +49,7 @@ int smbdirect_socket_set_kernel_settings(struct smbdire= ct_socket *sc, > #define SMBDIRECT_LOG_RDMA_MR 0x100 > #define SMBDIRECT_LOG_RDMA_RW 0x200 > #define SMBDIRECT_LOG_NEGOTIATE 0x400 > +#define SMBDIRECT_LOG_SK 0x800 > void smbdirect_socket_set_logging(struct smbdirect_socket *sc, > void *private_ptr, > bool (*needed)(struct smbdirect_socket = *sc, > @@ -145,4 +146,6 @@ void smbdirect_connection_legacy_debug_proc_show(stru= ct smbdirect_socket *sc, > unsigned int rdma_readwr= ite_threshold, > struct seq_file *m); > > +struct smbdirect_socket *smbdirect_socket_from_sock(const struct socket = *sock); > + > #endif /* __FS_SMB_COMMON_SMBDIRECT_SMBDIRECT_PUBLIC_H__ */ > diff --git a/fs/smb/common/smbdirect/smbdirect_rw.c b/fs/smb/common/smbdi= rect/smbdirect_rw.c > index 3b2eb8c48efc..154339955617 100644 > --- a/fs/smb/common/smbdirect/smbdirect_rw.c > +++ b/fs/smb/common/smbdirect/smbdirect_rw.c > @@ -105,11 +105,11 @@ static void smbdirect_connection_rdma_write_done(st= ruct ib_cq *cq, struct ib_wc > smbdirect_connection_rdma_rw_done(cq, wc, DMA_TO_DEVICE); > } > > -int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc, > - void *buf, size_t buf_len, > - struct smbdirect_buffer_descriptor_v1 = *desc, > - size_t desc_len, > - bool is_read) > +static int smbdirect_connection_rdma_xmit_locked(struct smbdirect_socket= *sc, > + void *buf, size_t buf_le= n, > + struct smbdirect_buffer_= descriptor_v1 *desc, > + size_t desc_len, > + bool is_read) > { > const struct smbdirect_socket_parameters *sp =3D &sc->parameters; > enum dma_data_direction direction =3D is_read ? DMA_FROM_DEVICE := DMA_TO_DEVICE; > @@ -123,6 +123,8 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_s= ocket *sc, > int credits_needed; > size_t desc_buf_len, desc_num =3D 0; > > + smbdirect_socket_sk_owned_by_me(sc); > + > if (sc->status !=3D SMBDIRECT_SOCKET_CONNECTED) > return -ENOTCONN; > > @@ -235,7 +237,9 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_s= ocket *sc, > } > > msg =3D list_last_entry(&msg_list, struct smbdirect_rw_io, list); > + smbdirect_socket_sk_unlock(sc); > wait_for_completion(&completion); > + smbdirect_socket_sk_lock(sc); > ret =3D msg->error; > out: > list_for_each_entry_safe(msg, next_msg, &msg_list, list) { > @@ -252,4 +256,19 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_= socket *sc, > kfree(msg); > goto out; > } > + > +int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc, > + void *buf, size_t buf_len, > + struct smbdirect_buffer_descriptor_v1 = *desc, > + size_t desc_len, > + bool is_read) > +{ > + int ret; > + > + smbdirect_socket_sk_lock(sc); > + ret =3D smbdirect_connection_rdma_xmit_locked(sc, buf, buf_len, d= esc, desc_len, is_read); > + smbdirect_socket_sk_unlock(sc); > + > + return ret; > +} > __SMBDIRECT_EXPORT_SYMBOL__(smbdirect_connection_rdma_xmit); > diff --git a/fs/smb/common/smbdirect/smbdirect_socket.c b/fs/smb/common/s= mbdirect/smbdirect_socket.c > index 9153e1dbf53d..76e406999588 100644 > --- a/fs/smb/common/smbdirect/smbdirect_socket.c > +++ b/fs/smb/common/smbdirect/smbdirect_socket.c > @@ -5,6 +5,7 @@ > */ > > #include "smbdirect_internal.h" > +#include <net/transp_v6.h> > > bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs) > { > @@ -217,6 +218,7 @@ int smbdirect_socket_set_kernel_settings(struct smbdi= rect_socket *sc, > sc->send_io.mem.gfp_mask =3D gfp_mask; > sc->recv_io.mem.gfp_mask =3D gfp_mask; > sc->rw_io.mem.gfp_mask =3D gfp_mask; > + sc->sk.sk_allocation =3D gfp_mask; > > return 0; > } > @@ -242,6 +244,106 @@ void smbdirect_socket_set_logging(struct smbdirect_= socket *sc, > } > __SMBDIRECT_EXPORT_SYMBOL__(smbdirect_socket_set_logging); > > +int smbdirect_socket_sync_saddr_to_sk(struct smbdirect_socket *sc, bool = *_is_any_addr) > +{ > + struct sock *sk =3D &sc->sk; > + const struct sockaddr_storage *saddr; > + const struct sockaddr_in *sin; > + const struct sockaddr_in6 *sin6; > + struct in_addr sin_addr =3D { .s_addr =3D htonl(INADDR_ANY), }; > + struct in6_addr sin6_addr =3D in6addr_any; > + __be32 sin6_flowinfo =3D 0; > + bool is_any_addr =3D true; > + u16 sport =3D 0; > + int ret; > + > + saddr =3D &sc->rdma.cm_id->route.addr.src_addr; > + > + if (WARN_ON_ONCE(saddr->ss_family !=3D sk->sk_family)) { > + ret =3D -EINVAL; > + return ret; > + } > + > + switch (saddr->ss_family) { > + case AF_INET: > + sin =3D (struct sockaddr_in *)saddr; > + sport =3D ntohs(sin->sin_port); > + sin_addr =3D sin->sin_addr; > + is_any_addr =3D (sin_addr.s_addr =3D=3D htonl(INADDR_ANY)= ); > + break; > + > + case AF_INET6: > + sin6 =3D (struct sockaddr_in6 *)saddr; > + sport =3D ntohs(sin6->sin6_port); > + sin_addr.s_addr =3D LOOPBACK4_IPV6; > + sin6_addr =3D sin6->sin6_addr; > + is_any_addr =3D ipv6_addr_any(&sin6_addr); > + sin6_flowinfo =3D sin6->sin6_flowinfo; > + break; > + } > + > + sk->sk_bound_dev_if =3D sc->rdma.cm_id->route.addr.dev_addr.bound= _dev_if; > + sk->sk_rcv_saddr =3D sc->inet.inet_saddr =3D sin_addr.s_addr; > +#if IS_ENABLED(CONFIG_IPV6) > + sk->sk_v6_rcv_saddr =3D sc->inet6.saddr =3D sin6_addr; > +#else > + sc->inet6.saddr =3D sin6_addr; > +#endif > + sc->inet6.flow_label =3D sin6_flowinfo; > + sk->sk_num =3D sport; > + sc->inet.inet_sport =3D htons(sport); > + > + if (_is_any_addr) > + *_is_any_addr =3D is_any_addr; > + return 0; > +} > + > +int smbdirect_socket_sync_daddr_to_sk(struct smbdirect_socket *sc) > +{ > + struct sock *sk =3D &sc->sk; > + const struct sockaddr_storage *daddr; > + const struct sockaddr_in *sin; > + const struct sockaddr_in6 *sin6; > + struct in_addr sin_addr =3D { .s_addr =3D htonl(INADDR_ANY), }; > +#if IS_ENABLED(CONFIG_IPV6) > + struct in6_addr sin6_addr =3D in6addr_any; > +#endif > + u16 dport =3D 0; > + int ret; > + > + daddr =3D &sc->rdma.cm_id->route.addr.dst_addr; > + > + if (WARN_ON_ONCE(daddr->ss_family !=3D sk->sk_family)) { > + ret =3D -EINVAL; > + return ret; > + } > + > + switch (daddr->ss_family) { > + case AF_INET: > + sin =3D (struct sockaddr_in *)daddr; > + dport =3D ntohs(sin->sin_port); > + sin_addr =3D sin->sin_addr; > + break; > + > + case AF_INET6: > + sin6 =3D (struct sockaddr_in6 *)daddr; > + dport =3D ntohs(sin6->sin6_port); > + sin_addr.s_addr =3D LOOPBACK4_IPV6; > +#if IS_ENABLED(CONFIG_IPV6) > + sin6_addr =3D sin6->sin6_addr; > +#endif > + break; > + } > + > + sk->sk_daddr =3D sc->inet.inet_daddr =3D sin_addr.s_addr; > +#if IS_ENABLED(CONFIG_IPV6) > + sk->sk_v6_daddr =3D sin6_addr; > +#endif > + sk->sk_dport =3D sc->inet.inet_dport =3D htons(dport); > + > + return 0; > +} > + > static void smbdirect_socket_wake_up_all(struct smbdirect_socket *sc) > { > /* > @@ -257,6 +359,38 @@ static void smbdirect_socket_wake_up_all(struct smbd= irect_socket *sc) > wake_up_all(&sc->recv_io.reassembly.wait_queue); > wake_up_all(&sc->rw_io.credits.wait_queue); > wake_up_all(&sc->mr_io.ready.wait_queue); > + > + if (sc->sk.sk_family) { > + struct sock *sk =3D &sc->sk; > + > + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); > + > + WARN_ON_ONCE(sc->first_error =3D=3D 0); > + if (sc->first_error < 0) > + WRITE_ONCE(sk->sk_err, -sc->first_error); > + else > + WRITE_ONCE(sk->sk_err, sc->first_error); > + > + if (sc->status >=3D SMBDIRECT_SOCKET_DISCONNECTED) { > + inet_sk_set_state(sk, TCP_CLOSE); > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_socket->state =3D SS_UNCONNECTED; > + } else { > + inet_sk_set_state(sk, TCP_CLOSING); > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_socket->state =3D SS_DISCONNECTING= ; > + } > + > + /* > + * Note tcp_done_with_error() also calls both > + * sk->sk_state_change(sk) via tcp_done() > + * and sk_error_report() directly. > + */ > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk->sk_state_change(sk); > + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket) > + sk_error_report(sk); > + } > } > > void __smbdirect_socket_schedule_cleanup(struct smbdirect_socket *sc, > @@ -510,11 +644,13 @@ static void smbdirect_socket_destroy(struct smbdire= ct_socket *sc) > */ > smbdirect_socket_wake_up_all(sc); > > + smbdirect_socket_sk_unlock(sc); > disable_work_sync(&sc->disconnect_work); > disable_work_sync(&sc->connect.work); > disable_work_sync(&sc->recv_io.posted.refill_work); > disable_work_sync(&sc->idle.immediate_work); > disable_delayed_work_sync(&sc->idle.timer_work); > + smbdirect_socket_sk_lock(sc); > > if (sc->rdma.cm_id) > rdma_lock_handler(sc->rdma.cm_id); > @@ -600,6 +736,8 @@ void smbdirect_socket_destroy_sync(struct smbdirect_s= ocket *sc) > */ > WARN_ON_ONCE(in_interrupt()); > > + smbdirect_socket_sk_owned_by_me(sc); > + > /* > * First we try to disable the work > * without disable_work_sync() in a > @@ -625,7 +763,9 @@ void smbdirect_socket_destroy_sync(struct smbdirect_s= ocket *sc) > > smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, > "cancelling and disable disconnect_work\n"); > + smbdirect_socket_sk_unlock(sc); > disable_work_sync(&sc->disconnect_work); > + smbdirect_socket_sk_lock(sc); > > smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, > "destroying rdma session\n"); > @@ -634,7 +774,9 @@ void smbdirect_socket_destroy_sync(struct smbdirect_s= ocket *sc) > if (sc->status < SMBDIRECT_SOCKET_DISCONNECTED) { > smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, > "wait for transport being disconnected\n"); > + smbdirect_socket_sk_unlock(sc); > wait_event(sc->status_wait, sc->status =3D=3D SMBDIRECT_S= OCKET_DISCONNECTED); > + smbdirect_socket_sk_lock(sc); > smbdirect_log_rdma_event(sc, SMBDIRECT_LOG_INFO, > "waited for transport being disconnected\n"); > } > @@ -723,6 +865,8 @@ int smbdirect_socket_wait_for_credits(struct smbdirec= t_socket *sc, > { > int ret; > > + smbdirect_socket_sk_owned_by_me(sc); > + > if (WARN_ON_ONCE(needed < 0)) > return -EINVAL; > > @@ -731,9 +875,12 @@ int smbdirect_socket_wait_for_credits(struct smbdire= ct_socket *sc, > return 0; > > atomic_add(needed, total_credits); > + > + smbdirect_socket_sk_unlock(sc); > ret =3D wait_event_interruptible(*waitq, > atomic_read(total_credits)= >=3D needed || > sc->status !=3D expected_s= tatus); > + smbdirect_socket_sk_lock(sc); > > if (sc->status !=3D expected_status) > return unexpected_errno; > diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/s= mbdirect/smbdirect_socket.h > index c09eddd8ad16..6bb201683259 100644 > --- a/fs/smb/common/smbdirect/smbdirect_socket.h > +++ b/fs/smb/common/smbdirect/smbdirect_socket.h > @@ -104,6 +104,18 @@ enum smbdirect_keepalive_status { > }; > > struct smbdirect_socket { > + union { > + struct sock sk; > + struct inet_sock inet; > + }; > + /* needed by inet6_create() */ > + struct ipv6_pinfo inet6; > + void (*orig_sk_destruct)(struct sock *sk); > + > + /* > + * This is the first element that is > + * initialized in smbdirect_socket_init() > + */ > enum smbdirect_socket_status status; > wait_queue_head_t status_wait; > int first_error; > @@ -548,14 +560,18 @@ static void __smbdirect_log_printf(struct smbdirect= _socket *sc, > __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_RDMA_RW, f= mt, ##args) > #define smbdirect_log_negotiate(sc, lvl, fmt, args...) \ > __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_NEGOTIATE,= fmt, ##args) > +#define smbdirect_log_sk(sc, lvl, fmt, args...) \ > + __smbdirect_log_generic(sc, lvl, SMBDIRECT_LOG_SK, fmt, #= #args) > > static __always_inline void smbdirect_socket_init(struct smbdirect_socke= t *sc) > { > + const size_t status_offset =3D offsetof(struct smbdirect_socket, = status); > + > /* > * This also sets status =3D SMBDIRECT_SOCKET_CREATED > */ > BUILD_BUG_ON(SMBDIRECT_SOCKET_CREATED !=3D 0); > - memset(sc, 0, sizeof(*sc)); > + memset(((u8 *)sc)+status_offset, 0, sizeof(*sc)-status_offset); > > init_waitqueue_head(&sc->status_wait); > > @@ -700,6 +716,14 @@ static __always_inline void smbdirect_socket_init(st= ruct smbdirect_socket *sc) > __SMBDIRECT_CHECK_STATUS_WARN(__sc, __expected_status, \ > __SMBDIRECT_SOCKET_DISCONNECT(__sc);) > > +static __always_inline struct smbdirect_socket * > +smbdirect_socket_from_sk(const struct sock *sk) > +{ > + WARN_ON_ONCE(!sk); > + BUILD_BUG_ON(offsetof(struct smbdirect_socket, sk) !=3D 0); > + return container_of(sk, struct smbdirect_socket, sk); > +} > + > struct smbdirect_send_io { > struct smbdirect_socket *socket; > struct ib_cqe cqe; > -- > 2.43.0 >