* Re: [PATCH net-next v2 3/4] bpf-timestamp: keep track of the skb when wait_for_space occurs
From: Jason Xing @ 2026-04-08 0:35 UTC (permalink / raw)
To: Willem de Bruijn
Cc: davem, edumazet, kuba, pabeni, horms, willemb, martin.lau, netdev,
bpf, Jason Xing, Yushan Zhou
In-Reply-To: <willemdebruijn.kernel.1b49845d3acc7@gmail.com>
On Wed, Apr 8, 2026 at 5:17 AM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> Jason Xing wrote:
> > On Tue, Apr 7, 2026 at 11:33 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> > >
> > > On Mon, Apr 6, 2026 at 10:37 PM Willem de Bruijn
> > > <willemdebruijn.kernel@gmail.com> wrote:
> > > >
> > > > Jason Xing wrote:
> > > > > On Mon, Apr 6, 2026 at 10:28 AM Willem de Bruijn
> > > > > <willemdebruijn.kernel@gmail.com> wrote:
> > > > > >
> > > > > > Jason Xing wrote:
> > > > > > > From: Jason Xing <kernelxing@tencent.com>
> > > > > > >
> > > > > > > The patch is the 1/2 part of push-level granularity feature.
> > > > > > >
> > > > > > > Tag the skb in tcp_sendmsg_locked() when wait_for_space occurs even
> > > > > > > though it might not carry the last byte of the sendmsg.
> > > > > > >
> > > > > > > Prior to the patch, BPF timestamping cannot cover this case:
> > > > > > > The following steps reproduce this:
> > > > > > > 1) skb A is the current last skb before entering wait_for_space process
> > > > > > > 2) tcp_push() pushes A without any tag
> > > > > > > 3) A is transmitted from TCP to driver without putting any skb carrying
> > > > > > > timestamps in the error queue, like SCHED, DRV/HARDWARE.
> > > > > > > 4) sk_stream_wait_memory() sleeps for a while and then returns with an
> > > > > > > error code. Note that the socket lock is released.
> > > > > > > 5) skb A finally gets acked and removed from the rtx queue.
> > > > > > > 6) continue with the rest of tcp_sendmsg_locked(): it will jump to(goto)
> > > > > > > 'do_error' label and then 'out' label.
> > > > > > > 7) at this moment, skb A turns out to be the last one in this send
> > > > > > > syscall, and miss the following tcp_bpf_tx_timestamp() opportunity
> > > > > > > before the final tcp_push()
> > > > > > > 8) BPF script fails to see any timestamps this time
> > > > > > >
> > > > > > > Signed-off-by: Yushan Zhou <katrinzhou@tencent.com>
> > > > > > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > > > > > ---
> > > > > > > net/ipv4/tcp.c | 4 +++-
> > > > > > > 1 file changed, 3 insertions(+), 1 deletion(-)
> > > > > > >
> > > > > > > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
> > > > > > > index c603b90057f6..7d030a11d004 100644
> > > > > > > --- a/net/ipv4/tcp.c
> > > > > > > +++ b/net/ipv4/tcp.c
> > > > > > > @@ -1400,9 +1400,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
> > > > > > > wait_for_space:
> > > > > > > set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
> > > > > > > tcp_remove_empty_skb(sk);
> > > > > > > - if (copied)
> > > > > > > + if (copied) {
> > > > > > > + tcp_bpf_tx_timestamp(sk);
> > > > > > > tcp_push(sk, flags & ~MSG_MORE, mss_now,
> > > > > > > TCP_NAGLE_PUSH, size_goal);
> > > > > >
> > > > > > Now the number of skbs that will be tracked will be unpredictable,
> > > > > > varying based on memory pressure.
> > > > >
> > > > > Right, I put some effort into writing a selftests to check how many
> > > > > push functions get called at one time and failed to do so.
> > > > >
> > > > > >
> > > > > > That sounds hard to use to me. Especially if these extra pushes
> > > > > > cannot be identified as such.
> > > > > >
> > > > > > Perhaps if all skbs from the same sendmsg call can be identified,
> > > > > > that would help explain pattern in data resulting from these
> > > > > > uncommon extra data points.
> > > > >
> > > > > You meant move tcp_bpf_tx_timestamp before tcp_skb_entail()? That is
> > > > > close to packet basis without considering fragmentation of skb :)
> > > >
> > > > No, I meant somehow in the notification having a way to identify all
> > > > the skbs belonging to the same sendmsg call, to allow filtering on
> > > > that. But I also don't immediately see how to do that (without adding
> > > > yet another counter say).
> > >
> > > If we don't build the relationship between skb and sendmsg (just like
> > > the SENDMSG sock option), we will have no clue on how to calculate. If
> > > we only take care of the skb from the view of the syscall layer, it's
> > > fine by moving tcp_bpf_tx_timestamp() before tcp_skb_entail(). But in
> > > terms of per skb even generated beneath TCP due to gso/tso, there is
> > > only one way to correlate: adding an additional member in the skb
> > > structure to store its sendmsg time. This discussion is only suitable
> > > for use cases like net_timestamping.
> > >
> > > Well, my key point is that, I have to admit, the above (including
> > > existing bpf script net_timestamping) is a less effective way which
> > > definitely harms the performance because of the extremely frequent
> > > look-up process. It's not suitable for 7x24 observability in
> > > production. What we've done internally is make the kernel layer as
> > > lightweight/easy as possible and let the timestamping feature throw
> > > each record into a ring buffer that the application can read, sort and
> > > calculate. This arch survives the performance. But that's simply what
> > > the design of the kernel module is, given the fast deployment in
> > > production. I suppose in the future we could build a userspace tool
> > > like blktrace to monitor efficiently instead of the selftest sample.
> > > Honestly I don't like look-up action.
> > >
> > > Since we're modifying the kernel, how about adding a new member to
> > > record sendmsg time which bpf script is able to read. The whole
> > > scenario looks like this:
> > > 1) in tcp_sendmsg_locked(), record the sendmsg time for each skb
> > > 2) in either tso_fragment() or tcp_gso_tstamp(), each new skb will get
> > > a copy of its original skb
> > > 3) in each stage, bpf script reads the skb's sendmsg time and the
> > > current time, and then effortlessly do the math.
> > >
> > > At this point, what I had in mind is we have two options:
> > > 1) only handle the skb from the view of the send syscall layer, which
> > > is, for sure, very simple but not thorough.
> > > 2) stick to a pure authentic packet basis, then adding a new member
> > > seems inevitable. so the question would be where to add? The space of
> > > the skb structure is very precious :(
> >
> > Finding a suitable place to put this timestamp is really hard. IIRC,
> > we can't expand the size of struct skb_shared_info so easily since
> > it's a global effect.
> >
> > I'm wondering if we can turn the per-packet mode into a non-compatible
> > feature by reusing 'u32 tskey' to store a microsecond timestamp of
> > sendmsg.
>
> Agreed that an extra field is hard. We should avoid that.
Avoiding adding a new one makes the whole work extremely hard. I'm
wondering since we have hwtstamp in shared info, why not add a
software one for timestamping use? Then, we would support more
different protocols in more different stages in a finer grain, which
is a big coarse picture in my mind.
Adding a software bit will completely reduce the whole complexity and
be very easy to use. Would you expect to see a draft by adding such a
bit first?
Or just like I mentioned, repurposing tskey seems an alternative,
which, however, makes the new feature incompatible.
>
> If the purpose is to group skbs by sendmsg call (e.g., to filter out
> all but the last one), it is probably also unnecessary.
>
> From a process PoV, since the process knows the sendmsg len and each
> skb has a tskey in byte offset, it can correlate the skb with a given
> sendmsg buffer.
>
> The BPF program is under control of a third-party admin. So that does
> not follow directly. But it can be passed additional metadata.
>
> I thought about passing the offset of the skb from the start of the
> sendmsg buffer to identify all consecutive skbs for a sendmsg call,
> as each new buffer will start with an skb with offset 0 ..
>
> .. but that won't work as there is no guarantee that a sendmsg call
> will not append to an existing outstanding skb.
Right. TCP is way too complex and we indeed see some tough issues when
trying to deploy the feature. So my humble take is to make the design
as simple as possible.
>
> Anyway, the general idea is to pass to the BPF program through
> bpf_skops_tx_timestamping some relevant signal , without having to
> expand either skb or sk itself.
>
> I hear you on that measuring every skb is too frequent. But is calling
> the BPF program and letting it decide whether to measure too? BPF
> program invocation itself should be cheap.
Oh, I was clear enough. Sorry. I meant tracing per skb is definitely
an awesome way to go. My ultimate goal is to do so. Instead of letting
people implement various fine grained bpf progs, we can provide a very
easy/understandable/efficient approach with more samples. It should be
very beneficial.
>
> If per-push is preferable, with a filter ability like the above, it
> seems more useful to me already.
Push-level is a compromise plan. Packet-level is what I always pursue :)
The current series has this ability: the bpf prog noticed it's a
SENDMSG sock option and will selectively call
bpf_sock_ops_enable_tx_tstamp() to do so. Only by calling
bpf_sock_ops_enable_tx_tstamp() could the skb be tracked.
Thanks,
Jason
^ permalink raw reply
* Re: [PATCH net-next v1] r8169: implement get_module functions for rtl8127atf
From: Andrew Lunn @ 2026-04-08 0:37 UTC (permalink / raw)
To: Fabio Baltieri
Cc: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King - ARM Linux, netdev, linux-kernel
In-Reply-To: <adWIy9d9VgDha_ub@google.com>
> Also find an issue
> in the txgbe driver (I think?), sent
> https://lore.kernel.org/netdev/20260405222013.5347-1-fabio.baltieri@gmail.com/
> for it.
I looked at that, and it seems like you are correct, but i also don't
understand why it has not crashed. So it would be good to get some
feedback from the trustnetic and net-swift people.
Andrew
^ permalink raw reply
* Re: [PATCH net-next] selftests: net: py: add test case filtering and listing
From: Willem de Bruijn @ 2026-04-08 0:38 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
shuah, petrm, willemb, linux-kselftest
In-Reply-To: <20260407151715.3800579-1-kuba@kernel.org>
Jakub Kicinski wrote:
> When developing new test cases and reproducing failures in
> existing ones we currently have to run the entire test which
> can take minutes to finish.
>
> Add command line options for test selection, modeled after
> kselftest_harness.h:
>
> -l list tests (all or filtered)
> -t name include test
> -T name exclude test
>
> Since we don't have as clean separation into fixture / variant /
> test as kselftest_harness this is not really a 1 to 1 match.
> We have to lean on glob patterns instead.
>
> Like in kselftest_harness filters are evaluated in order, first
> match wins. If only exclusions are specified everything else is
> included and vice versa.
>
> Glob patterns (*, ?, [) are supported in addition to exact
> matching.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Jim Mattson @ 2026-04-08 0:47 UTC (permalink / raw)
To: Dave Hansen
Cc: Pawan Gupta, x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin,
Josh Poimboeuf, David Kaplan, Sean Christopherson,
Borislav Petkov, Dave Hansen, Peter Zijlstra, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, KP Singh, Jiri Olsa,
David S. Miller, David Laight, Andy Lutomirski, Thomas Gleixner,
Ingo Molnar, David Ahern, Martin KaFai Lau, Eduard Zingerman,
Song Liu, Yonghong Song, John Fastabend, Stanislav Fomichev,
Hao Luo, Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm,
Asit Mallick, Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <a605fb45-f8e3-45ad-8924-1da43b9a9e05@intel.com>
On Tue, Apr 7, 2026 at 4:41 PM Dave Hansen <dave.hansen@intel.com> wrote:
>
> On 4/7/26 16:27, Jim Mattson wrote:
> > What is your proposed BHI_DIS_S override mechanism, then?
>
> Let me make sure I get this right. The desire is to:
>
> 1. Have hypervisors lie to guests about the CPU they are running on (for
> the benefit of large/diverse migration pools)
> 2. Have guests be allowed to boot with BHI_DIS_S for performance
> 3. Have apps in those guests that care about security to opt back in to
> BHI_DIS_S for themselves?
I just want guests on heterogeneous migration pools to properly
protect themselves from native BHI when running on host kernels at
least as far back as Linux v6.6.
To that end, I would be satisfied with using the longer BHB clearing
sequence when HYPERVISOR is true and BHI_CTRL is false.
^ permalink raw reply
* Re: [PATCH net-next] vsock/virtio: remove unnecessary call to `virtio_transport_get_ops`
From: Jakub Kicinski @ 2026-04-08 0:59 UTC (permalink / raw)
To: Luigi Leonardi
Cc: Michael S. Tsirkin, Jason Wang, Xuan Zhuo, Eugenio Pérez,
Stefan Hajnoczi, Stefano Garzarella, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, Arseniy Krasnov, kvm,
virtualization, netdev, linux-kernel
In-Reply-To: <adUvRegULccN0hiL@leonardi-redhat>
On Tue, 7 Apr 2026 18:22:57 +0200 Luigi Leonardi wrote:
> >> Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support")
> >> Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
> >
> >Acked-by: Michael S. Tsirkin <mst@redhat.com>
> >
> >but I think we should drop the Fixes tag.
>
> ok, should I send a v2 without the tag?
Yes please
--
pw-bot: cr
^ permalink raw reply
* Re: [PATCH net-next 00/13] netfilter: updates for net-next
From: Jakub Kicinski @ 2026-04-08 1:00 UTC (permalink / raw)
To: Florian Westphal
Cc: netdev, Paolo Abeni, David S. Miller, Eric Dumazet,
netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>
On Tue, 7 Apr 2026 16:15:27 +0200 Florian Westphal wrote:
> netfilter pull request nf-next-26-04-07
IIUC plan is to send v2, LMK if I misread
^ permalink raw reply
* Re: [PATCH 6/8] smb: smbdirect: add in kernel only support for IPPROTO_SMBDIRECT
From: Kuniyuki Iwashima @ 2026-04-08 1:04 UTC (permalink / raw)
To: Stefan Metzmacher
Cc: linux-cifs, samba-technical, Steve French, Tom Talpey, Long Li,
Namjae Jeon, David Howells, Henrique Carvalho, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, Xin Long, quic, linux-rdma,
linux-kernel
In-Reply-To: <4563333e367417d98a32779ae1358c0964eb6e79.1775571957.git.metze@samba.org>
On Tue, Apr 7, 2026 at 7:47 AM Stefan Metzmacher <metze@samba.org> 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 SMB3,
> 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 <smfrench@gmail.com>
> Cc: Tom Talpey <tom@talpey.com>
> Cc: Long Li <longli@microsoft.com>
> Cc: Namjae Jeon <linkinjeon@kernel.org>
> Cc: David Howells <dhowells@redhat.com>
> Cc: Henrique Carvalho <henrique.carvalho@suse.com>
> Cc: linux-cifs@vger.kernel.org
> Cc: samba-technical@lists.samba.org
> Cc: David S. Miller <davem@davemloft.net>
> Cc: Eric Dumazet <edumazet@google.com>
> Cc: Jakub Kicinski <kuba@kernel.org>
> Cc: Paolo Abeni <pabeni@redhat.com>
> Cc: Simon Horman <horms@kernel.org>
> Cc: Kuniyuki Iwashima <kuniyu@google.com>
> Cc: Willem de Bruijn <willemb@google.com>
> Cc: netdev@vger.kernel.org
> Cc: Xin Long <lucien.xin@gmail.com>
> Cc: quic@lists.linux.dev
> Cc: linux-rdma@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Stefan Metzmacher <metze@samba.org>
> ---
> 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/Makefile
> index 423f533e1002..fcff485d7c45 100644
> --- a/fs/smb/common/smbdirect/Makefile
> +++ b/fs/smb/common/smbdirect/Makefile
> @@ -10,6 +10,7 @@ smbdirect-y := \
> 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/smbdirect/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 = 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 = CMSG_SPACE(payloadlen);
> + size_t cmsg_len = CMSG_LEN(payloadlen);
> + struct cmsghdr *cmsg = NULL;
> + void *dataptr = NULL;
> +
> + BUILD_BUG_ON(cmsg_space > sizeof(cbuffer->msg_control));
> +
> + memset(cbuffer, 0, sizeof(*cbuffer));
> +
> + msg->msg_control = cbuffer->msg_control;
> + msg->msg_controllen = cmsg_space;
> +
> + cmsg = CMSG_FIRSTHDR(msg);
> + cmsg->cmsg_level = SOL_SMBDIRECT;
> + cmsg->cmsg_type = cmsg_type;
> + cmsg->cmsg_len = cmsg_len;
> + dataptr = CMSG_DATA(cmsg);
> + memcpy(dataptr, payload, payloadlen);
> + msg->msg_controllen = 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_cmsg_buffer *cbuffer,
> + const __u32 *remote_token)
> +{
> + if (remote_token) {
> + struct smbdirect_buffer_remote_invalidate_args args = {
> + .remote_token = *remote_token,
> + };
> +
> + __smbdirect_cmsg_prepare(msg, cbuffer,
> + SMBDIRECT_BUFFER_REMOTE_INVALIDATE_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/smbdirect/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(struct 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 = &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 = wait_event_interruptible_timeout(lsc->listen.wait_queue,
> !list_empty_careful(&lsc->listen.ready) ||
> lsc->status != SMBDIRECT_SOCKET_LISTENING ||
> lsc->first_error,
> timeo);
> + smbdirect_socket_sk_lock(lsc);
> if (lsc->status != SMBDIRECT_SOCKET_LISTENING)
> return -EINVAL;
> if (lsc->first_error)
> @@ -850,6 +857,11 @@ struct smbdirect_socket *smbdirect_socket_accept(struct smbdirect_socket *lsc,
> * order to grant credits to the peer.
> */
> nsc->status = SMBDIRECT_SOCKET_CONNECTED;
> + if (nsc->sk.sk_family) {
> + struct sock *nsk = &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/common/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 smbdirect_socket *sc)
>
> sc->rdma.cm_id->event_handler = smbdirect_connection_rdma_event_handler;
> sc->rdma.expected_event = RDMA_CM_EVENT_DISCONNECTED;
> +
> + if (sc->sk.sk_family) {
> + struct sock *sk = &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 smbdirect_socket *sc)
> smbdirect_socket_status_string(sc->status),
> SMBDIRECT_DEBUG_ERR_PTR(sc->first_error));
> sc->status = SMBDIRECT_SOCKET_CONNECTED;
> + if (sc->sk.sk_family) {
> + struct sock *sk = &sc->sk;
> +
> + inet_sk_set_state(sk, TCP_ESTABLISHED);
> + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)
> + sk->sk_socket->state = SS_CONNECTED;
> + }
>
> /*
> * We need to setup the refill and send immediate work
> @@ -203,6 +219,13 @@ void smbdirect_connection_negotiation_done(struct smbdirect_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 = &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 remote: %pISpsfc\n",
> IB_DEVICE_NAME_MAX, devname, src, dst);
>
> + smbdirect_socket_sk_unlock(sc);
> ret = wait_event_interruptible_timeout(sc->status_wait,
> sc->status == SMBDIRECT_SOCKET_CONNECTED ||
> sc->first_error,
> msecs_to_jiffies(sp->negotiate_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 smbdirect_socket *sc,
> atomic_add(batch->credit, &sc->send_io.bcredits.count);
> batch->credit = 0;
> wake_up(&sc->send_io.bcredits.wait_queue);
> + if (sc->sk.sk_family) {
> + struct sock *sk = &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 smbdirect_socket *sc,
> u32 data_length = 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 = wait_event_interruptible(sc->send_io.credits.wait_queue,
> atomic_read(&sc->send_io.credits.count) >= 1 ||
> atomic_read(&sc->recv_io.credits.available) >= 1 ||
> sc->status != SMBDIRECT_SOCKET_CONNECTED);
> + smbdirect_socket_sk_lock(sc);
> if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
> ret = -ENOTCONN;
> if (ret < 0)
> @@ -1268,9 +1303,11 @@ int smbdirect_connection_send_wait_zero_pending(struct smbdirect_socket *sc)
> * that means all the I/Os have been out and we are good to return
> */
>
> + smbdirect_socket_sk_unlock(sc);
> wait_event(sc->send_io.pending.zero_wait_queue,
> atomic_read(&sc->send_io.pending.count) == 0 ||
> sc->status != SMBDIRECT_SOCKET_CONNECTED);
> + smbdirect_socket_sk_lock(sc);
> if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
> smbdirect_log_write(sc, SMBDIRECT_LOG_ERR,
> "status=%s first_error=%1pe => %1pe\n",
> @@ -1297,6 +1334,8 @@ int smbdirect_connection_send_iter(struct smbdirect_socket *sc,
> int error = 0;
> __be32 hdr;
>
> + smbdirect_socket_sk_owned_by_me(sc);
> +
> if (WARN_ONCE(flags, "unexpected flags=0x%x\n", flags))
> return -EINVAL; /* no flags support for now */
>
> @@ -1448,7 +1487,9 @@ static void smbdirect_connection_send_immediate_work(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 = 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=%1pe\n",
> @@ -1632,6 +1673,12 @@ void smbdirect_connection_recv_io_done(struct ib_cq *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 = &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_cq *cq, struct ib_wc *wc)
>
> smbdirect_connection_reassembly_append_recv_io(sc, recv_io, data_length);
> wake_up(&sc->recv_io.reassembly.wait_queue);
> + if (sc->sk.sk_family) {
> + struct sock *sk = &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 smbdirect_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) == 0 &&
> atomic_read(&sc->send_io.credits.count) == 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 = wait_event_interruptible(sc->recv_io.reassembly.wait_queue,
> sc->recv_io.reassembly.data_length >= size ||
> sc->status != SMBDIRECT_SOCKET_CONNECTED);
> + 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 smbdirect_socket *sc)
> +{
> + if (sc->sk.sk_family) {
> + struct sock *sk = &sc->sk;
> +
> + /* assert it is already locked */
> + sock_owned_by_me(sk);
> + }
> +}
> +
> +static __always_inline void smbdirect_socket_sk_not_owned_by_me(struct smbdirect_socket *sc)
> +{
> + if (sc->sk.sk_family) {
> + struct sock *sk = &sc->sk;
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> + }
> +}
> +
> +static __always_inline void smbdirect_socket_sk_lock(struct smbdirect_socket *sc)
> +{
> + if (sc->sk.sk_family) {
> + struct sock *sk = &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 = &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 smbdirect_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 smbdirect_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/smbdirect/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 *sc, int backlog)
> */
> sc->listen.backlog = backlog;
>
> + if (sc->sk.sk_family) {
> + struct sock *sk = &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 smbdirect_socket *lsc,
> const struct rdma_cm_event *event)
> {
> const struct smbdirect_socket_parameters *lsp = &lsc->parameters;
> + struct sock *nsk = NULL;
> struct smbdirect_socket *nsc;
> unsigned long flags;
> size_t backlog = max_t(size_t, 1, lsc->listen.backlog);
> @@ -265,9 +272,39 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,
> return -EBUSY;
> }
>
> - ret = smbdirect_socket_create_accepting(new_id, &nsc);
> - if (ret)
> - goto socket_init_failed;
> + if (lsc->sk.sk_family) {
> + struct sock *lsk = &lsc->sk;
> +
> + ret = -ENOMEM;
> + nsk = 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 = smbdirect_socket_from_sk(nsk);
> +
> + ret = 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 != lsc->orig_sk_destruct);
> + WARN_ON_ONCE(nsk->sk_destruct != lsk->sk_destruct);
> + WARN_ON_ONCE(nsk->sk_ipv6only != lsk->sk_ipv6only);
> + } else {
> + ret = smbdirect_socket_create_accepting(new_id, &nsc);
> + if (ret)
> + goto socket_init_failed;
> + }
>
> nsc->logging = lsc->logging;
> ret = smbdirect_socket_set_initial_parameters(nsc, &lsc->parameters);
> @@ -302,7 +339,11 @@ static int smbdirect_listen_connect_request(struct smbdirect_socket *lsc,
> */
> nsc->ib.dev = NULL;
> nsc->rdma.cm_id = 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/smbdirect/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 = {
>
> static __init int smbdirect_module_init(void)
> {
> + struct smbdirect_socket_parameters *sp;
> int ret = -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 = &smbdirect_globals.default_parameters;
> + sp->resolve_addr_timeout_msec = 5 * 1000;
> + sp->resolve_route_timeout_msec = 5 * 1000;
> + sp->rdma_connect_timeout_msec = 5 * 1000;
> + sp->negotiate_timeout_msec = 120 * 1000;
> + sp->initiator_depth = 1; /* the server should change this */
> + sp->responder_resources = 1; /* the client should change this */
> + sp->recv_credit_max = 255;
> + sp->send_credit_target = 255;
> + sp->max_send_size = 1364;
> + /*
> + * The maximum fragmented upper-layer payload receive size supported
> + *
> + * Assume max_payload_per_credit is
> + * smbd_max_receive_size - 24 = 1340
> + *
> + * The maximum number would be
> + * smbd_receive_credit_max * max_payload_per_credit
> + *
> + * 1340 * 255 = 341700 (0x536C4)
> + *
> + * The minimum value from the spec is 131072 (0x20000)
> + *
> + * For now we use the logic we used before:
> + * (1364 * 255) / 2 = 173910 (0x2A756)
> + */
> + sp->max_fragmented_recv_size = (1364 * 255) / 2;
> + sp->max_recv_size = 1364;
> + sp->max_read_write_size = 0; /* the server should change this */
> + sp->max_frmr_depth = 0; /* the client should change this */
> + sp->keepalive_interval_msec = 120 * 1000;
> + sp->keepalive_timeout_msec = 5 * 1000;
> +
> + ret = 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/smbdirect/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_socket *sc)
> int ret;
>
> again:
> + smbdirect_socket_sk_unlock(sc);
> ret = wait_event_interruptible(sc->mr_io.ready.wait_queue,
> atomic_read(&sc->mr_io.ready.count) ||
> sc->status != SMBDIRECT_SOCKET_CONNECTED);
> + smbdirect_socket_sk_lock(sc);
> if (ret) {
> smbdirect_log_rdma_mr(sc, SMBDIRECT_LOG_ERR,
> "wait_event_interruptible ret=%d (%1pe)\n",
> @@ -281,7 +283,9 @@ smbdirect_connection_register_mr_io(struct smbdirect_socket *sc,
> return NULL;
> }
>
> + smbdirect_socket_sk_lock(sc);
> mr = 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 smbdirect_mr_io *mr)
> if (mr->state == 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 != 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/smbdirect/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 = smbdirect_socket_from_sk(__sk); \
> + __smbdirect_log_generic(__sc, SMBDIRECT_LOG_INFO, SMBDIRECT_LOG_SK, \
> + __fmt " sc=%p %s first_error=%1pe kern=%u locked=%u refs=%u dead=%u mrefs=%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=0x%x for", __mask)
> +
> +#define SMBDIRECT_FN_RETURN_INT(__sk, __ret) do { \
> + bool __is_err = IS_ERR(SMBDIRECT_DEBUG_ERR_PTR(__ret)); \
> + SMBDIRECT_FN_GENERIC(__sk, "Returning ret=%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 <= 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 <= 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-SMBDIRECT",
> + &slock_key[0],
> + "sk_lock-AF_INET-IPPROTO-SMBDIRECT",
> + &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-SMBDIRECT",
> + &slock_key[1],
> + "sk_lock-AF_INET6-IPPROTO-SMBDIRECT",
> + &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 = smbdirect_socket_from_sk(sk);
> +
> + /*
> + * Called by sk_free()
> + */
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + if (WARN_ON_ONCE(sc->status != 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 = sk->sk_socket;
> + struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);
> + const struct smbdirect_socket_parameters *sp = &smbdirect_globals.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=%p family=%u protocol=%u type=%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 = 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 = orig_sk_destruct;
> + sk->sk_destruct = 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 = true;
> +
> + /*
> + * No userspace sockets yet...
> + */
> + if (!sk->sk_kern_sock) {
> + sc->first_error = -EPROTONOSUPPORT;
> + SMBDIRECT_FN_COMMENT(sk, "No userspace sockets");
> + return -EPROTONOSUPPORT;
> + }
> +
> + ret = smbdirect_socket_init_new(sock_net(sk), sc);
> + if (ret)
> + goto socket_init_failed;
> + /*
> + * smbdirect_socket_init_new() called smbdirect_socket_init() again,
> + * 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 != orig_sk_destruct);
> + WARN_ON_ONCE(sk->sk_destruct != smbdirect_sk_destruct);
> +
> + ret = smbdirect_socket_set_initial_parameters(sc, sp);
> + if (ret)
> + goto set_params_failed;
> +
> + ret = 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 = ret;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static void smbdirect_sk_destroy(struct sock *sk)
> +{
> + struct smbdirect_socket *sc = 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 != 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 = ntohs(((struct sockaddr_in *)uaddr)->sin_port);
> +
> + return BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr_len,
> + CGROUP_INET4_BIND,
> + 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) instead 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 = ntohs(((struct sockaddr_in6 *)uaddr)->sin6_port);
> +
> + return BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, addr_len,
> + CGROUP_INET6_BIND,
> + 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 = smbdirect_socket_from_sk(sk);
> + bool is_any_addr = 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 = smbdirect_socket_bind(sc, (struct sockaddr *)uaddr);
> + if (ret) {
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> +
> + ret = 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 = BPF_CGROUP_RUN_PROG_INET4_POST_BIND(sk);
> + if (ret) {
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> + break;
> +
> + case AF_INET6:
> + ret = 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 |= SOCK_BINDADDR_LOCK;
> + if (port)
> + sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
Can this socket be passed to SOCK_BINDPORT_LOCK user,
inet_bhash2_reset_saddr(), inet_sk_rebuild_header() ?
> +
> + ret = 0;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static int smbdirect_sk_bind(struct sock *sk, struct sockaddr_unsized *addr, int addr_len)
> +{
> + struct net *net = sock_net(sk);
> + u32 flags = BIND_WITH_LOCK;
> + u16 port = 0;
> + u16 check_port = 0;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + ret = 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 == 5445)
> + check_port = 445;
> + else
> + check_port = 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 = -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 = 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_accept_arg *arg)
> +{
> + struct smbdirect_socket *lsc = smbdirect_socket_from_sk(lsk);
> + long timeo = 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 = smbdirect_socket_accept(lsc, timeo, arg);
> + release_sock(lsk);
> + if (!nsc) {
> + SMBDIRECT_FN_RETURN_INT(lsk, arg->err);
> + return NULL;
> + }
> + nsk = &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_unsized *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 != 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) instead 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 = smbdirect_socket_from_sk(sk);
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is already locked */
> + sock_owned_by_me(sk);
> +
> + ret = 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 optname,
> + sockptr_t optval, unsigned int optlen)
> +{
> + struct smbdirect_socket *sc = 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=%d optname=%d for sk=%p\n",
> + level, optname, sk);
> + ret = -EOPNOTSUPP;
> + break;
> + }
> +
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static int smbdirect_sk_getsockopt(struct sock *sk, int level, int optname,
> + char __user *optval, int __user *optlen)
> +{
> + struct smbdirect_socket *sc = 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=%d optname=%d for sk=%p\n",
> + level, optname, sk);
> + ret = -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 = 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=%d (0x%x) for sk=%p\n",
> + cmd, cmd, sk);
> + ret = -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 = (struct msghdr *)(uintptr_t)(const void *)_msg;
> + struct cmsghdr *cmsg = NULL;
> + size_t count = 0;
> +
> + if (first_sol_smbdirect_type != NULL)
> + *first_sol_smbdirect_type = -1;
> +
> + for (cmsg = CMSG_FIRSTHDR(msg);
> + cmsg != NULL;
> + cmsg = CMSG_NXTHDR(msg, cmsg)) {
> + count++;
> + if (cmsg->cmsg_level != SOL_SMBDIRECT)
> + continue;
> + if (first_sol_smbdirect_type != NULL) {
> + *first_sol_smbdirect_type = cmsg->cmsg_type;
> + first_sol_smbdirect_type = 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 = (struct msghdr *)(uintptr_t)(const void *)_msg;
> + size_t cmsg_len_min = CMSG_LEN(payloadmin);
> + size_t cmsg_len_max = CMSG_LEN(payloadmax);
> + const size_t cmsg_len_hdr = CMSG_LEN(0);
> + uint8_t *payload = (uint8_t *)_payload;
> + struct cmsghdr *cmsg = 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 = CMSG_FIRSTHDR(msg);
> + cmsg != NULL;
> + cmsg = CMSG_NXTHDR(msg, cmsg)) {
> + if (cmsg->cmsg_level != SOL_SMBDIRECT)
> + continue;
> +
> + if (cmsg->cmsg_type != cmsg_type)
> + continue;
> +
> + if (cmsg->cmsg_len < cmsg_len_min)
> + return -EBADMSG;
> +
> + if (cmsg->cmsg_len > cmsg_len_max)
> + return -EMSGSIZE;
> +
> + payloadlen = cmsg->cmsg_len - cmsg_len_hdr;
> + if (payloadlen > 0)
> + memcpy(payload, CMSG_DATA(cmsg), payloadlen);
> + if (payloadlen < payloadmax)
> + memset(payload + payloadlen, 0, payloadmax - payloadlen);
> + 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 = {
> + .remote_token = 0,
> + };
> + ssize_t ret;
> +
> + ret = __smbdirect_cmsg_extract(msg,
> + SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE,
> + &args, sizeof(args), sizeof(args));
> + if (ret < 0)
> + return ret;
> +
> + *remote_token = args.remote_token;
> + return 0;
> +}
> +
> +static int smbdirect_sk_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t msg_len)
> +{
> + struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);
> + struct iov_iter *iter = &msg->msg_iter;
> + unsigned int flags = msg->msg_flags;
> + size_t cmsg_count = 0;
> + int cmsg_type = -1;
> + bool need_invalidate = false;
> + u32 remote_key = 0;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is already locked */
> + sock_owned_by_me(sk);
> +
> + cmsg_count = smbdirect_cmsg_count(msg, &cmsg_type);
> + if (cmsg_count > 1) {
> + ret = -EINVAL;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> +
> + if (flags & ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL)) {
> + ret = -EINVAL;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> +
> + if (cmsg_type == SMBDIRECT_BUFFER_REMOTE_INVALIDATE_CMSG_TYPE) {
> + ret = smbdirect_buffer_remote_invalidate_cmsg_extract(msg, &remote_key);
> + if (!ret)
> + need_invalidate = true; /* remote_key is valid */
> + else if (ret != -ENOMSG) {
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> + } else if (cmsg_count) {
> + ret = -EINVAL;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> +
> + if (WARN_ON_ONCE(iov_iter_rw(iter) != ITER_SOURCE)) {
> + ret = -EINVAL;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> +
> + if (WARN_ON_ONCE(iov_iter_count(iter) != msg_len)) {
> + ret = -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) == 0) {
> + ret = -EAGAIN;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> + }
> + flags &= ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL);
> +
> + ret = smbdirect_connection_send_iter(sc,
> + iter,
> + flags,
> + need_invalidate,
> + remote_key);
> + if (ret < 0)
> + /* Handle error and possibly send SIGPIPE. */
> + ret = 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, size_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 = 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, size_t len, int flags)
> +{
> + struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);
> + struct iov_iter *iter = &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 = -EINVAL;
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> +
> + if (WARN_ON_ONCE(iov_iter_rw(iter) != ITER_DEST)) {
> + ret = -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) != len)) {
> + ret = -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.data_length == 0) {
> + ret = -EAGAIN;
> + release_sock(sk);
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> + }
> + }
> + flags &= ~(MSG_DONTWAIT|MSG_WAITALL|MSG_NOSIGNAL);
> + ret = smbdirect_connection_recvmsg(sc, msg, flags);
> + if (msg->msg_get_inq && ret >= 0)
> + msg->msg_inq = 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 = 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 = 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 >= 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 = {
> + .name = "smbdirect",
> + .owner = THIS_MODULE,
> + .obj_size = sizeof(struct smbdirect_socket),
> + .ipv6_pinfo_offset = offsetof(struct smbdirect_socket, inet6),
> + .init = smbdirect_sk_init,
> + .destroy = smbdirect_sk_destroy,
> + .hash = smbdirect_sk_hash,
> + .unhash = smbdirect_sk_unhash,
> + .release_cb = smbdirect_sk_release_cb,
> + .bind = smbdirect_sk_bind,
> + .accept = smbdirect_sk_accept,
> + .pre_connect = smbdirect_sk_pre_connect,
> + .connect = smbdirect_sk_connect,
> + .setsockopt = smbdirect_sk_setsockopt,
> + .getsockopt = smbdirect_sk_getsockopt,
> + .ioctl = smbdirect_sk_ioctl,
> + .sendmsg = smbdirect_sk_sendmsg,
> + .recvmsg = smbdirect_sk_recvmsg,
> + .shutdown = smbdirect_sk_shutdown,
> + .disconnect = smbdirect_sk_disconnect,
> + .close = smbdirect_sk_close,
> + .sockets_allocated = &smbdirect_sockets_allocated,
> +};
> +
> +static int smbdirect_sock_release(struct socket *sock)
> +{
> + struct sock *sk = 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 = 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 = inet6_release(sock);
> +#else
> + ret = -EAFNOSUPPORT;
> +#endif
> + break;
> + default:
> + ret = -EAFNOSUPPORT;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int smbdirect_sock_bind(struct socket *sock, struct sockaddr_unsized *saddr, int len)
> +{
> + struct sock *sk = 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 = 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 = inet6_bind(sock, saddr, len);
> +#else
> + ret = -EAFNOSUPPORT;
> +#endif
> + break;
> + default:
> + ret = -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 = sock->sk;
> + struct smbdirect_socket *sc = 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 != 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) instead 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 == SMBDIRECT_SOCKET_CONNECTED)
> + return -EISCONN;
> +
> + if (sc->status != SMBDIRECT_SOCKET_CREATED)
> + return -EINVAL;
> +
> + if (BPF_CGROUP_PRE_CONNECT_ENABLED(sk)) {
> + ret = sk->sk_prot->pre_connect(sk, uaddr, addr_len);
> + if (ret)
> + return ret;
> + }
> +
> + ret = sk->sk_prot->connect(sk, uaddr, addr_len);
> + if (ret < 0)
> + return ret;
> +
> + inet_sk_set_state(sk, TCP_SYN_SENT);
> + sock->state = SS_CONNECTING;
> +
> + if (flags & O_NONBLOCK)
> + return -EINPROGRESS;
> +
> + ret = smbdirect_connection_wait_for_connected(sc);
> + if (ret)
> + goto sock_error;
> +
> + return 0;
> +
> +sock_error:
> + sock->state = SS_UNCONNECTED;
> + sk->sk_disconnects++;
> + if (sk->sk_prot->disconnect(sk, flags))
> + sock->state = SS_DISCONNECTING;
> + return ret;
> +}
> +
> +static int smbdirect_sock_connect(struct socket *sock,
> + struct sockaddr_unsized *uaddr,
> + int addr_len, int flags)
> +{
> + struct sock *sk = sock->sk;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + lock_sock(sk);
> + ret = smbdirect_sock_connect_locked(sock, uaddr, addr_len, flags);
> + release_sock(sk);
> +
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static int smbdirect_sock_listen(struct socket *sock, int backlog)
> +{
> + struct sock *sk = sock->sk;
> + struct smbdirect_socket *sc = 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 = 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 *nsock,
> + struct proto_accept_arg *arg)
> +{
> + struct sock *lsk = lsock->sk;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(lsk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(lsk);
> +
> + ret = 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 = 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 = inet_getname(sock, uaddr, peer);
> + break;
> + case AF_INET6:
> +#if IS_ENABLED(CONFIG_IPV6)
> + ret = inet6_getname(sock, uaddr, peer);
> +#else
> + ret = -EAFNOSUPPORT;
> +#endif
> + break;
> + default:
> + ret = -EAFNOSUPPORT;
> + break;
> + }
> +
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static __poll_t smbdirect_sock_poll(struct file *file, struct socket *sock, poll_table *wait)
> +{
> + struct sock *sk = sock->sk;
> + struct smbdirect_socket *sc = smbdirect_socket_from_sk(sk);
> + __poll_t mask = 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 == SMBDIRECT_SOCKET_LISTENING) {
> + if (!list_empty_careful(&sc->listen.ready))
> + mask |= 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 |= EPOLLHUP;
> + mask |= EPOLLERR;
> + mask |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP;
> + mask |= EPOLLOUT | EPOLLWRNORM;
> + SMBDIRECT_FN_RETURN_POLL(sk, mask);
> + return mask;
> + }
> +
> + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
> + /*
> + * A just created socket.
> + */
> + SMBDIRECT_FN_RETURN_POLL(sk, mask);
> + return mask;
> + }
> +
> + if (sc->recv_io.reassembly.data_length > 0)
> + mask |= 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 |= 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 |= EPOLLOUT | EPOLLWRNORM;
> + }
> +
> + SMBDIRECT_FN_RETURN_POLL(sk, mask);
> + return mask;
> +}
> +
> +static int smbdirect_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
> +{
> + struct sock *sk = 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 == SOCK_RAW, in case we ever
> + * implement SOCK_RAW...
> + *
> + * It calls smbdirect_sk_ioctl()...
> + */
> + ret = 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 = sock->sk;
> + int ret = 0;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + /*
> + * We have these from userspace:
> + * SHUT_RD = 0, SHUT_WR = 1 and SHUT_RDWR = 2
> + *
> + * And we map them to SHUTDOWN_MASK = 3
> + * RCV_SHUTDOWN = 1, SEND_SHUTDOWN = 2, BOTH = 3
> + */
> + how++;
> + if ((how & ~SHUTDOWN_MASK) || !how) /* MAXINT->0 */
> + return -EINVAL;
> +
> + lock_sock(sk);
> +
> + switch (sk->sk_state) {
> + case TCP_CLOSE:
> + ret = -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 = 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 optlen)
> +{
> + struct sock *sk = sock->sk;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + ret = sock_common_setsockopt(sock, level, optname, optval, optlen);
> +
> + 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 *optlen)
> +{
> + struct sock *sk = sock->sk;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + ret = sock_common_getsockopt(sock, level, optname, optval, optlen);
> +
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static int smbdirect_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
> +{
> + struct sock *sk = sock->sk;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + ret = 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 *msg, size_t size,
> + int flags)
> +{
> + struct sock *sk = sock->sk;
> + int ret;
> +
> + SMBDIRECT_FN_CALLED(sk);
> +
> + /* assert it is not already locked */
> + sock_not_owned_by_me(sk);
> +
> + ret = sock_common_recvmsg(sock, msg, size, flags);
> +
> + SMBDIRECT_FN_RETURN_INT(sk, ret);
> + return ret;
> +}
> +
> +static const struct proto_ops smbdirect_inet_proto_ops = {
> + .family = PF_INET,
> + .owner = THIS_MODULE,
> + .release = smbdirect_sock_release,
> + .bind = smbdirect_sock_bind,
> + .connect = smbdirect_sock_connect,
> + .socketpair = sock_no_socketpair,
> + .listen = smbdirect_sock_listen,
> + .accept = smbdirect_sock_accept,
> + .getname = smbdirect_sock_getname,
> + .poll = smbdirect_sock_poll,
> + .ioctl = smbdirect_sock_ioctl,
> + .shutdown = smbdirect_sock_shutdown,
> + .setsockopt = smbdirect_sock_setsockopt,
> + .getsockopt = smbdirect_sock_getsockopt,
> + .sendmsg = smbdirect_sock_sendmsg,
> + .sendmsg_locked = smbdirect_sk_sendmsg_locked,
> + .recvmsg = smbdirect_sock_recvmsg,
> + .mmap = sock_no_mmap,
> +};
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +static const struct proto_ops smbdirect_inet6_proto_ops = {
> + .family = PF_INET6,
> + .owner = THIS_MODULE,
> + .release = smbdirect_sock_release,
> + .bind = smbdirect_sock_bind,
> + .connect = smbdirect_sock_connect,
> + .socketpair = sock_no_socketpair,
> + .listen = smbdirect_sock_listen,
> + .accept = smbdirect_sock_accept,
> + .getname = smbdirect_sock_getname,
> + .poll = smbdirect_sock_poll,
> + .ioctl = smbdirect_sock_ioctl,
> + .shutdown = smbdirect_sock_shutdown,
> + .setsockopt = smbdirect_sock_setsockopt,
> + .getsockopt = smbdirect_sock_getsockopt,
> + .sendmsg = smbdirect_sock_sendmsg,
> + .sendmsg_locked = smbdirect_sk_sendmsg_locked,
> + .recvmsg = smbdirect_sock_recvmsg,
> + .mmap = sock_no_mmap,
> +};
> +#endif
> +
> +static struct inet_protosw smbdirect_inet_stream_protosw = {
> + .type = SOCK_STREAM,
> + .protocol = IPPROTO_SMBDIRECT,
> + .prot = &smbdirect_prot,
> + .ops = &smbdirect_inet_proto_ops,
> +};
> +
> +#if IS_ENABLED(CONFIG_IPV6)
> +static struct inet_protosw smbdirect_inet6_stream_protosw = {
> + .type = SOCK_STREAM,
> + .protocol = IPPROTO_SMBDIRECT,
> + .prot = &smbdirect_prot,
> + .ops = &smbdirect_inet6_proto_ops,
> +};
> +#endif
> +
> +struct smbdirect_socket *smbdirect_socket_from_sock(const struct socket *sock)
> +{
> + if (!sock ||
> + !sock->sk ||
> + sock->sk->sk_protocol != IPPROTO_SMBDIRECT)
> + return NULL;
> +
> + if (WARN_ON_ONCE(sock->sk->sk_destruct != 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 = 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 = percpu_counter_init(&smbdirect_sockets_allocated, 0, GFP_KERNEL);
> + if (err)
> + goto err_percpu_counter;
> +
> + err = 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 */, SOCK_STREAM);
> +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 257 /* IPPROTO_SMBDIRECT */, SOCK_STREAM);
> diff --git a/fs/smb/common/smbdirect/smbdirect_public.h b/fs/smb/common/smbdirect/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 smbdirect_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(struct smbdirect_socket *sc,
> unsigned int rdma_readwrite_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/smbdirect/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(struct 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_len,
> + struct smbdirect_buffer_descriptor_v1 *desc,
> + size_t desc_len,
> + bool is_read)
> {
> const struct smbdirect_socket_parameters *sp = &sc->parameters;
> enum dma_data_direction direction = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> @@ -123,6 +123,8 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,
> int credits_needed;
> size_t desc_buf_len, desc_num = 0;
>
> + smbdirect_socket_sk_owned_by_me(sc);
> +
> if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
> return -ENOTCONN;
>
> @@ -235,7 +237,9 @@ int smbdirect_connection_rdma_xmit(struct smbdirect_socket *sc,
> }
>
> msg = 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 = 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 = smbdirect_connection_rdma_xmit_locked(sc, buf, buf_len, desc, 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/smbdirect/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 smbdirect_socket *sc,
> sc->send_io.mem.gfp_mask = gfp_mask;
> sc->recv_io.mem.gfp_mask = gfp_mask;
> sc->rw_io.mem.gfp_mask = gfp_mask;
> + sc->sk.sk_allocation = 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 = &sc->sk;
> + const struct sockaddr_storage *saddr;
> + const struct sockaddr_in *sin;
> + const struct sockaddr_in6 *sin6;
> + struct in_addr sin_addr = { .s_addr = htonl(INADDR_ANY), };
> + struct in6_addr sin6_addr = in6addr_any;
> + __be32 sin6_flowinfo = 0;
> + bool is_any_addr = true;
> + u16 sport = 0;
> + int ret;
> +
> + saddr = &sc->rdma.cm_id->route.addr.src_addr;
> +
> + if (WARN_ON_ONCE(saddr->ss_family != sk->sk_family)) {
> + ret = -EINVAL;
> + return ret;
> + }
> +
> + switch (saddr->ss_family) {
> + case AF_INET:
> + sin = (struct sockaddr_in *)saddr;
> + sport = ntohs(sin->sin_port);
> + sin_addr = sin->sin_addr;
> + is_any_addr = (sin_addr.s_addr == htonl(INADDR_ANY));
> + break;
> +
> + case AF_INET6:
> + sin6 = (struct sockaddr_in6 *)saddr;
> + sport = ntohs(sin6->sin6_port);
> + sin_addr.s_addr = LOOPBACK4_IPV6;
> + sin6_addr = sin6->sin6_addr;
> + is_any_addr = ipv6_addr_any(&sin6_addr);
> + sin6_flowinfo = sin6->sin6_flowinfo;
> + break;
> + }
> +
> + sk->sk_bound_dev_if = sc->rdma.cm_id->route.addr.dev_addr.bound_dev_if;
> + sk->sk_rcv_saddr = sc->inet.inet_saddr = sin_addr.s_addr;
> +#if IS_ENABLED(CONFIG_IPV6)
> + sk->sk_v6_rcv_saddr = sc->inet6.saddr = sin6_addr;
> +#else
> + sc->inet6.saddr = sin6_addr;
> +#endif
> + sc->inet6.flow_label = sin6_flowinfo;
> + sk->sk_num = sport;
> + sc->inet.inet_sport = htons(sport);
> +
> + if (_is_any_addr)
> + *_is_any_addr = is_any_addr;
> + return 0;
> +}
> +
> +int smbdirect_socket_sync_daddr_to_sk(struct smbdirect_socket *sc)
> +{
> + struct sock *sk = &sc->sk;
> + const struct sockaddr_storage *daddr;
> + const struct sockaddr_in *sin;
> + const struct sockaddr_in6 *sin6;
> + struct in_addr sin_addr = { .s_addr = htonl(INADDR_ANY), };
> +#if IS_ENABLED(CONFIG_IPV6)
> + struct in6_addr sin6_addr = in6addr_any;
> +#endif
> + u16 dport = 0;
> + int ret;
> +
> + daddr = &sc->rdma.cm_id->route.addr.dst_addr;
> +
> + if (WARN_ON_ONCE(daddr->ss_family != sk->sk_family)) {
> + ret = -EINVAL;
> + return ret;
> + }
> +
> + switch (daddr->ss_family) {
> + case AF_INET:
> + sin = (struct sockaddr_in *)daddr;
> + dport = ntohs(sin->sin_port);
> + sin_addr = sin->sin_addr;
> + break;
> +
> + case AF_INET6:
> + sin6 = (struct sockaddr_in6 *)daddr;
> + dport = ntohs(sin6->sin6_port);
> + sin_addr.s_addr = LOOPBACK4_IPV6;
> +#if IS_ENABLED(CONFIG_IPV6)
> + sin6_addr = sin6->sin6_addr;
> +#endif
> + break;
> + }
> +
> + sk->sk_daddr = sc->inet.inet_daddr = sin_addr.s_addr;
> +#if IS_ENABLED(CONFIG_IPV6)
> + sk->sk_v6_daddr = sin6_addr;
> +#endif
> + sk->sk_dport = sc->inet.inet_dport = 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 smbdirect_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 = &sc->sk;
> +
> + WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK);
> +
> + WARN_ON_ONCE(sc->first_error == 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 >= SMBDIRECT_SOCKET_DISCONNECTED) {
> + inet_sk_set_state(sk, TCP_CLOSE);
> + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)
> + sk->sk_socket->state = SS_UNCONNECTED;
> + } else {
> + inet_sk_set_state(sk, TCP_CLOSING);
> + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_socket)
> + sk->sk_socket->state = 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 smbdirect_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_socket *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_socket *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_socket *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 == SMBDIRECT_SOCKET_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 smbdirect_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 smbdirect_socket *sc,
> return 0;
>
> atomic_add(needed, total_credits);
> +
> + smbdirect_socket_sk_unlock(sc);
> ret = wait_event_interruptible(*waitq,
> atomic_read(total_credits) >= needed ||
> sc->status != expected_status);
> + smbdirect_socket_sk_lock(sc);
>
> if (sc->status != expected_status)
> return unexpected_errno;
> diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/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, fmt, ##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_socket *sc)
> {
> + const size_t status_offset = offsetof(struct smbdirect_socket, status);
> +
> /*
> * This also sets status = SMBDIRECT_SOCKET_CREATED
> */
> BUILD_BUG_ON(SMBDIRECT_SOCKET_CREATED != 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(struct 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) != 0);
> + return container_of(sk, struct smbdirect_socket, sk);
> +}
> +
> struct smbdirect_send_io {
> struct smbdirect_socket *socket;
> struct ib_cqe cqe;
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net-next 1/3] psp: add crypt-offset and spi-threshold get/set attributes
From: Jakub Kicinski @ 2026-04-08 1:04 UTC (permalink / raw)
To: Willem de Bruijn
Cc: Akhilesh Samineni, davem, edumazet, pabeni, andrew+netdev, horms,
willemb, daniel.zahka, netdev, linux-kernel,
jayakrishnan.udayavarma, ajit.khaparde, kiran.kella, sachin.suman
In-Reply-To: <willemdebruijn.kernel.1d7f9f774aa55@gmail.com>
On Tue, 07 Apr 2026 17:37:41 -0400 Willem de Bruijn wrote:
> > + if (info->attrs[PSP_A_DEV_CRYPT_OFFSET])
> > + new_config.crypt_offset =
> > + nla_get_u8(info->attrs[PSP_A_DEV_CRYPT_OFFSET]);
>
> PSP defines a 6-bit field in 4 octet units. Does this need bounds checking?
More fundamentally, were we to support this -- is it a device property
or an assoc property?
^ permalink raw reply
* Re: [PATCH net-next 0/3] psp: add crypt-offset and spi-threshold attributes
From: Jakub Kicinski @ 2026-04-08 1:09 UTC (permalink / raw)
To: Akhilesh Samineni
Cc: davem, edumazet, pabeni, andrew+netdev, horms, willemb,
daniel.zahka, netdev, linux-kernel, jayakrishnan.udayavarma,
ajit.khaparde, kiran.kella, sachin.suman
In-Reply-To: <CANQF7iAF6DAMQWv=VSu=nTmb1CyALvVtH8xT2k0CCY0rKk3ggQ@mail.gmail.com>
On Tue, 7 Apr 2026 21:09:38 +0530 Akhilesh Samineni wrote:
> > Please read this document:
> > https://www.kernel.org/doc/html/next/process/maintainer-netdev.html
>
> Thank you for the link. I have reviewed the netdev process documentation.
It is one thing to make an unknowing mistake and another thing
to ignore someone asking you to read the documentation.
Please read the doc top to bottom and tell your entire team to read it.
^ permalink raw reply
* Re: [PATCH net-next 1/2] tcp: rehash onto different ECMP path on retransmit timeout
From: Eric Dumazet @ 2026-04-08 1:09 UTC (permalink / raw)
To: Neil Spring
Cc: netdev, ncardwell, kuniyu, davem, dsahern, kuba, pabeni, horms,
shuah, linux-kselftest, Ido Schimmel
In-Reply-To: <20260408002802.2448424-2-ntspring@meta.com>
On Tue, Apr 7, 2026 at 5:28 PM Neil Spring <ntspring@meta.com> wrote:
>
> Add sk_dst_reset() alongside sk_rethink_txhash() in the RTO, PLB,
> and spurious-retrans paths so that the next transmit triggers a fresh
> route lookup. Propagate sk_txhash into fl6->mp_hash in
> inet6_csk_route_req() and inet6_csk_route_socket() so
> fib6_select_path() uses the socket's current hash for ECMP selection.
>
> The ir_iif update in tcp_check_req() covers both IPv4 and IPv6
> because it was cleaner than gating on address family; IPv4 is
> otherwise unaltered, and not having autoflowlabel in IPv4 means
> I wouldn't expect a new path on timeout.
>
> It is possible that PLB does not need this (that there are other
> methods of reacting to local congestion); I added the sk_dst_reset
> for consistency.
>
> Signed-off-by: Neil Spring <ntspring@meta.com>
> ---
> net/ipv4/tcp_input.c | 4 +++-
> net/ipv4/tcp_minisocks.c | 9 +++++++++
> net/ipv4/tcp_plb.c | 1 +
> net/ipv4/tcp_timer.c | 1 +
> net/ipv6/inet6_connection_sock.c | 8 ++++++++
> 5 files changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index 7171442c3ed7..3d42ab45066c 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -5014,8 +5014,10 @@ static void tcp_rcv_spurious_retrans(struct sock *sk,
> skb->protocol == htons(ETH_P_IPV6) &&
> (tcp_sk(sk)->inet_conn.icsk_ack.lrcv_flowlabel !=
> ntohl(ip6_flowlabel(ipv6_hdr(skb)))) &&
> - sk_rethink_txhash(sk))
> + sk_rethink_txhash(sk)) {
> NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDUPLICATEDATAREHASH);
> + sk_dst_reset(sk);
> + }
>
> /* Save last flowlabel after a spurious retrans. */
> tcp_save_lrcv_flowlabel(sk, skb);
> diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
> index 199f0b579e89..ef4b3771e9d8 100644
> --- a/net/ipv4/tcp_minisocks.c
> +++ b/net/ipv4/tcp_minisocks.c
> @@ -750,6 +750,15 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
> * Reset timer after retransmitting SYNACK, similar to
> * the idea of fast retransmit in recovery.
> */
> +
What is the following part doing?
tcp_v6_init_req() uses something quite different before setting ir_iif
A comment explaining the rationale would be nice.
> +#if IS_ENABLED(CONFIG_IPV6)
> + if (sk->sk_family == AF_INET6)
> + inet_rsk(req)->ir_iif = tcp_v6_iif(skb);
> + else
> +#endif
> + inet_rsk(req)->ir_iif =
> + inet_request_bound_dev_if(sk, skb);
> +
> if (!tcp_oow_rate_limited(sock_net(sk), skb,
> LINUX_MIB_TCPACKSKIPPEDSYNRECV,
> &tcp_rsk(req)->last_oow_ack_time)) {
> diff --git a/net/ipv4/tcp_plb.c b/net/ipv4/tcp_plb.c
> index 68ccdb9a5412..d7cc00a58e53 100644
> --- a/net/ipv4/tcp_plb.c
> +++ b/net/ipv4/tcp_plb.c
> @@ -79,6 +79,7 @@ void tcp_plb_check_rehash(struct sock *sk, struct tcp_plb_state *plb)
> return;
>
> sk_rethink_txhash(sk);
> + sk_dst_reset(sk);
> plb->consec_cong_rounds = 0;
> tcp_sk(sk)->plb_rehash++;
> NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPLBREHASH);
> diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
> index ea99988795e7..acc22fc532c2 100644
> --- a/net/ipv4/tcp_timer.c
> +++ b/net/ipv4/tcp_timer.c
> @@ -299,6 +299,7 @@ static int tcp_write_timeout(struct sock *sk)
> if (sk_rethink_txhash(sk)) {
> tp->timeout_rehash++;
> __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTREHASH);
> + sk_dst_reset(sk);
> }
>
> return 0;
> diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
> index 37534e116899..2fe753bb38b4 100644
> --- a/net/ipv6/inet6_connection_sock.c
> +++ b/net/ipv6/inet6_connection_sock.c
> @@ -48,6 +48,11 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk,
> fl6->flowi6_uid = sk_uid(sk);
> security_req_classify_flow(req, flowi6_to_flowi_common(fl6));
>
> + if (req->num_retrans)
> + fl6->mp_hash = jhash_1word(req->num_retrans,
> + (__force u32)ireq->ir_rmt_port)
> + >> 1;
Why not setting mp_hash to sk_txhash ?
Why are you using ">> 1" ?
rt6_multipath_hash() seems to be bypassed, it might be time to add a
comment there
explaining that mp_hash needs to be 31-bit only...
Perhaps use rt6_multipath_hash() and expand it to use a socket pointer
to retrieve sk->sk_txhash when/if possible
instead of yet another flow dissection.
> +
> if (!dst) {
> dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p);
> if (IS_ERR(dst))
> @@ -70,6 +75,9 @@ struct dst_entry *inet6_csk_route_socket(struct sock *sk,
> fl6->saddr = np->saddr;
> fl6->flowlabel = np->flow_label;
> IP6_ECN_flow_xmit(sk, fl6->flowlabel);
> +
> + if (sk->sk_txhash)
> + fl6->mp_hash = sk->sk_txhash >> 1;
Seems inconsistent, and same question about the right shift.
> fl6->flowi6_oif = sk->sk_bound_dev_if;
> fl6->flowi6_mark = sk->sk_mark;
> fl6->fl6_sport = inet->inet_sport;
> --
> 2.52.0
>
^ permalink raw reply
* Re: [PATCH net] xfrm_user: fix info leak in build_mapping()
From: Jakub Kicinski @ 2026-04-08 1:12 UTC (permalink / raw)
To: Greg Kroah-Hartman, Steffen Klassert
Cc: netdev, linux-kernel, Herbert Xu, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman
In-Reply-To: <2026040745-crafter-awkward-8901@gregkh>
On Tue, 7 Apr 2026 07:51:15 +0200 Greg Kroah-Hartman wrote:
> > > I guess nlmsg_append() would work? It tries to do some zeroing out for
> > > alignment for some reason...
> > >
> > > Want me to do that? I don't have a way to test any of this, I just
> > > found it using some static code analysis tools that looked at holes in
> > > structures.
> >
> > Do you have any more Netlink leaks in the queue? If you do let's do it,
> > if you don't we can wait until the next victi^w patch to arrive.
>
> I do not have any more, sorry. So is it worth it for just these 2?
> Your call :)
These are fine. I would have applied but I think Steffen will take them
via the ipsec tree first (LMK if that's not the plan, Steffen)
^ permalink raw reply
* Re: [PATCH net-next v5 00/10] Decouple receive and transmit enablement in team driver
From: Jakub Kicinski @ 2026-04-08 1:17 UTC (permalink / raw)
To: Marc Harvey, kuniyu
Cc: Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <CANkEMgmeHoJkffx3B+N04buW-V0pHD9c=jajNWafmev6f6K85A@mail.gmail.com>
On Tue, 7 Apr 2026 16:06:02 -0700 Marc Harvey wrote:
> Thank you very much to kuniyu@google.com, who figured out how to
> recreate the issue on Fedora. Fedora's /etc/services maps TCP port
> 1234 to the "search-agent" service (normal), which tcpdump then uses
> to text-replace port numbers in its output. So the tests were looking
> for ${ip_address}.1234, but tcpdump was spitting out
> ${ip_address}.search_agent. What is strange is that the test already
> uses tcpdump's "-n" option: "Don't convert addresses (i.e., host
> addresses, port numbers, etc.) to names."
>
> It turns out that Fedora has a patched version of tcpdump that
> separates the normal "-n" option into two options! "-n" handles host
> addresses, and "-nn" handles port and protocol numbers. The tcpdump
> invocation used by the selftests only uses "-n". What's stranger is
> that passing "-nn" to tcpdump is actually portable, because under the
> hood it is treated as a counter, with or without the Fedora patch:
> https://github.com/the-tcpdump-group/tcpdump/blob/master/tcpdump.c#L1915
> (thanks again to Kuniyuki for discovering this).
Oh wow! Thanks to both of you for not giving up and getting to the
bottom of this :)
^ permalink raw reply
* Re: [PATCH net-next] ipv6: sit: remove redundant ret = 0 assignment
From: Yue Haibing @ 2026-04-08 1:26 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, dsahern, edumazet, pabeni, horms, netdev, linux-kernel
In-Reply-To: <20260406184313.5c446354@kernel.org>
On 2026/4/7 9:43, Jakub Kicinski wrote:
> On Fri, 3 Apr 2026 16:44:02 +0800 Yue Haibing wrote:
>> The variable ret is initialized to 0 when it is defined
>> and is not modified before copy_to_user().
>
> Makes more sense to remove the init during definition IMO.
OK,will do this in v2
^ permalink raw reply
* Re: [PATCH net] net: stmmac: dwmac-motorcomm: fix eFUSE MAC address read failure
From: patchwork-bot+netdevbpf @ 2026-04-08 1:30 UTC (permalink / raw)
To: Johan Alvarado
Cc: me, davem, kuba, pabeni, edumazet, ggo, andrew+netdev, netdev,
linux-kernel
In-Reply-To: <fc5992a4-9532-49c3-8ec1-c2f8c5b84ca1@smtp-relay.sendinblue.com>
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Mon, 6 Apr 2026 07:44:25 +0000 you wrote:
> This patch fixes an issue where reading the MAC address from the eFUSE
> fails due to a race condition.
>
> The root cause was identified by comparing the driver's behavior with a
> custom U-Boot port. In U-Boot, the MAC address was read successfully
> every time because the driver was loaded later in the boot process, giving
> the hardware ample time to initialize. In Linux, reading the eFUSE
> immediately returns all zeros, resulting in a fallback to a random MAC address.
>
> [...]
Here is the summary with links:
- [net] net: stmmac: dwmac-motorcomm: fix eFUSE MAC address read failure
https://git.kernel.org/netdev/net/c/f2777d5cb5c0
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v3] net: sfp: add quirks for Hisense and HSGQ GPON ONT SFP modules
From: patchwork-bot+netdevbpf @ 2026-04-08 1:30 UTC (permalink / raw)
To: John Pavlick
Cc: linux, andrew, hkallweit1, davem, edumazet, kuba, pabeni, netdev,
linux-kernel, marcin.nita
In-Reply-To: <20260406132321.72563-1-jspavlick@posteo.net>
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Mon, 06 Apr 2026 13:23:33 +0000 you wrote:
> Several GPON ONT SFP sticks based on Realtek RTL960x report
> 1000BASE-LX at 1300MBd in their EEPROM but can operate at 2500base-X.
> On hosts capable of 2500base-X (e.g. Banana Pi R3 / MT7986), the
> kernel negotiates only 1G because it trusts the incorrect EEPROM data.
>
> Add quirks for:
> - Hisense-Leox LXT-010S-H
> - Hisense ZNID-GPON-2311NA
> - HSGQ HSGQ-XPON-Stick
>
> [...]
Here is the summary with links:
- [v3] net: sfp: add quirks for Hisense and HSGQ GPON ONT SFP modules
https://git.kernel.org/netdev/net/c/95aca8602ef7
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net v2 1/1] af_unix: read UNIX_DIAG_VFS data under unix_state_lock
From: Kuniyuki Iwashima @ 2026-04-08 1:41 UTC (permalink / raw)
To: Ren Wei
Cc: netdev, davem, edumazet, kuba, pabeni, horms, xemul, yifanwucs,
tomapufckgml, yuantan098, bird, enjou1224z, wangjiexun2025
In-Reply-To: <20260407080015.1744197-1-n05ec@lzu.edu.cn>
On Tue, Apr 7, 2026 at 1:00 AM Ren Wei <n05ec@lzu.edu.cn> wrote:
>
> From: Jiexun Wang <wangjiexun2025@gmail.com>
>
> Exact UNIX diag lookups hold a reference to the socket, but not to
> u->path. Meanwhile, unix_release_sock() clears u->path under
> unix_state_lock() and drops the path reference after unlocking.
>
> Read the inode and device numbers for UNIX_DIAG_VFS while holding
> unix_state_lock(), then emit the netlink attribute after dropping the
> lock.
>
> This keeps the VFS data stable while the reply is being built.
>
> Fixes: 5f7b0569460b ("unix_diag: Unix inode info NLA")
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Co-developed-by: Yuan Tan <yuantan098@gmail.com>
> Signed-off-by: Yuan Tan <yuantan098@gmail.com>
> Suggested-by: Xin Liu <bird@lzu.edu.cn>
> Tested-by: Ren Wei <enjou1224z@gmail.com>
> Signed-off-by: Jiexun Wang <wangjiexun2025@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Looks good, thanks !
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Next time your team post patches, please make sure
to follow this guideline:
Documentation/process/maintainer-netdev.rst
(e.g. post a next revision as a separate thread, etc)
> ---
> Changes in v2:
> - reorder local variables in reverse xmas tree order
>
> net/unix/diag.c | 21 +++++++++++++--------
> 1 file changed, 13 insertions(+), 8 deletions(-)
>
> diff --git a/net/unix/diag.c b/net/unix/diag.c
> index ca3473026151..c9c1e51c4419 100644
> --- a/net/unix/diag.c
> +++ b/net/unix/diag.c
> @@ -28,18 +28,23 @@ static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb)
>
> static int sk_diag_dump_vfs(struct sock *sk, struct sk_buff *nlskb)
> {
> - struct dentry *dentry = unix_sk(sk)->path.dentry;
> + struct unix_diag_vfs uv;
> + struct dentry *dentry;
> + bool have_vfs = false;
>
> + unix_state_lock(sk);
> + dentry = unix_sk(sk)->path.dentry;
> if (dentry) {
> - struct unix_diag_vfs uv = {
> - .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
> - .udiag_vfs_dev = dentry->d_sb->s_dev,
> - };
> -
> - return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
> + uv.udiag_vfs_ino = d_backing_inode(dentry)->i_ino;
> + uv.udiag_vfs_dev = dentry->d_sb->s_dev;
> + have_vfs = true;
> }
> + unix_state_unlock(sk);
>
> - return 0;
> + if (!have_vfs)
> + return 0;
> +
> + return nla_put(nlskb, UNIX_DIAG_VFS, sizeof(uv), &uv);
> }
>
> static int sk_diag_dump_peer(struct sock *sk, struct sk_buff *nlskb)
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH v3 net-next 00/15] ip6mr: No RTNL for RTNL_FAMILY_IP6MR rtnetlink.
From: Jakub Kicinski @ 2026-04-08 1:42 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: David S . Miller, David Ahern, Eric Dumazet, Paolo Abeni,
Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260407212001.2368593-1-kuniyu@google.com>
On Tue, 7 Apr 2026 21:19:33 +0000 Kuniyuki Iwashima wrote:
> This series is the IPv6 version of
>
> https://lore.kernel.org/netdev/20260228221800.1082070-1-kuniyu@google.com/
>
> and removes RTNL from ip6mr rtnetlink handlers.
CI says:
[ 857.648411][ T63] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000006: 0000 [#1] SMP KASAN
[ 857.648639][ T63] KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037]
[ 857.648765][ T63] CPU: 2 UID: 0 PID: 63 Comm: kworker/2:2 Not tainted 7.0.0-rc6-virtme #1 PREEMPT(full)
[ 857.648903][ T63] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 857.649006][ T63] Workqueue: mld mld_ifc_work
[ 857.649090][ T63] RIP: 0010:mroute6_is_socket+0x4a/0x80
[ 857.649172][ T63] Code: df 48 89 f9 48 c1 e9 03 80 3c 11 00 75 2f 48 8b 80 e8 10 00 00 48 ba 00 00 00 00 00 fc ff df 48 8d 78 30 48 89 f9 48 c1 e9 03 <80> 3c 11 00 75 1c 48 83 78 30 00 0f 95 c0 48 83 c4 08 c3 48 89 04
[ 857.649439][ T63] RSP: 0018:ffa00000004478b0 EFLAGS: 00010216
[ 857.649533][ T63] RAX: 0000000000000000 RBX: ff1100000e2ae200 RCX: 0000000000000006
[ 857.649645][ T63] RDX: dffffc0000000000 RSI: ff1100000e2ae200 RDI: 0000000000000030
[ 857.649755][ T63] RBP: ff110000077d0900 R08: ffffffffa63b8550 R09: 1fe2200002937e85
[ 857.649863][ T63] R10: ff11000010736898 R11: ff11000008ab40b0 R12: 1ff4000000088f20
[ 857.649974][ T63] R13: ff11000008ab4000 R14: ff11000008c78800 R15: ff11000010736880
[ 857.650083][ T63] FS: 0000000000000000(0000) GS:ff110000c2de4000(0000) knlGS:0000000000000000
[ 857.650213][ T63] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 857.650307][ T63] CR2: 00007f1dc25d2574 CR3: 000000001c74f002 CR4: 0000000000771ef0
[ 857.650421][ T63] PKRU: 55555554
[ 857.650480][ T63] Call Trace:
[ 857.650536][ T63] <TASK>
[ 857.650575][ T63] ip6_finish_output2+0xa7f/0x1310
[ 857.650653][ T63] ? ip6_xmit+0x2000/0x2000
[ 857.650724][ T63] ? find_held_lock+0x2b/0x80
[ 857.650800][ T63] ? __lock_release.isra.0+0x6b/0x1a0
[ 857.650873][ T63] ? ip6_mtu+0x174/0x410
[ 857.650929][ T63] ip6_finish_output+0x701/0xe80
[ 857.651002][ T63] ip6_output+0x23f/0x7f0
[ 857.651056][ T63] ? ip6_finish_output+0xe80/0xe80
[ 857.651128][ T63] ? __lock_release.isra.0+0x6b/0x1a0
[ 857.651201][ T63] ? xfrm_bundle_lookup.constprop.0+0xba0/0xba0
[ 857.651296][ T63] ? mark_held_locks+0x40/0x70
[ 857.651369][ T63] ? __local_bh_enable_ip+0xa5/0x140
[ 857.651443][ T63] ? __local_bh_enable_ip+0xa5/0x140
[ 857.651516][ T63] ? icmp6_dst_alloc+0x317/0x4d0
[ 857.651589][ T63] mld_sendpack+0x9d6/0xec0
[ 857.651661][ T63] ? nf_hook.constprop.0+0x340/0x340
[ 857.651734][ T63] ? mld_send_cr+0x50f/0x820
[ 857.651808][ T63] mld_ifc_work+0x36/0x190
[ 857.651880][ T63] ? process_one_work+0xdb4/0x1410
[ 857.651954][ T63] process_one_work+0xdf5/0x1410
[ 857.652028][ T63] ? pwq_dec_nr_in_flight+0x710/0x710
[ 857.652100][ T63] ? lock_acquire.part.0+0xbc/0x260
[ 857.652172][ T63] worker_thread+0x4f1/0xd60
[ 857.652244][ T63] ? rescuer_thread+0x1320/0x1320
[ 857.652318][ T63] ? __kthread_parkme+0xbd/0x210
[ 857.652391][ T63] ? rescuer_thread+0x1320/0x1320
[ 857.652495][ T63] kthread+0x364/0x460
[ 857.652550][ T63] ? trace_irq_enable.constprop.0+0x9b/0x180
[ 857.652641][ T63] ? kthread_affine_node+0x330/0x330
[ 857.652714][ T63] ret_from_fork+0x474/0x6b0
[ 857.652787][ T63] ? arch_exit_to_user_mode_prepare.isra.0+0x140/0x140
[ 857.652877][ T63] ? __switch_to+0x540/0xd10
[ 857.652952][ T63] ? kthread_affine_node+0x330/0x330
[ 857.653025][ T63] ret_from_fork_asm+0x11/0x20
[ 857.653102][ T63] </TASK>
[ 857.653157][ T63] Modules linked in: ip6_gre ip_gre gre act_gact cls_matchall sch_ingress ipt_REJECT nf_reject_ipv4 nft_compat nf_tables
[ 857.653387][ T63] ---[ end trace 0000000000000000 ]---
[ 857.653511][ T63] RIP: 0010:mroute6_is_socket+0x4a/0x80
[ 857.653630][ T63] Code: df 48 89 f9 48 c1 e9 03 80 3c 11 00 75 2f 48 8b 80 e8 10 00 00 48 ba 00 00 00 00 00 fc ff df 48 8d 78 30 48 89 f9 48 c1 e9 03 <80> 3c 11 00 75 1c 48 83 78 30 00 0f 95 c0 48 83 c4 08 c3 48 89 04
[ 857.653928][ T63] RSP: 0018:ffa00000004478b0 EFLAGS: 00010216
[ 857.654063][ T63] RAX: 0000000000000000 RBX: ff1100000e2ae200 RCX: 0000000000000006
[ 857.654267][ T63] RDX: dffffc0000000000 RSI: ff1100000e2ae200 RDI: 0000000000000030
[ 857.654422][ T63] RBP: ff110000077d0900 R08: ffffffffa63b8550 R09: 1fe2200002937e85
[ 857.654579][ T63] R10: ff11000010736898 R11: ff11000008ab40b0 R12: 1ff4000000088f20
[ 857.654728][ T63] R13: ff11000008ab4000 R14: ff11000008c78800 R15: ff11000010736880
[ 857.654876][ T63] FS: 0000000000000000(0000) GS:ff110000c2de4000(0000) knlGS:0000000000000000
[ 857.655046][ T63] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 857.655189][ T63] CR2: 00007f1dc25d2574 CR3: 000000001c74f002 CR4: 0000000000771ef0
[ 857.655340][ T63] PKRU: 55555554
[ 857.655470][ T63] Kernel panic - not syncing: Fatal exception
[ 857.655657][ T63] Kernel Offset: 0x23000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[ 857.655864][ T63] ---[ end Kernel panic - not syncing: Fatal exception ]---
^ permalink raw reply
* Re: [PATCH net-next v5 0/2] net: mana: add ethtool private flag for full-page RX buffers
From: Jakub Kicinski @ 2026-04-08 1:51 UTC (permalink / raw)
To: Alexander Lobakin
Cc: Dipayaan Roy, kys, haiyangz, wei.liu, decui, andrew+netdev, davem,
edumazet, pabeni, leon, longli, kotaranov, horms, shradhagupta,
ssengar, ernis, shirazsaleem, linux-hyperv, netdev, linux-kernel,
linux-rdma, stephen, jacob.e.keller, dipayanroy, leitao, kees
In-Reply-To: <e80b603d-8be0-4aee-8a31-c9cbb4a8ab00@intel.com>
On Tue, 7 Apr 2026 15:10:45 +0200 Alexander Lobakin wrote:
> > On some ARM64 platforms with 4K PAGE_SIZE, utilizing page_pool
> > fragments for allocation in the RX refill path (~2kB buffer per fragment)
> > causes 15-20% throughput regression under high connection counts
> > (>16 TCP streams at 180+ Gbps). Using full-page buffers on these
> > platforms shows no regression and restores line-rate performance.
> >
> > This behavior is observed on a single platform; other platforms
> > perform better with page_pool fragments, indicating this is not a
> > page_pool issue but platform-specific.
> >
> > This series adds an ethtool private flag "full-page-rx" to let the
> > user opt in to one RX buffer per page:
> >
> > ethtool --set-priv-flags eth0 full-page-rx on
>
> Sorry I may've missed the previous threads.
>
> Has this approach been discussed here? Private flags are generally
> discouraged.
>
> Alternatively, you can provide Ethtool ops to change the Rx buffer size,
> so that you'd be able to set it to PAGE_SIZE on affected platforms and
> the result would be the same.
Actually, hm. Now that you spoke up I wonder how much this is
an inherent ARM problem vs problem in whatever ARM Microsoft's
management empire-built themselves into.
Do you have access to any ARM servers? Google says GCP offers ARM
instances with idpf NICs. So if idpf benefits from the same
"tuning" we should totally push for a proper API not priv flags.
^ permalink raw reply
* [PATCH net-next 2/2] pppox: convert pppox_sk() to use container_of()
From: Qingfang Deng @ 2026-04-08 1:51 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, netdev, linux-kernel
Cc: linux-ppp, Qingfang Deng
In-Reply-To: <20260408015138.280687-1-qingfang.deng@linux.dev>
Use container_of() macro instead of direct pointer casting to get the
pppox_sock from a sock pointer. This improves type safety and removes
the requirement that sk must be the first struct member.
Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
include/linux/if_pppox.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index 636772693f9a..4f8d9e02cd7f 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -38,7 +38,6 @@ struct pptp_opt {
#include <net/sock.h>
struct pppox_sock {
- /* struct sock must be the first member of pppox_sock */
struct sock sk;
struct ppp_channel chan;
struct pppox_sock __rcu *next; /* for hash table */
@@ -54,7 +53,7 @@ struct pppox_sock {
static inline struct pppox_sock *pppox_sk(struct sock *sk)
{
- return (struct pppox_sock *)sk;
+ return container_of(sk, struct pppox_sock, sk);
}
struct module;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 1/2] pppox: remove sk_pppox() helper
From: Qingfang Deng @ 2026-04-08 1:51 UTC (permalink / raw)
To: Michal Ostrowski, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Dawid Osuchowski,
Qingfang Deng, netdev, linux-kernel
Cc: linux-ppp
The sk member can be directly accessed from struct pppox_sock without
relying on type casting. Remove the sk_pppox() helper and update all
call sites to use po->sk directly.
Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
drivers/net/ppp/pppoe.c | 10 +++++-----
drivers/net/ppp/pptp.c | 6 +++---
include/linux/if_pppox.h | 5 -----
3 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 1ac61c273b28..d546a7af0d54 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -231,7 +231,7 @@ static inline struct pppox_sock *get_item(struct pppoe_net *pn, __be16 sid,
struct pppox_sock *po;
po = __get_item(pn, sid, addr, ifindex);
- if (po && !refcount_inc_not_zero(&sk_pppox(po)->sk_refcnt))
+ if (po && !refcount_inc_not_zero(&po->sk.sk_refcnt))
po = NULL;
return po;
@@ -273,7 +273,7 @@ static void pppoe_flush_dev(struct net_device *dev)
if (!po)
break;
- sk = sk_pppox(po);
+ sk = &po->sk;
/* We always grab the socket lock, followed by the
* hash_lock, in that order. Since we should hold the
@@ -413,7 +413,7 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev,
if (!po)
goto drop;
- return __sk_receive_skb(sk_pppox(po), skb, 0, 1, false);
+ return __sk_receive_skb(&po->sk, skb, 0, 1, false);
drop:
kfree_skb(skb);
@@ -425,7 +425,7 @@ static void pppoe_unbind_sock_work(struct work_struct *work)
{
struct pppox_sock *po = container_of(work, struct pppox_sock,
proto.pppoe.padt_work);
- struct sock *sk = sk_pppox(po);
+ struct sock *sk = &po->sk;
lock_sock(sk);
if (po->pppoe_dev) {
@@ -469,7 +469,7 @@ static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
if (po)
if (!schedule_work(&po->proto.pppoe.padt_work))
- sock_put(sk_pppox(po));
+ sock_put(&po->sk);
abort:
kfree_skb(skb);
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index b18acd810561..cc8c102122d8 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -62,7 +62,7 @@ static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
if (opt->dst_addr.sin_addr.s_addr != s_addr)
sock = NULL;
else
- sock_hold(sk_pppox(sock));
+ sock_hold(&sock->sk);
}
rcu_read_unlock();
@@ -164,7 +164,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
struct iphdr *iph;
int max_headroom;
- if (sk_pppox(po)->sk_state & PPPOX_DEAD)
+ if (po->sk.sk_state & PPPOX_DEAD)
goto tx_drop;
rt = pptp_route_output(po, &fl4);
@@ -375,7 +375,7 @@ static int pptp_rcv(struct sk_buff *skb)
if (po) {
skb_dst_drop(skb);
nf_reset_ct(skb);
- return sk_receive_skb(sk_pppox(po), skb, 0);
+ return sk_receive_skb(&po->sk, skb, 0);
}
drop:
kfree_skb(skb);
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index 8bbf676c2a85..636772693f9a 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -57,11 +57,6 @@ static inline struct pppox_sock *pppox_sk(struct sock *sk)
return (struct pppox_sock *)sk;
}
-static inline struct sock *sk_pppox(struct pppox_sock *po)
-{
- return (struct sock *)po;
-}
-
struct module;
struct pppox_proto {
--
2.43.0
^ permalink raw reply related
* Re: [PATCH net-next] selftests: drv-net: adjust to socat changes
From: patchwork-bot+netdevbpf @ 2026-04-08 2:00 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, leitao,
shuah, hawk, john.fastabend, sdf, linux-kselftest
In-Reply-To: <20260404230103.2719103-1-kuba@kernel.org>
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Sat, 4 Apr 2026 16:01:03 -0700 you wrote:
> socat v1.8.1.0 now defaults to shut-null, it sends an extra
> 0-length UDP packet when sender disconnects. This breaks
> our tests which expect the exact packet sequence.
>
> Add shut-none which was the old default where necessary.
>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
>
> [...]
Here is the summary with links:
- [net-next] selftests: drv-net: adjust to socat changes
https://git.kernel.org/netdev/net-next/c/e65d8b6f3092
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [RFC net-next 2/4] selftests: drv-net: tso: add helpers for double tunneling GSO
From: Xu Du @ 2026-04-08 2:04 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, edumazet, pabeni, horms, shuah, netdev, linux-kselftest,
linux-kernel
In-Reply-To: <20260407080844.35368eaa@kernel.org>
On Tue, Apr 7, 2026 at 11:08 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 7 Apr 2026 10:45:09 +0800 Xu Du wrote:
> > As the YNL Python module cannot be invoked across different devices or
> > environments directly in its current form, the helper abstracts the
> > YNL CLI calls to ensure proper configuration of the tunneling device
> > features.
>
> Can you explain more? Why can't you use class RtnlFamily?
>
Hi Jakub,
I want to test the gro-hint parameter functionality of the GENEVE tunnel,
so I intend to use YNL for the testing. I am conducting the test between
two machines using SSH type. I want to add the gro-hint parameter on
both the local and remote nodes; however, I am unable to invoke class
RtnlFamily on the remote node via SSH.
--
Regards,
Xu
^ permalink raw reply
* Re: [PATCH net-next] net: Add net_cookie to Dead loop messages
From: Jakub Kicinski @ 2026-04-08 2:05 UTC (permalink / raw)
To: Chris J Arges
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
David Ahern, kernel-team, netdev, linux-kernel
In-Reply-To: <20260403221352.186383-1-carges@cloudflare.com>
On Fri, 3 Apr 2026 17:13:39 -0500 Chris J Arges wrote:
> Network devices can have the same name within different network namespaces.
> To help distinguish these devices, add the net_cookie value which can be
> used to identify the netns.
Doesn't apply, please respin?
Applying: net: Add net_cookie to Dead loop messages
Using index info to reconstruct a base tree...
M net/core/dev.c
M net/ipv4/ip_tunnel_core.c
Falling back to patching base and 3-way merge...
Auto-merging net/core/dev.c
CONFLICT (content): Merge conflict in net/core/dev.c
Auto-merging net/ipv4/ip_tunnel_core.c
Recorded preimage for 'net/core/dev.c'
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 net: Add net_cookie to Dead loop messages
--
pw-bot: cr
^ permalink raw reply
* Re: [PATCH net] net: avoid nul-deref trying to bind mp to incapable device
From: patchwork-bot+netdevbpf @ 2026-04-08 2:10 UTC (permalink / raw)
To: Jakub Kicinski
Cc: davem, netdev, edumazet, pabeni, andrew+netdev, horms, sdf,
almasrymina, daniel
In-Reply-To: <20260404001938.2425670-1-kuba@kernel.org>
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Fri, 3 Apr 2026 17:19:38 -0700 you wrote:
> Sashiko points out that we use qops in __net_mp_open_rxq()
> but never validate they are null. This was introduced when
> check was moved from netdev_rx_queue_restart().
>
> Look at ops directly instead of the locking config.
> qops imply netdev_need_ops_lock(). We used netdev_need_ops_lock()
> initially to signify that the real_num_rx_queues check below
> is safe without rtnl_lock, but I'm not sure if this is actually
> clear to most people, anyway.
>
> [...]
Here is the summary with links:
- [net] net: avoid nul-deref trying to bind mp to incapable device
https://git.kernel.org/netdev/net/c/944b3b734cfb
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net] ipv6: add READ_ONCE() annotations to fib6_metrics reader paths
From: Hangbin Liu @ 2026-04-08 2:13 UTC (permalink / raw)
To: Paolo Abeni
Cc: David S. Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Simon Horman, David Ahern, netdev, linux-kernel, Jiayuan Chen
In-Reply-To: <bc004773-aead-4431-95fc-860490773c49@redhat.com>
On Tue, Apr 07, 2026 at 12:27:30PM +0200, Paolo Abeni wrote:
> On 4/3/26 6:16 AM, Hangbin Liu wrote:
> > diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
> > index 9f8b6814a96a..2dfb04fab5da 100644
> > --- a/include/net/ip6_fib.h
> > +++ b/include/net/ip6_fib.h
> > @@ -594,7 +594,9 @@ void fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i);
> > void fib6_metric_set(struct fib6_info *f6i, int metric, u32 val);
> > static inline bool fib6_metric_locked(struct fib6_info *f6i, int metric)
> > {
> > - return !!(f6i->fib6_metrics->metrics[RTAX_LOCK - 1] & (1 << metric));
> > + struct dst_metrics *m = READ_ONCE(f6i->fib6_metrics);
> > +
> > + return !!(m->metrics[RTAX_LOCK - 1] & (1 << metric));
>
> Sashiko notes that here you may want to add an additional READ_ONCE() on
> m->metrics[RTAX_LOCK - 1], which in turn looks like more a
> follow-up/separate change than a change specific to this patch
OK, I will drop this change in the patch.
>
> > }
> > void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
> > bool offload, bool trap, bool offload_failed);
> > diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> > index dd26657b6a4a..3c96580c2a03 100644
> > --- a/net/ipv6/ip6_fib.c
> > +++ b/net/ipv6/ip6_fib.c
> > @@ -1144,9 +1144,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
> > iter->fib6_flags &= ~RTF_PREFIX_RT;
> > }
> >
> > - if (rt->fib6_pmtu)
> > + u32 pmtu = READ_ONCE(rt->fib6_pmtu);
>
> Here the READ_ONCE() on rt->metrics is still missing.
Which rt->metrics do you mean? I can't find it..
>
> > @@ -1635,11 +1635,12 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket,
> > static unsigned int fib6_mtu(const struct fib6_result *res)
> > {
> > const struct fib6_nh *nh = res->nh;
> > + struct dst_metrics *m;
> > unsigned int mtu;
> >
> > - if (res->f6i->fib6_pmtu) {
> > - mtu = res->f6i->fib6_pmtu;
> > - } else {
> > + m = READ_ONCE(res->f6i->fib6_metrics);
> > + mtu = READ_ONCE(m->metrics[RTAX_MTU - 1]);
This is already a fib6_mtu() function.
>
> After this patch there will be a single usage of the `fib6_pmtu` macro.
> I think it would be better to entirely drop it and replace with an helper:
>
> static inline unsigned int fib6_mtu(const struct fib6_info *f6i)
> {
> const struct dst_metrics *m = READ_ONCE(f6i->fib6_metrics);
> return READ_ONCE(m->metrics[RTAX_MTU - 1]);
> }
>
Should we rename the helper? e.g. get_fib6_mtu ?
Hangbin
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox