* Re: [PATCH 0/3] SM8450 IPA support
From: Konrad Dybcio @ 2026-07-20 14:21 UTC (permalink / raw)
To: Esteban Urrutia, Alex Elder, Bjorn Andersson, Konrad Dybcio,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Alex Elder
Cc: linux-arm-msm, devicetree, linux-kernel, netdev
In-Reply-To: <3e70d77e-6bec-4e16-ae88-a4f5161f182e@proton.me>
On 7/8/26 10:45 PM, Esteban Urrutia wrote:
> On 7/8/26 3:49 PM, Alex Elder wrote:
>> This was interesting to see. It's something I should probably
>> document better. Most everything maps to the downstream code,
>> but it's not always completely obvious how, because the upstream
>> driver has evolved substantially.
>
> On a personal note, I'm surprised to see someone not from Qualcomm
> maintaining this driver. It must be hard, so kudos.
>
>> This means that the SRAM size (ipa_mem_data->smem_size) should
>> possibly be defined in devicetree (as the IMEM address and size
>> now are).
>>
>> The SMEM region is used for "IPA filter tables", and access to
>> it is shared between the AP and the modem. Unlike the other
>> (host) memory regions, the size used is *not* included in the
>> ipa_init_modem_driver_req message that communicates from the
>> AP to the modem where the regions are, and their sizes.
>>
>> So it's possible that the size used must actually match what
>> is expected by both the AP and modem. If that is the case,
>> using the smaller size might have problems on whichever
>> platform (SM8450?) expects the larger one.
>>
>> So I'm not sure whether using the smaller size for both
>> platforms is OK; someone from Qualcomm might be able to
>> answer that question.
>
> I actually went ahead and reviewed downstream device trees I found on
> GitHub (1) which contain both SM8450 and SM8475 device trees looking for
> the qcom,ipa-q6-smem-size property, which would correspond to the SRAM
> size, and to my surprise, this was set to 0x9000 for both SoCs.
> Most likely the commit I got the SRAM information from (2) never made it
> to production devices.
OK you meant the qcom,ipa-q6-smem-size property. I can't find what
it corresponds to, the name of the associated IOMMU stream
unfortunately doesn't hint at anything useful either..
Konrad
^ permalink raw reply
* [PATCH net-next v2 0/6] Deliver TLS control records to kernel read_sock consumers
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs,
Chuck Lever
When kTLS is active, kernel consumers of the read_sock API cannot
see TLS control records: tls_sw_read_sock() rejects Alerts and
Handshake records with -EINVAL. A consumer that needs those records
falls back to sock_recvmsg() with ancillary buffers, which is why
NFSD's svcsock runs a MSG_CTRUNC recovery dance on every receive.
This series adds a read_sock_rectype proto_ops method that delivers
non-data records through a separate callback, then converts its first
consumer, NFSD's svcsock, to use it. NFS client (xprtsock), NVMe
target, and NVMe host are meant to move onto the same mechanism in
later series. The existing read_sock data path is unchanged.
The improvement is more than cosmetic. All of these consumers need
to handle TLS Alert messages efficiently and securely (in particular,
for KeyUpdate). A few design points worth mentioning:
- The new sk_read_rectype_actor_t callback does not share the data
actor's byte-counting contract. It returns 0 to consume a record or
a negative value to requeue it and stop delivery. The return is
never a byte count. svc_tcp_rectype_actor() relies on this,
returning 0 to consume every non-data record while stopping the
read loop through desc->count on a fatal alert.
- The no-data cap (patch 1) is a prerequisite, not a stand-alone fix.
Once control records reach read_sock, a record carrying no payload
stops advancing the caller's read descriptor, so a peer streaming
such records would pin the socket lock and the kernel receive
context for as long as the flood lasts. Bounding consecutive
no-data records supplies the return boundary a system call would
otherwise provide. The cap is scoped to tls_sw_read_sock() alone:
splice and recvmsg run in the caller's own context, reschedule, and
drop the lock on return, so they need nothing.
- The svcsock conversion is split so the new path can be reviewed
against the old. The old path is then removed by the last patch
in the series.
- read_sock_rectype has no direct userspace entry point, so its
selftest coverage (patch 4) drives the shared decryption and
rx_list delivery pipeline through the recvmsg and splice paths
instead.
---
Changes in v2:
- Renamed the read_sock_cmsg proto_ops method to read_sock_rectype.
- Non-data records now use a record-type actor, not RFC cmsg-style.
- Bound no-data records so they can't pin the socket lock (new patch 1).
- Added selftests for data/control record interleaving (new patch 4).
- Link to v1: https://lore.kernel.org/r/20260217222033.1929211-1-cel@kernel.org
---
Chuck Lever (6):
net/tls: Bound consecutive no-data records in tls_sw_read_sock()
net: Introduce read_sock_rectype proto_ops for control record delivery
tls: Implement read_sock_rectype for kTLS software path
selftests/tls: Add tests for data/control record interleaving
SUNRPC: Use read_sock_rectype for svcsock TCP receives
SUNRPC: Remove sock_recvmsg path from svcsock TCP receives
include/linux/net.h | 28 +++
net/sunrpc/svcsock.c | 381 +++++++++++++++++---------------------
net/tls/tls.h | 3 +
net/tls/tls_main.c | 5 +
net/tls/tls_sw.c | 54 +++++-
tools/testing/selftests/net/tls.c | 298 ++++++++++++++++++++++++++++-
6 files changed, 556 insertions(+), 213 deletions(-)
---
base-commit: 298bb2b8903323f6ef2eab4819a2e477765f0ff1
change-id: 20260327-tcp-read-sock-778a49660ff7
Best regards,
--
Chuck Lever <cel@kernel.org>
^ permalink raw reply
* [PATCH net-next v2 1/6] net/tls: Bound consecutive no-data records in tls_sw_read_sock()
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs,
Chuck Lever
In-Reply-To: <20260720-tcp-read-sock-v2-0-29545d034f3c@kernel.org>
A record that delivers no payload -- an empty TLS 1.3 data record
today, a control record once read_sock_rectype() lands -- leaves
tls_sw_read_sock() in its loop without advancing the caller's read
descriptor. A peer that streams such records keeps the receive loop
running, and the socket lock held, for as long as the records
arrive.
Cap the number of consecutive no-data records consumed per call. The
count resets on any record that delivers bytes, so a normal stream
is unaffected; a peer supplying only empty records is bounded to
TLS_RX_NODATA_LIMIT iterations before the call returns 0. read_sock
consumers treat that as "no progress, re-poll" rather than EOF, so
the connection stays up and makes progress once real data arrives.
Only tls_sw_read_sock() needs this cap. Its consumers drive the receive
loop from kernel context -- a work item or service thread holding the
socket lock across the whole call with no return to userspace -- so an
unbounded empty-record stream keeps that context and the lock pinned
for as long as the flood lasts. The cap supplies the return boundary
that a system call would otherwise provide. tls_sw_splice_read()
and tls_sw_recvmsg() already have one: they run in the calling task's
context, reschedule while draining the socket backlog (cond_resched()
in __release_sock()), and drop the socket lock when the call returns. A
flood there costs the caller only its own scheduler time, so the cap
would add nothing.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
net/tls/tls_sw.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index d4afc90fd796..087950ca639c 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2049,6 +2049,11 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
goto splice_read_end;
}
+/* Consecutive empty data records deliver no bytes; cap them per
+ * call so a peer streaming them cannot hold the socket lock here.
+ */
+#define TLS_RX_NODATA_LIMIT 16
+
int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t read_actor)
{
@@ -2057,6 +2062,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
struct tls_prot_info *prot = &tls_ctx->prot_info;
struct strp_msg *rxm = NULL;
struct sk_buff *skb = NULL;
+ unsigned int nodata_count = 0;
struct sk_psock *psock;
size_t flushed_at = 0;
bool released = true;
@@ -2122,7 +2128,13 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
* here instead.
*/
if (rxm->full_len == 0) {
+ err = 0;
consume_skb(skb);
+ /* tls_rx_reader_release() announces any parsed record
+ * on exit, so returning 0 here cannot strand it.
+ */
+ if (++nodata_count >= TLS_RX_NODATA_LIMIT)
+ break;
continue;
}
@@ -2133,6 +2145,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_requeue;
}
copied += used;
+ nodata_count = 0;
if (used < rxm->full_len) {
rxm->offset += used;
rxm->full_len -= used;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 2/6] net: Introduce read_sock_rectype proto_ops for control record delivery
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
In-Reply-To: <20260720-tcp-read-sock-v2-0-29545d034f3c@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
Kernel TCP consumers that use the read_sock interface
(proto_ops.read_sock) cannot receive TLS control messages (Alerts,
Handshake records) when kTLS is active. The current
tls_sw_read_sock() method rejects non-data records with -EINVAL, and
the sk_read_actor_t callback has no channel for delivering record-
type metadata.
Four kernel subsystems are affected: NFSD (sunrpc svcsock), NFS
client (sunrpc xprtsock), NVMe target (nvmet-tcp), and NVMe host
(nvme-tcp). Each of these either falls back to the sock_recvmsg()
API or lacks TLS alert handling entirely.
A new read_sock_rectype method in struct proto_ops provides a
separate code path that delivers non-data TLS records to a callback,
without changing the behavior seen by existing read_sock consumers.
The new sk_read_rectype_actor_t callback type extends the
sk_read_actor_t signature with a rectype parameter carrying the
protocol-layer record type (for example, TLS_RECORD_TYPE_ALERT). The
record-type callback returns 0 to consume a record or a negative
value to requeue it and stop delivery; unlike the data callback, its
return value does not count bytes.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
include/linux/net.h | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/include/linux/net.h b/include/linux/net.h
index 277188a40c72..7a19a743a617 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -198,6 +198,13 @@ struct sk_buff;
struct proto_accept_arg;
typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *,
unsigned int, size_t);
+/* rectype carries the transport record type, for example a
+ * TLS_RECORD_TYPE_* value.
+ */
+typedef int (*sk_read_rectype_actor_t)(read_descriptor_t *,
+ struct sk_buff *,
+ unsigned int, size_t,
+ u8 rectype);
typedef int (*skb_read_actor_t)(struct sock *, struct sk_buff *);
@@ -264,6 +271,27 @@ struct proto_ops {
*/
int (*read_sock)(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor);
+ /*
+ * read_sock_rectype splits delivery across two callbacks:
+ * recv_actor for data records, per the sk_read_actor_t
+ * convention, and rectype_actor for all other records,
+ * with rectype identifying each. A NULL rectype_actor
+ * leaves non-data records pending. rectype_actor returns 0
+ * to consume a record or negative to leave it pending for
+ * redelivery and stop delivery; the negative return is a
+ * backpressure signal, not a fatal error. Both callbacks
+ * report errors and early stop the way recv_actor does:
+ * by setting desc->count to 0 and recording the reason in
+ * desc->error, per the read_descriptor_t convention and
+ * independent of the return value. The return value reports
+ * only data bytes consumed by recv_actor; the caller
+ * detects an error or early stop via desc->count and
+ * desc->error.
+ */
+ int (*read_sock_rectype)(struct sock *sk,
+ read_descriptor_t *desc,
+ sk_read_actor_t recv_actor,
+ sk_read_rectype_actor_t rectype_actor);
/* This is different from read_sock(), it reads an entire skb at a time. */
int (*read_skb)(struct sock *sk, skb_read_actor_t recv_actor);
int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg,
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 3/6] tls: Implement read_sock_rectype for kTLS software path
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
In-Reply-To: <20260720-tcp-read-sock-v2-0-29545d034f3c@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
tls_sw_read_sock() rejects non-data records (alerts, handshake
messages) with -EINVAL. Kernel consumers that need TLS alert
delivery, such as NFSD, NFS client, and NVMe target, must fall back
to the sock_recvmsg() API to receive control messages via CMSG.
Implement the new read_sock_rectype() method for these consumers,
delivering non-data records to a callback through the kTLS software
receive path.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/tls/tls.h | 3 +++
net/tls/tls_main.c | 5 +++++
net/tls/tls_sw.c | 41 ++++++++++++++++++++++++++++++++++++-----
3 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/net/tls/tls.h b/net/tls/tls.h
index 60a37bdaaa25..c21d2a985e13 100644
--- a/net/tls/tls.h
+++ b/net/tls/tls.h
@@ -168,6 +168,9 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
size_t len, unsigned int flags);
int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t read_actor);
+int tls_sw_read_sock_rectype(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor,
+ sk_read_rectype_actor_t rectype_actor);
int tls_device_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
void tls_device_splice_eof(struct socket *sock);
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 8c588cdab733..4963e0caf6d5 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -949,12 +949,17 @@ static void build_proto_ops(struct proto_ops ops[TLS_NUM_CONFIG][TLS_NUM_CONFIG]
ops[TLS_BASE][TLS_SW ].splice_read = tls_sw_splice_read;
ops[TLS_BASE][TLS_SW ].poll = tls_sk_poll;
ops[TLS_BASE][TLS_SW ].read_sock = tls_sw_read_sock;
+ ops[TLS_BASE][TLS_SW ].read_sock_rectype = tls_sw_read_sock_rectype;
ops[TLS_SW ][TLS_SW ] = ops[TLS_SW ][TLS_BASE];
ops[TLS_SW ][TLS_SW ].splice_read = tls_sw_splice_read;
ops[TLS_SW ][TLS_SW ].poll = tls_sk_poll;
ops[TLS_SW ][TLS_SW ].read_sock = tls_sw_read_sock;
+ ops[TLS_SW ][TLS_SW ].read_sock_rectype = tls_sw_read_sock_rectype;
+ /* TLS_HW (device offload) RX entries inherit
+ * read_sock{,_rectype} from SW via the struct copies below.
+ */
#ifdef CONFIG_TLS_DEVICE
ops[TLS_HW ][TLS_BASE] = ops[TLS_BASE][TLS_BASE];
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 087950ca639c..af347b5b17fa 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2054,8 +2054,9 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos,
*/
#define TLS_RX_NODATA_LIMIT 16
-int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
- sk_read_actor_t read_actor)
+static int __tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor,
+ sk_read_rectype_actor_t rectype_actor)
{
struct tls_context *tls_ctx = tls_get_ctx(sk);
struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx);
@@ -2115,10 +2116,27 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
tls_rx_rec_done(ctx);
}
- /* read_sock does not support reading control messages */
+ /* Control records (alerts, handshake) reach a consumer
+ * only through rectype_actor; without one, read_sock
+ * rejects them.
+ */
if (tlm->control != TLS_RECORD_TYPE_DATA) {
- err = -EINVAL;
- goto read_sock_requeue;
+ if (!rectype_actor) {
+ err = -EINVAL;
+ goto read_sock_requeue;
+ }
+ err = rectype_actor(desc, skb, rxm->offset,
+ rxm->full_len,
+ tlm->control);
+ if (err < 0)
+ goto read_sock_requeue;
+ err = 0;
+ /* rectype_actor consumes the whole record; no partial path */
+ consume_skb(skb);
+ skb = NULL;
+ if (++nodata_count >= TLS_RX_NODATA_LIMIT)
+ break;
+ continue;
}
/* An empty data record (legal in TLS 1.3) gives a zero
@@ -2164,6 +2182,19 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
goto read_sock_end;
}
+int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor)
+{
+ return __tls_sw_read_sock(sk, desc, read_actor, NULL);
+}
+
+int tls_sw_read_sock_rectype(struct sock *sk, read_descriptor_t *desc,
+ sk_read_actor_t read_actor,
+ sk_read_rectype_actor_t rectype_actor)
+{
+ return __tls_sw_read_sock(sk, desc, read_actor, rectype_actor);
+}
+
bool tls_sw_sock_is_readable(struct sock *sk)
{
struct tls_context *tls_ctx = tls_get_ctx(sk);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 4/6] selftests/tls: Add tests for data/control record interleaving
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
In-Reply-To: <20260720-tcp-read-sock-v2-0-29545d034f3c@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
The new read_sock_rectype proto_ops method delivers interleaved
data and control TLS records to kernel consumers through
separate callbacks. The existing selftest coverage for these
interleaving patterns is limited: data_control_data only
peeks, and splice_cmsg_to_pipe tests a single control record
in isolation.
Add seven tests that exercise the record patterns
read_sock_rectype is designed to handle:
- splice_data_cmsg_data: data-control-data via splice, with
the control record drained through recvmsg between the two
splice calls
- splice_multi_cmsg_data: data-control-control-data with
distinct content types, verifying that each control record
is independently drainable with its type preserved, and
splice resumes afterward
- recv_data_cmsg_data: complete consumption of a
data-control-data sequence through recv and recvmsg
- peek_cmsg_after_data: peek at an interleaved control
record after the preceding data record has been consumed,
then consume it
- cmsg_before_data: control record as the first record in the
stream, followed by data
- mixed_control_types: two different control record types
(distinct content_type values) interleaved with data,
verifying type preservation through delivery
- data_cmsg_eof: trailing control record followed by
connection close, verifying the receiver drains the
control record and then observes EOF
The fixture teardown is also updated to skip closing fd
when a test has already closed it (as data_cmsg_eof does).
These tests exercise the record decryption and rx_list
delivery pipeline shared by the recvmsg, splice, and
read_sock paths. read_sock_rectype itself is a kernel-internal
API without a direct userspace entry point, so the tests
validate through the userspace-accessible paths.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
tools/testing/selftests/net/tls.c | 298 +++++++++++++++++++++++++++++++++++++-
1 file changed, 297 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c
index cbdd3ea28b99..8136306b5caa 100644
--- a/tools/testing/selftests/net/tls.c
+++ b/tools/testing/selftests/net/tls.c
@@ -427,7 +427,8 @@ FIXTURE_SETUP(tls)
FIXTURE_TEARDOWN(tls)
{
- close(self->fd);
+ if (self->fd >= 0)
+ close(self->fd);
close(self->cfd);
}
@@ -897,6 +898,114 @@ TEST_F(tls, splice_dec_cmsg_to_pipe)
EXPECT_EQ(memcmp(test_str, buf, send_len), 0);
}
+/* Verify splice handles data-control-data: splice reads the data
+ * records successfully while the intervening control record must
+ * be drained via recvmsg before splice can continue.
+ */
+TEST_F(tls, splice_data_cmsg_data)
+{
+ char mem_send[TLS_PAYLOAD_MAX_LEN];
+ char mem_recv[TLS_PAYLOAD_MAX_LEN];
+ int send_len = 4096;
+ char *ctrl_str = "control";
+ int ctrl_len = strlen(ctrl_str) + 1;
+ char ctrl_buf[8];
+ int p[2];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ memrnd(mem_send, sizeof(mem_send));
+
+ ASSERT_GE(pipe(p), 0);
+
+ /* Send: data, control, data */
+ EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl_str, ctrl_len, 0),
+ ctrl_len);
+ EXPECT_EQ(send(self->fd, &mem_send[send_len], send_len, 0), send_len);
+
+ /* Splice first data record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+
+ /* Splice hits control record, fails */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Drain the control record via recvmsg */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ ctrl_buf, sizeof(ctrl_buf), MSG_WAITALL),
+ ctrl_len);
+ EXPECT_EQ(memcmp(ctrl_str, ctrl_buf, ctrl_len), 0);
+
+ /* Splice second data record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(&mem_send[send_len], mem_recv, send_len), 0);
+}
+
+/* Verify that multiple consecutive control records between data
+ * records can each be drained individually, and splice resumes
+ * afterward. The two control records use different content types
+ * to verify type preservation across the splice boundary.
+ */
+TEST_F(tls, splice_multi_cmsg_data)
+{
+ char mem_send[TLS_PAYLOAD_MAX_LEN];
+ char mem_recv[TLS_PAYLOAD_MAX_LEN];
+ int send_len = 4096;
+ char *ctrl1 = "alert1";
+ char *ctrl2 = "alert2";
+ int ctrl_len = strlen(ctrl1) + 1;
+ char ctrl_buf[7];
+ int p[2];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ memrnd(mem_send, sizeof(mem_send));
+
+ ASSERT_GE(pipe(p), 0);
+
+ /* Send: data, control(100), control(200), data */
+ EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl1, ctrl_len, 0), ctrl_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 200, ctrl2, ctrl_len, 0), ctrl_len);
+ EXPECT_EQ(send(self->fd, &mem_send[send_len], send_len, 0), send_len);
+
+ /* Splice first data */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0);
+
+ /* Splice fails on first control record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Drain first control (type 100) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ ctrl_buf, sizeof(ctrl_buf), MSG_WAITALL),
+ ctrl_len);
+ EXPECT_EQ(memcmp(ctrl1, ctrl_buf, ctrl_len), 0);
+
+ /* Splice fails on second control record */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1);
+ EXPECT_EQ(errno, EINVAL);
+
+ /* Drain second control (type 200) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 200,
+ ctrl_buf, sizeof(ctrl_buf), MSG_WAITALL),
+ ctrl_len);
+ EXPECT_EQ(memcmp(ctrl2, ctrl_buf, ctrl_len), 0);
+
+ /* Splice second data */
+ EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len);
+ EXPECT_EQ(read(p[0], mem_recv, send_len), send_len);
+ EXPECT_EQ(memcmp(&mem_send[send_len], mem_recv, send_len), 0);
+}
+
TEST_F(tls, recv_and_splice)
{
int send_len = TLS_PAYLOAD_MAX_LEN;
@@ -1682,6 +1791,193 @@ TEST_F(tls, data_control_data)
EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_PEEK), send_len);
}
+/* Fully consume a data-control-data sequence. The existing
+ * data_control_data test only peeks; this exercises complete
+ * record delivery through recv and recvmsg.
+ */
+TEST_F(tls, recv_data_cmsg_data)
+{
+ char *data1 = "first_data";
+ char *ctrl = "ctrl_msg";
+ char *data2 = "second_data";
+ int d1_len = strlen(data1) + 1;
+ int c_len = strlen(ctrl) + 1;
+ int d2_len = strlen(data2) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data1, d1_len, 0), d1_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(send(self->fd, data2, d2_len, 0), d2_len);
+
+ /* First data record */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d1_len);
+ EXPECT_EQ(memcmp(buf, data1, d1_len), 0);
+
+ /* recv without cmsg buffer fails on control record */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
+ EXPECT_EQ(errno, EIO);
+
+ /* Drain control via recvmsg */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_WAITALL), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Second data record */
+ EXPECT_EQ(recv(self->cfd, buf, d2_len, MSG_WAITALL), d2_len);
+ EXPECT_EQ(memcmp(buf, data2, d2_len), 0);
+}
+
+/* Peek at an interleaved control record after the preceding data
+ * record has been consumed, then consume it. MSG_PEEK exposes the
+ * control record's type without consuming it.
+ */
+TEST_F(tls, peek_cmsg_after_data)
+{
+ char *data = "leading";
+ char *ctrl = "middle";
+ char *tail = "trailing";
+ int d_len = strlen(data) + 1;
+ int c_len = strlen(ctrl) + 1;
+ int t_len = strlen(tail) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data, d_len, 0), d_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(send(self->fd, tail, t_len, 0), t_len);
+
+ /* Consume leading data */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d_len);
+ EXPECT_EQ(memcmp(buf, data, d_len), 0);
+
+ /* Peek at the control record */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_PEEK), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Consume the control record */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), 0), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Trailing data */
+ EXPECT_EQ(recv(self->cfd, buf, t_len, MSG_WAITALL), t_len);
+ EXPECT_EQ(memcmp(buf, tail, t_len), 0);
+}
+
+/* Control record as the first record in the stream, followed by
+ * data. The control record must be drained before the data record
+ * becomes available.
+ */
+TEST_F(tls, cmsg_before_data)
+{
+ char *ctrl = "alert";
+ char *data = "payload";
+ int c_len = strlen(ctrl) + 1;
+ int d_len = strlen(data) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(send(self->fd, data, d_len, 0), d_len);
+
+ /* recv without cmsg fails */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), -1);
+ EXPECT_EQ(errno, EIO);
+
+ /* Drain control via recvmsg */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_WAITALL), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Data follows */
+ EXPECT_EQ(recv(self->cfd, buf, d_len, MSG_WAITALL), d_len);
+ EXPECT_EQ(memcmp(buf, data, d_len), 0);
+}
+
+/* Two different control record types interleaved with data.
+ * Each control record is delivered with its own type preserved;
+ * verify both types arrive intact.
+ */
+TEST_F(tls, mixed_control_types)
+{
+ char *data1 = "data1";
+ char *ctrl1 = "handshake";
+ char *ctrl2 = "alert_msg";
+ char *data2 = "data2";
+ int d1_len = strlen(data1) + 1;
+ int c1_len = strlen(ctrl1) + 1;
+ int c2_len = strlen(ctrl2) + 1;
+ int d2_len = strlen(data2) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data1, d1_len, 0), d1_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl1, c1_len, 0), c1_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 200, ctrl2, c2_len, 0), c2_len);
+ EXPECT_EQ(send(self->fd, data2, d2_len, 0), d2_len);
+
+ /* First data */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d1_len);
+ EXPECT_EQ(memcmp(buf, data1, d1_len), 0);
+
+ /* First control (type 100) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), MSG_WAITALL), c1_len);
+ EXPECT_EQ(memcmp(buf, ctrl1, c1_len), 0);
+
+ /* Second control (type 200) */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 200,
+ buf, sizeof(buf), MSG_WAITALL), c2_len);
+ EXPECT_EQ(memcmp(buf, ctrl2, c2_len), 0);
+
+ /* Second data */
+ EXPECT_EQ(recv(self->cfd, buf, d2_len, MSG_WAITALL), d2_len);
+ EXPECT_EQ(memcmp(buf, data2, d2_len), 0);
+}
+
+/* Trailing control record with no data following it. The sender
+ * closes the connection after the control record; the receiver
+ * drains the control and then observes EOF.
+ */
+TEST_F(tls, data_cmsg_eof)
+{
+ char *data = "payload";
+ char *ctrl = "final";
+ int d_len = strlen(data) + 1;
+ int c_len = strlen(ctrl) + 1;
+ char buf[20];
+
+ if (self->notls)
+ SKIP(return, "no TLS support");
+
+ EXPECT_EQ(send(self->fd, data, d_len, 0), d_len);
+ EXPECT_EQ(tls_send_cmsg(self->fd, 100, ctrl, c_len, 0), c_len);
+ EXPECT_EQ(close(self->fd), 0);
+ self->fd = -1;
+
+ /* Consume data */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_WAITALL), d_len);
+ EXPECT_EQ(memcmp(buf, data, d_len), 0);
+
+ /* Drain trailing control */
+ EXPECT_EQ(tls_recv_cmsg(_metadata, self->cfd, 100,
+ buf, sizeof(buf), 0), c_len);
+ EXPECT_EQ(memcmp(buf, ctrl, c_len), 0);
+
+ /* Next recv returns EOF */
+ EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), 0), 0);
+}
+
TEST_F(tls, shutdown)
{
char const *test_str = "test_read";
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 5/6] SUNRPC: Use read_sock_rectype for svcsock TCP receives
From: Chuck Lever @ 2026-07-20 14:27 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
In-Reply-To: <20260720-tcp-read-sock-v2-0-29545d034f3c@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
The svcsock TCP receive path uses sock_recvmsg() with ancillary data
buffers to detect TLS alerts when kTLS is active. This CMSG-based
approach requires a MSG_CTRUNC recovery dance on every receive and
cannot deliver control records through the read_sock interface.
When the socket provides a read_sock_rectype method (now set by
kTLS), svc_tcp_recvfrom() now dispatches to a new
svc_tcp_recvfrom_readsock() path. Two actor callbacks handle the
data:
svc_tcp_recv_actor() parses the RPC record byte stream directly from
skbs. Fragment header bytes fill sk_marker first; subsequent body
bytes are copied into rq_pages at the position tracked by
sk_datalen. When the last fragment of a complete RPC message
arrives, the actor sets desc->count to zero, stopping the read loop.
svc_tcp_rectype_actor() handles non-data TLS records. For fatal
alerts, the transport is marked for deferred close and the read loop
is stopped via desc->count. All non-data records are consumed by
returning 0 so that subsequent data records remain deliverable.
Sockets without read_sock_rectype (plain TCP, non-kTLS) continue to
use the existing sock_recvmsg() path unchanged. A follow-up patch
retires that path in favor of read_sock.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/sunrpc/svcsock.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 244 insertions(+)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 50e5e7f5b762..e40931d11491 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1134,6 +1134,247 @@ static void svc_tcp_fragment_received(struct svc_sock *svsk)
svsk->sk_marker = xdr_zero;
}
+/*
+ * read_sock_rectype data actor: receives decrypted application data
+ * from the TLS layer, parsing the RPC record stream (fragment
+ * headers and message bodies) and assembling complete RPC messages
+ * into rqstp->rq_pages.
+ */
+static int svc_tcp_recv_actor(read_descriptor_t *desc,
+ struct sk_buff *skb,
+ unsigned int offset, size_t len)
+{
+ struct svc_rqst *rqstp = desc->arg.data;
+ struct svc_sock *svsk =
+ container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
+ size_t reclen, received, want, take, done, n;
+ size_t consumed = 0;
+
+ if (!desc->count)
+ return 0;
+
+ if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) {
+ want = sizeof(rpc_fraghdr) - svsk->sk_tcplen;
+ n = min(want, len);
+
+ if (skb_copy_bits(skb, offset,
+ (char *)&svsk->sk_marker +
+ svsk->sk_tcplen, n))
+ goto fault;
+ svsk->sk_tcplen += n;
+ offset += n;
+ len -= n;
+ consumed += n;
+
+ if (svsk->sk_tcplen < sizeof(rpc_fraghdr))
+ return consumed;
+
+ trace_svcsock_marker(&svsk->sk_xprt, svsk->sk_marker);
+ if (svc_sock_reclen(svsk) + svsk->sk_datalen >
+ svsk->sk_xprt.xpt_server->sv_max_mesg) {
+ net_notice_ratelimited("svc: %s oversized RPC fragment (%u octets) from %pISpc\n",
+ svsk->sk_xprt.xpt_server->sv_name,
+ svc_sock_reclen(svsk),
+ (struct sockaddr *)&svsk->sk_xprt.xpt_remote);
+ desc->error = -EMSGSIZE;
+ desc->count = 0;
+ return consumed;
+ }
+ }
+
+ reclen = svc_sock_reclen(svsk);
+ received = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+ want = reclen - received;
+ take = min(want, len);
+ done = 0;
+
+ while (done < take) {
+ unsigned int pg = svsk->sk_datalen >> PAGE_SHIFT;
+ unsigned int pg_off = svsk->sk_datalen & (PAGE_SIZE - 1);
+ size_t chunk = min(take - done,
+ PAGE_SIZE - (size_t)pg_off);
+
+ if (skb_copy_bits(skb, offset,
+ page_address(rqstp->rq_pages[pg]) + pg_off,
+ chunk))
+ goto fault;
+ flush_dcache_page(rqstp->rq_pages[pg]);
+ offset += chunk;
+ done += chunk;
+ svsk->sk_datalen += chunk;
+ }
+ svsk->sk_tcplen += take;
+ consumed += take;
+
+ if (svsk->sk_tcplen - sizeof(rpc_fraghdr) >= reclen) {
+ if (svc_sock_final_rec(svsk))
+ desc->count = 0;
+ else
+ svc_tcp_fragment_received(svsk);
+ }
+
+ return consumed;
+
+fault:
+ desc->error = -EFAULT;
+ desc->count = 0;
+ return consumed;
+}
+
+/*
+ * read_sock_rectype non-data record actor: receives non-data TLS
+ * records (alerts, handshake messages) and translates them into
+ * transport-level actions.
+ *
+ * Returns 0 to consume the record and allow the TLS layer to
+ * continue delivering subsequent records. A negative return
+ * causes the TLS layer to requeue the skb on its rx_list,
+ * blocking all further record delivery on this connection.
+ */
+static int svc_tcp_rectype_actor(read_descriptor_t *desc,
+ struct sk_buff *skb,
+ unsigned int offset, size_t len,
+ u8 rectype)
+{
+ struct svc_rqst *rqstp = desc->arg.data;
+ struct svc_sock *svsk =
+ container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
+
+ switch (rectype) {
+ case TLS_RECORD_TYPE_ALERT: {
+ u8 alert[2] = {}, level, description;
+ struct kvec kvec = {
+ .iov_base = alert,
+ .iov_len = sizeof(alert),
+ };
+ struct msghdr msg = {};
+
+ if (skb_copy_bits(skb, offset, alert, min(len, sizeof(alert))))
+ break;
+ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &kvec, 1, sizeof(alert));
+ tls_alert_recv(svsk->sk_sk, &msg, &level, &description);
+ if (level == TLS_ALERT_LEVEL_FATAL) {
+ svc_xprt_deferred_close(&svsk->sk_xprt);
+ desc->error = -ENOTCONN;
+ desc->count = 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
+{
+ struct svc_sock *svsk =
+ container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
+ struct svc_serv *serv = svsk->sk_xprt.xpt_server;
+ struct sock *sk = svsk->sk_sk;
+ read_descriptor_t desc = {
+ .arg.data = rqstp,
+ };
+ ssize_t len;
+ __be32 *p;
+ __be32 calldir;
+
+ clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+
+ svc_tcp_restore_pages(svsk, rqstp);
+ rqstp->rq_arg.head[0].iov_base = page_address(rqstp->rq_pages[0]);
+
+ desc.count = serv->sv_max_mesg;
+ lock_sock(sk);
+ len = svsk->sk_sock->ops->read_sock_rectype(sk, &desc,
+ svc_tcp_recv_actor,
+ svc_tcp_rectype_actor);
+ release_sock(sk);
+
+ if (desc.error < 0) {
+ len = desc.error;
+ goto err_discard;
+ }
+ if (desc.count != 0) {
+ if (len > 0)
+ set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ goto err_incomplete;
+ }
+
+ if (svsk->sk_datalen < 8)
+ goto err_nuts;
+
+ rqstp->rq_arg.len = svsk->sk_datalen;
+ rqstp->rq_arg.page_base = 0;
+ if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
+ rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
+ rqstp->rq_arg.page_len = 0;
+ } else {
+ rqstp->rq_arg.page_len = rqstp->rq_arg.len -
+ rqstp->rq_arg.head[0].iov_len;
+ }
+
+ rqstp->rq_xprt_ctxt = NULL;
+ rqstp->rq_prot = IPPROTO_TCP;
+ if (test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags))
+ set_bit(RQ_LOCAL, &rqstp->rq_flags);
+ else
+ clear_bit(RQ_LOCAL, &rqstp->rq_flags);
+
+ p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
+ calldir = p[1];
+ if (calldir)
+ len = receive_cb_reply(svsk, rqstp);
+
+ /* Reset TCP read info */
+ svsk->sk_datalen = 0;
+ svc_tcp_fragment_received(svsk);
+
+ if (len < 0)
+ goto error;
+
+ trace_svcsock_tcp_recv(&svsk->sk_xprt, rqstp->rq_arg.len);
+ svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt);
+ if (serv->sv_stats)
+ serv->sv_stats->nettcpcnt++;
+
+ svc_sock_secure_port(rqstp);
+ set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ svc_xprt_received(rqstp->rq_xprt);
+ return rqstp->rq_arg.len;
+
+err_incomplete:
+ svc_tcp_save_pages(svsk, rqstp);
+ if (len < 0 && len != -EAGAIN)
+ goto err_delete;
+ if (svsk->sk_tcplen >= sizeof(rpc_fraghdr))
+ trace_svcsock_tcp_recv_short(&svsk->sk_xprt,
+ svc_sock_reclen(svsk),
+ svsk->sk_tcplen - sizeof(rpc_fraghdr));
+ goto err_noclose;
+error:
+ if (len != -EAGAIN)
+ goto err_delete;
+ trace_svcsock_tcp_recv_eagain(&svsk->sk_xprt, 0);
+ goto err_noclose;
+err_nuts:
+ svsk->sk_datalen = 0;
+ goto err_delete;
+err_discard:
+ /*
+ * Clear sk_datalen so the teardown-time
+ * svc_tcp_clear_pages() does not walk the emptied
+ * svsk->sk_pages[].
+ */
+ svsk->sk_datalen = 0;
+err_delete:
+ trace_svcsock_tcp_recv_err(&svsk->sk_xprt, len);
+ svc_xprt_deferred_close(&svsk->sk_xprt);
+err_noclose:
+ svc_xprt_received(rqstp->rq_xprt);
+ return 0;
+}
+
/**
* svc_tcp_recvfrom - Receive data from a TCP socket
* @rqstp: request structure into which to receive an RPC Call
@@ -1162,6 +1403,9 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
__be32 *p;
__be32 calldir;
+ if (svsk->sk_sock->ops->read_sock_rectype)
+ return svc_tcp_recvfrom_readsock(rqstp);
+
clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
len = svc_tcp_read_marker(svsk, rqstp);
if (len < 0)
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v2 6/6] SUNRPC: Remove sock_recvmsg path from svcsock TCP receives
From: Chuck Lever @ 2026-07-20 14:28 UTC (permalink / raw)
To: Jakub Kicinski, Paolo Abeni, Simon Horman, John Fastabend,
Sabrina Dubroca, Shuah Khan, Jeff Layton, NeilBrown,
Olga Kornievskaia, Dai Ngo, Tom Talpey, Chuck Lever
Cc: netdev, kernel-tls-handshake, linux-kselftest, linux-nfs
In-Reply-To: <20260720-tcp-read-sock-v2-0-29545d034f3c@kernel.org>
From: Chuck Lever <chuck.lever@oracle.com>
The svcsock TCP receive path maintains two code paths: one
using read_sock/read_sock_rectype and a legacy path using
sock_recvmsg. Plain TCP sockets already provide read_sock
(tcp_read_sock) in their proto_ops, so a single
read_sock-based receive path handles all cases relevant to
NFSD, using read_sock_rectype under kTLS and read_sock
otherwise.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
net/sunrpc/svcsock.c | 329 ++++-----------------------------------------------
1 file changed, 26 insertions(+), 303 deletions(-)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index e40931d11491..9b9e0da9e73c 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -8,15 +8,6 @@
* evenly when servicing a single client. May need to modify the
* svc_xprt_enqueue procedure...
*
- * TCP support is largely untested and may be a little slow. The problem
- * is that we currently do two separate recvfrom's, one for the 4-byte
- * record length, and the second for the actual record. This could possibly
- * be improved by always reading a minimum size of around 100 bytes and
- * tucking any superfluous bytes away in a temporary store. Still, that
- * leaves write requests out in the rain. An alternative may be to peek at
- * the first skb in the queue, and if it matches the next TCP sequence
- * number, to extract the record marker. Yuck.
- *
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
@@ -238,138 +229,6 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
return len;
}
-static int
-svc_tcp_sock_process_cmsg(struct socket *sock, struct msghdr *msg,
- struct cmsghdr *cmsg, int ret)
-{
- u8 content_type = tls_get_record_type(sock->sk, cmsg);
- u8 level, description;
-
- switch (content_type) {
- case 0:
- break;
- case TLS_RECORD_TYPE_DATA:
- /* TLS sets EOR at the end of each application data
- * record, even though there might be more frames
- * waiting to be decrypted.
- */
- msg->msg_flags &= ~MSG_EOR;
- break;
- case TLS_RECORD_TYPE_ALERT:
- tls_alert_recv(sock->sk, msg, &level, &description);
- ret = (level == TLS_ALERT_LEVEL_FATAL) ?
- -ENOTCONN : -EAGAIN;
- break;
- default:
- /* discard this record type */
- ret = -EAGAIN;
- }
- return ret;
-}
-
-static int
-svc_tcp_sock_recv_cmsg(struct socket *sock, unsigned int *msg_flags)
-{
- union {
- struct cmsghdr cmsg;
- u8 buf[CMSG_SPACE(sizeof(u8))];
- } u;
- u8 alert[2];
- struct kvec alert_kvec = {
- .iov_base = alert,
- .iov_len = sizeof(alert),
- };
- struct msghdr msg = {
- .msg_flags = *msg_flags,
- .msg_control = &u,
- .msg_controllen = sizeof(u),
- };
- int ret;
-
- iov_iter_kvec(&msg.msg_iter, ITER_DEST, &alert_kvec, 1,
- alert_kvec.iov_len);
- ret = sock_recvmsg(sock, &msg, MSG_DONTWAIT);
- if (ret > 0 &&
- tls_get_record_type(sock->sk, &u.cmsg) == TLS_RECORD_TYPE_ALERT) {
- iov_iter_revert(&msg.msg_iter, ret);
- ret = svc_tcp_sock_process_cmsg(sock, &msg, &u.cmsg, -EAGAIN);
- }
- return ret;
-}
-
-static int
-svc_tcp_sock_recvmsg(struct svc_sock *svsk, struct msghdr *msg)
-{
- int ret;
- struct socket *sock = svsk->sk_sock;
-
- ret = sock_recvmsg(sock, msg, MSG_DONTWAIT);
- if (msg->msg_flags & MSG_CTRUNC) {
- msg->msg_flags &= ~(MSG_CTRUNC | MSG_EOR);
- if (ret == 0 || ret == -EIO)
- ret = svc_tcp_sock_recv_cmsg(sock, &msg->msg_flags);
- }
- return ret;
-}
-
-#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
-static void svc_flush_bvec(const struct bio_vec *bvec, size_t size, size_t seek)
-{
- struct bvec_iter bi = {
- .bi_size = size + seek,
- };
- struct bio_vec bv;
-
- bvec_iter_advance(bvec, &bi, seek & PAGE_MASK);
- for_each_bvec(bv, bvec, bi, bi)
- flush_dcache_page(bv.bv_page);
-}
-#else
-static inline void svc_flush_bvec(const struct bio_vec *bvec, size_t size,
- size_t seek)
-{
-}
-#endif
-
-/*
- * Read from @rqstp's transport socket. The incoming message fills whole
- * pages in @rqstp's rq_pages array until the last page of the message
- * has been received into a partial page.
- */
-static ssize_t svc_tcp_read_msg(struct svc_rqst *rqstp, size_t buflen,
- size_t seek)
-{
- struct svc_sock *svsk =
- container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
- struct bio_vec *bvec = rqstp->rq_bvec;
- struct msghdr msg = { NULL };
- unsigned int i;
- ssize_t len;
- size_t t;
-
- clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
-
- for (i = 0, t = 0; t < buflen; i++, t += PAGE_SIZE)
- bvec_set_page(&bvec[i], rqstp->rq_pages[i], PAGE_SIZE, 0);
-
- iov_iter_bvec(&msg.msg_iter, ITER_DEST, bvec, i, buflen);
- if (seek) {
- iov_iter_advance(&msg.msg_iter, seek);
- buflen -= seek;
- }
- len = svc_tcp_sock_recvmsg(svsk, &msg);
- if (len > 0)
- svc_flush_bvec(bvec, len, seek);
-
- /* If we read a full record, then assume there may be more
- * data to read (stream based sockets only!)
- */
- if (len == buflen)
- set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
-
- return len;
-}
-
/*
* Set socket snd and rcv buffer lengths
*/
@@ -1048,50 +907,6 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk)
svsk->sk_datalen = 0;
}
-/*
- * Receive fragment record header into sk_marker.
- */
-static ssize_t svc_tcp_read_marker(struct svc_sock *svsk,
- struct svc_rqst *rqstp)
-{
- ssize_t want, len;
-
- /* If we haven't gotten the record length yet,
- * get the next four bytes.
- */
- if (svsk->sk_tcplen < sizeof(rpc_fraghdr)) {
- struct msghdr msg = { NULL };
- struct kvec iov;
-
- want = sizeof(rpc_fraghdr) - svsk->sk_tcplen;
- iov.iov_base = ((char *)&svsk->sk_marker) + svsk->sk_tcplen;
- iov.iov_len = want;
- iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, want);
- len = svc_tcp_sock_recvmsg(svsk, &msg);
- if (len < 0)
- return len;
- svsk->sk_tcplen += len;
- if (len < want) {
- /* call again to read the remaining bytes */
- goto err_short;
- }
- trace_svcsock_marker(&svsk->sk_xprt, svsk->sk_marker);
- if (svc_sock_reclen(svsk) + svsk->sk_datalen >
- svsk->sk_xprt.xpt_server->sv_max_mesg)
- goto err_too_large;
- }
- return svc_sock_reclen(svsk);
-
-err_too_large:
- net_notice_ratelimited("svc: %s oversized RPC fragment (%u octets) from %pISpc\n",
- svsk->sk_xprt.xpt_server->sv_name,
- svc_sock_reclen(svsk),
- (struct sockaddr *)&svsk->sk_xprt.xpt_remote);
- svc_xprt_deferred_close(&svsk->sk_xprt);
-err_short:
- return -EAGAIN;
-}
-
static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
{
struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt;
@@ -1135,10 +950,10 @@ static void svc_tcp_fragment_received(struct svc_sock *svsk)
}
/*
- * read_sock_rectype data actor: receives decrypted application data
- * from the TLS layer, parsing the RPC record stream (fragment
- * headers and message bodies) and assembling complete RPC messages
- * into rqstp->rq_pages.
+ * read_sock data actor: receives application data from the
+ * transport socket, parsing the RPC record stream (fragment
+ * headers and message bodies) and assembling complete RPC
+ * messages into rqstp->rq_pages.
*/
static int svc_tcp_recv_actor(read_descriptor_t *desc,
struct sk_buff *skb,
@@ -1266,7 +1081,21 @@ static int svc_tcp_rectype_actor(read_descriptor_t *desc,
return 0;
}
-static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
+/**
+ * svc_tcp_recvfrom - Receive data from a TCP socket
+ * @rqstp: request structure into which to receive an RPC Call
+ *
+ * Called in a loop when XPT_DATA has been set.
+ *
+ * Returns:
+ * On success, the number of bytes in a received RPC Call, or
+ * %0 if a complete RPC Call message was not ready to return
+ *
+ * The zero return case handles partial receives and callback Replies.
+ * The state of a partial receive is preserved in the svc_sock for
+ * the next call to svc_tcp_recvfrom.
+ */
+static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
{
struct svc_sock *svsk =
container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
@@ -1286,9 +1115,13 @@ static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
desc.count = serv->sv_max_mesg;
lock_sock(sk);
- len = svsk->sk_sock->ops->read_sock_rectype(sk, &desc,
- svc_tcp_recv_actor,
- svc_tcp_rectype_actor);
+ if (svsk->sk_sock->ops->read_sock_rectype)
+ len = svsk->sk_sock->ops->read_sock_rectype(sk, &desc,
+ svc_tcp_recv_actor,
+ svc_tcp_rectype_actor);
+ else
+ len = svsk->sk_sock->ops->read_sock(sk, &desc,
+ svc_tcp_recv_actor);
release_sock(sk);
if (desc.error < 0) {
@@ -1375,116 +1208,6 @@ static int svc_tcp_recvfrom_readsock(struct svc_rqst *rqstp)
return 0;
}
-/**
- * svc_tcp_recvfrom - Receive data from a TCP socket
- * @rqstp: request structure into which to receive an RPC Call
- *
- * Called in a loop when XPT_DATA has been set.
- *
- * Read the 4-byte stream record marker, then use the record length
- * in that marker to set up exactly the resources needed to receive
- * the next RPC message into @rqstp.
- *
- * Returns:
- * On success, the number of bytes in a received RPC Call, or
- * %0 if a complete RPC Call message was not ready to return
- *
- * The zero return case handles partial receives and callback Replies.
- * The state of a partial receive is preserved in the svc_sock for
- * the next call to svc_tcp_recvfrom.
- */
-static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
-{
- struct svc_sock *svsk =
- container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt);
- struct svc_serv *serv = svsk->sk_xprt.xpt_server;
- size_t want, base;
- ssize_t len;
- __be32 *p;
- __be32 calldir;
-
- if (svsk->sk_sock->ops->read_sock_rectype)
- return svc_tcp_recvfrom_readsock(rqstp);
-
- clear_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
- len = svc_tcp_read_marker(svsk, rqstp);
- if (len < 0)
- goto error;
-
- base = svc_tcp_restore_pages(svsk, rqstp);
- want = len - (svsk->sk_tcplen - sizeof(rpc_fraghdr));
- len = svc_tcp_read_msg(rqstp, base + want, base);
- if (len >= 0) {
- trace_svcsock_tcp_recv(&svsk->sk_xprt, len);
- svsk->sk_tcplen += len;
- svsk->sk_datalen += len;
- }
- if (len != want || !svc_sock_final_rec(svsk))
- goto err_incomplete;
- if (svsk->sk_datalen < 8)
- goto err_nuts;
-
- rqstp->rq_arg.len = svsk->sk_datalen;
- rqstp->rq_arg.page_base = 0;
- if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
- rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
- rqstp->rq_arg.page_len = 0;
- } else
- rqstp->rq_arg.page_len = rqstp->rq_arg.len - rqstp->rq_arg.head[0].iov_len;
-
- rqstp->rq_xprt_ctxt = NULL;
- rqstp->rq_prot = IPPROTO_TCP;
- if (test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags))
- set_bit(RQ_LOCAL, &rqstp->rq_flags);
- else
- clear_bit(RQ_LOCAL, &rqstp->rq_flags);
-
- p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
- calldir = p[1];
- if (calldir)
- len = receive_cb_reply(svsk, rqstp);
-
- /* Reset TCP read info */
- svsk->sk_datalen = 0;
- svc_tcp_fragment_received(svsk);
-
- if (len < 0)
- goto error;
-
- svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt);
- if (serv->sv_stats)
- serv->sv_stats->nettcpcnt++;
-
- svc_sock_secure_port(rqstp);
- svc_xprt_received(rqstp->rq_xprt);
- return rqstp->rq_arg.len;
-
-err_incomplete:
- svc_tcp_save_pages(svsk, rqstp);
- if (len < 0 && len != -EAGAIN)
- goto err_delete;
- if (len == want)
- svc_tcp_fragment_received(svsk);
- else
- trace_svcsock_tcp_recv_short(&svsk->sk_xprt,
- svc_sock_reclen(svsk),
- svsk->sk_tcplen - sizeof(rpc_fraghdr));
- goto err_noclose;
-error:
- if (len != -EAGAIN)
- goto err_delete;
- trace_svcsock_tcp_recv_eagain(&svsk->sk_xprt, 0);
- goto err_noclose;
-err_nuts:
- svsk->sk_datalen = 0;
-err_delete:
- trace_svcsock_tcp_recv_err(&svsk->sk_xprt, len);
- svc_xprt_deferred_close(&svsk->sk_xprt);
-err_noclose:
- svc_xprt_received(rqstp->rq_xprt);
- return 0; /* record not complete */
-}
-
/*
* MSG_SPLICE_PAGES is used exclusively to reduce the number of
* copy operations in this path. Therefore the caller must ensure
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net v3] phonet: check register_netdevice_notifier() error in phonet_device_init()
From: Andrew Lunn @ 2026-07-20 14:29 UTC (permalink / raw)
To: Minhong He
Cc: courmisch, davem, edumazet, kuba, pabeni, horms,
remi.denis-courmont, netdev, linux-kernel
In-Reply-To: <20260720070031.108248-1-heminhong@kylinos.cn>
On Mon, Jul 20, 2026 at 03:00:31PM +0800, Minhong He wrote:
> phonet_device_init() registers a netdevice notifier before calling
> phonet_netlink_register(), but does not check whether notifier
> registration succeeded. On failure, netlink setup still proceeds and
> init may return success without the notifier in place.
>
> Also, the existing phonet_netlink_register() failure path called
> phonet_device_exit(), which runs rtnl_unregister_all() even though
> rtnl_register_many() already unwound any partial registration. Calling
> the full exit helper on a partial init is not correct.
>
> Check each registration error and unwind only the steps that have
> succeeded so far.
>
> Signed-off-by: Minhong He <heminhong@kylinos.cn>
> ---
> v3:
> - Use goto-based unwind; do not call phonet_device_exit() on
> phonet_netlink_register() failure (avoids rtnl_unregister_all()
> after rtnl_register_many() already unwound).
> - Drop Fixes tag (theoretical init failure path; not suitable for
> stable autosel).
> v2: https://lore.kernel.org/netdev/20260716101504.158387-1-heminhong@kylinos.cn/
> - On notifier registration failure, unwind only proc/pernet.
> v1: https://lore.kernel.org/netdev/20260713075212.431455-1-heminhong@kylinos.cn/
>
> net/phonet/pn_dev.c | 20 +++++++++++++++++---
> 1 file changed, 17 insertions(+), 3 deletions(-)
>
> diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
> index ad44831d6745..f41322a12fb7 100644
> --- a/net/phonet/pn_dev.c
> +++ b/net/phonet/pn_dev.c
> @@ -350,16 +350,30 @@ static struct pernet_operations phonet_net_ops = {
> /* Initialize Phonet devices list */
> int __init phonet_device_init(void)
> {
> - int err = register_pernet_subsys(&phonet_net_ops);
> + int err;
> +
> + err = register_pernet_subsys(&phonet_net_ops);
> if (err)
> return err;
>
> proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops,
> sizeof(struct seq_net_private));
> - register_netdevice_notifier(&phonet_device_notifier);
> +
> + err = register_netdevice_notifier(&phonet_device_notifier);
> + if (err)
> + goto err_pernet;
> +
> err = phonet_netlink_register();
> if (err)
> - phonet_device_exit();
> + goto err_notifier;
> +
> + return 0;
> +
> +err_notifier:
> + unregister_netdevice_notifier(&phonet_device_notifier);
> +err_pernet:
> + unregister_pernet_subsys(&phonet_net_ops);
> + remove_proc_entry("pnresource", init_net.proc_net);
It is good practice to undo in the opposite order to which it was
done. Sometimes there are dependencies, and things will break if you
tear them down in the wrong order.
It is also unusual to see two undo steps without a goto label between
them. Can proc_create_net() fail? Should the return value be tested
and cleanup done if it fails?
Andrew
---
pw-bot: cr
^ permalink raw reply
* Re: [PATCH 3/8] iommu/fsl: use platform_device_set_fwnode()
From: Frank Li @ 2026-07-20 14:34 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla, brgl, driver-core,
linuxppc-dev, linux-kernel, linux-i2c, iommu, netdev, linux-pm,
imx, linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-3-2dee93f42c54@oss.qualcomm.com>
On Mon, Jul 20, 2026 at 11:24:50AM +0200, Bartosz Golaszewski wrote:
> Prefer the higher-level platform_device_set_fwnode() over the
> OF-specific platform_device_set_of_node() for dynamically allocated
> platform devices.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/iommu/fsl_pamu.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c
> index c83bbc3faad56d6ee1c89b0a7f74028af02c81e9..268a1f752fbceab4fd24013aeea5df1b6982fbb1 100644
> --- a/drivers/iommu/fsl_pamu.c
> +++ b/drivers/iommu/fsl_pamu.c
> @@ -975,7 +975,7 @@ static __init int fsl_pamu_init(void)
> goto error_device_alloc;
> }
>
> - platform_device_set_of_node(pdev, np);
> + platform_device_set_fwnode(pdev, of_fwnode_handle(np));
>
> ret = pamu_domain_init();
> if (ret)
>
> --
> 2.47.3
>
>
^ permalink raw reply
* Re: [PATCH 3/8] iommu/fsl: use platform_device_set_fwnode()
From: Robin Murphy @ 2026-07-20 14:34 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: driver-core, linuxppc-dev, linux-kernel, linux-i2c, iommu, netdev,
linux-pm, imx, linux-arm-kernel, mfd, linux-arm-msm, linux-sound,
Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Andi Shyti,
Joerg Roedel (AMD), Will Deacon, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla
In-Reply-To: <CAMRc=MefqCMgVv-o5hWoRwS2iXPQNs5nH2qHiz55+ew162LfSA@mail.gmail.com>
On 20/07/2026 2:39 pm, Bartosz Golaszewski wrote:
> On Mon, 20 Jul 2026 14:58:27 +0200, Robin Murphy <robin.murphy@arm.com> said:
>> On 20/07/2026 10:24 am, Bartosz Golaszewski wrote:
>>> Prefer the higher-level platform_device_set_fwnode() over the
>>> OF-specific platform_device_set_of_node() for dynamically allocated
>>> platform devices.
>>
>> This is very much non-portable code specific to OF-only platforms, but
>> if the intention is to remove platform_device_set_of_node() again
>> already, then FWIW,
>>
>
> Providing platform_device_set_of_node() and using it was done to make the
> transision to expanding reference counting to all firmware nodes possible.
> I don't think we'll remove it just yet as it doesn't make sense to convert
> the code under drivers/of/ to using the fwnode variant.
OK, but in that case why convert these users either? If the OF helper
does continue to exist then I'd imagine the static checker brigade will
eventually end up sending patches to "simplify" these open-coded
equivalents back to using it. And frankly, if drivers do know for sure
they're exclusively dealing with of_nodes, rather than doing something
conditional under an is_of_node() check, then I see little justification
for them *not* using the dedicated helper.
If the complaint is that there are no *public* users to justify
exporting platform_device_set_fwnode(), then as I say AFAICS that's much
more neatly addressed with the static inline approach, such that we
still get to unify the public APIs, actively eliminate something from
the symbol table and save a bit of source and object code, but without
any need to churn the truly OF-based callers at all.
Thanks,
Robin.
>
>> Acked-by: Robin Murphy <robin.murphy@arm.com>
>>
>> (Although I'm slightly puzzled by the cover letter - AFAICS in -next,
>> platform_device_set_of_node() is itself very much a user of
>> platform_device_set_fwnode(), however in terms of symbol exports,
>> perhaps the former could now just be a static inline wrapper?)
>>
>
> Sure that can be done independently later.
>
> Bart
^ permalink raw reply
* Re: [PATCH 5/8] pmdomain: imx: use platform_device_set_fwnode()
From: Frank Li @ 2026-07-20 14:34 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla, brgl, driver-core,
linuxppc-dev, linux-kernel, linux-i2c, iommu, netdev, linux-pm,
imx, linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-5-2dee93f42c54@oss.qualcomm.com>
On Mon, Jul 20, 2026 at 11:24:52AM +0200, Bartosz Golaszewski wrote:
> Prefer the higher-level platform_device_set_fwnode() over the
> OF-specific platform_device_set_of_node() for dynamically allocated
> platform devices.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
> drivers/pmdomain/imx/gpc.c | 2 +-
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c
> index abca5f449a226fbae4213926e1395c413160c950..c147eaf048ba2b79a744ec87029420981581e48e 100644
> --- a/drivers/pmdomain/imx/gpc.c
> +++ b/drivers/pmdomain/imx/gpc.c
> @@ -487,7 +487,7 @@ static int imx_gpc_probe(struct platform_device *pdev)
> domain->ipg_rate_mhz = ipg_rate_mhz;
>
> pd_pdev->dev.parent = &pdev->dev;
> - platform_device_set_of_node(pd_pdev, np);
> + platform_device_set_fwnode(pd_pdev, of_fwnode_handle(np));
>
> ret = platform_device_add(pd_pdev);
> if (ret) {
>
> --
> 2.47.3
>
>
^ permalink raw reply
* [PATCH v3 net] idpf: disable PTM on probe failure and on remove
From: Myeonghun Pak @ 2026-07-20 14:35 UTC (permalink / raw)
To: Tony Nguyen, Przemek Kitszel, intel-wired-lan
Cc: Milena Olech, Emil Tantilov, Mina Almasry, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, linux-kernel, Myeonghun Pak, Ijae Kim
idpf_probe() enables PCIe Precision Time Measurement with
pci_enable_ptm(), which takes a reference on the device and on every
PTM-capable device up the path to the PTM Root.
Neither the probe error path nor idpf_remove() drops that reference, so
the PTM enable counts of this device and of its upstream path stay
elevated with no bound driver, and the device's PTM control bits remain
set. pcim_enable_device() only arranges for pci_disable_device() and
does not undo the PTM enable.
Add the matching pci_disable_ptm() to the common probe unwind and to
idpf_remove(). pci_enable_ptm() failure is not fatal here, so guard both
calls with pcie_ptm_enabled(): pci_disable_ptm() decrements
dev->ptm_enable_cnt unconditionally and then recurses upstream, so
calling it after a failed enable would drive this device's count negative
and wrongly decrement parents shared with other endpoints.
This issue was identified during our ongoing static-analysis research
while reviewing kernel code.
Fixes: 8d5e12c5921c ("idpf: add initial PTP support")
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
---
Changes in v3:
- Rebased; aa8671af0c38 ("PCI/PTM: Drop pci_enable_ptm() granularity
parameter") changed the call signature, so v2 no longer applied.
- Guard both pci_disable_ptm() calls with pcie_ptm_enabled(), as
pci_disable_ptm() is refcounted and recurses upstream since
e1092d5e15e6 ("PCI/PTM: Do not enable PTM automatically for Root and
Switch Upstream Ports"). Raised by Tony Nguyen.
- Dropped the v2 claim that pci_disable_ptm() is a no-op when PTM was not
enabled; that is no longer true.
Changes in v2:
- Disable PTM in the probe error path, as requested by Emil Tantilov.
drivers/net/ethernet/intel/idpf/idpf_main.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
index ab3c409..97bafeb 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_main.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -159,6 +159,8 @@ destroy_wqs:
mutex_destroy(&adapter->queue_lock);
mutex_destroy(&adapter->vc_buf_lock);
+ if (pcie_ptm_enabled(pdev))
+ pci_disable_ptm(pdev);
pci_set_drvdata(pdev, NULL);
kfree(adapter);
}
@@ -266,7 +268,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err) {
pci_err(pdev, "DMA configuration failed: %pe\n", ERR_PTR(err));
- goto err_free;
+ goto err_disable_ptm;
}
pci_set_master(pdev);
@@ -279,7 +281,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!adapter->init_wq) {
dev_err(dev, "Failed to allocate init workqueue\n");
err = -ENOMEM;
- goto err_free;
+ goto err_disable_ptm;
}
adapter->serv_wq = alloc_workqueue("%s-%s-service",
@@ -366,6 +368,9 @@ err_mbx_wq_alloc:
destroy_workqueue(adapter->serv_wq);
err_serv_wq_alloc:
destroy_workqueue(adapter->init_wq);
+err_disable_ptm:
+ if (pcie_ptm_enabled(pdev))
+ pci_disable_ptm(pdev);
err_free:
kfree(adapter);
return err;
--
2.47.1
^ permalink raw reply related
* Re: [PATCH 4/8] net: bcmgenet: use platform_device_set_fwnode()
From: Andrew Lunn @ 2026-07-20 14:38 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla, brgl, driver-core,
linuxppc-dev, linux-kernel, linux-i2c, iommu, netdev, linux-pm,
imx, linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-4-2dee93f42c54@oss.qualcomm.com>
On Mon, Jul 20, 2026 at 11:24:51AM +0200, Bartosz Golaszewski wrote:
> Prefer the higher-level platform_device_set_fwnode() over the
> OF-specific platform_device_set_of_node() for dynamically allocated
> platform devices.
Why?
This driver is OF only. It does not support ACPI, and probably never
will. In general, networking and ACPI don't go together, ACPI is not
sufficiently advanced.
What is you use case here?
Andrew
^ permalink raw reply
* Re: [PATCH 1/3] usb: chipidea: Use %pe to print error pointers
From: Frank Li @ 2026-07-20 14:39 UTC (permalink / raw)
To: Subasri S
Cc: Peter Chen, Greg Kroah-Hartman, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Duncan Sands,
Chas Williams, Minas Harutyunyan, Hans de Goede, Heikki Krogerus,
Badhri Jagan Sridharan, linux-usb, imx, linux-arm-kernel,
linux-kernel, linux-atm-general, netdev
In-Reply-To: <20260719-usb-ptr_err_patchset-v1-1-85f7f2e4fefb@gmail.com>
On Sun, Jul 19, 2026 at 06:25:46PM +0530, Subasri S wrote:
>
> Use the %pe format specifier instead of %ld with PTR_ERR() for printing
> error pointers in imx_get_clks(), ci_hdrc_imx_probe(), and
> ci_get_platdata(). This prints symbolic error names (e.g. -ENOMEM)
> instead of errno numbers (e.g. -12), making error logs more readable.
>
> This patch fixes coccinelle reported warnings:
Avoid use words "This patch", just
Fix coccinelle reported warnings:
> ./chipidea/ci_hdrc_imx.c:452:5-12: WARNING: Consider using %pe to print PTR_ERR()
> ./chipidea/ci_hdrc_imx.c:468:5-12: WARNING: Consider using %pe to print PTR_ERR()
> ./chipidea/ci_hdrc_imx.c:222:4-11: WARNING: Consider using %pe to print PTR_ERR()
> ./chipidea/ci_hdrc_imx.c:222:24-31: WARNING: Consider using %pe to print PTR_ERR()
keep one is enough
> ./chipidea/core.c:684:4-11: WARNING: Consider using %pe to print PTR_ERR()
>
> Compile-tested only.
>
> Signed-off-by: Subasri S <subasris1210@gmail.com>
> ---
> drivers/usb/chipidea/ci_hdrc_imx.c | 12 ++++++------
> drivers/usb/chipidea/core.c | 4 ++--
> 2 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
> index 56d2ba824a0b..7bfe37ed68ae 100644
> --- a/drivers/usb/chipidea/ci_hdrc_imx.c
> +++ b/drivers/usb/chipidea/ci_hdrc_imx.c
> @@ -218,8 +218,8 @@ static int imx_get_clks(struct device *dev)
> if (IS_ERR(data->clk)) {
> ret = PTR_ERR(data->clk);
> dev_err(dev,
> - "Failed to get clks, err=%ld,%ld\n",
> - PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
> + "Failed to get clks, err=%pe,%pe\n",
> + data->clk, data->clk_ipg);
> return ret;
> }
> /* Get wakeup clock. Not all of the platforms need to
> @@ -448,8 +448,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
> pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
> if (IS_ERR(pinctrl_hsic_idle)) {
> dev_err(dev,
> - "pinctrl_hsic_idle lookup failed, err=%ld\n",
> - PTR_ERR(pinctrl_hsic_idle));
> + "pinctrl_hsic_idle lookup failed, err=%pe\n",
> + pinctrl_hsic_idle);
> ret = PTR_ERR(pinctrl_hsic_idle);
> goto err_put;
> }
> @@ -464,8 +464,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
> "active");
> if (IS_ERR(data->pinctrl_hsic_active)) {
> dev_err(dev,
> - "pinctrl_hsic_active lookup failed, err=%ld\n",
> - PTR_ERR(data->pinctrl_hsic_active));
> + "pinctrl_hsic_active lookup failed, err=%pe\n",
> + data->pinctrl_hsic_active);
> ret = PTR_ERR(data->pinctrl_hsic_active);
> goto err_put;
> }
> diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
> index 07563be0013f..09db8a4eace2 100644
> --- a/drivers/usb/chipidea/core.c
> +++ b/drivers/usb/chipidea/core.c
> @@ -680,8 +680,8 @@ static int ci_get_platdata(struct device *dev,
> /* no vbus regulator is needed */
> platdata->reg_vbus = NULL;
> } else if (IS_ERR(platdata->reg_vbus)) {
> - dev_err(dev, "Getting regulator error: %ld\n",
> - PTR_ERR(platdata->reg_vbus));
> + dev_err(dev, "Getting regulator error: %pe\n",
> + platdata->reg_vbus);
> return PTR_ERR(platdata->reg_vbus);
> }
> /* Get TPL support */
>
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH net v2] net: stmmac: dwmac4: mask interrupts when stopping DMA in suspend
From: Maxime Chevallier @ 2026-07-20 14:40 UTC (permalink / raw)
To: Luis Lang, netdev
Cc: Andrew Lunn, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Russell King (Oracle), Oleksij Rempel, Ovidiu Panait,
Rohan G Thomas, moderated list:ARM/STM32 ARCHITECTURE,
moderated list:ARM/STM32 ARCHITECTURE, open list
In-Reply-To: <20260720111534.163416-1-luis.la@mail.de>
Hi Luis,
On 7/20/26 13:15, Luis Lang wrote:
> Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU
> interrupts"), suspending causes an interrupt storm from the RPS
> interrupt.
> Fix this by adding a deinit_chan() op to stmmac_dma_ops, which
> masks all default dma channel interrupts. This is called from
> stmmac_stop_all_dma(), so interrupts don't trigger while suspending.
>
> Fixes: 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
> Suggested-by: Andrew Lunn <andrew@lunn.ch>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Signed-off-by: Luis Lang <luis.la@mail.de>
I wasn't able to reproduce the original issue on dwmac4, however
I could test that suspend/resume as well as WoL still works on a
dwmac4 device with this patch applied.
Thanks for the patch !
Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Maxime
> ---
> .../net/ethernet/stmicro/stmmac/dwmac4_dma.c | 24 +++++++++++++++++++
> drivers/net/ethernet/stmicro/stmmac/hwif.h | 4 ++++
> .../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++
> 3 files changed, 32 insertions(+)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
> index 829a23bdad01..23ffe1adcd0d 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
> @@ -106,6 +106,17 @@ static void dwmac4_dma_init_channel(struct stmmac_priv *priv,
> ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> }
>
> +static void dwmac4_dma_deinit_channel(struct stmmac_priv *priv,
> + void __iomem *ioaddr, u32 chan)
> +{
> + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
> + u32 value;
> +
> + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> + value &= ~DMA_CHAN_INTR_DEFAULT_MASK;
> + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> +}
> +
> static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
> void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg, u32 chan)
> @@ -125,6 +136,17 @@ static void dwmac410_dma_init_channel(struct stmmac_priv *priv,
> ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> }
>
> +static void dwmac410_dma_deinit_channel(struct stmmac_priv *priv,
> + void __iomem *ioaddr, u32 chan)
> +{
> + const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs;
> + u32 value;
> +
> + value = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> + value &= ~DMA_CHAN_INTR_DEFAULT_MASK_4_10;
> + writel(value, ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan));
> +}
> +
> static void dwmac4_dma_init(void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg)
> {
> @@ -548,6 +570,7 @@ const struct stmmac_dma_ops dwmac4_dma_ops = {
> .reset = dwmac4_dma_reset,
> .init = dwmac4_dma_init,
> .init_chan = dwmac4_dma_init_channel,
> + .deinit_chan = dwmac4_dma_deinit_channel,
> .init_rx_chan = dwmac4_dma_init_rx_chan,
> .init_tx_chan = dwmac4_dma_init_tx_chan,
> .axi = dwmac4_dma_axi,
> @@ -577,6 +600,7 @@ const struct stmmac_dma_ops dwmac410_dma_ops = {
> .reset = dwmac4_dma_reset,
> .init = dwmac4_dma_init,
> .init_chan = dwmac410_dma_init_channel,
> + .deinit_chan = dwmac410_dma_deinit_channel,
> .init_rx_chan = dwmac4_dma_init_rx_chan,
> .init_tx_chan = dwmac4_dma_init_tx_chan,
> .axi = dwmac4_dma_axi,
> diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
> index e6317b94fff7..04dafec021b4 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
> @@ -170,6 +170,8 @@ struct stmmac_dma_ops {
> void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg);
> void (*init_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg, u32 chan);
> + void (*deinit_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
> + u32 chan);
> void (*init_rx_chan)(struct stmmac_priv *priv, void __iomem *ioaddr,
> struct stmmac_dma_cfg *dma_cfg,
> dma_addr_t phy, u32 chan);
> @@ -235,6 +237,8 @@ struct stmmac_dma_ops {
> stmmac_do_void_callback(__priv, dma, init, __args)
> #define stmmac_init_chan(__priv, __args...) \
> stmmac_do_void_callback(__priv, dma, init_chan, __priv, __args)
> +#define stmmac_deinit_chan(__priv, __args...) \
> + stmmac_do_void_callback(__priv, dma, deinit_chan, __priv, __args)
> #define stmmac_init_rx_chan(__priv, __args...) \
> stmmac_do_void_callback(__priv, dma, init_rx_chan, __priv, __args)
> #define stmmac_init_tx_chan(__priv, __args...) \
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 2a0d7eff88d3..af29a50ddb89 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -2560,6 +2560,7 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
> {
> u8 rx_channels_count = priv->plat->rx_queues_to_use;
> u8 tx_channels_count = priv->plat->tx_queues_to_use;
> + u8 dma_csr_ch = max(rx_channels_count, tx_channels_count);
> u8 chan;
>
> for (chan = 0; chan < rx_channels_count; chan++)
> @@ -2567,6 +2568,9 @@ static void stmmac_stop_all_dma(struct stmmac_priv *priv)
>
> for (chan = 0; chan < tx_channels_count; chan++)
> stmmac_stop_tx_dma(priv, chan);
> +
> + for (chan = 0; chan < dma_csr_ch; chan++)
> + stmmac_deinit_chan(priv, priv->ioaddr, chan);
> }
>
> /**
^ permalink raw reply
* [PATCH net-next v2] net: Replace %pK output with 0
From: Sebastian Andrzej Siewior @ 2026-07-20 14:40 UTC (permalink / raw)
To: linux-atm-general, linux-can, linux-sctp, netdev
Cc: David S. Miller, Eric Dumazet, Herbert Xu, Jakub Kicinski,
Kuniyuki Iwashima, Marc Kleine-Budde, Marcelo Ricardo Leitner,
Neal Cardwell, Oliver Hartkopp, Paolo Abeni, Remi Denis-Courmont,
Simon Horman, Steffen Klassert, Willem de Bruijn, Xin Long,
Petr Mladek, Thomas Weißschuh, Kees Cook
Commit 71338aa7d050c ("net: convert %p usage to %pK") which is from
2011 and changed the %p annotation for pointer to %pK. Back then the
default behaviour for %p was to print the pointer. The %pK modifier was
introduced to able to control the behaviour of specific pointer values
without changing the behaviour of %p for everyone. It was dedicated to
avoid leaking pointers via /proc.
There was also the idea to remove the check from formatting the string
and move to the open callback with some helpers but this did not happen.
Things changed over time. The default behaviour for %p is now to print a
hash pointer which does not leak the address but allows to correlate if
two pointers are equal.
The policy on %p is to not introduce new ones. This is somehow in
between since it already exists. The pointer are usually socket pointers
and I don't see any value in exposing them. Therefore I am following the
recommendation of removing them. Since their usage in /proc/ can be
considered ABI I replace the pointer with a 0.
Replace the %pK annotation with 0 value. Correct the spacing for the
cases where pointer is at the beginning. Use %ps in CAN where the read
callback is used.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
v1…v2: https://lore.kernel.org/all/20260706073824.xixrLxoD@linutronix.de
- This follows Kees' feedback regarding the general policy and "is it
really needed?". Since I don't think that this is of general usage I
replaced them all with 0 and corrected the spacing at the beginning.
- can_print_rcvlist() is now using a %ps to print the name of the
function. Everything is a sock pointer (or some other data structure)
and is now 0.
net/atm/proc.c | 7 +++----
net/can/bcm.c | 4 +---
net/can/proc.c | 12 ++++--------
net/ipv4/ping.c | 5 ++---
net/ipv4/raw.c | 4 ++--
net/ipv4/tcp_ipv4.c | 13 ++++++-------
net/ipv4/udp.c | 5 ++---
net/ipv6/datagram.c | 5 ++---
net/ipv6/tcp_ipv6.c | 12 ++++++------
net/key/af_key.c | 5 ++---
net/netlink/af_netlink.c | 5 ++---
net/packet/af_packet.c | 6 ++----
net/phonet/socket.c | 5 ++---
net/sctp/proc.c | 6 +++---
net/unix/af_unix.c | 5 ++---
15 files changed, 41 insertions(+), 58 deletions(-)
diff --git a/net/atm/proc.c b/net/atm/proc.c
index 8f20b49b9c02a..4db7036ba8e72 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -159,7 +159,7 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc)
{
struct sock *sk = sk_atm(vcc);
- seq_printf(seq, "%pK ", vcc);
+ seq_printf(seq, " 0 ");
if (!vcc->dev)
seq_printf(seq, "Unassigned ");
else
@@ -228,9 +228,8 @@ static const struct seq_operations pvc_seq_ops = {
static int vcc_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN) {
- seq_printf(seq, sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
- "Address ", "Itf VPI VCI Fam Flags Reply "
- "Send buffer Recv buffer [refcnt]\n");
+ seq_printf(seq, "Address Itf VPI VCI Fam Flags Reply "
+ "Send buffer Recv buffer [refcnt]\n");
} else {
struct vcc_state *state = seq->private;
struct atm_vcc *vcc = atm_sk(state->sk);
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 3d637a1e0ac1a..c262d9530f553 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -220,9 +220,7 @@ static int bcm_proc_show(struct seq_file *m, void *v)
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
- seq_printf(m, ">>> socket %pK", sk->sk_socket);
- seq_printf(m, " / sk %pK", sk);
- seq_printf(m, " / bo %pK", bo);
+ seq_printf(m, ">>> socket 0 / sk 0 / bo 0");
seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex));
seq_printf(m, " <<<\n");
diff --git a/net/can/proc.c b/net/can/proc.c
index de4d05ae34597..cc3050f4c8e75 100644
--- a/net/can/proc.c
+++ b/net/can/proc.c
@@ -192,12 +192,11 @@ static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
hlist_for_each_entry_rcu(r, rx_list, list) {
char *fmt = (r->can_id & CAN_EFF_FLAG)?
- " %-5s %08x %08x %pK %pK %8ld %s\n" :
- " %-5s %03x %08x %pK %pK %8ld %s\n";
+ " %-5s %08x %08x %ps %8u %8ld %s\n" :
+ " %-5s %03x %08x %-20ps %8u %8ld %s\n";
seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
- r->func, r->data, atomic_long_read(&r->matches),
- r->ident);
+ r->func, 0, atomic_long_read(&r->matches), r->ident);
}
}
@@ -207,10 +206,7 @@ static void can_print_recv_banner(struct seq_file *m)
* can1. 00000000 00000000 00000000
* ....... 0 tp20
*/
- if (IS_ENABLED(CONFIG_64BIT))
- seq_puts(m, " device can_id can_mask function userdata matches ident\n");
- else
- seq_puts(m, " device can_id can_mask function userdata matches ident\n");
+ seq_puts(m, " device can_id can_mask function userdata matches ident\n");
}
static int can_stats_proc_show(struct seq_file *m, void *v)
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index d36f1e273fde4..223a0108b74cc 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -1095,15 +1095,14 @@ static void ping_v4_format_sock(struct sock *sp, struct seq_file *f,
__u16 srcp = ntohs(inet->inet_sport);
seq_printf(f, "%5d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d %pK %u",
+ " %02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d 0 %u",
bucket, src, srcp, dest, destp, sp->sk_state,
sk_wmem_alloc_get(sp),
sk_rmem_alloc_get(sp),
0, 0L, 0,
from_kuid_munged(seq_user_ns(f), sk_uid(sp)),
0, sock_i_ino(sp),
- refcount_read(&sp->sk_refcnt), sp,
- sk_drops_read(sp));
+ refcount_read(&sp->sk_refcnt), sk_drops_read(sp));
}
static int ping_v4_seq_show(struct seq_file *seq, void *v)
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 2aebaf8297e04..0a3e561f670bd 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -1045,14 +1045,14 @@ static void raw_sock_seq_show(struct seq_file *seq, struct sock *sp, int i)
srcp = inet->inet_num;
seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d %pK %u\n",
+ " %02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d 0 %u\n",
i, src, srcp, dest, destp, sp->sk_state,
sk_wmem_alloc_get(sp),
sk_rmem_alloc_get(sp),
0, 0L, 0,
from_kuid_munged(seq_user_ns(seq), sk_uid(sp)),
0, sock_i_ino(sp),
- refcount_read(&sp->sk_refcnt), sp, sk_drops_read(sp));
+ refcount_read(&sp->sk_refcnt), sk_drops_read(sp));
}
static int raw_seq_show(struct seq_file *seq, void *v)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 4a46da375043b..3a0ce1743642a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2745,7 +2745,7 @@ static void get_openreq4(const struct request_sock *req,
long delta = req->rsk_timer.expires - jiffies;
seq_printf(f, "%4d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK",
+ " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d 0",
i,
ireq->ir_loc_addr,
ireq->ir_num,
@@ -2760,8 +2760,7 @@ static void get_openreq4(const struct request_sock *req,
sk_uid(req->rsk_listener)),
0, /* non standard timer */
0, /* open_requests have no inode */
- 0,
- req);
+ 0);
}
static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
@@ -2808,7 +2807,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
READ_ONCE(tp->copied_seq), 0);
seq_printf(f, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX "
- "%08X %5u %8d %llu %d %pK %lu %lu %u %u %d",
+ "%08X %5u %8d %llu %d 0 %lu %lu %u %u %d",
i, src, srcp, dest, destp, state,
READ_ONCE(tp->write_seq) - tp->snd_una,
rx_queue,
@@ -2818,7 +2817,7 @@ static void get_tcp4_sock(struct sock *sk, struct seq_file *f, int i)
from_kuid_munged(seq_user_ns(f), sk_uid(sk)),
READ_ONCE(icsk->icsk_probes_out),
sock_i_ino(sk),
- refcount_read(&sk->sk_refcnt), sk,
+ refcount_read(&sk->sk_refcnt),
jiffies_to_clock_t(icsk->icsk_rto),
jiffies_to_clock_t(icsk->icsk_ack.ato),
(icsk->icsk_ack.quick << 1) | inet_csk_in_pingpong_mode(sk),
@@ -2841,10 +2840,10 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw,
srcp = ntohs(tw->tw_sport);
seq_printf(f, "%4d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK",
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d 0",
i, src, srcp, dest, destp, READ_ONCE(tw->tw_substate), 0, 0,
3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
- refcount_read(&tw->tw_refcnt), tw);
+ refcount_read(&tw->tw_refcnt));
}
#define TMPSZ 150
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 59248a59358ca..b35e448e48c59 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -3280,15 +3280,14 @@ static void udp4_format_sock(struct sock *sp, struct seq_file *f,
__u16 srcp = ntohs(inet->inet_sport);
seq_printf(f, "%5d: %08X:%04X %08X:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d %pK %u",
+ " %02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d 0 %u",
bucket, src, srcp, dest, destp, sp->sk_state,
sk_wmem_alloc_get(sp),
udp_rqueue_get(sp),
0, 0L, 0,
from_kuid_munged(seq_user_ns(f), sk_uid(sp)),
0, sock_i_ino(sp),
- refcount_read(&sp->sk_refcnt), sp,
- sk_drops_read(sp));
+ refcount_read(&sp->sk_refcnt), sk_drops_read(sp));
}
static int udp4_seq_show(struct seq_file *seq, void *v)
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 38d7b48452817..191c9733ff9fa 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -1102,7 +1102,7 @@ void __ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
src = &sp->sk_v6_rcv_saddr;
seq_printf(seq,
"%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d %pK %u\n",
+ "%02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d 0 %u\n",
bucket,
src->s6_addr32[0], src->s6_addr32[1],
src->s6_addr32[2], src->s6_addr32[3], srcp,
@@ -1115,6 +1115,5 @@ void __ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
from_kuid_munged(seq_user_ns(seq), sk_uid(sp)),
0,
sock_i_ino(sp),
- refcount_read(&sp->sk_refcnt), sp,
- sk_drops_read(sp));
+ refcount_read(&sp->sk_refcnt), sk_drops_read(sp));
}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 522ba45ce9b75..bc45e647c4956 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -2101,7 +2101,7 @@ static void get_openreq6(struct seq_file *seq,
seq_printf(seq,
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08lX %08X %5u %8d %d %d %pK\n",
+ "%02X %08X:%08X %02X:%08lX %08X %5u %8d %d %d 0\n",
i,
src->s6_addr32[0], src->s6_addr32[1],
src->s6_addr32[2], src->s6_addr32[3],
@@ -2118,7 +2118,7 @@ static void get_openreq6(struct seq_file *seq,
sk_uid(req->rsk_listener)),
0, /* non standard timer */
0, /* open_requests have no inode */
- 0, req);
+ 0);
}
static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
@@ -2169,7 +2169,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
seq_printf(seq,
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d %pK %lu %lu %u %u %d\n",
+ "%02X %08X:%08X %02X:%08lX %08X %5u %8d %llu %d 0 %lu %lu %u %u %d\n",
i,
src->s6_addr32[0], src->s6_addr32[1],
src->s6_addr32[2], src->s6_addr32[3], srcp,
@@ -2184,7 +2184,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
from_kuid_munged(seq_user_ns(seq), sk_uid(sp)),
READ_ONCE(icsk->icsk_probes_out),
sock_i_ino(sp),
- refcount_read(&sp->sk_refcnt), sp,
+ refcount_read(&sp->sk_refcnt),
jiffies_to_clock_t(icsk->icsk_rto),
jiffies_to_clock_t(icsk->icsk_ack.ato),
(icsk->icsk_ack.quick << 1) | inet_csk_in_pingpong_mode(sp),
@@ -2209,7 +2209,7 @@ static void get_timewait6_sock(struct seq_file *seq,
seq_printf(seq,
"%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
- "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %pK\n",
+ "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d\n",
i,
src->s6_addr32[0], src->s6_addr32[1],
src->s6_addr32[2], src->s6_addr32[3], srcp,
@@ -2217,7 +2217,7 @@ static void get_timewait6_sock(struct seq_file *seq,
dest->s6_addr32[2], dest->s6_addr32[3], destp,
READ_ONCE(tw->tw_substate), 0, 0,
3, jiffies_delta_to_clock_t(delta), 0, 0, 0, 0,
- refcount_read(&tw->tw_refcnt), tw);
+ refcount_read(&tw->tw_refcnt));
}
static int tcp6_seq_show(struct seq_file *seq, void *v)
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 1d8965d7f4f3c..aa12e16edfdcb 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3803,10 +3803,9 @@ static int pfkey_seq_show(struct seq_file *f, void *v)
struct sock *s = sk_entry(v);
if (v == SEQ_START_TOKEN)
- seq_printf(f ,"sk RefCnt Rmem Wmem User Inode\n");
+ seq_printf(f ,"sk RefCnt Rmem Wmem User Inode\n");
else
- seq_printf(f, "%pK %-6d %-6u %-6u %-6u %-6llu\n",
- s,
+ seq_printf(f, "0 %-6d %-6u %-6u %-6u %-6llu\n",
refcount_read(&s->sk_refcnt),
sk_rmem_alloc_get(s),
sk_wmem_alloc_get(s),
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 5202fe0b08671..d5172778f2a4a 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2700,14 +2700,13 @@ static int netlink_native_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN) {
seq_puts(seq,
- "sk Eth Pid Groups "
+ "sk Eth Pid Groups "
"Rmem Wmem Dump Locks Drops Inode\n");
} else {
struct sock *s = v;
struct netlink_sock *nlk = nlk_sk(s);
- seq_printf(seq, "%pK %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8u %-8llu\n",
- s,
+ seq_printf(seq, "0 %-3d %-10u %08x %-8d %-8d %-5d %-8d %-8u %-8llu\n",
s->sk_protocol,
nlk->portid,
nlk->groups ? (u32)nlk->groups[0] : 0,
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 8e6f3a734ba0b..177c2810cff48 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4719,15 +4719,13 @@ static int packet_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_printf(seq,
- "%*sRefCnt Type Proto Iface R Rmem User Inode\n",
- IS_ENABLED(CONFIG_64BIT) ? -17 : -9, "sk");
+ "sk RefCnt Type Proto Iface R Rmem User Inode\n");
else {
struct sock *s = sk_entry(v);
const struct packet_sock *po = pkt_sk(s);
seq_printf(seq,
- "%pK %-6d %-4d %04x %-5d %1d %-6u %-6u %-6llu\n",
- s,
+ "0 %-6d %-4d %04x %-5d %1d %-6u %-6u %-6llu\n",
refcount_read(&s->sk_refcnt),
s->sk_type,
ntohs(READ_ONCE(po->num)),
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index 631a99cdbd006..ad12b746d4fca 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -586,14 +586,13 @@ static int pn_sock_seq_show(struct seq_file *seq, void *v)
struct pn_sock *pn = pn_sk(sk);
seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %llu "
- "%d %pK %u",
+ "%d 0 %u",
sk->sk_protocol, pn->sobject, pn->dobject,
pn->resource, sk->sk_state,
sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk),
from_kuid_munged(seq_user_ns(seq), sk_uid(sk)),
sock_i_ino(sk),
- refcount_read(&sk->sk_refcnt), sk,
- sk_drops_read(sk));
+ refcount_read(&sk->sk_refcnt), sk_drops_read(sk));
}
seq_pad(seq, '\n');
return 0;
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 43433d7e2acd7..7ea123b90aa59 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -174,7 +174,7 @@ static int sctp_eps_seq_show(struct seq_file *seq, void *v)
sk = ep->base.sk;
if (!net_eq(sock_net(sk), seq_file_net(seq)))
continue;
- seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5u %5llu ", ep, sk,
+ seq_printf(seq, "%8d %8d %-3d %-3d %-4d %-5d %5u %5llu ", 0, 0,
sctp_sk(sk)->type, sk->sk_state, hash,
ep->base.bind_addr.port,
from_kuid_munged(seq_user_ns(seq), sk_uid(sk)),
@@ -260,9 +260,9 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
sk = epb->sk;
seq_printf(seq,
- "%8pK %8pK %-3d %-3d %-2d %-4d "
+ "%8d %8d %-3d %-3d %-2d %-4d "
"%4d %8d %8d %7u %5llu %-5d %5d ",
- assoc, sk, sctp_sk(sk)->type, sk->sk_state,
+ 0, 0, sctp_sk(sk)->type, sk->sk_state,
assoc->state, 0,
assoc->assoc_id,
assoc->sndbuf_used,
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index f7a9d55eee8a1..ee3eb5eeee7e8 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -3547,15 +3547,14 @@ static int unix_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
- seq_puts(seq, "Num RefCount Protocol Flags Type St "
+ seq_puts(seq, "Num RefCount Protocol Flags Type St "
"Inode Path\n");
else {
struct sock *s = v;
struct unix_sock *u = unix_sk(s);
unix_state_lock(s);
- seq_printf(seq, "%pK: %08X %08X %08X %04X %02X %5llu",
- s,
+ seq_printf(seq, "0: %08X %08X %08X %04X %02X %5llu",
refcount_read(&s->sk_refcnt),
0,
s->sk_state == TCP_LISTEN ? __SO_ACCEPTCON : 0,
--
2.53.0
^ permalink raw reply related
* [PATCH net 0/6] pull request: fixes for ovpn 2026-07-20
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, Antonio Quartulli
Hi all!
This is a resend of the series of small fixes I sent on 2026-06-08,
now rebased on top of the latest net/main.
Changes compared to the previous submission are the addition of the
committer Signed-off-by tags that were missing on two patches (as
flagged by the SoB checker) and the amending of the commit message
about the new clock function being used for keepalive tracking.
No code was changed.
There are larger fixes in our queue which we are still working on,
therefore please ignore any "previous issue" Sashiko may report.
Please pull or let me know of any issue!
Thanks a lot,
Antonio
The following changes since commit e13caf1c26587434f0b768193100440939c0fb91:
Merge tag 'net-7.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-07-17 10:25:13 -0700)
are available in the Git repository at:
https://github.com/OpenVPN/ovpn-net-next.git ovpn-net-20260720
for you to fetch changes up to f7e6287ccd3abeed9e638b581dc3fdf742106ba3:
ovpn: use monotonic clock for peer keepalive timeouts (2026-07-20 16:29:31 +0200)
----------------------------------------------------------------
Included fixes:
* ensure keepalive timestamps are computed using monotonic source
* avoid UAF in unlock_ovpn() when iterating over release_list
* fix memleak in selftest tool
* ensure reference to peer is acquired before scheduling worker
(which may drop the not-yet-taken ref)
* fix refcount leak in case of concurrent TX and RX TCP error
* fix potential refcount unbalance in case of sock release in
P2P mode
----------------------------------------------------------------
Marco Baffo (2):
ovpn: fix use after free in unlock_ovpn()
ovpn: use monotonic clock for peer keepalive timeouts
Pavitra Jha (1):
ovpn: fix peer refcount leak in TCP error paths
Qing Ming (1):
ovpn: avoid putting unrelated P2P peer on socket release
Shuvam Pandey (1):
ovpn: hold peer before scheduling keepalive work
longlong yan (1):
selftests/net: ovpn: fix getaddrinfo memory leak in ovpn_parse_remote()
drivers/net/ovpn/io.c | 4 ++--
drivers/net/ovpn/peer.c | 16 +++++++++-------
drivers/net/ovpn/tcp.c | 6 ++++--
tools/testing/selftests/net/ovpn/ovpn-cli.c | 4 +++-
4 files changed, 18 insertions(+), 12 deletions(-)
^ permalink raw reply
* [PATCH net 1/6] ovpn: avoid putting unrelated P2P peer on socket release
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, Qing Ming, Simon Horman,
Antonio Quartulli
In-Reply-To: <20260720144131.3657121-1-antonio@openvpn.net>
From: Qing Ming <a0yami@mailbox.org>
ovpn_peer_release_p2p() is called when an OVPN UDP socket is being
destroyed. It checks the currently published P2P peer and releases it only
if that peer still uses the socket being destroyed.
A peer replacement can publish a new peer before the old UDP socket is
destroyed. When the old socket destruction path runs afterwards,
ovpn_peer_release_p2p() observes the new peer through ovpn->peer. Since the
new peer uses a different socket, the function takes the socket mismatch
branch.
That branch still calls ovpn_peer_put(peer). At this point, however, peer
is the currently published replacement peer, not the peer associated with
the socket being destroyed. Dropping its reference can free it while
ovpn->peer still points to it, leading to later use-after-free accesses
from the peer and socket cleanup paths.
KASAN reports this as a slab-use-after-free on the kmalloc-1k ovpn_peer
object. In the reproducer, the object is allocated from ovpn_peer_new() via
ovpn_nl_peer_new_doit(), and freed through ovpn_peer_release_rcu() from RCU
callback processing. Observed access sites include ovpn_peer_remove(),
ovpn_socket_release(), ovpn_nl_peer_del_notify(), and unlock_ovpn().
Fix this by returning from the socket mismatch branch without putting the
peer.
Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object")
Signed-off-by: Qing Ming <a0yami@mailbox.org>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/peer.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index a09d61296425..1844d97154ce 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -1167,7 +1167,6 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk,
ovpn_sock = rcu_access_pointer(peer->sock);
if (!ovpn_sock || ovpn_sock->sk != sk) {
spin_unlock_bh(&ovpn->lock);
- ovpn_peer_put(peer);
return;
}
}
--
2.54.0
^ permalink raw reply related
* [PATCH net 2/6] ovpn: fix peer refcount leak in TCP error paths
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, Pavitra Jha, stable,
Antonio Quartulli
In-Reply-To: <20260720144131.3657121-1-antonio@openvpn.net>
From: Pavitra Jha <jhapavitra98@gmail.com>
When either the TCP RX or TX error path calls ovpn_peer_hold() followed
by schedule_work(&peer->tcp.defer_del_work), and the work item is already
pending from the other path, schedule_work() returns false and the work
runs only once. Since ovpn_tcp_peer_del_work() calls ovpn_peer_put()
exactly once, the extra reference taken by the losing path is never
dropped, leaking the peer object.
The race window:
CPU0 (strparser/RX error): CPU1 (tcp_tx_work/TX error):
ovpn_peer_hold() <- refcnt+1 ovpn_peer_hold() <- refcnt+2
schedule_work() <- queued schedule_work() <- NO-OP
(work already pending)
ovpn_tcp_peer_del_work runs:
ovpn_peer_del()
ovpn_peer_put() <- refcnt+1
<- peer never freed
Fix by checking the return value of schedule_work() in both paths and
calling ovpn_peer_put() to drop the extra reference if the work was
already pending. ovpn_peer_hold() is kept unconditional in the TX path
as it cannot fail at that point.
Fixes: a6a5e87b3ee4 ("ovpn: avoid sleep in atomic context in TCP RX error path")
Cc: stable@vger.kernel.org
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/tcp.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 433bd07a4f1b..0af14055c39a 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -151,7 +151,8 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb)
/* take reference for deferred peer deletion. should never fail */
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
- schedule_work(&peer->tcp.defer_del_work);
+ if (!schedule_work(&peer->tcp.defer_del_work))
+ ovpn_peer_put(peer);
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
kfree_skb(skb);
@@ -283,7 +284,8 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
* stream therefore we abort the connection
*/
ovpn_peer_hold(peer);
- schedule_work(&peer->tcp.defer_del_work);
+ if (!schedule_work(&peer->tcp.defer_del_work))
+ ovpn_peer_put(peer);
/* we bail out immediately and keep tx_in_progress set
* to true. This way we prevent more TX attempts
--
2.54.0
^ permalink raw reply related
* [PATCH net 3/6] ovpn: hold peer before scheduling keepalive work
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, Shuvam Pandey, stable,
Antonio Quartulli
In-Reply-To: <20260720144131.3657121-1-antonio@openvpn.net>
From: Shuvam Pandey <shuvampandey1@gmail.com>
ovpn_peer_keepalive_send() passes its peer reference to
ovpn_xmit_special(), which ultimately drops it. The keepalive scheduler
currently queues the work first and takes the reference only after
schedule_work() reports that the work was queued.
Once schedule_work() queues the item, another CPU may run the worker
before the caller gets to ovpn_peer_hold(). In that case the worker can
consume a reference that was not acquired for it, corrupting the peer
lifetime accounting.
Take the peer reference before queueing the work and drop it again when
the work was already pending.
Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism")
Cc: stable@vger.kernel.org
Signed-off-by: Shuvam Pandey <shuvampandey1@gmail.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/peer.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 1844d97154ce..2b6096d8b1cc 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -1284,8 +1284,10 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
netdev_dbg(peer->ovpn->dev,
"sending keepalive to peer %u\n",
peer->id);
- if (schedule_work(&peer->keepalive_work))
- ovpn_peer_hold(peer);
+ if (WARN_ON(!ovpn_peer_hold(peer)))
+ return 0;
+ if (!schedule_work(&peer->keepalive_work))
+ ovpn_peer_put(peer);
}
if (next_run1 < next_run2)
--
2.54.0
^ permalink raw reply related
* [PATCH net 4/6] selftests/net: ovpn: fix getaddrinfo memory leak in ovpn_parse_remote()
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, longlong yan,
Antonio Quartulli
In-Reply-To: <20260720144131.3657121-1-antonio@openvpn.net>
From: longlong yan <yanlonglong@kylinos.cn>
The ovpn_parse_remote() function has two memory management issues:
1. When both 'host' and 'vpnip' are non-NULL, the first getaddrinfo()
allocation is leaked because 'result' is overwritten by the second
getaddrinfo() call without freeing the first allocation.
2. When both 'host' and 'vpnip' are NULL, 'result' is an uninitialized
stack variable passed to freeaddrinfo(), which is undefined behavior.
Fix by initializing 'result' to NULL and calling freeaddrinfo() after
the first getaddrinfo() result is consumed.
Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module")
Signed-off-by: longlong yan <yanlonglong@kylinos.cn>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/ovpn-cli.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index d40953375c86..f4effa7580c0 100644
--- a/tools/testing/selftests/net/ovpn/ovpn-cli.c
+++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c
@@ -1785,7 +1785,7 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
const char *service, const char *vpnip)
{
int ret;
- struct addrinfo *result;
+ struct addrinfo *result = NULL;
struct addrinfo hints = {
.ai_family = ovpn->sa_family,
.ai_socktype = SOCK_DGRAM,
@@ -1809,6 +1809,8 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
}
memcpy(&ovpn->remote, result->ai_addr, result->ai_addrlen);
+ freeaddrinfo(result);
+ result = NULL;
}
if (vpnip) {
--
2.54.0
^ permalink raw reply related
* [PATCH net 5/6] ovpn: fix use after free in unlock_ovpn()
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, Marco Baffo,
Antonio Quartulli
In-Reply-To: <20260720144131.3657121-1-antonio@openvpn.net>
From: Marco Baffo <marco@mandelbit.com>
unlock_ovpn() iterates over the release_list using llist_for_each_entry()
and drops the peer reference inside the loop body via ovpn_peer_put().
If this drops the last reference, the peer is eventually freed. However,
llist_for_each_entry() reads peer->release_entry.next in the loop advance
expression, which runs after the body. By that time the peer may have
already been freed, resulting in a use after free when advancing to the
next list entry.
Fix this by using llist_for_each_entry_safe(), which caches the next
pointer before executing the loop body.
Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object")
Signed-off-by: Marco Baffo <marco@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/peer.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 2b6096d8b1cc..8fdbb5050690 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -26,11 +26,12 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
struct llist_head *release_list)
__releases(&ovpn->lock)
{
- struct ovpn_peer *peer;
+ struct ovpn_peer *peer, *next;
spin_unlock_bh(&ovpn->lock);
- llist_for_each_entry(peer, release_list->first, release_entry) {
+ llist_for_each_entry_safe(peer, next, release_list->first,
+ release_entry) {
ovpn_socket_release(peer);
ovpn_peer_put(peer);
}
--
2.54.0
^ permalink raw reply related
* [PATCH net 6/6] ovpn: use monotonic clock for peer keepalive timeouts
From: Antonio Quartulli @ 2026-07-20 14:41 UTC (permalink / raw)
To: netdev
Cc: Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, David S. Miller,
Eric Dumazet, Andrew Lunn, Ralf Lici, Marco Baffo,
Antonio Quartulli
In-Reply-To: <20260720144131.3657121-1-antonio@openvpn.net>
From: Marco Baffo <marco@mandelbit.com>
Replace ktime_get_real_seconds() with the monotonic
ktime_get_boottime_seconds() to ensure the keepalive mechanism is robust
against system clock modifications.
Right now, the driver uses ktime_get_real_seconds() to track peer
timeouts, relying on the system wall-clock.
An administrative time adjustment or an NTP sync that steps the clock
forward can cause `now' to instantly exceed `last_recv + timeout'.
When this occurs, the driver artificially expires healthy peers.
Depending on the OpenVPN user-space configuration, this triggers a
premature tunnel restart (if --keepalive or --ping-restart is used) or
a complete disconnection of the client (if --ping-exit is used).
Fixes: 3ecfd9349f40 ("ovpn: implement keepalive mechanism")
Signed-off-by: Marco Baffo <marco@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
drivers/net/ovpn/io.c | 4 ++--
drivers/net/ovpn/peer.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index a6b777a9c2d9..9a66d693039a 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -142,7 +142,7 @@ void ovpn_decrypt_post(void *data, int ret)
}
/* keep track of last received authenticated packet for keepalive */
- WRITE_ONCE(peer->last_recv, ktime_get_real_seconds());
+ WRITE_ONCE(peer->last_recv, ktime_get_boottime_seconds());
rcu_read_lock();
sock = rcu_dereference(peer->sock);
@@ -294,7 +294,7 @@ void ovpn_encrypt_post(void *data, int ret)
ovpn_peer_stats_increment_tx(&peer->link_stats, orig_len);
/* keep track of last sent packet for keepalive */
- WRITE_ONCE(peer->last_sent, ktime_get_real_seconds());
+ WRITE_ONCE(peer->last_sent, ktime_get_boottime_seconds());
/* skb passed down the stack - don't free it */
skb = NULL;
err_unlock:
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 8fdbb5050690..a21d02ac715e 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -45,7 +45,7 @@ static void unlock_ovpn(struct ovpn_priv *ovpn,
*/
void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
{
- time64_t now = ktime_get_real_seconds();
+ time64_t now = ktime_get_boottime_seconds();
netdev_dbg(peer->ovpn->dev,
"scheduling keepalive for peer %u: interval=%u timeout=%u\n",
@@ -1359,7 +1359,7 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
{
struct ovpn_priv *ovpn = container_of(work, struct ovpn_priv,
keepalive_work.work);
- time64_t next_run = 0, now = ktime_get_real_seconds();
+ time64_t next_run = 0, now = ktime_get_boottime_seconds();
LLIST_HEAD(release_list);
spin_lock_bh(&ovpn->lock);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH 7/8] net: mv643xx: use platform_device_set_fwnode()
From: Andrew Lunn @ 2026-07-20 14:43 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla, brgl, driver-core,
linuxppc-dev, linux-kernel, linux-i2c, iommu, netdev, linux-pm,
imx, linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-7-2dee93f42c54@oss.qualcomm.com>
On Mon, Jul 20, 2026 at 11:24:54AM +0200, Bartosz Golaszewski wrote:
> Prefer the higher-level platform_device_set_fwnode() over the
> OF-specific platform_device_set_of_node() for dynamically allocated
> platform devices.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
> drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
> index 9caa1e47c174c9d7a161b7f2e2ee12a829b813d4..2f2d6cce8d852b9ec3ab42678a04a7915d1f00cc 100644
> --- a/drivers/net/ethernet/marvell/mv643xx_eth.c
> +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
> @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
> goto put_err;
> }
> ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> - platform_device_set_of_node(ppdev, pnp);
> + platform_device_set_fwnode(ppdev, of_fwnode_handle(pnp));
This is definitely an OF only driver. There are no other calls to
fwnode functions in this driver, so this is the wrong thing to do.
Sorry, NACK.
Andrew
^ 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