[PATCH 8/8] proc: take a lock-free exec_update_seq fast path in do_io_accounting()

From: Christian Brauner

Date: Mon May 25 2026 - 03:32:23 EST


/proc/<pid>/io took exec_update_lock for read to check
ptrace_may_access() before sampling the task's (or thread group's) IO
accounting. Convert it to exec_update_read_begin_or_lock(): snapshot the
accounting into a local under the speculative section (the whole-process
variant keeps its inner rcu + stats_lock), then emit after validation;
fall back to exec_update_lock on a racing exec()/TSYNC.

Signed-off-by: Christian Brauner (Amutable) <brauner@xxxxxxxxxx>
---
fs/proc/base.c | 31 +++++++++++++++++--------------
1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 83b851b7f9d9..3706c5167df0 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -3033,20 +3033,18 @@ static const struct file_operations proc_coredump_filter_operations = {
#ifdef CONFIG_TASK_IO_ACCOUNTING
static int do_io_accounting(struct task_struct *task, struct seq_file *m, int whole)
{
+ struct signal_struct *sig = task->signal;
struct task_io_accounting acct;
+ unsigned int seq = 0;
+ bool allowed = false;
int result;

- result = down_read_killable(&task->signal->exec_update_lock);
+retry:
+ result = exec_update_read_begin_or_killable(sig, &seq);
if (result)
return result;
-
- if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
- result = -EACCES;
- goto out_unlock;
- }
-
- if (whole) {
- struct signal_struct *sig = task->signal;
+ allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
+ if (allowed && whole) {
struct task_struct *t;

guard(rcu)();
@@ -3056,9 +3054,17 @@ static int do_io_accounting(struct task_struct *task, struct seq_file *m, int wh
task_io_accounting_add(&acct, &t->ioac);

}
- } else {
+ } else if (allowed) {
acct = task->ioac;
}
+ if (exec_update_read_needs_retry(sig, seq)) {
+ seq = 1;
+ goto retry;
+ }
+ exec_update_read_done(sig, seq);
+
+ if (!allowed)
+ return -EACCES;

seq_printf(m,
"rchar: %llu\n"
@@ -3075,11 +3081,8 @@ static int do_io_accounting(struct task_struct *task, struct seq_file *m, int wh
(unsigned long long)acct.read_bytes,
(unsigned long long)acct.write_bytes,
(unsigned long long)acct.cancelled_write_bytes);
- result = 0;

-out_unlock:
- up_read(&task->signal->exec_update_lock);
- return result;
+ return 0;
}

static int proc_tid_io_accounting(struct seq_file *m, struct pid_namespace *ns,
--
2.47.3


--j3ezp33mpunnwnqz--