Netdev List
 help / color / mirror / Atom feed
* [PATCH RFC 0/7] proc: move procfs to what kernfs does
@ 2026-11-02 14:19 Christian Brauner
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                   ` (6 more replies)
  0 siblings, 7 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:19 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Christian Brauner, Linus Torvalds, Jann Horn, Josef Bacik,
	Chris Mason, Jeff Layton, NeilBrown, Eric Dumazet,
	Thomas Gleixner, Al Viro, Jonathan Corbet, Christoph Hellwig,
	Greg Kroah-Hartman, Joel Granados, Kees Cook, Jan Kara, Jan Kara,
	linux-kernel, linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi, linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Chuck Lever, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers

Ok, as mentioned off-list I have an RFC version of getting rid of page
fault induced stalls in procfs when removing entry which plagues
networking devices among others.

There was a hackish very narrow AI solution to a part of this bug class
that was floated off-list. It used a second buffering mechanism on top
of seqfiles which was really nasty. Plus, it didn't fix the whole bug
class, it only eliminated a small portion. If we want to get rid of the
whole page fault issue we need a bigger hammer. So I tried to work
through that over the last couple of weeks. The series in a shape where
it's not totally insane to have someone else look at it. So here it is.

I understand it's big but I thought it's probably nicer to look at the
end result than just making a promise of what it might end up looking
like.

It's not super polished yet and I haven't gotten into sorting out all
the warts as there were some really gnarly bugs in there like the whole
readdir mess that repeated entries or terminated early. I think we could
go a lot further there if we wanted to but I think that blows this out
of proportion and at some point it becomes difficult to grasp the scope.
So I'd rather delay that until I have a sense of how acceptable this all
is.

remove_proc_entry() waits until every call into the entry has returned,
and most of those calls copy to or from userspace with the entry pinned.

A reader or writer can make a page fault in that copy take as long as
it wants, with userfaultfd or by punching holes into a memfd over and
over. And the callers of remove_proc_entry() like to hold locks while
they wait. addrconf holds RTNL when it removes /proc/net/dev_snmp6/<dev>
and xt_hashlimit holds a mutex that all network namespaces share.

Every network device with IPv6 gets a /proc/net/dev_snmp6/<dev> that
everyone can read, and addrconf_ifdown() removes it with RTNL held. So
any local user can keep RTNL for as long as their read stays stalled.
Until then no device can be registered, removed or renamed and no
network namespace can be created or torn down.

Without privileges a FUSE mount in a user namespace whose daemon never
answers is enough to stall the copy. Killing the reader ends it, but
until then whoever removes the file sits in D state.

xt_hashlimit removes the proc file of a table with hashlimit_mutex
held. Every rule insert in every network namespace takes that mutex and
so does network namespace cleanup. cleanup_net() runs on a single
workqueue with pernet_ops_rwsem held, so a stalled reader also stops
every later network namespace teardown and every module load that
registers pernet operations. xt_recent does the same with recent_mutex.

And it's not specific to networking. remove_proc_entry(), proc_remove()
and remove_proc_subtree() have over 200 callers and plenty of them hold
locks. A stalled reader of /proc/fs/ext4/<dev>/options holds up the
unmount of that filesystem with s_umount held, and every sync(2) with
it. The same goes for PCI hot-removal, for freeing IRQ descriptors,
which CPU bring-up waits on, and for unplugging a USB sound card.
syzbot has hit removal under locks before (cf. [1]), and the xt_recent
deadlock fixed in (1) below is another one.

Stalling the kernel in a copy to or from userspace is a well-known
exploitation primitive. It's why unprivileged userfaultfd can't do it
by default anymore since v5.11 (d0d4730ac2e4), and FUSE has been used
the same way.

kernfs doesn't have that problem. The code of a kernfs file only ever
runs with the node pinned and never touches userspace memory, kernfs
copies in and out itself. This moves procfs over to the same scheme.

sysfs has only held its active reference around ->show() and ->store()
since v2.6.23, the same release that made remove_proc_entry() wait for
the entry's callers. A copy to userspace couldn't be stalled for long
back then. userfaultfd only came in v4.3 and FUSE in user namespaces in
v4.18. configfs moved its copies out of its active reference in v5.3
and DRM copies ioctl arguments in and out around the driver's handler
the same way. debugfs still holds its reference across the copies,
ran into a deadlock with wifi drivers because of it and grew an API to
cancel operations instead (cf. [2]).

That touches just about every ->proc_read(), ->proc_write() and
->proc_ioctl() in the tree, so I've split it up into seven series. They
go out as replies to this mail:

(1) proc: fix a few old bugs in /proc handlers (38 patches)

    Bugs in the handlers and in procfs itself that are a lot older than
    the conversion and that reviewing it turned up. Most of them are for
    stable. They come first so they can go there on their own and the
    conversion doesn't have to bother with that crap.

(2) proc: let procfs own the data path (16 patches)

    The procfs side. procfs owns the seq_file of an entry, copies in
    writes, copies out reads and copies ioctl arguments in and out, and
    only calls into an entry with it pinned. proc_create_seq() and
    friends and the plain seq_file users are switched over here too.

(3) proc: convert the arch and platform entries (22 patches)
(4) proc: convert the driver entries (23 patches)
(5) proc: convert the fs, kernel and mm entries (27 patches)
(6) proc: convert the net entries and the kfifo samples (11 patches)

(7) proc: drop the __user methods (5 patches)

    Drops the old methods for every entry that can be removed and gives
    the new ones the names kernfs uses, ->proc_read(), ->proc_write()
    and ->proc_ioctl(). This needs all four conversion series.

The series should all indicate cleanly what dependencies they have so b4
should be able to pull it for review. But I could be wrong.

With all of it on a KASAN and lockdep kernel the removal selftests of
every part pass, the proc, pktgen and allocinfo selftests behave like
without it and the bugs from (1) that reproduce in a VM are gone. Every
patch builds on its own on x86_64 and i386, and with W=1 on the
architectures it touches.

[1]: https://lore.kernel.org/r/000000000000f24f4705b6988f3e@google.com
[2]: https://lore.kernel.org/r/20231109212251.213873-7-johannes@sipsolutions.net

-- 
Christian Brauner <brauner@kernel.org>

^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
@ 2026-11-02 14:21 ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses Christian Brauner
                     ` (37 more replies)
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                   ` (5 subsequent siblings)
  6 siblings, 38 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

Moving procfs to what kernfs does touches just about every
->proc_read(), ->proc_write() and ->proc_ioctl() in the tree. Reviewing
that turned up a bunch of bugs that have nothing to do with the
conversion and are a lot older. They're fixed here first so they can go
to stable on their own and the conversion doesn't have to carry them.

Two of them matter for the conversion itself:

- proc_bus_pci_write() truncates the file position to an int. A
  pwrite() at an offset like 0xfffffc00 makes it negative and the loop
  then writes a lot more than it was handed into config space at
  negative offsets. Once procfs hands it a kernel copy of the write
  that's a heap out-of-bounds read, so this has to be fixed before the
  PCI conversion:

    BUG: KASAN: slab-out-of-bounds in proc_bus_pci_write+0x528/0x5c0
    Read of size 4 at addr ff11000112103b20 by task pci-check/241
    Call Trace:
     proc_bus_pci_write+0x528/0x5c0
     proc_reg_write+0x2d7/0x730
     vfs_write+0x1cd/0xfa0
     __x64_sys_pwrite64+0x191/0x250

  isapnp has the same bug on the read side.

- xt_recent removes the proc entries of its tables with a spinlock held
  when a network namespace goes away. remove_proc_entry() waits for the
  readers and a reader takes that very lock. That's a deadlock.

/proc/sys readdir resumes a listing at a counted position. The entries
are sorted by name, so a device that comes or goes in front of that
position shifts them and the next getdents64() repeats an entry or
skips one. Even ls needs a second call to find the end of a directory.
It resumes after the name of the last entry it returned now, the way
kernfs resumes after its last node, and a selftest lists a directory in
two pieces around a device coming and going. That one is first, it
goes to stable on its own.

/proc/sys also has the bug the conversion fixes for the rest of /proc.
proc_sys_call_handler() copies what is read and written and
proc_sys_readdir() copies the names of the entries to and from userspace
while the sysctl tables are in use. Unregistering a table waits until
nobody uses it anymore. So a read(), write() or getdents64() whose
buffer faults for as long as userspace wants keeps
unregister_sysctl_table() waiting. addrconf unregisters the ipv6 sysctls
of a device under RTNL, so that stalls every RTNL user. The handler
copies in before it grabs the table and out after letting go of it.
readdir doesn't take the tables into use at all anymore. It pins the
memory of the entry it is at, which unregistering doesn't wait for,
finds the next one under sysctl_lock and emits with nothing held, the
way kernfs_fop_readdir() and proc_readdir_de() work. That's the same
one trip through sysctl_lock per entry as before. A selftest covers all
three.

/proc symlinks have it as well. proc_get_link() pins the entry and
vfs_readlink() copies the link to userspace before the delayed call
unpins it. ALSA removes /proc/asound/<id> with snd_card_mutex and
info_mutex held when a card changes its id, so a faulting readlink()
holds those up. The pin keeps nothing alive, the inode holds
the entry, so it goes. A selftest covers that too.

Chris' review of the conversion turned up more old bugs, in procfs
itself and in its users. Those are here as well:

- __xlate_proc_name() walks through whatever it finds for the
  components of a name, regular files included, and pktgen hands it the
  names that the writer of "add_device" picks. So an unprivileged user
  in namespaces of its own can hang an entry below another file and get
  it freed behind pktgen's back. procfs doesn't walk through anything
  but directories anymore and pktgen refuses names with a '/'.

- SUNRPC moved the waitqueue that poll() and epoll use for its channel
  files into the cache_detail of a network namespace. That's freed when
  the namespace goes away, but procfs leaves the waiters of a removed
  file queued. That's a use-after-free an unprivileged user can trigger
  too. It goes back to a single static waitqueue. procfs should really
  own the waitqueues of its files like kernfs does. I'll do that
  separately, it's nothing for stable.

- proc_register() WARNs when a name is already registered, and pktgen
  lets userspace pick names that collide with its own files. It reports
  the duplicate with pr_warn() and a stack dump now, like sysfs does.

- proc_readdir_de() resumes at a counted position just like /proc/sys
  and on top of that ends a listing early when the entry it is emitting
  goes away. It resumes after the last name now too, and a selftest
  covers both.

- proc_remove() removes by name, so removing an entry that is already
  gone can take out a different one. It removes the entry it's given
  now. And procfs warns about entries that a dying network namespace
  leaves in /proc/net instead of silently freeing their parent.

- On NOMMU every /proc file can be mapped privately, which reads it with
  nommu_region_sem held, and reading /proc/maps takes that lock too. Any
  user can hang the machine with that. procfs files can't be mapped on
  NOMMU anymore.

- esas2r takes the lengths of its HBA, SMP and firmware ioctls from
  userspace without checking them against the buffer they're copied
  into, through a file everyone can open.

- vmcore_cleanup() can run twice at once. powerpc's copy_oldmem_page()
  doesn't check ioremap_cache() and only recovers from a machine check
  for read(), not for splice() or vmcore's own reads. The pseries ofdt
  parser can read past its input and lparcfg reports a failed hcall as
  a huge write.

The rest is small: dasd looks in front of its buffer on an empty write,
toshiba_acpi's input checks can't fail, simdisk reads the file name
without the lock that protects it, rtas_flash can read past its message
buffer, rtasd hands out whatever vmalloc() left in its log buffer,
ALSA's info layer doesn't clamp writes past the end of a data entry,
BusLogic, ips and libipw return 0 from writes so stdio writers spin
forever, the scsi host files and um's exitcode parse what is written
without NUL-terminating it, and ntfs3 warns when a huge label is
written.

The PCI, xt_recent, sysctl, readdir, readlink, pktgen, SUNRPC,
duplicate name and /proc readdir bugs reproduce on a KASAN and lockdep
kernel in a VM and are gone with the series. The rest is build tested
only.

The procfs conversion is on top of this, starting with "proc: let
procfs own the data path".

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (38):
      PCI/proc: don't truncate the position of config space accesses
      PNP: isapnp: don't read past the end of the buffer on a large pread()
      netfilter: xt_recent: don't remove the proc entries under a spinlock
      s390/dasd: don't look in front of the buffer on an empty write
      platform/x86: toshiba_acpi: reject malformed brightness and fan writes
      xtensa: simdisk: don't read the file name without the lock
      powerpc/rtas_flash: don't read past the validate message
      powerpc/rtasd: zero the error log buffer
      ALSA: info: don't write past the end of a data entry
      scsi: BusLogic: don't return 0 from writes to the proc file
      scsi: ips: don't return 0 from writes to the proc file
      wifi: libipw: don't return 0 from writes to debug_level
      scsi: core: NUL-terminate what is written to the host proc files
      um: NUL-terminate what is written to /proc/exitcode
      ntfs3: don't warn when a huge label is written
      proc: don't walk through a non-directory when resolving a name
      net: pktgen: reject device names with a '/'
      proc: don't WARN about a name that is already registered
      proc: resume readdir after the last name it returned
      selftests/proc: check a /proc listing while entries come and go
      proc: let proc_remove() remove the entry it was given
      proc: warn about entries left in /proc/net of a dying namespace
      proc: don't let NOMMU map procfs files
      proc/vmcore: don't let vmcore_cleanup() run twice at once
      powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page()
      powerpc/crash_dump: recover from a machine check in copy_oldmem_page()
      sunrpc: make queue_wait a global wait queue again
      scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls
      powerpc/pseries: bound the lengths in the ofdt property parser
      powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long
      proc: sysctl: resume readdir after the last name it returned
      selftests/proc: check a sysctl listing while entries come and go
      proc: sysctl: copy to and from userspace without holding the table
      proc: sysctl: look at the table under sysctl_lock when creating an inode
      proc: sysctl: don't hold the tables while readdir copies to userspace
      selftests/proc: check that faulting sysctl accesses don't hold up removal
      proc: don't pin a symlink's entry while readlink() copies it out
      selftests/proc: check that a faulting readlink() doesn't hold up removal

 arch/powerpc/kernel/crash_dump.c                   |   6 +-
 arch/powerpc/kernel/rtas_flash.c                   |   2 +-
 arch/powerpc/kernel/rtasd.c                        |   2 +-
 arch/powerpc/platforms/pseries/lparcfg.c           |   2 +-
 arch/powerpc/platforms/pseries/reconfig.c          |  18 +-
 arch/um/kernel/exitcode.c                          |   3 +-
 arch/xtensa/platforms/iss/simdisk.c                |  25 +-
 drivers/net/wireless/intel/ipw2x00/libipw_module.c |   2 +-
 drivers/pci/proc.c                                 |  15 +-
 drivers/platform/x86/toshiba_acpi.c                |   8 +-
 drivers/pnp/isapnp/proc.c                          |   6 +-
 drivers/s390/block/dasd.c                          |   2 +-
 drivers/scsi/BusLogic.c                            |   2 +-
 drivers/scsi/esas2r/esas2r.h                       |   2 +-
 drivers/scsi/esas2r/esas2r_ioctl.c                 |  41 ++-
 drivers/scsi/esas2r/esas2r_main.c                  |   3 +-
 drivers/scsi/ips.c                                 |   2 +-
 drivers/scsi/scsi_proc.c                           |   1 +
 fs/ntfs3/super.c                                   |  11 +-
 fs/proc/generic.c                                  | 174 ++++++---
 fs/proc/inode.c                                    |  38 +-
 fs/proc/internal.h                                 |   1 +
 fs/proc/proc_net.c                                 |   7 +
 fs/proc/proc_sysctl.c                              | 389 ++++++++++++++-------
 fs/proc/root.c                                     |   1 +
 fs/proc/vmcore.c                                   |   5 +
 include/linux/sunrpc/cache.h                       |   1 -
 net/core/pktgen.c                                  |   4 +
 net/netfilter/xt_recent.c                          |   4 +-
 net/sunrpc/cache.c                                 |  12 +-
 sound/core/info.c                                  |   2 +
 tools/testing/selftests/proc/.gitignore            |   5 +
 tools/testing/selftests/proc/Makefile              |   5 +
 tools/testing/selftests/proc/config                |   6 +
 tools/testing/selftests/proc/proc-readdir-churn.c  | 236 +++++++++++++
 .../selftests/proc/proc-remove-slow-readlink.c     | 191 ++++++++++
 .../selftests/proc/proc-remove-slow-sysctl.c       | 213 +++++++++++
 .../selftests/proc/proc-sysctl-readdir-churn.c     | 147 ++++++++
 38 files changed, 1342 insertions(+), 252 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261101-work-procfs-bugfixes-054d00028491


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 17:58     ` Linus Torvalds
  2026-11-02 14:21   ` [PATCH RFC 02/38] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
                     ` (36 subsequent siblings)
  37 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_bus_pci_write() truncates the file position to an int. A pwrite()
at an offset like 0xfffff000 turns it negative, the check against the
size of the config space passes and the count it clamps to ends up
larger than what was written. The loop then reads past the end of the
caller's buffer and writes all of it into config space at negative
offsets, which pci_user_write_config_*() pass on to the config
accessors unchecked. The write also returns more than it was asked to
write.

An offset above 4G that truncates to a small positive value writes to
the start of config space instead. proc_bus_pci_read() keeps the
position in an unsigned int, so a read at such an offset returns the
start of config space instead of hitting EOF.

Check the position against the size of the config space before
narrowing it.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pci/proc.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 3d7a9ed5fc22..e0dc70123f82 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -32,8 +32,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 				 size_t nbytes, loff_t *ppos)
 {
 	struct pci_dev *dev = pde_data(file_inode(file));
-	unsigned int pos = *ppos;
-	unsigned int cnt, size;
+	unsigned int pos, cnt, size;
 
 	/*
 	 * Normal users can read only the standardized portion of the
@@ -51,8 +50,9 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	if (!nbytes)
 		return 0;
 
-	if (pos >= size)
+	if (*ppos >= size)
 		return 0;
+	pos = *ppos;
 	if (nbytes >= size)
 		nbytes = size;
 	if (pos + nbytes > size)
@@ -119,9 +119,8 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
 {
 	struct inode *ino = file_inode(file);
 	struct pci_dev *dev = pde_data(ino);
-	int pos = *ppos;
 	int size = dev->cfg_size;
-	int cnt, ret;
+	int pos, cnt, ret;
 
 	ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
 	if (ret)
@@ -130,14 +129,16 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
 	if (!nbytes)
 		return 0;
 
+	if (*ppos >= size)
+		return 0;
+	pos = *ppos;
+
 	if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
 		pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
 			      current->comm, pos);
 		add_taint(TAINT_USER, LOCKDEP_STILL_OK);
 	}
 
-	if (pos >= size)
-		return 0;
 	if (nbytes >= size)
 		nbytes = size;
 	if (pos + nbytes > size)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 02/38] PNP: isapnp: don't read past the end of the buffer on a large pread()
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:51     ` Rafael J. Wysocki (Intel)
  2026-11-02 14:21   ` [PATCH RFC 03/38] netfilter: xt_recent: don't remove the proc entries under a spinlock Christian Brauner
                     ` (35 subsequent siblings)
  37 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

isapnp_proc_bus_read() truncates the position to an int. A pread() at
an offset like 0xfffffc00 turns it negative, the check against the size
of the configuration space passes, and the loop then puts everything
from that position up to byte 256 into the reader's buffer, no matter
how few bytes were asked for.

Keep the position as a loff_t and clamp the count against what is left
of the configuration space.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pnp/isapnp/proc.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c
index c1f6e6bd0eb2..8197c7f29743 100644
--- a/drivers/pnp/isapnp/proc.c
+++ b/drivers/pnp/isapnp/proc.c
@@ -23,14 +23,12 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
 				    size_t nbytes, loff_t * ppos)
 {
 	struct pnp_dev *dev = pde_data(file_inode(file));
-	int pos = *ppos;
+	loff_t pos = *ppos;
 	int cnt, size = 256;
 
 	if (pos >= size)
 		return 0;
-	if (nbytes >= size)
-		nbytes = size;
-	if (pos + nbytes > size)
+	if (nbytes > size - pos)
 		nbytes = size - pos;
 	cnt = nbytes;
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 03/38] netfilter: xt_recent: don't remove the proc entries under a spinlock
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 02/38] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:41     ` Florian Westphal
  2026-11-02 14:21   ` [PATCH RFC 04/38] s390/dasd: don't look in front of the buffer on an empty write Christian Brauner
                     ` (34 subsequent siblings)
  37 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

recent_proc_net_exit() removes the proc entries of all tables with
recent_lock held. remove_proc_entry() waits until nobody calls into the
entry anymore, and reading one of them takes recent_lock in
recent_seq_start() while procfs has the entry pinned. So when a reader
gets there while the network namespace goes away, the exit path sleeps
with a spinlock held and bh disabled, waiting for a reader that spins
on that very lock:

  BUG: sleeping function called from invalid context at kernel/sched/completion.c:116
  in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 12, name: kworker/u16:0
  locks held by kworker/u16:0/12: 4, last CPU#2:
   #2: (pernet_ops_rwsem){++++}-{4:4}, at: cleanup_net+0xbf/0x860
   #3: (recent_lock){+...}-{3:3}, at: recent_net_exit+0x3d/0x170 [xt_recent]
  Workqueue: netns cleanup_net
  Call Trace:
   wait_for_completion+0x8e/0x400
   proc_entry_rundown+0x1ab/0x1f0
   remove_proc_entry+0x1ab/0x530
   recent_net_exit+0x9f/0x170 [xt_recent]
   ops_undo_list+0x271/0x8d0
   cleanup_net+0x3f3/0x860
   process_one_work+0x946/0x1820
  BUG: scheduling while atomic: kworker/u16:0/12/0x00000202

  watchdog: BUG: soft lockup - CPU#0 stuck for 26s! [rloop:252]
  Call Trace:
   _raw_spin_lock_bh+0x7a/0x80
   recent_seq_start+0x86/0x1e0 [xt_recent]
   seq_read_iter+0x26d/0x1230
   seq_read+0x2a9/0x560
   proc_reg_read+0x1c2/0x3c0
   vfs_read+0x176/0xb40
   __x64_sys_pread64+0x191/0x250

Tables are added and removed with recent_mutex held and that's also
what recent_mt_destroy() looks at ->xt_recent under. Nothing that runs
with a proc entry pinned takes it. So take recent_mutex instead.

Fixes: 665e205c16c1 ("netfilter: xt_recent: fix namespace destroy path")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/netfilter/xt_recent.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index 55f1f2d89952..11acb9a50d4b 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -652,12 +652,12 @@ static void __net_exit recent_proc_net_exit(struct net *net)
 	 * that the parent xt_recent proc entry is empty before trying to
 	 * remove it.
 	 */
-	spin_lock_bh(&recent_lock);
+	mutex_lock(&recent_mutex);
 	list_for_each_entry(t, &recent_net->tables, list)
 	        remove_proc_entry(t->name, recent_net->xt_recent);
 
 	recent_net->xt_recent = NULL;
-	spin_unlock_bh(&recent_lock);
+	mutex_unlock(&recent_mutex);
 
 	remove_proc_entry("xt_recent", net->proc_net);
 }

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 04/38] s390/dasd: don't look in front of the buffer on an empty write
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 03/38] netfilter: xt_recent: don't remove the proc entries under a spinlock Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 05/38] platform/x86: toshiba_acpi: reject malformed brightness and fan writes Christian Brauner
                     ` (33 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

dasd_get_user_string() strips a trailing newline by looking at the last
byte that was written. For an empty write that's the byte in front of
the buffer it just allocated. Only look if something was written.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/s390/block/dasd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 2ffe404b890f..e97891e58957 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -933,7 +933,7 @@ char *dasd_get_user_string(const char __user *user_buf, size_t user_len)
 		return ERR_PTR(-EFAULT);
 	}
 	/* got the string, now strip linefeed. */
-	if (buffer[user_len - 1] == '\n')
+	if (user_len && buffer[user_len - 1] == '\n')
 		buffer[user_len - 1] = 0;
 	else
 		buffer[user_len] = 0;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 05/38] platform/x86: toshiba_acpi: reject malformed brightness and fan writes
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 04/38] s390/dasd: don't look in front of the buffer on an empty write Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 06/38] xtensa: simdisk: don't read the file name without the lock Christian Brauner
                     ` (32 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

When lcd_proc_write() and fan_proc_write() were refactored their checks
were inverted but the && stayed. They only fail if sscanf() didn't
parse anything and the value is out of range at the same time, and for
the brightness value < 0 && value > levels can't be true at all. So an
out of range brightness or fan setting is passed on to the hardware,
and so is the uninitialized value if nothing could be parsed.

Restore the checks as they were before the refactoring.

Fixes: e1a949c1b988 ("toshiba_acpi: Refactor *{get, set} functions return value")
Fixes: 3e07e5baa2af ("toshiba_acpi: Add set_fan_status function")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/platform/x86/toshiba_acpi.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index a0b8060836d0..9eb8f1e5aeda 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1498,8 +1498,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
 	cmd[len] = '\0';
 
 	levels = dev->backlight_dev->props.max_brightness + 1;
-	if (sscanf(cmd, " brightness : %i", &value) != 1 &&
-	    value < 0 && value > levels)
+	if (sscanf(cmd, " brightness : %i", &value) != 1 ||
+	    value < 0 || value >= levels)
 		return -EINVAL;
 
 	if (set_lcd_brightness(dev, value))
@@ -1678,8 +1678,8 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
 		return -EFAULT;
 	cmd[len] = '\0';
 
-	if (sscanf(cmd, " force_on : %i", &value) != 1 &&
-	    value != 0 && value != 1)
+	if (sscanf(cmd, " force_on : %i", &value) != 1 ||
+	    value < 0 || value > 1)
 		return -EINVAL;
 
 	if (set_fan_status(dev, value))

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 06/38] xtensa: simdisk: don't read the file name without the lock
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 05/38] platform/x86: toshiba_acpi: reject malformed brightness and fan writes Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 07/38] powerpc/rtas_flash: don't read past the validate message Christian Brauner
                     ` (31 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_read_simdisk() looks at dev->filename without holding dev->lock. A
write to the same file detaches the disk and frees the name under the
lock, so a concurrent reader can copy out freed memory. Copy the name
with the lock held.

Fixes: b6c7e873daf7 ("xtensa: ISS: add host file-based simulated disk")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/xtensa/platforms/iss/simdisk.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index a2ddb49c269d..7b115e023de9 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -238,22 +238,21 @@ static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
 			size_t size, loff_t *ppos)
 {
 	struct simdisk *dev = pde_data(file_inode(file));
-	const char *s = dev->filename;
-	if (s) {
-		ssize_t len = strlen(s);
-		char *temp = kmalloc(len + 2, GFP_KERNEL);
+	ssize_t len;
+	char *temp;
 
-		if (!temp)
-			return -ENOMEM;
+	/* a write sets at most a page, see proc_write_simdisk() */
+	temp = kmalloc(PAGE_SIZE + 2, GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
 
-		len = scnprintf(temp, len + 2, "%s\n", s);
-		len = simple_read_from_buffer(buf, size, ppos,
-					      temp, len);
+	spin_lock(&dev->lock);
+	len = scnprintf(temp, PAGE_SIZE + 2, "%s\n", dev->filename ?: "");
+	spin_unlock(&dev->lock);
 
-		kfree(temp);
-		return len;
-	}
-	return simple_read_from_buffer(buf, size, ppos, "\n", 1);
+	len = simple_read_from_buffer(buf, size, ppos, temp, len);
+	kfree(temp);
+	return len;
 }
 
 static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 07/38] powerpc/rtas_flash: don't read past the validate message
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 06/38] xtensa: simdisk: don't read the file name without the lock Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 08/38] powerpc/rtasd: zero the error log buffer Christian Brauner
                     ` (30 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

get_validate_flash_msg() adds up what snprintf() returns, which is the
length of the whole string and not what fit into msg. If the update
results the firmware hands back don't fit into what is left of the 256
bytes, the length ends up larger than msg and validate_flash_read()
copies whatever follows it on the stack to userspace. Use scnprintf().

Fixes: a94a14720eaf ("powerpc/rtas_flash: Fix validate_flash buffer overflow issue")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/rtas_flash.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index 722dbfb6fbf8..f1cd105f7426 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -448,7 +448,7 @@ static int get_validate_flash_msg(struct rtas_validate_flash_t *args_buf,
 		n = sprintf(msg, "%d\n", args_buf->update_results);
 		if ((args_buf->update_results >= VALIDATE_CUR_UNKNOWN) ||
 		    (args_buf->update_results == VALIDATE_TMP_UPDATE))
-			n += snprintf(msg + n, msglen - n, "%s\n",
+			n += scnprintf(msg + n, msglen - n, "%s\n",
 					args_buf->buf);
 	} else {
 		n = sprintf(msg, "%d\n", args_buf->status);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 08/38] powerpc/rtasd: zero the error log buffer
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 07/38] powerpc/rtas_flash: don't read past the validate message Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 09/38] ALSA: info: don't write past the end of a data entry Christian Brauner
                     ` (29 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

rtas_log_read() hands out a whole slot of the error log but
pSeries_log_error() only fills in the sequence number and the event,
which is usually a lot shorter. The rest of the slot is whatever
vmalloc() left there. Use vzalloc().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/rtasd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index fd40864bdb70..dd0df5e164a3 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -523,7 +523,7 @@ static int __init rtas_event_scan_init(void)
 	rtas_error_log_max = rtas_get_error_log_max();
 	rtas_error_log_buffer_max = rtas_error_log_max + sizeof(int);
 
-	rtas_log_buf = vmalloc(array_size(LOG_NUMBER,
+	rtas_log_buf = vzalloc(array_size(LOG_NUMBER,
 					  rtas_error_log_buffer_max));
 	if (!rtas_log_buf) {
 		printk(KERN_ERR "rtasd: no memory\n");

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 09/38] ALSA: info: don't write past the end of a data entry
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 08/38] powerpc/rtasd: zero the error log buffer Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 10/38] scsi: BusLogic: don't return 0 from writes to the proc file Christian Brauner
                     ` (28 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

snd_info_entry_write() clamps the count to what is left of the entry
by computing entry->size - pos as a size_t. For a position past the end
that wraps and the write goes to the driver unclamped, for opl4 into
sample memory outside of what the entry covers. At the end of the entry
the driver gets a count of 0 and vmalloc(0) warns. Before the clamp
moved into the common code the driver ignored such writes. Fail them
with -ENOSPC instead, like a block device does at the end of the
device. Returning 0 would make writers that use stdio retry forever.

Fixes: d97e1b78239c ("ALSA: info - Check file position validity in common layer")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 sound/core/info.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/core/info.c b/sound/core/info.c
index c4d57d0809ad..982087bc8575 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -171,6 +171,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer
 	pos = *offset;
 	if (!valid_pos(pos, count))
 		return -EIO;
+	if (pos >= entry->size)
+		return count ? -ENOSPC : 0;
 	if (count > 0) {
 		size_t maxsize = entry->size - pos;
 		count = min(count, maxsize);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 10/38] scsi: BusLogic: don't return 0 from writes to the proc file
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 09/38] ALSA: info: don't write past the end of a data entry Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 11/38] scsi: ips: " Christian Brauner
                     ` (27 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

A write to /proc/scsi/BusLogic/<n> resets the statistics and returns 0.
Writers that use stdio, echo for example, take that as no progress and
retry forever. Return the number of bytes that were written.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/BusLogic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c
index 27b446c0c5d0..7ad814f549ae 100644
--- a/drivers/scsi/BusLogic.c
+++ b/drivers/scsi/BusLogic.c
@@ -3328,7 +3328,7 @@ static int blogic_write_info(struct Scsi_Host *shost, char *procbuf,
 	adapter->ext_resets = 0;
 	adapter->adapter_intern_errors = 0;
 	memset(tgt_stats, 0, BLOGIC_MAXDEV * sizeof(struct blogic_tgt_stats));
-	return 0;
+	return bytes_avail;
 }
 
 static int blogic_show_info(struct seq_file *m, struct Scsi_Host *shost)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 11/38] scsi: ips: don't return 0 from writes to the proc file
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 10/38] scsi: BusLogic: don't return 0 from writes to the proc file Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 12/38] wifi: libipw: don't return 0 from writes to debug_level Christian Brauner
                     ` (26 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

ips_write_info() ignores what is written to /proc/scsi/ips/<n> and
returns 0. Writers that use stdio, echo for example, take that as no
progress and retry forever. Return the number of bytes that were
written.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/ips.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 45c5b9acb664..bf3a961834fb 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -1428,7 +1428,7 @@ ips_write_info(struct Scsi_Host *host, char *buffer, int length)
 	if (!ha)
 		return (-EINVAL);
 
-	return 0;
+	return length;
 }
 
 static int

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 12/38] wifi: libipw: don't return 0 from writes to debug_level
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (10 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 11/38] scsi: ips: " Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 13/38] scsi: core: NUL-terminate what is written to the host proc files Christian Brauner
                     ` (25 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

debug_level_proc_write() returns the length of the string it parsed. A
write that starts with a NUL byte thus returns 0 and writers that use
stdio retry it forever. Return the number of bytes that were written
instead, which also stops writes longer than the 11 bytes it looks at
from coming up short.

Fixes: b453872c35cf ("[NET] ieee80211 subsystem")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/net/wireless/intel/ipw2x00/libipw_module.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index 22358b2c2ecd..552c8ff3928b 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
@@ -236,7 +236,7 @@ static ssize_t debug_level_proc_write(struct file *file,
 	else
 		libipw_debug_level = val;
 
-	return strnlen(buf, len);
+	return count;
 }
 
 static const struct proc_ops debug_level_proc_ops = {

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 13/38] scsi: core: NUL-terminate what is written to the host proc files
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (11 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 12/38] wifi: libipw: don't return 0 from writes to debug_level Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 14/38] um: NUL-terminate what is written to /proc/exitcode Christian Brauner
                     ` (24 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_scsi_host_write() copies what is written into a page and hands it
to ->write_info() without terminating it. ppa and imm parse it with
strncmp() and simple_strtoul(), which read on past what was written into
whatever the page held before. Terminate the copy. A write is at most
PROC_BLOCK_SIZE bytes, so the page always has room for that.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/scsi_proc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index fdc355d783da..dbd03360b5a4 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -80,6 +80,7 @@ static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
 		ret = -EFAULT;
 		if (copy_from_user(page, buf, count))
 			goto out;
+		page[count] = '\0';
 		ret = shost->hostt->write_info(shost, page, count);
 	}
 out:

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 14/38] um: NUL-terminate what is written to /proc/exitcode
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (12 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 13/38] scsi: core: NUL-terminate what is written to the host proc files Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 15/38] ntfs3: don't warn when a huge label is written Christian Brauner
                     ` (23 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

exitcode_proc_write() copies up to sizeof(buf) bytes of the write into
buf and hands it to simple_strtol() without terminating it. So
"echo -n 5" parses whatever the stack held after the '5', and a write of
seven bytes or more reads past the end of buf. Terminate the copy.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/um/kernel/exitcode.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c
index 43edc2aa57e4..0230354f4812 100644
--- a/arch/um/kernel/exitcode.c
+++ b/arch/um/kernel/exitcode.c
@@ -43,9 +43,10 @@ static ssize_t exitcode_proc_write(struct file *file,
 	size_t size;
 	int tmp;
 
-	size = min(count, sizeof(buf));
+	size = min(count, sizeof(buf) - 1);
 	if (copy_from_user(buf, buffer, size))
 		return -EFAULT;
+	buf[size] = '\0';
 
 	tmp = simple_strtol(buf, &end, 0);
 	if ((*end != '\0') && !isspace(*end))

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 15/38] ntfs3: don't warn when a huge label is written
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (13 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 14/38] um: NUL-terminate what is written to /proc/exitcode Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 16/38] proc: don't walk through a non-directory when resolving a name Christian Brauner
                     ` (22 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

ntfs3_label_write() allocates as many bytes as are written with plain
kmalloc(). A write that is larger than what kmalloc() can hand out then
trips the page allocator's warning, which panics a panic_on_warn kernel.
Use memdup_user(), which fails such an allocation quietly with -ENOMEM
like before.

Fixes: 7832e123490a ("fs/ntfs3: Add support /proc/fs/ntfs3/<dev>/volinfo and /proc/fs/ntfs3/<dev>/label")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/ntfs3/super.c | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 58ef6be3286b..b4479cd6bd65 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -552,15 +552,10 @@ static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
 	if (sb_rdonly(sb))
 		return -EROFS;
 
-	label = kmalloc(count, GFP_NOFS);
+	label = memdup_user(buffer, count);
+	if (IS_ERR(label))
+		return PTR_ERR(label);
 
-	if (!label)
-		return -ENOMEM;
-
-	if (copy_from_user(label, buffer, ret)) {
-		ret = -EFAULT;
-		goto out;
-	}
 	while (ret > 0 && label[ret - 1] == '\n')
 		ret -= 1;
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 16/38] proc: don't walk through a non-directory when resolving a name
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (14 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 15/38] ntfs3: don't warn when a huge label is written Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 17/38] net: pktgen: reject device names with a '/' Christian Brauner
                     ` (21 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

__xlate_proc_name() walks the components of a name like "a/b/c" and
takes whatever it finds for "a" and "b" as the directory to go on in.
It never checks that they actually are directories. So
proc_create("file/x") hangs x into the subdir tree of the regular file
"file". Nobody can look x up there, but remove_proc_subtree() on the
file removes and frees x along with it, behind the back of whoever
registered x.

pktgen passes the name the writer of "add_device" picks to
proc_create_data(), and only the part in front of the '@' has to name
a network device:

  echo "add_device lo@"   > /proc/net/pktgen/kpktgend_0
  echo "add_device lo@/x" > /proc/net/pktgen/kpktgend_1
  echo "rem_device_all"   > /proc/net/pktgen/kpktgend_0
  echo "rem_device_all"   > /proc/net/pktgen/kpktgend_1

The third write frees both entries and the fourth one makes kpktgend_1
call proc_remove() on the freed second one:

  BUG: KASAN: slab-use-after-free in proc_remove+0x8f/0xa0
  Read of size 8 at addr ff110001134c8e70 by task kpktgend_1/243
  Call Trace:
   proc_remove+0x8f/0xa0
   pktgen_remove_device.isra.0+0x118/0x500
   pktgen_rem_all_ifs+0xeb/0x1a0
   pktgen_thread_worker+0x1b42/0x7280

The files of a network namespace belong to the root of its user
namespace and these writes don't require a capability. So once pktgen
is loaded an unprivileged user can do this in namespaces of its own.

Treat a component that isn't a directory like one that doesn't exist.
All names with a '/' that the tree passes in as literals only walk
through directories. pktgen is fixed separately to not pass such names
in the first place.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 2b1971da4a85..c560c96a64eb 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -170,7 +170,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
 	de = *ret ?: &proc_root;
 	while ((next = strchr(cp, '/')) != NULL) {
 		de = pde_subdir_find(de, cp, next - cp);
-		if (!de) {
+		if (!de || !S_ISDIR(de->mode)) {
 			WARN(1, "name '%s'\n", name);
 			return -ENOENT;
 		}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 17/38] net: pktgen: reject device names with a '/'
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (15 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 16/38] proc: don't walk through a non-directory when resolving a name Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 18/38] proc: don't WARN about a name that is already registered Christian Brauner
                     ` (20 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

"add_device" takes a name like eth0@1. Only the part in front of the
'@' has to name a network device. The rest is free text and all of it
becomes the name of a file in /proc/net/pktgen, where procfs takes a
'/' as a directory separator. So "add_device lo@a/b" makes procfs look
for a directory "lo@a" that doesn't exist and warn about it. Any user
can do that in a network namespace of its own:

  name 'lo@a/b'
  WARNING: fs/proc/generic.c:174 at __xlate_proc_name+0xb1/0xf0, CPU#3: bash/241
  Call Trace:
   __proc_create+0xd0/0x9b0
   proc_create_reg+0x45/0xf0
   proc_create_data+0x77/0xf0
   pktgen_thread_write+0xd8d/0x1750

With panic_on_warn that takes the machine down. And if "lo@a" exists
because somebody added a device of that name, procfs used to register
the new entry below that regular file, and removing "lo@a" freed both.

Reject names with a '/'.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: e6fce5b916cd ("pktgen: multiqueue etc.")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/core/pktgen.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index d63eeadddfcc..02af70d360bc 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3831,6 +3831,10 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
 	int err;
 	int node = cpu_to_node(t->cpu);
 
+	/* The name becomes the name of a file in /proc/net/pktgen. */
+	if (strchr(ifname, '/'))
+		return -EINVAL;
+
 	/* We don't allow a device to be on several threads */
 
 	if (__pktgen_NN_threads(t->net, ifname, FIND)) {

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 18/38] proc: don't WARN about a name that is already registered
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (16 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 17/38] net: pktgen: reject device names with a '/' Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 19/38] proc: resume readdir after the last name it returned Christian Brauner
                     ` (19 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_register() WARNs when the directory already has an entry of the
same name and fails the registration. Callers that pick their names
themselves should never get there but some take names from userspace.
pktgen creates a file for each device a writer of "add_device" adds,
named after the device. A device can have any name, including "pgctrl"
or "kpktgend_0", the names of pktgen's own files:

  ip link set lo name pgctrl
  ip link set pgctrl up
  echo "add_device pgctrl" > /proc/net/pktgen/kpktgend_0

  proc_dir_entry 'pktgen/pgctrl' already registered
  WARNING: fs/proc/generic.c:404 at proc_register+0x555/0x8b0, CPU#0: bash/241
  Call Trace:
   proc_create_data+0x9f/0xf0
   pktgen_thread_write+0xd9b/0x1780
   proc_reg_write+0x1c2/0x3c0
   vfs_write+0x1cd/0xfa0

The files of a network namespace belong to the root of its user
namespace and none of this requires a capability. So once pktgen is
loaded an unprivileged user can taint the kernel, or crash it with
panic_on_warn, from namespaces of its own. pktgen itself copes with
the failed registration just fine.

So go back to what proc_register() did before commit 6c2f91e077f1
("proc: use WARN() rather than printk+backtrace") and what
sysfs_warn_dup() does for sysfs: report the name with pr_warn() and
dump the stack. While at it, do that after dropping proc_subdir_lock
instead of printing a backtrace while holding the lock that every
lookup and readdir in procfs needs.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 6c2f91e077f1 ("proc: use WARN() rather than printk+backtrace")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index c560c96a64eb..ef804cd3ec45 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -401,9 +401,10 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
 	write_lock(&proc_subdir_lock);
 	dp->parent = dir;
 	if (pde_subdir_insert(dir, dp) == false) {
-		WARN(1, "proc_dir_entry '%s/%s' already registered\n",
-		     dir->name, dp->name);
 		write_unlock(&proc_subdir_lock);
+		pr_warn("proc_dir_entry '%s/%s' already registered\n",
+			dir->name, dp->name);
+		dump_stack();
 		goto out_free_inum;
 	}
 	write_unlock(&proc_subdir_lock);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 19/38] proc: resume readdir after the last name it returned
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (17 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 18/38] proc: don't WARN about a name that is already registered Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 20/38] selftests/proc: check a /proc listing while entries come and go Christian Brauner
                     ` (18 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_readdir_de() counts the entries of a directory and resumes a
listing at the number getdents64() stopped at. The entries are sorted
by name, so one that is added or removed in between shifts everything
behind it by one. Listing /proc/net/dev_snmp6 with a buffer that holds
".", ".." and "lo" and adding a device whose name sorts before "lo"
makes the next call return "lo" a second time. Removing one that sorted
before the position makes the next call skip an entry instead.

Since commit 895b4c0c79b0 ("fs/proc: fix uaf in proc_readdir_de()") an
entry that is removed while it is being emitted also ends the listing
right there. pde_subdir_next() finds nothing after an erased entry, so
proc_readdir_de() reports the end of the directory and the next call
resumes by number, skipping another entry. For /proc itself
proc_root_readdir() takes that as the end of the static entries and
moves on to the pids, so the rest of the static entries is missing.
And if the position is behind the last entry when a call starts
proc_readdir_de() returns 0, so /proc then lists no pids at all.

So remember the name of the last entry a call emitted in the file and
resume at the first entry after it, like kernfs resumes after the node
it stopped at. An entry that was removed while it was emitted is
continued after by its name as well. If the position was moved by a
seek in between the listing counts from the start like before.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 895b4c0c79b0 ("fs/proc: fix uaf in proc_readdir_de()")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c  | 89 ++++++++++++++++++++++++++++++++++++++++++++++--------
 fs/proc/internal.h |  1 +
 fs/proc/proc_net.c |  1 +
 fs/proc/root.c     |  1 +
 4 files changed, 80 insertions(+), 12 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index ef804cd3ec45..47830d6af255 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -87,6 +87,28 @@ static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir,
 	return NULL;
 }
 
+/* The first entry of @dir that sorts after @name. */
+static struct proc_dir_entry *pde_subdir_after(struct proc_dir_entry *dir,
+					       const char *name,
+					       unsigned int len)
+{
+	struct rb_node *node = dir->subdir.rb_node, *after = NULL;
+
+	while (node) {
+		struct proc_dir_entry *de = rb_entry(node,
+						     struct proc_dir_entry,
+						     subdir_node);
+
+		if (proc_match(name, de, len) < 0) {
+			after = node;
+			node = node->rb_left;
+		} else {
+			node = node->rb_right;
+		}
+	}
+	return rb_entry_safe(after, struct proc_dir_entry, subdir_node);
+}
+
 static bool pde_subdir_insert(struct proc_dir_entry *dir,
 			      struct proc_dir_entry *de)
 {
@@ -277,6 +299,40 @@ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
 	return proc_lookup_de(dir, dentry, PDE(dir));
 }
 
+/*
+ * Where the next readdir resumes. The entries are counted, so one that is
+ * added or removed in front of the position would shift what the next call
+ * returns. The name of the last entry emitted doesn't move.
+ */
+struct proc_dir_cursor {
+	loff_t pos;			/* the position behind the entry */
+	unsigned int len;
+	char name[NAME_MAX + 1];
+};
+
+/* If this can't remember the entry the next call counts from the start. */
+static void proc_dir_cursor_set(struct file *file, struct proc_dir_entry *de,
+				loff_t pos)
+{
+	struct proc_dir_cursor *cursor = file->private_data;
+
+	if (!cursor) {
+		cursor = kmalloc(sizeof(*cursor), GFP_KERNEL);
+		if (!cursor)
+			return;
+		file->private_data = cursor;
+	}
+	cursor->pos = pos;
+	cursor->len = de->namelen;
+	memcpy(cursor->name, de->name, de->namelen);
+}
+
+int proc_dir_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
 /*
  * This returns non-zero if at EOF, so that the /proc
  * root directory can use this and check if it should
@@ -289,23 +345,26 @@ struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
 int proc_readdir_de(struct file *file, struct dir_context *ctx,
 		    struct proc_dir_entry *de)
 {
+	struct proc_dir_cursor *cursor = file->private_data;
+	struct proc_dir_entry *dir = de;
 	int i;
 
 	if (!dir_emit_dots(file, ctx))
 		return 0;
 
-	i = ctx->pos - 2;
 	read_lock(&proc_subdir_lock);
-	de = pde_subdir_first(de);
-	for (;;) {
-		if (!de) {
-			read_unlock(&proc_subdir_lock);
-			return 0;
-		}
-		if (!i)
-			break;
-		de = pde_subdir_next(de);
-		i--;
+	if (cursor && cursor->pos == ctx->pos) {
+		/* Resume after the last entry emitted, wherever it is now. */
+		de = pde_subdir_after(dir, cursor->name, cursor->len);
+	} else {
+		/* Moved by a seek, count from the start like always. */
+		de = pde_subdir_first(dir);
+		for (i = ctx->pos - 2; de && i; i--)
+			de = pde_subdir_next(de);
+	}
+	if (!de) {
+		read_unlock(&proc_subdir_lock);
+		return 1;
 	}
 
 	do {
@@ -318,8 +377,13 @@ int proc_readdir_de(struct file *file, struct dir_context *ctx,
 			return 0;
 		}
 		ctx->pos++;
+		proc_dir_cursor_set(file, de, ctx->pos);
 		read_lock(&proc_subdir_lock);
-		next = pde_subdir_next(de);
+		/* Removed in the meantime, go on after its name. */
+		if (RB_EMPTY_NODE(&de->subdir_node))
+			next = pde_subdir_after(dir, de->name, de->namelen);
+		else
+			next = pde_subdir_next(de);
 		pde_put(de);
 		de = next;
 	} while (de);
@@ -347,6 +411,7 @@ static const struct file_operations proc_dir_operations = {
 	.llseek			= generic_file_llseek,
 	.read			= generic_read_dir,
 	.iterate_shared		= proc_readdir,
+	.release		= proc_dir_release,
 };
 
 static int proc_net_d_revalidate(struct inode *dir, const struct qstr *name,
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index df873df9afc5..04f7ce2b24ed 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -287,6 +287,7 @@ extern struct dentry *proc_lookup(struct inode *, struct dentry *, unsigned int)
 struct dentry *proc_lookup_de(struct inode *, struct dentry *, struct proc_dir_entry *);
 extern int proc_readdir(struct file *, struct dir_context *);
 int proc_readdir_de(struct file *, struct dir_context *, struct proc_dir_entry *);
+int proc_dir_release(struct inode *, struct file *);
 
 static inline void pde_get(struct proc_dir_entry *pde)
 {
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index b1f5eafb069a..dbcf87a257e1 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -351,6 +351,7 @@ const struct file_operations proc_net_operations = {
 	.llseek		= generic_file_llseek,
 	.read		= generic_read_dir,
 	.iterate_shared	= proc_tgid_net_readdir,
+	.release	= proc_dir_release,
 };
 
 static __net_init int proc_net_ns_init(struct net *net)
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 7fbbe92bf73a..dfee1caa26f7 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -441,6 +441,7 @@ static const struct file_operations proc_root_operations = {
 	.read		 = generic_read_dir,
 	.iterate_shared	 = proc_root_readdir,
 	.llseek		= generic_file_llseek,
+	.release	= proc_dir_release,
 };
 
 /*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 20/38] selftests/proc: check a /proc listing while entries come and go
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (18 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 19/38] proc: resume readdir after the last name it returned Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 21/38] proc: let proc_remove() remove the entry it was given Christian Brauner
                     ` (17 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

List /proc/net/dev_snmp6 of a new network namespace while a tun device
comes and goes:

- with a buffer that holds the dots and "lo", add a device whose name
  sorts first, finish the listing: "lo" is listed once

- with that device added and a buffer that holds the dots and it,
  remove it, finish the listing: "lo" is still listed

- with that device added, stall getdents64() on a userfaultfd page
  while it writes the device's entry, remove the device, finish the
  listing: "lo" is still listed

All three fail without the fix, the first lists "lo" twice, the other
two not at all.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/proc/.gitignore           |   1 +
 tools/testing/selftests/proc/Makefile             |   1 +
 tools/testing/selftests/proc/proc-readdir-churn.c | 236 ++++++++++++++++++++++
 3 files changed, 238 insertions(+)

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 9c9735570abf..8520c7878ea2 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -8,6 +8,7 @@
 /proc-maps-race
 /proc-multiple-procfs
 /proc-net-dev-lseek
+/proc-readdir-churn
 /proc-empty-vm
 /proc-pid-vm
 /proc-self-map-files-001
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index a7de2bb6d8be..e0c0d313f4d0 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -11,6 +11,7 @@ TEST_GEN_PROGS += proc-2-is-kthread
 TEST_GEN_PROGS += proc-loadavg-001
 TEST_GEN_PROGS += proc-maps-race
 TEST_GEN_PROGS += proc-net-dev-lseek
+TEST_GEN_PROGS += proc-readdir-churn
 TEST_GEN_PROGS += proc-empty-vm
 TEST_GEN_PROGS += proc-pid-vm
 TEST_GEN_PROGS += proc-self-map-files-001
diff --git a/tools/testing/selftests/proc/proc-readdir-churn.c b/tools/testing/selftests/proc/proc-readdir-churn.c
new file mode 100644
index 000000000000..223b8a6698ee
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-readdir-churn.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A listing of a /proc directory that takes more than one getdents64()
+ * must not show an entry twice or skip one when an entry is added or
+ * removed in between, and a listing must go on after an entry that is
+ * removed while it is being emitted. /proc/net/dev_snmp6/<dev> of a tun
+ * device comes and goes with the device.
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define SNMP6_DIR	"/proc/net/dev_snmp6"
+#define TUN_NAME	"a0"		/* sorts before "lo" */
+#define TUN_PATH	SNMP6_DIR "/" TUN_NAME
+#define SMALL_BUF	80		/* ".", ".." and one short name */
+#define NAMES		64
+
+static int tun_open(const char *name)
+{
+	struct ifreq ifr = {};
+	int fd;
+
+	fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		return -1;
+	strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+	if (ioctl(fd, TUNSETIFF, &ifr)) {
+		close(fd);
+		return -1;
+	}
+	return fd;
+}
+
+/* one getdents64() into @buf, the names appended to @names */
+static int list(int fd, char *buf, size_t size, char names[][NAME_MAX + 1],
+		int *n)
+{
+	long ret, off;
+	int got = 0;
+
+	ret = syscall(__NR_getdents64, fd, buf, size);
+	if (ret < 0)
+		return -errno;
+	for (off = 0; off < ret;) {
+		struct dirent64 *d = (void *)(buf + off);
+
+		if (*n < NAMES)
+			strcpy(names[(*n)++], d->d_name);
+		got++;
+		off += d->d_reclen;
+	}
+	return got;
+}
+
+static int count(char names[][NAME_MAX + 1], int n, const char *name)
+{
+	int i, c = 0;
+
+	for (i = 0; i < n; i++)
+		if (!strcmp(names[i], name))
+			c++;
+	return c;
+}
+
+/* removes the device once getdents64() faults on its entry, then resolves */
+struct stall {
+	int uffd;
+	int tun;
+	unsigned long addr;
+	unsigned long len;
+	int removed;
+};
+
+static void *stall_resolve(void *arg)
+{
+	struct stall *s = arg;
+	struct pollfd pfd = { .fd = s->uffd, .events = POLLIN };
+	struct uffdio_zeropage zero = {
+		.range = { .start = s->addr, .len = s->len },
+	};
+	struct uffd_msg msg;
+
+	if (poll(&pfd, 1, 10000) != 1)
+		return NULL;
+	if (read(s->uffd, &msg, sizeof(msg)) != sizeof(msg) ||
+	    msg.event != UFFD_EVENT_PAGEFAULT)
+		return NULL;
+	close(s->tun);
+	s->removed = access(TUN_PATH, F_OK) == -1;
+	ioctl(s->uffd, UFFDIO_ZEROPAGE, &zero);
+	return NULL;
+}
+
+FIXTURE(proc_readdir) {
+	int dir;
+	int n;
+	char names[NAMES][NAME_MAX + 1];
+	char buf[65536];
+};
+
+FIXTURE_SETUP(proc_readdir)
+{
+	if (unshare(CLONE_NEWNET))
+		SKIP(return, "unshare(CLONE_NEWNET): %s", strerror(errno));
+	if (access("/dev/net/tun", F_OK))
+		SKIP(return, "/dev/net/tun: %s", strerror(errno));
+	self->dir = open(SNMP6_DIR, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+	if (self->dir < 0)
+		SKIP(return, SNMP6_DIR ": %s", strerror(errno));
+	self->n = 0;
+}
+
+FIXTURE_TEARDOWN(proc_readdir)
+{
+	close(self->dir);
+}
+
+/* a device added in front of the position doesn't repeat an entry */
+TEST_F(proc_readdir, add_in_front)
+{
+	int tun;
+
+	/* ".", ".." and "lo" */
+	ASSERT_EQ(list(self->dir, self->buf, SMALL_BUF, self->names, &self->n), 3);
+	ASSERT_EQ(count(self->names, self->n, "lo"), 1);
+
+	tun = tun_open(TUN_NAME);
+	ASSERT_GE(tun, 0);
+	ASSERT_EQ(access(TUN_PATH, F_OK), 0);
+
+	while (list(self->dir, self->buf, sizeof(self->buf), self->names,
+		    &self->n) > 0)
+		;
+	EXPECT_EQ(count(self->names, self->n, "lo"), 1);
+	close(tun);
+}
+
+/* a device removed in front of the position doesn't skip an entry */
+TEST_F(proc_readdir, remove_in_front)
+{
+	int tun;
+
+	tun = tun_open(TUN_NAME);
+	ASSERT_GE(tun, 0);
+	ASSERT_EQ(access(TUN_PATH, F_OK), 0);
+
+	/* ".", ".." and the device, "lo" doesn't fit */
+	ASSERT_EQ(list(self->dir, self->buf, SMALL_BUF, self->names, &self->n), 3);
+	ASSERT_EQ(count(self->names, self->n, TUN_NAME), 1);
+
+	/* closing the device unregisters it and removes its entry */
+	close(tun);
+	ASSERT_EQ(access(TUN_PATH, F_OK), -1);
+
+	while (list(self->dir, self->buf, sizeof(self->buf), self->names,
+		    &self->n) > 0)
+		;
+	EXPECT_EQ(count(self->names, self->n, "lo"), 1);
+}
+
+/* the entry being emitted is removed meanwhile, the listing goes on */
+TEST_F(proc_readdir, remove_current)
+{
+	struct uffdio_api api = { .api = UFFD_API };
+	struct uffdio_register reg = {};
+	long page = sysconf(_SC_PAGESIZE);
+	struct stall s = {};
+	pthread_t thread;
+	char *map;
+	long ret, off;
+
+	s.uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (s.uffd < 0)
+		SKIP(return, "userfaultfd: %s", strerror(errno));
+	ASSERT_EQ(ioctl(s.uffd, UFFDIO_API, &api), 0);
+
+	/* the first page is there, the second one faults */
+	map = mmap(NULL, 2 * page, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(map, MAP_FAILED);
+	map[0] = 0;
+	s.addr = (unsigned long)map + page;
+	s.len = page;
+	reg.range.start = s.addr;
+	reg.range.len = s.len;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ASSERT_EQ(ioctl(s.uffd, UFFDIO_REGISTER, &reg), 0);
+
+	s.tun = tun_open(TUN_NAME);
+	ASSERT_GE(s.tun, 0);
+	ASSERT_EQ(access(TUN_PATH, F_OK), 0);
+	ASSERT_EQ(pthread_create(&thread, NULL, stall_resolve, &s), 0);
+
+	/*
+	 * ".." ends right before the second page, so writing the record of
+	 * the device stalls until it has been removed.
+	 */
+	ret = syscall(__NR_getdents64, self->dir, map + page - 48, 48 + page);
+	ASSERT_EQ(pthread_join(thread, NULL), 0);
+	ASSERT_TRUE(s.removed);
+	ASSERT_GT(ret, 0);
+	for (off = 0; off < ret;) {
+		struct dirent64 *d = (void *)(map + page - 48 + off);
+
+		if (self->n < NAMES)
+			strcpy(self->names[self->n++], d->d_name);
+		off += d->d_reclen;
+	}
+	ASSERT_EQ(count(self->names, self->n, TUN_NAME), 1);
+
+	while (list(self->dir, self->buf, sizeof(self->buf), self->names,
+		    &self->n) > 0)
+		;
+	EXPECT_EQ(count(self->names, self->n, "lo"), 1);
+	munmap(map, 2 * page);
+	close(s.uffd);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 21/38] proc: let proc_remove() remove the entry it was given
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (19 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 20/38] selftests/proc: check a /proc listing while entries come and go Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 22/38] proc: warn about entries left in /proc/net of a dying namespace Christian Brauner
                     ` (16 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_remove() looks the entry up by its name in its parent and removes
whatever it finds there:

  remove_proc_subtree(de->name, de->parent);

If the entry was removed already, say along with its parent, that is
either nothing or a different entry that got the same name in the
meantime, and de->parent may well be gone. Two removals of the same
entry are a bug in the caller, but going by the name turns it into
removing an entry that belongs to somebody else.

Since commit 895b4c0c79b0 ("fs/proc: fix uaf in proc_readdir_de()")
an entry that was unlinked has an empty rb node. So check for that
under proc_subdir_lock and otherwise remove the entry itself, with the
loop of remove_proc_subtree() moved into a helper both of them use.
Nothing can help a caller that passes an entry that has been freed
already.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: a8ca16ea7b0a ("proc: Supply a function to remove a proc entry by PDE")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c | 78 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 51 insertions(+), 27 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 47830d6af255..23fc026d7bbb 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -817,33 +817,14 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
 }
 EXPORT_SYMBOL(remove_proc_entry);
 
-int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
+/*
+ * Remove @root, already erased from its parent, and everything below it.
+ * Called with proc_subdir_lock held for writing, returns with it dropped.
+ */
+static int __remove_proc_subtree(struct proc_dir_entry *root)
 {
-	struct proc_dir_entry *root = NULL, *de, *next;
-	const char *fn = name;
-	unsigned int len;
-
-	write_lock(&proc_subdir_lock);
-	if (__xlate_proc_name(name, &parent, &fn) != 0) {
-		write_unlock(&proc_subdir_lock);
-		return -ENOENT;
-	}
-	len = strlen(fn);
+	struct proc_dir_entry *de = root, *next;
 
-	root = pde_subdir_find(parent, fn, len);
-	if (!root) {
-		write_unlock(&proc_subdir_lock);
-		return -ENOENT;
-	}
-	if (unlikely(pde_is_permanent(root))) {
-		write_unlock(&proc_subdir_lock);
-		WARN(1, "removing permanent /proc entry '%s/%s'",
-			root->parent->name, root->name);
-		return -EINVAL;
-	}
-	pde_erase(root, parent);
-
-	de = root;
 	while (1) {
 		next = pde_subdir_first(de);
 		if (next) {
@@ -871,6 +852,34 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
 	pde_put(root);
 	return 0;
 }
+
+int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *root = NULL;
+	const char *fn = name;
+	unsigned int len;
+
+	write_lock(&proc_subdir_lock);
+	if (__xlate_proc_name(name, &parent, &fn) != 0) {
+		write_unlock(&proc_subdir_lock);
+		return -ENOENT;
+	}
+	len = strlen(fn);
+
+	root = pde_subdir_find(parent, fn, len);
+	if (!root) {
+		write_unlock(&proc_subdir_lock);
+		return -ENOENT;
+	}
+	if (unlikely(pde_is_permanent(root))) {
+		write_unlock(&proc_subdir_lock);
+		WARN(1, "removing permanent /proc entry '%s/%s'",
+			root->parent->name, root->name);
+		return -EINVAL;
+	}
+	pde_erase(root, parent);
+	return __remove_proc_subtree(root);
+}
 EXPORT_SYMBOL(remove_proc_subtree);
 
 void *proc_get_parent_data(const struct inode *inode)
@@ -882,8 +891,23 @@ EXPORT_SYMBOL_GPL(proc_get_parent_data);
 
 void proc_remove(struct proc_dir_entry *de)
 {
-	if (de)
-		remove_proc_subtree(de->name, de->parent);
+	if (!de)
+		return;
+
+	write_lock(&proc_subdir_lock);
+	/* Removed already, maybe along with its parent. */
+	if (RB_EMPTY_NODE(&de->subdir_node)) {
+		write_unlock(&proc_subdir_lock);
+		return;
+	}
+	if (unlikely(pde_is_permanent(de))) {
+		write_unlock(&proc_subdir_lock);
+		WARN(1, "removing permanent /proc entry '%s/%s'",
+		     de->parent->name, de->name);
+		return;
+	}
+	pde_erase(de, de->parent);
+	__remove_proc_subtree(de);
 }
 EXPORT_SYMBOL(proc_remove);
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 22/38] proc: warn about entries left in /proc/net of a dying namespace
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (20 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 21/38] proc: let proc_remove() remove the entry it was given Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 23/38] proc: don't let NOMMU map procfs files Christian Brauner
                     ` (15 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_net_ns_exit() runs after all the other pernet exits and frees
net->proc_net right after removing "stat" from it. An entry that some
pernet exit forgot to remove is left with a freed ->parent, and so
PDE_NET() hands seq_open_net() and single_open_net() a freed struct net
when the entry is opened through a file descriptor that someone kept.

There is no such entry in the tree that we know of, but nothing would
tell us about one either. So warn about it like remove_proc_entry()
does for a directory that isn't empty.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: e9720acd728a ("[NET]: Make /proc/net a symlink on /proc/self/net (v3)")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_net.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index dbcf87a257e1..df5484813d5d 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -410,7 +410,13 @@ static __net_init int proc_net_ns_init(struct net *net)
 
 static __net_exit void proc_net_ns_exit(struct net *net)
 {
+	struct proc_dir_entry *left;
+
 	remove_proc_entry("stat", net->proc_net);
+	/* Whatever is left still points at net->proc_net and so at net. */
+	left = rb_entry_safe(rb_first(&net->proc_net->subdir),
+			     struct proc_dir_entry, subdir_node);
+	WARN(left, "/proc/net: leaking at least '%s'\n", left->name);
 	pde_free(net->proc_net);
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 23/38] proc: don't let NOMMU map procfs files
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (21 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 22/38] proc: warn about entries left in /proc/net of a dying namespace Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 24/38] proc/vmcore: don't let vmcore_cleanup() run twice at once Christian Brauner
                     ` (14 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

Every procfs file has a ->mmap since commit 786d7e1612f0 ("Fix
rmmod/read/write races in /proc entries"), whether the entry has a
->proc_mmap or not. On NOMMU validate_mmap_request() takes that to mean
the file can be mapped. procfs has no ->mmap_capabilities so a regular
file gets NOMMU_MAP_COPY, and a private mapping never calls ->mmap at
all. Instead do_mmap() takes nommu_region_sem for writing and
do_mmap_private() reads the file into the copy with kernel_read().

Reading /proc/maps takes nommu_region_sem as well:

  mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, open("/proc/maps", O_RDONLY), 0)

  do_mmap()                          // down_write(&nommu_region_sem)
  -> do_mmap_private()
     -> kernel_read()
        -> seq_read_iter()
           -> nommu_region_list_start()   // down_read()

The task hangs for good, holding nommu_region_sem and its own mmap_lock,
and every mmap(), munmap() and exit after that hangs behind it.
/proc/maps is 0444 so any user can do that.

Give the procfs files a ->mmap_capabilities on NOMMU that allows
nothing. Mapping a procfs file then fails with -ENODEV, like mapping a
file without ->mmap did before 786d7e1612f0. NOMMU never called
->proc_mmap() for a regular file anyway, procfs never offered
NOMMU_MAP_DIRECT.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 786d7e1612f0 ("Fix rmmod/read/write races in /proc entries")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/inode.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index b7634f975d98..9933f03ab9fb 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -466,6 +466,18 @@ proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
 	return rv;
 }
 
+#ifndef CONFIG_MMU
+/*
+ * NOMMU never calls ->proc_mmap() for a regular file. It reads the file into
+ * a private copy instead, with nommu_region_sem held for writing, which
+ * reading /proc/maps takes as well. Don't map procfs files at all.
+ */
+static unsigned int proc_reg_mmap_capabilities(struct file *file)
+{
+	return 0;
+}
+#endif
+
 static int proc_reg_open(struct inode *inode, struct file *file)
 {
 	struct proc_dir_entry *pde = PDE(inode);
@@ -559,6 +571,9 @@ static const struct file_operations proc_reg_file_ops = {
 	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
@@ -572,6 +587,9 @@ static const struct file_operations proc_iter_file_ops = {
 	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
@@ -586,6 +604,9 @@ static const struct file_operations proc_reg_file_ops_compat = {
 	.compat_ioctl	= proc_reg_compat_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
@@ -600,6 +621,9 @@ static const struct file_operations proc_iter_file_ops_compat = {
 	.compat_ioctl	= proc_reg_compat_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 24/38] proc/vmcore: don't let vmcore_cleanup() run twice at once
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (22 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 23/38] proc: don't let NOMMU map procfs files Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 25/38] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page() Christian Brauner
                     ` (13 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

vmcore_cleanup() has a single caller, fadump's release_mem_store() on
powerpc. That checks fw_dump.dump_active without a lock and calls
vmcore_cleanup() without one, and kernfs only serializes the writers of
one open file. So two tasks that write to
/sys/kernel/fadump/release_mem through different files run
vmcore_cleanup() at the same time.

The first one removes /proc/vmcore and waits for its readers in
proc_remove(). The second one finds the entry already unlinked, returns
from proc_remove() right away and frees vmcore_list, the ELF headers
and the device dumps while those readers still walk them. Or both of
them free the same lists at once.

Serialize vmcore_cleanup() with a mutex of its own. It can't be
vmcore_mutex, removing /proc/vmcore calls release_vmcore() which takes
it. The second caller then finds nothing left to free.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: b500afff11f6 ("fadump: Invalidate registration and release reserved memory for general use.")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/vmcore.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 1fe70c4ecc54..ccb795b85509 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -1764,6 +1764,11 @@ fs_initcall(vmcore_init);
 /* Cleanup function for vmcore module. */
 void vmcore_cleanup(void)
 {
+	/* Not vmcore_mutex, removing /proc/vmcore calls release_vmcore(). */
+	static DEFINE_MUTEX(vmcore_cleanup_mutex);
+
+	/* Two writers of fadump's release_mem can get here at once. */
+	guard(mutex)(&vmcore_cleanup_mutex);
 	if (proc_vmcore) {
 		proc_remove(proc_vmcore);
 		proc_vmcore = NULL;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 25/38] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page()
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (23 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 24/38] proc/vmcore: don't let vmcore_cleanup() run twice at once Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 26/38] powerpc/crash_dump: recover from a machine check " Christian Brauner
                     ` (12 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

copy_oldmem_page() maps a page of the old kernel that isn't in the
linear mapping with ioremap_cache() and copies from it without checking
whether the mapping worked. If it didn't, the copy reads from an offset
into the NULL page. That oopses unless the copy happens to be a user
copy, whose exception table entries turn the fault into a short copy.

Return -ENOMEM like the other architectures do. read_from_oldmem()
turns that into -EFAULT like any other short copy.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 54c32021eb6f ("[PATCH] powerpc: Add arch-dependent copy_oldmem_page")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/crash_dump.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c
index 103b6605dd68..1543917bd3e1 100644
--- a/arch/powerpc/kernel/crash_dump.c
+++ b/arch/powerpc/kernel/crash_dump.c
@@ -86,6 +86,8 @@ ssize_t copy_oldmem_page(struct iov_iter *iter, unsigned long pfn,
 		csize = copy_to_iter(vaddr + offset, csize, iter);
 	} else {
 		vaddr = ioremap_cache(paddr, PAGE_SIZE);
+		if (!vaddr)
+			return -ENOMEM;
 		csize = copy_to_iter(vaddr + offset, csize, iter);
 		iounmap(vaddr);
 	}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 26/38] powerpc/crash_dump: recover from a machine check in copy_oldmem_page()
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (24 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 25/38] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page() Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 27/38] sunrpc: make queue_wait a global wait queue again Christian Brauner
                     ` (11 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

A capture kernel reads the memory of the crashed kernel, and that memory
may well have uncorrectable errors. On powerpc a machine check in the
kernel is only recovered from if the instruction that took it has an
exception table entry (cf. mce_common_process_ue()). copy_oldmem_page()
copies with copy_to_iter(). For a read() of /proc/vmcore that is a user
copy, all of whose loads have exception table entries, so the machine
check turns into a short copy and read_from_oldmem() returns -EFAULT.

But since commit 4a22fd20379c ("vmcore: convert __read_vmcore to use an
iov_iter") /proc/vmcore can be spliced too, and splice(), sendfile()
and copy_file_range() copy into kernel pages with memcpy(), which has
no exception table entries. A machine check there panics the capture
kernel. The same goes for the reads of the ELF headers and notes that
vmcore does itself.

Copy with _copy_mc_to_iter() instead. PPC64 selects ARCH_HAS_COPY_MC
and there it uses copy_mc_generic() for kernel and user destinations
alike, which has the exception table entries. Everywhere else it is
_copy_to_iter().

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 4a22fd20379c ("vmcore: convert __read_vmcore to use an iov_iter")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/crash_dump.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c
index 1543917bd3e1..ca7e019ae3e9 100644
--- a/arch/powerpc/kernel/crash_dump.c
+++ b/arch/powerpc/kernel/crash_dump.c
@@ -83,12 +83,12 @@ ssize_t copy_oldmem_page(struct iov_iter *iter, unsigned long pfn,
 
 	if (memblock_is_region_memory(paddr, csize)) {
 		vaddr = __va(paddr);
-		csize = copy_to_iter(vaddr + offset, csize, iter);
+		csize = _copy_mc_to_iter(vaddr + offset, csize, iter);
 	} else {
 		vaddr = ioremap_cache(paddr, PAGE_SIZE);
 		if (!vaddr)
 			return -ENOMEM;
-		csize = copy_to_iter(vaddr + offset, csize, iter);
+		csize = _copy_mc_to_iter(vaddr + offset, csize, iter);
 		iounmap(vaddr);
 	}
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 27/38] sunrpc: make queue_wait a global wait queue again
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (25 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 26/38] powerpc/crash_dump: recover from a machine check " Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 15:12     ` Chuck Lever
  2026-11-02 14:21   ` [PATCH RFC 28/38] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls Christian Brauner
                     ` (10 subsequent siblings)
  37 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

Since commit 552d0e17ea04 ("sunrpc: convert queue_wait from global to
per-cache-detail waitqueue") poll() and epoll queue their waiters for a
channel file on the queue_wait head in struct cache_detail. But the
cache_detail can be freed while its channel file is still open and
waited on.

The procfs channel files of a network namespace are removed when the
namespace goes away. Removing a procfs file waits for the callers that
are in one of its methods right now and then calls ->release on every
file that is still open (cf. close_pdeo()). The waiters that poll() and
epoll left on the head stay queued until their file is really closed.
So:

  sunrpc_exit_net()
  -> ip_map_cache_destroy()
     -> cache_unregister_net()
        -> remove_cache_proc_entries()
           -> proc_remove()      // ->release, the waiters stay queued
     -> cache_destroy_net()      // kfree(cd)

and when poll() returns remove_wait_queue() writes to the freed
cache_detail:

  BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x4b/0xa0
  Read of size 1 at addr ff110001131bb570 by task sunrpc-poll-uaf/242
  Call Trace:
   remove_wait_queue+0x22/0x1c0
   poll_freewait+0xc2/0x230
   do_sys_poll+0x9ba/0xd50
   __x64_sys_poll+0x165/0x470
  Freed by task 12:
   kfree+0x267/0x620
   sunrpc_exit_net+0x34/0x80
   ops_undo_list+0x271/0x8d0
   cleanup_net+0x3f3/0x860

Closing an epoll instance that watches the file gets there through
ep_unregister_pollwait() instead.

The channel files of a network namespace belong to the root of its
user namespace and cache_open() doesn't check for a capability. So an
unprivileged user can open the channel file of a namespace of its own
from the outside, poll it and let the namespace die.

Go back to a single static wait queue that never goes away. A wakeup
for one cache wakes the waiters of all caches again but cache_poll()
reports per cache_detail so that's just a spurious wakeup. procfs
should really own the wait queue of its files like kernfs does but
that's nothing for stable.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 552d0e17ea04 ("sunrpc: convert queue_wait from global to per-cache-detail waitqueue")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 include/linux/sunrpc/cache.h |  1 -
 net/sunrpc/cache.c           | 12 +++++++++---
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 2735c332ddb7..bf4fc543e1e5 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -119,7 +119,6 @@ struct cache_detail {
 	struct list_head	requests;
 	struct list_head	readers;
 	spinlock_t		queue_lock;
-	wait_queue_head_t	queue_wait;
 	u64			next_seqno;
 
 	atomic_t		writers;		/* how many time is /channel open */
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 1bc04109d213..1ceaea7e01b9 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -404,7 +404,6 @@ void sunrpc_init_cache_detail(struct cache_detail *cd)
 	INIT_LIST_HEAD(&cd->requests);
 	INIT_LIST_HEAD(&cd->readers);
 	spin_lock_init(&cd->queue_lock);
-	init_waitqueue_head(&cd->queue_wait);
 	cd->next_seqno = 1;
 	spin_lock(&cache_list_lock);
 	cd->nextcheck = 0;
@@ -965,13 +964,20 @@ static ssize_t cache_write(struct file *filp, const char __user *buf,
 	return ret;
 }
 
+/*
+ * Not in struct cache_detail: poll and epoll waiters stay queued until
+ * their file is closed, but a channel file in procfs can stay open after
+ * the cache_detail of its dying network namespace has been freed.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(queue_wait);
+
 static __poll_t cache_poll(struct file *filp, poll_table *wait,
 			       struct cache_detail *cd)
 {
 	__poll_t mask;
 	struct cache_reader *rp = filp->private_data;
 
-	poll_wait(filp, &cd->queue_wait, wait);
+	poll_wait(filp, &queue_wait, wait);
 
 	/* alway allow write */
 	mask = EPOLLOUT | EPOLLWRNORM;
@@ -1236,7 +1242,7 @@ static int cache_do_upcall(struct cache_detail *detail, struct cache_head *h)
 	spin_unlock(&detail->queue_lock);
 	if (ret != -EAGAIN && detail->cache_notify)
 		detail->cache_notify(detail, h);
-	wake_up(&detail->queue_wait);
+	wake_up(&queue_wait);
 	if (ret == -EAGAIN) {
 		kfree(buf);
 		kfree(crq);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 28/38] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (26 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 27/38] sunrpc: make queue_wait a global wait queue again Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 29/38] powerpc/pseries: bound the lengths in the ofdt property parser Christian Brauner
                     ` (9 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

esas2r_ioctl_handler() copies in a struct atto_express_ioctl, a bit over
512k, and hands the union member for the command to the handlers. For
EXPRESS_IOCTL_HBA and EXPRESS_IOCTL_SMP the length of the buffered ioctl
is the size of the member plus lengths that come straight from
userspace:

  bi.length = sizeof(struct atto_ioctl) + ioctl_hba->data_length;

  bi.length = sizeof(struct atto_ioctl_smp)
	    + le32_to_cpu(si->req_length)
	    + le32_to_cpu(si->rsp_length);

handle_buffered_ioctl() copies bi.length bytes from the member into a
DMA buffer and, once the adapter is done, copies the DMA buffer back
over the member. Nothing checks that the lengths fit into the struct
that was copied in. So a large data_length makes it read past the
struct and then write the DMA buffer back over the memory behind it.

The firmware commands pass sizeof(struct atto_express_ioctl) to
esas2r_{read,write}_{fw,fs}() as the length of data.fwrw.image, which
starts 28 bytes into the struct. So they can read and write 28 bytes
past it as well.

The handler is reachable through /proc/scsi/esas2r/ATTONode. It is
created with mode 0, which procfs turns into 0444, and esas2r doesn't
check for a capability anywhere. So any local user can do this on a
machine with such an adapter.

Bound all of these by the room from the member to the end of the
struct. read_hw() passes handle_hba_ioctl() a single struct atto_ioctl
and only ever returns that, so there the data length has to be zero.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 26780d9e12ed ("[SCSI] esas2r: ATTO Technology ExpressSAS 6G SAS/SATA RAID Adapter Driver")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/esas2r/esas2r.h       |  2 +-
 drivers/scsi/esas2r/esas2r_ioctl.c | 41 ++++++++++++++++++++++++--------------
 drivers/scsi/esas2r/esas2r_main.c  |  3 ++-
 3 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index a763edd05fe4..10101f50b282 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -967,7 +967,7 @@ int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
 int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg);
 int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg);
 u8 handle_hba_ioctl(struct esas2r_adapter *a,
-		    struct atto_ioctl *ioctl_hba);
+		    struct atto_ioctl *ioctl_hba, size_t room);
 enum scsi_qc_status esas2r_queuecommand(struct Scsi_Host *host,
 					struct scsi_cmnd *cmd);
 int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh);
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
index 3f7c1d131ec3..f99318f61c93 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -311,17 +311,27 @@ static int smp_ioctl_callback(struct esas2r_adapter *a,
 	return true;
 }
 
+/* The room from @member to the end of a struct atto_express_ioctl. */
+#define EXPRESS_IOCTL_ROOM(member)				\
+	(sizeof(struct atto_express_ioctl) -			\
+	 offsetof(struct atto_express_ioctl, member))
+
 static u8 handle_smp_ioctl(struct esas2r_adapter *a, struct atto_ioctl_smp *si)
 {
 	struct esas2r_buffered_ioctl bi;
+	u64 length = (u64)sizeof(struct atto_ioctl_smp) +
+		     le32_to_cpu(si->req_length) +
+		     le32_to_cpu(si->rsp_length);
+
+	/* The request and the response follow @si in the same buffer. */
+	if (length > EXPRESS_IOCTL_ROOM(data.ioctl_smp))
+		return IOCTL_INVALID_PARAM;
 
 	memset(&bi, 0, sizeof(bi));
 
 	bi.a = a;
 	bi.ioctl = si;
-	bi.length = sizeof(struct atto_ioctl_smp)
-		    + le32_to_cpu(si->req_length)
-		    + le32_to_cpu(si->rsp_length);
+	bi.length = length;
 	bi.offset = 0;
 	bi.callback = smp_ioctl_callback;
 	return handle_buffered_ioctl(&bi);
@@ -1221,11 +1231,16 @@ static void hba_ioctl_done_callback(struct esas2r_adapter *a,
 	}
 }
 
+/* @room is the size of the buffer that starts with @ioctl_hba. */
 u8 handle_hba_ioctl(struct esas2r_adapter *a,
-		    struct atto_ioctl *ioctl_hba)
+		    struct atto_ioctl *ioctl_hba, size_t room)
 {
 	struct esas2r_buffered_ioctl bi;
 
+	/* The data follows @ioctl_hba in the same buffer. */
+	if (ioctl_hba->data_length > room - sizeof(struct atto_ioctl))
+		return IOCTL_INVALID_PARAM;
+
 	memset(&bi, 0, sizeof(bi));
 
 	bi.a = a;
@@ -1349,31 +1364,27 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 			err = esas2r_write_fw(a,
 					      (char *)ioctl->data.fwrw.image,
 					      0,
-					      sizeof(struct
-						     atto_express_ioctl));
+					      EXPRESS_IOCTL_ROOM(data.fwrw.image));
 
 			if (err >= 0) {
 				err = esas2r_read_fw(a,
 						     (char *)ioctl->data.fwrw.
 						     image,
 						     0,
-						     sizeof(struct
-							    atto_express_ioctl));
+						     EXPRESS_IOCTL_ROOM(data.fwrw.image));
 			}
 		} else if (ioctl->data.fwrw.img_type == FW_IMG_FS_API) {
 			err = esas2r_write_fs(a,
 					      (char *)ioctl->data.fwrw.image,
 					      0,
-					      sizeof(struct
-						     atto_express_ioctl));
+					      EXPRESS_IOCTL_ROOM(data.fwrw.image));
 
 			if (err >= 0) {
 				err = esas2r_read_fs(a,
 						     (char *)ioctl->data.fwrw.
 						     image,
 						     0,
-						     sizeof(struct
-							    atto_express_ioctl));
+						     EXPRESS_IOCTL_ROOM(data.fwrw.image));
 			}
 		} else {
 			ioctl->header.return_code = IOCTL_BAD_FLASH_IMGTYPE;
@@ -1441,9 +1452,9 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 		break;
 
 	case EXPRESS_IOCTL_HBA:
-		ioctl->header.return_code = handle_hba_ioctl(a,
-							     &ioctl->data.
-							     ioctl_hba);
+		ioctl->header.return_code =
+			handle_hba_ioctl(a, &ioctl->data.ioctl_hba,
+					 EXPRESS_IOCTL_ROOM(data.ioctl_hba));
 		break;
 
 	case EXPRESS_IOCTL_VDA:
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index ada278c24c51..75a70ae9441b 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -178,7 +178,8 @@ static ssize_t read_hw(struct file *file, struct kobject *kobj,
 	if (!a->local_atto_ioctl)
 		return -ENOMEM;
 
-	if (handle_hba_ioctl(a, a->local_atto_ioctl) != IOCTL_SUCCESS)
+	if (handle_hba_ioctl(a, a->local_atto_ioctl,
+			     sizeof(struct atto_ioctl)) != IOCTL_SUCCESS)
 		return -ENOMEM;
 
 	memcpy(buf, a->local_atto_ioctl, length);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 29/38] powerpc/pseries: bound the lengths in the ofdt property parser
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (27 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 28/38] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 30/38] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long Christian Brauner
                     ` (8 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

The commands for /proc/powerpc/ofdt that carry properties, "add_node",
"add_property" and "update_property", can make the parser read past the
buffer they were copied into:

(1) do_update_property() computes the end of the buffer after
    parse_node() moved buf past the phandle. So the end is off by the
    length of the phandle string, which can be padded with zeroes, and
    parse_next_property() accepts a value that reaches that far past
    the command. new_property() then copies those bytes into the
    property, which everyone can read in /proc/device-tree.

(2) parse_next_property() parses the length with simple_strtoul() into
    an int and only rejects -1. A length above INT_MAX turns negative,
    tmp += *length moves backwards and passes the check against the
    end, so *tmp is read up to 2G in front of the buffer.

(3) Updating "slb-size" or "ibm,slb-size" reads an int from the value
    whatever its length.

Compute the end before parsing the phandle, reject lengths that don't
fit into an int or into what is left of the buffer, and only take the
slb size from a value that holds one. The file is root-only and
refused under lockdown but the parser still shouldn't read past its
input.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: 610d91511f99 ("[PATCH] powerpc: Add support for changing properties from userspace")
Fixes: 46db2f86a3b2 ("powerpc/pseries: Fix to handle slb resize across migration")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/platforms/pseries/reconfig.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index 18e3f1a036e3..b41c5db726c9 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -114,6 +114,7 @@ static void release_prop_list(const struct property *prop)
 static char * parse_next_property(char *buf, char *end, char **name, int *length,
 				  unsigned char **value)
 {
+	unsigned long len;
 	char *tmp;
 
 	*name = buf;
@@ -133,13 +134,13 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
 	}
 
 	/* now we're on the length */
-	*length = -1;
-	*length = simple_strtoul(tmp, &tmp, 10);
-	if (*length == -1) {
+	len = simple_strtoul(tmp, &tmp, 10);
+	if (len > INT_MAX) {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
 		return NULL;
 	}
+	*length = len;
 	if (*tmp != ' ' || ++tmp >= end) {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
@@ -148,13 +149,13 @@ static char * parse_next_property(char *buf, char *end, char **name, int *length
 
 	/* now we're on the value */
 	*value = tmp;
-	tmp += *length;
-	if (tmp > end) {
+	if (len > end - tmp) {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
 		return NULL;
 	}
-	else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
+	tmp += len;
+	if (tmp < end && *tmp != ' ' && *tmp != '\0') {
 		printk(KERN_ERR "property parse failed in %s at line %d\n",
 		       __func__, __LINE__);
 		return NULL;
@@ -320,8 +321,8 @@ static int do_update_property(char *buf, size_t bufsize)
 	char *name, *end, *next_prop;
 	int length;
 	struct property *newprop;
-	buf = parse_node(buf, bufsize, &np);
 	end = buf + bufsize;
+	buf = parse_node(buf, bufsize, &np);
 
 	if (!np)
 		return -ENODEV;
@@ -337,7 +338,8 @@ static int do_update_property(char *buf, size_t bufsize)
 	if (!newprop)
 		return -ENOMEM;
 
-	if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
+	if ((!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size")) &&
+	    length >= (int)sizeof(int))
 		slb_set_size(*(int *)value);
 
 	return of_update_property(np, newprop);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 30/38] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (28 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 29/38] powerpc/pseries: bound the lengths in the ofdt property parser Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 31/38] proc: sysctl: resume readdir after the last name it returned Christian Brauner
                     ` (7 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

h_get_ppp() returns the status of the H_GET_PPP hcall as an unsigned
int. update_ppp() hands it on as its ssize_t result, and
lparcfg_write() turns the hcall statuses it knows into an errno and
returns anything else as it is. So when H_GET_PPP fails, say with
H_PARAMETER (-4), the status comes back as 4294967292 and a write to
/proc/powerpc/lparcfg reports that many bytes written.

Return the status as the long it is. lparcfg_write() then treats it
like the status of H_SET_PPP and an unknown one is at least negative.
Before commit 11529396ea31 ("powerpc/pseries: Split processor
entitlement retrieval and gathering to helper routines") a failed
H_GET_PPP turned into -EIO.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 11529396ea31 ("powerpc/pseries: Split processor entitlement retrieval and gathering to helper routines")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/platforms/pseries/lparcfg.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
index 3280b7968cab..aa9b5b141434 100644
--- a/arch/powerpc/platforms/pseries/lparcfg.c
+++ b/arch/powerpc/platforms/pseries/lparcfg.c
@@ -113,7 +113,7 @@ struct hvcall_ppp_data {
  *	          XXXXXX - Entitled procs capacity % available to the
  *			   partitions pool.
  */
-static unsigned int h_get_ppp(struct hvcall_ppp_data *ppp_data)
+static long h_get_ppp(struct hvcall_ppp_data *ppp_data)
 {
 	unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
 	long rc;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 31/38] proc: sysctl: resume readdir after the last name it returned
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (29 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 30/38] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 32/38] selftests/proc: check a sysctl listing while entries come and go Christian Brauner
                     ` (6 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_readdir() counts the entries of a directory and resumes a
listing at the number getdents64() stopped at. The entries are sorted
by name, so one that is registered or unregistered in between shifts
everything behind it by one. Listing /proc/sys/net/ipv4/conf with a
buffer that holds ".", ".." and "all" and registering a device whose
name sorts before "all" makes the next call return "all" a second time.
Unregistering one that sorted before the position makes it skip an
entry instead. So a listing that takes more than one call shows a name
twice, or not at all. Even the 64k buffer of ls needs a second call to
find the end of the directory, so a device that comes up right then
repeats the last entry.

So remember the name of the last entry a call emitted in the file and
resume at the first usable entry after it, like kernfs resumes after
the node it stopped at. The entry itself can't be pinned across calls,
unregistering waits for that. If the position was moved by a seek in
between the listing counts from the start like before, seeking to an
arbitrary position has never been better than that.

That also takes one walk down the tree to resume instead of a walk over
every entry in front of the position with a round trip through
sysctl_lock for each of them.

Fixes: 77b14db502cb ("[PATCH] sysctl: reimplement the sysctl proc support")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 103 insertions(+), 3 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 6c0859c66563..38b61887b59c 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -417,6 +417,54 @@ static void next_entry(struct ctl_table_header **phead, const struct ctl_table *
 	*pentry = entry;
 }
 
+/*
+ * The first usable entry of @dir after @name, or the first one at all for a
+ * NULL @name. The entries are sorted by name, so that is where a listing
+ * resumes when entries in front of it came or went in between. -ENOENT if
+ * the directory is being unregistered.
+ */
+static int entry_after(struct ctl_dir *dir, const char *name, int namelen,
+		       struct ctl_table_header **phead,
+		       const struct ctl_table **pentry)
+{
+	struct ctl_table_header *head = NULL;
+	const struct ctl_table *entry = NULL;
+	struct rb_node *node, *after = NULL;
+	struct ctl_node *ctl_node;
+
+	spin_lock(&sysctl_lock);
+	if (unlikely(dir->header.unregistering)) {
+		spin_unlock(&sysctl_lock);
+		return -ENOENT;
+	}
+	if (!name) {
+		after = rb_first(&dir->root);
+	} else for (node = dir->root.rb_node; node;) {
+		const struct ctl_table *table;
+		struct ctl_table_header *h;
+
+		ctl_node = rb_entry(node, struct ctl_node, node);
+		h = ctl_node->header;
+		table = &h->ctl_table[ctl_node - h->node];
+		if (namecmp(table->procname, strlen(table->procname),
+			    name, namelen) > 0) {
+			after = node;
+			node = node->rb_left;
+		} else {
+			node = node->rb_right;
+		}
+	}
+	ctl_node = first_usable_entry(after);
+	spin_unlock(&sysctl_lock);
+	if (ctl_node) {
+		head = ctl_node->header;
+		entry = &head->ctl_table[ctl_node - head->node];
+	}
+	*phead = head;
+	*pentry = entry;
+	return 0;
+}
+
 /*
  * sysctl_perm does NOT grant the superuser all rights automatically, because
  * some sysctl variables are readonly even to root.
@@ -675,6 +723,36 @@ static __poll_t proc_sys_poll(struct file *filp, poll_table *wait)
 	return ret;
 }
 
+/*
+ * Where the next readdir resumes. The entries are counted, so one that is
+ * registered or unregistered in front of the position would shift what
+ * the next call returns. The name of the last entry emitted doesn't move.
+ */
+struct sysctl_cursor {
+	loff_t pos;			/* the position behind the entry */
+	int len;
+	char name[NAME_MAX + 1];
+};
+
+/* If this can't remember the entry the next call counts from the start. */
+static void sysctl_cursor_set(struct file *file, const char *name, int len,
+			      loff_t pos)
+{
+	struct sysctl_cursor *cursor = file->private_data;
+
+	if (len > NAME_MAX)
+		return;
+	if (!cursor) {
+		cursor = kmalloc(sizeof(*cursor), GFP_KERNEL);
+		if (!cursor)
+			return;
+		file->private_data = cursor;
+	}
+	cursor->pos = pos;
+	cursor->len = len;
+	memcpy(cursor->name, name, len);
+}
+
 static bool proc_sys_fill_cache(struct file *file,
 				struct dir_context *ctx,
 				struct ctl_table_header *head,
@@ -753,8 +831,11 @@ static int scan(struct ctl_table_header *head, const struct ctl_table *table,
 	else
 		res = proc_sys_fill_cache(file, ctx, head, table);
 
-	if (res)
+	if (res) {
 		ctx->pos = *pos;
+		sysctl_cursor_set(file, table->procname, strlen(table->procname),
+				  ctx->pos);
+	}
 
 	return res;
 }
@@ -762,6 +843,7 @@ static int scan(struct ctl_table_header *head, const struct ctl_table *table,
 static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 {
 	struct ctl_table_header *head = grab_header(file_inode(file));
+	struct sysctl_cursor *cursor = file->private_data;
 	struct ctl_table_header *h = NULL;
 	const struct ctl_table *entry;
 	struct ctl_dir *ctl_dir;
@@ -775,9 +857,20 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 	if (!dir_emit_dots(file, ctx))
 		goto out;
 
-	pos = 2;
+	/*
+	 * Resume after the last entry the previous call emitted. Only a seek
+	 * that moved the position away from it counts from the start, like
+	 * seeking to an arbitrary position always did.
+	 */
+	if (cursor && cursor->pos == ctx->pos) {
+		entry_after(ctl_dir, cursor->name, cursor->len, &h, &entry);
+		pos = ctx->pos;
+	} else {
+		first_entry(ctl_dir, &h, &entry);
+		pos = 2;
+	}
 
-	for (first_entry(ctl_dir, &h, &entry); h; next_entry(&h, &entry)) {
+	for (; h; next_entry(&h, &entry)) {
 		if (!scan(h, entry, &pos, file, ctx)) {
 			sysctl_head_finish(h);
 			break;
@@ -788,6 +881,12 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 	return 0;
 }
 
+static int proc_sys_dir_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+	return 0;
+}
+
 static int proc_sys_permission(const struct mnt_idmap *idmap,
 			       struct inode *inode, int mask)
 {
@@ -867,6 +966,7 @@ static const struct file_operations proc_sys_dir_file_operations = {
 	.read		= generic_read_dir,
 	.iterate_shared	= proc_sys_readdir,
 	.llseek		= generic_file_llseek,
+	.release	= proc_sys_dir_release,
 };
 
 static const struct inode_operations proc_sys_inode_operations = {

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 32/38] selftests/proc: check a sysctl listing while entries come and go
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (30 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 31/38] proc: sysctl: resume readdir after the last name it returned Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 33/38] proc: sysctl: copy to and from userspace without holding the table Christian Brauner
                     ` (5 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

List /proc/sys/net/ipv4/conf of a new network namespace in two pieces:

- with a buffer that holds the dots and "all", register a tun device
  whose name sorts first, finish the listing: "all" is listed once

- with that device registered and a buffer that holds the dots and it,
  unregister it, finish the listing: "all" is still listed

Both fail without the fix, the first lists "all" twice, the second not
at all.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/proc/.gitignore            |   1 +
 tools/testing/selftests/proc/Makefile              |   1 +
 .../selftests/proc/proc-sysctl-readdir-churn.c     | 147 +++++++++++++++++++++
 3 files changed, 149 insertions(+)

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 8520c7878ea2..5b5e24e39cbd 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -25,4 +25,5 @@
 /self
 /setns-dcache
 /setns-sysvipc
+/proc-sysctl-readdir-churn
 /thread-self
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index e0c0d313f4d0..dae09f333455 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -30,6 +30,7 @@ TEST_GEN_PROGS += setns-sysvipc
 TEST_GEN_PROGS += thread-self
 TEST_GEN_PROGS += proc-multiple-procfs
 TEST_GEN_PROGS += proc-fsconfig-hidepid
+TEST_GEN_PROGS += proc-sysctl-readdir-churn
 TEST_GEN_PROGS += proc-pidns
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/proc-sysctl-readdir-churn.c b/tools/testing/selftests/proc/proc-sysctl-readdir-churn.c
new file mode 100644
index 000000000000..0e067a3619c4
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-sysctl-readdir-churn.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A listing of a /proc/sys directory that takes more than one getdents64()
+ * must not show an entry twice or skip one when an entry is registered or
+ * unregistered in between. /proc/sys/net/ipv4/conf/<dev> of a tun device
+ * comes and goes with the device.
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define CONF_DIR	"/proc/sys/net/ipv4/conf"
+#define TUN_NAME	"a0"		/* sorts before "all" */
+#define TUN_PATH	CONF_DIR "/" TUN_NAME
+#define SMALL_BUF	80		/* ".", ".." and one short name */
+#define NAMES		64
+
+static int tun_open(const char *name)
+{
+	struct ifreq ifr = {};
+	int fd;
+
+	fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		return -1;
+	strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+	if (ioctl(fd, TUNSETIFF, &ifr)) {
+		close(fd);
+		return -1;
+	}
+	return fd;
+}
+
+/* one getdents64() into @buf, the names appended to @names */
+static int list(int fd, char *buf, size_t size, char names[][NAME_MAX + 1],
+		int *n)
+{
+	long ret, off;
+	int got = 0;
+
+	ret = syscall(__NR_getdents64, fd, buf, size);
+	if (ret < 0)
+		return -errno;
+	for (off = 0; off < ret;) {
+		struct dirent64 *d = (void *)(buf + off);
+
+		if (*n < NAMES)
+			strcpy(names[(*n)++], d->d_name);
+		got++;
+		off += d->d_reclen;
+	}
+	return got;
+}
+
+static int count(char names[][NAME_MAX + 1], int n, const char *name)
+{
+	int i, c = 0;
+
+	for (i = 0; i < n; i++)
+		if (!strcmp(names[i], name))
+			c++;
+	return c;
+}
+
+FIXTURE(sysctl_readdir) {
+	int dir;
+	int n;
+	char names[NAMES][NAME_MAX + 1];
+	char buf[65536];
+};
+
+FIXTURE_SETUP(sysctl_readdir)
+{
+	if (unshare(CLONE_NEWNET))
+		SKIP(return, "unshare(CLONE_NEWNET): %s", strerror(errno));
+	if (access("/dev/net/tun", F_OK))
+		SKIP(return, "/dev/net/tun: %s", strerror(errno));
+	self->dir = open(CONF_DIR, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+	ASSERT_GE(self->dir, 0);
+	self->n = 0;
+}
+
+FIXTURE_TEARDOWN(sysctl_readdir)
+{
+	close(self->dir);
+}
+
+/* a device registered in front of the position doesn't repeat an entry */
+TEST_F(sysctl_readdir, register_in_front)
+{
+	int tun;
+
+	/* ".", ".." and "all", "default" doesn't fit */
+	ASSERT_EQ(list(self->dir, self->buf, SMALL_BUF, self->names, &self->n), 3);
+	ASSERT_EQ(count(self->names, self->n, "all"), 1);
+
+	tun = tun_open(TUN_NAME);
+	ASSERT_GE(tun, 0);
+	ASSERT_EQ(access(TUN_PATH, F_OK), 0);
+
+	while (list(self->dir, self->buf, sizeof(self->buf), self->names,
+		    &self->n) > 0)
+		;
+	EXPECT_EQ(count(self->names, self->n, "all"), 1);
+	EXPECT_EQ(count(self->names, self->n, "default"), 1);
+	EXPECT_EQ(count(self->names, self->n, "lo"), 1);
+	close(tun);
+}
+
+/* a device unregistered in front of the position doesn't skip an entry */
+TEST_F(sysctl_readdir, unregister_in_front)
+{
+	int tun;
+
+	tun = tun_open(TUN_NAME);
+	ASSERT_GE(tun, 0);
+	ASSERT_EQ(access(TUN_PATH, F_OK), 0);
+
+	/* ".", ".." and the device, "all" doesn't fit */
+	ASSERT_EQ(list(self->dir, self->buf, SMALL_BUF, self->names, &self->n), 3);
+	ASSERT_EQ(count(self->names, self->n, TUN_NAME), 1);
+
+	/* closing the device unregisters its sysctls under RTNL */
+	close(tun);
+	ASSERT_EQ(access(TUN_PATH, F_OK), -1);
+
+	while (list(self->dir, self->buf, sizeof(self->buf), self->names,
+		    &self->n) > 0)
+		;
+	EXPECT_EQ(count(self->names, self->n, "all"), 1);
+	EXPECT_EQ(count(self->names, self->n, "default"), 1);
+	EXPECT_EQ(count(self->names, self->n, "lo"), 1);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 33/38] proc: sysctl: copy to and from userspace without holding the table
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (31 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 32/38] selftests/proc: check a sysctl listing while entries come and go Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 34/38] proc: sysctl: look at the table under sysctl_lock when creating an inode Christian Brauner
                     ` (4 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_call_handler() grabs the table, copies in what is written,
calls the handler and copies what it produced out to userspace before
it lets go of the table again. Unregistering a table waits until nobody
uses it anymore. So a reader or writer whose copy faults for as long as
it wants, with userfaultfd or a stalled FUSE mapping, keeps
unregister_sysctl_table() waiting, and its callers like to hold locks
while they do that. addrconf unregisters the ipv6 sysctls of a device
with RTNL held, so a stalled read of /proc/sys/net/ipv6/conf/<dev>/mtu
holds up every RTNL user until the reader gives up.

Copy in before grabbing the table and copy out after letting go of it,
like procfs and kernfs do for their files. The buffer is ours, nothing
needs the table for the copies.

That moves the allocation and the copy in before the permission check.
A write that can't be copied in, or a request that is too large, now
fails with -EFAULT or -ENOMEM where it used to fail with -EPERM.

Fixes: 330d57fb98a9 ("[PATCH] Fix sysctl unregistration oops (CVE-2005-2709)")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 50 ++++++++++++++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 38b61887b59c..2150cf842919 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -602,14 +602,36 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
 		int write)
 {
 	struct inode *inode = file_inode(iocb->ki_filp);
-	struct ctl_table_header *head = grab_header(inode);
 	const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+	struct ctl_table_header *head;
 	size_t count = iov_iter_count(iter);
 	char *kbuf;
 	ssize_t error;
 
-	if (IS_ERR(head))
-		return PTR_ERR(head);
+	/* don't even try if the size is too large */
+	if (count >= KMALLOC_MAX_SIZE)
+		return -ENOMEM;
+	kbuf = kvzalloc(count + 1, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	/*
+	 * Copy in before grabbing the table and copy out after letting go of
+	 * it. Unregistering the table waits for us and a copy that faults can
+	 * take as long as userspace wants.
+	 */
+	if (write) {
+		error = -EFAULT;
+		if (!copy_from_iter_full(kbuf, count, iter))
+			goto out_free_buf;
+		kbuf[count] = '\0';
+	}
+
+	head = grab_header(inode);
+	if (IS_ERR(head)) {
+		error = PTR_ERR(head);
+		goto out_free_buf;
+	}
 
 	/*
 	 * At this point we know that the sysctl was not unregistered
@@ -624,28 +646,15 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
 	if (!table->proc_handler)
 		goto out;
 
-	/* don't even try if the size is too large */
-	error = -ENOMEM;
-	if (count >= KMALLOC_MAX_SIZE)
-		goto out;
-	kbuf = kvzalloc(count + 1, GFP_KERNEL);
-	if (!kbuf)
-		goto out;
-
-	if (write) {
-		error = -EFAULT;
-		if (!copy_from_iter_full(kbuf, count, iter))
-			goto out_free_buf;
-		kbuf[count] = '\0';
-	}
-
 	error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, &kbuf, &count,
 					   &iocb->ki_pos);
 	if (error)
-		goto out_free_buf;
+		goto out;
 
 	/* careful: calling conventions are nasty here */
 	error = table->proc_handler(table, write, kbuf, &count, &iocb->ki_pos);
+out:
+	sysctl_head_finish(head);
 	if (error)
 		goto out_free_buf;
 
@@ -658,9 +667,6 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
 	error = count;
 out_free_buf:
 	kvfree(kbuf);
-out:
-	sysctl_head_finish(head);
-
 	return error;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 34/38] proc: sysctl: look at the table under sysctl_lock when creating an inode
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (32 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 33/38] proc: sysctl: copy to and from userspace without holding the table Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:21   ` [PATCH RFC 35/38] proc: sysctl: don't hold the tables while readdir copies to userspace Christian Brauner
                     ` (3 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_sys_make_inode() checks under sysctl_lock that @head isn't being
unregistered, lets go of the lock, and then reads the mode of @table
and calls ->set_ownership() with @head. That's fine as long as the
caller holds @head in use, which keeps unregistering from getting past
that check. The next patch has a caller that only pins the memory of
@head. For that one the table, and whatever the owner of the header
keeps behind it, is only there for as long as nobody unregisters the
header, which happens under sysctl_lock. So read the mode and set the
ownership before letting go of the lock. The ->set_ownership()
callbacks only look up ids in a user namespace.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 2150cf842919..a1242247f492 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -500,6 +500,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
 	struct ctl_table_root *root = head->root;
 	struct inode *inode;
 	struct proc_inode *ei;
+	umode_t mode;
 
 	inode = new_inode(sb);
 	if (!inode)
@@ -509,12 +510,22 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
 
 	ei = PROC_I(inode);
 
+	inode->i_uid = GLOBAL_ROOT_UID;
+	inode->i_gid = GLOBAL_ROOT_GID;
 	spin_lock(&sysctl_lock);
 	if (unlikely(head->unregistering)) {
 		spin_unlock(&sysctl_lock);
 		iput(inode);
 		return ERR_PTR(-ENOENT);
 	}
+	/*
+	 * The table, and whatever the owner of the header keeps behind it,
+	 * is only there for as long as nobody unregisters the header, which
+	 * happens under sysctl_lock. So look at them before letting go of it.
+	 */
+	mode = table->mode;
+	if (root->set_ownership)
+		root->set_ownership(head, &inode->i_uid, &inode->i_gid);
 	ei->sysctl = head;
 	ei->sysctl_entry = table;
 	hlist_add_head_rcu(&ei->sibling_inodes, &head->inodes);
@@ -522,8 +533,8 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
 	spin_unlock(&sysctl_lock);
 
 	simple_inode_init_ts(inode);
-	inode->i_mode = table->mode;
-	if (!S_ISDIR(table->mode)) {
+	inode->i_mode = mode;
+	if (!S_ISDIR(mode)) {
 		inode->i_mode |= S_IFREG;
 		inode->i_op = &proc_sys_inode_operations;
 		inode->i_fop = &proc_sys_file_operations;
@@ -535,11 +546,6 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
 			make_empty_dir_inode(inode);
 	}
 
-	inode->i_uid = GLOBAL_ROOT_UID;
-	inode->i_gid = GLOBAL_ROOT_GID;
-	if (root->set_ownership)
-		root->set_ownership(head, &inode->i_uid, &inode->i_gid);
-
 	return inode;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 35/38] proc: sysctl: don't hold the tables while readdir copies to userspace
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (33 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 34/38] proc: sysctl: look at the table under sysctl_lock when creating an inode Christian Brauner
@ 2026-11-02 14:21   ` Christian Brauner
  2026-11-02 14:43   ` [PATCH RFC 36/38] selftests/proc: check that faulting sysctl accesses don't hold up removal Christian Brauner
                     ` (2 subsequent siblings)
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:21 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_sys_readdir() holds the directory's table for the whole readdir
and the table of each entry while dir_emit() copies its name to
userspace. Unregistering a table waits until nobody uses it anymore. So
a getdents64() whose buffer faults for as long as it wants keeps
unregister_sysctl_table() waiting, just like a stalled read() does.
Listing /proc/sys/net/ipv6/conf/<dev>/ that way holds up the removal of
the device under RTNL.

So don't take any table into use for a listing. A header counts what
refers to its memory, the inodes made for it, and unregistering doesn't
wait for those. readdir takes one of those references on the entry it
is at instead, which keeps the header around and nothing else. Under
sysctl_lock, where unregistering erases an entry, it finds the entry,
copies its name and mode, follows a link to the entry it stands for and
pins that. Then it looks the dentry up, creates the inode if there is
none, which checks for itself under sysctl_lock that the table is still
registered, and emits the entry with nothing held at all. The next
entry is the first live one after that name, which is where a listing
resumes between calls since "proc: sysctl: resume readdir after the
last name it returned" anyway. kernfs_fop_readdir() and
proc_readdir_de() work the same way, a reference on the entry and the
lock only to find the next one.

While the entry is still registered its node is still in the tree and
the next one is right behind it, like kernfs_dir_next_pos() continues
from the node it holds. Only once it went away, and to resume, is the
next entry found by name. That's one trip through sysctl_lock per entry
like before, and the link entries of /proc/sys/net no longer take two
more. The directory isn't
taken into use either, an entry keeps its directory registered while
it is used and readdir looks at the directory under the lock. So the
dots don't wait for anything, a directory that went away lists them and
then fails with -ENOENT. A seek to an arbitrary position counts the
entries from the start like before, one trip per entry.

Fixes: 77b14db502cb ("[PATCH] sysctl: reimplement the sysctl proc support")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/proc_sysctl.c | 307 +++++++++++++++++++++++++++-----------------------
 1 file changed, 166 insertions(+), 141 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index a1242247f492..0b7a1e487ed6 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -91,6 +91,8 @@ static void drop_sysctl_table(struct ctl_table_header *header);
 static int sysctl_follow_link(struct ctl_table_header **phead,
 	const struct ctl_table **pentry);
 static int insert_links(struct ctl_table_header *head);
+static struct ctl_table_set *lookup_header_set(struct ctl_table_root *root);
+static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir);
 static void put_links(struct ctl_table_header *header);
 
 static void sysctl_print_dir(struct ctl_dir *dir)
@@ -367,86 +369,97 @@ static const struct ctl_table *lookup_entry(struct ctl_table_header **phead,
 	return entry;
 }
 
-static struct ctl_node *first_usable_entry(struct rb_node *node)
-{
-	struct ctl_node *ctl_node;
+/*
+ * What readdir takes along for an entry between the trips through
+ * sysctl_lock: the header pinned, which keeps its memory and nothing else,
+ * the entry, its name and mode. For a link the header and entry are the
+ * ones it stands for in the caller's set.
+ */
+struct sysctl_dirent {
+	struct ctl_table_header *head;
+	const struct ctl_table *table;
+	struct ctl_node *node;		/* the entry's node in the directory, NULL for a link */
+	ino_t ino;
+	unsigned int type;
+	umode_t mode;
+	bool skip;
+	int len;
+	char name[NAME_MAX + 1];
+};
 
-	for (;node; node = rb_next(node)) {
-		ctl_node = rb_entry(node, struct ctl_node, node);
-		if (use_table(ctl_node->header))
-			return ctl_node;
-	}
-	return NULL;
+/* A reference on the memory of @head, like an inode made for it holds. */
+static void sysctl_pin(struct ctl_table_header *head)
+{
+	lockdep_assert_held(&sysctl_lock);
+	head->count++;
 }
 
-static void first_entry(struct ctl_dir *dir,
-	struct ctl_table_header **phead, const struct ctl_table **pentry)
+static void sysctl_unpin(struct ctl_table_header *head)
 {
-	struct ctl_table_header *head = NULL;
-	const struct ctl_table *entry = NULL;
-	struct ctl_node *ctl_node;
-
-	spin_lock(&sysctl_lock);
-	ctl_node = first_usable_entry(rb_first(&dir->root));
-	spin_unlock(&sysctl_lock);
-	if (ctl_node) {
-		head = ctl_node->header;
-		entry = &head->ctl_table[ctl_node - head->node];
-	}
-	*phead = head;
-	*pentry = entry;
+	lockdep_assert_held(&sysctl_lock);
+	if (!--head->count)
+		kfree_rcu(head, rcu);
 }
 
-static void next_entry(struct ctl_table_header **phead, const struct ctl_table **pentry)
+/* the first entry from @node on whose table isn't being unregistered */
+static struct ctl_node *first_live_entry(struct rb_node *node)
 {
-	struct ctl_table_header *head = *phead;
-	const struct ctl_table *entry = *pentry;
-	struct ctl_node *ctl_node = &head->node[entry - head->ctl_table];
-
-	spin_lock(&sysctl_lock);
-	unuse_table(head);
+	struct ctl_node *ctl_node;
 
-	ctl_node = first_usable_entry(rb_next(&ctl_node->node));
-	spin_unlock(&sysctl_lock);
-	head = NULL;
-	if (ctl_node) {
-		head = ctl_node->header;
-		entry = &head->ctl_table[ctl_node - head->node];
+	lockdep_assert_held(&sysctl_lock);
+	for (; node; node = rb_next(node)) {
+		ctl_node = rb_entry(node, struct ctl_node, node);
+		if (!ctl_node->header->unregistering)
+			return ctl_node;
 	}
-	*phead = head;
-	*pentry = entry;
+	return NULL;
 }
 
 /*
- * The first usable entry of @dir after @name, or the first one at all for a
- * NULL @name. The entries are sorted by name, so that is where a listing
- * resumes when entries in front of it came or went in between. -ENOENT if
- * the directory is being unregistered.
+ * Fill @d with the entry of @dir behind the one it holds, letting go of that
+ * one, or with the first live entry after @name, or the first one at all for
+ * a NULL @name. While the entry @d holds is still registered its node is
+ * still in the tree and the next one is right behind it. Once it is gone the
+ * entries are found by name, they are sorted by it, so that is also where a
+ * listing resumes when entries in front of it came or went in between.
+ * 1 with the next entry pinned in @d, 0 at the end of the directory and
+ * -ENOENT once @dir itself is being unregistered.
+ *
+ * Everything that reads the tables happens under sysctl_lock, where an entry
+ * is erased when it is unregistered, since nothing holds the tables in use.
  */
-static int entry_after(struct ctl_dir *dir, const char *name, int namelen,
-		       struct ctl_table_header **phead,
-		       const struct ctl_table **pentry)
+static int sysctl_dir_next(struct ctl_dir *dir, struct sysctl_dirent *d,
+			   const char *name, int namelen)
 {
-	struct ctl_table_header *head = NULL;
-	const struct ctl_table *entry = NULL;
 	struct rb_node *node, *after = NULL;
+	struct ctl_table_header *head;
+	const struct ctl_table *entry;
 	struct ctl_node *ctl_node;
+	bool by_name = true;
+	int ret = 0;
 
 	spin_lock(&sysctl_lock);
+	if (d->head) {
+		if (d->node && !d->head->unregistering) {
+			after = rb_next(&d->node->node);
+			by_name = false;
+		}
+		sysctl_unpin(d->head);
+		d->head = NULL;
+	}
 	if (unlikely(dir->header.unregistering)) {
-		spin_unlock(&sysctl_lock);
-		return -ENOENT;
+		ret = -ENOENT;
+		goto out;
 	}
-	if (!name) {
+	if (!by_name) {
+		/* behind the entry @d held */
+	} else if (!name) {
 		after = rb_first(&dir->root);
 	} else for (node = dir->root.rb_node; node;) {
-		const struct ctl_table *table;
-		struct ctl_table_header *h;
-
 		ctl_node = rb_entry(node, struct ctl_node, node);
-		h = ctl_node->header;
-		table = &h->ctl_table[ctl_node - h->node];
-		if (namecmp(table->procname, strlen(table->procname),
+		head = ctl_node->header;
+		entry = &head->ctl_table[ctl_node - head->node];
+		if (namecmp(entry->procname, strlen(entry->procname),
 			    name, namelen) > 0) {
 			after = node;
 			node = node->rb_left;
@@ -454,15 +467,56 @@ static int entry_after(struct ctl_dir *dir, const char *name, int namelen,
 			node = node->rb_right;
 		}
 	}
-	ctl_node = first_usable_entry(after);
-	spin_unlock(&sysctl_lock);
-	if (ctl_node) {
-		head = ctl_node->header;
-		entry = &head->ctl_table[ctl_node - head->node];
+	ctl_node = first_live_entry(after);
+	if (!ctl_node)
+		goto out;
+	head = ctl_node->header;
+	entry = &head->ctl_table[ctl_node - head->node];
+
+	d->len = strlen(entry->procname);
+	if (d->len > NAME_MAX) {
+		ret = -ENAMETOOLONG;
+		goto out;
 	}
-	*phead = head;
-	*pentry = entry;
-	return 0;
+	memcpy(d->name, entry->procname, d->len);
+	d->mode = entry->mode;
+	d->skip = false;
+	d->node = ctl_node;
+	if (unlikely(S_ISLNK(entry->mode))) {
+		/* the entry it stands for in the caller's set, or nothing */
+		struct ctl_table_root *root = entry->data;
+		struct ctl_table_set *set = lookup_header_set(root);
+		struct ctl_dir *xdir = xlate_dir(set, head->parent);
+		struct ctl_table_header *xhead = NULL;
+		const struct ctl_table *xentry = NULL;
+
+		if (!IS_ERR(xdir))
+			xentry = find_entry(&xhead, xdir, d->name, d->len);
+		if (!xentry || xhead->unregistering) {
+			d->skip = true;
+		} else {
+			head = xhead;
+			entry = xentry;
+		}
+		d->node = NULL;
+	}
+	sysctl_pin(head);
+	d->head = head;
+	d->table = entry;
+	ret = 1;
+out:
+	spin_unlock(&sysctl_lock);
+	return ret;
+}
+
+static void sysctl_dir_done(struct sysctl_dirent *d)
+{
+	if (!d->head)
+		return;
+	spin_lock(&sysctl_lock);
+	sysctl_unpin(d->head);
+	spin_unlock(&sysctl_lock);
+	d->head = NULL;
 }
 
 /*
@@ -765,29 +819,29 @@ static void sysctl_cursor_set(struct file *file, const char *name, int len,
 	memcpy(cursor->name, name, len);
 }
 
-static bool proc_sys_fill_cache(struct file *file,
-				struct dir_context *ctx,
-				struct ctl_table_header *head,
-				const struct ctl_table *table)
+/*
+ * The inode number and type of the entry @d holds, instantiating its dentry
+ * if there is none. Needs the header pinned, proc_sys_make_inode() checks
+ * for itself that the table is still registered.
+ */
+static int proc_sys_fill_dirent(struct file *file, struct sysctl_dirent *d)
 {
 	struct dentry *child, *dir = file->f_path.dentry;
 	struct inode *inode;
 	struct qstr qname;
-	ino_t ino = 0;
-	unsigned type = DT_UNKNOWN;
 
-	qname.name = table->procname;
-	qname.len  = strlen(table->procname);
+	qname.name = d->name;
+	qname.len  = d->len;
 	qname.hash = full_name_hash(dir, qname.name, qname.len);
 
 	child = d_lookup(dir, &qname);
 	if (!child) {
 		child = d_alloc_parallel(dir, &qname);
 		if (IS_ERR(child))
-			return false;
+			return PTR_ERR(child);
 		if (d_in_lookup(child)) {
 			struct dentry *res;
-			inode = proc_sys_make_inode(dir->d_sb, head, table);
+			inode = proc_sys_make_inode(dir->d_sb, d->head, d->table);
 			res = d_splice_alias_ops(inode, child,
 						 &proc_sys_dentry_operations);
 			d_lookup_done(child);
@@ -795,79 +849,41 @@ static bool proc_sys_fill_cache(struct file *file,
 				dput(child);
 
 				if (IS_ERR(res))
-					return false;
+					return PTR_ERR(res);
 
 				child = res;
 			}
 		}
 	}
 	inode = d_inode(child);
-	ino  = inode->i_ino;
-	type = inode->i_mode >> 12;
+	d->ino = inode->i_ino;
+	d->type = inode->i_mode >> 12;
 	dput(child);
-	return dir_emit(ctx, qname.name, qname.len, ino, type);
-}
-
-static bool proc_sys_link_fill_cache(struct file *file,
-				    struct dir_context *ctx,
-				    struct ctl_table_header *head,
-				    const struct ctl_table *table)
-{
-	bool ret = true;
-
-	head = sysctl_head_grab(head);
-	if (IS_ERR(head))
-		return false;
-
-	/* It is not an error if we can not follow the link ignore it */
-	if (sysctl_follow_link(&head, &table))
-		goto out;
-
-	ret = proc_sys_fill_cache(file, ctx, head, table);
-out:
-	sysctl_head_finish(head);
-	return ret;
-}
-
-static int scan(struct ctl_table_header *head, const struct ctl_table *table,
-		unsigned long *pos, struct file *file,
-		struct dir_context *ctx)
-{
-	bool res;
-
-	if ((*pos)++ < ctx->pos)
-		return true;
-
-	if (unlikely(S_ISLNK(table->mode)))
-		res = proc_sys_link_fill_cache(file, ctx, head, table);
-	else
-		res = proc_sys_fill_cache(file, ctx, head, table);
-
-	if (res) {
-		ctx->pos = *pos;
-		sysctl_cursor_set(file, table->procname, strlen(table->procname),
-				  ctx->pos);
-	}
-
-	return res;
+	return 0;
 }
 
+/*
+ * Unregistering a table waits until nobody uses it anymore and a copy to
+ * userspace can take as long as userspace wants. So a listing doesn't take
+ * any table into use. It pins the memory of the entry it is at, finds the
+ * next one under sysctl_lock and emits with nothing held, like
+ * kernfs_fop_readdir() and proc_readdir_de() do. Between calls a listing
+ * resumes after the name of the last entry it emitted.
+ */
 static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 {
-	struct ctl_table_header *head = grab_header(file_inode(file));
+	struct ctl_table_header *head = PROC_I(file_inode(file))->sysctl;
 	struct sysctl_cursor *cursor = file->private_data;
-	struct ctl_table_header *h = NULL;
-	const struct ctl_table *entry;
+	struct sysctl_dirent d = {};
 	struct ctl_dir *ctl_dir;
-	unsigned long pos;
-
-	if (IS_ERR(head))
-		return PTR_ERR(head);
+	int err;
 
+	if (!head)
+		head = &sysctl_table_root.default_set.dir.header;
 	ctl_dir = container_of(head, struct ctl_dir, header);
 
 	if (!dir_emit_dots(file, ctx))
-		goto out;
+		return 0;
 
 	/*
 	 * Resume after the last entry the previous call emitted. Only a seek
@@ -875,21 +891,30 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
 	 * seeking to an arbitrary position always did.
 	 */
 	if (cursor && cursor->pos == ctx->pos) {
-		entry_after(ctl_dir, cursor->name, cursor->len, &h, &entry);
-		pos = ctx->pos;
+		err = sysctl_dir_next(ctl_dir, &d, cursor->name, cursor->len);
 	} else {
-		first_entry(ctl_dir, &h, &entry);
-		pos = 2;
+		loff_t pos = 2;
+
+		err = sysctl_dir_next(ctl_dir, &d, NULL, 0);
+		while (err > 0 && pos++ < ctx->pos)
+			err = sysctl_dir_next(ctl_dir, &d, d.name, d.len);
 	}
+	if (err < 0)
+		return err;
 
-	for (; h; next_entry(&h, &entry)) {
-		if (!scan(h, entry, &pos, file, ctx)) {
-			sysctl_head_finish(h);
-			break;
+	while (err > 0) {
+		if (!d.skip) {
+			err = proc_sys_fill_dirent(file, &d);
+			if (err)
+				break;
+			if (!dir_emit(ctx, d.name, d.len, d.ino, d.type))
+				break;
 		}
+		ctx->pos++;
+		sysctl_cursor_set(file, d.name, d.len, ctx->pos);
+		err = sysctl_dir_next(ctl_dir, &d, d.name, d.len);
 	}
-out:
-	sysctl_head_finish(head);
+	sysctl_dir_done(&d);
 	return 0;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 03/38] netfilter: xt_recent: don't remove the proc entries under a spinlock
  2026-11-02 14:21   ` [PATCH RFC 03/38] netfilter: xt_recent: don't remove the proc entries under a spinlock Christian Brauner
@ 2026-11-02 14:41     ` Florian Westphal
  0 siblings, 0 replies; 160+ messages in thread
From: Florian Westphal @ 2026-11-02 14:41 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-fsdevel, Linus Torvalds, Jann Horn, Josef Bacik,
	Chris Mason, Jeff Layton, NeilBrown, Eric Dumazet,
	Thomas Gleixner, Al Viro, Jonathan Corbet, Christoph Hellwig,
	Greg Kroah-Hartman, Joel Granados, Kees Cook, Jan Kara, Jan Kara,
	linux-kernel, linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi, linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers, Chuck Lever, stable

Christian Brauner <brauner@kernel.org> wrote:
> recent_proc_net_exit() removes the proc entries of all tables with
> recent_lock held. remove_proc_entry() waits until nobody calls into the
> entry anymore, and reading one of them takes recent_lock in
> recent_seq_start() while procfs has the entry pinned. So when a reader
> gets there while the network namespace goes away, the exit path sleeps
> with a spinlock held and bh disabled, waiting for a reader that spins
> on that very lock:

Yes, but note that this change is already part of a pending nf -> net
merge request.

^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 36/38] selftests/proc: check that faulting sysctl accesses don't hold up removal
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (34 preceding siblings ...)
  2026-11-02 14:21   ` [PATCH RFC 35/38] proc: sysctl: don't hold the tables while readdir copies to userspace Christian Brauner
@ 2026-11-02 14:43   ` Christian Brauner
  2026-11-02 14:43   ` [PATCH RFC 37/38] proc: don't pin a symlink's entry while readlink() copies it out Christian Brauner
  2026-11-02 14:43   ` [PATCH RFC 38/38] selftests/proc: check that a faulting readlink() doesn't hold up removal Christian Brauner
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:43 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Read, write and list /proc/sys/net/ipv6/conf/<dev>/ of a tun device
with a buffer whose first page is backed by userfaultfd and remove the
device while the task sleeps in its copy from or to userspace:

- unregistering the table doesn't wait for a faulting read(), write()
  or getdents64()

- the read still gets the value that was produced before the removal
  and the next read fails with -ENOENT

- the write is copied in after the removal and fails with -ENOENT

- getdents64() still lists what it looked up before the removal and
  fails with -ENOENT afterwards

The tables are unregistered under RTNL, so a regression stalls each
test for five seconds before it fails.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/proc/.gitignore            |   1 +
 tools/testing/selftests/proc/Makefile              |   1 +
 tools/testing/selftests/proc/config                |   3 +
 .../selftests/proc/proc-remove-slow-sysctl.c       | 213 +++++++++++++++++++++
 4 files changed, 218 insertions(+)

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 5b5e24e39cbd..7b8133492782 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -21,6 +21,7 @@
 /proc-uptime-001
 /proc-uptime-002
 /proc-pidns
+/proc-remove-slow-sysctl
 /read
 /self
 /setns-dcache
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index dae09f333455..bce6beca7274 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -32,5 +32,6 @@ TEST_GEN_PROGS += proc-multiple-procfs
 TEST_GEN_PROGS += proc-fsconfig-hidepid
 TEST_GEN_PROGS += proc-sysctl-readdir-churn
 TEST_GEN_PROGS += proc-pidns
+TEST_GEN_PROGS += proc-remove-slow-sysctl
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/config b/tools/testing/selftests/proc/config
index 68fbd2b35884..3edd3c437fe4 100644
--- a/tools/testing/selftests/proc/config
+++ b/tools/testing/selftests/proc/config
@@ -1 +1,4 @@
 CONFIG_PROC_FS=y
+CONFIG_IPV6=y
+CONFIG_TUN=y
+CONFIG_USERFAULTFD=y
diff --git a/tools/testing/selftests/proc/proc-remove-slow-sysctl.c b/tools/testing/selftests/proc/proc-remove-slow-sysctl.c
new file mode 100644
index 000000000000..e1b1fd19a07a
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-remove-slow-sysctl.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A read(), write() or getdents64() of /proc/sys that faults while copying
+ * from or to userspace must not hold up unregistering the sysctl table.
+ * /proc/sys/net/ipv6/conf/<dev>/ of a tun device is unregistered under RTNL
+ * when the device's file is closed.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define TUN_NAME	"procslow0"
+#define CONF_PATH	"/proc/sys/net/ipv6/conf/" TUN_NAME
+#define MTU_PATH	CONF_PATH "/mtu"
+#define BUF_PAGES	16
+#define TIMEOUT_MS	5000
+
+enum sysctl_op {
+	SYSCTL_READ,
+	SYSCTL_WRITE,
+	SYSCTL_GETDENTS,
+};
+
+struct worker {
+	enum sysctl_op op;
+	int fd;
+	char *buf;
+	size_t len;
+	ssize_t ret;
+	int err;
+};
+
+struct closer {
+	int tun;
+	int done;
+};
+
+static void *work_thread(void *arg)
+{
+	struct worker *w = arg;
+
+	switch (w->op) {
+	case SYSCTL_READ:
+		w->ret = read(w->fd, w->buf, w->len);
+		break;
+	case SYSCTL_WRITE:
+		w->ret = write(w->fd, w->buf, w->len);
+		break;
+	case SYSCTL_GETDENTS:
+		w->ret = syscall(__NR_getdents64, w->fd, w->buf, w->len);
+		break;
+	}
+	w->err = errno;
+	return NULL;
+}
+
+static void *close_thread(void *arg)
+{
+	struct closer *c = arg;
+	ssize_t ret;
+
+	close(c->tun);
+	ret = write(c->done, "x", 1);
+	return (void *)ret;
+}
+
+FIXTURE(proc_sys_slow) {
+	int tun;
+	int uffd;
+	char *buf;
+	long psz;
+};
+
+FIXTURE_SETUP(proc_sys_slow)
+{
+	struct uffdio_api api = { .api = UFFD_API };
+	struct uffdio_register reg = {};
+	struct ifreq ifr = {};
+
+	if (unshare(CLONE_NEWNET))
+		SKIP(return, "unshare(CLONE_NEWNET): %s", strerror(errno));
+
+	self->tun = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (self->tun < 0)
+		SKIP(return, "/dev/net/tun: %s", strerror(errno));
+	strncpy(ifr.ifr_name, TUN_NAME, IFNAMSIZ - 1);
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+	ASSERT_EQ(ioctl(self->tun, TUNSETIFF, &ifr), 0);
+	if (access(CONF_PATH, F_OK))
+		SKIP(return, CONF_PATH ": %s", strerror(errno));
+
+	self->uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (self->uffd < 0)
+		SKIP(return, "userfaultfd: %s", strerror(errno));
+	ASSERT_EQ(ioctl(self->uffd, UFFDIO_API, &api), 0);
+
+	/* Only the first page of the buffer goes through userfaultfd. */
+	self->psz = sysconf(_SC_PAGESIZE);
+	self->buf = mmap(NULL, BUF_PAGES * self->psz, PROT_READ | PROT_WRITE,
+			 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(self->buf, MAP_FAILED);
+	reg.range.start = (unsigned long)self->buf;
+	reg.range.len = self->psz;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ASSERT_EQ(ioctl(self->uffd, UFFDIO_REGISTER, &reg), 0);
+}
+
+FIXTURE_TEARDOWN(proc_sys_slow)
+{
+}
+
+/*
+ * Let @w sleep in its copy from or to the first page of the buffer, then
+ * close the device. Unregistering the table must not wait for @w.
+ */
+static void stall_and_remove(struct __test_metadata *_metadata,
+			     FIXTURE_DATA(proc_sys_slow) *self,
+			     struct worker *w)
+{
+	struct closer c = { .tun = self->tun };
+	struct uffdio_zeropage zero = {};
+	struct uffd_msg msg;
+	struct pollfd pfd;
+	pthread_t wt, ct;
+	int done[2];
+
+	w->buf = self->buf;
+	ASSERT_EQ(pthread_create(&wt, NULL, work_thread, w), 0);
+
+	pfd = (struct pollfd){ .fd = self->uffd, .events = POLLIN };
+	ASSERT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1);
+	ASSERT_EQ(read(self->uffd, &msg, sizeof(msg)), sizeof(msg));
+	ASSERT_EQ(msg.event, UFFD_EVENT_PAGEFAULT);
+
+	ASSERT_EQ(pipe2(done, O_CLOEXEC), 0);
+	c.done = done[1];
+	ASSERT_EQ(pthread_create(&ct, NULL, close_thread, &c), 0);
+	pfd = (struct pollfd){ .fd = done[0], .events = POLLIN };
+	EXPECT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1) {
+		TH_LOG("unregistering " CONF_PATH " waited for the faulting task");
+	}
+
+	zero.range.start = (unsigned long)self->buf;
+	zero.range.len = self->psz;
+	ASSERT_EQ(ioctl(self->uffd, UFFDIO_ZEROPAGE, &zero), 0);
+	ASSERT_EQ(pthread_join(wt, NULL), 0);
+	ASSERT_EQ(pthread_join(ct, NULL), 0);
+}
+
+TEST_F(proc_sys_slow, read)
+{
+	struct worker w = {
+		.op	= SYSCTL_READ,
+		.len	= BUF_PAGES * self->psz,
+	};
+
+	w.fd = open(MTU_PATH, O_RDONLY | O_CLOEXEC);
+	ASSERT_GE(w.fd, 0);
+	stall_and_remove(_metadata, self, &w);
+
+	/* The value was produced before the table went away, then nothing. */
+	EXPECT_GT(w.ret, 0);
+	EXPECT_EQ(read(w.fd, self->buf, self->psz), -1);
+	EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_F(proc_sys_slow, write)
+{
+	struct worker w = {
+		.op	= SYSCTL_WRITE,
+		.len	= 5,
+	};
+
+	w.fd = open(MTU_PATH, O_WRONLY | O_CLOEXEC);
+	ASSERT_GE(w.fd, 0);
+	stall_and_remove(_metadata, self, &w);
+
+	/* The table went away while the value was copied in. */
+	EXPECT_EQ(w.ret, -1);
+	EXPECT_EQ(w.err, ENOENT);
+}
+
+TEST_F(proc_sys_slow, getdents)
+{
+	struct worker w = {
+		.op	= SYSCTL_GETDENTS,
+		.len	= BUF_PAGES * self->psz,
+	};
+
+	w.fd = open(CONF_PATH, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+	ASSERT_GE(w.fd, 0);
+	stall_and_remove(_metadata, self, &w);
+
+	/* What was looked up before the removal still arrives, then nothing. */
+	EXPECT_GT(w.ret, 0);
+	EXPECT_EQ(syscall(__NR_getdents64, w.fd, self->buf, self->psz), -1);
+	EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 37/38] proc: don't pin a symlink's entry while readlink() copies it out
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (35 preceding siblings ...)
  2026-11-02 14:43   ` [PATCH RFC 36/38] selftests/proc: check that faulting sysctl accesses don't hold up removal Christian Brauner
@ 2026-11-02 14:43   ` Christian Brauner
  2026-11-02 14:43   ` [PATCH RFC 38/38] selftests/proc: check that a faulting readlink() doesn't hold up removal Christian Brauner
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:43 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever, stable

proc_get_link() pins the entry with use_pde() and the delayed call
unpins it. vfs_readlink() copies the link to userspace in between. So a
page fault in that copy holds up remove_proc_entry() just like the
copies of reads and writes do, and the callers hold locks while they
wait. /proc/asound/<id> is removed with snd_card_mutex and info_mutex
held when a card changes its id.

The pin doesn't keep anything alive. proc_symlink() copies the target
and pde_free() frees it with the entry, and the inode holds a reference
to the entry. What the pin does is keep a removed symlink from being
followed. So check ->in_use instead and drop the delayed call. That
also takes an atomic pair off every traversal of /proc/net and
/proc/mounts.

Fixes: 7e0e953bb0cf ("procfs: fix race between symlink removals and traversals")
Cc: stable@vger.kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/inode.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 9933f03ab9fb..2d65f3c8ec03 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -629,19 +629,19 @@ static const struct file_operations proc_iter_file_ops_compat = {
 };
 #endif
 
-static void proc_put_link(void *p)
-{
-	unuse_pde(p);
-}
-
 static const char *proc_get_link(struct dentry *dentry,
 				 struct inode *inode,
 				 struct delayed_call *done)
 {
 	struct proc_dir_entry *pde = PDE(inode);
-	if (!use_pde(pde))
+
+	/*
+	 * No pin. The link lives with the entry, which the inode holds, and a
+	 * reader could fault while vfs_readlink() copies it out. Just don't
+	 * follow a removed one.
+	 */
+	if (unlikely(atomic_read(&pde->in_use) < 0))
 		return ERR_PTR(-EINVAL);
-	set_delayed_call(done, proc_put_link, pde);
 	return pde->data;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 38/38] selftests/proc: check that a faulting readlink() doesn't hold up removal
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
                     ` (36 preceding siblings ...)
  2026-11-02 14:43   ` [PATCH RFC 37/38] proc: don't pin a symlink's entry while readlink() copies it out Christian Brauner
@ 2026-11-02 14:43   ` Christian Brauner
  37 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:43 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Add the symlink case:

- readlink() /proc/asound/<id> of a dummy card into a buffer backed by
  userfaultfd

- change the card's id through sysfs while the reader faults, which
  removes the symlink with snd_card_mutex and info_mutex held

- expect that to finish without waiting for the reader

Skipped without snd-dummy.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/proc/.gitignore            |   2 +
 tools/testing/selftests/proc/Makefile              |   2 +
 tools/testing/selftests/proc/config                |   3 +
 .../selftests/proc/proc-remove-slow-readlink.c     | 191 +++++++++++++++++++++
 4 files changed, 198 insertions(+)

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 7b8133492782..e2566cc0ae07 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -22,6 +22,8 @@
 /proc-uptime-002
 /proc-pidns
 /proc-remove-slow-sysctl
+/proc-remove-slow-reader
+/proc-remove-slow-readlink
 /read
 /self
 /setns-dcache
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index bce6beca7274..6a9bdc999f54 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -33,5 +33,7 @@ TEST_GEN_PROGS += proc-fsconfig-hidepid
 TEST_GEN_PROGS += proc-sysctl-readdir-churn
 TEST_GEN_PROGS += proc-pidns
 TEST_GEN_PROGS += proc-remove-slow-sysctl
+TEST_GEN_PROGS += proc-remove-slow-reader
+TEST_GEN_PROGS += proc-remove-slow-readlink
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/config b/tools/testing/selftests/proc/config
index 3edd3c437fe4..7b0e1d9be1a2 100644
--- a/tools/testing/selftests/proc/config
+++ b/tools/testing/selftests/proc/config
@@ -2,3 +2,6 @@ CONFIG_PROC_FS=y
 CONFIG_IPV6=y
 CONFIG_TUN=y
 CONFIG_USERFAULTFD=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DUMMY=m
diff --git a/tools/testing/selftests/proc/proc-remove-slow-readlink.c b/tools/testing/selftests/proc/proc-remove-slow-readlink.c
new file mode 100644
index 000000000000..3290225daecd
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-remove-slow-readlink.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A readlink() that faults while copying out the link must not hold up
+ * removal of the /proc symlink. Changing a sound card's id removes
+ * /proc/asound/<id> with snd_card_mutex and info_mutex held.
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define CARD_ID		"Dummy"
+#define NEW_ID		"procslow"
+#define LINK_PATH	"/proc/asound/" CARD_ID
+#define TIMEOUT_MS	5000
+
+struct reader {
+	char *buf;
+	size_t len;
+	ssize_t ret;
+};
+
+struct renamer {
+	const char *id_path;
+	const char *id;
+	int done;
+	int ret;
+};
+
+static void *readlink_thread(void *arg)
+{
+	struct reader *r = arg;
+
+	r->ret = readlink(LINK_PATH, r->buf, r->len);
+	return NULL;
+}
+
+/* a new id removes the card's old /proc/asound/<id> link */
+static int set_id(const char *id_path, const char *id)
+{
+	int fd, ret;
+
+	fd = open(id_path, O_WRONLY | O_CLOEXEC);
+	if (fd < 0)
+		return -errno;
+	ret = write(fd, id, strlen(id)) < 0 ? -errno : 0;
+	close(fd);
+	return ret;
+}
+
+static void *rename_thread(void *arg)
+{
+	struct renamer *r = arg;
+	ssize_t ret;
+
+	r->ret = set_id(r->id_path, r->id);
+	ret = write(r->done, "x", 1);
+	return (void *)ret;
+}
+
+/* the sysfs id file of the card with @id */
+static int find_card(const char *id, char *id_path, size_t size)
+{
+	struct dirent *d;
+	int ret = -1;
+	DIR *dir;
+
+	dir = opendir("/sys/class/sound");
+	if (!dir)
+		return -1;
+	while (ret && (d = readdir(dir))) {
+		char cur[32];
+		FILE *f;
+
+		if (strncmp(d->d_name, "card", 4))
+			continue;
+		snprintf(id_path, size, "/sys/class/sound/%s/id", d->d_name);
+		f = fopen(id_path, "r");
+		if (!f)
+			continue;
+		if (fgets(cur, sizeof(cur), f)) {
+			cur[strcspn(cur, "\n")] = '\0';
+			if (!strcmp(cur, id))
+				ret = 0;
+		}
+		fclose(f);
+	}
+	closedir(dir);
+	return ret;
+}
+
+FIXTURE(dummy) {
+	char id_path[PATH_MAX];
+	bool renamed;
+};
+
+FIXTURE_SETUP(dummy)
+{
+	self->renamed = false;
+	if (access(LINK_PATH, F_OK) && system("modprobe -q snd-dummy"))
+		SKIP(return, "no snd-dummy");
+	if (access(LINK_PATH, F_OK))
+		SKIP(return, LINK_PATH ": %s", strerror(errno));
+	if (find_card(CARD_ID, self->id_path, sizeof(self->id_path)))
+		SKIP(return, "no sysfs id file for " CARD_ID);
+	if (access(self->id_path, W_OK))
+		SKIP(return, "%s: %s", self->id_path, strerror(errno));
+}
+
+FIXTURE_TEARDOWN(dummy)
+{
+	if (self->renamed && set_id(self->id_path, CARD_ID))
+		TH_LOG("couldn't name the card " CARD_ID " again");
+}
+
+TEST_F(dummy, remove_while_readlink_faults)
+{
+	struct uffdio_api api = { .api = UFFD_API };
+	struct uffdio_register reg = {};
+	struct uffdio_zeropage zero = {};
+	long psz = sysconf(_SC_PAGESIZE);
+	struct renamer rn = {};
+	struct reader r = {};
+	struct uffd_msg msg;
+	struct pollfd pfd;
+	pthread_t rt, nt;
+	int uffd, done[2];
+	char *buf;
+
+	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (uffd < 0)
+		SKIP(return, "userfaultfd: %s", strerror(errno));
+	ASSERT_EQ(ioctl(uffd, UFFDIO_API, &api), 0);
+
+	buf = mmap(NULL, psz, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(buf, MAP_FAILED);
+	reg.range.start = (unsigned long)buf;
+	reg.range.len = psz;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_REGISTER, &reg), 0);
+
+	r.buf = buf;
+	r.len = psz;
+	ASSERT_EQ(pthread_create(&rt, NULL, readlink_thread, &r), 0);
+
+	/* The reader now sleeps in the copy of the link to userspace. */
+	pfd = (struct pollfd){ .fd = uffd, .events = POLLIN };
+	ASSERT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1);
+	ASSERT_EQ(read(uffd, &msg, sizeof(msg)), sizeof(msg));
+	ASSERT_EQ(msg.event, UFFD_EVENT_PAGEFAULT);
+
+	/* Renaming the card removes the link that is being read. */
+	ASSERT_EQ(pipe2(done, O_CLOEXEC), 0);
+	rn.id_path = self->id_path;
+	rn.id = NEW_ID;
+	rn.done = done[1];
+	self->renamed = true;
+	ASSERT_EQ(pthread_create(&nt, NULL, rename_thread, &rn), 0);
+	pfd = (struct pollfd){ .fd = done[0], .events = POLLIN };
+	EXPECT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1) {
+		TH_LOG("removing " LINK_PATH " waited for the reader");
+	}
+
+	zero.range.start = (unsigned long)buf;
+	zero.range.len = psz;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_ZEROPAGE, &zero), 0);
+	ASSERT_EQ(pthread_join(rt, NULL), 0);
+	ASSERT_EQ(pthread_join(nt, NULL), 0);
+	EXPECT_EQ(rn.ret, 0);
+
+	/* The link was read before it went away. */
+	EXPECT_GT(r.ret, 0);
+	EXPECT_EQ(readlink(LINK_PATH, buf, psz), -1);
+	EXPECT_EQ(errno, ENOENT);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 00/16] proc: let procfs own the data path
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
@ 2026-11-02 14:48 ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 01/16] proc: drop the compat file operations Christian Brauner
                     ` (15 more replies)
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                   ` (4 subsequent siblings)
  6 siblings, 16 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

remove_proc_entry() waits until every call into the entry has returned.
For a seq_file entry that's all of seq_read_iter() including the copy
to userspace. For everything else it's ->proc_read(), ->proc_write()
and ->proc_ioctl(), which copy to and from userspace themselves. A
reader or writer can make a page fault in that copy take as long as it
wants, e.g. with userfaultfd or by punching holes into a memfd over and
over. And callers of remove_proc_entry() like to hold locks while they
wait. addrconf holds RTNL when it removes /proc/net/dev_snmp6/<dev> and
xt_hashlimit holds hashlimit_mutex which is shared by all network
namespaces.

An earlier AI generated version bounced the text through a second buffer
in seq_file so the copy to userspace could happen with the entry
unpinned. That second buffering is really nasty and complicates
everything unnecessarily. So after chatting with Jann a bit I decided to
do what kernfs does instead: the entry's code only ever runs with the
entry pinned and never touches userspace memory. This series is the
procfs side of that. The entries are converted in four series on top of
it, one per area of the tree, which don't depend on each other, and a
last one drops the __user methods.

seq_file reads: procfs owns the seq_file and only calls into the entry
with it pinned. An entry declares its seq_operations, or just a show
function, and the size of its private state in struct proc_ops. procfs
opens and frees the seq_file and reads and seeks through seq_file
directly. ->proc_open() and ->proc_release() only deal with whatever
the entry needs on top of that. proc_create_seq() and friends already
keep the seq_operations in the entry so they're switched over in one
go here. That covers most of /proc/net. The open coded single_open()
and seq_open() users that don't do anything else are converted with a
coccinelle script, also here. A nice side effect is that opening one of
these entries no longer allocates a pde_opener, they have no
->proc_release() of their own anymore.

Writes: procfs copies in what is written before it pins the entry and
hands the entry a NUL-terminated kernel copy through ->proc_write_buf().
The copy is capped at PAGE_SIZE - 1 bytes unless the entry asks for
more in ->proc_write_size, which is what proc_simple_write() always
did. Entries that take binary data in pieces, the PCI config space,
ALSA's data entries, cio_ignore and the rtas flash files, ask for
PROC_ENTRY_SHORT_WRITES and their writes come up short. Since procfs
copies in the whole write first, a write that is too large or whose
buffer can't be read fails before the entry's own checks now,
permission checks included.

Reads that don't go through seq_file: an entry fills a kernel buffer in
->proc_read_buf(), procfs copies that to userspace after unpinning the
entry and only moves the position if that copy worked, like
kernfs_file_read_iter(). The buffer is a page unless the entry asks for
more in ->proc_read_size, and like with kernfs a larger read comes up
short. Entries whose readers want all of a large file in one read,
/proc/vmcore, /proc/config.gz and ALSA's data entries, ask for
PROC_ENTRY_FULL_READS and procfs fills their reads in pieces. These
entries are read through ->read_iter() now, so sendfile() and splice()
work for them.

ioctls: the command number doesn't reliably say what an ioctl copies.
The PCI ioctls aren't encoded at all, i8k's encode the wrong sizes and
esas2r uses a range of plain numbers. So an entry lists its commands in
->proc_ioctl_args with what goes in and what comes back out, like DRM's
ioctl tables. procfs copies the argument in before it pins the entry,
calls ->proc_ioctl_buf() with that copy and copies it back out after
unpinning the entry. A command that isn't in the table fails with
-ENOTTY. 32-bit tasks go through the same table, so the compat hooks go
away.

The limits of reads and writes are remembered in the entry when it is
registered, the proc_ops go away with the entry and procfs shouldn't
have to pin it just to look up a number. An entry that declares a
seq_file and a read or seek method of its own, or one of the new
methods next to the old one it replaces, fails to register.

Entries that are never removed, kcore, kmsg, the kpage files and
/proc/profile, keep reading straight into userspace. procfs never pins
those, so nothing can wait for them. The last series enforces that.

What this doesn't cover is mmap(). procfs pins the entry around
->proc_mmap() only and a page fault into the mapping runs the entry's
vm_ops unpinned. kernfs wraps those. That's for another day.

On a KASAN and lockdep kernel the reader selftest fails without the
series and passes with it. The other proc selftests behave the same
with and without the series and so do the entries converted here that I
could get at in a VM.

This is on top of "proc: fix a few old bugs in /proc handlers".

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (16):
      proc: drop the compat file operations
      proc: add pde_pin() and pde_unpin()
      proc: make room for more entry flags
      proc: let procfs own the seq_file of seq_file entries
      proc: let procfs own the seq_file of proc_create_seq() and friends
      proc: let procfs own the seq_file of DEFINE_PROC_SHOW_ATTRIBUTE() files
      treewide: let procfs own the seq_file of simple proc entries
      selftests/proc: check that a faulting reader doesn't hold up removal
      seq_file: add seq_preallocate()
      proc: let procfs own the seq_file of /proc/stat
      proc: let procfs copy in writes before pinning the entry
      proc: copy in writes to the network namespace entries before pinning
      proc: let procfs copy out reads that don't go through seq_file
      fs/proc/vmcore: let procfs copy out reads
      proc: let procfs copy ioctl arguments in and out
      Documentation: seq_file: describe how a /proc file uses seq_file

 Documentation/filesystems/seq_file.rst             |  42 +-
 arch/alpha/kernel/srm_env.c                        |  10 +-
 arch/arm/mm/alignment.c                            |  10 +-
 arch/powerpc/kernel/rtas-proc.c                    |  50 +--
 arch/powerpc/platforms/pseries/lpar.c              |  20 +-
 arch/powerpc/platforms/pseries/lparcfg.c           |  10 +-
 arch/sh/mm/alignment.c                             |  10 +-
 arch/sparc/kernel/led.c                            |  10 +-
 arch/um/kernel/exitcode.c                          |  10 +-
 drivers/acpi/proc.c                                |  12 +-
 drivers/hwmon/dell-smm-hwmon.c                     |  10 +-
 drivers/input/input.c                              |  24 +-
 drivers/macintosh/via-pmu.c                        |  10 +-
 drivers/net/wireless/intel/ipw2x00/libipw_module.c |  10 +-
 drivers/platform/x86/lenovo/thinkpad_acpi.c        |  10 +-
 drivers/platform/x86/toshiba_acpi.c                |  40 +-
 drivers/pnp/pnpbios/proc.c                         |  10 +-
 drivers/s390/block/dasd_proc.c                     |  10 +-
 drivers/s390/cio/blacklist.c                       |  13 +-
 drivers/scsi/scsi_devinfo.c                        |  10 +-
 drivers/scsi/scsi_proc.c                           |  21 +-
 drivers/usb/gadget/function/rndis.c                |  10 +-
 drivers/video/fbdev/via/viafbdev.c                 |  70 +---
 fs/jfs/jfs_debug.c                                 |  10 +-
 fs/ntfs3/super.c                                   |  20 +-
 fs/proc/cpuinfo.c                                  |  10 +-
 fs/proc/generic.c                                  | 126 +++---
 fs/proc/inode.c                                    | 457 ++++++++++++++++-----
 fs/proc/internal.h                                 |  54 ++-
 fs/proc/proc_net.c                                 |  44 +-
 fs/proc/stat.c                                     |   6 +-
 fs/proc/vmcore.c                                   |  14 +-
 fs/seq_file.c                                      |  36 +-
 fs/smb/client/cifs_debug.c                         |  79 +---
 fs/smb/client/dfs_cache.c                          |  10 +-
 include/linux/proc_fs.h                            |  91 +++-
 include/linux/seq_file.h                           |  11 +-
 kernel/irq/proc.c                                  |  30 +-
 kernel/latencytop.c                                |  10 +-
 lib/dynamic_debug.c                                |   6 +-
 mm/slab_common.c                                   |  10 +-
 net/core/pktgen.c                                  |  30 +-
 net/sunrpc/stats.c                                 |  10 +-
 .../selftests/proc/proc-remove-slow-reader.c       | 133 ++++++
 44 files changed, 837 insertions(+), 792 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261101-work-procfs-core-8fa4a4f412cf
prerequisite-change-id: 20261101-work-procfs-bugfixes-054d00028491:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 01/16] proc: drop the compat file operations
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 02/16] proc: add pde_pin() and pde_unpin() Christian Brauner
                     ` (14 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

procfs has both sets of file operations twice, with and without
->compat_ioctl(), and picks the one with it for entries that have
->proc_compat_ioctl(). That's from commit 778f3dd5a13c ("Fix procfs
compat_ioctl regression") when a file without ->compat_ioctl() went
through the translation tables in fs/compat_ioctl.c and one with it
didn't. Those tables are gone since commit 77b9040195de ("compat_ioctl:
simplify the implementation"). compat_sys_ioctl() answers -ENOTTY for a
file without ->compat_ioctl() now, which is what
proc_reg_compat_ioctl() answers for an entry without
->proc_compat_ioctl().

So always set ->compat_ioctl() and drop the second set of file
operations and the flag that picked it.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  4 ----
 fs/proc/inode.c         | 45 +++------------------------------------------
 fs/proc/internal.h      |  9 ---------
 include/linux/proc_fs.h |  1 -
 4 files changed, 3 insertions(+), 56 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 23fc026d7bbb..761f9958558f 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -445,10 +445,6 @@ static void pde_set_flags(struct proc_dir_entry *pde)
 		pde->flags |= PROC_ENTRY_PERMANENT;
 	if (proc_ops->proc_read_iter)
 		pde->flags |= PROC_ENTRY_proc_read_iter;
-#ifdef CONFIG_COMPAT
-	if (proc_ops->proc_compat_ioctl)
-		pde->flags |= PROC_ENTRY_proc_compat_ioctl;
-#endif
 	if (proc_ops->proc_lseek)
 		pde->flags |= PROC_ENTRY_proc_lseek;
 }
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 2d65f3c8ec03..18f6490bfe77 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -410,6 +410,8 @@ static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned
 	}
 	return rv;
 }
+#else
+#define proc_reg_compat_ioctl NULL
 #endif
 
 static int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_area_struct *vma)
@@ -569,6 +571,7 @@ static const struct file_operations proc_reg_file_ops = {
 	.write		= proc_reg_write,
 	.poll		= proc_reg_poll,
 	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
+	.compat_ioctl	= proc_reg_compat_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
 #ifndef CONFIG_MMU
@@ -585,39 +588,6 @@ static const struct file_operations proc_iter_file_ops = {
 	.splice_read	= copy_splice_read,
 	.poll		= proc_reg_poll,
 	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
-	.mmap		= proc_reg_mmap,
-	.get_unmapped_area = proc_reg_get_unmapped_area,
-#ifndef CONFIG_MMU
-	.mmap_capabilities = proc_reg_mmap_capabilities,
-#endif
-	.open		= proc_reg_open,
-	.release	= proc_reg_release,
-};
-
-#ifdef CONFIG_COMPAT
-static const struct file_operations proc_reg_file_ops_compat = {
-	.llseek		= proc_reg_llseek,
-	.read		= proc_reg_read,
-	.write		= proc_reg_write,
-	.poll		= proc_reg_poll,
-	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
-	.compat_ioctl	= proc_reg_compat_ioctl,
-	.mmap		= proc_reg_mmap,
-	.get_unmapped_area = proc_reg_get_unmapped_area,
-#ifndef CONFIG_MMU
-	.mmap_capabilities = proc_reg_mmap_capabilities,
-#endif
-	.open		= proc_reg_open,
-	.release	= proc_reg_release,
-};
-
-static const struct file_operations proc_iter_file_ops_compat = {
-	.llseek		= proc_reg_llseek,
-	.read_iter	= proc_reg_read_iter,
-	.splice_read	= copy_splice_read,
-	.write		= proc_reg_write,
-	.poll		= proc_reg_poll,
-	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
 	.compat_ioctl	= proc_reg_compat_ioctl,
 	.mmap		= proc_reg_mmap,
 	.get_unmapped_area = proc_reg_get_unmapped_area,
@@ -627,7 +597,6 @@ static const struct file_operations proc_iter_file_ops_compat = {
 	.open		= proc_reg_open,
 	.release	= proc_reg_release,
 };
-#endif
 
 static const char *proc_get_link(struct dentry *dentry,
 				 struct inode *inode,
@@ -683,14 +652,6 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 			inode->i_fop = &proc_iter_file_ops;
 		else
 			inode->i_fop = &proc_reg_file_ops;
-#ifdef CONFIG_COMPAT
-		if (pde_has_proc_compat_ioctl(de)) {
-			if (pde_has_proc_read_iter(de))
-				inode->i_fop = &proc_iter_file_ops_compat;
-			else
-				inode->i_fop = &proc_reg_file_ops_compat;
-		}
-#endif
 	} else if (S_ISDIR(inode->i_mode)) {
 		inode->i_op = de->proc_iops;
 		inode->i_fop = de->proc_dir_ops;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 04f7ce2b24ed..268a0523be12 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -92,15 +92,6 @@ static inline bool pde_has_proc_read_iter(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_proc_read_iter;
 }
 
-static inline bool pde_has_proc_compat_ioctl(const struct proc_dir_entry *pde)
-{
-#ifdef CONFIG_COMPAT
-	return pde->flags & PROC_ENTRY_proc_compat_ioctl;
-#else
-	return false;
-#endif
-}
-
 static inline bool pde_has_proc_lseek(const struct proc_dir_entry *pde)
 {
 	return pde->flags & PROC_ENTRY_proc_lseek;
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 47d7deaeed8f..6ad5150020bf 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -26,7 +26,6 @@ enum {
 #endif
 
 	PROC_ENTRY_proc_read_iter	= 1U << 1,
-	PROC_ENTRY_proc_compat_ioctl	= 1U << 2,
 	PROC_ENTRY_proc_lseek		= 1U << 3,
 
 	PROC_ENTRY_FORCE_LOOKUP		= 1U << 7,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 02/16] proc: add pde_pin() and pde_unpin()
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 01/16] proc: drop the compat file operations Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 03/16] proc: make room for more entry flags Christian Brauner
                     ` (13 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Every call into an entry that can go away is wrapped in use_pde() and
unuse_pde(), and a permanent entry is called straight away. The read,
write, poll, ioctl, mmap and lseek methods all spell that out as the
same if-else. The next patches add a few more callers. So put it into
pde_pin() and pde_unpin() and use them everywhere.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/inode.c | 116 +++++++++++++++++++++++++++-----------------------------
 1 file changed, 55 insertions(+), 61 deletions(-)

diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 18f6490bfe77..66a3688757dd 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -206,6 +206,18 @@ static void unuse_pde(struct proc_dir_entry *pde)
 		complete(pde->pde_unload_completion);
 }
 
+/* Pin an entry that can go away around a call into it, see use_pde(). */
+static bool pde_pin(struct proc_dir_entry *pde)
+{
+	return pde_is_permanent(pde) || use_pde(pde);
+}
+
+static void pde_unpin(struct proc_dir_entry *pde)
+{
+	if (!pde_is_permanent(pde))
+		unuse_pde(pde);
+}
+
 /*
  * At most 2 contexts can enter this function: the one doing the last
  * close on the descriptor and whoever is deleting PDE itself.
@@ -275,14 +287,12 @@ void proc_entry_rundown(struct proc_dir_entry *de)
 static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	loff_t rv = -EINVAL;
+	loff_t rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde->proc_ops->proc_lseek(file, offset, whence);
-	} else if (use_pde(pde)) {
-		rv = pde->proc_ops->proc_lseek(file, offset, whence);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return -EINVAL;
+	rv = pde->proc_ops->proc_lseek(file, offset, whence);
+	pde_unpin(pde);
 	return rv;
 }
 
@@ -291,13 +301,10 @@ static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 	struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp));
 	ssize_t ret;
 
-	if (pde_is_permanent(pde))
-		return pde->proc_ops->proc_read_iter(iocb, iter);
-
-	if (!use_pde(pde))
+	if (!pde_pin(pde))
 		return -EIO;
 	ret = pde->proc_ops->proc_read_iter(iocb, iter);
-	unuse_pde(pde);
+	pde_unpin(pde);
 	return ret;
 }
 
@@ -312,14 +319,12 @@ static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __us
 static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	ssize_t rv = -EIO;
+	ssize_t rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde_read(pde, file, buf, count, ppos);
-	} else if (use_pde(pde)) {
-		rv = pde_read(pde, file, buf, count, ppos);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return -EIO;
+	rv = pde_read(pde, file, buf, count, ppos);
+	pde_unpin(pde);
 	return rv;
 }
 
@@ -334,14 +339,12 @@ static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const ch
 static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	ssize_t rv = -EIO;
+	ssize_t rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde_write(pde, file, buf, count, ppos);
-	} else if (use_pde(pde)) {
-		rv = pde_write(pde, file, buf, count, ppos);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return -EIO;
+	rv = pde_write(pde, file, buf, count, ppos);
+	pde_unpin(pde);
 	return rv;
 }
 
@@ -356,14 +359,12 @@ static __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct p
 static __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	__poll_t rv = DEFAULT_POLLMASK;
+	__poll_t rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde_poll(pde, file, pts);
-	} else if (use_pde(pde)) {
-		rv = pde_poll(pde, file, pts);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return DEFAULT_POLLMASK;
+	rv = pde_poll(pde, file, pts);
+	pde_unpin(pde);
 	return rv;
 }
 
@@ -378,14 +379,12 @@ static long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned in
 static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	long rv = -ENOTTY;
+	long rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde_ioctl(pde, file, cmd, arg);
-	} else if (use_pde(pde)) {
-		rv = pde_ioctl(pde, file, cmd, arg);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return -ENOTTY;
+	rv = pde_ioctl(pde, file, cmd, arg);
+	pde_unpin(pde);
 	return rv;
 }
 
@@ -401,13 +400,12 @@ static long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsi
 static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	long rv = -ENOTTY;
-	if (pde_is_permanent(pde)) {
-		return pde_compat_ioctl(pde, file, cmd, arg);
-	} else if (use_pde(pde)) {
-		rv = pde_compat_ioctl(pde, file, cmd, arg);
-		unuse_pde(pde);
-	}
+	long rv;
+
+	if (!pde_pin(pde))
+		return -ENOTTY;
+	rv = pde_compat_ioctl(pde, file, cmd, arg);
+	pde_unpin(pde);
 	return rv;
 }
 #else
@@ -425,14 +423,12 @@ static int pde_mmap(struct proc_dir_entry *pde, struct file *file, struct vm_are
 static int proc_reg_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	int rv = -EIO;
+	int rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde_mmap(pde, file, vma);
-	} else if (use_pde(pde)) {
-		rv = pde_mmap(pde, file, vma);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return -EIO;
+	rv = pde_mmap(pde, file, vma);
+	pde_unpin(pde);
 	return rv;
 }
 
@@ -457,14 +453,12 @@ proc_reg_get_unmapped_area(struct file *file, unsigned long orig_addr,
 			   unsigned long flags)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	unsigned long rv = -EIO;
+	unsigned long rv;
 
-	if (pde_is_permanent(pde)) {
-		return pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
-	} else if (use_pde(pde)) {
-		rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
-		unuse_pde(pde);
-	}
+	if (!pde_pin(pde))
+		return -EIO;
+	rv = pde_get_unmapped_area(pde, file, orig_addr, len, pgoff, flags);
+	pde_unpin(pde);
 	return rv;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 03/16] proc: make room for more entry flags
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 01/16] proc: drop the compat file operations Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 02/16] proc: add pde_pin() and pde_unpin() Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 04/16] proc: let procfs own the seq_file of seq_file entries Christian Brauner
                     ` (12 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

->flags of struct proc_dir_entry is a u8 and the next patches need more
flags than it has room for, so make it a u16. Bit 6 stays free,
vfs-7.5.mount takes it for PROC_ENTRY_CANT_MOUNT.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/internal.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 268a0523be12..41e00c978ddd 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -61,7 +61,7 @@ struct proc_dir_entry {
 	struct rb_node subdir_node;
 	char *name;
 	umode_t mode;
-	u8 flags;
+	u16 flags;
 	u8 namelen;
 	char inline_name[];
 } __randomize_layout;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 04/16] proc: let procfs own the seq_file of seq_file entries
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 03/16] proc: make room for more entry flags Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 05/16] proc: let procfs own the seq_file of proc_create_seq() and friends Christian Brauner
                     ` (11 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

remove_proc_entry() waits until every call into the entry has returned.
For a seq_file entry that's all of seq_read_iter(), including the copy
to userspace. A page fault in that copy can be made to take as long as
the reader wants, e.g. with userfaultfd or by punching holes into a
memfd over and over. And callers of remove_proc_entry() like to hold
locks while they wait. xt_hashlimit holds hashlimit_mutex, which all
network namespaces share, and addrconf holds RTNL.

But the entry's code only runs between ->start() and ->stop() and the
copies to userspace happen outside of that. So let procfs own the
seq_file the way kernfs does. An entry declares its seq_operations, or
just a show function, and the size of its private state in struct
proc_ops. procfs then opens the seq_file with seq_operations of its own
that pin the entry around every call into the entry, and reads and
seeks through seq_file directly. The copies to userspace happen with
the entry unpinned.

That changes who owns the seq_file. remove_proc_entry() can now finish
while a reader is still copying out of the buffer, so the seq_file
can't go away with the entry. procfs frees it once the file is
released. ->proc_open() finds the seq_file in ->private_data and sets
up whatever the entry needs and ->proc_release() releases just that. It
still runs when the entry is removed, or when the file is closed before
that.

Since procfs reads these entries through seq_read_iter() itself they
all have ->splice_read() now, so sendfile(), splice(), aio, the RWF
flags and kernel_read() work on them, and readv() is one read instead
of one per segment. Note that a removed entry still hands out what it
had already formatted and then answers -EIO to reads, and to seeks that
have to walk the records. Seeking it failed with -EINVAL right away
before.

The seq_operations of procfs serve both kinds of entries. For an entry
that only has a show function they do what single_open() does, like
kernfs_seq_start(). seq_file's complaint about a ->next() that doesn't
move the position would name procfs' wrapper, so the wrapper checks
that itself and names the entry's. An entry that declares a seq_file
and also a read or seek method of its own, or one that releases the
seq_file with seq_release() itself, doesn't make sense, procfs reads,
seeks and releases the seq_file itself, so registering it fails.

No users yet.

Suggested-by: Jann Horn <jannh@google.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  36 +++++++++--
 fs/proc/inode.c         | 167 ++++++++++++++++++++++++++++++++++++++++++++----
 fs/proc/internal.h      |  16 +++++
 include/linux/proc_fs.h |  22 ++++++-
 4 files changed, 219 insertions(+), 22 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 761f9958558f..eff5c38e2219 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -434,19 +434,41 @@ static const struct inode_operations proc_dir_inode_operations = {
 	.setattr	= proc_setattr,
 };
 
-static void pde_set_flags(struct proc_dir_entry *pde)
+static int pde_set_flags(struct proc_dir_entry *pde)
 {
 	const struct proc_ops *proc_ops = pde->proc_ops;
 
 	if (!proc_ops)
-		return;
+		return 0;
 
 	if (proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
 		pde->flags |= PROC_ENTRY_PERMANENT;
-	if (proc_ops->proc_read_iter)
-		pde->flags |= PROC_ENTRY_proc_read_iter;
-	if (proc_ops->proc_lseek)
+	if (proc_ops->proc_seq_ops) {
+		pde->seq_ops = proc_ops->proc_seq_ops;
+		pde->state_size = proc_ops->proc_state_size;
+		pde->flags |= PROC_ENTRY_proc_seq_ops;
+	} else if (proc_ops->proc_show) {
+		pde->single_show = proc_ops->proc_show;
+		pde->flags |= PROC_ENTRY_proc_show;
+	}
+	if (pde_is_seq_file(pde)) {
+		/* procfs reads and seeks through the seq_file itself */
+		if (WARN_ON_ONCE(proc_ops->proc_read || proc_ops->proc_read_iter ||
+				 proc_ops->proc_lseek))
+			return -EINVAL;
+		/* and frees it, see pde_seq_release() */
+		if (WARN_ON_ONCE(proc_ops->proc_release == seq_release ||
+				 proc_ops->proc_release == seq_release_private ||
+				 proc_ops->proc_release == single_release))
+			return -EINVAL;
 		pde->flags |= PROC_ENTRY_proc_lseek;
+	} else {
+		if (proc_ops->proc_read_iter)
+			pde->flags |= PROC_ENTRY_proc_read_iter;
+		if (proc_ops->proc_lseek)
+			pde->flags |= PROC_ENTRY_proc_lseek;
+	}
+	return 0;
 }
 
 /* returns the registered entry, or frees dp and returns NULL on failure */
@@ -456,8 +478,8 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
 	if (proc_alloc_inum(&dp->low_ino))
 		goto out_free_entry;
 
-	if (!S_ISDIR(dp->mode))
-		pde_set_flags(dp);
+	if (!S_ISDIR(dp->mode) && pde_set_flags(dp))
+		goto out_free_inum;
 
 	write_lock(&proc_subdir_lock);
 	dp->parent = dir;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 66a3688757dd..8425465aae5d 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -218,6 +218,110 @@ static void pde_unpin(struct proc_dir_entry *pde)
 		unuse_pde(pde);
 }
 
+/*
+ * procfs owns the seq_file of a seq_file entry and only calls into the entry
+ * with it pinned. A read copies to userspace without the pin, so a reader
+ * that faults on its buffer doesn't hold up remove_proc_entry().
+ *
+ * An entry that only has a show function is iterated like single_open() does
+ * it, see kernfs_seq_start().
+ *
+ * Like ERR_PTR(-ENODEV) in kernfs_seq_start(), ERR_PTR(-EIO) means the entry
+ * isn't pinned. If the entry returns it itself, it is unpinned right away.
+ */
+static struct proc_dir_entry *seq_pde(struct seq_file *m)
+{
+	return PDE(file_inode(m->file));
+}
+
+static void pde_seq_stop_pinned(struct seq_file *m, void *v)
+{
+	struct proc_dir_entry *pde = seq_pde(m);
+
+	if (pde_has_proc_seq_ops(pde))
+		pde->seq_ops->stop(m, v);
+	pde_unpin(pde);
+}
+
+static void *pde_seq_start(struct seq_file *m, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq_pde(m);
+	void *v;
+
+	if (!pde_pin(pde))
+		return ERR_PTR(-EIO);
+	if (!pde_has_proc_seq_ops(pde))
+		return single_start(m, pos);
+	v = pde->seq_ops->start(m, pos);
+	if (v == ERR_PTR(-EIO))
+		pde_seq_stop_pinned(m, v);
+	return v;
+}
+
+static void *pde_seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	struct proc_dir_entry *pde = seq_pde(m);
+	loff_t old = *pos;
+
+	if (!pde_has_proc_seq_ops(pde)) {
+		++*pos;
+		return NULL;
+	}
+	v = pde->seq_ops->next(m, v, pos);
+	if (v == ERR_PTR(-EIO))
+		pde_seq_stop_pinned(m, v);
+	/* seq_file would blame these seq_operations, blame the entry's */
+	if (*pos == old) {
+		pr_info_ratelimited("buggy .next function %ps did not update position index\n",
+				    pde->seq_ops->next);
+		++*pos;
+	}
+	return v;
+}
+
+static void pde_seq_stop(struct seq_file *m, void *v)
+{
+	if (v != ERR_PTR(-EIO))
+		pde_seq_stop_pinned(m, v);
+}
+
+static int pde_seq_show(struct seq_file *m, void *v)
+{
+	struct proc_dir_entry *pde = seq_pde(m);
+
+	if (pde_has_proc_seq_ops(pde))
+		return pde->seq_ops->show(m, v);
+	return pde->single_show(m, v);
+}
+
+static const struct seq_operations pde_seq_ops = {
+	.start	= pde_seq_start,
+	.next	= pde_seq_next,
+	.stop	= pde_seq_stop,
+	.show	= pde_seq_show,
+};
+
+static int pde_seq_open(struct proc_dir_entry *pde, struct file *file)
+{
+	int err;
+
+	if (pde->state_size)
+		return seq_open_private(file, &pde_seq_ops, pde->state_size);
+	err = seq_open(file, &pde_seq_ops);
+	if (!err && pde_has_proc_show(pde))
+		((struct seq_file *)file->private_data)->private = pde->data;
+	return err;
+}
+
+static void pde_seq_release(struct proc_dir_entry *pde, struct inode *inode,
+			    struct file *file)
+{
+	if (pde->state_size)
+		seq_release_private(inode, file);
+	else
+		seq_release(inode, file);
+}
+
 /*
  * At most 2 contexts can enter this function: the one doing the last
  * close on the descriptor and whoever is deleting PDE itself.
@@ -474,22 +578,35 @@ static unsigned int proc_reg_mmap_capabilities(struct file *file)
 }
 #endif
 
+static int pde_open(struct proc_dir_entry *pde, struct inode *inode,
+		    struct file *file)
+{
+	const auto open = pde->proc_ops->proc_open;
+	int rv;
+
+	if (!pde_is_seq_file(pde))
+		return open ? open(inode, file) : 0;
+
+	rv = pde_seq_open(pde, file);
+	if (rv || !open)
+		return rv;
+	rv = open(inode, file);
+	if (rv)
+		pde_seq_release(pde, inode, file);
+	return rv;
+}
+
 static int proc_reg_open(struct inode *inode, struct file *file)
 {
 	struct proc_dir_entry *pde = PDE(inode);
 	int rv = 0;
-	typeof_member(struct proc_ops, proc_open) open;
 	struct pde_opener *pdeo;
 
 	if (!pde_has_proc_lseek(pde))
 		file->f_mode &= ~FMODE_LSEEK;
 
-	if (pde_is_permanent(pde)) {
-		open = pde->proc_ops->proc_open;
-		if (open)
-			rv = open(inode, file);
-		return rv;
-	}
+	if (pde_is_permanent(pde))
+		return pde_open(pde, inode, file);
 
 	/*
 	 * Ensure that
@@ -514,9 +631,7 @@ static int proc_reg_open(struct inode *inode, struct file *file)
 		}
 	}
 
-	open = pde->proc_ops->proc_open;
-	if (open)
-		rv = open(inode, file);
+	rv = pde_open(pde, inode, file);
 
 	if (release) {
 		if (rv == 0) {
@@ -544,21 +659,43 @@ static int proc_reg_release(struct inode *inode, struct file *file)
 	if (pde_is_permanent(pde)) {
 		const auto release = pde->proc_ops->proc_release;
 		if (release)
-			return release(inode, file);
-		return 0;
+			release(inode, file);
+		goto out;
 	}
 
 	spin_lock(&pde->pde_unload_lock);
 	list_for_each_entry(pdeo, &pde->pde_openers, lh) {
 		if (pdeo->file == file) {
 			close_pdeo(pde, pdeo);
-			return 0;
+			goto out;
 		}
 	}
 	spin_unlock(&pde->pde_unload_lock);
+out:
+	/* ->proc_release() has run, here or in remove_proc_entry() */
+	if (pde_is_seq_file(pde))
+		pde_seq_release(pde, inode, file);
 	return 0;
 }
 
+/* For seq_file entries, see pde_seq_ops. */
+static const struct file_operations proc_seq_file_ops = {
+	.llseek		= seq_lseek,
+	.read_iter	= seq_read_iter,
+	.splice_read	= copy_splice_read,
+	.write		= proc_reg_write,
+	.poll		= proc_reg_poll,
+	.unlocked_ioctl	= proc_reg_unlocked_ioctl,
+	.compat_ioctl	= proc_reg_compat_ioctl,
+	.mmap		= proc_reg_mmap,
+	.get_unmapped_area = proc_reg_get_unmapped_area,
+#ifndef CONFIG_MMU
+	.mmap_capabilities = proc_reg_mmap_capabilities,
+#endif
+	.open		= proc_reg_open,
+	.release	= proc_reg_release,
+};
+
 static const struct file_operations proc_reg_file_ops = {
 	.llseek		= proc_reg_llseek,
 	.read		= proc_reg_read,
@@ -642,7 +779,9 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 
 	if (S_ISREG(inode->i_mode)) {
 		inode->i_op = de->proc_iops;
-		if (pde_has_proc_read_iter(de))
+		if (pde_is_seq_file(de))
+			inode->i_fop = &proc_seq_file_ops;
+		else if (pde_has_proc_read_iter(de))
 			inode->i_fop = &proc_iter_file_ops;
 		else
 			inode->i_fop = &proc_reg_file_ops;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 41e00c978ddd..d8093451524a 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -97,6 +97,22 @@ static inline bool pde_has_proc_lseek(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_proc_lseek;
 }
 
+static inline bool pde_has_proc_seq_ops(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_proc_seq_ops;
+}
+
+static inline bool pde_has_proc_show(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_proc_show;
+}
+
+/* procfs owns the seq_file, see pde_seq_ops */
+static inline bool pde_is_seq_file(const struct proc_dir_entry *pde)
+{
+	return pde_has_proc_seq_ops(pde) || pde_has_proc_show(pde);
+}
+
 extern struct kmem_cache *proc_dir_entry_cache;
 void pde_free(struct proc_dir_entry *pde);
 
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 6ad5150020bf..ea55ed85a2d6 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -27,6 +27,8 @@ enum {
 
 	PROC_ENTRY_proc_read_iter	= 1U << 1,
 	PROC_ENTRY_proc_lseek		= 1U << 3,
+	PROC_ENTRY_proc_seq_ops		= 1U << 4,
+	PROC_ENTRY_proc_show		= 1U << 5,
 
 	PROC_ENTRY_FORCE_LOOKUP		= 1U << 7,
 };
@@ -37,7 +39,10 @@ struct proc_ops {
 	ssize_t	(*proc_read)(struct file *, char __user *, size_t, loff_t *);
 	ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *);
 	ssize_t	(*proc_write)(struct file *, const char __user *, size_t, loff_t *);
-	/* mandatory unless nonseekable_open() or equivalent is used */
+	/*
+	 * mandatory unless nonseekable_open() or equivalent is used, procfs
+	 * seeks seq_file entries itself
+	 */
 	loff_t	(*proc_lseek)(struct file *, loff_t, int);
 	int	(*proc_release)(struct inode *, struct file *);
 	__poll_t (*proc_poll)(struct file *, struct poll_table_struct *);
@@ -47,6 +52,21 @@ struct proc_ops {
 #endif
 	int	(*proc_mmap)(struct file *, struct vm_area_struct *);
 	unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+	/*
+	 * For seq_file entries procfs opens the seq_file, reads and seeks
+	 * through it and frees it once the file is released. ->proc_open()
+	 * finds the seq_file in ->private_data and ->proc_release() only
+	 * releases what ->proc_open() set up. It can run while a reader still
+	 * copies out of the seq_file, so it must leave that alone. procfs
+	 * allocates ->proc_state_size bytes of ->private state for
+	 * ->proc_seq_ops and points ->private at the entry's data for
+	 * ->proc_show(). Nothing that runs with the entry pinned may wait for
+	 * something a copy to or from userspace can hold, like the seq_file's
+	 * lock.
+	 */
+	const struct seq_operations *proc_seq_ops;
+	int	(*proc_show)(struct seq_file *, void *);
+	unsigned int proc_state_size;
 } __randomize_layout;
 
 /* definitions for hide_pid field */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 05/16] proc: let procfs own the seq_file of proc_create_seq() and friends
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 04/16] proc: let procfs own the seq_file of seq_file entries Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 06/16] proc: let procfs own the seq_file of DEFINE_PROC_SHOW_ATTRIBUTE() files Christian Brauner
                     ` (10 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_create_seq_private(), proc_create_single_data() and their network
namespace variants keep the seq_operations or the show function in the
entry already. Let procfs own the seq_file for all of them. For the
network namespace variants ->proc_open() and ->proc_release() keep
taking and dropping the reference to the network namespace.

That's most of /proc/net, among them the files of xt_hashlimit and
/proc/net/dev_snmp6/<dev>. A reader that faults on its buffer no longer
holds up their removal.

Reported-by: Chris Mason <mason@kernel.org>
Fixes: 786d7e1612f0 ("Fix rmmod/read/write races in /proc entries")
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c  | 42 ++++--------------------------------------
 fs/proc/proc_net.c | 40 +++++++++++++---------------------------
 2 files changed, 17 insertions(+), 65 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index eff5c38e2219..6ed8bc313db0 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -694,30 +694,9 @@ struct proc_dir_entry *proc_create(const char *name, umode_t mode,
 }
 EXPORT_SYMBOL(proc_create);
 
-static int proc_seq_open(struct inode *inode, struct file *file)
-{
-	struct proc_dir_entry *de = PDE(inode);
-
-	if (de->state_size)
-		return seq_open_private(file, de->seq_ops, de->state_size);
-	return seq_open(file, de->seq_ops);
-}
-
-static int proc_seq_release(struct inode *inode, struct file *file)
-{
-	struct proc_dir_entry *de = PDE(inode);
-
-	if (de->state_size)
-		return seq_release_private(inode, file);
-	return seq_release(inode, file);
-}
-
+/* procfs owns the seq_file of these, see pde_seq_ops */
 static const struct proc_ops proc_seq_ops = {
 	/* not permanent -- can call into arbitrary seq_operations */
-	.proc_open	= proc_seq_open,
-	.proc_read_iter	= seq_read_iter,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= proc_seq_release,
 };
 
 struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
@@ -732,25 +711,11 @@ struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
 	p->proc_ops = &proc_seq_ops;
 	p->seq_ops = ops;
 	p->state_size = state_size;
+	p->flags |= PROC_ENTRY_proc_seq_ops;
 	return proc_register(parent, p);
 }
 EXPORT_SYMBOL(proc_create_seq_private);
 
-static int proc_single_open(struct inode *inode, struct file *file)
-{
-	struct proc_dir_entry *de = PDE(inode);
-
-	return single_open(file, de->single_show, de->data);
-}
-
-static const struct proc_ops proc_single_ops = {
-	/* not permanent -- can call into arbitrary ->single_show */
-	.proc_open	= proc_single_open,
-	.proc_read_iter = seq_read_iter,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
-};
-
 struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
 		struct proc_dir_entry *parent,
 		int (*show)(struct seq_file *, void *), void *data)
@@ -760,8 +725,9 @@ struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
 	p = proc_create_reg(name, mode, &parent, data);
 	if (!p)
 		return NULL;
-	p->proc_ops = &proc_single_ops;
+	p->proc_ops = &proc_seq_ops;
 	p->single_show = show;
+	p->flags |= PROC_ENTRY_proc_show;
 	return proc_register(parent, p);
 }
 EXPORT_SYMBOL(proc_create_single_data);
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index df5484813d5d..fa485581d05e 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -37,13 +37,13 @@ static struct net *get_proc_net(const struct inode *inode)
 	return maybe_get_net(PDE_NET(PDE(inode)));
 }
 
+/* procfs opened the seq_file and allocated its state, see pde_seq_ops */
 static int seq_open_net(struct inode *inode, struct file *file)
 {
-	unsigned int state_size = PDE(inode)->state_size;
-	struct seq_net_private *p;
+	struct seq_net_private *p = ((struct seq_file *)file->private_data)->private;
 	struct net *net;
 
-	WARN_ON_ONCE(state_size < sizeof(*p));
+	WARN_ON_ONCE(PDE(inode)->state_size < sizeof(*p));
 
 	if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
 		return -EACCES;
@@ -51,12 +51,6 @@ static int seq_open_net(struct inode *inode, struct file *file)
 	net = get_proc_net(inode);
 	if (!net)
 		return -ENXIO;
-
-	p = __seq_open_private(file, PDE(inode)->seq_ops, state_size);
-	if (!p) {
-		put_net(net);
-		return -ENOMEM;
-	}
 #ifdef CONFIG_NET_NS
 	p->net = net;
 	netns_tracker_alloc(net, &p->ns_tracker, GFP_KERNEL);
@@ -77,18 +71,13 @@ static void seq_file_net_put_net(struct seq_file *seq)
 
 static int seq_release_net(struct inode *ino, struct file *f)
 {
-	struct seq_file *seq = f->private_data;
-
-	seq_file_net_put_net(seq);
-	seq_release_private(ino, f);
+	seq_file_net_put_net(f->private_data);
 	return 0;
 }
 
 static const struct proc_ops proc_net_seq_ops = {
 	.proc_open	= seq_open_net,
-	.proc_read	= seq_read,
 	.proc_write	= proc_simple_write,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= seq_release_net,
 };
 
@@ -125,6 +114,7 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
 	p->proc_ops = &proc_net_seq_ops;
 	p->seq_ops = ops;
 	p->state_size = state_size;
+	p->flags |= PROC_ENTRY_proc_seq_ops;
 	return proc_register(parent, p);
 }
 EXPORT_SYMBOL_GPL(proc_create_net_data);
@@ -170,6 +160,7 @@ struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode
 	p->proc_ops = &proc_net_seq_ops;
 	p->seq_ops = ops;
 	p->state_size = state_size;
+	p->flags |= PROC_ENTRY_proc_seq_ops;
 	p->write = write;
 	return proc_register(parent, p);
 }
@@ -177,32 +168,25 @@ EXPORT_SYMBOL_GPL(proc_create_net_data_write);
 
 static int single_open_net(struct inode *inode, struct file *file)
 {
-	struct proc_dir_entry *de = PDE(inode);
-	struct net *net;
-	int err;
+	struct net *net = get_proc_net(inode);
 
-	net = get_proc_net(inode);
 	if (!net)
 		return -ENXIO;
-
-	err = single_open(file, de->single_show, net);
-	if (err)
-		put_net(net);
-	return err;
+	((struct seq_file *)file->private_data)->private = net;
+	return 0;
 }
 
 static int single_release_net(struct inode *ino, struct file *f)
 {
 	struct seq_file *seq = f->private_data;
+
 	put_net(seq->private);
-	return single_release(ino, f);
+	return 0;
 }
 
 static const struct proc_ops proc_net_single_ops = {
 	.proc_open	= single_open_net,
-	.proc_read	= seq_read,
 	.proc_write	= proc_simple_write,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= single_release_net,
 };
 
@@ -218,6 +202,7 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
 	pde_force_lookup(p);
 	p->proc_ops = &proc_net_single_ops;
 	p->single_show = show;
+	p->flags |= PROC_ENTRY_proc_show;
 	return proc_register(parent, p);
 }
 EXPORT_SYMBOL_GPL(proc_create_net_single);
@@ -261,6 +246,7 @@ struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mo
 	pde_force_lookup(p);
 	p->proc_ops = &proc_net_single_ops;
 	p->single_show = show;
+	p->flags |= PROC_ENTRY_proc_show;
 	p->write = write;
 	return proc_register(parent, p);
 }

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 06/16] proc: let procfs own the seq_file of DEFINE_PROC_SHOW_ATTRIBUTE() files
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 05/16] proc: let procfs own the seq_file of proc_create_seq() and friends Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 07/16] treewide: let procfs own the seq_file of simple proc entries Christian Brauner
                     ` (9 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show function and let procfs open the seq_file.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 include/linux/seq_file.h | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h
index dc0e8c62d9e0..b813b7e63d89 100644
--- a/include/linux/seq_file.h
+++ b/include/linux/seq_file.h
@@ -233,16 +233,8 @@ static const struct file_operations __name ## _fops = {			\
 }
 
 #define DEFINE_PROC_SHOW_ATTRIBUTE(__name)				\
-static int __name ## _open(struct inode *inode, struct file *file)	\
-{									\
-	return single_open(file, __name ## _show, pde_data(inode));	\
-}									\
-									\
 static const struct proc_ops __name ## _proc_ops = {			\
-	.proc_open	= __name ## _open,				\
-	.proc_read	= seq_read,					\
-	.proc_lseek	= seq_lseek,					\
-	.proc_release	= single_release,				\
+	.proc_show	= __name ## _show,				\
 }
 
 static inline struct user_namespace *seq_user_ns(struct seq_file *seq)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 07/16] treewide: let procfs own the seq_file of simple proc entries
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 06/16] proc: let procfs own the seq_file of DEFINE_PROC_SHOW_ATTRIBUTE() files Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 08/16] selftests/proc: check that a faulting reader doesn't hold up removal Christian Brauner
                     ` (8 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Most proc entries that bring their own struct proc_ops only call
single_open(), seq_open() or seq_open_private() in ->proc_open() and
free the seq_file with the matching helper. Declare the show function
or the seq_operations instead and let procfs open and free the
seq_file. Reading these files then no longer holds up their removal.

Done with the following semantic patch. The new initializers were
aligned like their neighbours afterwards:

  // SPDX-License-Identifier: GPL-2.0-only
  /// Let procfs open the seq_file of proc entries whose ->proc_open() only
  /// calls single_open(), seq_open() or seq_open_private().
  //
  // Confidence: High
  // Options: --no-includes --include-headers

  virtual patch

  @single depends on patch@
  identifier open_fn, show_fn, i, f;
  @@
  static int open_fn(struct inode *i, struct file *f)
  {
  	return single_open(f, show_fn, \(NULL\|pde_data(i)\));
  }

  @single_ops depends on single@
  identifier single.open_fn, single.show_fn, ops;
  symbol single_release;
  @@
  const struct proc_ops ops = {
  -	.proc_open = open_fn,
  +	.proc_show = show_fn,
  	...,
  -	.proc_release = single_release,
  	...
  };

  @seq depends on patch@
  identifier open_fn, sops, i, f;
  @@
  static int open_fn(struct inode *i, struct file *f)
  {
  	return seq_open(f, &sops);
  }

  @seq_ops depends on seq@
  identifier seq.open_fn, seq.sops, ops;
  symbol seq_release;
  @@
  const struct proc_ops ops = {
  -	.proc_open = open_fn,
  +	.proc_seq_ops = &sops,
  	...,
  -	.proc_release = seq_release,
  	...
  };

  @priv depends on patch@
  identifier open_fn, sops, i, f;
  expression size;
  @@
  static int open_fn(struct inode *i, struct file *f)
  {
  	return seq_open_private(f, &sops, size);
  }

  @priv_ops depends on priv@
  identifier priv.open_fn, priv.sops, ops;
  expression priv.size;
  symbol seq_release_private;
  @@
  const struct proc_ops ops = {
  -	.proc_open = open_fn,
  +	.proc_seq_ops = &sops,
  +	.proc_state_size = size,
  	...,
  -	.proc_release = seq_release_private,
  	...
  };

  // procfs reads and seeks through the seq_file itself
  @depends on single_ops || seq_ops || priv_ops@
  identifier ops = {single_ops.ops, seq_ops.ops, priv_ops.ops};
  symbol seq_read_iter;
  @@
  const struct proc_ops ops = {
  	...,
  -	.proc_read_iter = seq_read_iter,
  	...
  };

  @depends on single_ops || seq_ops || priv_ops@
  identifier ops = {single_ops.ops, seq_ops.ops, priv_ops.ops};
  symbol seq_read;
  @@
  const struct proc_ops ops = {
  	...,
  -	.proc_read = seq_read,
  	...
  };

  @depends on single_ops || seq_ops || priv_ops@
  identifier ops = {single_ops.ops, seq_ops.ops, priv_ops.ops};
  symbol seq_lseek;
  @@
  const struct proc_ops ops = {
  	...,
  -	.proc_lseek = seq_lseek,
  	...
  };

  // drop the open function unless something else still uses it
  @open_used@
  identifier open_fn = {single.open_fn, seq.open_fn, priv.open_fn};
  @@
  open_fn

  @depends on !open_used && (single_ops || seq_ops || priv_ops)@
  identifier open_fn = {single.open_fn, seq.open_fn, priv.open_fn};
  @@
  - static int open_fn(...) { ... }

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/alpha/kernel/srm_env.c                        | 10 +--
 arch/arm/mm/alignment.c                            | 10 +--
 arch/powerpc/kernel/rtas-proc.c                    | 50 ++------------
 arch/powerpc/platforms/pseries/lpar.c              | 20 +-----
 arch/powerpc/platforms/pseries/lparcfg.c           | 10 +--
 arch/sh/mm/alignment.c                             | 10 +--
 arch/sparc/kernel/led.c                            | 10 +--
 arch/um/kernel/exitcode.c                          | 10 +--
 drivers/acpi/proc.c                                | 12 +---
 drivers/hwmon/dell-smm-hwmon.c                     | 10 +--
 drivers/input/input.c                              | 24 ++-----
 drivers/macintosh/via-pmu.c                        | 10 +--
 drivers/net/wireless/intel/ipw2x00/libipw_module.c | 10 +--
 drivers/platform/x86/lenovo/thinkpad_acpi.c        | 10 +--
 drivers/platform/x86/toshiba_acpi.c                | 40 ++---------
 drivers/pnp/pnpbios/proc.c                         | 10 +--
 drivers/s390/block/dasd_proc.c                     | 10 +--
 drivers/s390/cio/blacklist.c                       | 13 +---
 drivers/scsi/scsi_devinfo.c                        | 10 +--
 drivers/scsi/scsi_proc.c                           | 21 +-----
 drivers/usb/gadget/function/rndis.c                | 10 +--
 drivers/video/fbdev/via/viafbdev.c                 | 70 ++-----------------
 fs/jfs/jfs_debug.c                                 | 10 +--
 fs/ntfs3/super.c                                   | 20 +-----
 fs/proc/cpuinfo.c                                  | 10 +--
 fs/smb/client/cifs_debug.c                         | 79 +++-------------------
 fs/smb/client/dfs_cache.c                          | 10 +--
 kernel/irq/proc.c                                  | 30 +-------
 kernel/latencytop.c                                | 10 +--
 lib/dynamic_debug.c                                |  6 +-
 mm/slab_common.c                                   | 10 +--
 net/core/pktgen.c                                  | 30 +-------
 net/sunrpc/stats.c                                 | 10 +--
 33 files changed, 64 insertions(+), 551 deletions(-)

diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c
index 217b4dca51dd..faafc2df7886 100644
--- a/arch/alpha/kernel/srm_env.c
+++ b/arch/alpha/kernel/srm_env.c
@@ -81,11 +81,6 @@ static int srm_env_proc_show(struct seq_file *m, void *v)
 	return ret;
 }
 
-static int srm_env_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, srm_env_proc_show, pde_data(inode));
-}
-
 static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
 				  size_t count, loff_t *pos)
 {
@@ -120,10 +115,7 @@ static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
 }
 
 static const struct proc_ops srm_env_proc_ops = {
-	.proc_open	= srm_env_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= srm_env_proc_show,
 	.proc_write	= srm_env_proc_write,
 };
 
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index f6c1e6b61179..3e5e30968634 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -144,11 +144,6 @@ static int alignment_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int alignment_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, alignment_proc_show, NULL);
-}
-
 static ssize_t alignment_proc_write(struct file *file, const char __user *buffer,
 				    size_t count, loff_t *pos)
 {
@@ -164,10 +159,7 @@ static ssize_t alignment_proc_write(struct file *file, const char __user *buffer
 }
 
 static const struct proc_ops alignment_proc_ops = {
-	.proc_open	= alignment_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= alignment_proc_show,
 	.proc_write	= alignment_proc_write,
 };
 #endif /* CONFIG_PROC_FS */
diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index f38df72e64b8..ebf416840b3c 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -154,69 +154,29 @@ static ssize_t ppc_rtas_tone_volume_write(struct file *file,
 static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v);
 static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v);
 
-static int poweron_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ppc_rtas_poweron_show, NULL);
-}
-
 static const struct proc_ops ppc_rtas_poweron_proc_ops = {
-	.proc_open	= poweron_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= ppc_rtas_poweron_show,
 	.proc_write	= ppc_rtas_poweron_write,
-	.proc_release	= single_release,
 };
 
-static int progress_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ppc_rtas_progress_show, NULL);
-}
-
 static const struct proc_ops ppc_rtas_progress_proc_ops = {
-	.proc_open	= progress_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= ppc_rtas_progress_show,
 	.proc_write	= ppc_rtas_progress_write,
-	.proc_release	= single_release,
 };
 
-static int clock_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ppc_rtas_clock_show, NULL);
-}
-
 static const struct proc_ops ppc_rtas_clock_proc_ops = {
-	.proc_open	= clock_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= ppc_rtas_clock_show,
 	.proc_write	= ppc_rtas_clock_write,
-	.proc_release	= single_release,
 };
 
-static int tone_freq_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ppc_rtas_tone_freq_show, NULL);
-}
-
 static const struct proc_ops ppc_rtas_tone_freq_proc_ops = {
-	.proc_open	= tone_freq_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= ppc_rtas_tone_freq_show,
 	.proc_write	= ppc_rtas_tone_freq_write,
-	.proc_release	= single_release,
 };
 
-static int tone_volume_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ppc_rtas_tone_volume_show, NULL);
-}
-
 static const struct proc_ops ppc_rtas_tone_volume_proc_ops = {
-	.proc_open	= tone_volume_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= ppc_rtas_tone_volume_show,
 	.proc_write	= ppc_rtas_tone_volume_write,
-	.proc_release	= single_release,
 };
 
 static int ppc_rtas_find_all_sensors(void);
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 6a415febc53b..48fe0be71a89 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -584,17 +584,9 @@ static int vcpudispatch_stats_display(struct seq_file *p, void *v)
 	return 0;
 }
 
-static int vcpudispatch_stats_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, vcpudispatch_stats_display, NULL);
-}
-
 static const struct proc_ops vcpudispatch_stats_proc_ops = {
-	.proc_open	= vcpudispatch_stats_open,
-	.proc_read	= seq_read,
+	.proc_show	= vcpudispatch_stats_display,
 	.proc_write	= vcpudispatch_stats_write,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
 };
 
 static ssize_t vcpudispatch_stats_freq_write(struct file *file,
@@ -628,17 +620,9 @@ static int vcpudispatch_stats_freq_display(struct seq_file *p, void *v)
 	return 0;
 }
 
-static int vcpudispatch_stats_freq_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, vcpudispatch_stats_freq_display, NULL);
-}
-
 static const struct proc_ops vcpudispatch_stats_freq_proc_ops = {
-	.proc_open	= vcpudispatch_stats_freq_open,
-	.proc_read	= seq_read,
+	.proc_show	= vcpudispatch_stats_freq_display,
 	.proc_write	= vcpudispatch_stats_freq_write,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
 };
 
 static int __init vcpudispatch_stats_procfs_init(void)
diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
index aa9b5b141434..a6a137ce44ce 100644
--- a/arch/powerpc/platforms/pseries/lparcfg.c
+++ b/arch/powerpc/platforms/pseries/lparcfg.c
@@ -798,17 +798,9 @@ static int lparcfg_data(struct seq_file *m, void *v)
 	return pseries_lparcfg_data(m, v);
 }
 
-static int lparcfg_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, lparcfg_data, NULL);
-}
-
 static const struct proc_ops lparcfg_proc_ops = {
-	.proc_read	= seq_read,
 	.proc_write	= lparcfg_write,
-	.proc_open	= lparcfg_open,
-	.proc_release	= single_release,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= lparcfg_data,
 };
 
 static int __init lparcfg_init(void)
diff --git a/arch/sh/mm/alignment.c b/arch/sh/mm/alignment.c
index 3a76a766f423..2198c6f2ea63 100644
--- a/arch/sh/mm/alignment.c
+++ b/arch/sh/mm/alignment.c
@@ -132,11 +132,6 @@ static int alignment_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int alignment_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, alignment_proc_show, NULL);
-}
-
 static ssize_t alignment_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -153,10 +148,7 @@ static ssize_t alignment_proc_write(struct file *file,
 }
 
 static const struct proc_ops alignment_proc_ops = {
-	.proc_open	= alignment_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= alignment_proc_show,
 	.proc_write	= alignment_proc_write,
 };
 
diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c
index 9b53ac1fe533..12400bb59ab8 100644
--- a/arch/sparc/kernel/led.c
+++ b/arch/sparc/kernel/led.c
@@ -60,11 +60,6 @@ static int led_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int led_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, led_proc_show, NULL);
-}
-
 static ssize_t led_proc_write(struct file *file, const char __user *buffer,
 			      size_t count, loff_t *ppos)
 {
@@ -106,10 +101,7 @@ static ssize_t led_proc_write(struct file *file, const char __user *buffer,
 }
 
 static const struct proc_ops led_proc_ops = {
-	.proc_open	= led_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= led_proc_show,
 	.proc_write	= led_proc_write,
 };
 #endif
diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c
index 0230354f4812..c71657df38f5 100644
--- a/arch/um/kernel/exitcode.c
+++ b/arch/um/kernel/exitcode.c
@@ -31,11 +31,6 @@ static int exitcode_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int exitcode_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, exitcode_proc_show, NULL);
-}
-
 static ssize_t exitcode_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -57,10 +52,7 @@ static ssize_t exitcode_proc_write(struct file *file,
 }
 
 static const struct proc_ops exitcode_proc_ops = {
-	.proc_open	= exitcode_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= exitcode_proc_show,
 	.proc_write	= exitcode_proc_write,
 };
 
diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
index c08ead07252b..b8a0bd4580e0 100644
--- a/drivers/acpi/proc.c
+++ b/drivers/acpi/proc.c
@@ -121,19 +121,9 @@ acpi_system_write_wakeup_device(struct file *file,
 	return count;
 }
 
-static int
-acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
-{
-	return single_open(file, acpi_system_wakeup_device_seq_show,
-			   pde_data(inode));
-}
-
 static const struct proc_ops acpi_system_wakeup_device_proc_ops = {
-	.proc_open	= acpi_system_wakeup_device_open_fs,
-	.proc_read	= seq_read,
+	.proc_show	= acpi_system_wakeup_device_seq_show,
 	.proc_write	= acpi_system_write_wakeup_device,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
 };
 
 void __init acpi_sleep_proc_init(void)
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index 88837f028405..492b2b6c6d7e 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -694,16 +694,8 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
 	return 0;
 }
 
-static int i8k_open_fs(struct inode *inode, struct file *file)
-{
-	return single_open(file, i8k_proc_show, pde_data(inode));
-}
-
 static const struct proc_ops i8k_proc_ops = {
-	.proc_open	= i8k_open_fs,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= i8k_proc_show,
 	.proc_ioctl	= i8k_ioctl,
 };
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 0859b60ca924..d27eecbf4a03 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1218,18 +1218,10 @@ static const struct seq_operations input_devices_seq_ops = {
 	.show	= input_devices_seq_show,
 };
 
-static int input_proc_devices_open(struct inode *inode, struct file *file)
-{
-	return seq_open_private(file, &input_devices_seq_ops,
-				sizeof(struct input_seq_state));
-}
-
 static const struct proc_ops input_devices_proc_ops = {
-	.proc_open	= input_proc_devices_open,
+	.proc_seq_ops	= &input_devices_seq_ops,
+	.proc_state_size = sizeof(struct input_seq_state),
 	.proc_poll	= input_proc_devices_poll,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release_private,
 };
 
 static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
@@ -1279,17 +1271,9 @@ static const struct seq_operations input_handlers_seq_ops = {
 	.show	= input_handlers_seq_show,
 };
 
-static int input_proc_handlers_open(struct inode *inode, struct file *file)
-{
-	return seq_open_private(file, &input_handlers_seq_ops,
-				sizeof(struct input_seq_state));
-}
-
 static const struct proc_ops input_handlers_proc_ops = {
-	.proc_open	= input_proc_handlers_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release_private,
+	.proc_seq_ops = &input_handlers_seq_ops,
+	.proc_state_size = sizeof(struct input_seq_state),
 };
 
 static int __init input_proc_init(void)
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 8c652c15018c..49f929ae10e4 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -912,11 +912,6 @@ static int pmu_options_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int pmu_options_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, pmu_options_proc_show, NULL);
-}
-
 static ssize_t pmu_options_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -962,10 +957,7 @@ static ssize_t pmu_options_proc_write(struct file *file,
 }
 
 static const struct proc_ops pmu_options_proc_ops = {
-	.proc_open	= pmu_options_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= pmu_options_proc_show,
 	.proc_write	= pmu_options_proc_write,
 };
 #endif
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index 552c8ff3928b..15d9a5efb581 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
@@ -215,11 +215,6 @@ static int debug_level_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int debug_level_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, debug_level_proc_show, NULL);
-}
-
 static ssize_t debug_level_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -240,10 +235,7 @@ static ssize_t debug_level_proc_write(struct file *file,
 }
 
 static const struct proc_ops debug_level_proc_ops = {
-	.proc_open	= debug_level_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= debug_level_proc_show,
 	.proc_write	= debug_level_proc_write,
 };
 #endif				/* CONFIG_LIBIPW_DEBUG */
diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
index 1661f070c571..01e4c980fad6 100644
--- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
+++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
@@ -880,11 +880,6 @@ static int dispatch_proc_show(struct seq_file *m, void *v)
 	return ibm->read(m);
 }
 
-static int dispatch_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, dispatch_proc_show, pde_data(inode));
-}
-
 static ssize_t dispatch_proc_write(struct file *file,
 			const char __user *userbuf,
 			size_t count, loff_t *pos)
@@ -911,10 +906,7 @@ static ssize_t dispatch_proc_write(struct file *file,
 }
 
 static const struct proc_ops dispatch_proc_ops = {
-	.proc_open	= dispatch_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= dispatch_proc_show,
 	.proc_write	= dispatch_proc_write,
 };
 
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 9eb8f1e5aeda..560e62da05a9 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1448,11 +1448,6 @@ static int lcd_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int lcd_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, lcd_proc_show, pde_data(inode));
-}
-
 static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
 {
 	u32 result;
@@ -1509,10 +1504,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
 }
 
 static const struct proc_ops lcd_proc_ops = {
-	.proc_open	= lcd_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= lcd_proc_show,
 	.proc_write	= lcd_proc_write,
 };
 
@@ -1549,11 +1541,6 @@ static int video_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int video_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, video_proc_show, pde_data(inode));
-}
-
 static ssize_t video_proc_write(struct file *file, const char __user *buf,
 				size_t count, loff_t *pos)
 {
@@ -1614,10 +1601,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
 }
 
 static const struct proc_ops video_proc_ops = {
-	.proc_open	= video_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= video_proc_show,
 	.proc_write	= video_proc_write,
 };
 
@@ -1660,11 +1644,6 @@ static int fan_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int fan_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, fan_proc_show, pde_data(inode));
-}
-
 static ssize_t fan_proc_write(struct file *file, const char __user *buf,
 			      size_t count, loff_t *pos)
 {
@@ -1691,10 +1670,7 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
 }
 
 static const struct proc_ops fan_proc_ops = {
-	.proc_open	= fan_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= fan_proc_show,
 	.proc_write	= fan_proc_write,
 };
 
@@ -1731,11 +1707,6 @@ static int keys_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int keys_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, keys_proc_show, pde_data(inode));
-}
-
 static ssize_t keys_proc_write(struct file *file, const char __user *buf,
 			       size_t count, loff_t *pos)
 {
@@ -1758,10 +1729,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf,
 }
 
 static const struct proc_ops keys_proc_ops = {
-	.proc_open	= keys_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= keys_proc_show,
 	.proc_write	= keys_proc_write,
 };
 
diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c
index 0f0d819b157f..14632a8d0da0 100644
--- a/drivers/pnp/pnpbios/proc.c
+++ b/drivers/pnp/pnpbios/proc.c
@@ -171,11 +171,6 @@ static int pnpbios_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int pnpbios_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, pnpbios_proc_show, pde_data(inode));
-}
-
 static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf,
 				  size_t count, loff_t *pos)
 {
@@ -211,10 +206,7 @@ out:
 }
 
 static const struct proc_ops pnpbios_proc_ops = {
-	.proc_open	= pnpbios_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= pnpbios_proc_show,
 	.proc_write	= pnpbios_proc_write,
 };
 
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 48e12e81df00..86399427f01d 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -246,11 +246,6 @@ static int dasd_stats_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int dasd_stats_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, dasd_stats_proc_show, NULL);
-}
-
 static ssize_t dasd_stats_proc_write(struct file *file,
 		const char __user *user_buf, size_t user_len, loff_t *pos)
 {
@@ -316,10 +311,7 @@ out_error:
 }
 
 static const struct proc_ops dasd_stats_proc_ops = {
-	.proc_open	= dasd_stats_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= dasd_stats_proc_show,
 	.proc_write	= dasd_stats_proc_write,
 };
 
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 020d210bde9f..4e02f78f6102 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -397,18 +397,9 @@ static const struct seq_operations cio_ignore_proc_seq_ops = {
 	.show  = cio_ignore_proc_seq_show,
 };
 
-static int
-cio_ignore_proc_open(struct inode *inode, struct file *file)
-{
-	return seq_open_private(file, &cio_ignore_proc_seq_ops,
-				sizeof(struct ccwdev_iter));
-}
-
 static const struct proc_ops cio_ignore_proc_ops = {
-	.proc_open	= cio_ignore_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release_private,
+	.proc_seq_ops	= &cio_ignore_proc_seq_ops,
+	.proc_state_size = sizeof(struct ccwdev_iter),
 	.proc_write	= cio_ignore_write,
 };
 
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index cfc259fd0141..0b8f5ff40543 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -670,11 +670,6 @@ static const struct seq_operations scsi_devinfo_seq_ops = {
 	.show	= devinfo_seq_show,
 };
 
-static int proc_scsi_devinfo_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &scsi_devinfo_seq_ops);
-}
-
 /*
  * proc_scsi_dev_info_write - allow additions to scsi_dev_info_list via /proc.
  *
@@ -714,11 +709,8 @@ out:
 }
 
 static const struct proc_ops scsi_devinfo_proc_ops = {
-	.proc_open	= proc_scsi_devinfo_open,
-	.proc_read	= seq_read,
+	.proc_seq_ops	= &scsi_devinfo_seq_ops,
 	.proc_write	= proc_scsi_devinfo_write,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
 };
 #endif /* CONFIG_SCSI_PROC_FS */
 
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index dbd03360b5a4..3e49c4c8704f 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -521,28 +521,9 @@ static const struct seq_operations scsi_seq_ops = {
 	.show	= scsi_seq_show
 };
 
-/**
- * proc_scsi_open - glue function
- * @inode: not used
- * @file: passed to single_open()
- *
- * Associates proc_scsi_show with this file
- */
-static int proc_scsi_open(struct inode *inode, struct file *file)
-{
-	/*
-	 * We don't really need this for the write case but it doesn't
-	 * harm either.
-	 */
-	return seq_open(file, &scsi_seq_ops);
-}
-
 static const struct proc_ops scsi_scsi_proc_ops = {
-	.proc_open	= proc_scsi_open,
-	.proc_read	= seq_read,
+	.proc_seq_ops	= &scsi_seq_ops,
 	.proc_write	= proc_scsi_write,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
 };
 
 /**
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index a2fd239b7ad3..3c7bd30b8a3a 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -1183,16 +1183,8 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
 	return count;
 }
 
-static int rndis_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, rndis_proc_show, pde_data(inode));
-}
-
 static const struct proc_ops rndis_proc_ops = {
-	.proc_open	= rndis_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= rndis_proc_show,
 	.proc_write	= rndis_proc_write,
 };
 
diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c
index 2c67a18d16ad..4b617e80a77c 100644
--- a/drivers/video/fbdev/via/viafbdev.c
+++ b/drivers/video/fbdev/via/viafbdev.c
@@ -1120,11 +1120,6 @@ static int viafb_dvp0_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_dvp0_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_dvp0_proc_show, NULL);
-}
-
 static ssize_t viafb_dvp0_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1173,10 +1168,7 @@ static ssize_t viafb_dvp0_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_dvp0_proc_ops = {
-	.proc_open	= viafb_dvp0_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_dvp0_proc_show,
 	.proc_write	= viafb_dvp0_proc_write,
 };
 
@@ -1190,11 +1182,6 @@ static int viafb_dvp1_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_dvp1_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_dvp1_proc_show, NULL);
-}
-
 static ssize_t viafb_dvp1_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1237,10 +1224,7 @@ static ssize_t viafb_dvp1_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_dvp1_proc_ops = {
-	.proc_open	= viafb_dvp1_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_dvp1_proc_show,
 	.proc_write	= viafb_dvp1_proc_write,
 };
 
@@ -1252,11 +1236,6 @@ static int viafb_dfph_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_dfph_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_dfph_proc_show, NULL);
-}
-
 static ssize_t viafb_dfph_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1271,10 +1250,7 @@ static ssize_t viafb_dfph_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_dfph_proc_ops = {
-	.proc_open	= viafb_dfph_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_dfph_proc_show,
 	.proc_write	= viafb_dfph_proc_write,
 };
 
@@ -1286,11 +1262,6 @@ static int viafb_dfpl_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_dfpl_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_dfpl_proc_show, NULL);
-}
-
 static ssize_t viafb_dfpl_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1305,10 +1276,7 @@ static ssize_t viafb_dfpl_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_dfpl_proc_ops = {
-	.proc_open	= viafb_dfpl_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_dfpl_proc_show,
 	.proc_write	= viafb_dfpl_proc_write,
 };
 
@@ -1344,11 +1312,6 @@ static int viafb_vt1636_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_vt1636_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_vt1636_proc_show, NULL);
-}
-
 static ssize_t viafb_vt1636_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1440,10 +1403,7 @@ static ssize_t viafb_vt1636_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_vt1636_proc_ops = {
-	.proc_open	= viafb_vt1636_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_vt1636_proc_show,
 	.proc_write	= viafb_vt1636_proc_write,
 };
 
@@ -1491,11 +1451,6 @@ static int viafb_iga1_odev_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_iga1_odev_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_iga1_odev_proc_show, NULL);
-}
-
 static ssize_t viafb_iga1_odev_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1517,10 +1472,7 @@ static ssize_t viafb_iga1_odev_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_iga1_odev_proc_ops = {
-	.proc_open	= viafb_iga1_odev_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_iga1_odev_proc_show,
 	.proc_write	= viafb_iga1_odev_proc_write,
 };
 
@@ -1530,11 +1482,6 @@ static int viafb_iga2_odev_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int viafb_iga2_odev_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, viafb_iga2_odev_proc_show, NULL);
-}
-
 static ssize_t viafb_iga2_odev_proc_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *pos)
 {
@@ -1556,10 +1503,7 @@ static ssize_t viafb_iga2_odev_proc_write(struct file *file,
 }
 
 static const struct proc_ops viafb_iga2_odev_proc_ops = {
-	.proc_open	= viafb_iga2_odev_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= viafb_iga2_odev_proc_show,
 	.proc_write	= viafb_iga2_odev_proc_write,
 };
 
diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c
index 44b62b3c322e..5eb35b0d420f 100644
--- a/fs/jfs/jfs_debug.c
+++ b/fs/jfs/jfs_debug.c
@@ -23,11 +23,6 @@ static int jfs_loglevel_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int jfs_loglevel_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, jfs_loglevel_proc_show, NULL);
-}
-
 static ssize_t jfs_loglevel_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *ppos)
 {
@@ -44,10 +39,7 @@ static ssize_t jfs_loglevel_proc_write(struct file *file,
 }
 
 static const struct proc_ops jfs_loglevel_proc_ops = {
-	.proc_open	= jfs_loglevel_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= jfs_loglevel_proc_show,
 	.proc_write	= jfs_loglevel_proc_write,
 };
 #endif
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index b4479cd6bd65..3d669e957ea4 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -524,11 +524,6 @@ static int ntfs3_volinfo(struct seq_file *m, void *o)
 	return 0;
 }
 
-static int ntfs3_volinfo_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ntfs3_volinfo, pde_data(inode));
-}
-
 /* read /proc/fs/ntfs3/<dev>/label */
 static int ntfs3_label_show(struct seq_file *m, void *o)
 {
@@ -574,23 +569,12 @@ out:
 	return ret;
 }
 
-static int ntfs3_label_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, ntfs3_label_show, pde_data(inode));
-}
-
 static const struct proc_ops ntfs3_volinfo_fops = {
-	.proc_read = seq_read,
-	.proc_lseek = seq_lseek,
-	.proc_release = single_release,
-	.proc_open = ntfs3_volinfo_open,
+	.proc_show = ntfs3_volinfo,
 };
 
 static const struct proc_ops ntfs3_label_fops = {
-	.proc_read = seq_read,
-	.proc_lseek = seq_lseek,
-	.proc_release = single_release,
-	.proc_open = ntfs3_label_open,
+	.proc_show = ntfs3_label_show,
 	.proc_write = ntfs3_label_write,
 };
 
diff --git a/fs/proc/cpuinfo.c b/fs/proc/cpuinfo.c
index f38bda5b83ec..d8cd718c6030 100644
--- a/fs/proc/cpuinfo.c
+++ b/fs/proc/cpuinfo.c
@@ -7,17 +7,9 @@
 
 extern const struct seq_operations cpuinfo_op;
 
-static int cpuinfo_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &cpuinfo_op);
-}
-
 static const struct proc_ops cpuinfo_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
-	.proc_open	= cpuinfo_open,
-	.proc_read_iter	= seq_read_iter,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
+	.proc_seq_ops	= &cpuinfo_op,
 };
 
 static int __init proc_cpuinfo_init(void)
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 577f0ddede90..dfcd2cdbb2a8 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -357,10 +357,6 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
 }
 
 #ifdef CONFIG_CIFS_DEBUG
-static int cifs_debug_dirs_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifs_debug_dirs_proc_show, NULL);
-}
 
 /* Drop all cached directory entries across all CIFS mounts. */
 static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *buffer,
@@ -393,10 +389,7 @@ static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *
 }
 
 static const struct proc_ops cifs_debug_dirs_proc_ops = {
-	.proc_open	= cifs_debug_dirs_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= cifs_debug_dirs_proc_show,
 	.proc_write	= cifs_debug_dirs_proc_write,
 };
 #endif /* CONFIG_CIFS_DEBUG */
@@ -859,16 +852,8 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int cifs_stats_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifs_stats_proc_show, NULL);
-}
-
 static const struct proc_ops cifs_stats_proc_ops = {
-	.proc_open	= cifs_stats_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= cifs_stats_proc_show,
 	.proc_write	= cifs_stats_proc_write,
 };
 
@@ -1012,11 +997,6 @@ static int cifsFYI_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int cifsFYI_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifsFYI_proc_show, NULL);
-}
-
 static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
 		size_t count, loff_t *ppos)
 {
@@ -1038,10 +1018,7 @@ static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
 }
 
 static const struct proc_ops cifsFYI_proc_ops = {
-	.proc_open	= cifsFYI_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= cifsFYI_proc_show,
 	.proc_write	= cifsFYI_proc_write,
 };
 
@@ -1051,11 +1028,6 @@ static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int cifs_linux_ext_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifs_linux_ext_proc_show, NULL);
-}
-
 static ssize_t cifs_linux_ext_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *ppos)
 {
@@ -1069,10 +1041,7 @@ static ssize_t cifs_linux_ext_proc_write(struct file *file,
 }
 
 static const struct proc_ops cifs_linux_ext_proc_ops = {
-	.proc_open	= cifs_linux_ext_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= cifs_linux_ext_proc_show,
 	.proc_write	= cifs_linux_ext_proc_write,
 };
 
@@ -1082,11 +1051,6 @@ static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int cifs_lookup_cache_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifs_lookup_cache_proc_show, NULL);
-}
-
 static ssize_t cifs_lookup_cache_proc_write(struct file *file,
 		const char __user *buffer, size_t count, loff_t *ppos)
 {
@@ -1100,10 +1064,7 @@ static ssize_t cifs_lookup_cache_proc_write(struct file *file,
 }
 
 static const struct proc_ops cifs_lookup_cache_proc_ops = {
-	.proc_open	= cifs_lookup_cache_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= cifs_lookup_cache_proc_show,
 	.proc_write	= cifs_lookup_cache_proc_write,
 };
 
@@ -1113,11 +1074,6 @@ static int traceSMB_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int traceSMB_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, traceSMB_proc_show, NULL);
-}
-
 static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
 		size_t count, loff_t *ppos)
 {
@@ -1131,10 +1087,7 @@ static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
 }
 
 static const struct proc_ops traceSMB_proc_ops = {
-	.proc_open	= traceSMB_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= traceSMB_proc_show,
 	.proc_write	= traceSMB_proc_write,
 };
 
@@ -1144,11 +1097,6 @@ static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int cifs_security_flags_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifs_security_flags_proc_show, NULL);
-}
-
 /*
  * Ensure that if someone sets a MUST flag, that we disable all other MAY
  * flags except for the ones corresponding to the given MUST flag. If there are
@@ -1234,10 +1182,7 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
 }
 
 static const struct proc_ops cifs_security_flags_proc_ops = {
-	.proc_open	= cifs_security_flags_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= cifs_security_flags_proc_show,
 	.proc_write	= cifs_security_flags_proc_write,
 };
 
@@ -1271,16 +1216,8 @@ static int cifs_mount_params_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int cifs_mount_params_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, cifs_mount_params_proc_show, NULL);
-}
-
 static const struct proc_ops cifs_mount_params_proc_ops = {
-	.proc_open	= cifs_mount_params_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show = cifs_mount_params_proc_show,
 	/* No need for write for now */
 	/* .proc_write	= cifs_mount_params_proc_write, */
 };
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 26956e33037e..58f16cf83d36 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -213,16 +213,8 @@ static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
 	return count;
 }
 
-static int dfscache_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, dfscache_proc_show, NULL);
-}
-
 const struct proc_ops dfscache_proc_ops = {
-	.proc_open	= dfscache_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= dfscache_proc_show,
 	.proc_write	= dfscache_proc_write,
 };
 
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index 0c0df16de581..aa3d5544cfc1 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -188,29 +188,13 @@ static ssize_t irq_affinity_list_proc_write(struct file *file,
 	return write_irq_affinity(1, file, buffer, count, pos);
 }
 
-static int irq_affinity_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, irq_affinity_proc_show, pde_data(inode));
-}
-
-static int irq_affinity_list_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, irq_affinity_list_proc_show, pde_data(inode));
-}
-
 static const struct proc_ops irq_affinity_proc_ops = {
-	.proc_open	= irq_affinity_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= irq_affinity_proc_show,
 	.proc_write	= irq_affinity_proc_write,
 };
 
 static const struct proc_ops irq_affinity_list_proc_ops = {
-	.proc_open	= irq_affinity_list_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= irq_affinity_list_proc_show,
 	.proc_write	= irq_affinity_list_proc_write,
 };
 
@@ -263,16 +247,8 @@ out:
 	return err;
 }
 
-static int default_affinity_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, default_affinity_show, pde_data(inode));
-}
-
 static const struct proc_ops default_affinity_proc_ops = {
-	.proc_open	= default_affinity_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= default_affinity_show,
 	.proc_write	= default_affinity_write,
 };
 
diff --git a/kernel/latencytop.c b/kernel/latencytop.c
index d4281d1e13a6..b764f47606d3 100644
--- a/kernel/latencytop.c
+++ b/kernel/latencytop.c
@@ -275,17 +275,9 @@ lstats_write(struct file *file, const char __user *buf, size_t count,
 	return count;
 }
 
-static int lstats_open(struct inode *inode, struct file *filp)
-{
-	return single_open(filp, lstats_show, NULL);
-}
-
 static const struct proc_ops lstats_proc_ops = {
-	.proc_open	= lstats_open,
-	.proc_read	= seq_read,
+	.proc_show	= lstats_show,
 	.proc_write	= lstats_write,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
 };
 
 static int __init init_lstats_procfs(void)
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 18a71a9108d3..64a211290872 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1194,10 +1194,8 @@ static const struct file_operations ddebug_proc_fops = {
 };
 
 static const struct proc_ops proc_fops = {
-	.proc_open = ddebug_proc_open,
-	.proc_read = seq_read,
-	.proc_lseek = seq_lseek,
-	.proc_release = seq_release_private,
+	.proc_seq_ops = &ddebug_proc_seqops,
+	.proc_state_size = sizeof(struct ddebug_iter),
 	.proc_write = ddebug_proc_write
 };
 
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 01c64ef661e7..fbf303b45858 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -1233,17 +1233,9 @@ static const struct seq_operations slabinfo_op = {
 	.show = slab_show,
 };
 
-static int slabinfo_open(struct inode *inode, struct file *file)
-{
-	return seq_open(file, &slabinfo_op);
-}
-
 static const struct proc_ops slabinfo_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
-	.proc_open	= slabinfo_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
+	.proc_seq_ops	= &slabinfo_op,
 };
 
 static int __init slab_proc_init(void)
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 02af70d360bc..12363a373fb8 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -550,17 +550,9 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf,
 	return count;
 }
 
-static int pgctrl_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, pgctrl_show, pde_data(inode));
-}
-
 static const struct proc_ops pktgen_proc_ops = {
-	.proc_open	= pgctrl_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= pgctrl_show,
 	.proc_write	= pgctrl_write,
-	.proc_release	= single_release,
 };
 
 static int pktgen_if_show(struct seq_file *seq, void *v)
@@ -1870,17 +1862,9 @@ static ssize_t pktgen_if_write(struct file *file,
 	return -EINVAL;
 }
 
-static int pktgen_if_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, pktgen_if_show, pde_data(inode));
-}
-
 static const struct proc_ops pktgen_if_proc_ops = {
-	.proc_open	= pktgen_if_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= pktgen_if_show,
 	.proc_write	= pktgen_if_write,
-	.proc_release	= single_release,
 };
 
 static int pktgen_thread_show(struct seq_file *seq, void *v)
@@ -2010,17 +1994,9 @@ out:
 	return ret;
 }
 
-static int pktgen_thread_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, pktgen_thread_show, pde_data(inode));
-}
-
 static const struct proc_ops pktgen_thread_proc_ops = {
-	.proc_open	= pktgen_thread_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= pktgen_thread_show,
 	.proc_write	= pktgen_thread_write,
-	.proc_release	= single_release,
 };
 
 /* Think find or remove for NN */
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c
index d08711bee18e..f5ee18a0eb37 100644
--- a/net/sunrpc/stats.c
+++ b/net/sunrpc/stats.c
@@ -64,16 +64,8 @@ static int rpc_proc_show(struct seq_file *seq, void *v) {
 	return 0;
 }
 
-static int rpc_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, rpc_proc_show, pde_data(inode));
-}
-
 static const struct proc_ops rpc_proc_ops = {
-	.proc_open	= rpc_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show = rpc_proc_show,
 };
 
 /*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 08/16] selftests/proc: check that a faulting reader doesn't hold up removal
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 07/16] treewide: let procfs own the seq_file of simple proc entries Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 09/16] seq_file: add seq_preallocate() Christian Brauner
                     ` (7 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Read /proc/net/dev_snmp6/<dev> of a tun device into a buffer whose first
page is backed by userfaultfd and remove the device while the reader
sleeps in its copy to userspace:

- removing the entry doesn't wait for the reader

- the reader still gets what was printed before the removal

- and the next read fails with -EIO

The entry is removed under RTNL, so a regression stalls the test for
five seconds before it fails.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 .../selftests/proc/proc-remove-slow-reader.c       | 133 +++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/tools/testing/selftests/proc/proc-remove-slow-reader.c b/tools/testing/selftests/proc/proc-remove-slow-reader.c
new file mode 100644
index 000000000000..a201645beca9
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-remove-slow-reader.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A read that faults while copying to userspace must not hold up removal of
+ * the /proc entry. /proc/net/dev_snmp6/<dev> of a tun device is removed under
+ * RTNL when the device's file is closed.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sched.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define TUN_NAME	"procslow0"
+#define SNMP6_PATH	"/proc/net/dev_snmp6/" TUN_NAME
+#define READ_PAGES	16
+#define TIMEOUT_MS	5000
+
+struct reader {
+	int fd;
+	char *buf;
+	size_t len;
+	ssize_t ret;
+};
+
+struct closer {
+	int tun;
+	int done;
+};
+
+static void *read_thread(void *arg)
+{
+	struct reader *r = arg;
+
+	r->ret = read(r->fd, r->buf, r->len);
+	return NULL;
+}
+
+static void *close_thread(void *arg)
+{
+	struct closer *c = arg;
+	ssize_t ret;
+
+	close(c->tun);
+	ret = write(c->done, "x", 1);
+	return (void *)ret;
+}
+
+TEST(remove_while_reader_faults)
+{
+	struct uffdio_api api = { .api = UFFD_API };
+	struct uffdio_register reg = {};
+	struct uffdio_zeropage zero = {};
+	long psz = sysconf(_SC_PAGESIZE);
+	struct ifreq ifr = {};
+	struct reader r = {};
+	struct closer c = {};
+	struct uffd_msg msg;
+	struct pollfd pfd;
+	pthread_t rt, ct;
+	int uffd, done[2];
+	char *buf;
+
+	if (unshare(CLONE_NEWNET))
+		SKIP(return, "unshare(CLONE_NEWNET): %s", strerror(errno));
+
+	c.tun = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (c.tun < 0)
+		SKIP(return, "/dev/net/tun: %s", strerror(errno));
+	strncpy(ifr.ifr_name, TUN_NAME, IFNAMSIZ - 1);
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+	ASSERT_EQ(ioctl(c.tun, TUNSETIFF, &ifr), 0);
+
+	r.fd = open(SNMP6_PATH, O_RDONLY | O_CLOEXEC);
+	if (r.fd < 0)
+		SKIP(return, SNMP6_PATH ": %s", strerror(errno));
+
+	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (uffd < 0)
+		SKIP(return, "userfaultfd: %s", strerror(errno));
+	ASSERT_EQ(ioctl(uffd, UFFDIO_API, &api), 0);
+
+	/* Only the first page of the buffer goes through userfaultfd. */
+	buf = mmap(NULL, READ_PAGES * psz, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(buf, MAP_FAILED);
+	reg.range.start = (unsigned long)buf;
+	reg.range.len = psz;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_REGISTER, &reg), 0);
+
+	r.buf = buf;
+	r.len = READ_PAGES * psz;
+	ASSERT_EQ(pthread_create(&rt, NULL, read_thread, &r), 0);
+
+	/* The reader now sleeps in its copy to userspace. */
+	pfd = (struct pollfd){ .fd = uffd, .events = POLLIN };
+	ASSERT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1);
+	ASSERT_EQ(read(uffd, &msg, sizeof(msg)), sizeof(msg));
+	ASSERT_EQ(msg.event, UFFD_EVENT_PAGEFAULT);
+
+	/* Closing the device removes the file that is being read. */
+	ASSERT_EQ(pipe2(done, O_CLOEXEC), 0);
+	c.done = done[1];
+	ASSERT_EQ(pthread_create(&ct, NULL, close_thread, &c), 0);
+	pfd = (struct pollfd){ .fd = done[0], .events = POLLIN };
+	EXPECT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1) {
+		TH_LOG("removing " SNMP6_PATH " waited for the reader");
+	}
+
+	zero.range.start = (unsigned long)buf;
+	zero.range.len = psz;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_ZEROPAGE, &zero), 0);
+	ASSERT_EQ(pthread_join(rt, NULL), 0);
+	ASSERT_EQ(pthread_join(ct, NULL), 0);
+
+	/* What was printed before the removal still arrives, then nothing. */
+	EXPECT_GT(r.ret, 0);
+	EXPECT_EQ(read(r.fd, buf, psz), -1);
+	EXPECT_EQ(errno, EIO);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 09/16] seq_file: add seq_preallocate()
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 08/16] selftests/proc: check that a faulting reader doesn't hold up removal Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 10/16] proc: let procfs own the seq_file of /proc/stat Christian Brauner
                     ` (6 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

single_open_size() allocates the buffer itself because it does the
single_open() too. A file whose seq_file is opened by somebody else,
like procfs does now, needs to do that after the fact. Add
seq_preallocate() for that and use it in single_open_size().

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/seq_file.c            | 36 ++++++++++++++++++++++++++++--------
 include/linux/seq_file.h |  1 +
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/fs/seq_file.c b/fs/seq_file.c
index 456c78719fd0..a4644cdda7e6 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -590,21 +590,41 @@ int single_open(struct file *file, int (*show)(struct seq_file *, void *),
 }
 EXPORT_SYMBOL(single_open);
 
-int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),
-		void *data, size_t size)
+/**
+ *	seq_preallocate -	start out with a buffer of @size
+ *	@m: the seq_file, before it is first read
+ *	@size: the size of the buffer
+ *
+ *	For files whose records don't fit into a page, so that the first read
+ *	doesn't have to work its way up to the right buffer size.
+ */
+int seq_preallocate(struct seq_file *m, size_t size)
 {
 	char *buf = seq_buf_alloc(size);
-	int ret;
+
 	if (!buf)
 		return -ENOMEM;
+	kvfree(m->buf);
+	m->buf = buf;
+	m->size = size;
+	return 0;
+}
+EXPORT_SYMBOL(seq_preallocate);
+
+int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),
+		void *data, size_t size)
+{
+	int ret;
+
 	ret = single_open(file, show, data);
-	if (ret) {
-		kvfree(buf);
+	if (ret)
 		return ret;
+	ret = seq_preallocate(file->private_data, size);
+	if (ret) {
+		single_release(file_inode(file), file);
+		file->private_data = NULL;
 	}
-	((struct seq_file *)file->private_data)->buf = buf;
-	((struct seq_file *)file->private_data)->size = size;
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL(single_open_size);
 
diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h
index b813b7e63d89..834daff48b7d 100644
--- a/include/linux/seq_file.h
+++ b/include/linux/seq_file.h
@@ -175,6 +175,7 @@ int seq_path_root(struct seq_file *m, const struct path *path,
 void *single_start(struct seq_file *, loff_t *);
 int single_open(struct file *, int (*)(struct seq_file *, void *), void *);
 int single_open_size(struct file *, int (*)(struct seq_file *, void *), void *, size_t);
+int seq_preallocate(struct seq_file *m, size_t size);
 int single_release(struct inode *, struct file *);
 void *__seq_open_private(struct file *, const struct seq_operations *, int);
 int seq_open_private(struct file *, const struct seq_operations *, int);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 10/16] proc: let procfs own the seq_file of /proc/stat
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 09/16] seq_file: add seq_preallocate() Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 11/16] proc: let procfs copy in writes before pinning the entry Christian Brauner
                     ` (5 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show function and let procfs open and free the seq_file.
->proc_open() only preallocates the buffer now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/stat.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/fs/proc/stat.c b/fs/proc/stat.c
index 79b42f2bfd00..3bfb79edc0f6 100644
--- a/fs/proc/stat.c
+++ b/fs/proc/stat.c
@@ -161,15 +161,13 @@ static int stat_open(struct inode *inode, struct file *file)
 
 	/* minimum size to display an interrupt count : 2 bytes */
 	size += 2 * irq_get_nr_irqs();
-	return single_open_size(file, show_stat, NULL, size);
+	return seq_preallocate(file->private_data, size);
 }
 
 static const struct proc_ops stat_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
+	.proc_show	= show_stat,
 	.proc_open	= stat_open,
-	.proc_read_iter	= seq_read_iter,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
 };
 
 static int __init proc_stat_init(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 11/16] proc: let procfs copy in writes before pinning the entry
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 10/16] proc: let procfs own the seq_file of /proc/stat Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 12/16] proc: copy in writes to the network namespace entries before pinning Christian Brauner
                     ` (4 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_reg_write() pins the entry and calls ->proc_write() which copies
from userspace itself. A page fault in that copy can take as long as
the writer wants, just like the copy to userspace of a read, and
remove_proc_entry() waits for it.

So do what kernfs does for writes as well. An entry that implements
->proc_write_buf() gets a NUL-terminated kernel copy of what was
written. procfs makes that copy before it pins the entry. The copy is
capped at ->proc_write_size bytes, or at PAGE_SIZE - 1 like
proc_simple_write() if the entry doesn't care. Larger writes fail with
-EINVAL. The proc_ops go away with the entry, so procfs remembers the
limit in the entry when it is registered, like the size of the seq_file
state, instead of pinning the entry just to look up a number.

Entries that take binary data in pieces, like the PCI config space,
ALSA's data entries or a firmware image, can ask for
PROC_ENTRY_SHORT_WRITES instead. procfs then copies in the first
->proc_write_size bytes and the write comes up short.

->proc_write_buf() gets the position like ->proc_write() so entries
that write at an offset keep working.

Note that procfs copies in the whole write before the entry looks at any
of it. So a write that is too large or whose buffer can't be read fails
before the entry's own checks run, permission checks included, even
where the entry only ever looked at the first few bytes.

An entry that implements ->proc_write_buf() and still has a
->proc_write() fails to register.

No users yet.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  8 ++++++++
 fs/proc/inode.c         | 33 +++++++++++++++++++++++++++++++++
 fs/proc/internal.h      | 11 +++++++++++
 include/linux/proc_fs.h | 17 +++++++++++++++++
 4 files changed, 69 insertions(+)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 6ed8bc313db0..7dc7df101286 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -468,6 +468,14 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 		if (proc_ops->proc_lseek)
 			pde->flags |= PROC_ENTRY_proc_lseek;
 	}
+	if (proc_ops->proc_write_buf) {
+		if (WARN_ON_ONCE(proc_ops->proc_write))
+			return -EINVAL;
+		pde->flags |= PROC_ENTRY_proc_write_buf;
+		pde->write_size = proc_ops->proc_write_size ?: PAGE_SIZE - 1;
+		if (proc_ops->proc_flags & PROC_ENTRY_SHORT_WRITES)
+			pde->flags |= PROC_ENTRY_SHORT_WRITES;
+	}
 	return 0;
 }
 
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 8425465aae5d..c0bb40e7ff96 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -440,11 +440,44 @@ static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const ch
 	return -EIO;
 }
 
+/*
+ * procfs copies in what is written before it pins the entry. A writer that
+ * faults on its buffer then doesn't hold up remove_proc_entry().
+ */
+static ssize_t pde_write_buf(struct proc_dir_entry *pde, struct file *file,
+			     const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	char *buf __free(kvfree) = NULL;
+	size_t size;
+	ssize_t rv;
+
+	size = pde->write_size;
+	if (count > size) {
+		if (!pde_has_short_writes(pde))
+			return -EINVAL;
+		count = size;
+	}
+	buf = kvmalloc(count + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	if (copy_from_user(buf, ubuf, count))
+		return -EFAULT;
+	buf[count] = '\0';
+
+	if (!pde_pin(pde))
+		return -EIO;
+	rv = pde->proc_ops->proc_write_buf(file, buf, count, ppos);
+	pde_unpin(pde);
+	return rv;
+}
+
 static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
 	ssize_t rv;
 
+	if (pde_has_proc_write_buf(pde))
+		return pde_write_buf(pde, file, buf, count, ppos);
 	if (!pde_pin(pde))
 		return -EIO;
 	rv = pde_write(pde, file, buf, count, ppos);
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index d8093451524a..80b346a3299d 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -51,6 +51,7 @@ struct proc_dir_entry {
 	proc_write_t write;
 	void *data;
 	unsigned int state_size;
+	unsigned int write_size;
 	unsigned int low_ino;
 	nlink_t nlink;
 	kuid_t uid;
@@ -107,6 +108,16 @@ static inline bool pde_has_proc_show(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_proc_show;
 }
 
+static inline bool pde_has_proc_write_buf(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_proc_write_buf;
+}
+
+static inline bool pde_has_short_writes(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_SHORT_WRITES;
+}
+
 /* procfs owns the seq_file, see pde_seq_ops */
 static inline bool pde_is_seq_file(const struct proc_dir_entry *pde)
 {
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index ea55ed85a2d6..c81c93325044 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -31,6 +31,13 @@ enum {
 	PROC_ENTRY_proc_show		= 1U << 5,
 
 	PROC_ENTRY_FORCE_LOOKUP		= 1U << 7,
+	PROC_ENTRY_proc_write_buf	= 1U << 8,
+
+	/*
+	 * Writes larger than ->proc_write_size come up short instead of
+	 * failing with -EINVAL.
+	 */
+	PROC_ENTRY_SHORT_WRITES		= 1U << 9,
 };
 
 struct proc_ops {
@@ -67,6 +74,16 @@ struct proc_ops {
 	const struct seq_operations *proc_seq_ops;
 	int	(*proc_show)(struct seq_file *, void *);
 	unsigned int proc_state_size;
+	/*
+	 * procfs copies what is written into a kernel buffer and calls
+	 * ->proc_write_buf() with it. The buffer is NUL-terminated and holds
+	 * at most ->proc_write_size bytes, PAGE_SIZE - 1 if that's 0. Larger
+	 * writes fail with -EINVAL unless the entry asks for
+	 * PROC_ENTRY_SHORT_WRITES. The position is NULL for a stream, like
+	 * for ->write().
+	 */
+	ssize_t	(*proc_write_buf)(struct file *, char *, size_t, loff_t *);
+	unsigned int proc_write_size;
 } __randomize_layout;
 
 /* definitions for hide_pid field */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 12/16] proc: copy in writes to the network namespace entries before pinning
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (10 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 11/16] proc: let procfs copy in writes before pinning the entry Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 13/16] proc: let procfs copy out reads that don't go through seq_file Christian Brauner
                     ` (3 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_simple_write() already hands ->write() a kernel copy of what was
written but makes that copy with the entry pinned. Let procfs make it
before it pins the entry instead. That covers everything created with
proc_create_net_data_write() and proc_create_net_single_write(), the
afs files and the rxrpc stats.

So a write to a proc_create_net_single() file without ->write() can
fail with -EINVAL or -EFAULT now before proc_simple_write() gets to
refuse it with -EACCES.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c  | 15 ++++-----------
 fs/proc/internal.h |  2 +-
 fs/proc/proc_net.c |  4 ++--
 3 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 7dc7df101286..83d273778a60 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -904,26 +904,19 @@ void proc_remove(struct proc_dir_entry *de)
 EXPORT_SYMBOL(proc_remove);
 
 /*
- * Pull a user buffer into memory and pass it to the file's write handler if
- * one is supplied.  The ->write() method is permitted to modify the
- * kernel-side buffer.
+ * Pass what was written to the file's write handler if one is supplied. The
+ * ->write() method is permitted to modify the buffer.
  */
-ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
-			  loff_t *_pos)
+ssize_t proc_simple_write(struct file *f, char *buf, size_t size, loff_t *_pos)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(f));
-	char *buf;
 	int ret;
 
 	if (!pde->write)
 		return -EACCES;
-	if (size == 0 || size > PAGE_SIZE - 1)
+	if (size == 0)
 		return -EINVAL;
-	buf = memdup_user_nul(ubuf, size);
-	if (IS_ERR(buf))
-		return PTR_ERR(buf);
 	ret = pde->write(f, buf, size);
-	kfree(buf);
 	return ret == 0 ? size : ret;
 }
 
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 80b346a3299d..ed22c6dfb0bf 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -317,7 +317,7 @@ static inline bool is_empty_pde(const struct proc_dir_entry *pde)
 {
 	return S_ISDIR(pde->mode) && !pde->proc_iops;
 }
-extern ssize_t proc_simple_write(struct file *, const char __user *, size_t, loff_t *);
+extern ssize_t proc_simple_write(struct file *, char *, size_t, loff_t *);
 
 /*
  * inode.c
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index fa485581d05e..cdeb2e025d2e 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -77,7 +77,7 @@ static int seq_release_net(struct inode *ino, struct file *f)
 
 static const struct proc_ops proc_net_seq_ops = {
 	.proc_open	= seq_open_net,
-	.proc_write	= proc_simple_write,
+	.proc_write_buf	= proc_simple_write,
 	.proc_release	= seq_release_net,
 };
 
@@ -186,7 +186,7 @@ static int single_release_net(struct inode *ino, struct file *f)
 
 static const struct proc_ops proc_net_single_ops = {
 	.proc_open	= single_open_net,
-	.proc_write	= proc_simple_write,
+	.proc_write_buf	= proc_simple_write,
 	.proc_release	= single_release_net,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 13/16] proc: let procfs copy out reads that don't go through seq_file
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (11 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 12/16] proc: copy in writes to the network namespace entries before pinning Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 14/16] fs/proc/vmcore: let procfs copy out reads Christian Brauner
                     ` (2 subsequent siblings)
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_reg_read() and proc_reg_read_iter() pin the entry and call
->proc_read() or ->proc_read_iter() which copy to userspace
themselves. Like for seq_file reads and for writes, a reader can make
a page fault in that copy take as long as it wants and
remove_proc_entry() waits for it.

So do what kernfs does for these reads as well. An entry that implements
->proc_read_buf() fills a kernel buffer of at most ->proc_read_size
bytes, a page if the entry doesn't care, and procfs copies it to
userspace after unpinning the entry. The entry moves the position like
it did for ->proc_read() but that only sticks if the copy to userspace
worked, like in kernfs_file_read_iter(). If the copy fails halfway the
read fails with -EFAULT and the position stays, where
simple_read_from_buffer() returned what it had copied until then.
procfs remembers ->proc_read_size in the entry like ->proc_write_size.

Larger reads come up short, like with kernfs. An entry whose readers
want all of a large file in one read asks for PROC_ENTRY_FULL_READS and
procfs fills the read in pieces of ->proc_read_size, unpinning the entry
in between. A piece whose copy fails ends the read with what the pieces
before it returned. kexec-tools treats any short read of /proc/vmcore
as fatal for example (cf. [1]).

These entries are read through ->read_iter() so readv(), splice(), aio,
the RWF flags, kernel_read() and io_uring work for them like for the
->proc_read_iter() ones. io_uring's nonblocking attempt gets -EAGAIN
though, like loop_rw_iter() gave it for ->proc_read() entries. Neither
the allocation nor the entry can honor IOCB_NOWAIT, and io_uring would
take a short read of a record file for an unfinished one and glue the
next record to it. So it retries from a worker like before.

An entry that implements ->proc_read_buf() and also declares a seq_file
or one of the other read methods fails to register.

No users yet.

[1]: https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/tree/util_lib/elf_info.c

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  9 ++++++++
 fs/proc/inode.c         | 60 ++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/proc/internal.h      | 11 +++++++++
 include/linux/proc_fs.h | 16 +++++++++++++
 4 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 83d273778a60..fc4d10c92d7c 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -476,6 +476,15 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 		if (proc_ops->proc_flags & PROC_ENTRY_SHORT_WRITES)
 			pde->flags |= PROC_ENTRY_SHORT_WRITES;
 	}
+	if (proc_ops->proc_read_buf) {
+		if (WARN_ON_ONCE(pde_is_seq_file(pde) || proc_ops->proc_read ||
+				 proc_ops->proc_read_iter))
+			return -EINVAL;
+		pde->flags |= PROC_ENTRY_proc_read_buf;
+		pde->read_size = proc_ops->proc_read_size ?: PAGE_SIZE;
+		if (proc_ops->proc_flags & PROC_ENTRY_FULL_READS)
+			pde->flags |= PROC_ENTRY_FULL_READS;
+	}
 	return 0;
 }
 
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index c0bb40e7ff96..3f29a1663d26 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -23,6 +23,8 @@
 #include <linux/sysctl.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
+#include <linux/sched/signal.h>
+#include <linux/uio.h>
 #include <linux/mount.h>
 #include <linux/bug.h>
 
@@ -400,11 +402,67 @@ static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)
 	return rv;
 }
 
+/*
+ * The entry fills a kernel buffer and procfs copies that to userspace after
+ * unpinning the entry. A reader that faults on its buffer then doesn't hold
+ * up remove_proc_entry().
+ */
+static ssize_t pde_read_buf(struct proc_dir_entry *pde, struct kiocb *iocb,
+			    struct iov_iter *iter)
+{
+	struct file *file = iocb->ki_filp;
+	char *buf __free(kvfree) = NULL;
+	ssize_t rv, done = 0;
+	size_t len, count;
+	loff_t pos;
+
+	if (!iov_iter_count(iter))
+		return 0;
+	/* neither the allocation nor the entry can honor IOCB_NOWAIT */
+	if ((iocb->ki_flags & IOCB_NOWAIT) && !(file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+	len = min_t(size_t, iov_iter_count(iter), pde->read_size);
+	buf = kvmalloc(len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* PROC_ENTRY_FULL_READS fills the read in pieces */
+	do {
+		count = min_t(size_t, iov_iter_count(iter), len);
+		pos = iocb->ki_pos;
+		if (!pde_pin(pde)) {
+			rv = -EIO;
+			break;
+		}
+		rv = pde->proc_ops->proc_read_buf(file, buf, count, &pos);
+		pde_unpin(pde);
+		if (rv <= 0)
+			break;
+		if (WARN_ON_ONCE(rv > count)) {
+			rv = -EIO;
+			break;
+		}
+		if (copy_to_iter(buf, rv, iter) != rv) {
+			rv = -EFAULT;
+			break;
+		}
+		iocb->ki_pos = pos;
+		done += rv;
+		if (!pde_has_full_reads(pde) || rv < count)
+			break;
+		cond_resched();
+	} while (iov_iter_count(iter) && !fatal_signal_pending(current));
+
+	return done ?: rv;
+}
+
 static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp));
 	ssize_t ret;
 
+	if (pde_has_proc_read_buf(pde))
+		return pde_read_buf(pde, iocb, iter);
 	if (!pde_pin(pde))
 		return -EIO;
 	ret = pde->proc_ops->proc_read_iter(iocb, iter);
@@ -814,7 +872,7 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 		inode->i_op = de->proc_iops;
 		if (pde_is_seq_file(de))
 			inode->i_fop = &proc_seq_file_ops;
-		else if (pde_has_proc_read_iter(de))
+		else if (pde_has_proc_read_iter(de) || pde_has_proc_read_buf(de))
 			inode->i_fop = &proc_iter_file_ops;
 		else
 			inode->i_fop = &proc_reg_file_ops;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index ed22c6dfb0bf..11bd3fa07705 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -52,6 +52,7 @@ struct proc_dir_entry {
 	void *data;
 	unsigned int state_size;
 	unsigned int write_size;
+	unsigned int read_size;
 	unsigned int low_ino;
 	nlink_t nlink;
 	kuid_t uid;
@@ -118,6 +119,16 @@ static inline bool pde_has_short_writes(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_SHORT_WRITES;
 }
 
+static inline bool pde_has_proc_read_buf(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_proc_read_buf;
+}
+
+static inline bool pde_has_full_reads(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_FULL_READS;
+}
+
 /* procfs owns the seq_file, see pde_seq_ops */
 static inline bool pde_is_seq_file(const struct proc_dir_entry *pde)
 {
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index c81c93325044..0f0997898ba3 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -38,6 +38,13 @@ enum {
 	 * failing with -EINVAL.
 	 */
 	PROC_ENTRY_SHORT_WRITES		= 1U << 9,
+	PROC_ENTRY_proc_read_buf	= 1U << 10,
+
+	/*
+	 * Reads larger than ->proc_read_size are filled in pieces instead of
+	 * coming up short.
+	 */
+	PROC_ENTRY_FULL_READS		= 1U << 11,
 };
 
 struct proc_ops {
@@ -84,6 +91,15 @@ struct proc_ops {
 	 */
 	ssize_t	(*proc_write_buf)(struct file *, char *, size_t, loff_t *);
 	unsigned int proc_write_size;
+	/*
+	 * procfs calls ->proc_read_buf() to fill a kernel buffer of at most
+	 * ->proc_read_size bytes, PAGE_SIZE if that's 0, and copies it to
+	 * userspace afterwards. Larger reads come up short unless the entry
+	 * asks for PROC_ENTRY_FULL_READS. The position the entry moves to only
+	 * sticks if the copy works.
+	 */
+	ssize_t	(*proc_read_buf)(struct file *, char *, size_t, loff_t *);
+	unsigned int proc_read_size;
 } __randomize_layout;
 
 /* definitions for hide_pid field */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 14/16] fs/proc/vmcore: let procfs copy out reads
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (12 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 13/16] proc: let procfs copy out reads that don't go through seq_file Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 15/16] proc: let procfs copy ioctl arguments in and out Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 16/16] Documentation: seq_file: describe how a /proc file uses seq_file Christian Brauner
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry. __read_vmcore() fills procfs' buffer
through a kvec iov_iter. So a read goes through that buffer now instead
of straight from the old memory into the reader's, which is what
splice() did already. mmap() isn't affected. On ppc64 a machine check
while reading the old memory used to be recovered through the exception
table of copy_to_user(), by accident. copy_oldmem_page() copies with
_copy_mc_to_iter() now, so a read through procfs' buffer recovers from
it as well.

The buffer is 128k, which keeps the entry pinned for one such piece at
a time, and /proc/vmcore asks for PROC_ENTRY_FULL_READS so that a
larger read is filled in pieces. kexec-tools treats a short read of it
as fatal. A read that faults on its buffer in a later piece returns the
pieces before it now instead of failing with -EFAULT. fadump removes
/proc/vmcore when its memory is released and that no longer waits for a
reader that faults on its buffer.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/vmcore.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index ccb795b85509..1c1e71231f4e 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -27,6 +27,7 @@
 #include <linux/mutex.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
+#include <linux/sizes.h>
 #include <linux/uio.h>
 #include <linux/cc_platform.h>
 #include <asm/io.h>
@@ -427,9 +428,14 @@ static ssize_t __read_vmcore(struct iov_iter *iter, loff_t *fpos)
 	return acc;
 }
 
-static ssize_t read_vmcore(struct kiocb *iocb, struct iov_iter *iter)
+static ssize_t read_vmcore(struct file *file, char *buf, size_t count,
+			   loff_t *ppos)
 {
-	return __read_vmcore(iter, &iocb->ki_pos);
+	struct kvec kvec = { .iov_base = buf, .iov_len = count };
+	struct iov_iter iter;
+
+	iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count);
+	return __read_vmcore(&iter, ppos);
 }
 
 /**
@@ -709,9 +715,11 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
 #endif
 
 static const struct proc_ops vmcore_proc_ops = {
+	.proc_flags	= PROC_ENTRY_FULL_READS,
 	.proc_open	= open_vmcore,
 	.proc_release	= release_vmcore,
-	.proc_read_iter	= read_vmcore,
+	.proc_read_buf	= read_vmcore,
+	.proc_read_size	= SZ_128K,
 	.proc_lseek	= default_llseek,
 	.proc_mmap	= mmap_vmcore,
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 15/16] proc: let procfs copy ioctl arguments in and out
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (13 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 14/16] fs/proc/vmcore: let procfs copy out reads Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  2026-11-02 14:48   ` [PATCH RFC 16/16] Documentation: seq_file: describe how a /proc file uses seq_file Christian Brauner
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

proc_reg_unlocked_ioctl() pins the entry and calls ->proc_ioctl() which
copies its argument from and to userspace itself. Like for reads and
writes, an ioctl can make a page fault in those copies take as long as
it wants and remove_proc_entry() waits for it.

The command number doesn't reliably say what an ioctl copies. Some of
the procfs ones aren't encoded at all, like the PCI ones or esas2r's,
and i8k's encode the wrong sizes. So do what DRM does instead and list
what each command copies. An entry that implements ->proc_ioctl_buf()
lists its commands in ->proc_ioctl_args with the number of bytes that
go in and that come back out. procfs copies the argument in before it
pins the entry, calls ->proc_ioctl_buf() with that kernel copy and
copies it back out after unpinning the entry if the ioctl succeeded. A
command that copies neither gets its argument as is, a command that
isn't in the table fails with -ENOTTY. PROC_IOCTL_IOC() fills in an
entry from a command that encodes its argument.

Note that, like for writes, procfs copies the argument in before the
entry's own checks run, permission checks included. So a bad pointer
fails with -EFAULT where the entry used to say -EPERM first.

The table lives with the proc_ops, which go away with the entry, so
procfs pins the entry to look the command up and lets go of it again
before it copies anything. 32-bit tasks go through the same table with
their pointer converted by compat_ptr(). An entry that implements
->proc_ioctl_buf() and still has a ->proc_ioctl() fails to register, and
so does one that lists commands without implementing it or implements
it without listing any.

No users yet.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       | 12 ++++++++++
 fs/proc/inode.c         | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/proc/internal.h      |  5 +++++
 include/linux/proc_fs.h | 35 +++++++++++++++++++++++++++++
 4 files changed, 110 insertions(+)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index fc4d10c92d7c..35dd745a0357 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -485,6 +485,18 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 		if (proc_ops->proc_flags & PROC_ENTRY_FULL_READS)
 			pde->flags |= PROC_ENTRY_FULL_READS;
 	}
+	if (proc_ops->proc_ioctl_buf) {
+		if (WARN_ON_ONCE(proc_ops->proc_ioctl))
+			return -EINVAL;
+		/* the table says what procfs copies, see pde_ioctl_arg() */
+		if (WARN_ON_ONCE(!proc_ops->proc_ioctl_args ||
+				 !proc_ops->proc_nr_ioctl_args))
+			return -EINVAL;
+		pde->flags |= PROC_ENTRY_proc_ioctl_buf;
+	} else if (WARN_ON_ONCE(proc_ops->proc_ioctl_args ||
+				proc_ops->proc_nr_ioctl_args)) {
+		return -EINVAL;
+	}
 	return 0;
 }
 
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 3f29a1663d26..7e4ca5751abb 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/cache.h>
+#include <linux/compat.h>
 #include <linux/time.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
@@ -571,11 +572,66 @@ static long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned in
 	return -ENOTTY;
 }
 
+/* the table lives in proc_ops, which goes away with the entry */
+static bool pde_ioctl_arg(struct proc_dir_entry *pde, unsigned int cmd,
+			  struct proc_ioctl_arg *arg)
+{
+	const struct proc_ops *proc_ops = pde->proc_ops;
+	bool found = false;
+
+	if (!pde_pin(pde))
+		return false;
+	for (unsigned int i = 0; i < proc_ops->proc_nr_ioctl_args; i++) {
+		if (proc_ops->proc_ioctl_args[i].cmd == cmd) {
+			*arg = proc_ops->proc_ioctl_args[i];
+			found = true;
+			break;
+		}
+	}
+	pde_unpin(pde);
+	return found;
+}
+
+/*
+ * procfs copies the argument in before it pins the entry and copies it back
+ * out after unpinning it. An ioctl that faults on its argument then doesn't
+ * hold up remove_proc_entry().
+ */
+static long pde_ioctl_buf(struct proc_dir_entry *pde, struct file *file,
+			  unsigned int cmd, void __user *uarg, unsigned long arg)
+{
+	void *buf __free(kvfree) = NULL;
+	struct proc_ioctl_arg a;
+	size_t size;
+	long rv;
+
+	if (!pde_ioctl_arg(pde, cmd, &a))
+		return -ENOTTY;
+	size = max(a.in, a.out);
+	if (size) {
+		buf = kvzalloc(size, GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+		if (copy_from_user(buf, uarg, a.in))
+			return -EFAULT;
+	}
+
+	if (!pde_pin(pde))
+		return -ENOTTY;
+	rv = pde->proc_ops->proc_ioctl_buf(file, cmd, buf ?: (void *)arg);
+	pde_unpin(pde);
+	if (rv >= 0 && copy_to_user(uarg, buf, a.out))
+		return -EFAULT;
+	return rv;
+}
+
 static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
 	long rv;
 
+	if (pde_has_proc_ioctl_buf(pde))
+		return pde_ioctl_buf(pde, file, cmd, (void __user *)arg, arg);
 	if (!pde_pin(pde))
 		return -ENOTTY;
 	rv = pde_ioctl(pde, file, cmd, arg);
@@ -597,6 +653,8 @@ static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned
 	struct proc_dir_entry *pde = PDE(file_inode(file));
 	long rv;
 
+	if (pde_has_proc_ioctl_buf(pde))
+		return pde_ioctl_buf(pde, file, cmd, compat_ptr(arg), arg);
 	if (!pde_pin(pde))
 		return -ENOTTY;
 	rv = pde_compat_ioctl(pde, file, cmd, arg);
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 11bd3fa07705..93d20995b6fe 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -129,6 +129,11 @@ static inline bool pde_has_full_reads(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_FULL_READS;
 }
 
+static inline bool pde_has_proc_ioctl_buf(const struct proc_dir_entry *pde)
+{
+	return pde->flags & PROC_ENTRY_proc_ioctl_buf;
+}
+
 /* procfs owns the seq_file, see pde_seq_ops */
 static inline bool pde_is_seq_file(const struct proc_dir_entry *pde)
 {
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 0f0997898ba3..2ba4e4a39e13 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -8,6 +8,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/fs.h>
+#include <linux/ioctl.h>
 
 struct proc_dir_entry;
 struct seq_file;
@@ -45,8 +46,29 @@ enum {
 	 * coming up short.
 	 */
 	PROC_ENTRY_FULL_READS		= 1U << 11,
+	PROC_ENTRY_proc_ioctl_buf	= 1U << 12,
 };
 
+/*
+ * How many bytes of the argument of @cmd procfs copies in before calling
+ * ->proc_ioctl_buf() and copies back out after it. A command that copies
+ * neither gets its argument as is.
+ */
+struct proc_ioctl_arg {
+	unsigned int cmd;
+	unsigned int in;
+	unsigned int out;
+};
+
+#define PROC_IOCTL_ARG(_cmd, _in, _out) \
+	{ .cmd = (_cmd), .in = (_in), .out = (_out) }
+
+/* A command that encodes the size and direction of its argument. */
+#define PROC_IOCTL_IOC(_cmd)						\
+	PROC_IOCTL_ARG(_cmd,						\
+		       (_IOC_DIR(_cmd) & _IOC_WRITE) ? _IOC_SIZE(_cmd) : 0, \
+		       (_IOC_DIR(_cmd) & _IOC_READ) ? _IOC_SIZE(_cmd) : 0)
+
 struct proc_ops {
 	unsigned int proc_flags;
 	int	(*proc_open)(struct inode *, struct file *);
@@ -100,6 +122,19 @@ struct proc_ops {
 	 */
 	ssize_t	(*proc_read_buf)(struct file *, char *, size_t, loff_t *);
 	unsigned int proc_read_size;
+	/*
+	 * procfs looks the command up in ->proc_ioctl_args, copies in what it
+	 * passes before pinning the entry, calls ->proc_ioctl_buf() with that
+	 * copy and copies out what it returns after unpinning the entry.
+	 * Commands that aren't in the table fail with -ENOTTY. 32-bit tasks
+	 * go through the same table. The copy is zeroed and as large as the
+	 * larger of the two sizes, it only goes back out if the ioctl
+	 * succeeded, and a command that copies neither gets its argument as
+	 * is.
+	 */
+	long	(*proc_ioctl_buf)(struct file *, unsigned int, void *);
+	const struct proc_ioctl_arg *proc_ioctl_args;
+	unsigned int proc_nr_ioctl_args;
 } __randomize_layout;
 
 /* definitions for hide_pid field */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 16/16] Documentation: seq_file: describe how a /proc file uses seq_file
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
                     ` (14 preceding siblings ...)
  2026-11-02 14:48   ` [PATCH RFC 15/16] proc: let procfs copy ioctl arguments in and out Christian Brauner
@ 2026-11-02 14:48   ` Christian Brauner
  15 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 14:48 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

The example in seq_file.rst hands proc_create() file operations built
from an open method that calls seq_open() and from seq_read(),
seq_lseek() and seq_release(). procfs doesn't work that way anymore. It
opens and releases the seq_file itself when the entry declares its
seq_operations or its show function in struct proc_ops. Say so, and
point the extra-simple version at ->proc_show() for /proc files.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 Documentation/filesystems/seq_file.rst | 42 ++++++++++++++++++++++++++--------
 1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/Documentation/filesystems/seq_file.rst b/Documentation/filesystems/seq_file.rst
index d753d8177bcb..8cfb5fdabe99 100644
--- a/Documentation/filesystems/seq_file.rst
+++ b/Documentation/filesystems/seq_file.rst
@@ -68,7 +68,7 @@ kernel 3.10. Current versions require the following update::
     -	entry = create_proc_entry("sequence", 0, NULL);
     -	if (entry)
     -		entry->proc_fops = &ct_file_ops;
-    +	entry = proc_create("sequence", 0, NULL, &ct_file_ops);
+    +	entry = proc_create("sequence", 0, NULL, &ct_proc_ops);
 
 The iterator interface
 ======================
@@ -277,9 +277,10 @@ seq_file system, but we have not yet turned them into a file that a user
 can see. Creating a file within the kernel requires, of course, the
 creation of a set of file_operations which implement the operations on that
 file. The seq_file interface provides a set of canned operations which do
-most of the work. The virtual file author still must implement the open()
-method, however, to hook everything up. The open function is often a single
-line, as in the example module::
+most of the work. A file in /proc is special, procfs hooks the seq_file up
+itself, see below. Everywhere else the virtual file author still must
+implement the open() method to do that. The open function is often a
+single line::
 
 	static int ct_open(struct inode *inode, struct file *file)
 	{
@@ -343,19 +344,38 @@ file_operations structure will look like::
 There is also a seq_release_private() which passes the contents of the
 seq_file private field to kfree() before releasing the structure.
 
-The final step is the creation of the /proc file itself. In the example
-code, that is done in the initialization code in the usual way::
+The final step is the creation of the file itself. A file in /proc is
+special here: procfs opens and releases the seq_file itself, so there is
+no open method and no file_operations for it. The entry declares its
+seq_operations in struct proc_ops instead, and the size of a private
+block in proc_state_size if it wants procfs to allocate one like
+seq_open_private() would::
+
+	static const struct proc_ops ct_proc_ops = {
+	        .proc_seq_ops = &ct_seq_ops,
+	};
 
 	static int ct_init(void)
 	{
-	        struct proc_dir_entry *entry;
-
-	        proc_create("sequence", 0, NULL, &ct_file_ops);
+	        proc_create("sequence", 0, NULL, &ct_proc_ops);
 	        return 0;
 	}
 
 	module_init(ct_init);
 
+A ->proc_open() method of such an entry finds the seq_file in
+file->private_data and may set up more state; ->proc_release() releases
+only that. It runs when the entry is removed, which can be while a reader
+is still copying out of the seq_file, so it must leave the seq_file
+alone. procfs only calls into the entry between start() and stop() with
+the entry pinned, so nothing that runs there may wait for something a
+reader can hold while it copies to userspace, like the seq_file's lock.
+start() and next() may return ERR_PTR(-EIO) to say that the entry is
+gone; procfs unpins it right away then. An entry that wants to start out
+with a larger buffer, like single_open_size() gives, calls
+seq_preallocate() from ->proc_open(). The file_operations above are what
+a file elsewhere, in debugfs for example, uses.
+
 And that is pretty much it.
 
 
@@ -394,3 +414,7 @@ value given to single_open() can be found in the private field of the
 seq_file structure. When using single_open(), the programmer should use
 single_release() instead of seq_release() in the file_operations structure
 to avoid a memory leak.
+
+A file in /proc puts the show() function into the proc_show field of its
+struct proc_ops instead and procfs does the rest, with the entry's data in
+the private field of the seq_file structure.

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 02/38] PNP: isapnp: don't read past the end of the buffer on a large pread()
  2026-11-02 14:21   ` [PATCH RFC 02/38] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
@ 2026-11-02 14:51     ` Rafael J. Wysocki (Intel)
  0 siblings, 0 replies; 160+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-11-02 14:51 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-fsdevel, Linus Torvalds, Jann Horn, Josef Bacik,
	Chris Mason, Jeff Layton, NeilBrown, Eric Dumazet,
	Thomas Gleixner, Al Viro, Jonathan Corbet, Christoph Hellwig,
	Greg Kroah-Hartman, Joel Granados, Kees Cook, Jan Kara, Jan Kara,
	linux-kernel, linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi, linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers, Chuck Lever, stable

On Mon, Nov 2, 2026 at 3:22 PM Christian Brauner <brauner@kernel.org> wrote:
>
> isapnp_proc_bus_read() truncates the position to an int. A pread() at
> an offset like 0xfffffc00 turns it negative, the check against the size
> of the configuration space passes, and the loop then puts everything
> from that position up to byte 256 into the reader's buffer, no matter
> how few bytes were asked for.
>
> Keep the position as a loff_t and clamp the count against what is left
> of the configuration space.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>

> ---
>  drivers/pnp/isapnp/proc.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c
> index c1f6e6bd0eb2..8197c7f29743 100644
> --- a/drivers/pnp/isapnp/proc.c
> +++ b/drivers/pnp/isapnp/proc.c
> @@ -23,14 +23,12 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
>                                     size_t nbytes, loff_t * ppos)
>  {
>         struct pnp_dev *dev = pde_data(file_inode(file));
> -       int pos = *ppos;
> +       loff_t pos = *ppos;
>         int cnt, size = 256;
>
>         if (pos >= size)
>                 return 0;
> -       if (nbytes >= size)
> -               nbytes = size;
> -       if (pos + nbytes > size)
> +       if (nbytes > size - pos)
>                 nbytes = size - pos;
>         cnt = nbytes;
>
>
> --
> 2.53.0
>
>

^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 00/22] proc: convert the arch and platform entries
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
  2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
  2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
@ 2026-11-02 15:01 ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 01/22] alpha: srm_env: let procfs copy in writes Christian Brauner
                     ` (21 more replies)
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                   ` (3 subsequent siblings)
  6 siblings, 22 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

This converts the proc entries under arch/ and the ones of ACPI, the
platform drivers, via-pmu and PNP to what "proc: let procfs own the
data path" set up: procfs owns the seq_file, copies in writes, copies
out reads and copies ioctl arguments in and out, so the entries' code
only ever runs with the entry pinned and never touches userspace
memory.

One patch per subsystem and method, in the order seq_file, write, read,
ioctl. Most of them just stop copying in or out themselves, or switch
from kstrto*_from_user() to kstrto*() and from simple_read_from_buffer()
to memory_read_from_buffer(). Where a write that is larger than
anything the entry parses now fails with -EINVAL from procfs I've called
that out in the commit. The rtas flash files take their image in 4k
pieces and keep doing that with PROC_ENTRY_SHORT_WRITES, the rtas error
log is a stream and hands out a record before procfs copies it out, so
a reader that passes a bad buffer loses it. /proc/mtrr's ioctls are
table driven now, the native and the 32-bit commands share one table.

Build tested per commit on x86_64 and with W=1 on the touched objects on
alpha, arm, m68k, powerpc, sh, sparc, um, x86 and xtensa. The entries I
could get at in an x86 VM behave the same with and without the series.

This is on top of "proc: let procfs own the data path". The other three
conversion series don't depend on it and it doesn't depend on them.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (22):
      alpha: srm_env: let procfs copy in writes
      ARM: alignment: let procfs copy in writes
      ARM: atags: let procfs copy out reads of /proc/atags
      m68k/bootinfo: let procfs copy out reads of /proc/bootinfo
      powerpc/rtas: let procfs copy in writes
      powerpc/rtas: let procfs copy out reads
      powerpc/pseries: let procfs copy in writes
      powerpc/procfs: let procfs copy out reads of /proc/powerpc/systemcfg
      sh: alignment: let procfs copy in writes
      sparc: led: let procfs copy in writes
      um: let procfs copy in writes
      x86/mtrr: let procfs own the seq_file of /proc/mtrr
      x86/mtrr: let procfs copy in writes to /proc/mtrr
      x86/mtrr: let procfs copy the ioctl arguments of /proc/mtrr
      xtensa: simdisk: let procfs copy in writes
      xtensa: simdisk: let procfs copy out reads
      ACPI: proc: let procfs copy in writes to /proc/acpi/wakeup
      macintosh/via-pmu: let procfs copy in writes to the options file
      platform/x86: thinkpad_acpi: let procfs copy in writes
      platform/x86: toshiba_acpi: let procfs copy in writes
      PNP: pnpbios: let procfs copy in writes
      PNP: isapnp: let procfs copy out reads

 arch/alpha/kernel/srm_env.c                 | 12 +---
 arch/arm/kernel/atags_proc.c                |  6 +-
 arch/arm/mm/alignment.c                     |  7 +--
 arch/m68k/kernel/bootinfo_proc.c            |  6 +-
 arch/powerpc/kernel/proc_powerpc.c          |  6 +-
 arch/powerpc/kernel/rtas-proc.c             | 40 ++++++------
 arch/powerpc/kernel/rtas_flash.c            | 62 +++++++++----------
 arch/powerpc/kernel/rtasd.c                 | 19 ++----
 arch/powerpc/platforms/pseries/lpar.c       | 14 ++---
 arch/powerpc/platforms/pseries/lparcfg.c    |  7 +--
 arch/powerpc/platforms/pseries/reconfig.c   | 22 +++----
 arch/sh/mm/alignment.c                      |  7 +--
 arch/sparc/kernel/led.c                     | 13 +---
 arch/um/drivers/mconsole_kern.c             | 11 +---
 arch/um/kernel/exitcode.c                   | 14 ++---
 arch/x86/kernel/cpu/mtrr/if.c               | 95 ++++++++++++++++-------------
 arch/xtensa/platforms/iss/simdisk.c         | 32 ++++------
 drivers/acpi/proc.c                         |  7 +--
 drivers/macintosh/via-pmu.c                 |  7 +--
 drivers/platform/x86/lenovo/thinkpad_acpi.c | 14 +----
 drivers/platform/x86/toshiba_acpi.c         | 36 ++++-------
 drivers/pnp/isapnp/proc.c                   | 20 +++---
 drivers/pnp/pnpbios/proc.c                  |  9 +--
 23 files changed, 193 insertions(+), 273 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261101-work-procfs-arch-8d4ade48be31
prerequisite-change-id: 20261101-work-procfs-core-8fa4a4f412cf:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 01/22] alpha: srm_env: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 02/22] ARM: alignment: " Christian Brauner
                     ` (20 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/srm_environment/* before it pins the entry, and hand that copy to
the console callback through the page it used before.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/alpha/kernel/srm_env.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c
index faafc2df7886..6a1742b09a82 100644
--- a/arch/alpha/kernel/srm_env.c
+++ b/arch/alpha/kernel/srm_env.c
@@ -81,7 +81,7 @@ static int srm_env_proc_show(struct seq_file *m, void *v)
 	return ret;
 }
 
-static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
+static ssize_t srm_env_proc_write(struct file *file, char *buffer,
 				  size_t count, loff_t *pos)
 {
 	int res;
@@ -92,13 +92,8 @@ static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
 	if (!buf)
 		return -ENOMEM;
 
-	res = -EINVAL;
-	if (count >= PAGE_SIZE)
-		goto out;
-
 	res = -EFAULT;
-	if (copy_from_user(buf, buffer, count))
-		goto out;
+	memcpy(buf, buffer, count);
 	buf[count] = '\0';
 
 	ret1 = callback_setenv(id, buf, count);
@@ -109,14 +104,13 @@ static ssize_t srm_env_proc_write(struct file *file, const char __user *buffer,
 		res = (int) ret1;
 	}
 
- out:
 	free_page((unsigned long)buf);
 	return res;
 }
 
 static const struct proc_ops srm_env_proc_ops = {
 	.proc_show	= srm_env_proc_show,
-	.proc_write	= srm_env_proc_write,
+	.proc_write_buf	= srm_env_proc_write,
 };
 
 static int __init

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 02/22] ARM: alignment: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 01/22] alpha: srm_env: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 03/22] ARM: atags: let procfs copy out reads of /proc/atags Christian Brauner
                     ` (19 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/cpu/alignment before it pins the entry, and take the mode from
that copy. Writes larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/arm/mm/alignment.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 3e5e30968634..11ceefdfa388 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -144,14 +144,13 @@ static int alignment_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t alignment_proc_write(struct file *file, const char __user *buffer,
+static ssize_t alignment_proc_write(struct file *file, char *buffer,
 				    size_t count, loff_t *pos)
 {
 	char mode;
 
 	if (count > 0) {
-		if (get_user(mode, buffer))
-			return -EFAULT;
+		mode = buffer[0];
 		if (mode >= '0' && mode <= '5')
 			ai_usermode = safe_usermode(mode - '0', true);
 	}
@@ -160,7 +159,7 @@ static ssize_t alignment_proc_write(struct file *file, const char __user *buffer
 
 static const struct proc_ops alignment_proc_ops = {
 	.proc_show	= alignment_proc_show,
-	.proc_write	= alignment_proc_write,
+	.proc_write_buf	= alignment_proc_write,
 };
 #endif /* CONFIG_PROC_FS */
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 03/22] ARM: atags: let procfs copy out reads of /proc/atags
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 01/22] alpha: srm_env: let procfs copy in writes Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 02/22] ARM: alignment: " Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 04/22] m68k/bootinfo: let procfs copy out reads of /proc/bootinfo Christian Brauner
                     ` (18 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/arm/kernel/atags_proc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/kernel/atags_proc.c b/arch/arm/kernel/atags_proc.c
index 8411fbfbcb8b..ff28bf24dd30 100644
--- a/arch/arm/kernel/atags_proc.c
+++ b/arch/arm/kernel/atags_proc.c
@@ -10,15 +10,15 @@ struct buffer {
 	char data[] __counted_by(size);
 };
 
-static ssize_t atags_read(struct file *file, char __user *buf,
+static ssize_t atags_read(struct file *file, char *buf,
 			  size_t count, loff_t *ppos)
 {
 	struct buffer *b = pde_data(file_inode(file));
-	return simple_read_from_buffer(buf, count, ppos, b->data, b->size);
+	return memory_read_from_buffer(buf, count, ppos, b->data, b->size);
 }
 
 static const struct proc_ops atags_proc_ops = {
-	.proc_read	= atags_read,
+	.proc_read_buf	= atags_read,
 	.proc_lseek	= default_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 04/22] m68k/bootinfo: let procfs copy out reads of /proc/bootinfo
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 03/22] ARM: atags: let procfs copy out reads of /proc/atags Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 05/22] powerpc/rtas: let procfs copy in writes Christian Brauner
                     ` (17 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/m68k/kernel/bootinfo_proc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/m68k/kernel/bootinfo_proc.c b/arch/m68k/kernel/bootinfo_proc.c
index 857fa2aaa748..4936cf44e86a 100644
--- a/arch/m68k/kernel/bootinfo_proc.c
+++ b/arch/m68k/kernel/bootinfo_proc.c
@@ -19,15 +19,15 @@ static char bootinfo_tmp[1536] __initdata;
 static void *bootinfo_copy;
 static size_t bootinfo_size;
 
-static ssize_t bootinfo_read(struct file *file, char __user *buf,
+static ssize_t bootinfo_read(struct file *file, char *buf,
 			  size_t count, loff_t *ppos)
 {
-	return simple_read_from_buffer(buf, count, ppos, bootinfo_copy,
+	return memory_read_from_buffer(buf, count, ppos, bootinfo_copy,
 				       bootinfo_size);
 }
 
 static const struct proc_ops bootinfo_proc_ops = {
-	.proc_read	= bootinfo_read,
+	.proc_read_buf	= bootinfo_read,
 	.proc_lseek	= default_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 05/22] powerpc/rtas: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 04/22] m68k/bootinfo: let procfs copy out reads of /proc/bootinfo Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 06/22] powerpc/rtas: let procfs copy out reads Christian Brauner
                     ` (16 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the files in /proc/powerpc/rtas and
/proc/ppc64/rtas so procfs copies in what is written before it pins the
entry, and parse what was written from that copy.

firmware_flash, firmware_update and validate_flash take an image in 4k
pieces and relied on writes coming up short. They keep doing that with
PROC_ENTRY_SHORT_WRITES. Writes to the other files that are larger than
PAGE_SIZE - 1 bytes fail with -EINVAL now.

validate_flash only keeps the first 4k of the image and used to swallow
whatever was written beyond that in one go. It takes at most 4k per
write now, like for the part it keeps, so a writer has to loop there
like it has to for the image itself.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/rtas-proc.c  | 40 ++++++++++++++++++----------------------
 arch/powerpc/kernel/rtas_flash.c | 38 +++++++++++++++++---------------------
 2 files changed, 35 insertions(+), 43 deletions(-)

diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index ebf416840b3c..9598866f11cb 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -138,45 +138,45 @@ static unsigned long rtas_tone_volume = 0;
 static int ppc_rtas_sensors_show(struct seq_file *m, void *v);
 static int ppc_rtas_clock_show(struct seq_file *m, void *v);
 static ssize_t ppc_rtas_clock_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos);
+		char *buf, size_t count, loff_t *ppos);
 static int ppc_rtas_progress_show(struct seq_file *m, void *v);
 static ssize_t ppc_rtas_progress_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos);
+		char *buf, size_t count, loff_t *ppos);
 static int ppc_rtas_poweron_show(struct seq_file *m, void *v);
 static ssize_t ppc_rtas_poweron_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos);
+		char *buf, size_t count, loff_t *ppos);
 
 static ssize_t ppc_rtas_tone_freq_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos);
+		char *buf, size_t count, loff_t *ppos);
 static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v);
 static ssize_t ppc_rtas_tone_volume_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos);
+		char *buf, size_t count, loff_t *ppos);
 static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v);
 static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v);
 
 static const struct proc_ops ppc_rtas_poweron_proc_ops = {
 	.proc_show	= ppc_rtas_poweron_show,
-	.proc_write	= ppc_rtas_poweron_write,
+	.proc_write_buf	= ppc_rtas_poweron_write,
 };
 
 static const struct proc_ops ppc_rtas_progress_proc_ops = {
 	.proc_show	= ppc_rtas_progress_show,
-	.proc_write	= ppc_rtas_progress_write,
+	.proc_write_buf	= ppc_rtas_progress_write,
 };
 
 static const struct proc_ops ppc_rtas_clock_proc_ops = {
 	.proc_show	= ppc_rtas_clock_show,
-	.proc_write	= ppc_rtas_clock_write,
+	.proc_write_buf	= ppc_rtas_clock_write,
 };
 
 static const struct proc_ops ppc_rtas_tone_freq_proc_ops = {
 	.proc_show	= ppc_rtas_tone_freq_show,
-	.proc_write	= ppc_rtas_tone_freq_write,
+	.proc_write_buf	= ppc_rtas_tone_freq_write,
 };
 
 static const struct proc_ops ppc_rtas_tone_volume_proc_ops = {
 	.proc_show	= ppc_rtas_tone_volume_show,
-	.proc_write	= ppc_rtas_tone_volume_write,
+	.proc_write_buf	= ppc_rtas_tone_volume_write,
 };
 
 static int ppc_rtas_find_all_sensors(void);
@@ -216,16 +216,14 @@ static int __init proc_rtas_init(void)
 
 __initcall(proc_rtas_init);
 
-static int parse_number(const char __user *p, size_t count, u64 *val)
+static int parse_number(const char *p, size_t count, u64 *val)
 {
 	char buf[40];
 
 	if (count > 39)
 		return -EINVAL;
 
-	if (copy_from_user(buf, p, count))
-		return -EFAULT;
-
+	memcpy(buf, p, count);
 	buf[count] = 0;
 
 	return kstrtoull(buf, 10, val);
@@ -235,7 +233,7 @@ static int parse_number(const char __user *p, size_t count, u64 *val)
 /* POWER-ON-TIME                                                      */
 /* ****************************************************************** */
 static ssize_t ppc_rtas_poweron_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos)
+		char *buf, size_t count, loff_t *ppos)
 {
 	struct rtc_time tm;
 	time64_t nowtime;
@@ -269,15 +267,13 @@ static int ppc_rtas_poweron_show(struct seq_file *m, void *v)
 /* PROGRESS                                                           */
 /* ****************************************************************** */
 static ssize_t ppc_rtas_progress_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos)
+		char *buf, size_t count, loff_t *ppos)
 {
 	unsigned long hex;
 
 	if (count >= MAX_LINELENGTH)
 		count = MAX_LINELENGTH -1;
-	if (copy_from_user(progress_led, buf, count)) { /* save the string */
-		return -EFAULT;
-	}
+	memcpy(progress_led, buf, count); /* save the string */
 	progress_led[count] = 0;
 
 	/* Lets see if the user passed hexdigits */
@@ -301,7 +297,7 @@ static int ppc_rtas_progress_show(struct seq_file *m, void *v)
 /* CLOCK                                                              */
 /* ****************************************************************** */
 static ssize_t ppc_rtas_clock_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos)
+		char *buf, size_t count, loff_t *ppos)
 {
 	struct rtc_time tm;
 	time64_t nowtime;
@@ -660,7 +656,7 @@ static void get_location_code(struct seq_file *m, struct individual_sensor *s,
 /* INDICATORS - Tone Frequency                                        */
 /* ****************************************************************** */
 static ssize_t ppc_rtas_tone_freq_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos)
+		char *buf, size_t count, loff_t *ppos)
 {
 	u64 freq;
 	int error = parse_number(buf, count, &freq);
@@ -685,7 +681,7 @@ static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v)
 /* INDICATORS - Tone Volume                                           */
 /* ****************************************************************** */
 static ssize_t ppc_rtas_tone_volume_write(struct file *file,
-		const char __user *buf, size_t count, loff_t *ppos)
+		char *buf, size_t count, loff_t *ppos)
 {
 	u64 volume;
 	int error = parse_number(buf, count, &volume);
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index f1cd105f7426..77a4627f2507 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -307,7 +307,7 @@ static ssize_t rtas_flash_read_num(struct file *file, char __user *buf,
  * count is.  If the system is low on memory it will be just as well
  * that we fail....
  */
-static ssize_t rtas_flash_write(struct file *file, const char __user *buffer,
+static ssize_t rtas_flash_write(struct file *file, char *buffer,
 				size_t count, loff_t *off)
 {
 	struct rtas_update_flash_t *const uf = &rtas_update_flash_data;
@@ -343,16 +343,11 @@ static ssize_t rtas_flash_write(struct file *file, const char __user *buffer,
 		next_free = 0;
 	}
 
-	if (count > RTAS_BLK_SIZE)
-		count = RTAS_BLK_SIZE;
 	p = kmem_cache_zalloc(flash_block_cache, GFP_KERNEL);
 	if (!p)
 		return -ENOMEM;
 	
-	if(copy_from_user(p, buffer, count)) {
-		kmem_cache_free(flash_block_cache, p);
-		return -EFAULT;
-	}
+	memcpy(p, buffer, count);
 	fl->blocks[next_free].data = p;
 	fl->blocks[next_free].length = count;
 	fl->num_blocks++;
@@ -390,7 +385,7 @@ static ssize_t manage_flash_read(struct file *file, char __user *buf,
 	return simple_read_from_buffer(buf, count, ppos, msg, msglen);
 }
 
-static ssize_t manage_flash_write(struct file *file, const char __user *buf,
+static ssize_t manage_flash_write(struct file *file, char *buf,
 				size_t count, loff_t *off)
 {
 	struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data;
@@ -401,9 +396,8 @@ static ssize_t manage_flash_write(struct file *file, const char __user *buf,
 
 	if ((args_buf->status == MANAGE_AUTH) || (count == 0))
 		return count;
-		
-	if (get_user(cmd, buf))
-		return -EFAULT;
+
+	cmd = buf[0];
 
 	if (cmd == '0')
 		op = RTAS_REJECT_TMP_IMG;
@@ -471,7 +465,7 @@ static ssize_t validate_flash_read(struct file *file, char __user *buf,
 	return simple_read_from_buffer(buf, count, ppos, msg, msglen);
 }
 
-static ssize_t validate_flash_write(struct file *file, const char __user *buf,
+static ssize_t validate_flash_write(struct file *file, char *buf,
 				    size_t count, loff_t *off)
 {
 	struct rtas_validate_flash_t *const args_buf =
@@ -494,11 +488,7 @@ static ssize_t validate_flash_write(struct file *file, const char __user *buf,
 		args_buf->status = VALIDATE_INCOMPLETE;
 	}
 
-	if (!access_ok(buf, count))
-		return -EFAULT;
-
-	if (copy_from_user(args_buf->buf + *off, buf, count))
-		return -EFAULT;
+	memcpy(args_buf->buf + *off, buf, count);
 
 	*off += count;
 	return count;
@@ -632,8 +622,10 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.filename	= "powerpc/rtas/" FIRMWARE_FLASH_NAME,
 		.handle		= RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
 		.status		= &rtas_update_flash_data.status,
+		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 		.ops.proc_read	= rtas_flash_read_msg,
-		.ops.proc_write	= rtas_flash_write,
+		.ops.proc_write_buf = rtas_flash_write,
+		.ops.proc_write_size = RTAS_BLK_SIZE,
 		.ops.proc_release = rtas_flash_release,
 		.ops.proc_lseek	= default_llseek,
 	},
@@ -641,8 +633,10 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.filename	= "powerpc/rtas/" FIRMWARE_UPDATE_NAME,
 		.handle		= RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
 		.status		= &rtas_update_flash_data.status,
+		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 		.ops.proc_read	= rtas_flash_read_num,
-		.ops.proc_write	= rtas_flash_write,
+		.ops.proc_write_buf = rtas_flash_write,
+		.ops.proc_write_size = RTAS_BLK_SIZE,
 		.ops.proc_release = rtas_flash_release,
 		.ops.proc_lseek	= default_llseek,
 	},
@@ -650,8 +644,10 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.filename	= "powerpc/rtas/" VALIDATE_FLASH_NAME,
 		.handle		= RTAS_FN_IBM_VALIDATE_FLASH_IMAGE,
 		.status		= &rtas_validate_flash_data.status,
+		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 		.ops.proc_read	= validate_flash_read,
-		.ops.proc_write	= validate_flash_write,
+		.ops.proc_write_buf = validate_flash_write,
+		.ops.proc_write_size = VALIDATE_BUF_SIZE,
 		.ops.proc_release = validate_flash_release,
 		.ops.proc_lseek	= default_llseek,
 	},
@@ -660,7 +656,7 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_MANAGE_FLASH_IMAGE,
 		.status		= &rtas_manage_flash_data.status,
 		.ops.proc_read	= manage_flash_read,
-		.ops.proc_write	= manage_flash_write,
+		.ops.proc_write_buf = manage_flash_write,
 		.ops.proc_lseek	= default_llseek,
 	}
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 06/22] powerpc/rtas: let procfs copy out reads
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 05/22] powerpc/rtas: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 07/22] powerpc/pseries: let procfs copy in writes Christian Brauner
                     ` (15 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() for the error log and the flash files so
procfs copies what is read to userspace after it unpinned the entry.

The error log hands out a record the way it did before, the record is
consumed before it reaches userspace. A reader that passes a NULL buffer
now loses a record like one that passed a bad buffer always did, instead
of getting -EINVAL. The files can be spliced now, like every entry that
is read through ->read_iter(). Note that a sendfile() from the error log
into a target that fails or takes less than was read drops the records
in between, splice_direct_to_actor() rewinds any seekable file like
that and the error log doesn't hand a record out twice.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/rtas_flash.c | 24 ++++++++++++------------
 arch/powerpc/kernel/rtasd.c      | 19 +++++--------------
 2 files changed, 17 insertions(+), 26 deletions(-)

diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index 77a4627f2507..5ebaee7683ef 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -269,7 +269,7 @@ static size_t get_flash_status_msg(int status, char *buf)
 }
 
 /* Reading the proc file will show status (not the firmware contents) */
-static ssize_t rtas_flash_read_msg(struct file *file, char __user *buf,
+static ssize_t rtas_flash_read_msg(struct file *file, char *buf,
 				   size_t count, loff_t *ppos)
 {
 	struct rtas_update_flash_t *const uf = &rtas_update_flash_data;
@@ -283,10 +283,10 @@ static ssize_t rtas_flash_read_msg(struct file *file, char __user *buf,
 
 	/* Read as text message */
 	len = get_flash_status_msg(status, msg);
-	return simple_read_from_buffer(buf, count, ppos, msg, len);
+	return memory_read_from_buffer(buf, count, ppos, msg, len);
 }
 
-static ssize_t rtas_flash_read_num(struct file *file, char __user *buf,
+static ssize_t rtas_flash_read_num(struct file *file, char *buf,
 				   size_t count, loff_t *ppos)
 {
 	struct rtas_update_flash_t *const uf = &rtas_update_flash_data;
@@ -299,7 +299,7 @@ static ssize_t rtas_flash_read_num(struct file *file, char __user *buf,
 
 	/* Read as number */
 	sprintf(msg, "%d\n", status);
-	return simple_read_from_buffer(buf, count, ppos, msg, strlen(msg));
+	return memory_read_from_buffer(buf, count, ppos, msg, strlen(msg));
 }
 
 /* We could be much more efficient here.  But to keep this function
@@ -370,7 +370,7 @@ static void manage_flash(struct rtas_manage_flash_t *args_buf, unsigned int op)
 	args_buf->status = rc;
 }
 
-static ssize_t manage_flash_read(struct file *file, char __user *buf,
+static ssize_t manage_flash_read(struct file *file, char *buf,
 			       size_t count, loff_t *ppos)
 {
 	struct rtas_manage_flash_t *const args_buf = &rtas_manage_flash_data;
@@ -382,7 +382,7 @@ static ssize_t manage_flash_read(struct file *file, char __user *buf,
 	mutex_unlock(&rtas_manage_flash_mutex);
 
 	msglen = sprintf(msg, "%d\n", status);
-	return simple_read_from_buffer(buf, count, ppos, msg, msglen);
+	return memory_read_from_buffer(buf, count, ppos, msg, msglen);
 }
 
 static ssize_t manage_flash_write(struct file *file, char *buf,
@@ -450,7 +450,7 @@ static int get_validate_flash_msg(struct rtas_validate_flash_t *args_buf,
 	return n;
 }
 
-static ssize_t validate_flash_read(struct file *file, char __user *buf,
+static ssize_t validate_flash_read(struct file *file, char *buf,
 			       size_t count, loff_t *ppos)
 {
 	struct rtas_validate_flash_t *const args_buf =
@@ -462,7 +462,7 @@ static ssize_t validate_flash_read(struct file *file, char __user *buf,
 	msglen = get_validate_flash_msg(args_buf, msg, VALIDATE_MSG_LEN);
 	mutex_unlock(&rtas_validate_flash_mutex);
 
-	return simple_read_from_buffer(buf, count, ppos, msg, msglen);
+	return memory_read_from_buffer(buf, count, ppos, msg, msglen);
 }
 
 static ssize_t validate_flash_write(struct file *file, char *buf,
@@ -623,7 +623,7 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
 		.status		= &rtas_update_flash_data.status,
 		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
-		.ops.proc_read	= rtas_flash_read_msg,
+		.ops.proc_read_buf = rtas_flash_read_msg,
 		.ops.proc_write_buf = rtas_flash_write,
 		.ops.proc_write_size = RTAS_BLK_SIZE,
 		.ops.proc_release = rtas_flash_release,
@@ -634,7 +634,7 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
 		.status		= &rtas_update_flash_data.status,
 		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
-		.ops.proc_read	= rtas_flash_read_num,
+		.ops.proc_read_buf = rtas_flash_read_num,
 		.ops.proc_write_buf = rtas_flash_write,
 		.ops.proc_write_size = RTAS_BLK_SIZE,
 		.ops.proc_release = rtas_flash_release,
@@ -645,7 +645,7 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_VALIDATE_FLASH_IMAGE,
 		.status		= &rtas_validate_flash_data.status,
 		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
-		.ops.proc_read	= validate_flash_read,
+		.ops.proc_read_buf = validate_flash_read,
 		.ops.proc_write_buf = validate_flash_write,
 		.ops.proc_write_size = VALIDATE_BUF_SIZE,
 		.ops.proc_release = validate_flash_release,
@@ -655,7 +655,7 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.filename	= "powerpc/rtas/" MANAGE_FLASH_NAME,
 		.handle		= RTAS_FN_IBM_MANAGE_FLASH_IMAGE,
 		.status		= &rtas_manage_flash_data.status,
-		.ops.proc_read	= manage_flash_read,
+		.ops.proc_read_buf = manage_flash_read,
 		.ops.proc_write_buf = manage_flash_write,
 		.ops.proc_lseek	= default_llseek,
 	}
diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index dd0df5e164a3..360ed2e30fd5 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -291,26 +291,18 @@ static int rtas_log_release(struct inode * inode, struct file * file)
  * know that we can safely clear the events in NVRAM.
  * Next we'll sit and wait for something else to log.
  */
-static ssize_t rtas_log_read(struct file * file, char __user * buf,
+static ssize_t rtas_log_read(struct file * file, char * buf,
 			 size_t count, loff_t *ppos)
 {
 	int error;
-	char *tmp;
 	unsigned long s;
 	unsigned long offset;
 
-	if (!buf || count < rtas_error_log_buffer_max)
+	if (count < rtas_error_log_buffer_max)
 		return -EINVAL;
 
 	count = rtas_error_log_buffer_max;
 
-	if (!access_ok(buf, count))
-		return -EFAULT;
-
-	tmp = kmalloc(count, GFP_KERNEL);
-	if (!tmp)
-		return -ENOMEM;
-
 	spin_lock_irqsave(&rtasd_log_lock, s);
 
 	/* if it's 0, then we know we got the last one (the one in NVRAM) */
@@ -338,15 +330,14 @@ static ssize_t rtas_log_read(struct file * file, char __user * buf,
 	}
 
 	offset = rtas_error_log_buffer_max * (rtas_log_start & LOG_NUMBER_MASK);
-	memcpy(tmp, &rtas_log_buf[offset], count);
+	memcpy(buf, &rtas_log_buf[offset], count);
 
 	rtas_log_start += 1;
 	rtas_log_size -= 1;
 	spin_unlock_irqrestore(&rtasd_log_lock, s);
 
-	error = copy_to_user(buf, tmp, count) ? -EFAULT : count;
+	error = count;
 out:
-	kfree(tmp);
 	return error;
 }
 
@@ -359,7 +350,7 @@ static __poll_t rtas_log_poll(struct file *file, poll_table * wait)
 }
 
 static const struct proc_ops rtas_log_proc_ops = {
-	.proc_read	= rtas_log_read,
+	.proc_read_buf	= rtas_log_read,
 	.proc_poll	= rtas_log_poll,
 	.proc_open	= rtas_log_open,
 	.proc_release	= rtas_log_release,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 07/22] powerpc/pseries: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 06/22] powerpc/rtas: let procfs copy out reads Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 08/22] powerpc/procfs: let procfs copy out reads of /proc/powerpc/systemcfg Christian Brauner
                     ` (14 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for lparcfg, ofdt and the vcpudispatch
files so procfs copies in what is written before it pins the entry, and
parse what was written from that copy. ofdt takes as much as
memdup_user_nul() could copy before since device tree updates can be
large.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/platforms/pseries/lpar.c     | 14 ++++++--------
 arch/powerpc/platforms/pseries/lparcfg.c  |  7 +++----
 arch/powerpc/platforms/pseries/reconfig.c | 22 ++++++++--------------
 3 files changed, 17 insertions(+), 26 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 48fe0be71a89..dfd027cc9047 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -498,7 +498,7 @@ static void dtl_worker_disable(unsigned long *time_limit)
 	up_write(&dtl_access_lock);
 }
 
-static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
+static ssize_t vcpudispatch_stats_write(struct file *file, char *p,
 		size_t count, loff_t *ppos)
 {
 	unsigned long time_limit = jiffies + HZ;
@@ -509,8 +509,7 @@ static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,
 	if (count > 15)
 		return -EINVAL;
 
-	if (copy_from_user(buf, p, count))
-		return -EFAULT;
+	memcpy(buf, p, count);
 
 	buf[count] = 0;
 	rc = kstrtoint(buf, 0, &cmd);
@@ -586,11 +585,11 @@ static int vcpudispatch_stats_display(struct seq_file *p, void *v)
 
 static const struct proc_ops vcpudispatch_stats_proc_ops = {
 	.proc_show	= vcpudispatch_stats_display,
-	.proc_write	= vcpudispatch_stats_write,
+	.proc_write_buf	= vcpudispatch_stats_write,
 };
 
 static ssize_t vcpudispatch_stats_freq_write(struct file *file,
-		const char __user *p, size_t count, loff_t *ppos)
+		char *p, size_t count, loff_t *ppos)
 {
 	int rc, freq;
 	char buf[16];
@@ -598,8 +597,7 @@ static ssize_t vcpudispatch_stats_freq_write(struct file *file,
 	if (count > 15)
 		return -EINVAL;
 
-	if (copy_from_user(buf, p, count))
-		return -EFAULT;
+	memcpy(buf, p, count);
 
 	buf[count] = 0;
 	rc = kstrtoint(buf, 0, &freq);
@@ -622,7 +620,7 @@ static int vcpudispatch_stats_freq_display(struct seq_file *p, void *v)
 
 static const struct proc_ops vcpudispatch_stats_freq_proc_ops = {
 	.proc_show	= vcpudispatch_stats_freq_display,
-	.proc_write	= vcpudispatch_stats_freq_write,
+	.proc_write_buf	= vcpudispatch_stats_freq_write,
 };
 
 static int __init vcpudispatch_stats_procfs_init(void)
diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
index a6a137ce44ce..43af7f820fab 100644
--- a/arch/powerpc/platforms/pseries/lparcfg.c
+++ b/arch/powerpc/platforms/pseries/lparcfg.c
@@ -687,7 +687,7 @@ static ssize_t update_mpp(u64 *entitlement, u8 *weight)
  * This function should be invoked only on systems with
  * FW_FEATURE_SPLPAR.
  */
-static ssize_t lparcfg_write(struct file *file, const char __user * buf,
+static ssize_t lparcfg_write(struct file *file, char *buf,
 			     size_t count, loff_t * off)
 {
 	char kbuf[64];
@@ -702,8 +702,7 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf,
 	if (count == 0 || count > sizeof(kbuf))
 		return -EINVAL;
 
-	if (copy_from_user(kbuf, buf, count))
-		return -EFAULT;
+	memcpy(kbuf, buf, count);
 
 	kbuf[count - 1] = '\0';
 	tmp = strchr(kbuf, '=');
@@ -799,7 +798,7 @@ static int lparcfg_data(struct seq_file *m, void *v)
 }
 
 static const struct proc_ops lparcfg_proc_ops = {
-	.proc_write	= lparcfg_write,
+	.proc_write_buf	= lparcfg_write,
 	.proc_show	= lparcfg_data,
 };
 
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index b41c5db726c9..f406e826a03e 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -349,7 +349,7 @@ static int do_update_property(char *buf, size_t bufsize)
  * ofdt_write - perform operations on the Open Firmware device tree
  *
  * @file: not used
- * @buf: command and arguments
+ * @kbuf: command and arguments
  * @count: size of the command buffer
  * @off: not used
  *
@@ -357,26 +357,19 @@ static int do_update_property(char *buf, size_t bufsize)
  * whole nodes along with their properties.  Operations on individual
  * properties are not implemented (yet).
  */
-static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
+static ssize_t ofdt_write(struct file *file, char *kbuf, size_t count,
 			  loff_t *off)
 {
 	int rv;
-	char *kbuf;
 	char *tmp;
 
 	rv = security_locked_down(LOCKDOWN_DEVICE_TREE);
 	if (rv)
 		return rv;
 
-	kbuf = memdup_user_nul(buf, count);
-	if (IS_ERR(kbuf))
-		return PTR_ERR(kbuf);
-
 	tmp = strchr(kbuf, ' ');
-	if (!tmp) {
-		rv = -EINVAL;
-		goto out;
-	}
+	if (!tmp)
+		return -EINVAL;
 	*tmp = '\0';
 	tmp++;
 
@@ -392,13 +385,14 @@ static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t coun
 		rv = do_update_property(tmp, count - (tmp - kbuf));
 	else
 		rv = -EINVAL;
-out:
-	kfree(kbuf);
+
 	return rv ? rv : count;
 }
 
 static const struct proc_ops ofdt_proc_ops = {
-	.proc_write	= ofdt_write,
+	.proc_write_buf	= ofdt_write,
+	/* what memdup_user_nul() could copy in */
+	.proc_write_size = KMALLOC_MAX_SIZE - 1,
 	.proc_lseek	= noop_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 08/22] powerpc/procfs: let procfs copy out reads of /proc/powerpc/systemcfg
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 07/22] powerpc/pseries: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 09/22] sh: alignment: let procfs copy in writes Christian Brauner
                     ` (13 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/powerpc/kernel/proc_powerpc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/kernel/proc_powerpc.c b/arch/powerpc/kernel/proc_powerpc.c
index d083b4517065..46bc914d74b9 100644
--- a/arch/powerpc/kernel/proc_powerpc.c
+++ b/arch/powerpc/kernel/proc_powerpc.c
@@ -24,10 +24,10 @@ static loff_t page_map_seek(struct file *file, loff_t off, int whence)
 	return fixed_size_llseek(file, off, whence, PAGE_SIZE);
 }
 
-static ssize_t page_map_read( struct file *file, char __user *buf, size_t nbytes,
+static ssize_t page_map_read( struct file *file, char *buf, size_t nbytes,
 			      loff_t *ppos)
 {
-	return simple_read_from_buffer(buf, nbytes, ppos,
+	return memory_read_from_buffer(buf, nbytes, ppos,
 			pde_data(file_inode(file)), PAGE_SIZE);
 }
 
@@ -43,7 +43,7 @@ static int page_map_mmap( struct file *file, struct vm_area_struct *vma )
 
 static const struct proc_ops page_map_proc_ops = {
 	.proc_lseek	= page_map_seek,
-	.proc_read	= page_map_read,
+	.proc_read_buf	= page_map_read,
 	.proc_mmap	= page_map_mmap,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 09/22] sh: alignment: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 08/22] powerpc/procfs: let procfs copy out reads of /proc/powerpc/systemcfg Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 10/22] sparc: led: " Christian Brauner
                     ` (12 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to the
alignment files before it pins the entry, and take the mode from that
copy. Writes larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/sh/mm/alignment.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/sh/mm/alignment.c b/arch/sh/mm/alignment.c
index 2198c6f2ea63..d832249a8e18 100644
--- a/arch/sh/mm/alignment.c
+++ b/arch/sh/mm/alignment.c
@@ -133,14 +133,13 @@ static int alignment_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t alignment_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
 	int *data = pde_data(file_inode(file));
 	char mode;
 
 	if (count > 0) {
-		if (get_user(mode, buffer))
-			return -EFAULT;
+		mode = buffer[0];
 		if (mode >= '0' && mode <= '5')
 			*data = mode - '0';
 	}
@@ -149,7 +148,7 @@ static ssize_t alignment_proc_write(struct file *file,
 
 static const struct proc_ops alignment_proc_ops = {
 	.proc_show	= alignment_proc_show,
-	.proc_write	= alignment_proc_write,
+	.proc_write_buf	= alignment_proc_write,
 };
 
 /*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 10/22] sparc: led: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 09/22] sh: alignment: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 11/22] um: " Christian Brauner
                     ` (11 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/led before it pins the entry, and parse the command from that
copy. Writes larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/sparc/kernel/led.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c
index 12400bb59ab8..01449b056cdd 100644
--- a/arch/sparc/kernel/led.c
+++ b/arch/sparc/kernel/led.c
@@ -60,17 +60,12 @@ static int led_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t led_proc_write(struct file *file, const char __user *buffer,
+static ssize_t led_proc_write(struct file *file, char *buf,
 			      size_t count, loff_t *ppos)
 {
-	char *buf = NULL;
-
 	if (count > LED_MAX_LENGTH)
 		count = LED_MAX_LENGTH;
-
-	buf = memdup_user_nul(buffer, count);
-	if (IS_ERR(buf))
-		return PTR_ERR(buf);
+	buf[count] = '\0';
 
 	/* work around \n when echo'ing into proc */
 	if (count > 0 && buf[count - 1] == '\n')
@@ -95,14 +90,12 @@ static ssize_t led_proc_write(struct file *file, const char __user *buffer,
 		auxio_set_led(AUXIO_LED_OFF);
 	}
 
-	kfree(buf);
-
 	return count;
 }
 
 static const struct proc_ops led_proc_ops = {
 	.proc_show	= led_proc_show,
-	.proc_write	= led_proc_write,
+	.proc_write_buf	= led_proc_write,
 };
 #endif
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 11/22] um: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 10/22] sparc: led: " Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 12/22] x86/mtrr: let procfs own the seq_file of /proc/mtrr Christian Brauner
                     ` (10 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the mconsole and exitcode files so
procfs copies in what is written before it pins the entry. Writes larger
than PAGE_SIZE - 1 bytes fail with -EINVAL now.

exitcode parses procfs' copy as is, so it no longer stops at the first
six bytes of what is written.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/um/drivers/mconsole_kern.c | 11 ++---------
 arch/um/kernel/exitcode.c       | 14 ++++----------
 2 files changed, 6 insertions(+), 19 deletions(-)

diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index f01b7d8a07bc..a4370470286f 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -769,21 +769,14 @@ static int __init mconsole_init(void)
 __initcall(mconsole_init);
 
 static ssize_t mconsole_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buf, size_t count, loff_t *pos)
 {
-	char *buf;
-
-	buf = memdup_user_nul(buffer, count);
-	if (IS_ERR(buf))
-		return PTR_ERR(buf);
-
 	mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
-	kfree(buf);
 	return count;
 }
 
 static const struct proc_ops mconsole_proc_ops = {
-	.proc_write	= mconsole_proc_write,
+	.proc_write_buf	= mconsole_proc_write,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c
index c71657df38f5..8f76e3b20c18 100644
--- a/arch/um/kernel/exitcode.c
+++ b/arch/um/kernel/exitcode.c
@@ -32,18 +32,12 @@ static int exitcode_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t exitcode_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
-	char *end, buf[sizeof("nnnnn\0")];
-	size_t size;
+	char *end;
 	int tmp;
 
-	size = min(count, sizeof(buf) - 1);
-	if (copy_from_user(buf, buffer, size))
-		return -EFAULT;
-	buf[size] = '\0';
-
-	tmp = simple_strtol(buf, &end, 0);
+	tmp = simple_strtol(buffer, &end, 0);
 	if ((*end != '\0') && !isspace(*end))
 		return -EINVAL;
 
@@ -53,7 +47,7 @@ static ssize_t exitcode_proc_write(struct file *file,
 
 static const struct proc_ops exitcode_proc_ops = {
 	.proc_show	= exitcode_proc_show,
-	.proc_write	= exitcode_proc_write,
+	.proc_write_buf	= exitcode_proc_write,
 };
 
 static int make_proc_exitcode(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 12/22] x86/mtrr: let procfs own the seq_file of /proc/mtrr
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (10 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 11/22] um: " Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 13/22] x86/mtrr: let procfs copy in writes to /proc/mtrr Christian Brauner
                     ` (9 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show function and let procfs open and free the seq_file.
->proc_open() only does the checks now and ->proc_release() only drops
the MTRRs the file added.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/x86/kernel/cpu/mtrr/if.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c
index 4049235b1bfe..9510e01d5103 100644
--- a/arch/x86/kernel/cpu/mtrr/if.c
+++ b/arch/x86/kernel/cpu/mtrr/if.c
@@ -349,7 +349,7 @@ static int mtrr_close(struct inode *ino, struct file *file)
 		kfree(fcount);
 		FILE_FCOUNT(file) = NULL;
 	}
-	return single_release(ino, file);
+	return 0;
 }
 
 static int mtrr_seq_show(struct seq_file *seq, void *offset)
@@ -391,13 +391,12 @@ static int mtrr_open(struct inode *inode, struct file *file)
 		return -ENXIO;
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
-	return single_open(file, mtrr_seq_show, NULL);
+	return 0;
 }
 
 static const struct proc_ops mtrr_proc_ops = {
+	.proc_show		= mtrr_seq_show,
 	.proc_open		= mtrr_open,
-	.proc_read		= seq_read,
-	.proc_lseek		= seq_lseek,
 	.proc_write		= mtrr_write,
 	.proc_ioctl		= mtrr_ioctl,
 #ifdef CONFIG_COMPAT

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 13/22] x86/mtrr: let procfs copy in writes to /proc/mtrr
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (11 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 12/22] x86/mtrr: let procfs own the seq_file of /proc/mtrr Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 14/22] x86/mtrr: let procfs copy the ioctl arguments of /proc/mtrr Christian Brauner
                     ` (8 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and parse the command from that copy. Writes larger
than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/x86/kernel/cpu/mtrr/if.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c
index 9510e01d5103..038e2dc38797 100644
--- a/arch/x86/kernel/cpu/mtrr/if.c
+++ b/arch/x86/kernel/cpu/mtrr/if.c
@@ -91,7 +91,7 @@ mtrr_file_del(unsigned long base, unsigned long size,
  *    "base=%Lx size=%Lx type=%s" or "disable=%d"
  */
 static ssize_t
-mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
+mtrr_write(struct file *file, char *buf, size_t len, loff_t * ppos)
 {
 	int i, err;
 	unsigned long reg;
@@ -103,9 +103,8 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
 	memset(line, 0, LINE_SIZE);
 
 	len = min_t(size_t, len, LINE_SIZE - 1);
-	length = strncpy_from_user(line, buf, len);
-	if (length < 0)
-		return length;
+	length = strnlen(buf, len);
+	memcpy(line, buf, length);
 
 	ptr = line + length - 1;
 	if (length && *ptr == '\n')
@@ -397,7 +396,7 @@ static int mtrr_open(struct inode *inode, struct file *file)
 static const struct proc_ops mtrr_proc_ops = {
 	.proc_show		= mtrr_seq_show,
 	.proc_open		= mtrr_open,
-	.proc_write		= mtrr_write,
+	.proc_write_buf		= mtrr_write,
 	.proc_ioctl		= mtrr_ioctl,
 #ifdef CONFIG_COMPAT
 	.proc_compat_ioctl	= mtrr_ioctl,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 14/22] x86/mtrr: let procfs copy the ioctl arguments of /proc/mtrr
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (12 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 13/22] x86/mtrr: let procfs copy in writes to /proc/mtrr Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 15/22] xtensa: simdisk: let procfs copy in writes Christian Brauner
                     ` (7 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_ioctl_buf() so procfs copies the arguments in before
it pins the entry and back out after unpinning it. The native and the
32-bit commands encode their arguments and share one table.
mtrr_ioctl() reads and fills procfs' copy, also in the 32-bit layouts.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/x86/kernel/cpu/mtrr/if.c | 79 +++++++++++++++++++++++++------------------
 1 file changed, 46 insertions(+), 33 deletions(-)

diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c
index 038e2dc38797..9c33c0428dfe 100644
--- a/arch/x86/kernel/cpu/mtrr/if.c
+++ b/arch/x86/kernel/cpu/mtrr/if.c
@@ -149,7 +149,7 @@ mtrr_write(struct file *file, char *buf, size_t len, loff_t * ppos)
 }
 
 static long
-mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
+mtrr_ioctl(struct file *file, unsigned int cmd, void *arg)
 {
 	int err = 0;
 	mtrr_type type;
@@ -157,7 +157,6 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
 	unsigned long size;
 	struct mtrr_sentry sentry;
 	struct mtrr_gentry gentry;
-	void __user *arg = (void __user *) __arg;
 
 	memset(&gentry, 0, sizeof(gentry));
 
@@ -170,13 +169,11 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
 	case MTRRIOC_SET_PAGE_ENTRY:
 	case MTRRIOC_DEL_PAGE_ENTRY:
 	case MTRRIOC_KILL_PAGE_ENTRY:
-		if (copy_from_user(&sentry, arg, sizeof(sentry)))
-			return -EFAULT;
+		memcpy(&sentry, arg, sizeof(sentry));
 		break;
 	case MTRRIOC_GET_ENTRY:
 	case MTRRIOC_GET_PAGE_ENTRY:
-		if (copy_from_user(&gentry, arg, sizeof(gentry)))
-			return -EFAULT;
+		memcpy(&gentry, arg, sizeof(gentry));
 		break;
 #ifdef CONFIG_COMPAT
 	case MTRRIOC32_ADD_ENTRY:
@@ -187,27 +184,21 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
 	case MTRRIOC32_SET_PAGE_ENTRY:
 	case MTRRIOC32_DEL_PAGE_ENTRY:
 	case MTRRIOC32_KILL_PAGE_ENTRY: {
-		struct mtrr_sentry32 __user *s32;
+		struct mtrr_sentry32 *s32 = arg;
 
-		s32 = (struct mtrr_sentry32 __user *)__arg;
-		err = get_user(sentry.base, &s32->base);
-		err |= get_user(sentry.size, &s32->size);
-		err |= get_user(sentry.type, &s32->type);
-		if (err)
-			return err;
+		sentry.base = s32->base;
+		sentry.size = s32->size;
+		sentry.type = s32->type;
 		break;
 	}
 	case MTRRIOC32_GET_ENTRY:
 	case MTRRIOC32_GET_PAGE_ENTRY: {
-		struct mtrr_gentry32 __user *g32;
+		struct mtrr_gentry32 *g32 = arg;
 
-		g32 = (struct mtrr_gentry32 __user *)__arg;
-		err = get_user(gentry.regnum, &g32->regnum);
-		err |= get_user(gentry.base, &g32->base);
-		err |= get_user(gentry.size, &g32->size);
-		err |= get_user(gentry.type, &g32->type);
-		if (err)
-			return err;
+		gentry.regnum = g32->regnum;
+		gentry.base = g32->base;
+		gentry.size = g32->size;
+		gentry.type = g32->type;
 		break;
 	}
 #endif
@@ -312,19 +303,17 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
 	switch (cmd) {
 	case MTRRIOC_GET_ENTRY:
 	case MTRRIOC_GET_PAGE_ENTRY:
-		if (copy_to_user(arg, &gentry, sizeof(gentry)))
-			err = -EFAULT;
+		memcpy(arg, &gentry, sizeof(gentry));
 		break;
 #ifdef CONFIG_COMPAT
 	case MTRRIOC32_GET_ENTRY:
 	case MTRRIOC32_GET_PAGE_ENTRY: {
-		struct mtrr_gentry32 __user *g32;
+		struct mtrr_gentry32 *g32 = arg;
 
-		g32 = (struct mtrr_gentry32 __user *)__arg;
-		err = put_user(gentry.base, &g32->base);
-		err |= put_user(gentry.size, &g32->size);
-		err |= put_user(gentry.regnum, &g32->regnum);
-		err |= put_user(gentry.type, &g32->type);
+		g32->base = gentry.base;
+		g32->size = gentry.size;
+		g32->regnum = gentry.regnum;
+		g32->type = gentry.type;
 		break;
 	}
 #endif
@@ -393,14 +382,38 @@ static int mtrr_open(struct inode *inode, struct file *file)
 	return 0;
 }
 
+static const struct proc_ioctl_arg mtrr_ioctl_args[] = {
+	PROC_IOCTL_IOC(MTRRIOC_ADD_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_SET_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_DEL_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_GET_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_KILL_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_ADD_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_SET_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_DEL_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_GET_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC_KILL_PAGE_ENTRY),
+#ifdef CONFIG_COMPAT
+	PROC_IOCTL_IOC(MTRRIOC32_ADD_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_SET_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_DEL_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_GET_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_KILL_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_ADD_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_SET_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_DEL_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_GET_PAGE_ENTRY),
+	PROC_IOCTL_IOC(MTRRIOC32_KILL_PAGE_ENTRY),
+#endif
+};
+
 static const struct proc_ops mtrr_proc_ops = {
 	.proc_show		= mtrr_seq_show,
 	.proc_open		= mtrr_open,
 	.proc_write_buf		= mtrr_write,
-	.proc_ioctl		= mtrr_ioctl,
-#ifdef CONFIG_COMPAT
-	.proc_compat_ioctl	= mtrr_ioctl,
-#endif
+	.proc_ioctl_buf		= mtrr_ioctl,
+	.proc_ioctl_args	= mtrr_ioctl_args,
+	.proc_nr_ioctl_args	= ARRAY_SIZE(mtrr_ioctl_args),
 	.proc_release		= mtrr_close,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 15/22] xtensa: simdisk: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (13 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 14/22] x86/mtrr: let procfs copy the ioctl arguments of /proc/mtrr Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 16/22] xtensa: simdisk: let procfs copy out reads Christian Brauner
                     ` (6 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to the
simdisk files before it pins the entry, and take the file name from
that copy. The files took up to PAGE_SIZE bytes and keep doing that
through ->proc_write_size.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/xtensa/platforms/iss/simdisk.c | 26 ++++++++++----------------
 1 file changed, 10 insertions(+), 16 deletions(-)

diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index 7b115e023de9..b2f9e7f3ade3 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -241,7 +241,7 @@ static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
 	ssize_t len;
 	char *temp;
 
-	/* a write sets at most a page, see proc_write_simdisk() */
+	/* a write sets at most a page, see simdisk_proc_ops */
 	temp = kmalloc(PAGE_SIZE + 2, GFP_KERNEL);
 	if (!temp)
 		return -ENOMEM;
@@ -255,40 +255,34 @@ static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
 	return len;
 }
 
-static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,
+static ssize_t proc_write_simdisk(struct file *file, char *buf,
 			size_t count, loff_t *ppos)
 {
-	char *tmp;
 	struct simdisk *dev = pde_data(file_inode(file));
 	int err;
 
-	if (count == 0 || count > PAGE_SIZE)
+	if (count == 0)
 		return -EINVAL;
 
-	tmp = memdup_user_nul(buf, count);
-	if (IS_ERR(tmp))
-		return PTR_ERR(tmp);
-
 	err = simdisk_detach(dev);
 	if (err != 0)
-		goto out_free;
+		return err;
 
-	if (count > 0 && tmp[count - 1] == '\n')
-		tmp[count - 1] = 0;
+	if (count > 0 && buf[count - 1] == '\n')
+		buf[count - 1] = 0;
 
-	if (tmp[0])
-		err = simdisk_attach(dev, tmp);
+	if (buf[0])
+		err = simdisk_attach(dev, buf);
 
 	if (err == 0)
 		err = count;
-out_free:
-	kfree(tmp);
 	return err;
 }
 
 static const struct proc_ops simdisk_proc_ops = {
 	.proc_read	= proc_read_simdisk,
-	.proc_write	= proc_write_simdisk,
+	.proc_write_buf	= proc_write_simdisk,
+	.proc_write_size = PAGE_SIZE,
 	.proc_lseek	= default_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 16/22] xtensa: simdisk: let procfs copy out reads
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (14 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 15/22] xtensa: simdisk: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 17/22] ACPI: proc: let procfs copy in writes to /proc/acpi/wakeup Christian Brauner
                     ` (5 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/xtensa/platforms/iss/simdisk.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index b2f9e7f3ade3..b597d8642e8a 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -234,7 +234,7 @@ static int simdisk_detach(struct simdisk *dev)
 	return err;
 }
 
-static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
+static ssize_t proc_read_simdisk(struct file *file, char *buf,
 			size_t size, loff_t *ppos)
 {
 	struct simdisk *dev = pde_data(file_inode(file));
@@ -250,7 +250,7 @@ static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
 	len = scnprintf(temp, PAGE_SIZE + 2, "%s\n", dev->filename ?: "");
 	spin_unlock(&dev->lock);
 
-	len = simple_read_from_buffer(buf, size, ppos, temp, len);
+	len = memory_read_from_buffer(buf, size, ppos, temp, len);
 	kfree(temp);
 	return len;
 }
@@ -280,7 +280,7 @@ static ssize_t proc_write_simdisk(struct file *file, char *buf,
 }
 
 static const struct proc_ops simdisk_proc_ops = {
-	.proc_read	= proc_read_simdisk,
+	.proc_read_buf	= proc_read_simdisk,
 	.proc_write_buf	= proc_write_simdisk,
 	.proc_write_size = PAGE_SIZE,
 	.proc_lseek	= default_llseek,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 17/22] ACPI: proc: let procfs copy in writes to /proc/acpi/wakeup
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (15 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 16/22] xtensa: simdisk: let procfs copy out reads Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 18/22] macintosh/via-pmu: let procfs copy in writes to the options file Christian Brauner
                     ` (4 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and take the device name from that copy. Writes
larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/acpi/proc.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
index b8a0bd4580e0..feb8283fd855 100644
--- a/drivers/acpi/proc.c
+++ b/drivers/acpi/proc.c
@@ -86,7 +86,7 @@ static void physical_device_enable_wakeup(struct acpi_device *adev)
 
 static ssize_t
 acpi_system_write_wakeup_device(struct file *file,
-				const char __user * buffer,
+				char *buffer,
 				size_t count, loff_t * ppos)
 {
 	struct acpi_device *dev, *tmp;
@@ -96,8 +96,7 @@ acpi_system_write_wakeup_device(struct file *file,
 	if (count > 4)
 		count = 4;
 
-	if (copy_from_user(strbuf, buffer, count))
-		return -EFAULT;
+	memcpy(strbuf, buffer, count);
 	strbuf[count] = '\0';
 	sscanf(strbuf, "%s", str);
 
@@ -123,7 +122,7 @@ acpi_system_write_wakeup_device(struct file *file,
 
 static const struct proc_ops acpi_system_wakeup_device_proc_ops = {
 	.proc_show	= acpi_system_wakeup_device_seq_show,
-	.proc_write	= acpi_system_write_wakeup_device,
+	.proc_write_buf	= acpi_system_write_wakeup_device,
 };
 
 void __init acpi_sleep_proc_init(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 18/22] macintosh/via-pmu: let procfs copy in writes to the options file
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (16 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 17/22] ACPI: proc: let procfs copy in writes to /proc/acpi/wakeup Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 19/22] platform/x86: thinkpad_acpi: let procfs copy in writes Christian Brauner
                     ` (3 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and parse the option from that copy. Writes larger
than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/macintosh/via-pmu.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 49f929ae10e4..079c085be5d2 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -913,7 +913,7 @@ static int pmu_options_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t pmu_options_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
 	char tmp[33];
 	char *label, *val;
@@ -923,8 +923,7 @@ static ssize_t pmu_options_proc_write(struct file *file,
 		return -EINVAL;
 	if (count > 32)
 		count = 32;
-	if (copy_from_user(tmp, buffer, count))
-		return -EFAULT;
+	memcpy(tmp, buffer, count);
 	tmp[count] = 0;
 
 	label = tmp;
@@ -958,7 +957,7 @@ static ssize_t pmu_options_proc_write(struct file *file,
 
 static const struct proc_ops pmu_options_proc_ops = {
 	.proc_show	= pmu_options_proc_show,
-	.proc_write	= pmu_options_proc_write,
+	.proc_write_buf	= pmu_options_proc_write,
 };
 #endif
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 19/22] platform/x86: thinkpad_acpi: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (17 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 18/22] macintosh/via-pmu: let procfs copy in writes to the options file Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 20/22] platform/x86: toshiba_acpi: " Christian Brauner
                     ` (2 subsequent siblings)
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/acpi/ibm/* before it pins the entry, and hand that copy to
->write(). procfs already rejects writes larger than PAGE_SIZE - 1
bytes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/platform/x86/lenovo/thinkpad_acpi.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
index 01e4c980fad6..b4f253da9536 100644
--- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
+++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
@@ -881,33 +881,25 @@ static int dispatch_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t dispatch_proc_write(struct file *file,
-			const char __user *userbuf,
+			char *buf,
 			size_t count, loff_t *pos)
 {
 	struct ibm_struct *ibm = pde_data(file_inode(file));
-	char *kernbuf;
 	int ret;
 
 	if (!ibm || !ibm->write)
 		return -EINVAL;
-	if (count > PAGE_SIZE - 1)
-		return -EINVAL;
 
-	kernbuf = memdup_user_nul(userbuf, count);
-	if (IS_ERR(kernbuf))
-		return PTR_ERR(kernbuf);
-	ret = ibm->write(kernbuf);
+	ret = ibm->write(buf);
 	if (ret == 0)
 		ret = count;
 
-	kfree(kernbuf);
-
 	return ret;
 }
 
 static const struct proc_ops dispatch_proc_ops = {
 	.proc_show	= dispatch_proc_show,
-	.proc_write	= dispatch_proc_write,
+	.proc_write_buf	= dispatch_proc_write,
 };
 
 /****************************************************************************

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 20/22] platform/x86: toshiba_acpi: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (18 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 19/22] platform/x86: thinkpad_acpi: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 21/22] PNP: pnpbios: " Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 22/22] PNP: isapnp: let procfs copy out reads Christian Brauner
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the files in /proc/acpi/toshiba so
procfs copies in what is written before it pins the entry, and parse
what was written from that copy. Commands longer than PAGE_SIZE - 1
bytes are rejected now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/platform/x86/toshiba_acpi.c | 36 ++++++++++++------------------------
 1 file changed, 12 insertions(+), 24 deletions(-)

diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 560e62da05a9..895e321ada03 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1478,7 +1478,7 @@ static int set_lcd_status(struct backlight_device *bd)
 	return set_lcd_brightness(dev, bd->props.brightness);
 }
 
-static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
+static ssize_t lcd_proc_write(struct file *file, char *buf,
 			      size_t count, loff_t *pos)
 {
 	struct toshiba_acpi_dev *dev = pde_data(file_inode(file));
@@ -1488,8 +1488,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
 	int value;
 
 	len = min(count, sizeof(cmd) - 1);
-	if (copy_from_user(cmd, buf, len))
-		return -EFAULT;
+	memcpy(cmd, buf, len);
 	cmd[len] = '\0';
 
 	levels = dev->backlight_dev->props.max_brightness + 1;
@@ -1505,7 +1504,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
 
 static const struct proc_ops lcd_proc_ops = {
 	.proc_show	= lcd_proc_show,
-	.proc_write	= lcd_proc_write,
+	.proc_write_buf	= lcd_proc_write,
 };
 
 /* Video-Out */
@@ -1541,24 +1540,17 @@ static int video_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t video_proc_write(struct file *file, const char __user *buf,
+static ssize_t video_proc_write(struct file *file, char *buf,
 				size_t count, loff_t *pos)
 {
 	struct toshiba_acpi_dev *dev = pde_data(file_inode(file));
-	char *buffer;
-	char *cmd;
+	char *buffer = buf;
 	int lcd_out = -1, crt_out = -1, tv_out = -1;
 	int remain = count;
 	int value;
 	int ret;
 	u32 video_out;
 
-	cmd = memdup_user_nul(buf, count);
-	if (IS_ERR(cmd))
-		return PTR_ERR(cmd);
-
-	buffer = cmd;
-
 	/*
 	 * Scan expression.  Multiple expressions may be delimited with ;
 	 * NOTE: To keep scanning simple, invalid fields are ignored.
@@ -1577,8 +1569,6 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
 		} while (remain && *(buffer - 1) != ';');
 	}
 
-	kfree(cmd);
-
 	ret = get_video_status(dev, &video_out);
 	if (!ret) {
 		unsigned int new_video_out = video_out;
@@ -1602,7 +1592,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
 
 static const struct proc_ops video_proc_ops = {
 	.proc_show	= video_proc_show,
-	.proc_write	= video_proc_write,
+	.proc_write_buf	= video_proc_write,
 };
 
 /* Fan status */
@@ -1644,7 +1634,7 @@ static int fan_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t fan_proc_write(struct file *file, const char __user *buf,
+static ssize_t fan_proc_write(struct file *file, char *buf,
 			      size_t count, loff_t *pos)
 {
 	struct toshiba_acpi_dev *dev = pde_data(file_inode(file));
@@ -1653,8 +1643,7 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
 	int value;
 
 	len = min(count, sizeof(cmd) - 1);
-	if (copy_from_user(cmd, buf, len))
-		return -EFAULT;
+	memcpy(cmd, buf, len);
 	cmd[len] = '\0';
 
 	if (sscanf(cmd, " force_on : %i", &value) != 1 ||
@@ -1671,7 +1660,7 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
 
 static const struct proc_ops fan_proc_ops = {
 	.proc_show	= fan_proc_show,
-	.proc_write	= fan_proc_write,
+	.proc_write_buf	= fan_proc_write,
 };
 
 /* Fan RPM */
@@ -1707,7 +1696,7 @@ static int keys_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t keys_proc_write(struct file *file, const char __user *buf,
+static ssize_t keys_proc_write(struct file *file, char *buf,
 			       size_t count, loff_t *pos)
 {
 	struct toshiba_acpi_dev *dev = pde_data(file_inode(file));
@@ -1716,8 +1705,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf,
 	int value;
 
 	len = min(count, sizeof(cmd) - 1);
-	if (copy_from_user(cmd, buf, len))
-		return -EFAULT;
+	memcpy(cmd, buf, len);
 	cmd[len] = '\0';
 
 	if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0)
@@ -1730,7 +1718,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf,
 
 static const struct proc_ops keys_proc_ops = {
 	.proc_show	= keys_proc_show,
-	.proc_write	= keys_proc_write,
+	.proc_write_buf	= keys_proc_write,
 };
 
 static int __maybe_unused version_proc_show(struct seq_file *m, void *v)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 21/22] PNP: pnpbios: let procfs copy in writes
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (19 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 20/22] platform/x86: toshiba_acpi: " Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  2026-11-02 15:01   ` [PATCH RFC 22/22] PNP: isapnp: let procfs copy out reads Christian Brauner
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/bus/pnp/<node> before it pins the entry, and take the node data
from that copy. A node with more than PAGE_SIZE - 1 bytes of data can't
be written anymore. I don't think there are any.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pnp/pnpbios/proc.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c
index 14632a8d0da0..00163aedd738 100644
--- a/drivers/pnp/pnpbios/proc.c
+++ b/drivers/pnp/pnpbios/proc.c
@@ -171,7 +171,7 @@ static int pnpbios_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf,
+static ssize_t pnpbios_proc_write(struct file *file, char *buf,
 				  size_t count, loff_t *pos)
 {
 	void *data = pde_data(file_inode(file));
@@ -191,10 +191,7 @@ static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf,
 		ret = -EINVAL;
 		goto out;
 	}
-	if (copy_from_user(node->data, buf, count)) {
-		ret = -EFAULT;
-		goto out;
-	}
+	memcpy(node->data, buf, count);
 	if (pnp_bios_set_dev_node(node->handle, boot, node) != 0) {
 		ret = -EINVAL;
 		goto out;
@@ -207,7 +204,7 @@ out:
 
 static const struct proc_ops pnpbios_proc_ops = {
 	.proc_show	= pnpbios_proc_show,
-	.proc_write	= pnpbios_proc_write,
+	.proc_write_buf	= pnpbios_proc_write,
 };
 
 int pnpbios_interface_attach_device(struct pnp_bios_node *node)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 22/22] PNP: isapnp: let procfs copy out reads
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
                     ` (20 preceding siblings ...)
  2026-11-02 15:01   ` [PATCH RFC 21/22] PNP: pnpbios: " Christian Brauner
@ 2026-11-02 15:01   ` Christian Brauner
  21 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:01 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read from
/proc/bus/isapnp/<card>/<dev> to userspace after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pnp/isapnp/proc.c | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c
index 8197c7f29743..152fd5bfbc06 100644
--- a/drivers/pnp/isapnp/proc.c
+++ b/drivers/pnp/isapnp/proc.c
@@ -19,37 +19,31 @@ static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence)
 	return fixed_size_llseek(file, off, whence, 256);
 }
 
-static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf,
+static ssize_t isapnp_proc_bus_read(struct file *file, char *buf,
 				    size_t nbytes, loff_t * ppos)
 {
 	struct pnp_dev *dev = pde_data(file_inode(file));
 	loff_t pos = *ppos;
-	int cnt, size = 256;
+	int size = 256;
+	size_t cnt;
 
 	if (pos >= size)
 		return 0;
 	if (nbytes > size - pos)
 		nbytes = size - pos;
-	cnt = nbytes;
-
-	if (!access_ok(buf, cnt))
-		return -EINVAL;
 
 	isapnp_cfg_begin(dev->card->number, dev->number);
-	for (; pos < 256 && cnt > 0; pos++, buf++, cnt--) {
-		unsigned char val;
-		val = isapnp_read_byte(pos);
-		put_user(val, buf);
-	}
+	for (cnt = 0; cnt < nbytes; cnt++)
+		buf[cnt] = isapnp_read_byte(pos + cnt);
 	isapnp_cfg_end();
 
-	*ppos = pos;
+	*ppos = pos + nbytes;
 	return nbytes;
 }
 
 static const struct proc_ops isapnp_proc_bus_proc_ops = {
 	.proc_lseek	= isapnp_proc_bus_lseek,
-	.proc_read	= isapnp_proc_bus_read,
+	.proc_read_buf	= isapnp_proc_bus_read,
 };
 
 static int isapnp_proc_attach_device(struct pnp_dev *dev)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 00/23] proc: convert the driver entries
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
                   ` (2 preceding siblings ...)
  2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
@ 2026-11-02 15:07 ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/23] nubus: let procfs own the seq_file of the resource files Christian Brauner
                     ` (22 more replies)
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                   ` (2 subsequent siblings)
  6 siblings, 23 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

This converts the proc entries of nubus, zorro, PCI, md, dasd and cio,
scsi, dell-smm, viafb, rndis, ipw2x00, sysrq and ALSA to what "proc:
let procfs own the data path" set up: procfs owns the seq_file, copies
in writes, copies out reads and copies ioctl arguments in and out, so
the entries' code only ever runs with the entry pinned and never
touches userspace memory.

One patch per subsystem and method, in the order seq_file, write, read,
ioctl. The PCI config space and ALSA's data entries take binary data at
positions and keep doing that: PROC_ENTRY_SHORT_WRITES for writes in
pieces, and ALSA's data entries ask for PROC_ENTRY_FULL_READS so that
reading all of one at once keeps working. They hand procfs' buffer to
the drivers, so struct snd_info_entry_ops changes along with them. The
PCI ioctls only pass values and the i8k ones encode the wrong sizes,
both list what is really copied. ALSA's ioctl hook that no driver
implements goes away. Where a write that is larger than anything the
entry parses now fails with -EINVAL from procfs I've called that out in
the commit.

The selftest writes to the file of a scsi_debug host from a buffer
backed by userfaultfd and removes the host while procfs copies in. On a
KASAN and lockdep kernel it fails without the series and passes with
it. Build tested per commit on x86_64 and with W=1 on the touched
objects on their own architectures. The entries I could get at in a VM
behave the same with and without the series.

This is on top of "proc: let procfs own the data path". The other three
conversion series don't depend on it and it doesn't depend on them.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (23):
      nubus: let procfs own the seq_file of the resource files
      zorro: let procfs copy out reads
      PCI/proc: let procfs copy in config space writes
      PCI/proc: let procfs copy out config space reads
      PCI/proc: let procfs handle the ioctls
      md: let procfs own the seq_file of /proc/mdstat
      s390/dasd: let procfs copy in writes to /proc/dasd/statistics
      s390/cio: let procfs copy in writes
      scsi: let procfs own the seq_file of the host files
      scsi: core: let procfs copy in writes
      selftests/proc: check that a faulting writer doesn't hold up removal
      scsi: sg: let procfs own the seq_file of allow_dio and def_reserved_size
      scsi: sg: let procfs copy in writes
      scsi: esas2r: let procfs copy the ioctl argument of the ATTONode file
      hwmon: (dell-smm) let procfs copy the i8k ioctl arguments
      fbdev: viafb: let procfs copy in writes
      usb: gadget: function: rndis: let procfs copy in writes
      wifi: ipw2x00: let procfs copy in writes to the debug level
      tty: sysrq: let procfs copy in writes to /proc/sysrq-trigger
      ALSA: info: let procfs own the seq_file of text entries
      ALSA: info: let procfs copy in writes
      ALSA: info: let procfs copy out reads of data entries
      ALSA: info: drop the ioctl hook of data entries

 .../sound/kernel-api/writing-an-alsa-driver.rst    |  10 +-
 drivers/hwmon/dell-smm-hwmon.c                     |  48 ++---
 drivers/md/md.c                                    |  15 +-
 drivers/net/wireless/intel/ipw2x00/libipw_module.c |   7 +-
 drivers/nubus/proc.c                               |  12 +-
 drivers/pci/proc.c                                 |  69 ++++---
 drivers/s390/block/dasd_proc.c                     |  17 +-
 drivers/s390/cio/blacklist.c                       |  33 +---
 drivers/s390/cio/css.c                             |   4 +-
 drivers/scsi/esas2r/esas2r.h                       |   2 +
 drivers/scsi/esas2r/esas2r_ioctl.c                 |  58 +++---
 drivers/scsi/esas2r/esas2r_main.c                  |  44 ++++-
 drivers/scsi/scsi_devinfo.c                        |  29 +--
 drivers/scsi/scsi_proc.c                           |  55 ++----
 drivers/scsi/sg.c                                  |  44 ++---
 drivers/tty/sysrq.c                                |   9 +-
 drivers/usb/gadget/function/rndis.c                |   9 +-
 drivers/video/fbdev/via/viafbdev.c                 |  46 +++--
 drivers/zorro/proc.c                               |   7 +-
 include/sound/gus.h                                |   2 +-
 include/sound/info.h                               |   6 +-
 sound/core/info.c                                  |  57 +++---
 sound/drivers/opl4/opl4_proc.c                     |  23 +--
 sound/isa/gus/gus_dram.c                           |   7 +-
 sound/isa/gus/gus_mem_proc.c                       |   2 +-
 sound/pci/cs4281.c                                 |  10 +-
 sound/pci/cs46xx/cs46xx_lib.c                      |   5 +-
 sound/pci/emu10k1/emuproc.c                        |   8 +-
 sound/pci/mixart/mixart.c                          |  10 +-
 sound/usb/mixer_scarlett2.c                        |   9 +-
 tools/testing/selftests/proc/.gitignore            |   1 +
 tools/testing/selftests/proc/Makefile              |   1 +
 tools/testing/selftests/proc/config                |   3 +
 .../selftests/proc/proc-remove-slow-writer.c       | 203 +++++++++++++++++++++
 34 files changed, 480 insertions(+), 385 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261101-work-procfs-drivers-58cd83649658
prerequisite-change-id: 20261101-work-procfs-core-8fa4a4f412cf:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 01/23] nubus: let procfs own the seq_file of the resource files
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 02/23] zorro: let procfs copy out reads Christian Brauner
                     ` (21 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Get the inode from the file instead of ->private, declare the show
function and let procfs open and free the seq_file.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/nubus/proc.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/drivers/nubus/proc.c b/drivers/nubus/proc.c
index 3ea3d1738eae..cb21ad1d9964 100644
--- a/drivers/nubus/proc.c
+++ b/drivers/nubus/proc.c
@@ -107,7 +107,7 @@ nubus_proc_alloc_pde_data(unsigned char *ptr, unsigned int size)
 
 static int nubus_proc_rsrc_show(struct seq_file *m, void *v)
 {
-	struct inode *inode = m->private;
+	struct inode *inode = file_inode(m->file);
 	struct nubus_proc_pde_data *pded;
 
 	pded = pde_data(inode);
@@ -138,16 +138,8 @@ static int nubus_proc_rsrc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static int nubus_rsrc_proc_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, nubus_proc_rsrc_show, inode);
-}
-
 static const struct proc_ops nubus_rsrc_proc_ops = {
-	.proc_open	= nubus_rsrc_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= single_release,
+	.proc_show	= nubus_proc_rsrc_show,
 };
 
 void nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 02/23] zorro: let procfs copy out reads
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/23] nubus: let procfs own the seq_file of the resource files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 03/23] PCI/proc: let procfs copy in config space writes Christian Brauner
                     ` (20 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read from
/proc/bus/zorro/<slot> to userspace after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/zorro/proc.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/zorro/proc.c b/drivers/zorro/proc.c
index f916bf60b312..4e4ba04752ea 100644
--- a/drivers/zorro/proc.c
+++ b/drivers/zorro/proc.c
@@ -28,7 +28,7 @@ proc_bus_zorro_lseek(struct file *file, loff_t off, int whence)
 }
 
 static ssize_t
-proc_bus_zorro_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
+proc_bus_zorro_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
 {
 	struct zorro_dev *z = pde_data(file_inode(file));
 	struct ConfigDev cd;
@@ -49,8 +49,7 @@ proc_bus_zorro_read(struct file *file, char __user *buf, size_t nbytes, loff_t *
 	cd.cd_BoardAddr = cpu_to_be32(zorro_resource_start(z));
 	cd.cd_BoardSize = cpu_to_be32(zorro_resource_len(z));
 
-	if (copy_to_user(buf, (void *)&cd + pos, nbytes))
-		return -EFAULT;
+	memcpy(buf, (void *)&cd + pos, nbytes);
 	*ppos += nbytes;
 
 	return nbytes;
@@ -58,7 +57,7 @@ proc_bus_zorro_read(struct file *file, char __user *buf, size_t nbytes, loff_t *
 
 static const struct proc_ops bus_zorro_proc_ops = {
 	.proc_lseek	= proc_bus_zorro_lseek,
-	.proc_read	= proc_bus_zorro_read,
+	.proc_read_buf	= proc_bus_zorro_read,
 };
 
 static void * zorro_seq_start(struct seq_file *m, loff_t *pos)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 03/23] PCI/proc: let procfs copy in config space writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/23] nubus: let procfs own the seq_file of the resource files Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 02/23] zorro: let procfs copy out reads Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 04/23] PCI/proc: let procfs copy out config space reads Christian Brauner
                     ` (19 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and write the config space from that copy with
get_unaligned_le16() and get_unaligned_le32() instead of get_user().

The copy holds at most PCI_CFG_SPACE_EXP_SIZE bytes. Larger writes come
up short with PROC_ENTRY_SHORT_WRITES, like they did before because
they were cut at the end of the config space.

A buffer that can't be read used to write zeroes to the config space and
report success. It fails with -EFAULT now and nothing is written.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pci/proc.c | 32 +++++++++++---------------------
 1 file changed, 11 insertions(+), 21 deletions(-)

diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index e0dc70123f82..d420353ee2b3 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -16,6 +16,7 @@
 #include <linux/security.h>
 #include <linux/panic.h>
 #include <linux/sched.h>
+#include <linux/unaligned.h>
 #include <asm/byteorder.h>
 #include "pci.h"
 
@@ -114,8 +115,8 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	return nbytes;
 }
 
-static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
-				  size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_write(struct file *file, char *buf, size_t nbytes,
+				  loff_t *ppos)
 {
 	struct inode *ino = file_inode(file);
 	struct pci_dev *dev = pde_data(ino);
@@ -145,51 +146,38 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
 		nbytes = size - pos;
 	cnt = nbytes;
 
-	if (!access_ok(buf, cnt))
-		return -EINVAL;
-
 	pci_config_pm_runtime_get(dev);
 
 	if ((pos & 1) && cnt) {
-		unsigned char val;
-		get_user(val, buf);
-		pci_user_write_config_byte(dev, pos, val);
+		pci_user_write_config_byte(dev, pos, *buf);
 		buf++;
 		pos++;
 		cnt--;
 	}
 
 	if ((pos & 3) && cnt > 2) {
-		__le16 val;
-		get_user(val, (__le16 __user *) buf);
-		pci_user_write_config_word(dev, pos, le16_to_cpu(val));
+		pci_user_write_config_word(dev, pos, get_unaligned_le16(buf));
 		buf += 2;
 		pos += 2;
 		cnt -= 2;
 	}
 
 	while (cnt >= 4) {
-		__le32 val;
-		get_user(val, (__le32 __user *) buf);
-		pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
+		pci_user_write_config_dword(dev, pos, get_unaligned_le32(buf));
 		buf += 4;
 		pos += 4;
 		cnt -= 4;
 	}
 
 	if (cnt >= 2) {
-		__le16 val;
-		get_user(val, (__le16 __user *) buf);
-		pci_user_write_config_word(dev, pos, le16_to_cpu(val));
+		pci_user_write_config_word(dev, pos, get_unaligned_le16(buf));
 		buf += 2;
 		pos += 2;
 		cnt -= 2;
 	}
 
 	if (cnt) {
-		unsigned char val;
-		get_user(val, buf);
-		pci_user_write_config_byte(dev, pos, val);
+		pci_user_write_config_byte(dev, pos, *buf);
 		pos++;
 	}
 
@@ -337,9 +325,11 @@ static int proc_bus_pci_release(struct inode *inode, struct file *file)
 #endif /* HAVE_PCI_MMAP */
 
 static const struct proc_ops proc_bus_pci_ops = {
+	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_lseek	= proc_bus_pci_lseek,
 	.proc_read	= proc_bus_pci_read,
-	.proc_write	= proc_bus_pci_write,
+	.proc_write_buf	= proc_bus_pci_write,
+	.proc_write_size = PCI_CFG_SPACE_EXP_SIZE,
 	.proc_ioctl	= proc_bus_pci_ioctl,
 #ifdef CONFIG_COMPAT
 	.proc_compat_ioctl = proc_bus_pci_ioctl,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
                   ` (3 preceding siblings ...)
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
@ 2026-11-02 15:07 ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/27] jbd2: let procfs own the seq_file of the info file Christian Brauner
                     ` (26 more replies)
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
  6 siblings, 27 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

This converts the proc entries of jbd2, jfs, lockd, nfsd, ntfs3 and
cifs, the irq affinity files, latencytop, lockdep, /proc/profile,
/proc/pressure, kallsyms, /proc/modules, dynamic_debug, sysvipc,
allocinfo, /proc/swaps and /proc/config.gz to what "proc: let procfs
own the data path" set up: procfs owns the seq_file, copies in writes,
copies out reads and copies ioctl arguments in and out, so the entries'
code only ever runs with the entry pinned and never touches userspace
memory.

One patch per subsystem and method, in the order seq_file, write, read,
ioctl. Where a write that is larger than anything the entry parses now
fails with -EINVAL from procfs I've called that out in the commit.
/proc/profile and /proc/pressure/* are never removed and are marked
permanent, procfs calls into them without pinning. readprofile reads
all of /proc/profile in one go, so it keeps reading straight into
userspace. /proc/config.gz asks for PROC_ENTRY_FULL_READS so that
reading all of it at once keeps working.

The selftest unloads configs.ko while a reader of /proc/config.gz
faults in procfs' copy to userspace. On a KASAN and lockdep kernel it
fails without the series and passes with it. Build tested per commit on
x86_64 and with W=1 on the touched objects. The proc and allocinfo
selftests behave the same with and without the series and so do the
entries I could get at in a VM.

This is on top of "proc: let procfs own the data path". The other three
conversion series don't depend on it and it doesn't depend on them.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (27):
      jbd2: let procfs own the seq_file of the info file
      jfs: let procfs copy in writes to the loglevel file
      lockd: let procfs copy in writes to nlm_end_grace
      lockd: let procfs copy out reads of nlm_end_grace
      nfsd: factor out setting up the exports seq_file
      nfsd: let procfs own the seq_file of /proc/fs/nfs/exports
      ntfs3: let procfs copy in writes to the label file
      smb: client: let procfs own the seq_file of the smbdirect files
      smb: client: let procfs copy in writes
      genirq/proc: let procfs copy in the affinity masks
      latencytop: let procfs copy in writes to /proc/latency_stats
      locking/lockdep: let procfs own the seq_file of /proc/lock_stat
      locking/lockdep: let procfs copy in writes to /proc/lock_stat
      profiling: let procfs copy in writes to /proc/profile
      profiling: mark /proc/profile permanent
      sched/psi: let procfs own the seq_file of the pressure files
      sched/psi: let procfs copy in trigger writes
      sched/psi: mark the pressure files permanent
      kallsyms: let procfs own the seq_file of /proc/kallsyms
      module: procfs: let procfs own the seq_file of /proc/modules
      dyndbg: let procfs copy in writes to /proc/dynamic_debug/control
      ipc: let procfs own the seq_file of the sysvipc files
      alloc_tag: let procfs own the seq_file of /proc/allocinfo
      alloc_tag: let procfs copy the ioctl arguments of /proc/allocinfo
      mm/swapfile: let procfs own the seq_file of /proc/swaps
      kernel/configs: let procfs copy out reads of /proc/config.gz
      selftests/proc: check that unloading configs doesn't wait for a reader

 fs/jbd2/journal.c                                  |  33 ++---
 fs/jfs/jfs_debug.c                                 |  11 +-
 fs/lockd/procfs.c                                  |  19 +--
 fs/nfsd/nfsctl.c                                   |  41 ++++--
 fs/ntfs3/super.c                                   |  27 ++--
 fs/smb/client/cifs_debug.c                         |  71 ++++------
 fs/smb/client/dfs_cache.c                          |  13 +-
 ipc/util.c                                         |  14 +-
 kernel/configs.c                                   |   7 +-
 kernel/irq/proc.c                                  |  27 ++--
 kernel/kallsyms.c                                  |  12 +-
 kernel/latencytop.c                                |   5 +-
 kernel/locking/lockdep_proc.c                      |  60 ++++----
 kernel/module/procfs.c                             |  19 +--
 kernel/profile.c                                   |  11 +-
 kernel/sched/psi.c                                 |  82 ++++-------
 lib/dynamic_debug.c                                |  34 +++--
 mm/alloc_tag.c                                     |  75 ++++------
 mm/swapfile.c                                      |  13 +-
 tools/testing/selftests/proc/.gitignore            |   1 +
 tools/testing/selftests/proc/Makefile              |   1 +
 tools/testing/selftests/proc/config                |   4 +
 .../proc/proc-remove-slow-config-reader.c          | 157 +++++++++++++++++++++
 23 files changed, 400 insertions(+), 337 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261101-work-procfs-fs-41f9fdc489cb
prerequisite-change-id: 20261101-work-procfs-core-8fa4a4f412cf:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 04/23] PCI/proc: let procfs copy out config space reads
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 03/23] PCI/proc: let procfs copy in config space writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 05/23] PCI/proc: let procfs handle the ioctls Christian Brauner
                     ` (18 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry, and read the config space into procfs'
buffer with put_unaligned_le16() and put_unaligned_le32() instead of
put_user(). A read into a buffer that can't be written used to report
success. It fails with -EFAULT now. A page holds all of the config
space, so a single read of it still works, libpci treats a short read as
an error.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pci/proc.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index d420353ee2b3..a8d7469f222b 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -29,8 +29,8 @@ static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
 	return fixed_size_llseek(file, off, whence, dev->cfg_size);
 }
 
-static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
-				 size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_read(struct file *file, char *buf, size_t nbytes,
+				 loff_t *ppos)
 {
 	struct pci_dev *dev = pde_data(file_inode(file));
 	unsigned int pos, cnt, size;
@@ -60,15 +60,12 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 		nbytes = size - pos;
 	cnt = nbytes;
 
-	if (!access_ok(buf, cnt))
-		return -EINVAL;
-
 	pci_config_pm_runtime_get(dev);
 
 	if ((pos & 1) && cnt) {
 		unsigned char val;
 		pci_user_read_config_byte(dev, pos, &val);
-		put_user(val, buf);
+		*buf = val;
 		buf++;
 		pos++;
 		cnt--;
@@ -77,7 +74,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	if ((pos & 3) && cnt > 2) {
 		unsigned short val;
 		pci_user_read_config_word(dev, pos, &val);
-		put_user(cpu_to_le16(val), (__le16 __user *) buf);
+		put_unaligned_le16(val, buf);
 		buf += 2;
 		pos += 2;
 		cnt -= 2;
@@ -86,7 +83,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	while (cnt >= 4) {
 		unsigned int val;
 		pci_user_read_config_dword(dev, pos, &val);
-		put_user(cpu_to_le32(val), (__le32 __user *) buf);
+		put_unaligned_le32(val, buf);
 		buf += 4;
 		pos += 4;
 		cnt -= 4;
@@ -96,7 +93,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	if (cnt >= 2) {
 		unsigned short val;
 		pci_user_read_config_word(dev, pos, &val);
-		put_user(cpu_to_le16(val), (__le16 __user *) buf);
+		put_unaligned_le16(val, buf);
 		buf += 2;
 		pos += 2;
 		cnt -= 2;
@@ -105,7 +102,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
 	if (cnt) {
 		unsigned char val;
 		pci_user_read_config_byte(dev, pos, &val);
-		put_user(val, buf);
+		*buf = val;
 		pos++;
 	}
 
@@ -327,7 +324,7 @@ static int proc_bus_pci_release(struct inode *inode, struct file *file)
 static const struct proc_ops proc_bus_pci_ops = {
 	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_lseek	= proc_bus_pci_lseek,
-	.proc_read	= proc_bus_pci_read,
+	.proc_read_buf	= proc_bus_pci_read,
 	.proc_write_buf	= proc_bus_pci_write,
 	.proc_write_size = PCI_CFG_SPACE_EXP_SIZE,
 	.proc_ioctl	= proc_bus_pci_ioctl,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 01/27] jbd2: let procfs own the seq_file of the info file
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 02/27] jfs: let procfs copy in writes to the loglevel file Christian Brauner
                     ` (25 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and the size of the session state and let
procfs open and free the seq_file of /proc/fs/jbd2/<dev>/info.
->proc_open() only takes the snapshot of the statistics now and
->proc_release() frees it. Reading the file then no longer holds up its
removal when the journal goes away.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/jbd2/journal.c | 33 +++++++++------------------------
 1 file changed, 9 insertions(+), 24 deletions(-)

diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 3a4f3f613ead..1e4f4e95a00e 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -1173,51 +1173,36 @@ static const struct seq_operations jbd2_seq_info_ops = {
 	.show   = jbd2_seq_info_show,
 };
 
+/* procfs opened the seq_file and allocated @s */
 static int jbd2_seq_info_open(struct inode *inode, struct file *file)
 {
 	journal_t *journal = pde_data(inode);
-	struct jbd2_stats_proc_session *s;
-	int rc, size;
+	struct jbd2_stats_proc_session *s = ((struct seq_file *)file->private_data)->private;
+	int size = sizeof(struct transaction_stats_s);
 
-	s = kmalloc_obj(*s);
-	if (s == NULL)
-		return -ENOMEM;
-	size = sizeof(struct transaction_stats_s);
 	s->stats = kmalloc(size, GFP_KERNEL);
-	if (s->stats == NULL) {
-		kfree(s);
+	if (s->stats == NULL)
 		return -ENOMEM;
-	}
 	spin_lock(&journal->j_history_lock);
 	memcpy(s->stats, &journal->j_stats, size);
 	s->journal = journal;
 	spin_unlock(&journal->j_history_lock);
-
-	rc = seq_open(file, &jbd2_seq_info_ops);
-	if (rc == 0) {
-		struct seq_file *m = file->private_data;
-		m->private = s;
-	} else {
-		kfree(s->stats);
-		kfree(s);
-	}
-	return rc;
-
+	return 0;
 }
 
 static int jbd2_seq_info_release(struct inode *inode, struct file *file)
 {
 	struct seq_file *seq = file->private_data;
 	struct jbd2_stats_proc_session *s = seq->private;
+
 	kfree(s->stats);
-	kfree(s);
-	return seq_release(inode, file);
+	return 0;
 }
 
 static const struct proc_ops jbd2_info_proc_ops = {
+	.proc_seq_ops	= &jbd2_seq_info_ops,
+	.proc_state_size = sizeof(struct jbd2_stats_proc_session),
 	.proc_open	= jbd2_seq_info_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= jbd2_seq_info_release,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 05/23] PCI/proc: let procfs handle the ioctls
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 04/23] PCI/proc: let procfs copy out config space reads Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 06/23] md: let procfs own the seq_file of /proc/mdstat Christian Brauner
                     ` (17 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

The PCIIOC_* commands take and return plain values and don't touch
userspace memory. Implement ->proc_ioctl_buf() and list them with
nothing to copy. 32-bit tasks go through the same table, so the compat
hook goes away. Unknown commands fail with -ENOTTY from procfs now
instead of -EINVAL, and instead of -EPERM under lockdown, since they
never reach the handler.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/pci/proc.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index a8d7469f222b..6b0cb5bcbea3 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -192,8 +192,7 @@ struct pci_filp_private {
 };
 #endif /* HAVE_PCI_MMAP */
 
-static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
-			       unsigned long arg)
+static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, void *arg)
 {
 	struct pci_dev *dev = pde_data(file_inode(file));
 #ifdef HAVE_PCI_MMAP
@@ -321,16 +320,23 @@ static int proc_bus_pci_release(struct inode *inode, struct file *file)
 }
 #endif /* HAVE_PCI_MMAP */
 
+/* The commands take and return plain values. */
+static const struct proc_ioctl_arg proc_bus_pci_ioctl_args[] = {
+	PROC_IOCTL_ARG(PCIIOC_CONTROLLER, 0, 0),
+	PROC_IOCTL_ARG(PCIIOC_MMAP_IS_IO, 0, 0),
+	PROC_IOCTL_ARG(PCIIOC_MMAP_IS_MEM, 0, 0),
+	PROC_IOCTL_ARG(PCIIOC_WRITE_COMBINE, 0, 0),
+};
+
 static const struct proc_ops proc_bus_pci_ops = {
 	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_lseek	= proc_bus_pci_lseek,
 	.proc_read_buf	= proc_bus_pci_read,
 	.proc_write_buf	= proc_bus_pci_write,
 	.proc_write_size = PCI_CFG_SPACE_EXP_SIZE,
-	.proc_ioctl	= proc_bus_pci_ioctl,
-#ifdef CONFIG_COMPAT
-	.proc_compat_ioctl = proc_bus_pci_ioctl,
-#endif
+	.proc_ioctl_buf	= proc_bus_pci_ioctl,
+	.proc_ioctl_args = proc_bus_pci_ioctl_args,
+	.proc_nr_ioctl_args = ARRAY_SIZE(proc_bus_pci_ioctl_args),
 #ifdef HAVE_PCI_MMAP
 	.proc_open	= proc_bus_pci_open,
 	.proc_release	= proc_bus_pci_release,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 02/27] jfs: let procfs copy in writes to the loglevel file
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/27] jbd2: let procfs own the seq_file of the info file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 03/27] lockd: let procfs copy in writes to nlm_end_grace Christian Brauner
                     ` (24 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and take the level from that copy. Writes larger than
PAGE_SIZE - 1 bytes fail with -EINVAL now and so do empty writes, which
looked at a byte that wasn't written before.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/jfs/jfs_debug.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c
index 5eb35b0d420f..e1c0211a6a32 100644
--- a/fs/jfs/jfs_debug.c
+++ b/fs/jfs/jfs_debug.c
@@ -23,13 +23,10 @@ static int jfs_loglevel_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t jfs_loglevel_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t jfs_loglevel_proc_write(struct file *file, char *buffer,
+		size_t count, loff_t *ppos)
 {
-	char c;
-
-	if (get_user(c, buffer))
-		return -EFAULT;
+	char c = buffer[0];
 
 	/* yes, I know this is an ASCIIism.  --hch */
 	if (c < '0' || c > '9')
@@ -40,7 +37,7 @@ static ssize_t jfs_loglevel_proc_write(struct file *file,
 
 static const struct proc_ops jfs_loglevel_proc_ops = {
 	.proc_show	= jfs_loglevel_proc_show,
-	.proc_write	= jfs_loglevel_proc_write,
+	.proc_write_buf	= jfs_loglevel_proc_write,
 };
 #endif
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 06/23] md: let procfs own the seq_file of /proc/mdstat
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 05/23] PCI/proc: let procfs handle the ioctls Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 07/23] s390/dasd: let procfs copy in writes to /proc/dasd/statistics Christian Brauner
                     ` (16 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and let procfs open and free the seq_file.
->proc_open() only records the event count for ->proc_poll() now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/md/md.c | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/drivers/md/md.c b/drivers/md/md.c
index 680b34a63cb3..e642b763ebc2 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9141,18 +9141,13 @@ static const struct seq_operations md_seq_ops = {
 	.show   = md_seq_show,
 };
 
+/* procfs opens the seq_file */
 static int md_seq_open(struct inode *inode, struct file *file)
 {
-	struct seq_file *seq;
-	int error;
-
-	error = seq_open(file, &md_seq_ops);
-	if (error)
-		return error;
+	struct seq_file *seq = file->private_data;
 
-	seq = file->private_data;
 	seq->poll_event = atomic_read(&md_event_count);
-	return error;
+	return 0;
 }
 
 static int md_unloading;
@@ -9174,10 +9169,8 @@ static __poll_t mdstat_poll(struct file *filp, poll_table *wait)
 }
 
 static const struct proc_ops mdstat_proc_ops = {
+	.proc_seq_ops	= &md_seq_ops,
 	.proc_open	= md_seq_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
 	.proc_poll	= mdstat_poll,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 03/27] lockd: let procfs copy in writes to nlm_end_grace
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/27] jbd2: let procfs own the seq_file of the info file Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 02/27] jfs: let procfs copy in writes to the loglevel file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:23     ` Chuck Lever
  2026-11-02 15:07   ` [PATCH RFC 04/27] lockd: let procfs copy out reads of nlm_end_grace Christian Brauner
                     ` (23 subsequent siblings)
  26 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry. simple_transaction_get() was only used to get a copy
of what was written, so look at procfs' copy instead and drop the
transaction. A second write to the same file doesn't fail with -EBUSY
anymore. Writes larger than PAGE_SIZE - 1 bytes fail with -EINVAL from
procfs instead of -EFBIG from simple_transaction_get(), which refused a
few bytes less.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/lockd/procfs.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/fs/lockd/procfs.c b/fs/lockd/procfs.c
index a01f08c8c2f3..47949bec7a09 100644
--- a/fs/lockd/procfs.c
+++ b/fs/lockd/procfs.c
@@ -18,21 +18,15 @@
  * We only allow strings that start with 'Y', 'y', or '1'.
  */
 static ssize_t
-nlm_end_grace_write(struct file *file, const char __user *buf, size_t size,
-		    loff_t *pos)
+nlm_end_grace_write(struct file *file, char *buf, size_t size, loff_t *pos)
 {
-	char *data;
 	struct lockd_net *ln = net_generic(current->nsproxy->net_ns,
 					   lockd_net_id);
 
 	if (size < 1)
 		return -EINVAL;
 
-	data = simple_transaction_get(file, buf, size);
-	if (IS_ERR(data))
-		return PTR_ERR(data);
-
-	switch(data[0]) {
+	switch(buf[0]) {
 	case 'Y':
 	case 'y':
 	case '1':
@@ -61,10 +55,9 @@ nlm_end_grace_read(struct file *file, char __user *buf, size_t size,
 }
 
 static const struct proc_ops lockd_end_grace_proc_ops = {
-	.proc_write	= nlm_end_grace_write,
+	.proc_write_buf	= nlm_end_grace_write,
 	.proc_read	= nlm_end_grace_read,
 	.proc_lseek	= default_llseek,
-	.proc_release	= simple_transaction_release,
 };
 
 int __init

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 07/23] s390/dasd: let procfs copy in writes to /proc/dasd/statistics
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 06/23] md: let procfs own the seq_file of /proc/mdstat Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 08/23] s390/cio: let procfs copy in writes Christian Brauner
                     ` (15 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and parse the command from that copy. Commands longer
than PAGE_SIZE - 1 bytes are rejected now instead of cut at 64k.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/s390/block/dasd_proc.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 86399427f01d..6c3bbb7dfe85 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -15,7 +15,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/seq_file.h>
-#include <linux/vmalloc.h>
 #include <linux/proc_fs.h>
 
 #include <asm/debug.h>
@@ -247,17 +246,15 @@ static int dasd_stats_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t dasd_stats_proc_write(struct file *file,
-		const char __user *user_buf, size_t user_len, loff_t *pos)
+		char *buffer, size_t user_len, loff_t *pos)
 {
 #ifdef CONFIG_DASD_PROFILE
-	char *buffer, *str;
+	char *str;
 	int rc;
 
-	if (user_len > 65536)
-		user_len = 65536;
-	buffer = dasd_get_user_string(user_buf, user_len);
-	if (IS_ERR(buffer))
-		return PTR_ERR(buffer);
+	/* strip linefeed */
+	if (user_len && buffer[user_len - 1] == '\n')
+		buffer[user_len - 1] = 0;
 
 	/* check for valid verbs */
 	str = skip_spaces(buffer);
@@ -296,13 +293,11 @@ static ssize_t dasd_stats_proc_write(struct file *file,
 		pr_info("The statistics have been reset\n");
 	} else
 		goto out_parse_error;
-	vfree(buffer);
 	return user_len;
 out_parse_error:
 	rc = -EINVAL;
 	pr_warn("%s is not a supported value for /proc/dasd/statistics\n", str);
 out_error:
-	vfree(buffer);
 	return rc;
 #else
 	pr_warn("/proc/dasd/statistics: is not activated in this kernel\n");
@@ -312,7 +307,7 @@ out_error:
 
 static const struct proc_ops dasd_stats_proc_ops = {
 	.proc_show	= dasd_stats_proc_show,
-	.proc_write	= dasd_stats_proc_write,
+	.proc_write_buf	= dasd_stats_proc_write,
 };
 
 /*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 04/27] lockd: let procfs copy out reads of nlm_end_grace
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 03/27] lockd: let procfs copy in writes to nlm_end_grace Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:24     ` Chuck Lever
  2026-11-02 15:07   ` [PATCH RFC 05/27] nfsd: factor out setting up the exports seq_file Christian Brauner
                     ` (22 subsequent siblings)
  26 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/lockd/procfs.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/lockd/procfs.c b/fs/lockd/procfs.c
index 47949bec7a09..34a7c3705bb6 100644
--- a/fs/lockd/procfs.c
+++ b/fs/lockd/procfs.c
@@ -40,7 +40,7 @@ nlm_end_grace_write(struct file *file, char *buf, size_t size, loff_t *pos)
 }
 
 static ssize_t
-nlm_end_grace_read(struct file *file, char __user *buf, size_t size,
+nlm_end_grace_read(struct file *file, char *buf, size_t size,
 		   loff_t *pos)
 {
 	struct lockd_net *ln = net_generic(current->nsproxy->net_ns,
@@ -51,12 +51,12 @@ nlm_end_grace_read(struct file *file, char __user *buf, size_t size,
 	resp[1] = '\n';
 	resp[2] = '\0';
 
-	return simple_read_from_buffer(buf, size, pos, resp, sizeof(resp));
+	return memory_read_from_buffer(buf, size, pos, resp, sizeof(resp));
 }
 
 static const struct proc_ops lockd_end_grace_proc_ops = {
 	.proc_write_buf	= nlm_end_grace_write,
-	.proc_read	= nlm_end_grace_read,
+	.proc_read_buf	= nlm_end_grace_read,
 	.proc_lseek	= default_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
                   ` (4 preceding siblings ...)
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
@ 2026-11-02 15:07 ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/11] atm: let procfs copy out reads of the device files Christian Brauner
                     ` (10 more replies)
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
  6 siblings, 11 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

This converts the proc entries of atm, l2tp, pktgen, xt_recent and
sunrpc, and the kfifo samples, to what "proc: let procfs own the data
path" set up: procfs owns the seq_file, copies in writes, copies out
reads and copies ioctl arguments in and out, so the entries' code only
ever runs with the entry pinned and never touches userspace memory.

One patch per subsystem and method, in the order seq_file, write, read,
ioctl. The sunrpc channel files take 32k downcalls, which is what
->proc_write_size is for, and their ioctl goes away: do_vfs_ioctl()
answers FIONREAD for regular files before the file's ioctl ever sees
it. The sunrpc channel and the kfifo samples are streams. They hand out
what is read before procfs copies it out, so a reader that passes a bad
buffer loses that part. Where a write that is larger than anything the
entry parses now fails with -EINVAL from procfs I've called that out in
the commit.

Build tested per commit on x86_64 and with W=1 on the touched objects.
The pktgen selftest behaves the same with and without the series and so
do the entries I could get at in a VM.

This is on top of "proc: let procfs own the data path". The other three
conversion series don't depend on it and it doesn't depend on them.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (11):
      atm: let procfs copy out reads of the device files
      l2tp: let procfs own the seq_file of /proc/net/pppol2tp
      net: pktgen: let procfs copy in commands
      netfilter: xt_recent: let procfs own the seq_file
      netfilter: xt_recent: let procfs copy in writes
      SUNRPC: let procfs own the seq_file of the cache content file
      SUNRPC: let procfs copy in writes
      SUNRPC: let procfs copy out reads
      SUNRPC: drop the ioctl of the procfs channel files
      samples/kfifo: let procfs copy in writes
      samples/kfifo: let procfs copy out reads

 net/atm/proc.c                     |   9 ++-
 net/core/pktgen.c                  | 128 +++++++++++++----------------------
 net/l2tp/l2tp_ppp.c                |  20 +++---
 net/netfilter/xt_recent.c          |  19 ++----
 net/sunrpc/auth_gss/svcauth_gss.c  |  34 +++-------
 net/sunrpc/cache.c                 | 133 ++++++++++++++++++++++---------------
 samples/kfifo/bytestream-example.c |  18 ++---
 samples/kfifo/inttype-example.c    |  20 +++---
 samples/kfifo/record-example.c     |  21 +++---
 9 files changed, 177 insertions(+), 225 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261101-work-procfs-net-bc8b21e3eff0
prerequisite-change-id: 20261101-work-procfs-core-8fa4a4f412cf:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 08/23] s390/cio: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 07/23] s390/dasd: let procfs copy in writes to /proc/dasd/statistics Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 09/23] scsi: let procfs own the seq_file of the host files Christian Brauner
                     ` (14 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for cio_ignore and cio_settle so procfs
copies in what is written before it pins the entry. cio_ignore parses
the list from that copy, up to the first NUL like strncpy_from_user()
handed it to it before. Writes larger than 64k still come up short, now
through PROC_ENTRY_SHORT_WRITES. Writes to cio_settle larger than
PAGE_SIZE - 1 bytes fail with -EINVAL now, it never looked at what was
written.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/s390/cio/blacklist.c | 33 ++++++++++-----------------------
 drivers/s390/cio/css.c       |  4 ++--
 2 files changed, 12 insertions(+), 25 deletions(-)

diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 4e02f78f6102..1ec7915be6b3 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -12,7 +12,7 @@
 
 #include <linux/hex.h>
 #include <linux/init.h>
-#include <linux/vmalloc.h>
+#include <linux/sizes.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/ctype.h>
@@ -355,39 +355,24 @@ cio_ignore_proc_seq_show(struct seq_file *s, void *it)
 }
 
 static ssize_t
-cio_ignore_write(struct file *file, const char __user *user_buf,
+cio_ignore_write(struct file *file, char *buf,
 		 size_t user_len, loff_t *offset)
 {
-	char *buf;
-	ssize_t rc, ret, i;
+	ssize_t ret, i;
 
 	if (*offset)
 		return -EINVAL;
-	if (user_len > 65536)
-		user_len = 65536;
-	buf = vzalloc(user_len + 1); /* maybe better use the stack? */
-	if (buf == NULL)
-		return -ENOMEM;
 
-	if (strncpy_from_user (buf, user_buf, user_len) < 0) {
-		rc = -EFAULT;
-		goto out_free;
-	}
-
-	i = user_len - 1;
+	/* the parser never saw what follows a NUL */
+	i = strnlen(buf, user_len) - 1;
 	while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
 		buf[i] = '\0';
 		i--;
 	}
 	ret = blacklist_parse_proc_parameters(buf);
 	if (ret)
-		rc = ret;
-	else
-		rc = user_len;
-
-out_free:
-	vfree (buf);
-	return rc;
+		return ret;
+	return user_len;
 }
 
 static const struct seq_operations cio_ignore_proc_seq_ops = {
@@ -398,9 +383,11 @@ static const struct seq_operations cio_ignore_proc_seq_ops = {
 };
 
 static const struct proc_ops cio_ignore_proc_ops = {
+	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_seq_ops	= &cio_ignore_proc_seq_ops,
 	.proc_state_size = sizeof(struct ccwdev_iter),
-	.proc_write	= cio_ignore_write,
+	.proc_write_buf	= cio_ignore_write,
+	.proc_write_size = SZ_64K,
 };
 
 static int
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 54b8b8639694..efd3df3e90ba 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -1278,7 +1278,7 @@ static int __init channel_subsystem_init_sync(void)
 subsys_initcall_sync(channel_subsystem_init_sync);
 
 #ifdef CONFIG_PROC_FS
-static ssize_t cio_settle_write(struct file *file, const char __user *buf,
+static ssize_t cio_settle_write(struct file *file, char *buf,
 				size_t count, loff_t *ppos)
 {
 	int ret;
@@ -1292,7 +1292,7 @@ static ssize_t cio_settle_write(struct file *file, const char __user *buf,
 
 static const struct proc_ops cio_settle_proc_ops = {
 	.proc_open	= nonseekable_open,
-	.proc_write	= cio_settle_write,
+	.proc_write_buf	= cio_settle_write,
 };
 
 static int __init cio_settle_init(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 05/27] nfsd: factor out setting up the exports seq_file
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 04/27] lockd: let procfs copy out reads of nlm_end_grace Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 21:14     ` Chuck Lever
  2026-11-02 15:07   ` [PATCH RFC 06/27] nfsd: let procfs own the seq_file of /proc/fs/nfs/exports Christian Brauner
                     ` (21 subsequent siblings)
  26 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Move setting up ->private and taking the reference to the network
namespace into exports_net_init() and dropping it into
exports_net_put(). The procfs file needs them without the seq_open()
and seq_release() around them.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/nfsd/nfsctl.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index f32311f2d7cf..f8ce3c0fb077 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -143,28 +143,36 @@ static const struct file_operations transaction_ops = {
 	.llseek		= default_llseek,
 };
 
+static void exports_net_init(struct net *net, struct seq_file *seq)
+{
+	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+
+	seq->private = nn->svc_export_cache;
+	get_net(net);
+}
+
+static void exports_net_put(struct seq_file *seq)
+{
+	struct cache_detail *cd = seq->private;
+
+	put_net(cd->net);
+}
+
 static int exports_net_open(struct net *net, struct file *file)
 {
 	int err;
-	struct seq_file *seq;
-	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
 	err = seq_open(file, &nfs_exports_op);
 	if (err)
 		return err;
 
-	seq = file->private_data;
-	seq->private = nn->svc_export_cache;
-	get_net(net);
+	exports_net_init(net, file->private_data);
 	return 0;
 }
 
 static int exports_release(struct inode *inode, struct file *file)
 {
-	struct seq_file *seq = file->private_data;
-	struct cache_detail *cd = seq->private;
-
-	put_net(cd->net);
+	exports_net_put(file->private_data);
 	return seq_release(inode, file);
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 01/11] atm: let procfs copy out reads of the device files
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 02/11] l2tp: let procfs own the seq_file of /proc/net/pppol2tp Christian Brauner
                     ` (9 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read from
/proc/net/atm/<dev> to userspace after it unpinned the entry. The
drivers' ->proc_read() keep filling their page.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/atm/proc.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/net/atm/proc.c b/net/atm/proc.c
index ca0fc6b90998..cea930b4b840 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -30,11 +30,11 @@
 #include "resources.h"
 #include "common.h" /* atm_proc_init prototype */
 
-static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
+static ssize_t proc_dev_atm_read(struct file *file, char *buf,
 				 size_t count, loff_t *pos);
 
 static const struct proc_ops atm_dev_proc_ops = {
-	.proc_read	= proc_dev_atm_read,
+	.proc_read_buf	= proc_dev_atm_read,
 	.proc_lseek	= noop_llseek,
 };
 
@@ -246,7 +246,7 @@ static const struct seq_operations vcc_seq_ops = {
 	.show	= vcc_seq_show,
 };
 
-static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
+static ssize_t proc_dev_atm_read(struct file *file, char *buf,
 				 size_t count, loff_t *pos)
 {
 	struct atm_dev *dev;
@@ -267,8 +267,7 @@ static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
 			length = -EINVAL;
 	}
 	if (length >= 0) {
-		if (copy_to_user(buf, (char *)page, length))
-			length = -EFAULT;
+		memcpy(buf, (char *)page, length);
 		(*pos)++;
 	}
 	free_page(page);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 09/23] scsi: let procfs own the seq_file of the host files
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 08/23] s390/cio: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 10/23] scsi: core: let procfs copy in writes Christian Brauner
                     ` (13 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show function and let procfs open and free the seq_file of
/proc/scsi/<driver>/<host>. ->proc_open() only preallocates the buffer
now. Reading the file then no longer holds up its removal when the host
goes away.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/scsi_proc.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index 3e49c4c8704f..59229bf9938e 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -94,10 +94,10 @@ static int proc_scsi_show(struct seq_file *m, void *v)
 	return shost->hostt->show_info(m, shost);
 }
 
+/* procfs opens the seq_file */
 static int proc_scsi_host_open(struct inode *inode, struct file *file)
 {
-	return single_open_size(file, proc_scsi_show, pde_data(inode),
-				4 * PAGE_SIZE);
+	return seq_preallocate(file->private_data, 4 * PAGE_SIZE);
 }
 
 static struct scsi_proc_entry *
@@ -140,10 +140,8 @@ scsi_template_proc_dir(const struct scsi_host_template *sht)
 EXPORT_SYMBOL_GPL(scsi_template_proc_dir);
 
 static const struct proc_ops proc_scsi_ops = {
+	.proc_show	= proc_scsi_show,
 	.proc_open	= proc_scsi_host_open,
-	.proc_release	= single_release,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
 	.proc_write	= proc_scsi_host_write
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 06/27] nfsd: let procfs own the seq_file of /proc/fs/nfs/exports
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 05/27] nfsd: factor out setting up the exports seq_file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 21:14     ` Chuck Lever
  2026-11-02 15:07   ` [PATCH RFC 07/27] ntfs3: let procfs copy in writes to the label file Christian Brauner
                     ` (20 subsequent siblings)
  26 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and let procfs open and free the seq_file.
->proc_open() and ->proc_release() only set up ->private and take and
drop the reference to the network namespace now. Reading the file then
no longer holds up its removal.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/nfsd/nfsctl.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index f8ce3c0fb077..5b2782f9dbbd 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1407,16 +1407,23 @@ MODULE_ALIAS_FS("nfsd");
 
 #ifdef CONFIG_PROC_FS
 
+/* procfs opens the seq_file */
 static int exports_proc_open(struct inode *inode, struct file *file)
 {
-	return exports_net_open(current->nsproxy->net_ns, file);
+	exports_net_init(current->nsproxy->net_ns, file->private_data);
+	return 0;
+}
+
+static int exports_proc_release(struct inode *inode, struct file *file)
+{
+	exports_net_put(file->private_data);
+	return 0;
 }
 
 static const struct proc_ops exports_proc_ops = {
+	.proc_seq_ops	= &nfs_exports_op,
 	.proc_open	= exports_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= exports_release,
+	.proc_release	= exports_proc_release,
 };
 
 static int create_proc_exports_entry(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 02/11] l2tp: let procfs own the seq_file of /proc/net/pppol2tp
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/11] atm: let procfs copy out reads of the device files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 03/11] net: pktgen: let procfs copy in commands Christian Brauner
                     ` (8 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and the size of the iterator state and let
procfs open and free the seq_file. ->proc_open() only takes the
reference to the network namespace now and ->proc_release() drops that
and the references the iterator holds. Reading the file then no longer
holds up its removal.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/l2tp/l2tp_ppp.c | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 4f311d473a64..499e37eb788a 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -1596,21 +1596,17 @@ static const struct seq_operations pppol2tp_seq_ops = {
 	.show		= pppol2tp_seq_show,
 };
 
+/* procfs opened the seq_file and allocated @pd */
 static int pppol2tp_proc_open(struct inode *inode, struct file *file)
 {
-	struct net *net = pde_data(inode);
-	struct pppol2tp_seq_data *pd;
+	struct seq_file *seq = file->private_data;
+	struct pppol2tp_seq_data *pd __maybe_unused = seq->private;
+	struct net *net;
 
-	net = maybe_get_net(net);
+	net = maybe_get_net(pde_data(inode));
 	if (!net)
 		return -ENXIO;
 
-	pd = __seq_open_private(file, &pppol2tp_seq_ops, sizeof(*pd));
-	if (!pd) {
-		put_net(net);
-		return -ENOMEM;
-	}
-
 #ifdef CONFIG_NET_NS
 	pd->p.net = net;
 	netns_tracker_alloc(net, &pd->p.ns_tracker, GFP_KERNEL);
@@ -1633,13 +1629,13 @@ static int pppol2tp_proc_release(struct inode *inode, struct file *file)
 #else
 	put_net(&init_net);
 #endif
-	return seq_release_private(inode, file);
+	return 0;
 }
 
 static const struct proc_ops pppol2tp_proc_ops = {
+	.proc_seq_ops	= &pppol2tp_seq_ops,
+	.proc_state_size = sizeof(struct pppol2tp_seq_data),
 	.proc_open	= pppol2tp_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= pppol2tp_proc_release,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 10/23] scsi: core: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 09/23] scsi: let procfs own the seq_file of the host files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 11/23] selftests/proc: check that a faulting writer doesn't hold up removal Christian Brauner
                     ` (12 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for /proc/scsi/scsi, /proc/scsi/device_info
and the host files so procfs copies in what is written before it pins
the entry, and parse what was written from that copy. /proc/scsi/scsi
and device_info take up to PAGE_SIZE bytes like before. The host files
pass the copy to ->write_info(), writes to them that are larger than
PAGE_SIZE - 1 bytes fail with -EINVAL from procfs instead of -EOVERFLOW.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/scsi_devinfo.c | 29 +++++-----------------------
 drivers/scsi/scsi_proc.c    | 47 +++++++++++----------------------------------
 2 files changed, 16 insertions(+), 60 deletions(-)

diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 0b8f5ff40543..d9093851b23e 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -678,39 +678,20 @@ static const struct seq_operations scsi_devinfo_seq_ops = {
  * To use, echo "vendor:model:flag" > /proc/scsi/device_info
  */
 static ssize_t proc_scsi_devinfo_write(struct file *file,
-				       const char __user *buf,
+				       char *buffer,
 				       size_t length, loff_t *ppos)
 {
-	char *buffer;
-	ssize_t err = length;
-
-	if (!buf || length>PAGE_SIZE)
+	if (length == PAGE_SIZE && buffer[PAGE_SIZE-1])
 		return -EINVAL;
-	buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-	if (copy_from_user(buffer, buf, length)) {
-		err =-EFAULT;
-		goto out;
-	}
-
-	if (length < PAGE_SIZE)
-		buffer[length] = '\0';
-	else if (buffer[PAGE_SIZE-1]) {
-		err = -EINVAL;
-		goto out;
-	}
 
 	scsi_dev_info_list_add_str(buffer);
-
-out:
-	kfree(buffer);
-	return err;
+	return length;
 }
 
 static const struct proc_ops scsi_devinfo_proc_ops = {
 	.proc_seq_ops	= &scsi_devinfo_seq_ops,
-	.proc_write	= proc_scsi_devinfo_write,
+	.proc_write_buf	= proc_scsi_devinfo_write,
+	.proc_write_size = PAGE_SIZE,
 };
 #endif /* CONFIG_SCSI_PROC_FS */
 
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index 59229bf9938e..5962e671cc83 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -62,30 +62,18 @@ struct scsi_proc_entry {
 	unsigned int		present;
 };
 
-static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
+static ssize_t proc_scsi_host_write(struct file *file, char *buf,
                            size_t count, loff_t *ppos)
 {
 	struct Scsi_Host *shost = pde_data(file_inode(file));
-	ssize_t ret = -ENOMEM;
-	char *page;
-    
+
 	if (count > PROC_BLOCK_SIZE)
 		return -EOVERFLOW;
 
 	if (!shost->hostt->write_info)
 		return -EINVAL;
 
-	page = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (page) {
-		ret = -EFAULT;
-		if (copy_from_user(page, buf, count))
-			goto out;
-		page[count] = '\0';
-		ret = shost->hostt->write_info(shost, page, count);
-	}
-out:
-	kfree(page);
-	return ret;
+	return shost->hostt->write_info(shost, buf, count);
 }
 
 static int proc_scsi_show(struct seq_file *m, void *v)
@@ -142,7 +130,7 @@ EXPORT_SYMBOL_GPL(scsi_template_proc_dir);
 static const struct proc_ops proc_scsi_ops = {
 	.proc_show	= proc_scsi_show,
 	.proc_open	= proc_scsi_host_open,
-	.proc_write	= proc_scsi_host_write
+	.proc_write_buf	= proc_scsi_host_write
 };
 
 /**
@@ -386,8 +374,8 @@ static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
 /**
  * proc_scsi_write - handle writes to /proc/scsi/scsi
  * @file: not used
- * @buf: buffer to write
- * @length: length of buf, at most PAGE_SIZE
+ * @buffer: buffer to write
+ * @length: length of buffer, at most PAGE_SIZE
  * @ppos: not used
  *
  * Description: this provides a legacy mechanism to add or remove devices by
@@ -402,32 +390,20 @@ static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
  */
 
 
-static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
+static ssize_t proc_scsi_write(struct file *file, char *buffer,
 			       size_t length, loff_t *ppos)
 {
 	int host, channel, id, lun;
-	char *buffer, *end, *p;
+	char *end, *p;
 	int err;
 
-	if (!buf || length > PAGE_SIZE)
-		return -EINVAL;
-
-	buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!buffer)
-		return -ENOMEM;
-
-	err = -EFAULT;
-	if (copy_from_user(buffer, buf, length))
-		goto out;
-
 	err = -EINVAL;
 	if (length < PAGE_SIZE) {
 		end = buffer + length;
-		*end = '\0';
 	} else {
 		end = buffer + PAGE_SIZE - 1;
 		if (*end)
-			goto out;
+			return err;
 	}
 
 	/*
@@ -466,8 +442,6 @@ static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
 	if (!err)
 		err = length;
 
- out:
-	kfree(buffer);
 	return err;
 }
 
@@ -521,7 +495,8 @@ static const struct seq_operations scsi_seq_ops = {
 
 static const struct proc_ops scsi_scsi_proc_ops = {
 	.proc_seq_ops	= &scsi_seq_ops,
-	.proc_write	= proc_scsi_write,
+	.proc_write_buf	= proc_scsi_write,
+	.proc_write_size = PAGE_SIZE,
 };
 
 /**

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 07/27] ntfs3: let procfs copy in writes to the label file
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 06/27] nfsd: let procfs own the seq_file of /proc/fs/nfs/exports Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 08/27] smb: client: let procfs own the seq_file of the smbdirect files Christian Brauner
                     ` (19 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and set the label from that copy. Writes larger than
PAGE_SIZE - 1 bytes fail with -EINVAL from procfs now. A write that
large only ever worked when most of it was trailing newlines or came
after a NUL, ntfs_set_label() refused a label that long with
-ENAMETOOLONG or -EFBIG.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/ntfs3/super.c | 27 +++++++++------------------
 1 file changed, 9 insertions(+), 18 deletions(-)

diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index 3d669e957ea4..fca73d2766c0 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -536,37 +536,28 @@ static int ntfs3_label_show(struct seq_file *m, void *o)
 }
 
 /* write /proc/fs/ntfs3/<dev>/label */
-static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
-				 size_t count, loff_t *ppos)
+static ssize_t ntfs3_label_write(struct file *file, char *buf, size_t count,
+				 loff_t *ppos)
 {
 	int err;
 	struct super_block *sb = pde_data(file_inode(file));
-	ssize_t ret = count;
-	u8 *label;
+	size_t len = count;
 
 	if (sb_rdonly(sb))
 		return -EROFS;
 
-	label = memdup_user(buffer, count);
-	if (IS_ERR(label))
-		return PTR_ERR(label);
+	while (len > 0 && buf[len - 1] == '\n')
+		len -= 1;
 
-	while (ret > 0 && label[ret - 1] == '\n')
-		ret -= 1;
-
-	err = ntfs_set_label(sb->s_fs_info, label, ret);
+	err = ntfs_set_label(sb->s_fs_info, (u8 *)buf, len);
 
 	if (err < 0) {
 		ntfs_err(sb, "failed (%d) to write label", err);
-		ret = err;
-		goto out;
+		return err;
 	}
 
 	*ppos += count;
-	ret = count;
-out:
-	kfree(label);
-	return ret;
+	return count;
 }
 
 static const struct proc_ops ntfs3_volinfo_fops = {
@@ -575,7 +566,7 @@ static const struct proc_ops ntfs3_volinfo_fops = {
 
 static const struct proc_ops ntfs3_label_fops = {
 	.proc_show = ntfs3_label_show,
-	.proc_write = ntfs3_label_write,
+	.proc_write_buf = ntfs3_label_write,
 };
 
 static void ntfs_create_procdir(struct super_block *sb)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 03/11] net: pktgen: let procfs copy in commands
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 01/11] atm: let procfs copy out reads of the device files Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 02/11] l2tp: let procfs own the seq_file of /proc/net/pppol2tp Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 04/11] netfilter: xt_recent: let procfs own the seq_file Christian Brauner
                     ` (7 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the files in /proc/net/pktgen so
procfs copies in what is written before it pins the entry, and parse
the commands from that copy. The parse helpers read the copy instead of
fetching every character with get_user(). Commands longer than
PAGE_SIZE - 1 bytes fail with -EINVAL now.

With debug on, the dump of the command went through strndup_user() and
failed the write with -EINVAL when there was no NUL right behind the
command in the writer's buffer. The copy is NUL-terminated, so that's
gone.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/core/pktgen.c | 128 +++++++++++++++++++-----------------------------------
 1 file changed, 45 insertions(+), 83 deletions(-)

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 12363a373fb8..84f4db86a5b0 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -516,11 +516,9 @@ static int pgctrl_show(struct seq_file *seq, void *v)
 	return 0;
 }
 
-static ssize_t pgctrl_write(struct file *file, const char __user *buf,
-			    size_t count, loff_t *ppos)
+static ssize_t pgctrl_write(struct file *file, char *buf, size_t count,
+			    loff_t *ppos)
 {
-	char data[128];
-	size_t max;
 	struct pktgen_net *pn = net_generic(current->nsproxy->net_ns, pg_net_id);
 
 	if (!capable(CAP_NET_ADMIN))
@@ -529,20 +527,15 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf,
 	if (count < 1)
 		return -EINVAL;
 
-	max = min(count, sizeof(data) - 1);
-	if (copy_from_user(data, buf, max))
-		return -EFAULT;
+	/* strip trailing '\n' */
+	if (buf[count - 1] == '\n')
+		buf[count - 1] = 0;
 
-	if (data[max - 1] == '\n')
-		data[max - 1] = 0; /* strip trailing '\n', terminate string */
-	else
-		data[max] = 0; /* terminate string */
-
-	if (!strcmp(data, "stop"))
+	if (!strcmp(buf, "stop"))
 		pktgen_stop_all_threads(pn);
-	else if (!strcmp(data, "start"))
+	else if (!strcmp(buf, "start"))
 		pktgen_run_all_threads(pn);
-	else if (!strcmp(data, "reset"))
+	else if (!strcmp(buf, "reset"))
 		pktgen_reset_all_threads(pn);
 	else
 		return -EINVAL;
@@ -552,7 +545,7 @@ static ssize_t pgctrl_write(struct file *file, const char __user *buf,
 
 static const struct proc_ops pktgen_proc_ops = {
 	.proc_show	= pgctrl_show,
-	.proc_write	= pgctrl_write,
+	.proc_write_buf	= pgctrl_write,
 };
 
 static int pktgen_if_show(struct seq_file *seq, void *v)
@@ -741,7 +734,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
 }
 
 
-static ssize_t hex32_arg(const char __user *user_buffer, size_t maxlen,
+static ssize_t hex32_arg(const char *user_buffer, size_t maxlen,
 			 __u32 *num)
 {
 	size_t i = 0;
@@ -752,8 +745,7 @@ static ssize_t hex32_arg(const char __user *user_buffer, size_t maxlen,
 		int value;
 		char c;
 
-		if (get_user(c, &user_buffer[i]))
-			return -EFAULT;
+		c = user_buffer[i];
 		value = hex_to_bin(c);
 		if (value >= 0) {
 			*num <<= 4;
@@ -765,15 +757,14 @@ static ssize_t hex32_arg(const char __user *user_buffer, size_t maxlen,
 	return i;
 }
 
-static ssize_t count_trail_chars(const char __user *user_buffer, size_t maxlen)
+static ssize_t count_trail_chars(const char *user_buffer, size_t maxlen)
 {
 	size_t i;
 
 	for (i = 0; i < maxlen; i++) {
 		char c;
 
-		if (get_user(c, &user_buffer[i]))
-			return -EFAULT;
+		c = user_buffer[i];
 		switch (c) {
 		case '\"':
 		case '\n':
@@ -790,7 +781,7 @@ done:
 	return i;
 }
 
-static ssize_t num_arg(const char __user *user_buffer, size_t maxlen,
+static ssize_t num_arg(const char *user_buffer, size_t maxlen,
 		       unsigned long *num)
 {
 	size_t i;
@@ -799,8 +790,7 @@ static ssize_t num_arg(const char __user *user_buffer, size_t maxlen,
 	for (i = 0; i < maxlen; i++) {
 		char c;
 
-		if (get_user(c, &user_buffer[i]))
-			return -EFAULT;
+		c = user_buffer[i];
 		if ((c >= '0') && (c <= '9')) {
 			*num *= 10;
 			*num += c - '0';
@@ -810,15 +800,14 @@ static ssize_t num_arg(const char __user *user_buffer, size_t maxlen,
 	return i;
 }
 
-static ssize_t strn_len(const char __user *user_buffer, size_t maxlen)
+static ssize_t strn_len(const char *user_buffer, size_t maxlen)
 {
 	size_t i;
 
 	for (i = 0; i < maxlen; i++) {
 		char c;
 
-		if (get_user(c, &user_buffer[i]))
-			return -EFAULT;
+		c = user_buffer[i];
 		switch (c) {
 		case '\"':
 		case '\n':
@@ -840,7 +829,7 @@ done_str:
  * where each entry consists of size and weight delimited by commas.
  * "size1,weight_1 size2,weight_2 ... size_n,weight_n" for example.
  */
-static ssize_t get_imix_entries(const char __user *buffer,
+static ssize_t get_imix_entries(const char *buffer,
 				size_t maxlen,
 				struct pktgen_dev *pkt_dev)
 {
@@ -867,8 +856,7 @@ static ssize_t get_imix_entries(const char __user *buffer,
 		i += len;
 		if (i >= maxlen)
 			return -EINVAL;
-		if (get_user(c, &buffer[i]))
-			return -EFAULT;
+		c = buffer[i];
 		/* Check for comma between size_i and weight_i */
 		if (c != ',')
 			return -EINVAL;
@@ -894,15 +882,14 @@ static ssize_t get_imix_entries(const char __user *buffer,
 
 		if (i >= maxlen)
 			break;
-		if (get_user(c, &buffer[i]))
-			return -EFAULT;
+		c = buffer[i];
 		i++;
 	} while (c == ' ');
 
 	return i;
 }
 
-static ssize_t get_labels(const char __user *buffer,
+static ssize_t get_labels(const char *buffer,
 			  size_t maxlen, struct pktgen_dev *pkt_dev)
 {
 	unsigned int n = 0;
@@ -936,8 +923,7 @@ static ssize_t get_labels(const char __user *buffer,
 		n++;
 		if (i >= maxlen)
 			break;
-		if (get_user(c, &buffer[i]))
-			return -EFAULT;
+		c = buffer[i];
 		i++;
 	} while (c == ',');
 
@@ -974,9 +960,8 @@ static __u32 pktgen_read_flag(const char *f, bool *disable)
 	return 0;
 }
 
-static ssize_t pktgen_if_write(struct file *file,
-			       const char __user *user_buffer, size_t count,
-			       loff_t *offset)
+static ssize_t pktgen_if_write(struct file *file, char *user_buffer,
+			       size_t count, loff_t *offset)
 {
 	struct seq_file *seq = file->private_data;
 	struct pktgen_dev *pkt_dev = seq->private;
@@ -1009,8 +994,7 @@ static ssize_t pktgen_if_write(struct file *file,
 		return len;
 
 	memset(name, 0, sizeof(name));
-	if (copy_from_user(name, &user_buffer[i], len))
-		return -EFAULT;
+	memcpy(name, &user_buffer[i], len);
 	i += len;
 
 	max = count - i;
@@ -1020,16 +1004,9 @@ static ssize_t pktgen_if_write(struct file *file,
 
 	i += len;
 
-	if (debug) {
-		size_t copy = min_t(size_t, count + 1, 1024);
-		char *tp = strndup_user(user_buffer, copy);
-
-		if (IS_ERR(tp))
-			return PTR_ERR(tp);
-
-		pr_debug("%s,%zu  buffer -:%s:-\n", name, count, tp);
-		kfree(tp);
-	}
+	if (debug)
+		pr_debug("%s,%zu  buffer -:%.*s:-\n", name, count,
+			 (int)min_t(size_t, count, 1023), user_buffer);
 
 	if (!strcmp(name, "min_pkt_size")) {
 		max = min(10, count - i);
@@ -1322,8 +1299,7 @@ static ssize_t pktgen_if_write(struct file *file,
 			return len;
 
 		memset(f, 0, sizeof(f));
-		if (copy_from_user(f, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(f, &user_buffer[i], len);
 
 		if (strcmp(f, "start_xmit") == 0) {
 			pkt_dev->xmit_mode = M_START_XMIT;
@@ -1362,8 +1338,7 @@ static ssize_t pktgen_if_write(struct file *file,
 			return len;
 
 		memset(f, 0, 32);
-		if (copy_from_user(f, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(f, &user_buffer[i], len);
 
 		flag = pktgen_read_flag(f, &disable);
 		if (flag) {
@@ -1410,8 +1385,7 @@ static ssize_t pktgen_if_write(struct file *file,
 		if (len < 0)
 			return len;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 		if (strcmp(buf, pkt_dev->dst_min) != 0) {
 			strscpy_pad(pkt_dev->dst_min, buf);
@@ -1430,8 +1404,7 @@ static ssize_t pktgen_if_write(struct file *file,
 		if (len < 0)
 			return len;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 		if (strcmp(buf, pkt_dev->dst_max) != 0) {
 			strscpy_pad(pkt_dev->dst_max, buf);
@@ -1452,8 +1425,7 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		pkt_dev->flags |= F_IPV6;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 
 		in6_pton(buf, -1, pkt_dev->in6_daddr.s6_addr, -1, NULL);
@@ -1475,8 +1447,7 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		pkt_dev->flags |= F_IPV6;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 
 		in6_pton(buf, -1, pkt_dev->min_in6_daddr.s6_addr, -1, NULL);
@@ -1497,8 +1468,7 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		pkt_dev->flags |= F_IPV6;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 
 		in6_pton(buf, -1, pkt_dev->max_in6_daddr.s6_addr, -1, NULL);
@@ -1518,8 +1488,7 @@ static ssize_t pktgen_if_write(struct file *file,
 
 		pkt_dev->flags |= F_IPV6;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 
 		in6_pton(buf, -1, pkt_dev->in6_saddr.s6_addr, -1, NULL);
@@ -1539,8 +1508,7 @@ static ssize_t pktgen_if_write(struct file *file,
 		if (len < 0)
 			return len;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 		if (strcmp(buf, pkt_dev->src_min) != 0) {
 			strscpy_pad(pkt_dev->src_min, buf);
@@ -1559,8 +1527,7 @@ static ssize_t pktgen_if_write(struct file *file,
 		if (len < 0)
 			return len;
 
-		if (copy_from_user(buf, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(buf, &user_buffer[i], len);
 		buf[len] = 0;
 		if (strcmp(buf, pkt_dev->src_max) != 0) {
 			strscpy_pad(pkt_dev->src_max, buf);
@@ -1580,8 +1547,7 @@ static ssize_t pktgen_if_write(struct file *file,
 			return len;
 
 		memset(valstr, 0, sizeof(valstr));
-		if (copy_from_user(valstr, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(valstr, &user_buffer[i], len);
 
 		if (!mac_pton(valstr, pkt_dev->dst_mac))
 			return -EINVAL;
@@ -1598,8 +1564,7 @@ static ssize_t pktgen_if_write(struct file *file,
 			return len;
 
 		memset(valstr, 0, sizeof(valstr));
-		if (copy_from_user(valstr, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(valstr, &user_buffer[i], len);
 
 		if (!mac_pton(valstr, pkt_dev->src_mac))
 			return -EINVAL;
@@ -1864,7 +1829,7 @@ static ssize_t pktgen_if_write(struct file *file,
 
 static const struct proc_ops pktgen_if_proc_ops = {
 	.proc_show	= pktgen_if_show,
-	.proc_write	= pktgen_if_write,
+	.proc_write_buf	= pktgen_if_write,
 };
 
 static int pktgen_thread_show(struct seq_file *seq, void *v)
@@ -1897,8 +1862,7 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
 	return 0;
 }
 
-static ssize_t pktgen_thread_write(struct file *file,
-				   const char __user *user_buffer,
+static ssize_t pktgen_thread_write(struct file *file, char *user_buffer,
 				   size_t count, loff_t *offset)
 {
 	struct seq_file *seq = file->private_data;
@@ -1927,8 +1891,7 @@ static ssize_t pktgen_thread_write(struct file *file,
 		return len;
 
 	memset(name, 0, sizeof(name));
-	if (copy_from_user(name, &user_buffer[i], len))
-		return -EFAULT;
+	memcpy(name, &user_buffer[i], len);
 	i += len;
 
 	max = count - i;
@@ -1959,8 +1922,7 @@ static ssize_t pktgen_thread_write(struct file *file,
 			ret = len;
 			goto out;
 		}
-		if (copy_from_user(f, &user_buffer[i], len))
-			return -EFAULT;
+		memcpy(f, &user_buffer[i], len);
 
 		mutex_lock(&pktgen_thread_lock);
 		ret = pktgen_add_device(t, f);
@@ -1996,7 +1958,7 @@ out:
 
 static const struct proc_ops pktgen_thread_proc_ops = {
 	.proc_show	= pktgen_thread_show,
-	.proc_write	= pktgen_thread_write,
+	.proc_write_buf	= pktgen_thread_write,
 };
 
 /* Think find or remove for NN */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 11/23] selftests/proc: check that a faulting writer doesn't hold up removal
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 10/23] scsi: core: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 12/23] scsi: sg: let procfs own the seq_file of allow_dio and def_reserved_size Christian Brauner
                     ` (11 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Write to /proc/scsi/scsi_debug/<host> from a buffer whose page is backed
by userfaultfd and remove the host while procfs copies in what is
written:

- removing the entry doesn't wait for the writer

- the write fails with -EIO once the fault is resolved

The test loads scsi_debug if it isn't there and skips without it.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/proc/.gitignore            |   1 +
 tools/testing/selftests/proc/Makefile              |   1 +
 tools/testing/selftests/proc/config                |   3 +
 .../selftests/proc/proc-remove-slow-writer.c       | 203 +++++++++++++++++++++
 4 files changed, 208 insertions(+)

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index e2566cc0ae07..51969f81afc5 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -24,6 +24,7 @@
 /proc-remove-slow-sysctl
 /proc-remove-slow-reader
 /proc-remove-slow-readlink
+/proc-remove-slow-writer
 /read
 /self
 /setns-dcache
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index 6a9bdc999f54..aecec6b93173 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -35,5 +35,6 @@ TEST_GEN_PROGS += proc-pidns
 TEST_GEN_PROGS += proc-remove-slow-sysctl
 TEST_GEN_PROGS += proc-remove-slow-reader
 TEST_GEN_PROGS += proc-remove-slow-readlink
+TEST_GEN_PROGS += proc-remove-slow-writer
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/config b/tools/testing/selftests/proc/config
index 7b0e1d9be1a2..c7c61a2d7927 100644
--- a/tools/testing/selftests/proc/config
+++ b/tools/testing/selftests/proc/config
@@ -5,3 +5,6 @@ CONFIG_USERFAULTFD=y
 CONFIG_SOUND=y
 CONFIG_SND=y
 CONFIG_SND_DUMMY=m
+CONFIG_SCSI=y
+CONFIG_SCSI_PROC_FS=y
+CONFIG_SCSI_DEBUG=m
diff --git a/tools/testing/selftests/proc/proc-remove-slow-writer.c b/tools/testing/selftests/proc/proc-remove-slow-writer.c
new file mode 100644
index 000000000000..f69ca75ba87c
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-remove-slow-writer.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A write that faults while procfs copies it in must not hold up removal of
+ * the /proc entry. Removing a scsi_debug host removes its
+ * /proc/scsi/scsi_debug/<host> file.
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define ADD_HOST	"/sys/bus/pseudo/drivers/scsi_debug/add_host"
+#define HOST_DIR	"/proc/scsi/scsi_debug"
+#define TIMEOUT_MS	5000
+#define MAX_HOSTS	4096
+#define HOST_BITS	(8 * sizeof(unsigned long))
+
+struct writer {
+	int fd;
+	char *buf;
+	ssize_t ret;
+	int err;
+};
+
+struct remover {
+	int done;
+	int ret;
+};
+
+/* a positive @nr adds hosts, a negative one removes the last ones added */
+static int add_host(int nr)
+{
+	char s[16];
+	int fd, len, ret;
+
+	fd = open(ADD_HOST, O_WRONLY | O_CLOEXEC);
+	if (fd < 0)
+		return -errno;
+	len = snprintf(s, sizeof(s), "%d", nr);
+	ret = write(fd, s, len) == len ? 0 : -errno;
+	close(fd);
+	return ret;
+}
+
+/* the numbers of the hosts in /proc/scsi/scsi_debug as a bitmap */
+static int list_hosts(unsigned long *hosts)
+{
+	struct dirent *d;
+	DIR *dir;
+
+	memset(hosts, 0, MAX_HOSTS / 8);
+	dir = opendir(HOST_DIR);
+	if (!dir)
+		/* the directory comes with the first host and goes with the last */
+		return errno == ENOENT ? 0 : -errno;
+	while ((d = readdir(dir))) {
+		char *end;
+		long nr = strtol(d->d_name, &end, 10);
+
+		if (end != d->d_name && !*end && nr >= 0 && nr < MAX_HOSTS)
+			hosts[nr / HOST_BITS] |= 1UL << (nr % HOST_BITS);
+	}
+	closedir(dir);
+	return 0;
+}
+
+/* the host that is in @after but wasn't in @before */
+static int new_host(const unsigned long *before, const unsigned long *after)
+{
+	for (int i = 0; i < MAX_HOSTS / HOST_BITS; i++) {
+		unsigned long new = after[i] & ~before[i];
+
+		if (new)
+			return i * HOST_BITS + __builtin_ctzl(new);
+	}
+	return -1;
+}
+
+static void *write_thread(void *arg)
+{
+	struct writer *w = arg;
+
+	w->ret = write(w->fd, w->buf, 2);
+	w->err = errno;
+	return NULL;
+}
+
+static void *remove_thread(void *arg)
+{
+	struct remover *r = arg;
+	ssize_t ret;
+
+	r->ret = add_host(-1);
+	ret = write(r->done, "x", 1);
+	return (void *)ret;
+}
+
+FIXTURE(host) {
+	char path[PATH_MAX];
+	int host;
+};
+
+FIXTURE_SETUP(host)
+{
+	unsigned long before[MAX_HOSTS / HOST_BITS];
+	unsigned long after[MAX_HOSTS / HOST_BITS];
+
+	self->host = -1;
+	if (access(ADD_HOST, W_OK) && system("modprobe -q scsi_debug add_host=0"))
+		SKIP(return, "no scsi_debug");
+	if (access(ADD_HOST, W_OK))
+		SKIP(return, ADD_HOST ": %s", strerror(errno));
+
+	/* host numbers are reused, so look for the one that's new */
+	ASSERT_EQ(list_hosts(before), 0);
+	ASSERT_EQ(add_host(1), 0);
+	ASSERT_EQ(list_hosts(after), 0);
+	self->host = new_host(before, after);
+	ASSERT_GE(self->host, 0);
+	snprintf(self->path, sizeof(self->path), HOST_DIR "/%d", self->host);
+}
+
+FIXTURE_TEARDOWN(host)
+{
+	if (self->host >= 0 && !access(self->path, F_OK))
+		add_host(-1);
+}
+
+TEST_F(host, remove_while_writer_faults)
+{
+	struct uffdio_api api = { .api = UFFD_API };
+	struct uffdio_register reg = {};
+	struct uffdio_zeropage zero = {};
+	long psz = sysconf(_SC_PAGESIZE);
+	struct writer w = {};
+	struct remover r = {};
+	struct uffd_msg msg;
+	struct pollfd pfd;
+	pthread_t wt, rt;
+	int uffd, done[2];
+	char *buf;
+
+	w.fd = open(self->path, O_WRONLY | O_CLOEXEC);
+	if (w.fd < 0)
+		SKIP(return, "%s: %s", self->path, strerror(errno));
+
+	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (uffd < 0)
+		SKIP(return, "userfaultfd: %s", strerror(errno));
+	ASSERT_EQ(ioctl(uffd, UFFDIO_API, &api), 0);
+
+	buf = mmap(NULL, psz, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(buf, MAP_FAILED);
+	reg.range.start = (unsigned long)buf;
+	reg.range.len = psz;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_REGISTER, &reg), 0);
+
+	w.buf = buf;
+	ASSERT_EQ(pthread_create(&wt, NULL, write_thread, &w), 0);
+
+	/* The writer now sleeps while what it writes is copied in. */
+	pfd = (struct pollfd){ .fd = uffd, .events = POLLIN };
+	ASSERT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1);
+	ASSERT_EQ(read(uffd, &msg, sizeof(msg)), sizeof(msg));
+	ASSERT_EQ(msg.event, UFFD_EVENT_PAGEFAULT);
+
+	/* Removing the host removes the file that is being written. */
+	ASSERT_EQ(pipe2(done, O_CLOEXEC), 0);
+	r.done = done[1];
+	ASSERT_EQ(pthread_create(&rt, NULL, remove_thread, &r), 0);
+	pfd = (struct pollfd){ .fd = done[0], .events = POLLIN };
+	EXPECT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1) {
+		TH_LOG("removing %s waited for the writer", self->path);
+	}
+
+	zero.range.start = (unsigned long)buf;
+	zero.range.len = psz;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_ZEROPAGE, &zero), 0);
+	ASSERT_EQ(pthread_join(wt, NULL), 0);
+	ASSERT_EQ(pthread_join(rt, NULL), 0);
+	EXPECT_EQ(r.ret, 0);
+
+	/* The entry was gone by the time the write got to it. */
+	EXPECT_EQ(w.ret, -1);
+	EXPECT_EQ(w.err, EIO);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 08/27] smb: client: let procfs own the seq_file of the smbdirect files
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 07/27] ntfs3: let procfs copy in writes to the label file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 09/27] smb: client: let procfs copy in writes Christian Brauner
                     ` (18 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show function in PROC_FILE_DEFINE() and let procfs open and
free the seq_file. That makes the open functions unnecessary.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/smb/client/cifs_debug.c | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index dfcd2cdbb2a8..664072ab3342 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -873,16 +873,9 @@ static int name##_proc_show(struct seq_file *m, void *v) \
 	seq_printf(m, "%d\n", name); \
 	return 0; \
 } \
-static int name##_open(struct inode *inode, struct file *file) \
-{ \
-	return single_open(file, name##_proc_show, NULL); \
-} \
 \
 static const struct proc_ops cifs_##name##_proc_fops = { \
-	.proc_open	= name##_open, \
-	.proc_read	= seq_read, \
-	.proc_lseek	= seq_lseek, \
-	.proc_release	= single_release, \
+	.proc_show	= name##_proc_show, \
 	.proc_write	= name##_write, \
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 04/11] netfilter: xt_recent: let procfs own the seq_file
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 03/11] net: pktgen: let procfs copy in commands Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 05/11] netfilter: xt_recent: let procfs copy in writes Christian Brauner
                     ` (6 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and the size of the iterator state and let
procfs open and free the seq_file. ->proc_open() only fills in the table
now. Reading /proc/net/xt_recent/<table> then no longer holds up the
removal of the table.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/netfilter/xt_recent.c | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index 11acb9a50d4b..b771cc463aab 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -546,11 +546,7 @@ static const struct seq_operations recent_seq_ops = {
 
 static int recent_seq_open(struct inode *inode, struct file *file)
 {
-	struct recent_iter_state *st;
-
-	st = __seq_open_private(file, &recent_seq_ops, sizeof(*st));
-	if (st == NULL)
-		return -ENOMEM;
+	struct recent_iter_state *st = ((struct seq_file *)file->private_data)->private;
 
 	st->table    = pde_data(inode);
 	return 0;
@@ -626,11 +622,10 @@ recent_mt_proc_write(struct file *file, const char __user *input,
 }
 
 static const struct proc_ops recent_mt_proc_ops = {
+	.proc_seq_ops	= &recent_seq_ops,
+	.proc_state_size = sizeof(struct recent_iter_state),
 	.proc_open	= recent_seq_open,
-	.proc_read	= seq_read,
 	.proc_write	= recent_mt_proc_write,
-	.proc_release	= seq_release_private,
-	.proc_lseek	= seq_lseek,
 };
 
 static int __net_init recent_proc_net_init(struct net *net)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 12/23] scsi: sg: let procfs own the seq_file of allow_dio and def_reserved_size
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (10 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 11/23] selftests/proc: check that a faulting writer doesn't hold up removal Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 13/23] scsi: sg: let procfs copy in writes Christian Brauner
                     ` (10 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Pass the integer they show as the data of the entry and declare the
show function, procfs then opens and frees the seq_file. That makes the
open functions unnecessary.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/sg.c | 28 ++++++----------------------
 1 file changed, 6 insertions(+), 22 deletions(-)

diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 3f9e08725602..c7386fbe5580 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -2287,26 +2287,18 @@ sg_get_dev(int dev)
 #ifdef CONFIG_SCSI_PROC_FS
 static int sg_proc_seq_show_int(struct seq_file *s, void *v);
 
-static int sg_proc_single_open_adio(struct inode *inode, struct file *file);
 static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer,
 			          size_t count, loff_t *off);
 static const struct proc_ops adio_proc_ops = {
-	.proc_open	= sg_proc_single_open_adio,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= sg_proc_seq_show_int,
 	.proc_write	= sg_proc_write_adio,
-	.proc_release	= single_release,
 };
 
-static int sg_proc_single_open_dressz(struct inode *inode, struct file *file);
 static ssize_t sg_proc_write_dressz(struct file *filp, 
 		const char __user *buffer, size_t count, loff_t *off);
 static const struct proc_ops dressz_proc_ops = {
-	.proc_open	= sg_proc_single_open_dressz,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= sg_proc_seq_show_int,
 	.proc_write	= sg_proc_write_dressz,
-	.proc_release	= single_release,
 };
 
 static int sg_proc_seq_show_version(struct seq_file *s, void *v);
@@ -2347,9 +2339,11 @@ sg_proc_init(void)
 	if (!p)
 		return 1;
 
-	proc_create("allow_dio", S_IRUGO | S_IWUSR, p, &adio_proc_ops);
+	proc_create_data("allow_dio", S_IRUGO | S_IWUSR, p, &adio_proc_ops,
+			 &sg_allow_dio);
 	proc_create_seq("debug", S_IRUGO, p, &debug_seq_ops);
-	proc_create("def_reserved_size", S_IRUGO | S_IWUSR, p, &dressz_proc_ops);
+	proc_create_data("def_reserved_size", S_IRUGO | S_IWUSR, p,
+			 &dressz_proc_ops, &def_reserved_size);
 	proc_create_single("device_hdr", S_IRUGO, p, sg_proc_seq_show_devhdr);
 	proc_create_seq("devices", S_IRUGO, p, &dev_seq_ops);
 	proc_create_seq("device_strs", S_IRUGO, p, &devstrs_seq_ops);
@@ -2364,11 +2358,6 @@ static int sg_proc_seq_show_int(struct seq_file *s, void *v)
 	return 0;
 }
 
-static int sg_proc_single_open_adio(struct inode *inode, struct file *file)
-{
-	return single_open(file, sg_proc_seq_show_int, &sg_allow_dio);
-}
-
 static ssize_t 
 sg_proc_write_adio(struct file *filp, const char __user *buffer,
 		   size_t count, loff_t *off)
@@ -2385,11 +2374,6 @@ sg_proc_write_adio(struct file *filp, const char __user *buffer,
 	return count;
 }
 
-static int sg_proc_single_open_dressz(struct inode *inode, struct file *file)
-{
-	return single_open(file, sg_proc_seq_show_int, &def_reserved_size);
-}
-
 static ssize_t 
 sg_proc_write_dressz(struct file *filp, const char __user *buffer,
 		     size_t count, loff_t *off)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 09/27] smb: client: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 08/27] smb: client: let procfs own the seq_file of the smbdirect files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 10/27] genirq/proc: let procfs copy in the affinity masks Christian Brauner
                     ` (17 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the files in /proc/fs/cifs so procfs
copies in what is written before it pins the entry, and parse what was
written from that copy. Writes larger than PAGE_SIZE - 1 bytes fail with
-EINVAL now, SecurityFlags never took more than 11 bytes. Empty writes
to cifsFYI and dfscache fail with -EINVAL too, they looked at a byte
that wasn't written before. kstrtoint_from_user() looked at the first
34 bytes of a write to cifs_debug_dirs and the smbdirect files, now the
whole write is parsed.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/smb/client/cifs_debug.c | 62 +++++++++++++++++++---------------------------
 fs/smb/client/dfs_cache.c  | 13 +++-------
 2 files changed, 29 insertions(+), 46 deletions(-)

diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 664072ab3342..fb1323bf44c7 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -359,12 +359,12 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
 #ifdef CONFIG_CIFS_DEBUG
 
 /* Drop all cached directory entries across all CIFS mounts. */
-static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *buffer,
+static ssize_t cifs_debug_dirs_proc_write(struct file *file, char *buffer,
 					  size_t count, loff_t *ppos)
 {
 	int rc, v;
 
-	rc = kstrtoint_from_user(buffer, count, 10, &v);
+	rc = kstrtoint(buffer, 10, &v);
 	if (rc)
 		return rc;
 
@@ -390,7 +390,7 @@ static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *
 
 static const struct proc_ops cifs_debug_dirs_proc_ops = {
 	.proc_show	= cifs_debug_dirs_proc_show,
-	.proc_write	= cifs_debug_dirs_proc_write,
+	.proc_write_buf	= cifs_debug_dirs_proc_write,
 };
 #endif /* CONFIG_CIFS_DEBUG */
 
@@ -713,8 +713,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t cifs_stats_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t cifs_stats_proc_write(struct file *file, char *buffer,
+		size_t count, loff_t *ppos)
 {
 	bool bv;
 	int rc;
@@ -722,7 +722,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
 	struct cifs_ses *ses;
 	struct cifs_tcon *tcon;
 
-	rc = kstrtobool_from_user(buffer, count, &bv);
+	rc = kstrtobool(buffer, &bv);
 	if (rc == 0) {
 #ifdef CONFIG_CIFS_STATS2
 		int i;
@@ -854,16 +854,16 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
 
 static const struct proc_ops cifs_stats_proc_ops = {
 	.proc_show	= cifs_stats_proc_show,
-	.proc_write	= cifs_stats_proc_write,
+	.proc_write_buf	= cifs_stats_proc_write,
 };
 
 #ifdef CONFIG_CIFS_SMB_DIRECT
 #define PROC_FILE_DEFINE(name) \
-static ssize_t name##_write(struct file *file, const char __user *buffer, \
+static ssize_t name##_write(struct file *file, char *buffer, \
 	size_t count, loff_t *ppos) \
 { \
 	int rc; \
-	rc = kstrtoint_from_user(buffer, count, 10, &name); \
+	rc = kstrtoint(buffer, 10, &name); \
 	if (rc) \
 		return rc; \
 	return count; \
@@ -876,7 +876,7 @@ static int name##_proc_show(struct seq_file *m, void *v) \
 \
 static const struct proc_ops cifs_##name##_proc_fops = { \
 	.proc_show	= name##_proc_show, \
-	.proc_write	= name##_write, \
+	.proc_write_buf	= name##_write, \
 }
 
 PROC_FILE_DEFINE(rdma_readwrite_threshold);
@@ -990,16 +990,12 @@ static int cifsFYI_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
+static ssize_t cifsFYI_proc_write(struct file *file, char *buffer,
 		size_t count, loff_t *ppos)
 {
-	char c[2] = { '\0' };
+	char c[2] = { buffer[0] };
 	bool bv;
-	int rc;
 
-	rc = get_user(c[0], buffer);
-	if (rc)
-		return rc;
 	if (kstrtobool(c, &bv) == 0)
 		cifsFYI = bv;
 	else if ((c[0] > '1') && (c[0] <= '9'))
@@ -1012,7 +1008,7 @@ static ssize_t cifsFYI_proc_write(struct file *file, const char __user *buffer,
 
 static const struct proc_ops cifsFYI_proc_ops = {
 	.proc_show	= cifsFYI_proc_show,
-	.proc_write	= cifsFYI_proc_write,
+	.proc_write_buf	= cifsFYI_proc_write,
 };
 
 static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
@@ -1021,12 +1017,12 @@ static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t cifs_linux_ext_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t cifs_linux_ext_proc_write(struct file *file, char *buffer,
+		size_t count, loff_t *ppos)
 {
 	int rc;
 
-	rc = kstrtobool_from_user(buffer, count, &linuxExtEnabled);
+	rc = kstrtobool(buffer, &linuxExtEnabled);
 	if (rc)
 		return rc;
 
@@ -1035,7 +1031,7 @@ static ssize_t cifs_linux_ext_proc_write(struct file *file,
 
 static const struct proc_ops cifs_linux_ext_proc_ops = {
 	.proc_show	= cifs_linux_ext_proc_show,
-	.proc_write	= cifs_linux_ext_proc_write,
+	.proc_write_buf	= cifs_linux_ext_proc_write,
 };
 
 static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
@@ -1044,12 +1040,12 @@ static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t cifs_lookup_cache_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t cifs_lookup_cache_proc_write(struct file *file, char *buffer,
+		size_t count, loff_t *ppos)
 {
 	int rc;
 
-	rc = kstrtobool_from_user(buffer, count, &lookupCacheEnabled);
+	rc = kstrtobool(buffer, &lookupCacheEnabled);
 	if (rc)
 		return rc;
 
@@ -1058,7 +1054,7 @@ static ssize_t cifs_lookup_cache_proc_write(struct file *file,
 
 static const struct proc_ops cifs_lookup_cache_proc_ops = {
 	.proc_show	= cifs_lookup_cache_proc_show,
-	.proc_write	= cifs_lookup_cache_proc_write,
+	.proc_write_buf	= cifs_lookup_cache_proc_write,
 };
 
 static int traceSMB_proc_show(struct seq_file *m, void *v)
@@ -1067,12 +1063,12 @@ static int traceSMB_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
+static ssize_t traceSMB_proc_write(struct file *file, char *buffer,
 		size_t count, loff_t *ppos)
 {
 	int rc;
 
-	rc = kstrtobool_from_user(buffer, count, &traceSMB);
+	rc = kstrtobool(buffer, &traceSMB);
 	if (rc)
 		return rc;
 
@@ -1081,7 +1077,7 @@ static ssize_t traceSMB_proc_write(struct file *file, const char __user *buffer,
 
 static const struct proc_ops traceSMB_proc_ops = {
 	.proc_show	= traceSMB_proc_show,
-	.proc_write	= traceSMB_proc_write,
+	.proc_write_buf	= traceSMB_proc_write,
 };
 
 static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
@@ -1111,21 +1107,15 @@ cifs_security_flags_handle_must_flags(unsigned int *flags)
 }
 
 static ssize_t cifs_security_flags_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *ppos)
+		char *flags_string, size_t count, loff_t *ppos)
 {
 	int rc;
 	unsigned int flags;
-	char flags_string[12];
 	bool bv;
 
 	if ((count < 1) || (count > 11))
 		return -EINVAL;
 
-	memset(flags_string, 0, sizeof(flags_string));
-
-	if (copy_from_user(flags_string, buffer, count))
-		return -EFAULT;
-
 	if (count < 3) {
 		/* single char or single char followed by null */
 		if (kstrtobool(flags_string, &bv) == 0) {
@@ -1176,7 +1166,7 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
 
 static const struct proc_ops cifs_security_flags_proc_ops = {
 	.proc_show	= cifs_security_flags_proc_show,
-	.proc_write	= cifs_security_flags_proc_write,
+	.proc_write_buf	= cifs_security_flags_proc_write,
 };
 
 /* To make it easier to debug, can help to show mount params */
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index 58f16cf83d36..a6aa05430b15 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -191,17 +191,10 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
+static ssize_t dfscache_proc_write(struct file *file, char *buffer,
 				   size_t count, loff_t *ppos)
 {
-	char c;
-	int rc;
-
-	rc = get_user(c, buffer);
-	if (rc)
-		return rc;
-
-	if (c != '0')
+	if (buffer[0] != '0')
 		return -EINVAL;
 
 	cifs_dbg(FYI, "clearing dfs cache\n");
@@ -215,7 +208,7 @@ static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
 
 const struct proc_ops dfscache_proc_ops = {
 	.proc_show	= dfscache_proc_show,
-	.proc_write	= dfscache_proc_write,
+	.proc_write_buf	= dfscache_proc_write,
 };
 
 #ifdef CONFIG_CIFS_DEBUG2

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 05/11] netfilter: xt_recent: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 04/11] netfilter: xt_recent: let procfs own the seq_file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 06/11] SUNRPC: let procfs own the seq_file of the cache content file Christian Brauner
                     ` (5 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/net/xt_recent/<table> before it pins the entry, and take the
address from that copy. The removal of a table runs under recent_mutex
and doesn't wait for a writer that faults on its buffer anymore. Writes
larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/netfilter/xt_recent.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index b771cc463aab..306557f25daf 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -553,8 +553,7 @@ static int recent_seq_open(struct inode *inode, struct file *file)
 }
 
 static ssize_t
-recent_mt_proc_write(struct file *file, const char __user *input,
-		     size_t size, loff_t *loff)
+recent_mt_proc_write(struct file *file, char *input, size_t size, loff_t *loff)
 {
 	struct recent_table *t = pde_data(file_inode(file));
 	struct recent_entry *e;
@@ -568,8 +567,7 @@ recent_mt_proc_write(struct file *file, const char __user *input,
 		return 0;
 	if (size > sizeof(buf))
 		size = sizeof(buf);
-	if (copy_from_user(buf, input, size) != 0)
-		return -EFAULT;
+	memcpy(buf, input, size);
 
 	/* Strict protocol! */
 	if (*loff != 0)
@@ -625,7 +623,7 @@ static const struct proc_ops recent_mt_proc_ops = {
 	.proc_seq_ops	= &recent_seq_ops,
 	.proc_state_size = sizeof(struct recent_iter_state),
 	.proc_open	= recent_seq_open,
-	.proc_write	= recent_mt_proc_write,
+	.proc_write_buf	= recent_mt_proc_write,
 };
 
 static int __net_init recent_proc_net_init(struct net *net)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 13/23] scsi: sg: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (11 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 12/23] scsi: sg: let procfs own the seq_file of allow_dio and def_reserved_size Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 14/23] scsi: esas2r: let procfs copy the ioctl argument of the ATTONode file Christian Brauner
                     ` (9 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for allow_dio and def_reserved_size so
procfs copies in what is written before it pins the entry, and parse
the number from that copy.

kstrtoul_from_user() looked at the first 66 bytes of whatever was
written. Now the whole write is parsed, and anything larger than
PAGE_SIZE - 1 bytes fails with -EINVAL from procfs before the
capability check.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/sg.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index c7386fbe5580..87569e302f26 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -2287,18 +2287,18 @@ sg_get_dev(int dev)
 #ifdef CONFIG_SCSI_PROC_FS
 static int sg_proc_seq_show_int(struct seq_file *s, void *v);
 
-static ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer,
+static ssize_t sg_proc_write_adio(struct file *filp, char *buffer,
 			          size_t count, loff_t *off);
 static const struct proc_ops adio_proc_ops = {
 	.proc_show	= sg_proc_seq_show_int,
-	.proc_write	= sg_proc_write_adio,
+	.proc_write_buf	= sg_proc_write_adio,
 };
 
 static ssize_t sg_proc_write_dressz(struct file *filp, 
-		const char __user *buffer, size_t count, loff_t *off);
+		char *buffer, size_t count, loff_t *off);
 static const struct proc_ops dressz_proc_ops = {
 	.proc_show	= sg_proc_seq_show_int,
-	.proc_write	= sg_proc_write_dressz,
+	.proc_write_buf	= sg_proc_write_dressz,
 };
 
 static int sg_proc_seq_show_version(struct seq_file *s, void *v);
@@ -2359,7 +2359,7 @@ static int sg_proc_seq_show_int(struct seq_file *s, void *v)
 }
 
 static ssize_t 
-sg_proc_write_adio(struct file *filp, const char __user *buffer,
+sg_proc_write_adio(struct file *filp, char *buffer,
 		   size_t count, loff_t *off)
 {
 	int err;
@@ -2367,7 +2367,7 @@ sg_proc_write_adio(struct file *filp, const char __user *buffer,
 
 	if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
 		return -EACCES;
-	err = kstrtoul_from_user(buffer, count, 0, &num);
+	err = kstrtoul(buffer, 0, &num);
 	if (err)
 		return err;
 	sg_allow_dio = num ? 1 : 0;
@@ -2375,7 +2375,7 @@ sg_proc_write_adio(struct file *filp, const char __user *buffer,
 }
 
 static ssize_t 
-sg_proc_write_dressz(struct file *filp, const char __user *buffer,
+sg_proc_write_dressz(struct file *filp, char *buffer,
 		     size_t count, loff_t *off)
 {
 	int err;
@@ -2384,7 +2384,7 @@ sg_proc_write_dressz(struct file *filp, const char __user *buffer,
 	if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
 		return -EACCES;
 
-	err = kstrtoul_from_user(buffer, count, 0, &k);
+	err = kstrtoul(buffer, 0, &k);
 	if (err)
 		return err;
 	if (k <= 1048576) {	/* limit "big buff" to 1 MB */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 10/27] genirq/proc: let procfs copy in the affinity masks
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 09/27] smb: client: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 11/27] latencytop: let procfs copy in writes to /proc/latency_stats Christian Brauner
                     ` (16 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for smp_affinity, smp_affinity_list and
default_smp_affinity so procfs copies in what is written before it pins
the entry, and parse the masks from that copy. A list of CPUs can be
longer than a page on large machines, so the files take as much as
memdup_user_nul() could copy in before. Anything larger fails with
-EINVAL instead of -ENOMEM.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/irq/proc.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index aa3d5544cfc1..fe683457a088 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -9,6 +9,7 @@
 #include <linux/gfp.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/kernel_stat.h>
@@ -134,8 +135,11 @@ static inline int irq_select_affinity_usr(unsigned int irq)
 }
 #endif
 
+/* lists of CPUs can outgrow a page, take what memdup_user_nul() took */
+#define IRQ_AFFINITY_WRITE_SIZE	(KMALLOC_MAX_SIZE - 1)
+
 static ssize_t write_irq_affinity(int type, struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
 	unsigned int irq = (int)(long)pde_data(file_inode(file));
 	cpumask_var_t new_value;
@@ -148,9 +152,9 @@ static ssize_t write_irq_affinity(int type, struct file *file,
 		return -ENOMEM;
 
 	if (type)
-		err = cpumask_parselist_user(buffer, count, new_value);
+		err = cpulist_parse(buffer, new_value);
 	else
-		err = cpumask_parse_user(buffer, count, new_value);
+		err = cpumask_parse(buffer, new_value);
 	if (err)
 		goto free_cpumask;
 
@@ -177,25 +181,27 @@ free_cpumask:
 }
 
 static ssize_t irq_affinity_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
 	return write_irq_affinity(0, file, buffer, count, pos);
 }
 
 static ssize_t irq_affinity_list_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
 	return write_irq_affinity(1, file, buffer, count, pos);
 }
 
 static const struct proc_ops irq_affinity_proc_ops = {
 	.proc_show	= irq_affinity_proc_show,
-	.proc_write	= irq_affinity_proc_write,
+	.proc_write_buf	= irq_affinity_proc_write,
+	.proc_write_size = IRQ_AFFINITY_WRITE_SIZE,
 };
 
 static const struct proc_ops irq_affinity_list_proc_ops = {
 	.proc_show	= irq_affinity_list_proc_show,
-	.proc_write	= irq_affinity_list_proc_write,
+	.proc_write_buf	= irq_affinity_list_proc_write,
+	.proc_write_size = IRQ_AFFINITY_WRITE_SIZE,
 };
 
 #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
@@ -217,7 +223,7 @@ static int default_affinity_show(struct seq_file *m, void *v)
 }
 
 static ssize_t default_affinity_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *ppos)
+		char *buffer, size_t count, loff_t *ppos)
 {
 	cpumask_var_t new_value;
 	int err;
@@ -225,7 +231,7 @@ static ssize_t default_affinity_write(struct file *file,
 	if (!zalloc_cpumask_var(&new_value, GFP_KERNEL))
 		return -ENOMEM;
 
-	err = cpumask_parse_user(buffer, count, new_value);
+	err = cpumask_parse(buffer, new_value);
 	if (err)
 		goto out;
 
@@ -249,7 +255,8 @@ out:
 
 static const struct proc_ops default_affinity_proc_ops = {
 	.proc_show	= default_affinity_show,
-	.proc_write	= default_affinity_write,
+	.proc_write_buf	= default_affinity_write,
+	.proc_write_size = IRQ_AFFINITY_WRITE_SIZE,
 };
 
 static int irq_node_proc_show(struct seq_file *m, void *v)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 06/11] SUNRPC: let procfs own the seq_file of the cache content file
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (4 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 05/11] netfilter: xt_recent: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 21:11     ` Chuck Lever
  2026-11-02 15:07   ` [PATCH RFC 07/11] SUNRPC: let procfs copy in writes Christian Brauner
                     ` (4 subsequent siblings)
  10 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and let procfs open and free the seq_file of
/proc/net/rpc/<cache>/content. ->proc_open() and ->proc_release() only
take and drop the module reference and set up ->private now. The
rpc_pipefs file is unchanged. Reading the procfs file then no longer
holds up its removal when the network namespace goes away.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/sunrpc/cache.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 1ceaea7e01b9..007521e8a57f 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1606,24 +1606,28 @@ static const struct proc_ops cache_channel_proc_ops = {
 	.proc_release	= cache_release_procfs,
 };
 
+/* procfs opens the seq_file */
 static int content_open_procfs(struct inode *inode, struct file *filp)
 {
 	struct cache_detail *cd = pde_data(inode);
 
-	return content_open(inode, filp, cd);
+	if (!cd || !try_module_get(cd->owner))
+		return -EACCES;
+	((struct seq_file *)filp->private_data)->private = cd;
+	return 0;
 }
 
 static int content_release_procfs(struct inode *inode, struct file *filp)
 {
 	struct cache_detail *cd = pde_data(inode);
 
-	return content_release(inode, filp, cd);
+	module_put(cd->owner);
+	return 0;
 }
 
 static const struct proc_ops content_proc_ops = {
+	.proc_seq_ops	= &cache_content_op,
 	.proc_open	= content_open_procfs,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= content_release_procfs,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 14/23] scsi: esas2r: let procfs copy the ioctl argument of the ATTONode file
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (12 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 13/23] scsi: sg: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 15/23] hwmon: (dell-smm) let procfs copy the i8k ioctl arguments Christian Brauner
                     ` (8 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Move the work of esas2r_ioctl_handler() into esas2r_ioctl_buf() which
works on a kernel copy of struct atto_express_ioctl. The SCSI ioctl and
the character device keep copying in and out in
esas2r_ioctl_handler().

The procfs file implements ->proc_ioctl_buf() with esas2r_ioctl_buf()
and lists every command from EXPRESS_IOCTL_MIN to EXPRESS_IOCTL_MAX, so
procfs copies the struct in before it pins the entry and back out after
unpinning it. Commands outside of that range fail with -ENOTTY instead
of -ENOTSUPP now, a NULL argument with -EFAULT.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/scsi/esas2r/esas2r.h       |  2 ++
 drivers/scsi/esas2r/esas2r_ioctl.c | 58 ++++++++++++++++++++++----------------
 drivers/scsi/esas2r/esas2r_main.c  | 44 +++++++++++++++++++++++------
 3 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index 10101f50b282..4d75b8adbb3b 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -964,6 +964,8 @@ struct esas2r_adapter {
 const char *esas2r_info(struct Scsi_Host *);
 int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
 			struct esas2r_sas_nvram *data);
+int esas2r_ioctl_buf(void *hostdata, unsigned int cmd,
+		     struct atto_express_ioctl *ioctl);
 int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg);
 int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg);
 u8 handle_hba_ioctl(struct esas2r_adapter *a,
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
index f99318f61c93..eb39ba8263b3 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -1279,37 +1279,25 @@ int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
 }
 
 
-/* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */
-int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
+/*
+ * This function only cares about ATTO-specific ioctls (atto_express_ioctl).
+ * @ioctl is a kernel copy of what the caller passed. It goes back to the
+ * caller unless this fails.
+ */
+int esas2r_ioctl_buf(void *hostdata, unsigned int cmd,
+		     struct atto_express_ioctl *ioctl)
 {
-	struct atto_express_ioctl *ioctl = NULL;
 	struct esas2r_adapter *a;
 	struct esas2r_request *rq;
 	u16 code;
 	int err;
 
-	esas2r_log(ESAS2R_LOG_DEBG, "ioctl (%p, %x, %p)", hostdata, cmd, arg);
-
-	if ((arg == NULL)
-	    || (cmd < EXPRESS_IOCTL_MIN)
-	    || (cmd > EXPRESS_IOCTL_MAX))
-		return -ENOTSUPP;
-
-	ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
-	if (IS_ERR(ioctl)) {
-		esas2r_log(ESAS2R_LOG_WARN,
-			   "ioctl_handler access_ok failed for cmd %u, address %p",
-			   cmd, arg);
-		return PTR_ERR(ioctl);
-	}
-
 	/* verify the signature */
 
 	if (memcmp(ioctl->header.signature,
 		   EXPRESS_IOCTL_SIGNATURE,
 		   EXPRESS_IOCTL_SIGNATURE_SIZE) != 0) {
 		esas2r_log(ESAS2R_LOG_WARN, "invalid signature");
-		kfree(ioctl);
 
 		return -ENOTSUPP;
 	}
@@ -1350,7 +1338,6 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 			esas2r_adapters[ioctl->header.channel] == NULL) {
 			ioctl->header.return_code = IOCTL_BAD_CHANNEL;
 			esas2r_log(ESAS2R_LOG_WARN, "bad channel value");
-			kfree(ioctl);
 
 			return -ENOTSUPP;
 		}
@@ -1403,7 +1390,6 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
 
 		rq = esas2r_alloc_request(a);
 		if (rq == NULL) {
-			kfree(ioctl);
 			esas2r_log(ESAS2R_LOG_WARN,
 			   "could not allocate an internal request");
 			return -ENOMEM;
@@ -1515,19 +1501,43 @@ ioctl_done:
 
 	}
 
+	return 0;
+}
+
+int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
+{
+	struct atto_express_ioctl *ioctl __free(kfree) = NULL;
+	int err;
+
+	esas2r_log(ESAS2R_LOG_DEBG, "ioctl (%p, %x, %p)", hostdata, cmd, arg);
+
+	if ((arg == NULL)
+	    || (cmd < EXPRESS_IOCTL_MIN)
+	    || (cmd > EXPRESS_IOCTL_MAX))
+		return -ENOTSUPP;
+
+	ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
+	if (IS_ERR(ioctl)) {
+		esas2r_log(ESAS2R_LOG_WARN,
+			   "ioctl_handler access_ok failed for cmd %u, address %p",
+			   cmd, arg);
+		return PTR_ERR(ioctl);
+	}
+
+	err = esas2r_ioctl_buf(hostdata, cmd, ioctl);
+	if (err)
+		return err;
+
 	/* Always copy the buffer back, if only to pick up the status */
 	err = copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
 	if (err != 0) {
 		esas2r_log(ESAS2R_LOG_WARN,
 			   "ioctl_handler copy_to_user didn't copy everything (err %d, cmd %u)",
 			   err, cmd);
-		kfree(ioctl);
 
 		return -EFAULT;
 	}
 
-	kfree(ioctl);
-
 	return 0;
 }
 
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index 75a70ae9441b..720be2a0907d 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -613,14 +613,6 @@ static const struct file_operations esas2r_proc_fops = {
 	.unlocked_ioctl = esas2r_proc_ioctl,
 };
 
-static const struct proc_ops esas2r_proc_ops = {
-	.proc_lseek		= default_llseek,
-	.proc_ioctl		= esas2r_proc_ioctl,
-#ifdef CONFIG_COMPAT
-	.proc_compat_ioctl	= compat_ptr_ioctl,
-#endif
-};
-
 static struct Scsi_Host *esas2r_proc_host;
 static int esas2r_proc_major;
 
@@ -630,6 +622,42 @@ long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 				    cmd, (void __user *)arg);
 }
 
+static long esas2r_proc_ioctl_buf(struct file *fp, unsigned int cmd, void *arg)
+{
+	return esas2r_ioctl_buf(esas2r_proc_host->hostdata, cmd, arg);
+}
+
+#define ESAS2R_PROC_IOCTL(_cmd)						\
+	PROC_IOCTL_ARG(_cmd, sizeof(struct atto_express_ioctl),		\
+		       sizeof(struct atto_express_ioctl))
+
+/* esas2r_ioctl_buf() takes every command from EXPRESS_IOCTL_MIN to _MAX. */
+static const struct proc_ioctl_arg esas2r_proc_ioctl_args[] = {
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_RW_FIRMWARE),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_READ_PARAMS),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_WRITE_PARAMS),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_FC_API),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_GET_CHANNELS),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_CHAN_INFO),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_DEFAULT_PARAMS),
+	ESAS2R_PROC_IOCTL(EXPRESS_ADDR_MEMORY),
+	ESAS2R_PROC_IOCTL(EXPRESS_RW_MEMORY),
+	ESAS2R_PROC_IOCTL(EXPRESS_TSDK_DUMP),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_SMP),
+	ESAS2R_PROC_IOCTL(EXPRESS_CSMI),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_HBA),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_VDA),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_GET_ID),
+	ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_GET_MOD_INFO),
+};
+
+static const struct proc_ops esas2r_proc_ops = {
+	.proc_lseek		= default_llseek,
+	.proc_ioctl_buf		= esas2r_proc_ioctl_buf,
+	.proc_ioctl_args	= esas2r_proc_ioctl_args,
+	.proc_nr_ioctl_args	= ARRAY_SIZE(esas2r_proc_ioctl_args),
+};
+
 static void __exit esas2r_exit(void)
 {
 	esas2r_log(ESAS2R_LOG_INFO, "%s called", __func__);

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 11/27] latencytop: let procfs copy in writes to /proc/latency_stats
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 10/27] genirq/proc: let procfs copy in the affinity masks Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 12/27] locking/lockdep: let procfs own the seq_file of /proc/lock_stat Christian Brauner
                     ` (15 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry. The write only clears the statistics and doesn't look
at what was written anyway. Writes larger than PAGE_SIZE - 1 bytes fail
with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/latencytop.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/kernel/latencytop.c b/kernel/latencytop.c
index b764f47606d3..35f73ca07656 100644
--- a/kernel/latencytop.c
+++ b/kernel/latencytop.c
@@ -267,8 +267,7 @@ static int lstats_show(struct seq_file *m, void *v)
 }
 
 static ssize_t
-lstats_write(struct file *file, const char __user *buf, size_t count,
-	     loff_t *offs)
+lstats_write(struct file *file, char *buf, size_t count, loff_t *offs)
 {
 	clear_global_latency_tracing();
 
@@ -277,7 +276,7 @@ lstats_write(struct file *file, const char __user *buf, size_t count,
 
 static const struct proc_ops lstats_proc_ops = {
 	.proc_show	= lstats_show,
-	.proc_write	= lstats_write,
+	.proc_write_buf	= lstats_write,
 };
 
 static int __init init_lstats_procfs(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 07/11] SUNRPC: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (5 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 06/11] SUNRPC: let procfs own the seq_file of the cache content file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 08/11] SUNRPC: let procfs copy out reads Christian Brauner
                     ` (3 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the procfs channel, flush and
use-gss-proxy files so procfs copies in what is written before it pins
the entry. The channel file parses the downcall from that copy and the
flush files share the parsing with rpc_pipefs in do_write_flush(). The
rpc_pipefs files keep copying in themselves.

CACHE_DOWNCALL_MAX and CACHE_FLUSH_MAX name the limits both sides
enforce, procfs through ->proc_write_size. The procfs files are removed
when the network namespace goes away and that no longer waits for a
writer that faults on its buffer.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/sunrpc/auth_gss/svcauth_gss.c | 10 +++----
 net/sunrpc/cache.c                | 63 +++++++++++++++++++++++++++------------
 2 files changed, 48 insertions(+), 25 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 43839e805531..8d105cd900d4 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1408,8 +1408,8 @@ svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
 
 #ifdef CONFIG_PROC_FS
 
-static ssize_t write_gssp(struct file *file, const char __user *buf,
-			 size_t count, loff_t *ppos)
+static ssize_t write_gssp(struct file *file, char *buf, size_t count,
+			  loff_t *ppos)
 {
 	struct net *net = pde_data(file_inode(file));
 	char tbuf[20];
@@ -1418,9 +1418,7 @@ static ssize_t write_gssp(struct file *file, const char __user *buf,
 
 	if (*ppos || count > sizeof(tbuf)-1)
 		return -EINVAL;
-	if (copy_from_user(tbuf, buf, count))
-		return -EFAULT;
-
+	memcpy(tbuf, buf, count);
 	tbuf[count] = 0;
 	res = kstrtoul(tbuf, 0, &i);
 	if (res)
@@ -1468,7 +1466,7 @@ static ssize_t read_gssp(struct file *file, char __user *buf,
 
 static const struct proc_ops use_gss_proxy_proc_ops = {
 	.proc_open	= nonseekable_open,
-	.proc_write	= write_gssp,
+	.proc_write_buf	= write_gssp,
 	.proc_read	= read_gssp,
 };
 
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 007521e8a57f..a257598768fa 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -924,6 +924,9 @@ static ssize_t cache_do_downcall(char *kaddr, const char __user *buf,
 	return ret;
 }
 
+/* 32k is max userland buffer, lets check anyway */
+#define CACHE_DOWNCALL_MAX	(32768 - 1)
+
 static ssize_t cache_downcall(struct address_space *mapping,
 			      const char __user *buf,
 			      size_t count, struct cache_detail *cd)
@@ -931,7 +934,7 @@ static ssize_t cache_downcall(struct address_space *mapping,
 	char *write_buf;
 	ssize_t ret = -ENOMEM;
 
-	if (count >= 32768) { /* 32k is max userland buffer, lets check anyway */
+	if (count > CACHE_DOWNCALL_MAX) {
 		ret = -EINVAL;
 		goto out;
 	}
@@ -1508,19 +1511,15 @@ static ssize_t read_flush(struct file *file, char __user *buf,
 	return simple_read_from_buffer(buf, count, ppos, tbuf, len);
 }
 
-static ssize_t write_flush(struct file *file, const char __user *buf,
-			   size_t count, loff_t *ppos,
-			   struct cache_detail *cd)
+/* longest number the flush files take */
+#define CACHE_FLUSH_MAX		19
+
+static ssize_t do_write_flush(char *tbuf, size_t count, loff_t *ppos,
+			      struct cache_detail *cd)
 {
-	char tbuf[20];
 	char *ep;
 	time64_t now;
 
-	if (*ppos || count > sizeof(tbuf)-1)
-		return -EINVAL;
-	if (copy_from_user(tbuf, buf, count))
-		return -EFAULT;
-	tbuf[count] = 0;
 	simple_strtoul(tbuf, &ep, 0);
 	if (*ep && *ep != '\n')
 		return -EINVAL;
@@ -1551,6 +1550,20 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
 	return count;
 }
 
+static ssize_t write_flush(struct file *file, const char __user *buf,
+			   size_t count, loff_t *ppos,
+			   struct cache_detail *cd)
+{
+	char tbuf[CACHE_FLUSH_MAX + 1];
+
+	if (*ppos || count > CACHE_FLUSH_MAX)
+		return -EINVAL;
+	if (copy_from_user(tbuf, buf, count))
+		return -EFAULT;
+	tbuf[count] = 0;
+	return do_write_flush(tbuf, count, ppos, cd);
+}
+
 static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
 				 size_t count, loff_t *ppos)
 {
@@ -1559,12 +1572,21 @@ static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
 	return cache_read(filp, buf, count, ppos, cd);
 }
 
-static ssize_t cache_write_procfs(struct file *filp, const char __user *buf,
-				  size_t count, loff_t *ppos)
+/* procfs copied in what was written */
+static ssize_t cache_write_procfs(struct file *filp, char *buf, size_t count,
+				  loff_t *ppos)
 {
 	struct cache_detail *cd = pde_data(file_inode(filp));
+	struct inode *inode = file_inode(filp);
+	ssize_t ret;
 
-	return cache_write(filp, buf, count, ppos, cd);
+	if (!cd->cache_parse || !count)
+		return -EINVAL;
+
+	inode_lock(inode);
+	ret = cd->cache_parse(cd, buf, count);
+	inode_unlock(inode);
+	return ret ? ret : count;
 }
 
 static __poll_t cache_poll_procfs(struct file *filp, poll_table *wait)
@@ -1599,7 +1621,8 @@ static int cache_release_procfs(struct inode *inode, struct file *filp)
 
 static const struct proc_ops cache_channel_proc_ops = {
 	.proc_read	= cache_read_procfs,
-	.proc_write	= cache_write_procfs,
+	.proc_write_buf	= cache_write_procfs,
+	.proc_write_size = CACHE_DOWNCALL_MAX,
 	.proc_poll	= cache_poll_procfs,
 	.proc_ioctl	= cache_ioctl_procfs, /* for FIONREAD */
 	.proc_open	= cache_open_procfs,
@@ -1653,19 +1676,21 @@ static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
 	return read_flush(filp, buf, count, ppos, cd);
 }
 
-static ssize_t write_flush_procfs(struct file *filp,
-				  const char __user *buf,
-				  size_t count, loff_t *ppos)
+static ssize_t write_flush_procfs(struct file *filp, char *buf, size_t count,
+				  loff_t *ppos)
 {
 	struct cache_detail *cd = pde_data(file_inode(filp));
 
-	return write_flush(filp, buf, count, ppos, cd);
+	if (*ppos)
+		return -EINVAL;
+	return do_write_flush(buf, count, ppos, cd);
 }
 
 static const struct proc_ops cache_flush_proc_ops = {
 	.proc_open	= open_flush_procfs,
 	.proc_read	= read_flush_procfs,
-	.proc_write	= write_flush_procfs,
+	.proc_write_buf	= write_flush_procfs,
+	.proc_write_size = CACHE_FLUSH_MAX,
 	.proc_release	= release_flush_procfs,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 15/23] hwmon: (dell-smm) let procfs copy the i8k ioctl arguments
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (13 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 14/23] scsi: esas2r: let procfs copy the ioctl argument of the ATTONode file Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 16/23] fbdev: viafb: let procfs copy in writes Christian Brauner
                     ` (7 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_ioctl_buf() so procfs copies the arguments in before
it pins the entry and back out after unpinning it. The command numbers
in <uapi/linux/i8k.h> encode the wrong sizes, so the table lists what
is really copied: an int or the 16 bytes of the machine id out, and one
or two ints in.

A NULL argument fails with -EFAULT now instead of -EINVAL, and so does
a bad pointer to SET_FAN from an unprivileged task, which got -EPERM
before, since procfs copies the argument in before the entry's own
checks run. 32-bit tasks can use the commands whose numbers don't
depend on the size of size_t.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/hwmon/dell-smm-hwmon.c | 48 +++++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index 492b2b6c6d7e..67b8c6e5752f 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -549,16 +549,13 @@ static int i8k_get_power_status(const struct dell_smm_data *data)
  * Procfs interface
  */
 
-static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+static long i8k_ioctl(struct file *fp, unsigned int cmd, void *arg)
 {
 	struct dell_smm_data *data = pde_data(file_inode(fp));
-	int __user *argp = (int __user *)arg;
+	int *argp = arg;
 	int speed, err;
 	int val = 0;
 
-	if (!argp)
-		return -EINVAL;
-
 	switch (cmd) {
 	case I8K_BIOS_VERSION:
 		if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) ||
@@ -568,17 +565,13 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		val = (data->bios_version[0] << 16) |
 				(data->bios_version[1] << 8) | data->bios_version[2];
 
-		if (copy_to_user(argp, &val, sizeof(val)))
-			return -EFAULT;
-
+		*argp = val;
 		return 0;
 	case I8K_MACHINE_ID:
 		if (restricted && !capable(CAP_SYS_ADMIN))
 			return -EPERM;
 
-		if (copy_to_user(argp, data->bios_machineid, sizeof(data->bios_machineid)))
-			return -EFAULT;
-
+		memcpy(argp, data->bios_machineid, sizeof(data->bios_machineid));
 		return 0;
 	case I8K_FN_STATUS:
 		val = i8k_get_fn_status(data);
@@ -593,8 +586,7 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		break;
 
 	case I8K_GET_SPEED:
-		if (copy_from_user(&val, argp, sizeof(int)))
-			return -EFAULT;
+		val = *argp;
 
 		if (val > U8_MAX || val < 0)
 			return -EINVAL;
@@ -603,8 +595,7 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		break;
 
 	case I8K_GET_FAN:
-		if (copy_from_user(&val, argp, sizeof(int)))
-			return -EFAULT;
+		val = *argp;
 
 		if (val > U8_MAX || val < 0)
 			return -EINVAL;
@@ -616,14 +607,11 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 		if (restricted && !capable(CAP_SYS_ADMIN))
 			return -EPERM;
 
-		if (copy_from_user(&val, argp, sizeof(int)))
-			return -EFAULT;
-
+		val = argp[0];
 		if (val > U8_MAX || val < 0)
 			return -EINVAL;
 
-		if (copy_from_user(&speed, argp + 1, sizeof(int)))
-			return -EFAULT;
+		speed = argp[1];
 
 		speed = clamp_val(speed, 0, data->i8k_fan_max);
 
@@ -643,12 +631,22 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 	if (val < 0)
 		return val;
 
-	if (copy_to_user(argp, &val, sizeof(int)))
-		return -EFAULT;
-
+	*argp = val;
 	return 0;
 }
 
+/* The command numbers don't encode what is copied, see <uapi/linux/i8k.h>. */
+static const struct proc_ioctl_arg i8k_ioctl_args[] = {
+	PROC_IOCTL_ARG(I8K_BIOS_VERSION, 0, sizeof(int)),
+	PROC_IOCTL_ARG(I8K_MACHINE_ID, 0, sizeof_field(struct dell_smm_data, bios_machineid)),
+	PROC_IOCTL_ARG(I8K_FN_STATUS, 0, sizeof(int)),
+	PROC_IOCTL_ARG(I8K_POWER_STATUS, 0, sizeof(int)),
+	PROC_IOCTL_ARG(I8K_GET_TEMP, 0, sizeof(int)),
+	PROC_IOCTL_ARG(I8K_GET_SPEED, sizeof(int), sizeof(int)),
+	PROC_IOCTL_ARG(I8K_GET_FAN, sizeof(int), sizeof(int)),
+	PROC_IOCTL_ARG(I8K_SET_FAN, 2 * sizeof(int), sizeof(int)),
+};
+
 /*
  * Print the information for /proc/i8k.
  */
@@ -696,7 +694,9 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
 
 static const struct proc_ops i8k_proc_ops = {
 	.proc_show	= i8k_proc_show,
-	.proc_ioctl	= i8k_ioctl,
+	.proc_ioctl_buf	= i8k_ioctl,
+	.proc_ioctl_args = i8k_ioctl_args,
+	.proc_nr_ioctl_args = ARRAY_SIZE(i8k_ioctl_args),
 };
 
 static void i8k_exit_procfs(void *param)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 12/27] locking/lockdep: let procfs own the seq_file of /proc/lock_stat
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (10 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 11/27] latencytop: let procfs copy in writes to /proc/latency_stats Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 13/27] locking/lockdep: let procfs copy in writes to /proc/lock_stat Christian Brauner
                     ` (14 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and let procfs open and free the seq_file.
->proc_open() only takes the snapshot of the statistics now and
->proc_release() frees it.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/locking/lockdep_proc.c | 48 +++++++++++++++++++------------------------
 1 file changed, 21 insertions(+), 27 deletions(-)

diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index 1916db9aa46b..9b034b5b94bd 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -638,40 +638,35 @@ static const struct seq_operations lockstat_ops = {
 	.show	= ls_show,
 };
 
+/* procfs opens the seq_file */
 static int lock_stat_open(struct inode *inode, struct file *file)
 {
-	int res;
-	struct lock_class *class;
 	struct lock_stat_seq *data = vmalloc(sizeof(struct lock_stat_seq));
+	struct lock_stat_data *iter;
+	struct seq_file *m = file->private_data;
+	struct lock_class *class;
+	unsigned long idx;
 
 	if (!data)
 		return -ENOMEM;
 
-	res = seq_open(file, &lockstat_ops);
-	if (!res) {
-		struct lock_stat_data *iter = data->stats;
-		struct seq_file *m = file->private_data;
-		unsigned long idx;
-
-		iterate_lock_classes(idx, class) {
-			if (!test_bit(idx, lock_classes_in_use))
-				continue;
-			iter->class = class;
-			lock_stats(class, &iter->stats);
-			iter++;
-		}
-
-		data->iter_end = iter;
+	iter = data->stats;
+	iterate_lock_classes(idx, class) {
+		if (!test_bit(idx, lock_classes_in_use))
+			continue;
+		iter->class = class;
+		lock_stats(class, &iter->stats);
+		iter++;
+	}
 
-		sort(data->stats, data->iter_end - data->stats,
-				sizeof(struct lock_stat_data),
-				lock_stat_cmp, NULL);
+	data->iter_end = iter;
 
-		m->private = data;
-	} else
-		vfree(data);
+	sort(data->stats, data->iter_end - data->stats,
+			sizeof(struct lock_stat_data),
+			lock_stat_cmp, NULL);
 
-	return res;
+	m->private = data;
+	return 0;
 }
 
 static ssize_t lock_stat_write(struct file *file, const char __user *buf,
@@ -702,14 +697,13 @@ static int lock_stat_release(struct inode *inode, struct file *file)
 	struct seq_file *seq = file->private_data;
 
 	vfree(seq->private);
-	return seq_release(inode, file);
+	return 0;
 }
 
 static const struct proc_ops lock_stat_proc_ops = {
+	.proc_seq_ops	= &lockstat_ops,
 	.proc_open	= lock_stat_open,
 	.proc_write	= lock_stat_write,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= lock_stat_release,
 };
 #endif /* CONFIG_LOCK_STAT */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 08/11] SUNRPC: let procfs copy out reads
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (6 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 07/11] SUNRPC: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 09/11] SUNRPC: drop the ioctl of the procfs channel files Christian Brauner
                     ` (2 subsequent siblings)
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() for the procfs channel, flush,
use-gss-proxy and gss_krb5_enctypes files so procfs copies what is read
to userspace after it unpinned the entry.

cache_read() takes an iov_iter now. The rpc_pipefs channel file passes
one for the user buffer and behaves like before. The procfs one passes
one for procfs' buffer, and a read still hands out at most one request.
A reader of the procfs channel file whose copy out fails, a readv()
with a bad later segment included, loses what was handed out, since the
request moves on before procfs copies it out. If that was only part of
a request the next read returns the rest of it. Both flush files share
flush_time_text().

The procfs files are removed when the network namespace goes away and
that no longer waits for a reader that faults on its buffer.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/sunrpc/auth_gss/svcauth_gss.c | 24 ++++++-------------
 net/sunrpc/cache.c                | 50 +++++++++++++++++++++++----------------
 2 files changed, 36 insertions(+), 38 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 8d105cd900d4..67e086a7127c 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1442,32 +1442,22 @@ static ssize_t write_gssp(struct file *file, char *buf, size_t count,
 	return count;
 }
 
-static ssize_t read_gssp(struct file *file, char __user *buf,
+static ssize_t read_gssp(struct file *file, char *buf,
 			 size_t count, loff_t *ppos)
 {
 	struct net *net = pde_data(file_inode(file));
 	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
-	unsigned long p = *ppos;
 	char tbuf[10];
 	size_t len;
 
-	snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy);
-	len = strlen(tbuf);
-	if (p >= len)
-		return 0;
-	len -= p;
-	if (len > count)
-		len = count;
-	if (copy_to_user(buf, (void *)(tbuf+p), len))
-		return -EFAULT;
-	*ppos += len;
-	return len;
+	len = snprintf(tbuf, sizeof(tbuf), "%d\n", sn->use_gss_proxy);
+	return memory_read_from_buffer(buf, count, ppos, tbuf, len);
 }
 
 static const struct proc_ops use_gss_proxy_proc_ops = {
 	.proc_open	= nonseekable_open,
 	.proc_write_buf	= write_gssp,
-	.proc_read	= read_gssp,
+	.proc_read_buf	= read_gssp,
 };
 
 static int create_use_gss_proxy_proc_entry(struct net *net)
@@ -1494,7 +1484,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net)
 	}
 }
 
-static ssize_t read_gss_krb5_enctypes(struct file *file, char __user *buf,
+static ssize_t read_gss_krb5_enctypes(struct file *file, char *buf,
 				      size_t count, loff_t *ppos)
 {
 	struct rpcsec_gss_oid oid = {
@@ -1512,7 +1502,7 @@ static ssize_t read_gss_krb5_enctypes(struct file *file, char __user *buf,
 		return 0;
 	}
 
-	ret = simple_read_from_buffer(buf, count, ppos,
+	ret = memory_read_from_buffer(buf, count, ppos,
 				      mech->gm_upcall_enctypes,
 				      strlen(mech->gm_upcall_enctypes));
 	gss_mech_put(mech);
@@ -1521,7 +1511,7 @@ static ssize_t read_gss_krb5_enctypes(struct file *file, char __user *buf,
 
 static const struct proc_ops gss_krb5_enctypes_proc_ops = {
 	.proc_open	= nonseekable_open,
-	.proc_read	= read_gss_krb5_enctypes,
+	.proc_read_buf	= read_gss_krb5_enctypes,
 };
 
 static int create_krb5_enctypes_proc_entry(struct net *net)
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index a257598768fa..81a3f0068f36 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -28,6 +28,7 @@
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
 #include <linux/pagemap.h>
+#include <linux/uio.h>
 #include <asm/ioctls.h>
 #include <linux/sunrpc/types.h>
 #include <linux/sunrpc/cache.h>
@@ -837,9 +838,10 @@ cache_next_request(struct cache_detail *cd, u64 seqno)
 	return NULL;
 }
 
-static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
-			  loff_t *ppos, struct cache_detail *cd)
+static ssize_t cache_read(struct file *filp, struct iov_iter *to,
+			  struct cache_detail *cd)
 {
+	size_t count = iov_iter_count(to);
 	struct cache_reader *rp = filp->private_data;
 	struct cache_request *rq;
 	struct inode *inode = file_inode(filp);
@@ -878,7 +880,7 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
 		if (rp->offset + count > rq->len)
 			count = rq->len - rp->offset;
 		err = -EFAULT;
-		if (copy_to_user(buf, rq->buf + rp->offset, count))
+		if (copy_to_iter(rq->buf + rp->offset, count, to) != count)
 			goto out;
 		rp->offset += count;
 		if (rp->offset >= rq->len) {
@@ -1499,16 +1501,11 @@ static int release_flush(struct inode *inode, struct file *file,
 	return 0;
 }
 
-static ssize_t read_flush(struct file *file, char __user *buf,
-			  size_t count, loff_t *ppos,
-			  struct cache_detail *cd)
+/* what reading the flush files returns */
+static size_t flush_time_text(struct cache_detail *cd, char *tbuf, size_t size)
 {
-	char tbuf[22];
-	size_t len;
-
-	len = snprintf(tbuf, sizeof(tbuf), "%llu\n",
+	return snprintf(tbuf, size, "%llu\n",
 			convert_to_wallclock(cd->flush_time));
-	return simple_read_from_buffer(buf, count, ppos, tbuf, len);
 }
 
 /* longest number the flush files take */
@@ -1564,12 +1561,15 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
 	return do_write_flush(tbuf, count, ppos, cd);
 }
 
-static ssize_t cache_read_procfs(struct file *filp, char __user *buf,
-				 size_t count, loff_t *ppos)
+static ssize_t cache_read_procfs(struct file *filp, char *buf, size_t count,
+				 loff_t *ppos)
 {
 	struct cache_detail *cd = pde_data(file_inode(filp));
+	struct kvec kvec = { .iov_base = buf, .iov_len = count };
+	struct iov_iter to;
 
-	return cache_read(filp, buf, count, ppos, cd);
+	iov_iter_kvec(&to, ITER_DEST, &kvec, 1, count);
+	return cache_read(filp, &to, cd);
 }
 
 /* procfs copied in what was written */
@@ -1620,7 +1620,7 @@ static int cache_release_procfs(struct inode *inode, struct file *filp)
 }
 
 static const struct proc_ops cache_channel_proc_ops = {
-	.proc_read	= cache_read_procfs,
+	.proc_read_buf	= cache_read_procfs,
 	.proc_write_buf	= cache_write_procfs,
 	.proc_write_size = CACHE_DOWNCALL_MAX,
 	.proc_poll	= cache_poll_procfs,
@@ -1668,12 +1668,15 @@ static int release_flush_procfs(struct inode *inode, struct file *filp)
 	return release_flush(inode, filp, cd);
 }
 
-static ssize_t read_flush_procfs(struct file *filp, char __user *buf,
-			    size_t count, loff_t *ppos)
+static ssize_t read_flush_procfs(struct file *filp, char *buf, size_t count,
+				 loff_t *ppos)
 {
 	struct cache_detail *cd = pde_data(file_inode(filp));
+	char tbuf[22];
+	size_t len;
 
-	return read_flush(filp, buf, count, ppos, cd);
+	len = flush_time_text(cd, tbuf, sizeof(tbuf));
+	return memory_read_from_buffer(buf, count, ppos, tbuf, len);
 }
 
 static ssize_t write_flush_procfs(struct file *filp, char *buf, size_t count,
@@ -1688,7 +1691,7 @@ static ssize_t write_flush_procfs(struct file *filp, char *buf, size_t count,
 
 static const struct proc_ops cache_flush_proc_ops = {
 	.proc_open	= open_flush_procfs,
-	.proc_read	= read_flush_procfs,
+	.proc_read_buf	= read_flush_procfs,
 	.proc_write_buf	= write_flush_procfs,
 	.proc_write_size = CACHE_FLUSH_MAX,
 	.proc_release	= release_flush_procfs,
@@ -1795,8 +1798,10 @@ static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
 				 size_t count, loff_t *ppos)
 {
 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
+	struct iov_iter to;
 
-	return cache_read(filp, buf, count, ppos, cd);
+	iov_iter_ubuf(&to, ITER_DEST, buf, count);
+	return cache_read(filp, &to, cd);
 }
 
 static ssize_t cache_write_pipefs(struct file *filp, const char __user *buf,
@@ -1886,8 +1891,11 @@ static ssize_t read_flush_pipefs(struct file *filp, char __user *buf,
 			    size_t count, loff_t *ppos)
 {
 	struct cache_detail *cd = RPC_I(file_inode(filp))->private;
+	char tbuf[22];
+	size_t len;
 
-	return read_flush(filp, buf, count, ppos, cd);
+	len = flush_time_text(cd, tbuf, sizeof(tbuf));
+	return simple_read_from_buffer(buf, count, ppos, tbuf, len);
 }
 
 static ssize_t write_flush_pipefs(struct file *filp,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 16/23] fbdev: viafb: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (14 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 15/23] hwmon: (dell-smm) let procfs copy the i8k ioctl arguments Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 17/23] usb: gadget: function: rndis: " Christian Brauner
                     ` (6 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the files in /proc/viafb so procfs
copies in what is written before it pins the entry, and parse what was
written from that copy. Writes larger than PAGE_SIZE - 1 bytes fail with
-EINVAL now, the output device files refused anything over 63 bytes
already. kstrtou8_from_user() looked at the first 10 bytes of what was
written to dfph and dfpl, now the whole write is parsed.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/video/fbdev/via/viafbdev.c | 46 +++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 25 deletions(-)

diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c
index 4b617e80a77c..94b303006915 100644
--- a/drivers/video/fbdev/via/viafbdev.c
+++ b/drivers/video/fbdev/via/viafbdev.c
@@ -1121,7 +1121,7 @@ static int viafb_dvp0_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_dvp0_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	char buf[20], *value, *pbuf;
 	u8 reg_val = 0;
@@ -1129,8 +1129,7 @@ static ssize_t viafb_dvp0_proc_write(struct file *file,
 	if (count < 1)
 		return -EINVAL;
 	length = count > 20 ? 20 : count;
-	if (copy_from_user(&buf[0], buffer, length))
-		return -EFAULT;
+	memcpy(&buf[0], buffer, length);
 	buf[length - 1] = '\0';	/*Ensure end string */
 	pbuf = &buf[0];
 	for (i = 0; i < 3; i++) {
@@ -1169,7 +1168,7 @@ static ssize_t viafb_dvp0_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dvp0_proc_ops = {
 	.proc_show	= viafb_dvp0_proc_show,
-	.proc_write	= viafb_dvp0_proc_write,
+	.proc_write_buf	= viafb_dvp0_proc_write,
 };
 
 static int viafb_dvp1_proc_show(struct seq_file *m, void *v)
@@ -1183,7 +1182,7 @@ static int viafb_dvp1_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_dvp1_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	char buf[20], *value, *pbuf;
 	u8 reg_val = 0;
@@ -1191,8 +1190,7 @@ static ssize_t viafb_dvp1_proc_write(struct file *file,
 	if (count < 1)
 		return -EINVAL;
 	length = count > 20 ? 20 : count;
-	if (copy_from_user(&buf[0], buffer, length))
-		return -EFAULT;
+	memcpy(&buf[0], buffer, length);
 	buf[length - 1] = '\0';	/*Ensure end string */
 	pbuf = &buf[0];
 	for (i = 0; i < 3; i++) {
@@ -1225,7 +1223,7 @@ static ssize_t viafb_dvp1_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dvp1_proc_ops = {
 	.proc_show	= viafb_dvp1_proc_show,
-	.proc_write	= viafb_dvp1_proc_write,
+	.proc_write_buf	= viafb_dvp1_proc_write,
 };
 
 static int viafb_dfph_proc_show(struct seq_file *m, void *v)
@@ -1237,11 +1235,11 @@ static int viafb_dfph_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_dfph_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	int err;
 	u8 reg_val;
-	err = kstrtou8_from_user(buffer, count, 0, &reg_val);
+	err = kstrtou8(buffer, 0, &reg_val);
 	if (err)
 		return err;
 
@@ -1251,7 +1249,7 @@ static ssize_t viafb_dfph_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dfph_proc_ops = {
 	.proc_show	= viafb_dfph_proc_show,
-	.proc_write	= viafb_dfph_proc_write,
+	.proc_write_buf	= viafb_dfph_proc_write,
 };
 
 static int viafb_dfpl_proc_show(struct seq_file *m, void *v)
@@ -1263,11 +1261,11 @@ static int viafb_dfpl_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_dfpl_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	int err;
 	u8 reg_val;
-	err = kstrtou8_from_user(buffer, count, 0, &reg_val);
+	err = kstrtou8(buffer, 0, &reg_val);
 	if (err)
 		return err;
 
@@ -1277,7 +1275,7 @@ static ssize_t viafb_dfpl_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dfpl_proc_ops = {
 	.proc_show	= viafb_dfpl_proc_show,
-	.proc_write	= viafb_dfpl_proc_write,
+	.proc_write_buf	= viafb_dfpl_proc_write,
 };
 
 static int viafb_vt1636_proc_show(struct seq_file *m, void *v)
@@ -1313,7 +1311,7 @@ static int viafb_vt1636_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_vt1636_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	char buf[30], *value, *pbuf;
 	struct IODATA reg_val;
@@ -1321,8 +1319,7 @@ static ssize_t viafb_vt1636_proc_write(struct file *file,
 	if (count < 1)
 		return -EINVAL;
 	length = count > 30 ? 30 : count;
-	if (copy_from_user(&buf[0], buffer, length))
-		return -EFAULT;
+	memcpy(&buf[0], buffer, length);
 	buf[length - 1] = '\0';	/*Ensure end string */
 	pbuf = &buf[0];
 	switch (viaparinfo->chip_info->lvds_chip_info.lvds_chip_name) {
@@ -1404,7 +1401,7 @@ static ssize_t viafb_vt1636_proc_write(struct file *file,
 
 static const struct proc_ops viafb_vt1636_proc_ops = {
 	.proc_show	= viafb_vt1636_proc_show,
-	.proc_write	= viafb_vt1636_proc_write,
+	.proc_write_buf	= viafb_vt1636_proc_write,
 };
 
 #endif /* CONFIG_FB_VIA_DIRECT_PROCFS */
@@ -1416,7 +1413,7 @@ static int __maybe_unused viafb_sup_odev_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t odev_update(const char __user *buffer, size_t count, u32 *odev)
+static ssize_t odev_update(const char *buffer, size_t count, u32 *odev)
 {
 	char buf[64], *ptr = buf;
 	u32 devices;
@@ -1424,8 +1421,7 @@ static ssize_t odev_update(const char __user *buffer, size_t count, u32 *odev)
 
 	if (count < 1 || count > 63)
 		return -EINVAL;
-	if (copy_from_user(&buf[0], buffer, count))
-		return -EFAULT;
+	memcpy(&buf[0], buffer, count);
 	buf[count] = '\0';
 	add = buf[0] == '+';
 	sub = buf[0] == '-';
@@ -1452,7 +1448,7 @@ static int viafb_iga1_odev_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_iga1_odev_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	u32 dev_on, dev_off, dev_old, dev_new;
 	ssize_t res;
@@ -1473,7 +1469,7 @@ static ssize_t viafb_iga1_odev_proc_write(struct file *file,
 
 static const struct proc_ops viafb_iga1_odev_proc_ops = {
 	.proc_show	= viafb_iga1_odev_proc_show,
-	.proc_write	= viafb_iga1_odev_proc_write,
+	.proc_write_buf	= viafb_iga1_odev_proc_write,
 };
 
 static int viafb_iga2_odev_proc_show(struct seq_file *m, void *v)
@@ -1483,7 +1479,7 @@ static int viafb_iga2_odev_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t viafb_iga2_odev_proc_write(struct file *file,
-	const char __user *buffer, size_t count, loff_t *pos)
+	char *buffer, size_t count, loff_t *pos)
 {
 	u32 dev_on, dev_off, dev_old, dev_new;
 	ssize_t res;
@@ -1504,7 +1500,7 @@ static ssize_t viafb_iga2_odev_proc_write(struct file *file,
 
 static const struct proc_ops viafb_iga2_odev_proc_ops = {
 	.proc_show	= viafb_iga2_odev_proc_show,
-	.proc_write	= viafb_iga2_odev_proc_write,
+	.proc_write_buf	= viafb_iga2_odev_proc_write,
 };
 
 #define IS_VT1636(lvds_chip)	((lvds_chip).lvds_chip_name == VT1636_LVDS)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 13/27] locking/lockdep: let procfs copy in writes to /proc/lock_stat
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (11 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 12/27] locking/lockdep: let procfs own the seq_file of /proc/lock_stat Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 14/27] profiling: let procfs copy in writes to /proc/profile Christian Brauner
                     ` (13 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and look at the first character of that copy. Writes
larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/locking/lockdep_proc.c | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index 9b034b5b94bd..2a66ea09c2aa 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -669,18 +669,14 @@ static int lock_stat_open(struct inode *inode, struct file *file)
 	return 0;
 }
 
-static ssize_t lock_stat_write(struct file *file, const char __user *buf,
-			       size_t count, loff_t *ppos)
+static ssize_t lock_stat_write(struct file *file, char *buf, size_t count,
+			       loff_t *ppos)
 {
 	struct lock_class *class;
 	unsigned long idx;
-	char c;
 
 	if (count) {
-		if (get_user(c, buf))
-			return -EFAULT;
-
-		if (c != '0')
+		if (buf[0] != '0')
 			return count;
 
 		iterate_lock_classes(idx, class) {
@@ -703,7 +699,7 @@ static int lock_stat_release(struct inode *inode, struct file *file)
 static const struct proc_ops lock_stat_proc_ops = {
 	.proc_seq_ops	= &lockstat_ops,
 	.proc_open	= lock_stat_open,
-	.proc_write	= lock_stat_write,
+	.proc_write_buf	= lock_stat_write,
 	.proc_release	= lock_stat_release,
 };
 #endif /* CONFIG_LOCK_STAT */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 09/11] SUNRPC: drop the ioctl of the procfs channel files
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (7 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 08/11] SUNRPC: let procfs copy out reads Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 21:11     ` Chuck Lever
  2026-11-02 15:07   ` [PATCH RFC 10/11] samples/kfifo: let procfs copy in writes Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 11/11] samples/kfifo: let procfs copy out reads Christian Brauner
  10 siblings, 1 reply; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

do_vfs_ioctl() answers FIONREAD for regular files with i_size - f_pos
before it gets to the file's ->unlocked_ioctl(). The channel files are
regular files, so cache_ioctl_procfs() has never seen FIONREAD and only
ever failed other commands with -EINVAL. Drop it. Other commands fail
with -ENOTTY now.

The rpc_pipefs channel files are regular files as well.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 net/sunrpc/cache.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 81a3f0068f36..d242b6f33cbc 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1596,15 +1596,6 @@ static __poll_t cache_poll_procfs(struct file *filp, poll_table *wait)
 	return cache_poll(filp, wait, cd);
 }
 
-static long cache_ioctl_procfs(struct file *filp,
-			       unsigned int cmd, unsigned long arg)
-{
-	struct inode *inode = file_inode(filp);
-	struct cache_detail *cd = pde_data(inode);
-
-	return cache_ioctl(inode, filp, cmd, arg, cd);
-}
-
 static int cache_open_procfs(struct inode *inode, struct file *filp)
 {
 	struct cache_detail *cd = pde_data(inode);
@@ -1624,7 +1615,6 @@ static const struct proc_ops cache_channel_proc_ops = {
 	.proc_write_buf	= cache_write_procfs,
 	.proc_write_size = CACHE_DOWNCALL_MAX,
 	.proc_poll	= cache_poll_procfs,
-	.proc_ioctl	= cache_ioctl_procfs, /* for FIONREAD */
 	.proc_open	= cache_open_procfs,
 	.proc_release	= cache_release_procfs,
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 17/23] usb: gadget: function: rndis: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (15 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 16/23] fbdev: viafb: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 18/23] wifi: ipw2x00: let procfs copy in writes to the debug level Christian Brauner
                     ` (5 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written to
/proc/driver/rndis-<n> before it pins the entry, and handle the
commands from that copy. Writes larger than PAGE_SIZE - 1 bytes fail
with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/usb/gadget/function/rndis.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 3c7bd30b8a3a..e05e0c6ae8fb 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -1138,7 +1138,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
 	return 0;
 }
 
-static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
+static ssize_t rndis_proc_write(struct file *file, char *buffer,
 				size_t count, loff_t *ppos)
 {
 	rndis_params *p = pde_data(file_inode(file));
@@ -1146,9 +1146,8 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
 	int i, fl_speed = 0;
 
 	for (i = 0; i < count; i++) {
-		char c;
-		if (get_user(c, buffer))
-			return -EFAULT;
+		char c = *buffer;
+
 		switch (c) {
 		case '0':
 		case '1':
@@ -1185,7 +1184,7 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
 
 static const struct proc_ops rndis_proc_ops = {
 	.proc_show	= rndis_proc_show,
-	.proc_write	= rndis_proc_write,
+	.proc_write_buf	= rndis_proc_write,
 };
 
 #define	NAME_TEMPLATE "driver/rndis-%03d"

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 14/27] profiling: let procfs copy in writes to /proc/profile
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (12 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 13/27] locking/lockdep: let procfs copy in writes to /proc/lock_stat Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 15/27] profiling: mark /proc/profile permanent Christian Brauner
                     ` (12 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and take the multiplier from that copy. Writes
larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/profile.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/kernel/profile.c b/kernel/profile.c
index e7defa90a34c..6e4b34f35e3b 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -189,16 +189,14 @@ int __weak setup_profiling_timer(unsigned mult)
  * Writing a 'profiling multiplier' value into it also re-sets the profiling
  * interrupt frequency, on architectures that support this.
  */
-static ssize_t write_profile(struct file *file, const char __user *buf,
-			     size_t count, loff_t *ppos)
+static ssize_t write_profile(struct file *file, char *buf, size_t count,
+			     loff_t *ppos)
 {
 #ifdef CONFIG_SMP
 	if (count == sizeof(int)) {
 		unsigned int multiplier;
 
-		if (copy_from_user(&multiplier, buf, sizeof(int)))
-			return -EFAULT;
-
+		memcpy(&multiplier, buf, sizeof(int));
 		if (setup_profiling_timer(multiplier))
 			return -EINVAL;
 	}
@@ -209,7 +207,7 @@ static ssize_t write_profile(struct file *file, const char __user *buf,
 
 static const struct proc_ops profile_proc_ops = {
 	.proc_read	= read_profile,
-	.proc_write	= write_profile,
+	.proc_write_buf	= write_profile,
 	.proc_lseek	= default_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 10/11] samples/kfifo: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (8 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 09/11] SUNRPC: drop the ioctl of the procfs channel files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 11/11] samples/kfifo: let procfs copy out reads Christian Brauner
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and put that copy into the fifo with kfifo_in()
instead of kfifo_from_user(). Writes larger than PAGE_SIZE - 1 bytes
fail with -EINVAL now. kfifo_from_user() cut a record to 255 bytes,
what the one byte that holds its length can say, so the record sample
keeps doing that with PROC_ENTRY_SHORT_WRITES.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 samples/kfifo/bytestream-example.c |  9 +++------
 samples/kfifo/inttype-example.c    | 10 ++++------
 samples/kfifo/record-example.c     | 12 ++++++------
 3 files changed, 13 insertions(+), 18 deletions(-)

diff --git a/samples/kfifo/bytestream-example.c b/samples/kfifo/bytestream-example.c
index 4ae29a12cc8a..8c58fb46a2bf 100644
--- a/samples/kfifo/bytestream-example.c
+++ b/samples/kfifo/bytestream-example.c
@@ -110,20 +110,17 @@ static int __init testfunc(void)
 	return 0;
 }
 
-static ssize_t fifo_write(struct file *file, const char __user *buf,
+static ssize_t fifo_write(struct file *file, char *buf,
 						size_t count, loff_t *ppos)
 {
-	int ret;
 	unsigned int copied;
 
 	if (mutex_lock_interruptible(&write_access))
 		return -ERESTARTSYS;
 
-	ret = kfifo_from_user(&test, buf, count, &copied);
+	copied = kfifo_in(&test, buf, count);
 
 	mutex_unlock(&write_access);
-	if (ret)
-		return ret;
 
 	return copied;
 }
@@ -148,7 +145,7 @@ static ssize_t fifo_read(struct file *file, char __user *buf,
 
 static const struct proc_ops fifo_proc_ops = {
 	.proc_read	= fifo_read,
-	.proc_write	= fifo_write,
+	.proc_write_buf	= fifo_write,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/samples/kfifo/inttype-example.c b/samples/kfifo/inttype-example.c
index e4f93317c5d0..c5d16b815e73 100644
--- a/samples/kfifo/inttype-example.c
+++ b/samples/kfifo/inttype-example.c
@@ -103,20 +103,18 @@ static int __init testfunc(void)
 	return 0;
 }
 
-static ssize_t fifo_write(struct file *file, const char __user *buf,
+static ssize_t fifo_write(struct file *file, char *buf,
 						size_t count, loff_t *ppos)
 {
-	int ret;
 	unsigned int copied;
 
 	if (mutex_lock_interruptible(&write_access))
 		return -ERESTARTSYS;
 
-	ret = kfifo_from_user(&test, buf, count, &copied);
+	copied = kfifo_in(&test, (const int *)buf, count / sizeof(int)) *
+		 sizeof(int);
 
 	mutex_unlock(&write_access);
-	if (ret)
-		return ret;
 
 	return copied;
 }
@@ -141,7 +139,7 @@ static ssize_t fifo_read(struct file *file, char __user *buf,
 
 static const struct proc_ops fifo_proc_ops = {
 	.proc_read	= fifo_read,
-	.proc_write	= fifo_write,
+	.proc_write_buf	= fifo_write,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/samples/kfifo/record-example.c b/samples/kfifo/record-example.c
index e4d1a2d7983c..dcc749247c64 100644
--- a/samples/kfifo/record-example.c
+++ b/samples/kfifo/record-example.c
@@ -117,20 +117,17 @@ static int __init testfunc(void)
 	return 0;
 }
 
-static ssize_t fifo_write(struct file *file, const char __user *buf,
+static ssize_t fifo_write(struct file *file, char *buf,
 						size_t count, loff_t *ppos)
 {
-	int ret;
 	unsigned int copied;
 
 	if (mutex_lock_interruptible(&write_access))
 		return -ERESTARTSYS;
 
-	ret = kfifo_from_user(&test, buf, count, &copied);
+	copied = kfifo_in(&test, buf, count);
 
 	mutex_unlock(&write_access);
-	if (ret)
-		return ret;
 
 	return copied;
 }
@@ -155,8 +152,11 @@ static ssize_t fifo_read(struct file *file, char __user *buf,
 
 static const struct proc_ops fifo_proc_ops = {
 	.proc_read	= fifo_read,
-	.proc_write	= fifo_write,
+	.proc_write_buf	= fifo_write,
 	.proc_lseek	= noop_llseek,
+	/* kfifo_from_user() cut a record to what its length byte holds */
+	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
+	.proc_write_size = U8_MAX,
 };
 
 static int __init example_init(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 18/23] wifi: ipw2x00: let procfs copy in writes to the debug level
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (16 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 17/23] usb: gadget: function: rndis: " Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 19/23] tty: sysrq: let procfs copy in writes to /proc/sysrq-trigger Christian Brauner
                     ` (4 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and parse the level from that copy. Writes larger
than PAGE_SIZE - 1 bytes fail with -EINVAL now, and a buffer that can't
be read fails with -EFAULT instead of looking like it was written.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/net/wireless/intel/ipw2x00/libipw_module.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index 15d9a5efb581..08cc0d7f57c1 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
@@ -216,14 +216,13 @@ static int debug_level_proc_show(struct seq_file *m, void *v)
 }
 
 static ssize_t debug_level_proc_write(struct file *file,
-		const char __user *buffer, size_t count, loff_t *pos)
+		char *buffer, size_t count, loff_t *pos)
 {
 	char buf[] = "0x00000000\n";
 	size_t len = min(sizeof(buf) - 1, count);
 	unsigned long val;
 
-	if (copy_from_user(buf, buffer, len))
-		return count;
+	memcpy(buf, buffer, len);
 	buf[len] = 0;
 	if (sscanf(buf, "%li", &val) != 1)
 		printk(KERN_INFO DRV_NAME
@@ -236,7 +235,7 @@ static ssize_t debug_level_proc_write(struct file *file,
 
 static const struct proc_ops debug_level_proc_ops = {
 	.proc_show	= debug_level_proc_show,
-	.proc_write	= debug_level_proc_write,
+	.proc_write_buf	= debug_level_proc_write,
 };
 #endif				/* CONFIG_LIBIPW_DEBUG */
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 15/27] profiling: mark /proc/profile permanent
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (13 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 14/27] profiling: let procfs copy in writes to /proc/profile Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 16/27] sched/psi: let procfs own the seq_file of the pressure files Christian Brauner
                     ` (11 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

/proc/profile is created when profiling is turned on and never removed.
Say so with PROC_ENTRY_PERMANENT, procfs then calls into it without
pinning it. It keeps reading straight into userspace through
->proc_read() since readprofile reads the whole file in one go and a
copy of all of it through a procfs buffer doesn't buy anything for an
entry that can't go away.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/profile.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/kernel/profile.c b/kernel/profile.c
index 6e4b34f35e3b..6f2441dd4a00 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -206,6 +206,7 @@ static ssize_t write_profile(struct file *file, char *buf, size_t count,
 }
 
 static const struct proc_ops profile_proc_ops = {
+	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_read	= read_profile,
 	.proc_write_buf	= write_profile,
 	.proc_lseek	= default_llseek,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 11/11] samples/kfifo: let procfs copy out reads
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
                     ` (9 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 10/11] samples/kfifo: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  10 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry, and take the data out of the fifo with
kfifo_out() instead of kfifo_to_user(). A reader that passes a bad
buffer loses what was taken out for it now, kfifo_to_user() left that in
the fifo. The samples can be spliced now too, and a sendfile() from them
into a target that fails or takes less than was read drops what was
taken out in between, like for the rtas error log.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 samples/kfifo/bytestream-example.c |  9 +++------
 samples/kfifo/inttype-example.c    | 10 ++++------
 samples/kfifo/record-example.c     |  9 +++------
 3 files changed, 10 insertions(+), 18 deletions(-)

diff --git a/samples/kfifo/bytestream-example.c b/samples/kfifo/bytestream-example.c
index 8c58fb46a2bf..214e46a64cc1 100644
--- a/samples/kfifo/bytestream-example.c
+++ b/samples/kfifo/bytestream-example.c
@@ -125,26 +125,23 @@ static ssize_t fifo_write(struct file *file, char *buf,
 	return copied;
 }
 
-static ssize_t fifo_read(struct file *file, char __user *buf,
+static ssize_t fifo_read(struct file *file, char *buf,
 						size_t count, loff_t *ppos)
 {
-	int ret;
 	unsigned int copied;
 
 	if (mutex_lock_interruptible(&read_access))
 		return -ERESTARTSYS;
 
-	ret = kfifo_to_user(&test, buf, count, &copied);
+	copied = kfifo_out(&test, buf, count);
 
 	mutex_unlock(&read_access);
-	if (ret)
-		return ret;
 
 	return copied;
 }
 
 static const struct proc_ops fifo_proc_ops = {
-	.proc_read	= fifo_read,
+	.proc_read_buf	= fifo_read,
 	.proc_write_buf	= fifo_write,
 	.proc_lseek	= noop_llseek,
 };
diff --git a/samples/kfifo/inttype-example.c b/samples/kfifo/inttype-example.c
index c5d16b815e73..8384d7f8de6c 100644
--- a/samples/kfifo/inttype-example.c
+++ b/samples/kfifo/inttype-example.c
@@ -119,26 +119,24 @@ static ssize_t fifo_write(struct file *file, char *buf,
 	return copied;
 }
 
-static ssize_t fifo_read(struct file *file, char __user *buf,
+static ssize_t fifo_read(struct file *file, char *buf,
 						size_t count, loff_t *ppos)
 {
-	int ret;
 	unsigned int copied;
 
 	if (mutex_lock_interruptible(&read_access))
 		return -ERESTARTSYS;
 
-	ret = kfifo_to_user(&test, buf, count, &copied);
+	copied = kfifo_out(&test, (int *)buf, count / sizeof(int)) *
+		 sizeof(int);
 
 	mutex_unlock(&read_access);
-	if (ret)
-		return ret;
 
 	return copied;
 }
 
 static const struct proc_ops fifo_proc_ops = {
-	.proc_read	= fifo_read,
+	.proc_read_buf	= fifo_read,
 	.proc_write_buf	= fifo_write,
 	.proc_lseek	= noop_llseek,
 };
diff --git a/samples/kfifo/record-example.c b/samples/kfifo/record-example.c
index dcc749247c64..29d08e90a63d 100644
--- a/samples/kfifo/record-example.c
+++ b/samples/kfifo/record-example.c
@@ -132,26 +132,23 @@ static ssize_t fifo_write(struct file *file, char *buf,
 	return copied;
 }
 
-static ssize_t fifo_read(struct file *file, char __user *buf,
+static ssize_t fifo_read(struct file *file, char *buf,
 						size_t count, loff_t *ppos)
 {
-	int ret;
 	unsigned int copied;
 
 	if (mutex_lock_interruptible(&read_access))
 		return -ERESTARTSYS;
 
-	ret = kfifo_to_user(&test, buf, count, &copied);
+	copied = kfifo_out(&test, buf, count);
 
 	mutex_unlock(&read_access);
-	if (ret)
-		return ret;
 
 	return copied;
 }
 
 static const struct proc_ops fifo_proc_ops = {
-	.proc_read	= fifo_read,
+	.proc_read_buf	= fifo_read,
 	.proc_write_buf	= fifo_write,
 	.proc_lseek	= noop_llseek,
 	/* kfifo_from_user() cut a record to what its length byte holds */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 19/23] tty: sysrq: let procfs copy in writes to /proc/sysrq-trigger
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (17 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 18/23] wifi: ipw2x00: let procfs copy in writes to the debug level Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 20/23] ALSA: info: let procfs own the seq_file of text entries Christian Brauner
                     ` (3 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and handle the keys from that copy. Writes larger
than PAGE_SIZE - 1 bytes fail with -EINVAL now. They used to trigger the
first key, or all of them in bulk mode. procfs allocates the copy, which
is as small as the write, opening the file allocated as much already.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 drivers/tty/sysrq.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index f4571d05b496..99363e1b59ed 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -1200,17 +1200,14 @@ EXPORT_SYMBOL(unregister_sysrq_key);
  * However, if the first character is an underscore,
  * all characters are processed.
  */
-static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
+static ssize_t write_sysrq_trigger(struct file *file, char *buf,
 				   size_t count, loff_t *ppos)
 {
 	bool bulk = false;
 	size_t i;
 
 	for (i = 0; i < count; i++) {
-		char c;
-
-		if (get_user(c, buf + i))
-			return -EFAULT;
+		char c = buf[i];
 
 		if (c == '_')
 			bulk = true;
@@ -1225,7 +1222,7 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
 }
 
 static const struct proc_ops sysrq_trigger_proc_ops = {
-	.proc_write	= write_sysrq_trigger,
+	.proc_write_buf	= write_sysrq_trigger,
 	.proc_lseek	= noop_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 16/27] sched/psi: let procfs own the seq_file of the pressure files
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (14 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 15/27] profiling: mark /proc/profile permanent Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 17/27] sched/psi: let procfs copy in trigger writes Christian Brauner
                     ` (10 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show functions and let procfs open and free the seq_file of
/proc/pressure/*. That makes the open functions unnecessary and
->proc_release() only destroys the trigger now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/sched/psi.c | 38 +++++---------------------------------
 1 file changed, 5 insertions(+), 33 deletions(-)

diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index 7c5381423334..bcb8bf4e0b69 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1535,21 +1535,6 @@ static int psi_cpu_show(struct seq_file *m, void *v)
 	return psi_show(m, &psi_system, PSI_CPU);
 }
 
-static int psi_io_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, psi_io_show, NULL);
-}
-
-static int psi_memory_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, psi_memory_show, NULL);
-}
-
-static int psi_cpu_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, psi_cpu_show, NULL);
-}
-
 static ssize_t psi_write(struct file *file, const char __user *user_buf,
 			 size_t nbytes, enum psi_res res)
 {
@@ -1635,31 +1620,25 @@ static int psi_fop_release(struct inode *inode, struct file *file)
 	struct seq_file *seq = file->private_data;
 
 	psi_trigger_destroy(seq->private);
-	return single_release(inode, file);
+	return 0;
 }
 
 static const struct proc_ops psi_io_proc_ops = {
-	.proc_open	= psi_io_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= psi_io_show,
 	.proc_write	= psi_io_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
 
 static const struct proc_ops psi_memory_proc_ops = {
-	.proc_open	= psi_memory_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= psi_memory_show,
 	.proc_write	= psi_memory_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
 
 static const struct proc_ops psi_cpu_proc_ops = {
-	.proc_open	= psi_cpu_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= psi_cpu_show,
 	.proc_write	= psi_cpu_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
@@ -1671,11 +1650,6 @@ static int psi_irq_show(struct seq_file *m, void *v)
 	return psi_show(m, &psi_system, PSI_IRQ);
 }
 
-static int psi_irq_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, psi_irq_show, NULL);
-}
-
 static ssize_t psi_irq_write(struct file *file, const char __user *user_buf,
 			     size_t nbytes, loff_t *ppos)
 {
@@ -1683,9 +1657,7 @@ static ssize_t psi_irq_write(struct file *file, const char __user *user_buf,
 }
 
 static const struct proc_ops psi_irq_proc_ops = {
-	.proc_open	= psi_irq_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
+	.proc_show	= psi_irq_show,
 	.proc_write	= psi_irq_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 20/23] ALSA: info: let procfs own the seq_file of text entries
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (18 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 19/23] tty: sysrq: let procfs copy in writes to /proc/sysrq-trigger Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 21/23] ALSA: info: let procfs copy in writes Christian Brauner
                     ` (2 subsequent siblings)
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the show function and let procfs open and free the seq_file.
->proc_open() only sets up the private data and preallocates the buffer
now and ->proc_release() no longer releases the seq_file. Reading an
entry then no longer holds up its removal when the card goes away.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 sound/core/info.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/sound/core/info.c b/sound/core/info.c
index 982087bc8575..8f905f398a0d 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -346,9 +346,11 @@ static int snd_info_seq_show(struct seq_file *seq, void *p)
 	return 0;
 }
 
+/* procfs opens the seq_file */
 static int snd_info_text_entry_open(struct inode *inode, struct file *file)
 {
 	struct snd_info_entry *entry = pde_data(inode);
+	struct seq_file *m = file->private_data;
 	struct snd_info_private_data *data;
 	int err;
 
@@ -362,13 +364,12 @@ static int snd_info_text_entry_open(struct inode *inode, struct file *file)
 		err = -ENOMEM;
 		goto error;
 	}
-	if (entry->size)
-		err = single_open_size(file, snd_info_seq_show, data,
-				       entry->size);
-	else
-		err = single_open(file, snd_info_seq_show, data);
-	if (err < 0)
-		goto error;
+	if (entry->size) {
+		err = seq_preallocate(m, entry->size);
+		if (err < 0)
+			goto error;
+	}
+	m->private = data;
 	return 0;
 
  error:
@@ -387,7 +388,6 @@ static int snd_info_text_entry_release(struct inode *inode, struct file *file)
 	if (data->wbuffer && entry->c.text.write)
 		entry->c.text.write(entry, data->wbuffer);
 
-	single_release(inode, file);
 	kfree(data->rbuffer);
 	if (data->wbuffer) {
 		kvfree(data->wbuffer->buffer);
@@ -401,11 +401,10 @@ static int snd_info_text_entry_release(struct inode *inode, struct file *file)
 
 static const struct proc_ops snd_info_text_entry_ops =
 {
+	.proc_show	= snd_info_seq_show,
 	.proc_open	= snd_info_text_entry_open,
 	.proc_release	= snd_info_text_entry_release,
 	.proc_write	= snd_info_text_entry_write,
-	.proc_lseek	= seq_lseek,
-	.proc_read	= seq_read,
 };
 
 static struct snd_info_entry *create_subdir(struct module *mod,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 17/27] sched/psi: let procfs copy in trigger writes
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (15 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 16/27] sched/psi: let procfs own the seq_file of the pressure files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 18/27] sched/psi: mark the pressure files permanent Christian Brauner
                     ` (9 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for the files in /proc/pressure so procfs
copies in what is written before it pins the entry, and take the
trigger from that copy. Only the first 31 bytes were ever looked at.
Writes larger than PAGE_SIZE - 1 bytes fail with -EINVAL now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/sched/psi.c | 40 +++++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index bcb8bf4e0b69..3132105bf382 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1535,8 +1535,8 @@ static int psi_cpu_show(struct seq_file *m, void *v)
 	return psi_show(m, &psi_system, PSI_CPU);
 }
 
-static ssize_t psi_write(struct file *file, const char __user *user_buf,
-			 size_t nbytes, enum psi_res res)
+static ssize_t psi_write(struct file *file, const char *kbuf, size_t nbytes,
+			 enum psi_res res)
 {
 	char buf[32];
 	size_t buf_size;
@@ -1552,9 +1552,7 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
 		return -EINVAL;
 
 	buf_size = min(nbytes, sizeof(buf));
-	if (copy_from_user(buf, user_buf, buf_size))
-		return -EFAULT;
-
+	memcpy(buf, kbuf, buf_size);
 	buf[buf_size - 1] = '\0';
 
 	seq = file->private_data;
@@ -1590,22 +1588,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
 	return nbytes;
 }
 
-static ssize_t psi_io_write(struct file *file, const char __user *user_buf,
-			    size_t nbytes, loff_t *ppos)
+static ssize_t psi_io_write(struct file *file, char *buf, size_t nbytes,
+			    loff_t *ppos)
 {
-	return psi_write(file, user_buf, nbytes, PSI_IO);
+	return psi_write(file, buf, nbytes, PSI_IO);
 }
 
-static ssize_t psi_memory_write(struct file *file, const char __user *user_buf,
-				size_t nbytes, loff_t *ppos)
+static ssize_t psi_memory_write(struct file *file, char *buf, size_t nbytes,
+				loff_t *ppos)
 {
-	return psi_write(file, user_buf, nbytes, PSI_MEM);
+	return psi_write(file, buf, nbytes, PSI_MEM);
 }
 
-static ssize_t psi_cpu_write(struct file *file, const char __user *user_buf,
-			     size_t nbytes, loff_t *ppos)
+static ssize_t psi_cpu_write(struct file *file, char *buf, size_t nbytes,
+			     loff_t *ppos)
 {
-	return psi_write(file, user_buf, nbytes, PSI_CPU);
+	return psi_write(file, buf, nbytes, PSI_CPU);
 }
 
 static __poll_t psi_fop_poll(struct file *file, poll_table *wait)
@@ -1625,21 +1623,21 @@ static int psi_fop_release(struct inode *inode, struct file *file)
 
 static const struct proc_ops psi_io_proc_ops = {
 	.proc_show	= psi_io_show,
-	.proc_write	= psi_io_write,
+	.proc_write_buf	= psi_io_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
 
 static const struct proc_ops psi_memory_proc_ops = {
 	.proc_show	= psi_memory_show,
-	.proc_write	= psi_memory_write,
+	.proc_write_buf	= psi_memory_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
 
 static const struct proc_ops psi_cpu_proc_ops = {
 	.proc_show	= psi_cpu_show,
-	.proc_write	= psi_cpu_write,
+	.proc_write_buf	= psi_cpu_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
@@ -1650,15 +1648,15 @@ static int psi_irq_show(struct seq_file *m, void *v)
 	return psi_show(m, &psi_system, PSI_IRQ);
 }
 
-static ssize_t psi_irq_write(struct file *file, const char __user *user_buf,
-			     size_t nbytes, loff_t *ppos)
+static ssize_t psi_irq_write(struct file *file, char *buf, size_t nbytes,
+			     loff_t *ppos)
 {
-	return psi_write(file, user_buf, nbytes, PSI_IRQ);
+	return psi_write(file, buf, nbytes, PSI_IRQ);
 }
 
 static const struct proc_ops psi_irq_proc_ops = {
 	.proc_show	= psi_irq_show,
-	.proc_write	= psi_irq_write,
+	.proc_write_buf	= psi_irq_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 21/23] ALSA: info: let procfs copy in writes
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (19 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 20/23] ALSA: info: let procfs own the seq_file of text entries Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 22/23] ALSA: info: let procfs copy out reads of data entries Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 23/23] ALSA: info: drop the ioctl hook " Christian Brauner
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_write_buf() for data and text entries so procfs copies
in what is written before it pins the entry.

Data entries take writes a page at a time with PROC_ENTRY_SHORT_WRITES.
They used to get the whole write at once. ->write() of struct
snd_info_entry_ops gets the kernel copy and opl4, its only user, writes
it to the sample memory without bouncing it through a vmalloc() buffer
of its own.

Text entries take up to SND_INFO_TEXT_WRITE_MAX bytes at once like
before. A single write that is larger fails with -EINVAL from procfs
instead of -EIO now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 include/sound/info.h           |  2 +-
 sound/core/info.c              | 21 ++++++++++++---------
 sound/drivers/opl4/opl4_proc.c | 11 +----------
 3 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/include/sound/info.h b/include/sound/info.h
index adbc506860d6..32fc608d056c 100644
--- a/include/sound/info.h
+++ b/include/sound/info.h
@@ -42,7 +42,7 @@ struct snd_info_entry_ops {
 			struct file *file, char __user *buf,
 			size_t count, loff_t pos);
 	ssize_t (*write)(struct snd_info_entry *entry, void *file_private_data,
-			 struct file *file, const char __user *buf,
+			 struct file *file, const char *buf,
 			 size_t count, loff_t pos);
 	loff_t (*llseek)(struct snd_info_entry *entry,
 			 void *file_private_data, struct file *file,
diff --git a/sound/core/info.c b/sound/core/info.c
index 8f905f398a0d..09d4420202e5 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -160,7 +160,7 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
 	return size;
 }
 
-static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
+static ssize_t snd_info_entry_write(struct file *file, char *buffer,
 				    size_t count, loff_t * offset)
 {
 	struct snd_info_private_data *data = file->private_data;
@@ -277,9 +277,11 @@ static int snd_info_entry_release(struct inode *inode, struct file *file)
 
 static const struct proc_ops snd_info_entry_operations =
 {
+	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_lseek	= snd_info_entry_llseek,
 	.proc_read	= snd_info_entry_read,
-	.proc_write	= snd_info_entry_write,
+	.proc_write_buf	= snd_info_entry_write,
+	.proc_write_size = PAGE_SIZE,
 	.proc_poll	= snd_info_entry_poll,
 	.proc_ioctl	= snd_info_entry_ioctl,
 	.proc_mmap	= snd_info_entry_mmap,
@@ -287,11 +289,13 @@ static const struct proc_ops snd_info_entry_operations =
 	.proc_release	= snd_info_entry_release,
 };
 
+/* don't handle too large text inputs */
+#define SND_INFO_TEXT_WRITE_MAX	(16 * 1024)
+
 /*
  * file ops for text proc files
  */
-static ssize_t snd_info_text_entry_write(struct file *file,
-					 const char __user *buffer,
+static ssize_t snd_info_text_entry_write(struct file *file, char *buffer,
 					 size_t count, loff_t *offset)
 {
 	struct seq_file *m = file->private_data;
@@ -307,8 +311,7 @@ static ssize_t snd_info_text_entry_write(struct file *file,
 	if (!valid_pos(pos, count))
 		return -EIO;
 	next = pos + count;
-	/* don't handle too large text inputs */
-	if (next > 16 * 1024)
+	if (next > SND_INFO_TEXT_WRITE_MAX)
 		return -EIO;
 	guard(mutex)(&entry->access);
 	buf = data->wbuffer;
@@ -325,8 +328,7 @@ static ssize_t snd_info_text_entry_write(struct file *file,
 		buf->buffer = nbuf;
 		buf->len = PAGE_ALIGN(next);
 	}
-	if (copy_from_user(buf->buffer + pos, buffer, count))
-		return -EFAULT;
+	memcpy(buf->buffer + pos, buffer, count);
 	buf->size = next;
 	*offset = next;
 	return count;
@@ -404,7 +406,8 @@ static const struct proc_ops snd_info_text_entry_ops =
 	.proc_show	= snd_info_seq_show,
 	.proc_open	= snd_info_text_entry_open,
 	.proc_release	= snd_info_text_entry_release,
-	.proc_write	= snd_info_text_entry_write,
+	.proc_write_buf	= snd_info_text_entry_write,
+	.proc_write_size = SND_INFO_TEXT_WRITE_MAX,
 };
 
 static struct snd_info_entry *create_subdir(struct module *mod,
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c
index fd0ba4704d9f..bdcae02a4dc2 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -54,21 +54,12 @@ static ssize_t snd_opl4_mem_proc_read(struct snd_info_entry *entry,
 static ssize_t snd_opl4_mem_proc_write(struct snd_info_entry *entry,
 				       void *file_private_data,
 				       struct file *file,
-				       const char __user *_buf,
+				       const char *buf,
 				       size_t count, loff_t pos)
 {
 	struct snd_opl4 *opl4 = entry->private_data;
-	char *buf;
 
-	buf = vmalloc(count);
-	if (!buf)
-		return -ENOMEM;
-	if (copy_from_user(buf, _buf, count)) {
-		vfree(buf);
-		return -EFAULT;
-	}
 	snd_opl4_write_memory(opl4, buf, pos, count);
-	vfree(buf);
 	return count;
 }
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 18/27] sched/psi: mark the pressure files permanent
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (16 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 17/27] sched/psi: let procfs copy in trigger writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 19/27] kallsyms: let procfs own the seq_file of /proc/kallsyms Christian Brauner
                     ` (8 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

/proc/pressure/* are created when psi initializes and never removed.
Say so with PROC_ENTRY_PERMANENT, procfs then calls into them without
pinning them, like it does for /proc/profile.

That also takes care of a rule the files bent. psi_write() takes the
seq_file's lock to serialize the triggers. A reader holds that lock
while it copies to userspace, and now that procfs owns the seq_file it
does that with the entry unpinned. So nothing that runs with the entry
pinned may wait for that lock, and psi_write() ran pinned. Nothing ever
waited for it since the files are never removed, but with the files
permanent nothing runs pinned at all.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/sched/psi.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index 3132105bf382..9811c3b705eb 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1622,6 +1622,7 @@ static int psi_fop_release(struct inode *inode, struct file *file)
 }
 
 static const struct proc_ops psi_io_proc_ops = {
+	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_io_show,
 	.proc_write_buf	= psi_io_write,
 	.proc_poll	= psi_fop_poll,
@@ -1629,6 +1630,7 @@ static const struct proc_ops psi_io_proc_ops = {
 };
 
 static const struct proc_ops psi_memory_proc_ops = {
+	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_memory_show,
 	.proc_write_buf	= psi_memory_write,
 	.proc_poll	= psi_fop_poll,
@@ -1636,6 +1638,7 @@ static const struct proc_ops psi_memory_proc_ops = {
 };
 
 static const struct proc_ops psi_cpu_proc_ops = {
+	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_cpu_show,
 	.proc_write_buf	= psi_cpu_write,
 	.proc_poll	= psi_fop_poll,
@@ -1655,6 +1658,7 @@ static ssize_t psi_irq_write(struct file *file, char *buf, size_t nbytes,
 }
 
 static const struct proc_ops psi_irq_proc_ops = {
+	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_irq_show,
 	.proc_write_buf	= psi_irq_write,
 	.proc_poll	= psi_fop_poll,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 22/23] ALSA: info: let procfs copy out reads of data entries
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (20 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 21/23] ALSA: info: let procfs copy in writes Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 23/23] ALSA: info: drop the ioctl hook " Christian Brauner
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() for data entries so procfs copies what is
read to userspace after it unpinned the entry. ->read() of struct
snd_info_entry_ops fills procfs' buffer now. opl4 reads the sample
memory into it without a vmalloc() buffer of its own, gus, cs46xx,
emu10k1, scarlett2, cs4281 and mixart copy into it instead of to
userspace. The driver guide still told raw callbacks to copy to and from
userspace, so update its example as well. Data entries ask for
PROC_ENTRY_FULL_READS so that reading all of an entry at once keeps
working. A read that faults on its buffer in a later piece returns the
pieces before it now instead of failing with -EFAULT.

Unbinding a card then no longer waits for a reader that faults on its
buffer.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 Documentation/sound/kernel-api/writing-an-alsa-driver.rst | 10 +++++-----
 include/sound/gus.h                                       |  2 +-
 include/sound/info.h                                      |  2 +-
 sound/core/info.c                                         |  6 +++---
 sound/drivers/opl4/opl4_proc.c                            | 12 +-----------
 sound/isa/gus/gus_dram.c                                  |  7 +++----
 sound/isa/gus/gus_mem_proc.c                              |  2 +-
 sound/pci/cs4281.c                                        | 10 ++++------
 sound/pci/cs46xx/cs46xx_lib.c                             |  5 ++---
 sound/pci/emu10k1/emuproc.c                               |  8 +++-----
 sound/pci/mixart/mixart.c                                 | 10 ++++------
 sound/usb/mixer_scarlett2.c                               |  9 ++++-----
 12 files changed, 32 insertions(+), 51 deletions(-)

diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
index 86b64de03140..d4b4753879b6 100644
--- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
+++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
@@ -3679,9 +3679,10 @@ For raw data, ``size`` field must be set properly. This specifies
 the maximum size of the proc file access.
 
 The read/write callbacks of raw mode are more direct than the text mode.
-You need to use a low-level I/O functions such as
-:c:func:`copy_from_user()` and :c:func:`copy_to_user()` to transfer the
-data::
+They get a kernel buffer that procfs copies to or from userspace, so they
+just copy the data. The buffer holds at most a page and isn't zeroed, so
+a read callback must return exactly what it copied into it; a larger
+read comes back for the rest::
 
   static ssize_t my_file_io_read(struct snd_info_entry *entry,
                               void *file_private_data,
@@ -3690,8 +3691,7 @@ data::
                               size_t count,
                               loff_t pos)
   {
-          if (copy_to_user(buf, local_data + pos, count))
-                  return -EFAULT;
+          memcpy(buf, local_data + pos, count);
           return count;
   }
 
diff --git a/include/sound/gus.h b/include/sound/gus.h
index 3feb42627de1..1b504f516ebf 100644
--- a/include/sound/gus.h
+++ b/include/sound/gus.h
@@ -594,7 +594,7 @@ void snd_gf1_uart_resume(struct snd_gus_card *gus);
 /* gus_dram.c */
 int snd_gus_dram_write(struct snd_gus_card *gus, char __user *ptr,
 		       unsigned int addr, unsigned int size);
-int snd_gus_dram_read(struct snd_gus_card *gus, char __user *ptr,
+int snd_gus_dram_read(struct snd_gus_card *gus, char *ptr,
 		      unsigned int addr, unsigned int size, int rom);
 
 /* gus_timer.c */
diff --git a/include/sound/info.h b/include/sound/info.h
index 32fc608d056c..b4b5bdd6898d 100644
--- a/include/sound/info.h
+++ b/include/sound/info.h
@@ -39,7 +39,7 @@ struct snd_info_entry_ops {
 	int (*release)(struct snd_info_entry *entry,
 		       unsigned short mode, void *file_private_data);
 	ssize_t (*read)(struct snd_info_entry *entry, void *file_private_data,
-			struct file *file, char __user *buf,
+			struct file *file, char *buf,
 			size_t count, loff_t pos);
 	ssize_t (*write)(struct snd_info_entry *entry, void *file_private_data,
 			 struct file *file, const char *buf,
diff --git a/sound/core/info.c b/sound/core/info.c
index 09d4420202e5..9665600ca5ac 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -138,7 +138,7 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
 	return offset;
 }
 
-static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
+static ssize_t snd_info_entry_read(struct file *file, char *buffer,
 				   size_t count, loff_t * offset)
 {
 	struct snd_info_private_data *data = file->private_data;
@@ -277,9 +277,9 @@ static int snd_info_entry_release(struct inode *inode, struct file *file)
 
 static const struct proc_ops snd_info_entry_operations =
 {
-	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
+	.proc_flags	= PROC_ENTRY_SHORT_WRITES | PROC_ENTRY_FULL_READS,
 	.proc_lseek	= snd_info_entry_llseek,
-	.proc_read	= snd_info_entry_read,
+	.proc_read_buf	= snd_info_entry_read,
 	.proc_write_buf	= snd_info_entry_write,
 	.proc_write_size = PAGE_SIZE,
 	.proc_poll	= snd_info_entry_poll,
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c
index bdcae02a4dc2..b8b282cb0a02 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -5,7 +5,6 @@
  */
 
 #include "opl4_local.h"
-#include <linux/vmalloc.h>
 #include <linux/export.h>
 #include <sound/info.h>
 
@@ -33,21 +32,12 @@ static int snd_opl4_mem_proc_release(struct snd_info_entry *entry,
 
 static ssize_t snd_opl4_mem_proc_read(struct snd_info_entry *entry,
 				      void *file_private_data,
-				      struct file *file, char __user *_buf,
+				      struct file *file, char *buf,
 				      size_t count, loff_t pos)
 {
 	struct snd_opl4 *opl4 = entry->private_data;
-	char* buf;
 
-	buf = vmalloc(count);
-	if (!buf)
-		return -ENOMEM;
 	snd_opl4_read_memory(opl4, buf, pos, count);
-	if (copy_to_user(_buf, buf, count)) {
-		vfree(buf);
-		return -EFAULT;
-	}
-	vfree(buf);
 	return count;
 }
 
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
index 50fe738ee3ea..98b94db8e819 100644
--- a/sound/isa/gus/gus_dram.c
+++ b/sound/isa/gus/gus_dram.c
@@ -45,7 +45,7 @@ int snd_gus_dram_write(struct snd_gus_card *gus, char __user *buffer,
 	return snd_gus_dram_poke(gus, buffer, address, size);
 }
 
-static int snd_gus_dram_peek(struct snd_gus_card *gus, char __user *_buffer,
+static int snd_gus_dram_peek(struct snd_gus_card *gus, char *_buffer,
 			     unsigned int address, unsigned int size,
 			     int rom)
 {
@@ -67,15 +67,14 @@ static int snd_gus_dram_peek(struct snd_gus_card *gus, char __user *_buffer,
 			while (size2--)
 				*pbuffer++ = snd_gf1_peek(gus, address++);
 		}
-		if (copy_to_user(_buffer, buffer, size1))
-			return -EFAULT;
+		memcpy(_buffer, buffer, size1);
 		size -= size1;
 		_buffer += size1;
 	}
 	return 0;
 }
 
-int snd_gus_dram_read(struct snd_gus_card *gus, char __user *buffer,
+int snd_gus_dram_read(struct snd_gus_card *gus, char *buffer,
 		      unsigned int address, unsigned int size,
 		      int rom)
 {
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
index 04c212882718..c939bfd5a966 100644
--- a/sound/isa/gus/gus_mem_proc.c
+++ b/sound/isa/gus/gus_mem_proc.c
@@ -18,7 +18,7 @@ struct gus_proc_private {
 
 static ssize_t snd_gf1_mem_proc_dump(struct snd_info_entry *entry,
 				     void *file_private_data,
-				     struct file *file, char __user *buf,
+				     struct file *file, char *buf,
 				     size_t count, loff_t pos)
 {
 	struct gus_proc_private *priv = entry->private_data;
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index b831f1d50750..1444e39fdbc1 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1104,25 +1104,23 @@ static void snd_cs4281_proc_read(struct snd_info_entry *entry,
 
 static ssize_t snd_cs4281_BA0_read(struct snd_info_entry *entry,
 				   void *file_private_data,
-				   struct file *file, char __user *buf,
+				   struct file *file, char *buf,
 				   size_t count, loff_t pos)
 {
 	struct cs4281 *chip = entry->private_data;
 	
-	if (copy_to_user_fromio(buf, chip->ba0 + pos, count))
-		return -EFAULT;
+	memcpy_fromio(buf, chip->ba0 + pos, count);
 	return count;
 }
 
 static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry,
 				   void *file_private_data,
-				   struct file *file, char __user *buf,
+				   struct file *file, char *buf,
 				   size_t count, loff_t pos)
 {
 	struct cs4281 *chip = entry->private_data;
 	
-	if (copy_to_user_fromio(buf, chip->ba1 + pos, count))
-		return -EFAULT;
+	memcpy_fromio(buf, chip->ba1 + pos, count);
 	return count;
 }
 
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 797a5f09cade..2a4910e5b497 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2741,13 +2741,12 @@ static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
 
 static ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry,
 				  void *file_private_data,
-				  struct file *file, char __user *buf,
+				  struct file *file, char *buf,
 				  size_t count, loff_t pos)
 {
 	struct snd_cs46xx_region *region = entry->private_data;
 	
-	if (copy_to_user_fromio(buf, region->remap_addr + pos, count))
-		return -EFAULT;
+	memcpy_fromio(buf, region->remap_addr + pos, count);
 	return count;
 }
 
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index f6186b5be049..acacce976415 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -382,7 +382,7 @@ static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
 
 static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
 				       void *file_private_data,
-				       struct file *file, char __user *buf,
+				       struct file *file, char *buf,
 				       size_t count, loff_t pos)
 {
 	struct snd_emu10k1 *emu = entry->private_data;
@@ -415,10 +415,8 @@ static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
 		}
 		tmp[idx] = val;
 	}
-	if (copy_to_user(buf, ((char *)tmp) + (pos & 3), count))
-		res = -EFAULT;
-	else
-		res = count;
+	memcpy(buf, ((char *)tmp) + (pos & 3), count);
+	res = count;
 	kfree(tmp);
 	return res;
 }
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index f451554cff5f..c44001f0d320 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -1110,14 +1110,13 @@ static int snd_mixart_free(struct mixart_mgr *mgr)
  */
 static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry,
 				   void *file_private_data,
-				   struct file *file, char __user *buf,
+				   struct file *file, char *buf,
 				   size_t count, loff_t pos)
 {
 	struct mixart_mgr *mgr = entry->private_data;
 
 	count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
-	if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count))
-		return -EFAULT;
+	memcpy_fromio(buf, MIXART_MEM(mgr, pos), count);
 	return count;
 }
 
@@ -1126,14 +1125,13 @@ static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry,
  */
 static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry,
 				   void *file_private_data,
-				   struct file *file, char __user *buf,
+				   struct file *file, char *buf,
 				   size_t count, loff_t pos)
 {
 	struct mixart_mgr *mgr = entry->private_data;
 
 	count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
-	if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count))
-		return -EFAULT;
+	memcpy_fromio(buf, MIXART_REG(mgr, pos), count);
 	return count;
 }
 
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index ed5fe746d438..48557235c389 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -9763,7 +9763,7 @@ static ssize_t scarlett2_devmap_read(
 	struct snd_info_entry *entry,
 	void                  *file_private_data,
 	struct file           *file,
-	char __user           *buf,
+	char                  *buf,
 	size_t                 count,
 	loff_t                 pos)
 {
@@ -9785,7 +9785,7 @@ static ssize_t scarlett2_devmap_read(
 	while (count > 0) {
 		/* SCARLETT2_USB_GET_DEVMAP reads only on block boundaries,
 		 * so we need to read a whole block and copy the requested
-		 * chunk to userspace.
+		 * chunk out of it.
 		 */
 
 		__le32 req;
@@ -9800,7 +9800,7 @@ static ssize_t scarlett2_devmap_read(
 					 block_size,
 					 entry->size - block_start);
 
-		/* size of the chunk to copy to userspace */
+		/* size of the chunk to copy out of the block */
 		size_t copy_size = min_t(size_t, count, read_size - offset);
 
 		/* request the block */
@@ -9810,8 +9810,7 @@ static ssize_t scarlett2_devmap_read(
 		if (err < 0)
 			return copied ? copied : err;
 
-		if (copy_to_user(buf, resp_buf + offset, copy_size))
-			return -EFAULT;
+		memcpy(buf, resp_buf + offset, copy_size);
 
 		buf += copy_size;
 		pos += copy_size;

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 19/27] kallsyms: let procfs own the seq_file of /proc/kallsyms
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (17 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 18/27] sched/psi: mark the pressure files permanent Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 20/27] module: procfs: let procfs own the seq_file of /proc/modules Christian Brauner
                     ` (7 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and the size of the iterator and let procfs
open and free the seq_file. ->proc_open() only initializes the iterator
now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/kallsyms.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index aec2f06858af..2d309fcee730 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -888,10 +888,9 @@ static int kallsyms_open(struct inode *inode, struct file *file)
 	 * s_start from where we left off, so we avoid doing
 	 * using get_symbol_offset for every symbol.
 	 */
-	struct kallsym_iter *iter;
-	iter = __seq_open_private(file, &kallsyms_op, sizeof(*iter));
-	if (!iter)
-		return -ENOMEM;
+	struct seq_file *m = file->private_data;
+	struct kallsym_iter *iter = m->private;
+
 	reset_iter(iter, 0);
 
 	/*
@@ -923,10 +922,9 @@ const char *kdb_walk_kallsyms(loff_t *pos)
 #endif	/* CONFIG_KGDB_KDB */
 
 static const struct proc_ops kallsyms_proc_ops = {
+	.proc_seq_ops	= &kallsyms_op,
+	.proc_state_size = sizeof(struct kallsym_iter),
 	.proc_open	= kallsyms_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release_private,
 };
 
 static int __init kallsyms_init(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 23/23] ALSA: info: drop the ioctl hook of data entries
  2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
                     ` (21 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 22/23] ALSA: info: let procfs copy out reads of data entries Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  22 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

No driver implements ->ioctl() of struct snd_info_entry_ops.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 include/sound/info.h |  2 --
 sound/core/info.c    | 13 -------------
 2 files changed, 15 deletions(-)

diff --git a/include/sound/info.h b/include/sound/info.h
index b4b5bdd6898d..f32e81ef0c8f 100644
--- a/include/sound/info.h
+++ b/include/sound/info.h
@@ -50,8 +50,6 @@ struct snd_info_entry_ops {
 	__poll_t (*poll)(struct snd_info_entry *entry,
 			     void *file_private_data, struct file *file,
 			     poll_table *wait);
-	int (*ioctl)(struct snd_info_entry *entry, void *file_private_data,
-		     struct file *file, unsigned int cmd, unsigned long arg);
 	int (*mmap)(struct snd_info_entry *entry, void *file_private_data,
 		    struct inode *inode, struct file *file,
 		    struct vm_area_struct *vma);
diff --git a/sound/core/info.c b/sound/core/info.c
index 9665600ca5ac..639554bb17c8 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -201,18 +201,6 @@ static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
 	return mask;
 }
 
-static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	struct snd_info_private_data *data = file->private_data;
-	struct snd_info_entry *entry = data->entry;
-
-	if (!entry->c.ops->ioctl)
-		return -ENOTTY;
-	return entry->c.ops->ioctl(entry, data->file_private_data,
-				   file, cmd, arg);
-}
-
 static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct inode *inode = file_inode(file);
@@ -283,7 +271,6 @@ static const struct proc_ops snd_info_entry_operations =
 	.proc_write_buf	= snd_info_entry_write,
 	.proc_write_size = PAGE_SIZE,
 	.proc_poll	= snd_info_entry_poll,
-	.proc_ioctl	= snd_info_entry_ioctl,
 	.proc_mmap	= snd_info_entry_mmap,
 	.proc_open	= snd_info_entry_open,
 	.proc_release	= snd_info_entry_release,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 20/27] module: procfs: let procfs own the seq_file of /proc/modules
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (18 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 19/27] kallsyms: let procfs own the seq_file of /proc/kallsyms Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 21/27] dyndbg: let procfs copy in writes to /proc/dynamic_debug/control Christian Brauner
                     ` (6 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and let procfs open and free the seq_file.
->proc_open() only decides whether to hide the addresses now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/module/procfs.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/kernel/module/procfs.c b/kernel/module/procfs.c
index 90712aa9dd13..a759bd916df7 100644
--- a/kernel/module/procfs.c
+++ b/kernel/module/procfs.c
@@ -117,31 +117,24 @@ static const struct seq_operations modules_op = {
 };
 
 /*
- * This also sets the "private" pointer to non-NULL if the
- * kernel pointers should be hidden (so you can just test
+ * procfs opens the seq_file. This sets the "private" pointer to non-NULL
+ * if the kernel pointers should be hidden (so you can just test
  * "m->private" to see if you should keep the values private).
  *
  * We use the same logic as for /proc/kallsyms.
  */
 static int modules_open(struct inode *inode, struct file *file)
 {
-	int err = seq_open(file, &modules_op);
+	struct seq_file *m = file->private_data;
 
-	if (!err) {
-		struct seq_file *m = file->private_data;
-
-		m->private = kallsyms_show_value(file->f_cred) ? NULL : (void *)8ul;
-	}
-
-	return err;
+	m->private = kallsyms_show_value(file->f_cred) ? NULL : (void *)8ul;
+	return 0;
 }
 
 static const struct proc_ops modules_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
+	.proc_seq_ops	= &modules_op,
 	.proc_open	= modules_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
 };
 
 static int __init proc_modules_init(void)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 21/27] dyndbg: let procfs copy in writes to /proc/dynamic_debug/control
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (19 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 20/27] module: procfs: let procfs own the seq_file of /proc/modules Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 22/27] ipc: let procfs own the seq_file of the sysvipc files Christian Brauner
                     ` (5 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Move the parsing into ddebug_proc_write_buf() and implement
->proc_write_buf() with it so procfs copies in what is written before
it pins the entry. The debugfs file keeps copying in itself. Writes
larger than USER_BUF_PAGE - 1 bytes to the procfs file now fail with
-EINVAL from procfs instead of -E2BIG.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 lib/dynamic_debug.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 64a211290872..1d6012c9eb41 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1002,11 +1002,28 @@ __setup("dyndbg=", dyndbg_setup);
  * command text from userspace, parses and executes it.
  */
 #define USER_BUF_PAGE 4096
+
+static ssize_t ddebug_proc_write_buf(struct file *file, char *buf, size_t len,
+				     loff_t *offp)
+{
+	int ret;
+
+	if (len == 0)
+		return 0;
+	v2pr_info("read %zu bytes from userspace\n", len);
+
+	ret = ddebug_exec_queries(buf, NULL);
+	if (ret < 0)
+		return ret;
+
+	*offp += len;
+	return len;
+}
+
 static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
 				  size_t len, loff_t *offp)
 {
-	char *tmpbuf;
-	int ret;
+	char *tmpbuf __free(kfree) = NULL;
 
 	if (len == 0)
 		return 0;
@@ -1017,15 +1034,7 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
 	tmpbuf = memdup_user_nul(ubuf, len);
 	if (IS_ERR(tmpbuf))
 		return PTR_ERR(tmpbuf);
-	v2pr_info("read %zu bytes from userspace\n", len);
-
-	ret = ddebug_exec_queries(tmpbuf, NULL);
-	kfree(tmpbuf);
-	if (ret < 0)
-		return ret;
-
-	*offp += len;
-	return len;
+	return ddebug_proc_write_buf(file, tmpbuf, len, offp);
 }
 
 /*
@@ -1196,7 +1205,8 @@ static const struct file_operations ddebug_proc_fops = {
 static const struct proc_ops proc_fops = {
 	.proc_seq_ops = &ddebug_proc_seqops,
 	.proc_state_size = sizeof(struct ddebug_iter),
-	.proc_write = ddebug_proc_write
+	.proc_write_buf = ddebug_proc_write_buf,
+	.proc_write_size = USER_BUF_PAGE - 1,
 };
 
 static void ddebug_attach_module_classes(struct ddebug_table *dt,

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 22/27] ipc: let procfs own the seq_file of the sysvipc files
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (20 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 21/27] dyndbg: let procfs copy in writes to /proc/dynamic_debug/control Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 23/27] alloc_tag: let procfs own the seq_file of /proc/allocinfo Christian Brauner
                     ` (4 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and the size of the iterator and let procfs
open and free the seq_file of /proc/sysvipc/*. ->proc_open() only fills
in the iterator now and ->proc_release() drops the references it holds.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 ipc/util.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/ipc/util.c b/ipc/util.c
index 1737d776bc08..8ec3bc328657 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -894,13 +894,11 @@ static const struct seq_operations sysvipc_proc_seqops = {
 	.show  = sysvipc_proc_show,
 };
 
+/* procfs opened the seq_file and allocated @iter */
 static int sysvipc_proc_open(struct inode *inode, struct file *file)
 {
-	struct ipc_proc_iter *iter;
-
-	iter = __seq_open_private(file, &sysvipc_proc_seqops, sizeof(*iter));
-	if (!iter)
-		return -ENOMEM;
+	struct seq_file *m = file->private_data;
+	struct ipc_proc_iter *iter = m->private;
 
 	iter->iface = pde_data(inode);
 	iter->ns    = get_ipc_ns(current->nsproxy->ipc_ns);
@@ -915,14 +913,14 @@ static int sysvipc_proc_release(struct inode *inode, struct file *file)
 	struct ipc_proc_iter *iter = seq->private;
 	put_ipc_ns(iter->ns);
 	put_pid_ns(iter->pid_ns);
-	return seq_release_private(inode, file);
+	return 0;
 }
 
 static const struct proc_ops sysvipc_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
+	.proc_seq_ops	= &sysvipc_proc_seqops,
+	.proc_state_size = sizeof(struct ipc_proc_iter),
 	.proc_open	= sysvipc_proc_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
 	.proc_release	= sysvipc_proc_release,
 };
 #endif /* CONFIG_PROC_FS */

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 23/27] alloc_tag: let procfs own the seq_file of /proc/allocinfo
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (21 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 22/27] ipc: let procfs own the seq_file of the sysvipc files Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 24/27] alloc_tag: let procfs copy the ioctl arguments " Christian Brauner
                     ` (3 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and the size of the private state and let
procfs open and free the seq_file. ->proc_open() and ->proc_release()
only set up and destroy the ioctl lock now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 mm/alloc_tag.c | 28 +++++++++++-----------------
 1 file changed, 11 insertions(+), 17 deletions(-)

diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
index 82e2c3448dcf..788c9d7f7151 100644
--- a/mm/alloc_tag.c
+++ b/mm/alloc_tag.c
@@ -151,27 +151,21 @@ static const struct seq_operations allocinfo_seq_op = {
 };
 
 /*
- * Initializes seq_file operations and allocates private state when opening
- * the /proc/allocinfo procfs entry.
+ * Initializes the private state when opening the /proc/allocinfo procfs
+ * entry. procfs opened the seq_file and allocated the state.
  */
 static int allocinfo_open(struct inode *inode, struct file *file)
 {
-	int ret;
-
-	ret = seq_open_private(file, &allocinfo_seq_op,
-			       sizeof(struct allocinfo_private));
-	if (!ret) {
-		struct seq_file *m = file->private_data;
-		struct allocinfo_private *priv = m->private;
+	struct seq_file *m = file->private_data;
+	struct allocinfo_private *priv = m->private;
 
-		mutex_init(&priv->ioctl_lock);
-	}
-	return ret;
+	mutex_init(&priv->ioctl_lock);
+	return 0;
 }
 
 /*
- * Cleans up the seq_file state and frees up the private state allocated in
- * allocinfo_open() when closing the /proc/allocinfo file descriptor.
+ * Undoes allocinfo_open() when closing the /proc/allocinfo file descriptor.
+ * procfs frees the seq_file and the private state.
  */
 static int allocinfo_release(struct inode *inode, struct file *file)
 {
@@ -179,7 +173,7 @@ static int allocinfo_release(struct inode *inode, struct file *file)
 	struct allocinfo_private *priv = m->private;
 
 	mutex_destroy(&priv->ioctl_lock);
-	return seq_release_private(inode, file);
+	return 0;
 }
 
 /*
@@ -474,9 +468,9 @@ static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
 #endif
 
 static const struct proc_ops allocinfo_proc_ops = {
+	.proc_seq_ops		= &allocinfo_seq_op,
+	.proc_state_size	= sizeof(struct allocinfo_private),
 	.proc_open		= allocinfo_open,
-	.proc_read_iter		= seq_read_iter,
-	.proc_lseek		= seq_lseek,
 	.proc_release		= allocinfo_release,
 	.proc_ioctl		= allocinfo_ioctl,
 #ifdef CONFIG_COMPAT

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 24/27] alloc_tag: let procfs copy the ioctl arguments of /proc/allocinfo
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (22 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 23/27] alloc_tag: let procfs own the seq_file of /proc/allocinfo Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 25/27] mm/swapfile: let procfs own the seq_file of /proc/swaps Christian Brauner
                     ` (2 subsequent siblings)
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_ioctl_buf() so procfs copies the arguments in before
it pins the entry and back out after unpinning it. The commands encode
their arguments. procfs converts the pointers of 32-bit tasks, so
allocinfo_compat_ioctl() goes away.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 mm/alloc_tag.c | 47 +++++++++++++++++------------------------------
 1 file changed, 17 insertions(+), 30 deletions(-)

diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
index 788c9d7f7151..1734705e2990 100644
--- a/mm/alloc_tag.c
+++ b/mm/alloc_tag.c
@@ -6,7 +6,6 @@
 #include <linux/kallsyms.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
-#include <linux/compat.h>
 #include <linux/page_ext.h>
 #include <linux/pgalloc_tag.h>
 #include <linux/proc_fs.h>
@@ -235,16 +234,14 @@ static void allocinfo_to_params(struct codetag *ct,
  * Retrieves the unique content ID representing the current allocation tag module
  * layout, allowing userspace to detect if modules were loaded / unloaded.
  */
-static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg)
+static int allocinfo_ioctl_get_content_id(struct seq_file *m, void *arg)
 {
 	struct allocinfo_content_id params;
 
 	codetag_lock_module_list(alloc_tag_cttype);
 	params.id = codetag_get_content_id(alloc_tag_cttype);
 	codetag_unlock_module_list(alloc_tag_cttype);
-	if (copy_to_user(arg, &params, sizeof(params)))
-		return -EFAULT;
-
+	memcpy(arg, &params, sizeof(params));
 	return 0;
 }
 
@@ -309,7 +306,7 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter,
  * Seeks the ioctl iterator to the specified 0-indexed tag position, reads its
  * profiling data and returns it to userspace.
  */
-static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
+static int allocinfo_ioctl_get_at(struct seq_file *m, void *arg)
 {
 	struct allocinfo_private *priv;
 	struct codetag *ct;
@@ -318,8 +315,7 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
 	struct alloc_tag_counters counters;
 	bool fetched_counters;
 
-	if (copy_from_user(&params, arg, sizeof(params)))
-		return -EFAULT;
+	memcpy(&params, arg, sizeof(params));
 
 	if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS)
 		return -EINVAL;
@@ -374,9 +370,7 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
 	if (!ct)
 		return -ENOENT;
 
-	if (copy_to_user(arg, &params, sizeof(params)))
-		return -EFAULT;
-
+	memcpy(arg, &params, sizeof(params));
 	return 0;
 }
 
@@ -384,7 +378,7 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
  * Advances the ioctl iterator to the next allocation tag in the sequence and
  * returns its profiling data to userspace.
  */
-static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
+static int allocinfo_ioctl_get_next(struct seq_file *m, void *arg)
 {
 	struct allocinfo_private *priv;
 	struct codetag *ct;
@@ -424,10 +418,8 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
 	codetag_unlock_module_list(alloc_tag_cttype);
 	mutex_unlock(&priv->ioctl_lock);
 
-	if (ret == 0) {
-		if (copy_to_user(arg, &params, sizeof(params)))
-			return -EFAULT;
-	}
+	if (ret == 0)
+		memcpy(arg, &params, sizeof(params));
 	return ret;
 }
 
@@ -435,10 +427,8 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg)
  * Entry point ioctl function for /proc/allocinfo routing requests to fetch the
  * layout content ID, seek to a specific tag, or read sequential tags.
  */
-static long allocinfo_ioctl(struct file *file, unsigned int cmd,
-			    unsigned long __arg)
+static long allocinfo_ioctl(struct file *file, unsigned int cmd, void *arg)
 {
-	void __user *arg = (void __user *)__arg;
 	int ret;
 
 	switch (cmd) {
@@ -459,23 +449,20 @@ static long allocinfo_ioctl(struct file *file, unsigned int cmd,
 	return ret;
 }
 
-#ifdef CONFIG_COMPAT
-static long allocinfo_compat_ioctl(struct file *file, unsigned int cmd,
-				   unsigned long arg)
-{
-	return allocinfo_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
+static const struct proc_ioctl_arg allocinfo_ioctl_args[] = {
+	PROC_IOCTL_IOC(ALLOCINFO_IOC_CONTENT_ID),
+	PROC_IOCTL_IOC(ALLOCINFO_IOC_GET_AT),
+	PROC_IOCTL_IOC(ALLOCINFO_IOC_GET_NEXT),
+};
 
 static const struct proc_ops allocinfo_proc_ops = {
 	.proc_seq_ops		= &allocinfo_seq_op,
 	.proc_state_size	= sizeof(struct allocinfo_private),
 	.proc_open		= allocinfo_open,
 	.proc_release		= allocinfo_release,
-	.proc_ioctl		= allocinfo_ioctl,
-#ifdef CONFIG_COMPAT
-	.proc_compat_ioctl	= allocinfo_compat_ioctl,
-#endif
+	.proc_ioctl_buf		= allocinfo_ioctl,
+	.proc_ioctl_args	= allocinfo_ioctl_args,
+	.proc_nr_ioctl_args	= ARRAY_SIZE(allocinfo_ioctl_args),
 };
 
 size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 25/27] mm/swapfile: let procfs own the seq_file of /proc/swaps
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (23 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 24/27] alloc_tag: let procfs copy the ioctl arguments " Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 26/27] kernel/configs: let procfs copy out reads of /proc/config.gz Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 27/27] selftests/proc: check that unloading configs doesn't wait for a reader Christian Brauner
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Declare the seq_operations and let procfs open and free the seq_file.
->proc_open() only records the event count for ->proc_poll() now.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 mm/swapfile.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/mm/swapfile.c b/mm/swapfile.c
index 254ce86fa923..ee4868ecc3a7 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -3410,26 +3410,19 @@ static const struct seq_operations swaps_op = {
 	.show =		swap_show
 };
 
+/* procfs opens the seq_file */
 static int swaps_open(struct inode *inode, struct file *file)
 {
-	struct seq_file *seq;
-	int ret;
-
-	ret = seq_open(file, &swaps_op);
-	if (ret)
-		return ret;
+	struct seq_file *seq = file->private_data;
 
-	seq = file->private_data;
 	seq->poll_event = atomic_read(&proc_poll_event);
 	return 0;
 }
 
 static const struct proc_ops swaps_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
+	.proc_seq_ops	= &swaps_op,
 	.proc_open	= swaps_open,
-	.proc_read	= seq_read,
-	.proc_lseek	= seq_lseek,
-	.proc_release	= seq_release,
 	.proc_poll	= swaps_poll,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 26/27] kernel/configs: let procfs copy out reads of /proc/config.gz
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (24 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 25/27] mm/swapfile: let procfs own the seq_file of /proc/swaps Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  2026-11-02 15:07   ` [PATCH RFC 27/27] selftests/proc: check that unloading configs doesn't wait for a reader Christian Brauner
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Implement ->proc_read_buf() so procfs copies what is read to userspace
after it unpinned the entry. Unloading configs.ko then no longer waits
for a reader that faults on its buffer. /proc/config.gz asks for
PROC_ENTRY_FULL_READS so that reading all of it at once keeps working.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 kernel/configs.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/kernel/configs.c b/kernel/configs.c
index a28c79c5f713..05b12c440168 100644
--- a/kernel/configs.c
+++ b/kernel/configs.c
@@ -38,17 +38,18 @@ extern char kernel_config_data;
 extern char kernel_config_data_end;
 
 static ssize_t
-ikconfig_read_current(struct file *file, char __user *buf,
+ikconfig_read_current(struct file *file, char *buf,
 		      size_t len, loff_t * offset)
 {
-	return simple_read_from_buffer(buf, len, offset,
+	return memory_read_from_buffer(buf, len, offset,
 				       &kernel_config_data,
 				       &kernel_config_data_end -
 				       &kernel_config_data);
 }
 
 static const struct proc_ops config_gz_proc_ops = {
-	.proc_read	= ikconfig_read_current,
+	.proc_flags	= PROC_ENTRY_FULL_READS,
+	.proc_read_buf	= ikconfig_read_current,
 	.proc_lseek	= default_llseek,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 27/27] selftests/proc: check that unloading configs doesn't wait for a reader
  2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
                     ` (25 preceding siblings ...)
  2026-11-02 15:07   ` [PATCH RFC 26/27] kernel/configs: let procfs copy out reads of /proc/config.gz Christian Brauner
@ 2026-11-02 15:07   ` Christian Brauner
  26 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:07 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Read /proc/config.gz into a buffer whose first page is backed by
userfaultfd and unload configs.ko while procfs copies what was read to
userspace:

- removing the entry doesn't wait for the reader

- the reader still gets the page that was read before the removal, and
  nothing more

- and the next read fails with -EIO

The test loads configs.ko if it isn't there, skips if configs is built
in or can't be unloaded, and loads it again afterwards. It needs
CONFIG_IKCONFIG=m. kselftest-merge keeps the =y that other fragments
ask for, so it skips there.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 tools/testing/selftests/proc/.gitignore            |   1 +
 tools/testing/selftests/proc/Makefile              |   1 +
 tools/testing/selftests/proc/config                |   4 +
 .../proc/proc-remove-slow-config-reader.c          | 157 +++++++++++++++++++++
 4 files changed, 163 insertions(+)

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 51969f81afc5..ed3f28652bb7 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -25,6 +25,7 @@
 /proc-remove-slow-reader
 /proc-remove-slow-readlink
 /proc-remove-slow-writer
+/proc-remove-slow-config-reader
 /read
 /self
 /setns-dcache
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index aecec6b93173..adbb4b1e9e9e 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -36,5 +36,6 @@ TEST_GEN_PROGS += proc-remove-slow-sysctl
 TEST_GEN_PROGS += proc-remove-slow-reader
 TEST_GEN_PROGS += proc-remove-slow-readlink
 TEST_GEN_PROGS += proc-remove-slow-writer
+TEST_GEN_PROGS += proc-remove-slow-config-reader
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/config b/tools/testing/selftests/proc/config
index c7c61a2d7927..8f672a8b49a8 100644
--- a/tools/testing/selftests/proc/config
+++ b/tools/testing/selftests/proc/config
@@ -8,3 +8,7 @@ CONFIG_SND_DUMMY=m
 CONFIG_SCSI=y
 CONFIG_SCSI_PROC_FS=y
 CONFIG_SCSI_DEBUG=m
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_IKCONFIG=m
+CONFIG_IKCONFIG_PROC=y
diff --git a/tools/testing/selftests/proc/proc-remove-slow-config-reader.c b/tools/testing/selftests/proc/proc-remove-slow-config-reader.c
new file mode 100644
index 000000000000..5f7bcb27b315
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-remove-slow-config-reader.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A read that faults while procfs copies it to userspace must not hold up
+ * removal of the /proc entry. Unloading configs.ko removes /proc/config.gz,
+ * which isn't a seq_file.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../kselftest_harness.h"
+
+#define CONFIG_PATH	"/proc/config.gz"
+#define CONFIG_MODULE	"/sys/module/configs"
+#define READ_PAGES	2
+#define TIMEOUT_MS	5000
+
+struct reader {
+	int fd;
+	char *buf;
+	size_t len;
+	ssize_t ret;
+};
+
+struct unloader {
+	int done;
+	int ret;
+};
+
+static void *read_thread(void *arg)
+{
+	struct reader *r = arg;
+
+	r->ret = read(r->fd, r->buf, r->len);
+	return NULL;
+}
+
+static void *unload_thread(void *arg)
+{
+	struct unloader *u = arg;
+	ssize_t ret;
+
+	u->ret = syscall(__NR_delete_module, "configs", O_NONBLOCK) ? -errno : 0;
+	ret = write(u->done, "x", 1);
+	return (void *)ret;
+}
+
+FIXTURE(configs) {
+	bool loaded;
+};
+
+FIXTURE_SETUP(configs)
+{
+	self->loaded = false;
+	if (access(CONFIG_PATH, R_OK) && system("modprobe -q configs"))
+		SKIP(return, "no " CONFIG_PATH);
+	if (access(CONFIG_PATH, R_OK))
+		SKIP(return, CONFIG_PATH ": %s", strerror(errno));
+	if (access(CONFIG_MODULE, F_OK))
+		SKIP(return, "configs isn't a module");
+	self->loaded = true;
+}
+
+FIXTURE_TEARDOWN(configs)
+{
+	/* an ASSERT may have fired with the module unloaded */
+	if (self->loaded && access(CONFIG_PATH, F_OK) &&
+	    system("modprobe -q configs"))
+		TH_LOG("couldn't load configs again");
+}
+
+TEST_F(configs, remove_while_reader_faults)
+{
+	struct uffdio_api api = { .api = UFFD_API };
+	struct uffdio_register reg = {};
+	struct uffdio_zeropage zero = {};
+	long psz = sysconf(_SC_PAGESIZE);
+	struct unloader u = {};
+	struct reader r = {};
+	struct uffd_msg msg;
+	struct pollfd pfd;
+	pthread_t rt, ut;
+	int uffd, done[2];
+	char *buf, *ref;
+	ssize_t ref_len;
+
+	r.fd = open(CONFIG_PATH, O_RDONLY | O_CLOEXEC);
+	ASSERT_GE(r.fd, 0);
+
+	/* what the first page of a read looks like */
+	ref = malloc(psz);
+	ASSERT_NE(ref, NULL);
+	ref_len = pread(r.fd, ref, psz, 0);
+	ASSERT_GT(ref_len, 0);
+
+	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (uffd < 0)
+		SKIP(return, "userfaultfd: %s", strerror(errno));
+	ASSERT_EQ(ioctl(uffd, UFFDIO_API, &api), 0);
+
+	/* Only the first page of the buffer goes through userfaultfd. */
+	buf = mmap(NULL, READ_PAGES * psz, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(buf, MAP_FAILED);
+	reg.range.start = (unsigned long)buf;
+	reg.range.len = psz;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_REGISTER, &reg), 0);
+
+	r.buf = buf;
+	r.len = READ_PAGES * psz;
+	ASSERT_EQ(pthread_create(&rt, NULL, read_thread, &r), 0);
+
+	/* The reader now sleeps in the copy to userspace. */
+	pfd = (struct pollfd){ .fd = uffd, .events = POLLIN };
+	ASSERT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1);
+	ASSERT_EQ(read(uffd, &msg, sizeof(msg)), sizeof(msg));
+	ASSERT_EQ(msg.event, UFFD_EVENT_PAGEFAULT);
+
+	/* Unloading configs.ko removes the file that is being read. */
+	ASSERT_EQ(pipe2(done, O_CLOEXEC), 0);
+	u.done = done[1];
+	ASSERT_EQ(pthread_create(&ut, NULL, unload_thread, &u), 0);
+	pfd = (struct pollfd){ .fd = done[0], .events = POLLIN };
+	EXPECT_EQ(poll(&pfd, 1, TIMEOUT_MS), 1) {
+		TH_LOG("removing " CONFIG_PATH " waited for the reader");
+	}
+
+	zero.range.start = (unsigned long)buf;
+	zero.range.len = psz;
+	ASSERT_EQ(ioctl(uffd, UFFDIO_ZEROPAGE, &zero), 0);
+	ASSERT_EQ(pthread_join(rt, NULL), 0);
+	ASSERT_EQ(pthread_join(ut, NULL), 0);
+	if (u.ret)
+		SKIP(return, "unloading configs: %s", strerror(-u.ret));
+
+	/*
+	 * The page that was read before the removal still arrives and the
+	 * next piece isn't filled anymore, then nothing.
+	 */
+	EXPECT_EQ(r.ret, ref_len);
+	EXPECT_EQ(memcmp(buf, ref, ref_len), 0);
+	EXPECT_EQ(read(r.fd, buf, psz), -1);
+	EXPECT_EQ(errno, EIO);
+	free(ref);
+}
+
+TEST_HARNESS_MAIN

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 27/38] sunrpc: make queue_wait a global wait queue again
  2026-11-02 14:21   ` [PATCH RFC 27/38] sunrpc: make queue_wait a global wait queue again Christian Brauner
@ 2026-11-02 15:12     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 15:12 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers, stable



On Mon, Nov 2, 2026, at 9:21 AM, Christian Brauner wrote:
> Since commit 552d0e17ea04 ("sunrpc: convert queue_wait from global to
> per-cache-detail waitqueue") poll() and epoll queue their waiters for a
> channel file on the queue_wait head in struct cache_detail. But the
> cache_detail can be freed while its channel file is still open and
> waited on.
>
> The procfs channel files of a network namespace are removed when the
> namespace goes away. Removing a procfs file waits for the callers that
> are in one of its methods right now and then calls ->release on every
> file that is still open (cf. close_pdeo()). The waiters that poll() and
> epoll left on the head stay queued until their file is really closed.
> So:
>
>   sunrpc_exit_net()
>   -> ip_map_cache_destroy()
>      -> cache_unregister_net()
>         -> remove_cache_proc_entries()
>            -> proc_remove()      // ->release, the waiters stay queued
>      -> cache_destroy_net()      // kfree(cd)
>
> and when poll() returns remove_wait_queue() writes to the freed
> cache_detail:
>
>   BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x4b/0xa0
>   Read of size 1 at addr ff110001131bb570 by task sunrpc-poll-uaf/242
>   Call Trace:
>    remove_wait_queue+0x22/0x1c0
>    poll_freewait+0xc2/0x230
>    do_sys_poll+0x9ba/0xd50
>    __x64_sys_poll+0x165/0x470
>   Freed by task 12:
>    kfree+0x267/0x620
>    sunrpc_exit_net+0x34/0x80
>    ops_undo_list+0x271/0x8d0
>    cleanup_net+0x3f3/0x860
>
> Closing an epoll instance that watches the file gets there through
> ep_unregister_pollwait() instead.
>
> The channel files of a network namespace belong to the root of its
> user namespace and cache_open() doesn't check for a capability. So an
> unprivileged user can open the channel file of a namespace of its own
> from the outside, poll it and let the namespace die.
>
> Go back to a single static wait queue that never goes away. A wakeup
> for one cache wakes the waiters of all caches again but cache_poll()
> reports per cache_detail so that's just a spurious wakeup. procfs
> should really own the wait queue of its files like kernfs does but
> that's nothing for stable.
>
> Reported-by: Chris Mason <mason@kernel.org>
> Fixes: 552d0e17ea04 ("sunrpc: convert queue_wait from global to 
> per-cache-detail waitqueue")
> Cc: stable@vger.kernel.org
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>


-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 0/5] proc: drop the __user methods
  2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
                   ` (5 preceding siblings ...)
  2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
@ 2026-11-02 15:15 ` Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 1/5] proc: drop ->proc_write() Christian Brauner
                     ` (4 more replies)
  6 siblings, 5 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:15 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

This is the last part of moving procfs to what kernfs does. Every entry
that can be removed reads, writes and handles ioctls through procfs'
buffers now, so its code only runs with the entry pinned and never
touches userspace memory.

Drop ->proc_write(), ->proc_ioctl() and ->proc_compat_ioctl(). Keep
->proc_read() and ->proc_read_iter() for the entries that are never
removed, kcore, kmsg, the kpage files and /proc/profile. procfs never
pins those, so nothing can wait for them. procfs refuses to register an
entry that can be removed and has one of them.

With the old names free the _buf suffix of the new methods doesn't say
anything anymore, so ->proc_write_buf(), ->proc_read_buf() and
->proc_ioctl_buf() become ->proc_write(), ->proc_read() and
->proc_ioctl() at the end, which is what kernfs calls its read and
write as well. That's one mechanical patch over all the converted
entries.

With the whole stack on a KASAN and lockdep kernel the removal
selftests of all parts pass, the proc, pktgen and allocinfo selftests
behave like without it and the permanent entries read like before.

This is on top of the four conversion series, "proc: convert the arch
and platform entries", "proc: convert the driver entries", "proc:
convert the fs, kernel and mm entries" and "proc: convert the net
entries and the kfifo samples".

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Christian Brauner (5):
      proc: drop ->proc_write()
      proc: drop ->proc_ioctl() and ->proc_compat_ioctl()
      proc: only let permanent entries read straight into userspace
      proc: rename ->proc_read() and ->proc_read_iter()
      proc: drop the _buf suffix from the new methods

 arch/alpha/kernel/srm_env.c                        |   2 +-
 arch/arm/kernel/atags_proc.c                       |   2 +-
 arch/arm/mm/alignment.c                            |   2 +-
 arch/m68k/kernel/bootinfo_proc.c                   |   2 +-
 arch/powerpc/kernel/proc_powerpc.c                 |   2 +-
 arch/powerpc/kernel/rtas-proc.c                    |  10 +-
 arch/powerpc/kernel/rtas_flash.c                   |  16 ++--
 arch/powerpc/kernel/rtasd.c                        |   2 +-
 arch/powerpc/platforms/pseries/lpar.c              |   4 +-
 arch/powerpc/platforms/pseries/lparcfg.c           |   2 +-
 arch/powerpc/platforms/pseries/reconfig.c          |   2 +-
 arch/sh/mm/alignment.c                             |   2 +-
 arch/sparc/kernel/led.c                            |   2 +-
 arch/um/drivers/mconsole_kern.c                    |   2 +-
 arch/um/kernel/exitcode.c                          |   2 +-
 arch/x86/kernel/cpu/mtrr/if.c                      |   4 +-
 arch/xtensa/platforms/iss/simdisk.c                |   4 +-
 drivers/acpi/proc.c                                |   2 +-
 drivers/hwmon/dell-smm-hwmon.c                     |   2 +-
 drivers/macintosh/via-pmu.c                        |   2 +-
 drivers/net/wireless/intel/ipw2x00/libipw_module.c |   2 +-
 drivers/pci/proc.c                                 |   6 +-
 drivers/platform/x86/lenovo/thinkpad_acpi.c        |   2 +-
 drivers/platform/x86/toshiba_acpi.c                |   8 +-
 drivers/pnp/isapnp/proc.c                          |   2 +-
 drivers/pnp/pnpbios/proc.c                         |   2 +-
 drivers/s390/block/dasd_proc.c                     |   2 +-
 drivers/s390/cio/blacklist.c                       |   2 +-
 drivers/s390/cio/css.c                             |   2 +-
 drivers/scsi/esas2r/esas2r_main.c                  |   2 +-
 drivers/scsi/scsi_devinfo.c                        |   2 +-
 drivers/scsi/scsi_proc.c                           |   4 +-
 drivers/scsi/sg.c                                  |   4 +-
 drivers/tty/sysrq.c                                |   2 +-
 drivers/usb/gadget/function/rndis.c                |   2 +-
 drivers/video/fbdev/via/viafbdev.c                 |  14 +--
 drivers/zorro/proc.c                               |   2 +-
 fs/jfs/jfs_debug.c                                 |   2 +-
 fs/lockd/procfs.c                                  |   4 +-
 fs/ntfs3/super.c                                   |   2 +-
 fs/proc/generic.c                                  |  33 ++++---
 fs/proc/inode.c                                    | 103 ++++++---------------
 fs/proc/internal.h                                 |  17 ++--
 fs/proc/kcore.c                                    |   2 +-
 fs/proc/kmsg.c                                     |   2 +-
 fs/proc/page.c                                     |   6 +-
 fs/proc/proc_net.c                                 |   4 +-
 fs/proc/vmcore.c                                   |   2 +-
 fs/smb/client/cifs_debug.c                         |  16 ++--
 fs/smb/client/dfs_cache.c                          |   2 +-
 include/linux/proc_fs.h                            |  39 ++++----
 kernel/configs.c                                   |   2 +-
 kernel/irq/proc.c                                  |   6 +-
 kernel/latencytop.c                                |   2 +-
 kernel/locking/lockdep_proc.c                      |   2 +-
 kernel/profile.c                                   |   4 +-
 kernel/sched/psi.c                                 |   8 +-
 lib/dynamic_debug.c                                |   2 +-
 mm/alloc_tag.c                                     |   2 +-
 net/atm/proc.c                                     |   2 +-
 net/core/pktgen.c                                  |   6 +-
 net/netfilter/xt_recent.c                          |   2 +-
 net/sunrpc/auth_gss/svcauth_gss.c                  |   6 +-
 net/sunrpc/cache.c                                 |   8 +-
 samples/kfifo/bytestream-example.c                 |   4 +-
 samples/kfifo/inttype-example.c                    |   4 +-
 samples/kfifo/record-example.c                     |   4 +-
 sound/core/info.c                                  |   6 +-
 68 files changed, 195 insertions(+), 237 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261030-work-procfs-cleanup-1694f71bb156
prerequisite-change-id: 20261101-work-procfs-arch-8d4ade48be31:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c
prerequisite-patch-id: 504e13cb7af5da6f3c8503f9336116ddd6ee9fc0
prerequisite-patch-id: 079f5420c7c6660679b25a93ab2912b42af19d6d
prerequisite-patch-id: 717bfde0d4141e875562d62b0e3c22d687517462
prerequisite-patch-id: 3e465d93cacbb875c5508b106bcaae53665de29e
prerequisite-patch-id: da2eff4c67005d05b39c39d6d164b94c2837a4dd
prerequisite-patch-id: a293472d78b43026dacbfe22a34ba1f8f2626e63
prerequisite-patch-id: 56469933009bbfd5a57d843f698f91bb0cdd846a
prerequisite-patch-id: 16433e5bde53510f9e6b7e04db2279bd0a8f63f5
prerequisite-patch-id: 03d349761a5a56d223067b051b1a38d594006ae2
prerequisite-patch-id: 9112aaf35b0ab26bf396285fbfbb51b81901d80b
prerequisite-patch-id: 901a0c1b4c2da010722471826fbfc51402ab8573
prerequisite-patch-id: 1204b4883154dea8cb7d93efadb9f49e70ecd7a8
prerequisite-patch-id: d2762d5dd1254bf7cf89a16b317b7b421718935f
prerequisite-patch-id: d1337f7a08fd4758a94bc5b2faeb30cbad1fe9cf
prerequisite-patch-id: a1d46785e75f0ec87cd12807cb57adc869a7f6a8
prerequisite-patch-id: 44c5d2365c5237b44a0162be9e73dcb392deab2c
prerequisite-patch-id: ad7ca39e7946dbf9e9ed4677b577bb5efad15af5
prerequisite-patch-id: 3984bbb1dbd74d87215a789483a75e968e81b453
prerequisite-patch-id: 0db54371960496caafd93db8801125a92f43d6c6
prerequisite-patch-id: 4d97d31af7bca868bf68a61fe7825dcbd7cc500b
prerequisite-patch-id: 227908b78f668bc6301cfa2217fa581471176b0f
prerequisite-patch-id: d4fba49d9b4aaee2cc42d91651041faa4b1a4d38
prerequisite-change-id: 20261101-work-procfs-drivers-58cd83649658:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c
prerequisite-patch-id: 504e13cb7af5da6f3c8503f9336116ddd6ee9fc0
prerequisite-patch-id: 079f5420c7c6660679b25a93ab2912b42af19d6d
prerequisite-patch-id: 717bfde0d4141e875562d62b0e3c22d687517462
prerequisite-patch-id: 3e465d93cacbb875c5508b106bcaae53665de29e
prerequisite-patch-id: da2eff4c67005d05b39c39d6d164b94c2837a4dd
prerequisite-patch-id: a293472d78b43026dacbfe22a34ba1f8f2626e63
prerequisite-patch-id: 56469933009bbfd5a57d843f698f91bb0cdd846a
prerequisite-patch-id: 16433e5bde53510f9e6b7e04db2279bd0a8f63f5
prerequisite-patch-id: 03d349761a5a56d223067b051b1a38d594006ae2
prerequisite-patch-id: 9112aaf35b0ab26bf396285fbfbb51b81901d80b
prerequisite-patch-id: 901a0c1b4c2da010722471826fbfc51402ab8573
prerequisite-patch-id: 1204b4883154dea8cb7d93efadb9f49e70ecd7a8
prerequisite-patch-id: d2762d5dd1254bf7cf89a16b317b7b421718935f
prerequisite-patch-id: d1337f7a08fd4758a94bc5b2faeb30cbad1fe9cf
prerequisite-patch-id: a1d46785e75f0ec87cd12807cb57adc869a7f6a8
prerequisite-patch-id: 44c5d2365c5237b44a0162be9e73dcb392deab2c
prerequisite-patch-id: ad7ca39e7946dbf9e9ed4677b577bb5efad15af5
prerequisite-patch-id: 3984bbb1dbd74d87215a789483a75e968e81b453
prerequisite-patch-id: 0db54371960496caafd93db8801125a92f43d6c6
prerequisite-patch-id: 4d97d31af7bca868bf68a61fe7825dcbd7cc500b
prerequisite-patch-id: 227908b78f668bc6301cfa2217fa581471176b0f
prerequisite-patch-id: d4fba49d9b4aaee2cc42d91651041faa4b1a4d38
prerequisite-patch-id: fd779ec9acd1cd56b7eb9176cd0a10a548133f0c
prerequisite-patch-id: 5c61093340a745890222477d54f21e1121643c95
prerequisite-patch-id: 1199e9ee338002117fbe257118ff264e64eec0bb
prerequisite-patch-id: 08be0388a5aafb66919d0d678380c33f092738ce
prerequisite-patch-id: f98799e2973376543e67ae7a93ab62a20aa29564
prerequisite-patch-id: f670bed1eba8e1440bbfed164fb0c367f248e231
prerequisite-patch-id: 88a5543ceb70d27bd05b718480334d0a28133697
prerequisite-patch-id: 544f65704decf1a67848c8dc68ac60c5b76cdb81
prerequisite-patch-id: f36fad7a6eb02bf1b3c1171a61cbb854794bdf63
prerequisite-patch-id: 03a4a6747171c25f622df537a7608a4058acfc2c
prerequisite-patch-id: 7079fdab6ec5000d3a7b4868e138e9177bf03945
prerequisite-patch-id: 8483765548e3cdbd16e0a9ef44e9c60941611b12
prerequisite-patch-id: 6643c2dbd00ab86e742ef5a15f4d23c039575f07
prerequisite-patch-id: 9f288cab3b89ff67865b4e20c7b7f6f3835cec6f
prerequisite-patch-id: 6c8bae764061f224f8bf6bfb0a58b44689d1fb85
prerequisite-patch-id: c2d5675bad1219323ceb4f7c382dea8ef654256f
prerequisite-patch-id: f5fd02ee0a5960d146eba2f57ecf9ccb15a1b216
prerequisite-patch-id: d12cfa9299bb33218551b98efc8d2af8bb2ef321
prerequisite-patch-id: 889b86d3e246e3d6b4cd71e7e77571bc2bbaf45e
prerequisite-patch-id: 92f4f7c0df22746ab9dcf4c84bc14f25f5043dc2
prerequisite-patch-id: 172d56f9145b9ec517bb8ff3e33b33c6a2c39786
prerequisite-patch-id: f374a92609437c1cd7bf2e247c287f126d337a02
prerequisite-patch-id: 2d540c940f6a6e05946e9ba2ca12a3fb28135e8c
prerequisite-change-id: 20261101-work-procfs-fs-41f9fdc489cb:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c
prerequisite-patch-id: 504e13cb7af5da6f3c8503f9336116ddd6ee9fc0
prerequisite-patch-id: 079f5420c7c6660679b25a93ab2912b42af19d6d
prerequisite-patch-id: 717bfde0d4141e875562d62b0e3c22d687517462
prerequisite-patch-id: 3e465d93cacbb875c5508b106bcaae53665de29e
prerequisite-patch-id: da2eff4c67005d05b39c39d6d164b94c2837a4dd
prerequisite-patch-id: a293472d78b43026dacbfe22a34ba1f8f2626e63
prerequisite-patch-id: 56469933009bbfd5a57d843f698f91bb0cdd846a
prerequisite-patch-id: 16433e5bde53510f9e6b7e04db2279bd0a8f63f5
prerequisite-patch-id: 03d349761a5a56d223067b051b1a38d594006ae2
prerequisite-patch-id: 9112aaf35b0ab26bf396285fbfbb51b81901d80b
prerequisite-patch-id: 901a0c1b4c2da010722471826fbfc51402ab8573
prerequisite-patch-id: 1204b4883154dea8cb7d93efadb9f49e70ecd7a8
prerequisite-patch-id: d2762d5dd1254bf7cf89a16b317b7b421718935f
prerequisite-patch-id: d1337f7a08fd4758a94bc5b2faeb30cbad1fe9cf
prerequisite-patch-id: a1d46785e75f0ec87cd12807cb57adc869a7f6a8
prerequisite-patch-id: 44c5d2365c5237b44a0162be9e73dcb392deab2c
prerequisite-patch-id: ad7ca39e7946dbf9e9ed4677b577bb5efad15af5
prerequisite-patch-id: 3984bbb1dbd74d87215a789483a75e968e81b453
prerequisite-patch-id: 0db54371960496caafd93db8801125a92f43d6c6
prerequisite-patch-id: 4d97d31af7bca868bf68a61fe7825dcbd7cc500b
prerequisite-patch-id: 227908b78f668bc6301cfa2217fa581471176b0f
prerequisite-patch-id: d4fba49d9b4aaee2cc42d91651041faa4b1a4d38
prerequisite-patch-id: fd779ec9acd1cd56b7eb9176cd0a10a548133f0c
prerequisite-patch-id: 5c61093340a745890222477d54f21e1121643c95
prerequisite-patch-id: 1199e9ee338002117fbe257118ff264e64eec0bb
prerequisite-patch-id: 08be0388a5aafb66919d0d678380c33f092738ce
prerequisite-patch-id: f98799e2973376543e67ae7a93ab62a20aa29564
prerequisite-patch-id: f670bed1eba8e1440bbfed164fb0c367f248e231
prerequisite-patch-id: 88a5543ceb70d27bd05b718480334d0a28133697
prerequisite-patch-id: 544f65704decf1a67848c8dc68ac60c5b76cdb81
prerequisite-patch-id: f36fad7a6eb02bf1b3c1171a61cbb854794bdf63
prerequisite-patch-id: 03a4a6747171c25f622df537a7608a4058acfc2c
prerequisite-patch-id: 7079fdab6ec5000d3a7b4868e138e9177bf03945
prerequisite-patch-id: 8483765548e3cdbd16e0a9ef44e9c60941611b12
prerequisite-patch-id: 6643c2dbd00ab86e742ef5a15f4d23c039575f07
prerequisite-patch-id: 9f288cab3b89ff67865b4e20c7b7f6f3835cec6f
prerequisite-patch-id: 6c8bae764061f224f8bf6bfb0a58b44689d1fb85
prerequisite-patch-id: c2d5675bad1219323ceb4f7c382dea8ef654256f
prerequisite-patch-id: f5fd02ee0a5960d146eba2f57ecf9ccb15a1b216
prerequisite-patch-id: d12cfa9299bb33218551b98efc8d2af8bb2ef321
prerequisite-patch-id: 889b86d3e246e3d6b4cd71e7e77571bc2bbaf45e
prerequisite-patch-id: 92f4f7c0df22746ab9dcf4c84bc14f25f5043dc2
prerequisite-patch-id: 172d56f9145b9ec517bb8ff3e33b33c6a2c39786
prerequisite-patch-id: f374a92609437c1cd7bf2e247c287f126d337a02
prerequisite-patch-id: 2d540c940f6a6e05946e9ba2ca12a3fb28135e8c
prerequisite-patch-id: e1136dd06b641700522373064fc4f0b8988019d2
prerequisite-patch-id: 1f93c44de1de16d56b63f242e11c61eb99fb839e
prerequisite-patch-id: 9e05d183b1de52caab2cfdfc3ddf204626166018
prerequisite-patch-id: 715c9c767af5025546ebd4a73411f92fe69f2c5b
prerequisite-patch-id: a6e9771994c9e7839f6346ce84b88aa76b1fbb94
prerequisite-patch-id: 5cdbf669ab8cb5aae9695872b95cedcc895e8920
prerequisite-patch-id: 1181569f72ebcfd92fc854891a4cab295c99fd94
prerequisite-patch-id: 51fef3e3ccf3c0de31f96ace299dc6d1960af10b
prerequisite-patch-id: 2c3465f99c412708e4dd74d08a59c584d38cd011
prerequisite-patch-id: 4c2dd5a6b6956f87252fa471bfbdbec2f93e261f
prerequisite-patch-id: 637fd7a493983a181121f23dd2a9894bb941ea6e
prerequisite-patch-id: 7922f04bbdfc10776b16363f2414a179a168842b
prerequisite-patch-id: fbeeb1ac52cfcbd92b9670b4e69b948e80c874b6
prerequisite-patch-id: 76ce3f750a3c747b6dbbe4fe8dfca721504ff9e7
prerequisite-patch-id: d2d80316fbb9ce1cdad0ff0fcaddfc003715aff6
prerequisite-patch-id: 7d31311fd97ddb0b2833c153f22855a39516be16
prerequisite-patch-id: 17675d57b3957c9d0e81c1bb6f7ddf01233a3586
prerequisite-patch-id: a1a7ca09cabb1f3bde983a8f249865cfb33ffba9
prerequisite-patch-id: 30b8a6a426dd7b9f466bde876a813a4e27c4a284
prerequisite-patch-id: 95ece6bf7b926859312be956bb9b0ba7bf1d274a
prerequisite-patch-id: 4a896ec0ba4ac506b1f6a010a05f703f1bf888b8
prerequisite-patch-id: 5914904497e4739e4e94d391968d547b375c4def
prerequisite-patch-id: f98a02b2dae4ad56aa7d11de0361ff86b1b60846
prerequisite-patch-id: c4ba3c61e396328d84731308a7fa49348191f2a2
prerequisite-patch-id: 1b836097aa8c6e35ca7a9f5cbb0aafab59153720
prerequisite-patch-id: fa65e32038043b8430248870d256e3eaa47f4d34
prerequisite-patch-id: f2dd514af8011acaea88d58a6fefe06a6af349bb
prerequisite-change-id: 20261101-work-procfs-net-bc8b21e3eff0:v1
prerequisite-patch-id: 0474af12959624910718795bf86684383664c685
prerequisite-patch-id: 540aa9ea28adcbc54d8264f66314e29e3b8c9d49
prerequisite-patch-id: 2f7139d1c972c4439dccca610caa5f1288987a3d
prerequisite-patch-id: b0031a7c4855b7ec38259c59f30f65101a8fc4b5
prerequisite-patch-id: 0b7f226abf2b9ff41873a1aa55cf97747647d56b
prerequisite-patch-id: f63e345f897619af54813958046617c655fb7daf
prerequisite-patch-id: c0e21e762a2960e3324c5ab04634a7035e515fa6
prerequisite-patch-id: fd59c71eadd8f39c58b3baea93567bc093bc9b0e
prerequisite-patch-id: 5d52cc8a5c73e4c5ba16b8e24f51408c17583bf9
prerequisite-patch-id: 1f9179284e2f3314d4b46ac5e989089a21c7576c
prerequisite-patch-id: 10397bbf696bee16dc71eb79fff6c5e73842bcbc
prerequisite-patch-id: 9b44279dcf08912238d2f6438a3328a778a79a0c
prerequisite-patch-id: 16854b621a3a71dfc365aaf7abaa7f65adfe9f7e
prerequisite-patch-id: ca42eea21fae1fd1cd3518ab057c74b61ffd68fe
prerequisite-patch-id: 23255dc2be76b0479c083dbd4c897bfbb533aed5
prerequisite-patch-id: 82129c687ebf0a41b369893b92f2c038420ab801
prerequisite-patch-id: 56ad57a99a5bda075a9f4e844368974296744934
prerequisite-patch-id: 288b3cbaa11e01a778cc3d7beb6c0549aacf59f2
prerequisite-patch-id: a163b1549608652bfa5bd9ff93efa55b541010c4
prerequisite-patch-id: 78f067c343177e52d49ddfe9ee2992e602549869
prerequisite-patch-id: 19c12bc6e3fbed35acc1f657196b1a997f48cdfe
prerequisite-patch-id: 5800c3076694bad719954b2ad4d7448c6e013bfd
prerequisite-patch-id: 2a2f63d8843b159594b9eee6ddea028ce76a6270
prerequisite-patch-id: 40a1951d57906c4a1b98a4bd96a814b80339b9c5
prerequisite-patch-id: e2141077e69f20a2b27fab69c8d61d0686682f53
prerequisite-patch-id: c4d6018c26c40dc549d88330d23ce48f872c2b7f
prerequisite-patch-id: 6de8ec17746031bc6d1d9ac262c5be0f905fcd79
prerequisite-patch-id: b87bcb167884605bff1fd0f2ed8a88942d07efd5
prerequisite-patch-id: cfa9a8b4c67e20cfd22d77035bfd21ee96ab3298
prerequisite-patch-id: abf3482ed2399b7b2e1ee72e937af05bb9200667
prerequisite-patch-id: 02c175b2dee565a85b3308ecf2ec8e3c9a029e0f
prerequisite-patch-id: 4ed6b3d3d45de49d4967e88ba4e7f03372775f55
prerequisite-patch-id: dbe02fe3f784776fda5e7ae99f077bef0cd4b48d
prerequisite-patch-id: 53edc9a95afff57c4acba40095a8054924261ea2
prerequisite-patch-id: cf9ce0099005fd3bc31a1e15131d48e315ac3bee
prerequisite-patch-id: 16a657a5d0046068dbace29e7ea046ac1441f46f
prerequisite-patch-id: efeb2d6f7df71d5dede2c1d12fe999a28e38aab9
prerequisite-patch-id: e6ffe2a5c3ee5645f56c8366b568fbc9f140848c
prerequisite-patch-id: 117db27dcdef433de7c6be5e95973cac1e373927
prerequisite-patch-id: a0844aa26ffb65ccb362b88ef6c2f4758e15edbf
prerequisite-patch-id: 143fb91bddf33068b6ee11e205818c4c2815e23d
prerequisite-patch-id: 6490e4da1cb2f543683bd164bc9a5a1c943057e7
prerequisite-patch-id: cdefca57d3c552fb6f6b928699edd85f33be25fd
prerequisite-patch-id: 2f0fdb970ddd760d85ff5d2caad0b96348270182
prerequisite-patch-id: f2aa468ff5cbb1cabab3f70e1b8eeee0286cfb4a
prerequisite-patch-id: 037030d95eeb7c49b1e43f638ec954f2cebbcfb1
prerequisite-patch-id: 487141c49e2548310bfb6c2a0964a78f08b285a9
prerequisite-patch-id: c3d3008819fa5629a18142ec9ba6d7024ade7fdb
prerequisite-patch-id: ba15e7c86b60ccc2f0f7ee4c9bc99073068757bc
prerequisite-patch-id: 9423f410b99853693db06c29f12f1c08278b050d
prerequisite-patch-id: 4e7966978e4b11c4d843831f312cdcd5b848f00c
prerequisite-patch-id: 3d24be1897a624d519ef144801f76d152c7528a9
prerequisite-patch-id: 3b4d9fe7b26f6b7b9b3f7725ea333df357107c44
prerequisite-patch-id: 7d9dd345428baeb88c862ef902612f7160dc1d8c
prerequisite-patch-id: 504e13cb7af5da6f3c8503f9336116ddd6ee9fc0
prerequisite-patch-id: 079f5420c7c6660679b25a93ab2912b42af19d6d
prerequisite-patch-id: 717bfde0d4141e875562d62b0e3c22d687517462
prerequisite-patch-id: 3e465d93cacbb875c5508b106bcaae53665de29e
prerequisite-patch-id: da2eff4c67005d05b39c39d6d164b94c2837a4dd
prerequisite-patch-id: a293472d78b43026dacbfe22a34ba1f8f2626e63
prerequisite-patch-id: 56469933009bbfd5a57d843f698f91bb0cdd846a
prerequisite-patch-id: 16433e5bde53510f9e6b7e04db2279bd0a8f63f5
prerequisite-patch-id: 03d349761a5a56d223067b051b1a38d594006ae2
prerequisite-patch-id: 9112aaf35b0ab26bf396285fbfbb51b81901d80b
prerequisite-patch-id: 901a0c1b4c2da010722471826fbfc51402ab8573
prerequisite-patch-id: 1204b4883154dea8cb7d93efadb9f49e70ecd7a8
prerequisite-patch-id: d2762d5dd1254bf7cf89a16b317b7b421718935f
prerequisite-patch-id: d1337f7a08fd4758a94bc5b2faeb30cbad1fe9cf
prerequisite-patch-id: a1d46785e75f0ec87cd12807cb57adc869a7f6a8
prerequisite-patch-id: 44c5d2365c5237b44a0162be9e73dcb392deab2c
prerequisite-patch-id: ad7ca39e7946dbf9e9ed4677b577bb5efad15af5
prerequisite-patch-id: 3984bbb1dbd74d87215a789483a75e968e81b453
prerequisite-patch-id: 0db54371960496caafd93db8801125a92f43d6c6
prerequisite-patch-id: 4d97d31af7bca868bf68a61fe7825dcbd7cc500b
prerequisite-patch-id: 227908b78f668bc6301cfa2217fa581471176b0f
prerequisite-patch-id: d4fba49d9b4aaee2cc42d91651041faa4b1a4d38
prerequisite-patch-id: fd779ec9acd1cd56b7eb9176cd0a10a548133f0c
prerequisite-patch-id: 5c61093340a745890222477d54f21e1121643c95
prerequisite-patch-id: 1199e9ee338002117fbe257118ff264e64eec0bb
prerequisite-patch-id: 08be0388a5aafb66919d0d678380c33f092738ce
prerequisite-patch-id: f98799e2973376543e67ae7a93ab62a20aa29564
prerequisite-patch-id: f670bed1eba8e1440bbfed164fb0c367f248e231
prerequisite-patch-id: 88a5543ceb70d27bd05b718480334d0a28133697
prerequisite-patch-id: 544f65704decf1a67848c8dc68ac60c5b76cdb81
prerequisite-patch-id: f36fad7a6eb02bf1b3c1171a61cbb854794bdf63
prerequisite-patch-id: 03a4a6747171c25f622df537a7608a4058acfc2c
prerequisite-patch-id: 7079fdab6ec5000d3a7b4868e138e9177bf03945
prerequisite-patch-id: 8483765548e3cdbd16e0a9ef44e9c60941611b12
prerequisite-patch-id: 6643c2dbd00ab86e742ef5a15f4d23c039575f07
prerequisite-patch-id: 9f288cab3b89ff67865b4e20c7b7f6f3835cec6f
prerequisite-patch-id: 6c8bae764061f224f8bf6bfb0a58b44689d1fb85
prerequisite-patch-id: c2d5675bad1219323ceb4f7c382dea8ef654256f
prerequisite-patch-id: f5fd02ee0a5960d146eba2f57ecf9ccb15a1b216
prerequisite-patch-id: d12cfa9299bb33218551b98efc8d2af8bb2ef321
prerequisite-patch-id: 889b86d3e246e3d6b4cd71e7e77571bc2bbaf45e
prerequisite-patch-id: 92f4f7c0df22746ab9dcf4c84bc14f25f5043dc2
prerequisite-patch-id: 172d56f9145b9ec517bb8ff3e33b33c6a2c39786
prerequisite-patch-id: f374a92609437c1cd7bf2e247c287f126d337a02
prerequisite-patch-id: 2d540c940f6a6e05946e9ba2ca12a3fb28135e8c
prerequisite-patch-id: e1136dd06b641700522373064fc4f0b8988019d2
prerequisite-patch-id: 1f93c44de1de16d56b63f242e11c61eb99fb839e
prerequisite-patch-id: 9e05d183b1de52caab2cfdfc3ddf204626166018
prerequisite-patch-id: 715c9c767af5025546ebd4a73411f92fe69f2c5b
prerequisite-patch-id: a6e9771994c9e7839f6346ce84b88aa76b1fbb94
prerequisite-patch-id: 5cdbf669ab8cb5aae9695872b95cedcc895e8920
prerequisite-patch-id: 1181569f72ebcfd92fc854891a4cab295c99fd94
prerequisite-patch-id: 51fef3e3ccf3c0de31f96ace299dc6d1960af10b
prerequisite-patch-id: 2c3465f99c412708e4dd74d08a59c584d38cd011
prerequisite-patch-id: 4c2dd5a6b6956f87252fa471bfbdbec2f93e261f
prerequisite-patch-id: 637fd7a493983a181121f23dd2a9894bb941ea6e
prerequisite-patch-id: 7922f04bbdfc10776b16363f2414a179a168842b
prerequisite-patch-id: fbeeb1ac52cfcbd92b9670b4e69b948e80c874b6
prerequisite-patch-id: 76ce3f750a3c747b6dbbe4fe8dfca721504ff9e7
prerequisite-patch-id: d2d80316fbb9ce1cdad0ff0fcaddfc003715aff6
prerequisite-patch-id: 7d31311fd97ddb0b2833c153f22855a39516be16
prerequisite-patch-id: 17675d57b3957c9d0e81c1bb6f7ddf01233a3586
prerequisite-patch-id: a1a7ca09cabb1f3bde983a8f249865cfb33ffba9
prerequisite-patch-id: 30b8a6a426dd7b9f466bde876a813a4e27c4a284
prerequisite-patch-id: 95ece6bf7b926859312be956bb9b0ba7bf1d274a
prerequisite-patch-id: 4a896ec0ba4ac506b1f6a010a05f703f1bf888b8
prerequisite-patch-id: 5914904497e4739e4e94d391968d547b375c4def
prerequisite-patch-id: f98a02b2dae4ad56aa7d11de0361ff86b1b60846
prerequisite-patch-id: c4ba3c61e396328d84731308a7fa49348191f2a2
prerequisite-patch-id: 1b836097aa8c6e35ca7a9f5cbb0aafab59153720
prerequisite-patch-id: fa65e32038043b8430248870d256e3eaa47f4d34
prerequisite-patch-id: f2dd514af8011acaea88d58a6fefe06a6af349bb
prerequisite-patch-id: ed86b546d44c9d5612a132b8f188a1727bf20140
prerequisite-patch-id: 3531ac7c29c6d31f5c4d7830eec493700c7817cc
prerequisite-patch-id: d5636a3c6c5d806cac00791c49bf3703b2a61c7d
prerequisite-patch-id: a789b6da4762c80e4e223d6f20db32a4812cd87b
prerequisite-patch-id: 96c2d266089780f6a0b07f54da812b87b5749abd
prerequisite-patch-id: 51e5bdc34987e2266e8801368e622566ccf20968
prerequisite-patch-id: 48b7e3fe2d9ce6c61b8b7d34ad26ecf0bfcf53cc
prerequisite-patch-id: f39c6af65cc5b209517939533a7cb9cc1ee03da0
prerequisite-patch-id: 9e2cccf47b8c293ebef941d1268956901f1dd323
prerequisite-patch-id: 0126190ba10aa140572e0a652c91d1de7fae0106
prerequisite-patch-id: c0de7f6bcf931394d46239405296ce7008f7a095


^ permalink raw reply	[flat|nested] 160+ messages in thread

* [PATCH RFC 1/5] proc: drop ->proc_write()
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
@ 2026-11-02 15:15   ` Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 2/5] proc: drop ->proc_ioctl() and ->proc_compat_ioctl() Christian Brauner
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:15 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Every writable entry implements ->proc_write_buf() now and procfs
copies in what is written before it pins the entry. Remove
->proc_write().

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  2 --
 fs/proc/inode.c         | 15 +--------------
 include/linux/proc_fs.h |  1 -
 3 files changed, 1 insertion(+), 17 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 35dd745a0357..7b2ed3c42d32 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -469,8 +469,6 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 			pde->flags |= PROC_ENTRY_proc_lseek;
 	}
 	if (proc_ops->proc_write_buf) {
-		if (WARN_ON_ONCE(proc_ops->proc_write))
-			return -EINVAL;
 		pde->flags |= PROC_ENTRY_proc_write_buf;
 		pde->write_size = proc_ops->proc_write_size ?: PAGE_SIZE - 1;
 		if (proc_ops->proc_flags & PROC_ENTRY_SHORT_WRITES)
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 7e4ca5751abb..dac5974d4652 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -491,14 +491,6 @@ static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count,
 	return rv;
 }
 
-static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
-	const auto write = pde->proc_ops->proc_write;
-	if (write)
-		return write(file, buf, count, ppos);
-	return -EIO;
-}
-
 /*
  * procfs copies in what is written before it pins the entry. A writer that
  * faults on its buffer then doesn't hold up remove_proc_entry().
@@ -533,15 +525,10 @@ static ssize_t pde_write_buf(struct proc_dir_entry *pde, struct file *file,
 static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	ssize_t rv;
 
 	if (pde_has_proc_write_buf(pde))
 		return pde_write_buf(pde, file, buf, count, ppos);
-	if (!pde_pin(pde))
-		return -EIO;
-	rv = pde_write(pde, file, buf, count, ppos);
-	pde_unpin(pde);
-	return rv;
+	return -EIO;
 }
 
 static __poll_t pde_poll(struct proc_dir_entry *pde, struct file *file, struct poll_table_struct *pts)
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 2ba4e4a39e13..f21dd826eaa6 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -74,7 +74,6 @@ struct proc_ops {
 	int	(*proc_open)(struct inode *, struct file *);
 	ssize_t	(*proc_read)(struct file *, char __user *, size_t, loff_t *);
 	ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *);
-	ssize_t	(*proc_write)(struct file *, const char __user *, size_t, loff_t *);
 	/*
 	 * mandatory unless nonseekable_open() or equivalent is used, procfs
 	 * seeks seq_file entries itself

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 2/5] proc: drop ->proc_ioctl() and ->proc_compat_ioctl()
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 1/5] proc: drop ->proc_write() Christian Brauner
@ 2026-11-02 15:15   ` Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 3/5] proc: only let permanent entries read straight into userspace Christian Brauner
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:15 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Every entry with ioctls implements ->proc_ioctl_buf() now and procfs
copies the arguments in and out around pinning the entry, for 32-bit
tasks too. Remove ->proc_ioctl() and ->proc_compat_ioctl().

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  2 --
 fs/proc/inode.c         | 30 ++----------------------------
 include/linux/proc_fs.h |  4 ----
 3 files changed, 2 insertions(+), 34 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 7b2ed3c42d32..413382efefc7 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -484,8 +484,6 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 			pde->flags |= PROC_ENTRY_FULL_READS;
 	}
 	if (proc_ops->proc_ioctl_buf) {
-		if (WARN_ON_ONCE(proc_ops->proc_ioctl))
-			return -EINVAL;
 		/* the table says what procfs copies, see pde_ioctl_arg() */
 		if (WARN_ON_ONCE(!proc_ops->proc_ioctl_args ||
 				 !proc_ops->proc_nr_ioctl_args))
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index dac5974d4652..971f2aecc006 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -551,14 +551,6 @@ static __poll_t proc_reg_poll(struct file *file, struct poll_table_struct *pts)
 	return rv;
 }
 
-static long pde_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg)
-{
-	const auto ioctl = pde->proc_ops->proc_ioctl;
-	if (ioctl)
-		return ioctl(file, cmd, arg);
-	return -ENOTTY;
-}
-
 /* the table lives in proc_ops, which goes away with the entry */
 static bool pde_ioctl_arg(struct proc_dir_entry *pde, unsigned int cmd,
 			  struct proc_ioctl_arg *arg)
@@ -615,38 +607,20 @@ static long pde_ioctl_buf(struct proc_dir_entry *pde, struct file *file,
 static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	long rv;
 
 	if (pde_has_proc_ioctl_buf(pde))
 		return pde_ioctl_buf(pde, file, cmd, (void __user *)arg, arg);
-	if (!pde_pin(pde))
-		return -ENOTTY;
-	rv = pde_ioctl(pde, file, cmd, arg);
-	pde_unpin(pde);
-	return rv;
-}
-
-#ifdef CONFIG_COMPAT
-static long pde_compat_ioctl(struct proc_dir_entry *pde, struct file *file, unsigned int cmd, unsigned long arg)
-{
-	const auto compat_ioctl = pde->proc_ops->proc_compat_ioctl;
-	if (compat_ioctl)
-		return compat_ioctl(file, cmd, arg);
 	return -ENOTTY;
 }
 
+#ifdef CONFIG_COMPAT
 static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	long rv;
 
 	if (pde_has_proc_ioctl_buf(pde))
 		return pde_ioctl_buf(pde, file, cmd, compat_ptr(arg), arg);
-	if (!pde_pin(pde))
-		return -ENOTTY;
-	rv = pde_compat_ioctl(pde, file, cmd, arg);
-	pde_unpin(pde);
-	return rv;
+	return -ENOTTY;
 }
 #else
 #define proc_reg_compat_ioctl NULL
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index f21dd826eaa6..64242a9aa117 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -81,10 +81,6 @@ struct proc_ops {
 	loff_t	(*proc_lseek)(struct file *, loff_t, int);
 	int	(*proc_release)(struct inode *, struct file *);
 	__poll_t (*proc_poll)(struct file *, struct poll_table_struct *);
-	long	(*proc_ioctl)(struct file *, unsigned int, unsigned long);
-#ifdef CONFIG_COMPAT
-	long	(*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);
-#endif
 	int	(*proc_mmap)(struct file *, struct vm_area_struct *);
 	unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 	/*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 3/5] proc: only let permanent entries read straight into userspace
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 1/5] proc: drop ->proc_write() Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 2/5] proc: drop ->proc_ioctl() and ->proc_compat_ioctl() Christian Brauner
@ 2026-11-02 15:15   ` Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 4/5] proc: rename ->proc_read() and ->proc_read_iter() Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 5/5] proc: drop the _buf suffix from the new methods Christian Brauner
  4 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:15 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Every entry that can be removed implements ->proc_read_buf() now and
procfs copies what is read to userspace after unpinning the entry.
->proc_read() and ->proc_read_iter() are left for the entries that are
never removed, kcore, kmsg, kpagecount, kpageflags, kpagecgroup and
profile. procfs never pins those, so nothing can wait for them.

Say so in struct proc_ops and refuse to register an entry that can be
removed and has one of them. The read paths then don't need to pin the
entry anymore.

PROC_ENTRY_PERMANENT is 0 in modules, so a module can't register an
entry with one of them anymore.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       |  4 ++++
 fs/proc/inode.c         | 27 ++++++++-------------------
 include/linux/proc_fs.h |  6 ++++++
 3 files changed, 18 insertions(+), 19 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 413382efefc7..7c8def4ed9fc 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -443,6 +443,10 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 
 	if (proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
 		pde->flags |= PROC_ENTRY_PERMANENT;
+	/* These copy to userspace and only an entry that can't go away may. */
+	if (WARN_ON_ONCE((proc_ops->proc_read || proc_ops->proc_read_iter) &&
+			 !pde_is_permanent(pde)))
+		return -EINVAL;
 	if (proc_ops->proc_seq_ops) {
 		pde->seq_ops = proc_ops->proc_seq_ops;
 		pde->state_size = proc_ops->proc_state_size;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 971f2aecc006..a7f8818b0b74 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -460,35 +460,24 @@ static ssize_t pde_read_buf(struct proc_dir_entry *pde, struct kiocb *iocb,
 static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp));
-	ssize_t ret;
 
 	if (pde_has_proc_read_buf(pde))
 		return pde_read_buf(pde, iocb, iter);
-	if (!pde_pin(pde))
-		return -EIO;
-	ret = pde->proc_ops->proc_read_iter(iocb, iter);
-	pde_unpin(pde);
-	return ret;
-}
-
-static ssize_t pde_read(struct proc_dir_entry *pde, struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
-	const auto read = pde->proc_ops->proc_read;
-	if (read)
-		return read(file, buf, count, ppos);
-	return -EIO;
+	/* only permanent entries have one, see pde_set_flags() */
+	return pde->proc_ops->proc_read_iter(iocb, iter);
 }
 
 static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
-	ssize_t rv;
 
-	if (!pde_pin(pde))
+	/*
+	 * Only permanent entries have one, see pde_set_flags(). Anything else
+	 * might be gone along with its ->proc_ops.
+	 */
+	if (!pde_is_permanent(pde) || !pde->proc_ops->proc_read)
 		return -EIO;
-	rv = pde_read(pde, file, buf, count, ppos);
-	pde_unpin(pde);
-	return rv;
+	return pde->proc_ops->proc_read(file, buf, count, ppos);
 }
 
 /*
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 64242a9aa117..17516b064922 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -17,6 +17,8 @@ struct seq_operations;
 enum {
 	/*
 	 * All /proc entries using this ->proc_ops instance are never removed.
+	 * procfs calls into them without pinning them, so they may copy to
+	 * userspace themselves through ->proc_read() or ->proc_read_iter().
 	 *
 	 * If in doubt, ignore this flag.
 	 */
@@ -72,6 +74,10 @@ struct proc_ioctl_arg {
 struct proc_ops {
 	unsigned int proc_flags;
 	int	(*proc_open)(struct inode *, struct file *);
+	/*
+	 * These two are only for PROC_ENTRY_PERMANENT entries. Everything else
+	 * uses ->proc_read_buf().
+	 */
 	ssize_t	(*proc_read)(struct file *, char __user *, size_t, loff_t *);
 	ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *);
 	/*

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 4/5] proc: rename ->proc_read() and ->proc_read_iter()
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
                     ` (2 preceding siblings ...)
  2026-11-02 15:15   ` [PATCH RFC 3/5] proc: only let permanent entries read straight into userspace Christian Brauner
@ 2026-11-02 15:15   ` Christian Brauner
  2026-11-02 15:15   ` [PATCH RFC 5/5] proc: drop the _buf suffix from the new methods Christian Brauner
  4 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:15 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

Only entries that are never removed may implement ->proc_read() or
->proc_read_iter() now, and PROC_ENTRY_PERMANENT is 0 in modules. But a
module that sets one of them still builds fine and only finds out when
registering its entry fails with a warning.

So rename them to ->proc_permanent_read() and
->proc_permanent_read_iter(). A module that sets one of them doesn't
build anymore and the name says which entries may use them. Hiding the
members from modules behind #ifdef MODULE instead would give modules a
different layout of struct proc_ops than the rest of the kernel.

While at it, rename PROC_ENTRY_proc_read_iter and
pde_has_proc_read_iter() to match.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 fs/proc/generic.c       | 15 +++++++++------
 fs/proc/inode.c         |  9 +++++----
 fs/proc/internal.h      |  5 +++--
 fs/proc/kcore.c         |  2 +-
 fs/proc/kmsg.c          |  2 +-
 fs/proc/page.c          |  6 +++---
 include/linux/proc_fs.h | 10 ++++++----
 kernel/profile.c        |  2 +-
 8 files changed, 29 insertions(+), 22 deletions(-)

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 7c8def4ed9fc..4bcec5a29b5f 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -444,7 +444,8 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 	if (proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
 		pde->flags |= PROC_ENTRY_PERMANENT;
 	/* These copy to userspace and only an entry that can't go away may. */
-	if (WARN_ON_ONCE((proc_ops->proc_read || proc_ops->proc_read_iter) &&
+	if (WARN_ON_ONCE((proc_ops->proc_permanent_read ||
+			  proc_ops->proc_permanent_read_iter) &&
 			 !pde_is_permanent(pde)))
 		return -EINVAL;
 	if (proc_ops->proc_seq_ops) {
@@ -457,7 +458,8 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 	}
 	if (pde_is_seq_file(pde)) {
 		/* procfs reads and seeks through the seq_file itself */
-		if (WARN_ON_ONCE(proc_ops->proc_read || proc_ops->proc_read_iter ||
+		if (WARN_ON_ONCE(proc_ops->proc_permanent_read ||
+				 proc_ops->proc_permanent_read_iter ||
 				 proc_ops->proc_lseek))
 			return -EINVAL;
 		/* and frees it, see pde_seq_release() */
@@ -467,8 +469,8 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 			return -EINVAL;
 		pde->flags |= PROC_ENTRY_proc_lseek;
 	} else {
-		if (proc_ops->proc_read_iter)
-			pde->flags |= PROC_ENTRY_proc_read_iter;
+		if (proc_ops->proc_permanent_read_iter)
+			pde->flags |= PROC_ENTRY_proc_permanent_read_iter;
 		if (proc_ops->proc_lseek)
 			pde->flags |= PROC_ENTRY_proc_lseek;
 	}
@@ -479,8 +481,9 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 			pde->flags |= PROC_ENTRY_SHORT_WRITES;
 	}
 	if (proc_ops->proc_read_buf) {
-		if (WARN_ON_ONCE(pde_is_seq_file(pde) || proc_ops->proc_read ||
-				 proc_ops->proc_read_iter))
+		if (WARN_ON_ONCE(pde_is_seq_file(pde) ||
+				 proc_ops->proc_permanent_read ||
+				 proc_ops->proc_permanent_read_iter))
 			return -EINVAL;
 		pde->flags |= PROC_ENTRY_proc_read_buf;
 		pde->read_size = proc_ops->proc_read_size ?: PAGE_SIZE;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index a7f8818b0b74..16d8f00bfc26 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -464,7 +464,7 @@ static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 	if (pde_has_proc_read_buf(pde))
 		return pde_read_buf(pde, iocb, iter);
 	/* only permanent entries have one, see pde_set_flags() */
-	return pde->proc_ops->proc_read_iter(iocb, iter);
+	return pde->proc_ops->proc_permanent_read_iter(iocb, iter);
 }
 
 static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
@@ -475,9 +475,9 @@ static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count,
 	 * Only permanent entries have one, see pde_set_flags(). Anything else
 	 * might be gone along with its ->proc_ops.
 	 */
-	if (!pde_is_permanent(pde) || !pde->proc_ops->proc_read)
+	if (!pde_is_permanent(pde) || !pde->proc_ops->proc_permanent_read)
 		return -EIO;
-	return pde->proc_ops->proc_read(file, buf, count, ppos);
+	return pde->proc_ops->proc_permanent_read(file, buf, count, ppos);
 }
 
 /*
@@ -880,7 +880,8 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 		inode->i_op = de->proc_iops;
 		if (pde_is_seq_file(de))
 			inode->i_fop = &proc_seq_file_ops;
-		else if (pde_has_proc_read_iter(de) || pde_has_proc_read_buf(de))
+		else if (pde_has_proc_permanent_read_iter(de) ||
+			 pde_has_proc_read_buf(de))
 			inode->i_fop = &proc_iter_file_ops;
 		else
 			inode->i_fop = &proc_reg_file_ops;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 93d20995b6fe..f4677815ebfa 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -89,9 +89,10 @@ static inline void pde_make_permanent(struct proc_dir_entry *pde)
 	pde->flags |= PROC_ENTRY_PERMANENT;
 }
 
-static inline bool pde_has_proc_read_iter(const struct proc_dir_entry *pde)
+static inline bool
+pde_has_proc_permanent_read_iter(const struct proc_dir_entry *pde)
 {
-	return pde->flags & PROC_ENTRY_proc_read_iter;
+	return pde->flags & PROC_ENTRY_proc_permanent_read_iter;
 }
 
 static inline bool pde_has_proc_lseek(const struct proc_dir_entry *pde)
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index 390547b992ac..5584b2642f2f 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -664,7 +664,7 @@ static int release_kcore(struct inode *inode, struct file *file)
 
 static const struct proc_ops kcore_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
-	.proc_read_iter	= read_kcore_iter,
+	.proc_permanent_read_iter = read_kcore_iter,
 	.proc_open	= open_kcore,
 	.proc_release	= release_kcore,
 	.proc_lseek	= default_llseek,
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
index 2fc92a13f9f8..28b40ffccdfb 100644
--- a/fs/proc/kmsg.c
+++ b/fs/proc/kmsg.c
@@ -48,7 +48,7 @@ static __poll_t kmsg_poll(struct file *file, poll_table *wait)
 
 static const struct proc_ops kmsg_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
-	.proc_read	= kmsg_read,
+	.proc_permanent_read = kmsg_read,
 	.proc_poll	= kmsg_poll,
 	.proc_open	= kmsg_open,
 	.proc_release	= kmsg_release,
diff --git a/fs/proc/page.c b/fs/proc/page.c
index 6aa99e42ec22..6a68f392453c 100644
--- a/fs/proc/page.c
+++ b/fs/proc/page.c
@@ -133,7 +133,7 @@ static ssize_t kpagecount_read(struct file *file, char __user *buf,
 static const struct proc_ops kpagecount_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_lseek	= mem_lseek,
-	.proc_read	= kpagecount_read,
+	.proc_permanent_read = kpagecount_read,
 };
 
 
@@ -259,7 +259,7 @@ static ssize_t kpageflags_read(struct file *file, char __user *buf,
 static const struct proc_ops kpageflags_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_lseek	= mem_lseek,
-	.proc_read	= kpageflags_read,
+	.proc_permanent_read = kpageflags_read,
 };
 
 #ifdef CONFIG_MEMCG
@@ -271,7 +271,7 @@ static ssize_t kpagecgroup_read(struct file *file, char __user *buf,
 static const struct proc_ops kpagecgroup_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_lseek	= mem_lseek,
-	.proc_read	= kpagecgroup_read,
+	.proc_permanent_read = kpagecgroup_read,
 };
 #endif /* CONFIG_MEMCG */
 
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 17516b064922..4db79f1ab97e 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -18,7 +18,8 @@ enum {
 	/*
 	 * All /proc entries using this ->proc_ops instance are never removed.
 	 * procfs calls into them without pinning them, so they may copy to
-	 * userspace themselves through ->proc_read() or ->proc_read_iter().
+	 * userspace themselves through ->proc_permanent_read() or
+	 * ->proc_permanent_read_iter().
 	 *
 	 * If in doubt, ignore this flag.
 	 */
@@ -28,7 +29,7 @@ enum {
 	PROC_ENTRY_PERMANENT		= 1U << 0,
 #endif
 
-	PROC_ENTRY_proc_read_iter	= 1U << 1,
+	PROC_ENTRY_proc_permanent_read_iter = 1U << 1,
 	PROC_ENTRY_proc_lseek		= 1U << 3,
 	PROC_ENTRY_proc_seq_ops		= 1U << 4,
 	PROC_ENTRY_proc_show		= 1U << 5,
@@ -78,8 +79,9 @@ struct proc_ops {
 	 * These two are only for PROC_ENTRY_PERMANENT entries. Everything else
 	 * uses ->proc_read_buf().
 	 */
-	ssize_t	(*proc_read)(struct file *, char __user *, size_t, loff_t *);
-	ssize_t (*proc_read_iter)(struct kiocb *, struct iov_iter *);
+	ssize_t	(*proc_permanent_read)(struct file *, char __user *, size_t,
+				       loff_t *);
+	ssize_t (*proc_permanent_read_iter)(struct kiocb *, struct iov_iter *);
 	/*
 	 * mandatory unless nonseekable_open() or equivalent is used, procfs
 	 * seeks seq_file entries itself
diff --git a/kernel/profile.c b/kernel/profile.c
index 6f2441dd4a00..72e30bb30396 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -207,7 +207,7 @@ static ssize_t write_profile(struct file *file, char *buf, size_t count,
 
 static const struct proc_ops profile_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
-	.proc_read	= read_profile,
+	.proc_permanent_read = read_profile,
 	.proc_write_buf	= write_profile,
 	.proc_lseek	= default_llseek,
 };

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* [PATCH RFC 5/5] proc: drop the _buf suffix from the new methods
  2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
                     ` (3 preceding siblings ...)
  2026-11-02 15:15   ` [PATCH RFC 4/5] proc: rename ->proc_read() and ->proc_read_iter() Christian Brauner
@ 2026-11-02 15:15   ` Christian Brauner
  4 siblings, 0 replies; 160+ messages in thread
From: Christian Brauner @ 2026-11-02 15:15 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers,
	Christian Brauner (Amutable), Chuck Lever

The _buf suffix told them apart from ->proc_write(), ->proc_read() and
->proc_ioctl(), which copied to and from userspace themselves. Those
are gone now, and the read methods that are left are only for
permanent entries and say so in their names. So drop the suffix.
->proc_write(), ->proc_read() and ->proc_ioctl() are the methods that
work on procfs' buffers from now on, which is what kernfs calls its
read and write as well.

Done with sed. The flags and helpers in fs/proc that go with them are
renamed to match.

No functional changes.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
 arch/alpha/kernel/srm_env.c                        |  2 +-
 arch/arm/kernel/atags_proc.c                       |  2 +-
 arch/arm/mm/alignment.c                            |  2 +-
 arch/m68k/kernel/bootinfo_proc.c                   |  2 +-
 arch/powerpc/kernel/proc_powerpc.c                 |  2 +-
 arch/powerpc/kernel/rtas-proc.c                    | 10 ++++----
 arch/powerpc/kernel/rtas_flash.c                   | 16 ++++++------
 arch/powerpc/kernel/rtasd.c                        |  2 +-
 arch/powerpc/platforms/pseries/lpar.c              |  4 +--
 arch/powerpc/platforms/pseries/lparcfg.c           |  2 +-
 arch/powerpc/platforms/pseries/reconfig.c          |  2 +-
 arch/sh/mm/alignment.c                             |  2 +-
 arch/sparc/kernel/led.c                            |  2 +-
 arch/um/drivers/mconsole_kern.c                    |  2 +-
 arch/um/kernel/exitcode.c                          |  2 +-
 arch/x86/kernel/cpu/mtrr/if.c                      |  4 +--
 arch/xtensa/platforms/iss/simdisk.c                |  4 +--
 drivers/acpi/proc.c                                |  2 +-
 drivers/hwmon/dell-smm-hwmon.c                     |  2 +-
 drivers/macintosh/via-pmu.c                        |  2 +-
 drivers/net/wireless/intel/ipw2x00/libipw_module.c |  2 +-
 drivers/pci/proc.c                                 |  6 ++---
 drivers/platform/x86/lenovo/thinkpad_acpi.c        |  2 +-
 drivers/platform/x86/toshiba_acpi.c                |  8 +++---
 drivers/pnp/isapnp/proc.c                          |  2 +-
 drivers/pnp/pnpbios/proc.c                         |  2 +-
 drivers/s390/block/dasd_proc.c                     |  2 +-
 drivers/s390/cio/blacklist.c                       |  2 +-
 drivers/s390/cio/css.c                             |  2 +-
 drivers/scsi/esas2r/esas2r_main.c                  |  2 +-
 drivers/scsi/scsi_devinfo.c                        |  2 +-
 drivers/scsi/scsi_proc.c                           |  4 +--
 drivers/scsi/sg.c                                  |  4 +--
 drivers/tty/sysrq.c                                |  2 +-
 drivers/usb/gadget/function/rndis.c                |  2 +-
 drivers/video/fbdev/via/viafbdev.c                 | 14 +++++-----
 drivers/zorro/proc.c                               |  2 +-
 fs/jfs/jfs_debug.c                                 |  2 +-
 fs/lockd/procfs.c                                  |  4 +--
 fs/ntfs3/super.c                                   |  2 +-
 fs/proc/generic.c                                  | 12 ++++-----
 fs/proc/inode.c                                    | 30 +++++++++++-----------
 fs/proc/internal.h                                 | 12 ++++-----
 fs/proc/proc_net.c                                 |  4 +--
 fs/proc/vmcore.c                                   |  2 +-
 fs/smb/client/cifs_debug.c                         | 16 ++++++------
 fs/smb/client/dfs_cache.c                          |  2 +-
 include/linux/proc_fs.h                            | 22 ++++++++--------
 kernel/configs.c                                   |  2 +-
 kernel/irq/proc.c                                  |  6 ++---
 kernel/latencytop.c                                |  2 +-
 kernel/locking/lockdep_proc.c                      |  2 +-
 kernel/profile.c                                   |  2 +-
 kernel/sched/psi.c                                 |  8 +++---
 lib/dynamic_debug.c                                |  2 +-
 mm/alloc_tag.c                                     |  2 +-
 net/atm/proc.c                                     |  2 +-
 net/core/pktgen.c                                  |  6 ++---
 net/netfilter/xt_recent.c                          |  2 +-
 net/sunrpc/auth_gss/svcauth_gss.c                  |  6 ++---
 net/sunrpc/cache.c                                 |  8 +++---
 samples/kfifo/bytestream-example.c                 |  4 +--
 samples/kfifo/inttype-example.c                    |  4 +--
 samples/kfifo/record-example.c                     |  4 +--
 sound/core/info.c                                  |  6 ++---
 65 files changed, 152 insertions(+), 152 deletions(-)

diff --git a/arch/alpha/kernel/srm_env.c b/arch/alpha/kernel/srm_env.c
index 6a1742b09a82..2817bf78742a 100644
--- a/arch/alpha/kernel/srm_env.c
+++ b/arch/alpha/kernel/srm_env.c
@@ -110,7 +110,7 @@ static ssize_t srm_env_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops srm_env_proc_ops = {
 	.proc_show	= srm_env_proc_show,
-	.proc_write_buf	= srm_env_proc_write,
+	.proc_write	= srm_env_proc_write,
 };
 
 static int __init
diff --git a/arch/arm/kernel/atags_proc.c b/arch/arm/kernel/atags_proc.c
index ff28bf24dd30..ff6fb346d3ee 100644
--- a/arch/arm/kernel/atags_proc.c
+++ b/arch/arm/kernel/atags_proc.c
@@ -18,7 +18,7 @@ static ssize_t atags_read(struct file *file, char *buf,
 }
 
 static const struct proc_ops atags_proc_ops = {
-	.proc_read_buf	= atags_read,
+	.proc_read	= atags_read,
 	.proc_lseek	= default_llseek,
 };
 
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 11ceefdfa388..2d27b8bbb3b3 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -159,7 +159,7 @@ static ssize_t alignment_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops alignment_proc_ops = {
 	.proc_show	= alignment_proc_show,
-	.proc_write_buf	= alignment_proc_write,
+	.proc_write	= alignment_proc_write,
 };
 #endif /* CONFIG_PROC_FS */
 
diff --git a/arch/m68k/kernel/bootinfo_proc.c b/arch/m68k/kernel/bootinfo_proc.c
index 4936cf44e86a..f65555ddb7d3 100644
--- a/arch/m68k/kernel/bootinfo_proc.c
+++ b/arch/m68k/kernel/bootinfo_proc.c
@@ -27,7 +27,7 @@ static ssize_t bootinfo_read(struct file *file, char *buf,
 }
 
 static const struct proc_ops bootinfo_proc_ops = {
-	.proc_read_buf	= bootinfo_read,
+	.proc_read	= bootinfo_read,
 	.proc_lseek	= default_llseek,
 };
 
diff --git a/arch/powerpc/kernel/proc_powerpc.c b/arch/powerpc/kernel/proc_powerpc.c
index 46bc914d74b9..ca43a5dfe623 100644
--- a/arch/powerpc/kernel/proc_powerpc.c
+++ b/arch/powerpc/kernel/proc_powerpc.c
@@ -43,7 +43,7 @@ static int page_map_mmap( struct file *file, struct vm_area_struct *vma )
 
 static const struct proc_ops page_map_proc_ops = {
 	.proc_lseek	= page_map_seek,
-	.proc_read_buf	= page_map_read,
+	.proc_read	= page_map_read,
 	.proc_mmap	= page_map_mmap,
 };
 
diff --git a/arch/powerpc/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
index 9598866f11cb..76eddbb8d94d 100644
--- a/arch/powerpc/kernel/rtas-proc.c
+++ b/arch/powerpc/kernel/rtas-proc.c
@@ -156,27 +156,27 @@ static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v);
 
 static const struct proc_ops ppc_rtas_poweron_proc_ops = {
 	.proc_show	= ppc_rtas_poweron_show,
-	.proc_write_buf	= ppc_rtas_poweron_write,
+	.proc_write	= ppc_rtas_poweron_write,
 };
 
 static const struct proc_ops ppc_rtas_progress_proc_ops = {
 	.proc_show	= ppc_rtas_progress_show,
-	.proc_write_buf	= ppc_rtas_progress_write,
+	.proc_write	= ppc_rtas_progress_write,
 };
 
 static const struct proc_ops ppc_rtas_clock_proc_ops = {
 	.proc_show	= ppc_rtas_clock_show,
-	.proc_write_buf	= ppc_rtas_clock_write,
+	.proc_write	= ppc_rtas_clock_write,
 };
 
 static const struct proc_ops ppc_rtas_tone_freq_proc_ops = {
 	.proc_show	= ppc_rtas_tone_freq_show,
-	.proc_write_buf	= ppc_rtas_tone_freq_write,
+	.proc_write	= ppc_rtas_tone_freq_write,
 };
 
 static const struct proc_ops ppc_rtas_tone_volume_proc_ops = {
 	.proc_show	= ppc_rtas_tone_volume_show,
-	.proc_write_buf	= ppc_rtas_tone_volume_write,
+	.proc_write	= ppc_rtas_tone_volume_write,
 };
 
 static int ppc_rtas_find_all_sensors(void);
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index 5ebaee7683ef..e8ccfa614b19 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -623,8 +623,8 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
 		.status		= &rtas_update_flash_data.status,
 		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
-		.ops.proc_read_buf = rtas_flash_read_msg,
-		.ops.proc_write_buf = rtas_flash_write,
+		.ops.proc_read = rtas_flash_read_msg,
+		.ops.proc_write = rtas_flash_write,
 		.ops.proc_write_size = RTAS_BLK_SIZE,
 		.ops.proc_release = rtas_flash_release,
 		.ops.proc_lseek	= default_llseek,
@@ -634,8 +634,8 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_UPDATE_FLASH_64_AND_REBOOT,
 		.status		= &rtas_update_flash_data.status,
 		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
-		.ops.proc_read_buf = rtas_flash_read_num,
-		.ops.proc_write_buf = rtas_flash_write,
+		.ops.proc_read = rtas_flash_read_num,
+		.ops.proc_write = rtas_flash_write,
 		.ops.proc_write_size = RTAS_BLK_SIZE,
 		.ops.proc_release = rtas_flash_release,
 		.ops.proc_lseek	= default_llseek,
@@ -645,8 +645,8 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.handle		= RTAS_FN_IBM_VALIDATE_FLASH_IMAGE,
 		.status		= &rtas_validate_flash_data.status,
 		.ops.proc_flags	= PROC_ENTRY_SHORT_WRITES,
-		.ops.proc_read_buf = validate_flash_read,
-		.ops.proc_write_buf = validate_flash_write,
+		.ops.proc_read = validate_flash_read,
+		.ops.proc_write = validate_flash_write,
 		.ops.proc_write_size = VALIDATE_BUF_SIZE,
 		.ops.proc_release = validate_flash_release,
 		.ops.proc_lseek	= default_llseek,
@@ -655,8 +655,8 @@ static const struct rtas_flash_file rtas_flash_files[] = {
 		.filename	= "powerpc/rtas/" MANAGE_FLASH_NAME,
 		.handle		= RTAS_FN_IBM_MANAGE_FLASH_IMAGE,
 		.status		= &rtas_manage_flash_data.status,
-		.ops.proc_read_buf = manage_flash_read,
-		.ops.proc_write_buf = manage_flash_write,
+		.ops.proc_read = manage_flash_read,
+		.ops.proc_write = manage_flash_write,
 		.ops.proc_lseek	= default_llseek,
 	}
 };
diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index 360ed2e30fd5..a6e3091c793e 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -350,7 +350,7 @@ static __poll_t rtas_log_poll(struct file *file, poll_table * wait)
 }
 
 static const struct proc_ops rtas_log_proc_ops = {
-	.proc_read_buf	= rtas_log_read,
+	.proc_read	= rtas_log_read,
 	.proc_poll	= rtas_log_poll,
 	.proc_open	= rtas_log_open,
 	.proc_release	= rtas_log_release,
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index dfd027cc9047..8147129ecd59 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -585,7 +585,7 @@ static int vcpudispatch_stats_display(struct seq_file *p, void *v)
 
 static const struct proc_ops vcpudispatch_stats_proc_ops = {
 	.proc_show	= vcpudispatch_stats_display,
-	.proc_write_buf	= vcpudispatch_stats_write,
+	.proc_write	= vcpudispatch_stats_write,
 };
 
 static ssize_t vcpudispatch_stats_freq_write(struct file *file,
@@ -620,7 +620,7 @@ static int vcpudispatch_stats_freq_display(struct seq_file *p, void *v)
 
 static const struct proc_ops vcpudispatch_stats_freq_proc_ops = {
 	.proc_show	= vcpudispatch_stats_freq_display,
-	.proc_write_buf	= vcpudispatch_stats_freq_write,
+	.proc_write	= vcpudispatch_stats_freq_write,
 };
 
 static int __init vcpudispatch_stats_procfs_init(void)
diff --git a/arch/powerpc/platforms/pseries/lparcfg.c b/arch/powerpc/platforms/pseries/lparcfg.c
index 43af7f820fab..e63a2701a319 100644
--- a/arch/powerpc/platforms/pseries/lparcfg.c
+++ b/arch/powerpc/platforms/pseries/lparcfg.c
@@ -798,7 +798,7 @@ static int lparcfg_data(struct seq_file *m, void *v)
 }
 
 static const struct proc_ops lparcfg_proc_ops = {
-	.proc_write_buf	= lparcfg_write,
+	.proc_write	= lparcfg_write,
 	.proc_show	= lparcfg_data,
 };
 
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c
index f406e826a03e..bbbd8503e0d2 100644
--- a/arch/powerpc/platforms/pseries/reconfig.c
+++ b/arch/powerpc/platforms/pseries/reconfig.c
@@ -390,7 +390,7 @@ static ssize_t ofdt_write(struct file *file, char *kbuf, size_t count,
 }
 
 static const struct proc_ops ofdt_proc_ops = {
-	.proc_write_buf	= ofdt_write,
+	.proc_write	= ofdt_write,
 	/* what memdup_user_nul() could copy in */
 	.proc_write_size = KMALLOC_MAX_SIZE - 1,
 	.proc_lseek	= noop_llseek,
diff --git a/arch/sh/mm/alignment.c b/arch/sh/mm/alignment.c
index d832249a8e18..d76596820a6f 100644
--- a/arch/sh/mm/alignment.c
+++ b/arch/sh/mm/alignment.c
@@ -148,7 +148,7 @@ static ssize_t alignment_proc_write(struct file *file,
 
 static const struct proc_ops alignment_proc_ops = {
 	.proc_show	= alignment_proc_show,
-	.proc_write_buf	= alignment_proc_write,
+	.proc_write	= alignment_proc_write,
 };
 
 /*
diff --git a/arch/sparc/kernel/led.c b/arch/sparc/kernel/led.c
index 01449b056cdd..32fca52e198d 100644
--- a/arch/sparc/kernel/led.c
+++ b/arch/sparc/kernel/led.c
@@ -95,7 +95,7 @@ static ssize_t led_proc_write(struct file *file, char *buf,
 
 static const struct proc_ops led_proc_ops = {
 	.proc_show	= led_proc_show,
-	.proc_write_buf	= led_proc_write,
+	.proc_write	= led_proc_write,
 };
 #endif
 
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index a4370470286f..882c6f30a3c2 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -776,7 +776,7 @@ static ssize_t mconsole_proc_write(struct file *file,
 }
 
 static const struct proc_ops mconsole_proc_ops = {
-	.proc_write_buf	= mconsole_proc_write,
+	.proc_write	= mconsole_proc_write,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c
index 8f76e3b20c18..15b114fbaa05 100644
--- a/arch/um/kernel/exitcode.c
+++ b/arch/um/kernel/exitcode.c
@@ -47,7 +47,7 @@ static ssize_t exitcode_proc_write(struct file *file,
 
 static const struct proc_ops exitcode_proc_ops = {
 	.proc_show	= exitcode_proc_show,
-	.proc_write_buf	= exitcode_proc_write,
+	.proc_write	= exitcode_proc_write,
 };
 
 static int make_proc_exitcode(void)
diff --git a/arch/x86/kernel/cpu/mtrr/if.c b/arch/x86/kernel/cpu/mtrr/if.c
index 9c33c0428dfe..e0f0257cba32 100644
--- a/arch/x86/kernel/cpu/mtrr/if.c
+++ b/arch/x86/kernel/cpu/mtrr/if.c
@@ -410,8 +410,8 @@ static const struct proc_ioctl_arg mtrr_ioctl_args[] = {
 static const struct proc_ops mtrr_proc_ops = {
 	.proc_show		= mtrr_seq_show,
 	.proc_open		= mtrr_open,
-	.proc_write_buf		= mtrr_write,
-	.proc_ioctl_buf		= mtrr_ioctl,
+	.proc_write		= mtrr_write,
+	.proc_ioctl		= mtrr_ioctl,
 	.proc_ioctl_args	= mtrr_ioctl_args,
 	.proc_nr_ioctl_args	= ARRAY_SIZE(mtrr_ioctl_args),
 	.proc_release		= mtrr_close,
diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index b597d8642e8a..33108e043cc9 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -280,8 +280,8 @@ static ssize_t proc_write_simdisk(struct file *file, char *buf,
 }
 
 static const struct proc_ops simdisk_proc_ops = {
-	.proc_read_buf	= proc_read_simdisk,
-	.proc_write_buf	= proc_write_simdisk,
+	.proc_read	= proc_read_simdisk,
+	.proc_write	= proc_write_simdisk,
 	.proc_write_size = PAGE_SIZE,
 	.proc_lseek	= default_llseek,
 };
diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c
index feb8283fd855..4b5ca9300e9d 100644
--- a/drivers/acpi/proc.c
+++ b/drivers/acpi/proc.c
@@ -122,7 +122,7 @@ acpi_system_write_wakeup_device(struct file *file,
 
 static const struct proc_ops acpi_system_wakeup_device_proc_ops = {
 	.proc_show	= acpi_system_wakeup_device_seq_show,
-	.proc_write_buf	= acpi_system_write_wakeup_device,
+	.proc_write	= acpi_system_write_wakeup_device,
 };
 
 void __init acpi_sleep_proc_init(void)
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index 67b8c6e5752f..529386faa6a5 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -694,7 +694,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
 
 static const struct proc_ops i8k_proc_ops = {
 	.proc_show	= i8k_proc_show,
-	.proc_ioctl_buf	= i8k_ioctl,
+	.proc_ioctl	= i8k_ioctl,
 	.proc_ioctl_args = i8k_ioctl_args,
 	.proc_nr_ioctl_args = ARRAY_SIZE(i8k_ioctl_args),
 };
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 079c085be5d2..397a1c68cb04 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -957,7 +957,7 @@ static ssize_t pmu_options_proc_write(struct file *file,
 
 static const struct proc_ops pmu_options_proc_ops = {
 	.proc_show	= pmu_options_proc_show,
-	.proc_write_buf	= pmu_options_proc_write,
+	.proc_write	= pmu_options_proc_write,
 };
 #endif
 
diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index 08cc0d7f57c1..e4061a4e2254 100644
--- a/drivers/net/wireless/intel/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
@@ -235,7 +235,7 @@ static ssize_t debug_level_proc_write(struct file *file,
 
 static const struct proc_ops debug_level_proc_ops = {
 	.proc_show	= debug_level_proc_show,
-	.proc_write_buf	= debug_level_proc_write,
+	.proc_write	= debug_level_proc_write,
 };
 #endif				/* CONFIG_LIBIPW_DEBUG */
 
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 6b0cb5bcbea3..bae43df6e4ac 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -331,10 +331,10 @@ static const struct proc_ioctl_arg proc_bus_pci_ioctl_args[] = {
 static const struct proc_ops proc_bus_pci_ops = {
 	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_lseek	= proc_bus_pci_lseek,
-	.proc_read_buf	= proc_bus_pci_read,
-	.proc_write_buf	= proc_bus_pci_write,
+	.proc_read	= proc_bus_pci_read,
+	.proc_write	= proc_bus_pci_write,
 	.proc_write_size = PCI_CFG_SPACE_EXP_SIZE,
-	.proc_ioctl_buf	= proc_bus_pci_ioctl,
+	.proc_ioctl	= proc_bus_pci_ioctl,
 	.proc_ioctl_args = proc_bus_pci_ioctl_args,
 	.proc_nr_ioctl_args = ARRAY_SIZE(proc_bus_pci_ioctl_args),
 #ifdef HAVE_PCI_MMAP
diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c
index b4f253da9536..72d26e7438c9 100644
--- a/drivers/platform/x86/lenovo/thinkpad_acpi.c
+++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c
@@ -899,7 +899,7 @@ static ssize_t dispatch_proc_write(struct file *file,
 
 static const struct proc_ops dispatch_proc_ops = {
 	.proc_show	= dispatch_proc_show,
-	.proc_write_buf	= dispatch_proc_write,
+	.proc_write	= dispatch_proc_write,
 };
 
 /****************************************************************************
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index 895e321ada03..98c91463baf0 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1504,7 +1504,7 @@ static ssize_t lcd_proc_write(struct file *file, char *buf,
 
 static const struct proc_ops lcd_proc_ops = {
 	.proc_show	= lcd_proc_show,
-	.proc_write_buf	= lcd_proc_write,
+	.proc_write	= lcd_proc_write,
 };
 
 /* Video-Out */
@@ -1592,7 +1592,7 @@ static ssize_t video_proc_write(struct file *file, char *buf,
 
 static const struct proc_ops video_proc_ops = {
 	.proc_show	= video_proc_show,
-	.proc_write_buf	= video_proc_write,
+	.proc_write	= video_proc_write,
 };
 
 /* Fan status */
@@ -1660,7 +1660,7 @@ static ssize_t fan_proc_write(struct file *file, char *buf,
 
 static const struct proc_ops fan_proc_ops = {
 	.proc_show	= fan_proc_show,
-	.proc_write_buf	= fan_proc_write,
+	.proc_write	= fan_proc_write,
 };
 
 /* Fan RPM */
@@ -1718,7 +1718,7 @@ static ssize_t keys_proc_write(struct file *file, char *buf,
 
 static const struct proc_ops keys_proc_ops = {
 	.proc_show	= keys_proc_show,
-	.proc_write_buf	= keys_proc_write,
+	.proc_write	= keys_proc_write,
 };
 
 static int __maybe_unused version_proc_show(struct seq_file *m, void *v)
diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c
index 152fd5bfbc06..c6198bf3fa9c 100644
--- a/drivers/pnp/isapnp/proc.c
+++ b/drivers/pnp/isapnp/proc.c
@@ -43,7 +43,7 @@ static ssize_t isapnp_proc_bus_read(struct file *file, char *buf,
 
 static const struct proc_ops isapnp_proc_bus_proc_ops = {
 	.proc_lseek	= isapnp_proc_bus_lseek,
-	.proc_read_buf	= isapnp_proc_bus_read,
+	.proc_read	= isapnp_proc_bus_read,
 };
 
 static int isapnp_proc_attach_device(struct pnp_dev *dev)
diff --git a/drivers/pnp/pnpbios/proc.c b/drivers/pnp/pnpbios/proc.c
index 00163aedd738..6441212f7fe7 100644
--- a/drivers/pnp/pnpbios/proc.c
+++ b/drivers/pnp/pnpbios/proc.c
@@ -204,7 +204,7 @@ out:
 
 static const struct proc_ops pnpbios_proc_ops = {
 	.proc_show	= pnpbios_proc_show,
-	.proc_write_buf	= pnpbios_proc_write,
+	.proc_write	= pnpbios_proc_write,
 };
 
 int pnpbios_interface_attach_device(struct pnp_bios_node *node)
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 6c3bbb7dfe85..002e2d2d498c 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -307,7 +307,7 @@ out_error:
 
 static const struct proc_ops dasd_stats_proc_ops = {
 	.proc_show	= dasd_stats_proc_show,
-	.proc_write_buf	= dasd_stats_proc_write,
+	.proc_write	= dasd_stats_proc_write,
 };
 
 /*
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 1ec7915be6b3..d8a22c83626b 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -386,7 +386,7 @@ static const struct proc_ops cio_ignore_proc_ops = {
 	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
 	.proc_seq_ops	= &cio_ignore_proc_seq_ops,
 	.proc_state_size = sizeof(struct ccwdev_iter),
-	.proc_write_buf	= cio_ignore_write,
+	.proc_write	= cio_ignore_write,
 	.proc_write_size = SZ_64K,
 };
 
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index efd3df3e90ba..1813074c8c72 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -1292,7 +1292,7 @@ static ssize_t cio_settle_write(struct file *file, char *buf,
 
 static const struct proc_ops cio_settle_proc_ops = {
 	.proc_open	= nonseekable_open,
-	.proc_write_buf	= cio_settle_write,
+	.proc_write	= cio_settle_write,
 };
 
 static int __init cio_settle_init(void)
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index 720be2a0907d..5effad56bb39 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -653,7 +653,7 @@ static const struct proc_ioctl_arg esas2r_proc_ioctl_args[] = {
 
 static const struct proc_ops esas2r_proc_ops = {
 	.proc_lseek		= default_llseek,
-	.proc_ioctl_buf		= esas2r_proc_ioctl_buf,
+	.proc_ioctl		= esas2r_proc_ioctl_buf,
 	.proc_ioctl_args	= esas2r_proc_ioctl_args,
 	.proc_nr_ioctl_args	= ARRAY_SIZE(esas2r_proc_ioctl_args),
 };
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index d9093851b23e..6667661ca62d 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -690,7 +690,7 @@ static ssize_t proc_scsi_devinfo_write(struct file *file,
 
 static const struct proc_ops scsi_devinfo_proc_ops = {
 	.proc_seq_ops	= &scsi_devinfo_seq_ops,
-	.proc_write_buf	= proc_scsi_devinfo_write,
+	.proc_write	= proc_scsi_devinfo_write,
 	.proc_write_size = PAGE_SIZE,
 };
 #endif /* CONFIG_SCSI_PROC_FS */
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index 5962e671cc83..efc393fe9047 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -130,7 +130,7 @@ EXPORT_SYMBOL_GPL(scsi_template_proc_dir);
 static const struct proc_ops proc_scsi_ops = {
 	.proc_show	= proc_scsi_show,
 	.proc_open	= proc_scsi_host_open,
-	.proc_write_buf	= proc_scsi_host_write
+	.proc_write	= proc_scsi_host_write
 };
 
 /**
@@ -495,7 +495,7 @@ static const struct seq_operations scsi_seq_ops = {
 
 static const struct proc_ops scsi_scsi_proc_ops = {
 	.proc_seq_ops	= &scsi_seq_ops,
-	.proc_write_buf	= proc_scsi_write,
+	.proc_write	= proc_scsi_write,
 	.proc_write_size = PAGE_SIZE,
 };
 
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 87569e302f26..36d046f05c64 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -2291,14 +2291,14 @@ static ssize_t sg_proc_write_adio(struct file *filp, char *buffer,
 			          size_t count, loff_t *off);
 static const struct proc_ops adio_proc_ops = {
 	.proc_show	= sg_proc_seq_show_int,
-	.proc_write_buf	= sg_proc_write_adio,
+	.proc_write	= sg_proc_write_adio,
 };
 
 static ssize_t sg_proc_write_dressz(struct file *filp, 
 		char *buffer, size_t count, loff_t *off);
 static const struct proc_ops dressz_proc_ops = {
 	.proc_show	= sg_proc_seq_show_int,
-	.proc_write_buf	= sg_proc_write_dressz,
+	.proc_write	= sg_proc_write_dressz,
 };
 
 static int sg_proc_seq_show_version(struct seq_file *s, void *v);
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 99363e1b59ed..d6000c9c3cbe 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -1222,7 +1222,7 @@ static ssize_t write_sysrq_trigger(struct file *file, char *buf,
 }
 
 static const struct proc_ops sysrq_trigger_proc_ops = {
-	.proc_write_buf	= write_sysrq_trigger,
+	.proc_write	= write_sysrq_trigger,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index e05e0c6ae8fb..ef70d7bc1f0d 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -1184,7 +1184,7 @@ static ssize_t rndis_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops rndis_proc_ops = {
 	.proc_show	= rndis_proc_show,
-	.proc_write_buf	= rndis_proc_write,
+	.proc_write	= rndis_proc_write,
 };
 
 #define	NAME_TEMPLATE "driver/rndis-%03d"
diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c
index 94b303006915..7732a5eea857 100644
--- a/drivers/video/fbdev/via/viafbdev.c
+++ b/drivers/video/fbdev/via/viafbdev.c
@@ -1168,7 +1168,7 @@ static ssize_t viafb_dvp0_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dvp0_proc_ops = {
 	.proc_show	= viafb_dvp0_proc_show,
-	.proc_write_buf	= viafb_dvp0_proc_write,
+	.proc_write	= viafb_dvp0_proc_write,
 };
 
 static int viafb_dvp1_proc_show(struct seq_file *m, void *v)
@@ -1223,7 +1223,7 @@ static ssize_t viafb_dvp1_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dvp1_proc_ops = {
 	.proc_show	= viafb_dvp1_proc_show,
-	.proc_write_buf	= viafb_dvp1_proc_write,
+	.proc_write	= viafb_dvp1_proc_write,
 };
 
 static int viafb_dfph_proc_show(struct seq_file *m, void *v)
@@ -1249,7 +1249,7 @@ static ssize_t viafb_dfph_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dfph_proc_ops = {
 	.proc_show	= viafb_dfph_proc_show,
-	.proc_write_buf	= viafb_dfph_proc_write,
+	.proc_write	= viafb_dfph_proc_write,
 };
 
 static int viafb_dfpl_proc_show(struct seq_file *m, void *v)
@@ -1275,7 +1275,7 @@ static ssize_t viafb_dfpl_proc_write(struct file *file,
 
 static const struct proc_ops viafb_dfpl_proc_ops = {
 	.proc_show	= viafb_dfpl_proc_show,
-	.proc_write_buf	= viafb_dfpl_proc_write,
+	.proc_write	= viafb_dfpl_proc_write,
 };
 
 static int viafb_vt1636_proc_show(struct seq_file *m, void *v)
@@ -1401,7 +1401,7 @@ static ssize_t viafb_vt1636_proc_write(struct file *file,
 
 static const struct proc_ops viafb_vt1636_proc_ops = {
 	.proc_show	= viafb_vt1636_proc_show,
-	.proc_write_buf	= viafb_vt1636_proc_write,
+	.proc_write	= viafb_vt1636_proc_write,
 };
 
 #endif /* CONFIG_FB_VIA_DIRECT_PROCFS */
@@ -1469,7 +1469,7 @@ static ssize_t viafb_iga1_odev_proc_write(struct file *file,
 
 static const struct proc_ops viafb_iga1_odev_proc_ops = {
 	.proc_show	= viafb_iga1_odev_proc_show,
-	.proc_write_buf	= viafb_iga1_odev_proc_write,
+	.proc_write	= viafb_iga1_odev_proc_write,
 };
 
 static int viafb_iga2_odev_proc_show(struct seq_file *m, void *v)
@@ -1500,7 +1500,7 @@ static ssize_t viafb_iga2_odev_proc_write(struct file *file,
 
 static const struct proc_ops viafb_iga2_odev_proc_ops = {
 	.proc_show	= viafb_iga2_odev_proc_show,
-	.proc_write_buf	= viafb_iga2_odev_proc_write,
+	.proc_write	= viafb_iga2_odev_proc_write,
 };
 
 #define IS_VT1636(lvds_chip)	((lvds_chip).lvds_chip_name == VT1636_LVDS)
diff --git a/drivers/zorro/proc.c b/drivers/zorro/proc.c
index 4e4ba04752ea..af6a2d1f059d 100644
--- a/drivers/zorro/proc.c
+++ b/drivers/zorro/proc.c
@@ -57,7 +57,7 @@ proc_bus_zorro_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
 
 static const struct proc_ops bus_zorro_proc_ops = {
 	.proc_lseek	= proc_bus_zorro_lseek,
-	.proc_read_buf	= proc_bus_zorro_read,
+	.proc_read	= proc_bus_zorro_read,
 };
 
 static void * zorro_seq_start(struct seq_file *m, loff_t *pos)
diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c
index e1c0211a6a32..a1fec8e51c7d 100644
--- a/fs/jfs/jfs_debug.c
+++ b/fs/jfs/jfs_debug.c
@@ -37,7 +37,7 @@ static ssize_t jfs_loglevel_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops jfs_loglevel_proc_ops = {
 	.proc_show	= jfs_loglevel_proc_show,
-	.proc_write_buf	= jfs_loglevel_proc_write,
+	.proc_write	= jfs_loglevel_proc_write,
 };
 #endif
 
diff --git a/fs/lockd/procfs.c b/fs/lockd/procfs.c
index 34a7c3705bb6..0b6d404e83c3 100644
--- a/fs/lockd/procfs.c
+++ b/fs/lockd/procfs.c
@@ -55,8 +55,8 @@ nlm_end_grace_read(struct file *file, char *buf, size_t size,
 }
 
 static const struct proc_ops lockd_end_grace_proc_ops = {
-	.proc_write_buf	= nlm_end_grace_write,
-	.proc_read_buf	= nlm_end_grace_read,
+	.proc_write	= nlm_end_grace_write,
+	.proc_read	= nlm_end_grace_read,
 	.proc_lseek	= default_llseek,
 };
 
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index fca73d2766c0..0a0004936a22 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -566,7 +566,7 @@ static const struct proc_ops ntfs3_volinfo_fops = {
 
 static const struct proc_ops ntfs3_label_fops = {
 	.proc_show = ntfs3_label_show,
-	.proc_write_buf = ntfs3_label_write,
+	.proc_write = ntfs3_label_write,
 };
 
 static void ntfs_create_procdir(struct super_block *sb)
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 4bcec5a29b5f..693c0867889c 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -474,28 +474,28 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 		if (proc_ops->proc_lseek)
 			pde->flags |= PROC_ENTRY_proc_lseek;
 	}
-	if (proc_ops->proc_write_buf) {
-		pde->flags |= PROC_ENTRY_proc_write_buf;
+	if (proc_ops->proc_write) {
+		pde->flags |= PROC_ENTRY_proc_write;
 		pde->write_size = proc_ops->proc_write_size ?: PAGE_SIZE - 1;
 		if (proc_ops->proc_flags & PROC_ENTRY_SHORT_WRITES)
 			pde->flags |= PROC_ENTRY_SHORT_WRITES;
 	}
-	if (proc_ops->proc_read_buf) {
+	if (proc_ops->proc_read) {
 		if (WARN_ON_ONCE(pde_is_seq_file(pde) ||
 				 proc_ops->proc_permanent_read ||
 				 proc_ops->proc_permanent_read_iter))
 			return -EINVAL;
-		pde->flags |= PROC_ENTRY_proc_read_buf;
+		pde->flags |= PROC_ENTRY_proc_read;
 		pde->read_size = proc_ops->proc_read_size ?: PAGE_SIZE;
 		if (proc_ops->proc_flags & PROC_ENTRY_FULL_READS)
 			pde->flags |= PROC_ENTRY_FULL_READS;
 	}
-	if (proc_ops->proc_ioctl_buf) {
+	if (proc_ops->proc_ioctl) {
 		/* the table says what procfs copies, see pde_ioctl_arg() */
 		if (WARN_ON_ONCE(!proc_ops->proc_ioctl_args ||
 				 !proc_ops->proc_nr_ioctl_args))
 			return -EINVAL;
-		pde->flags |= PROC_ENTRY_proc_ioctl_buf;
+		pde->flags |= PROC_ENTRY_proc_ioctl;
 	} else if (WARN_ON_ONCE(proc_ops->proc_ioctl_args ||
 				proc_ops->proc_nr_ioctl_args)) {
 		return -EINVAL;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 16d8f00bfc26..3b38672c0a0c 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -408,7 +408,7 @@ static loff_t proc_reg_llseek(struct file *file, loff_t offset, int whence)
  * unpinning the entry. A reader that faults on its buffer then doesn't hold
  * up remove_proc_entry().
  */
-static ssize_t pde_read_buf(struct proc_dir_entry *pde, struct kiocb *iocb,
+static ssize_t pde_read(struct proc_dir_entry *pde, struct kiocb *iocb,
 			    struct iov_iter *iter)
 {
 	struct file *file = iocb->ki_filp;
@@ -435,7 +435,7 @@ static ssize_t pde_read_buf(struct proc_dir_entry *pde, struct kiocb *iocb,
 			rv = -EIO;
 			break;
 		}
-		rv = pde->proc_ops->proc_read_buf(file, buf, count, &pos);
+		rv = pde->proc_ops->proc_read(file, buf, count, &pos);
 		pde_unpin(pde);
 		if (rv <= 0)
 			break;
@@ -461,8 +461,8 @@ static ssize_t proc_reg_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
 	struct proc_dir_entry *pde = PDE(file_inode(iocb->ki_filp));
 
-	if (pde_has_proc_read_buf(pde))
-		return pde_read_buf(pde, iocb, iter);
+	if (pde_has_proc_read(pde))
+		return pde_read(pde, iocb, iter);
 	/* only permanent entries have one, see pde_set_flags() */
 	return pde->proc_ops->proc_permanent_read_iter(iocb, iter);
 }
@@ -484,7 +484,7 @@ static ssize_t proc_reg_read(struct file *file, char __user *buf, size_t count,
  * procfs copies in what is written before it pins the entry. A writer that
  * faults on its buffer then doesn't hold up remove_proc_entry().
  */
-static ssize_t pde_write_buf(struct proc_dir_entry *pde, struct file *file,
+static ssize_t pde_write(struct proc_dir_entry *pde, struct file *file,
 			     const char __user *ubuf, size_t count, loff_t *ppos)
 {
 	char *buf __free(kvfree) = NULL;
@@ -506,7 +506,7 @@ static ssize_t pde_write_buf(struct proc_dir_entry *pde, struct file *file,
 
 	if (!pde_pin(pde))
 		return -EIO;
-	rv = pde->proc_ops->proc_write_buf(file, buf, count, ppos);
+	rv = pde->proc_ops->proc_write(file, buf, count, ppos);
 	pde_unpin(pde);
 	return rv;
 }
@@ -515,8 +515,8 @@ static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
 
-	if (pde_has_proc_write_buf(pde))
-		return pde_write_buf(pde, file, buf, count, ppos);
+	if (pde_has_proc_write(pde))
+		return pde_write(pde, file, buf, count, ppos);
 	return -EIO;
 }
 
@@ -565,7 +565,7 @@ static bool pde_ioctl_arg(struct proc_dir_entry *pde, unsigned int cmd,
  * out after unpinning it. An ioctl that faults on its argument then doesn't
  * hold up remove_proc_entry().
  */
-static long pde_ioctl_buf(struct proc_dir_entry *pde, struct file *file,
+static long pde_ioctl(struct proc_dir_entry *pde, struct file *file,
 			  unsigned int cmd, void __user *uarg, unsigned long arg)
 {
 	void *buf __free(kvfree) = NULL;
@@ -586,7 +586,7 @@ static long pde_ioctl_buf(struct proc_dir_entry *pde, struct file *file,
 
 	if (!pde_pin(pde))
 		return -ENOTTY;
-	rv = pde->proc_ops->proc_ioctl_buf(file, cmd, buf ?: (void *)arg);
+	rv = pde->proc_ops->proc_ioctl(file, cmd, buf ?: (void *)arg);
 	pde_unpin(pde);
 	if (rv >= 0 && copy_to_user(uarg, buf, a.out))
 		return -EFAULT;
@@ -597,8 +597,8 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
 
-	if (pde_has_proc_ioctl_buf(pde))
-		return pde_ioctl_buf(pde, file, cmd, (void __user *)arg, arg);
+	if (pde_has_proc_ioctl(pde))
+		return pde_ioctl(pde, file, cmd, (void __user *)arg, arg);
 	return -ENOTTY;
 }
 
@@ -607,8 +607,8 @@ static long proc_reg_compat_ioctl(struct file *file, unsigned int cmd, unsigned
 {
 	struct proc_dir_entry *pde = PDE(file_inode(file));
 
-	if (pde_has_proc_ioctl_buf(pde))
-		return pde_ioctl_buf(pde, file, cmd, compat_ptr(arg), arg);
+	if (pde_has_proc_ioctl(pde))
+		return pde_ioctl(pde, file, cmd, compat_ptr(arg), arg);
 	return -ENOTTY;
 }
 #else
@@ -881,7 +881,7 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 		if (pde_is_seq_file(de))
 			inode->i_fop = &proc_seq_file_ops;
 		else if (pde_has_proc_permanent_read_iter(de) ||
-			 pde_has_proc_read_buf(de))
+			 pde_has_proc_read(de))
 			inode->i_fop = &proc_iter_file_ops;
 		else
 			inode->i_fop = &proc_reg_file_ops;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index f4677815ebfa..00f7c0bbb82b 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -110,9 +110,9 @@ static inline bool pde_has_proc_show(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_proc_show;
 }
 
-static inline bool pde_has_proc_write_buf(const struct proc_dir_entry *pde)
+static inline bool pde_has_proc_write(const struct proc_dir_entry *pde)
 {
-	return pde->flags & PROC_ENTRY_proc_write_buf;
+	return pde->flags & PROC_ENTRY_proc_write;
 }
 
 static inline bool pde_has_short_writes(const struct proc_dir_entry *pde)
@@ -120,9 +120,9 @@ static inline bool pde_has_short_writes(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_SHORT_WRITES;
 }
 
-static inline bool pde_has_proc_read_buf(const struct proc_dir_entry *pde)
+static inline bool pde_has_proc_read(const struct proc_dir_entry *pde)
 {
-	return pde->flags & PROC_ENTRY_proc_read_buf;
+	return pde->flags & PROC_ENTRY_proc_read;
 }
 
 static inline bool pde_has_full_reads(const struct proc_dir_entry *pde)
@@ -130,9 +130,9 @@ static inline bool pde_has_full_reads(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_FULL_READS;
 }
 
-static inline bool pde_has_proc_ioctl_buf(const struct proc_dir_entry *pde)
+static inline bool pde_has_proc_ioctl(const struct proc_dir_entry *pde)
 {
-	return pde->flags & PROC_ENTRY_proc_ioctl_buf;
+	return pde->flags & PROC_ENTRY_proc_ioctl;
 }
 
 /* procfs owns the seq_file, see pde_seq_ops */
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
index cdeb2e025d2e..fa485581d05e 100644
--- a/fs/proc/proc_net.c
+++ b/fs/proc/proc_net.c
@@ -77,7 +77,7 @@ static int seq_release_net(struct inode *ino, struct file *f)
 
 static const struct proc_ops proc_net_seq_ops = {
 	.proc_open	= seq_open_net,
-	.proc_write_buf	= proc_simple_write,
+	.proc_write	= proc_simple_write,
 	.proc_release	= seq_release_net,
 };
 
@@ -186,7 +186,7 @@ static int single_release_net(struct inode *ino, struct file *f)
 
 static const struct proc_ops proc_net_single_ops = {
 	.proc_open	= single_open_net,
-	.proc_write_buf	= proc_simple_write,
+	.proc_write	= proc_simple_write,
 	.proc_release	= single_release_net,
 };
 
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 1c1e71231f4e..33af42dc9a6a 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -718,7 +718,7 @@ static const struct proc_ops vmcore_proc_ops = {
 	.proc_flags	= PROC_ENTRY_FULL_READS,
 	.proc_open	= open_vmcore,
 	.proc_release	= release_vmcore,
-	.proc_read_buf	= read_vmcore,
+	.proc_read	= read_vmcore,
 	.proc_read_size	= SZ_128K,
 	.proc_lseek	= default_llseek,
 	.proc_mmap	= mmap_vmcore,
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index fb1323bf44c7..df449f6f5282 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -390,7 +390,7 @@ static ssize_t cifs_debug_dirs_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops cifs_debug_dirs_proc_ops = {
 	.proc_show	= cifs_debug_dirs_proc_show,
-	.proc_write_buf	= cifs_debug_dirs_proc_write,
+	.proc_write	= cifs_debug_dirs_proc_write,
 };
 #endif /* CONFIG_CIFS_DEBUG */
 
@@ -854,7 +854,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
 
 static const struct proc_ops cifs_stats_proc_ops = {
 	.proc_show	= cifs_stats_proc_show,
-	.proc_write_buf	= cifs_stats_proc_write,
+	.proc_write	= cifs_stats_proc_write,
 };
 
 #ifdef CONFIG_CIFS_SMB_DIRECT
@@ -876,7 +876,7 @@ static int name##_proc_show(struct seq_file *m, void *v) \
 \
 static const struct proc_ops cifs_##name##_proc_fops = { \
 	.proc_show	= name##_proc_show, \
-	.proc_write_buf	= name##_write, \
+	.proc_write	= name##_write, \
 }
 
 PROC_FILE_DEFINE(rdma_readwrite_threshold);
@@ -1008,7 +1008,7 @@ static ssize_t cifsFYI_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops cifsFYI_proc_ops = {
 	.proc_show	= cifsFYI_proc_show,
-	.proc_write_buf	= cifsFYI_proc_write,
+	.proc_write	= cifsFYI_proc_write,
 };
 
 static int cifs_linux_ext_proc_show(struct seq_file *m, void *v)
@@ -1031,7 +1031,7 @@ static ssize_t cifs_linux_ext_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops cifs_linux_ext_proc_ops = {
 	.proc_show	= cifs_linux_ext_proc_show,
-	.proc_write_buf	= cifs_linux_ext_proc_write,
+	.proc_write	= cifs_linux_ext_proc_write,
 };
 
 static int cifs_lookup_cache_proc_show(struct seq_file *m, void *v)
@@ -1054,7 +1054,7 @@ static ssize_t cifs_lookup_cache_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops cifs_lookup_cache_proc_ops = {
 	.proc_show	= cifs_lookup_cache_proc_show,
-	.proc_write_buf	= cifs_lookup_cache_proc_write,
+	.proc_write	= cifs_lookup_cache_proc_write,
 };
 
 static int traceSMB_proc_show(struct seq_file *m, void *v)
@@ -1077,7 +1077,7 @@ static ssize_t traceSMB_proc_write(struct file *file, char *buffer,
 
 static const struct proc_ops traceSMB_proc_ops = {
 	.proc_show	= traceSMB_proc_show,
-	.proc_write_buf	= traceSMB_proc_write,
+	.proc_write	= traceSMB_proc_write,
 };
 
 static int cifs_security_flags_proc_show(struct seq_file *m, void *v)
@@ -1166,7 +1166,7 @@ static ssize_t cifs_security_flags_proc_write(struct file *file,
 
 static const struct proc_ops cifs_security_flags_proc_ops = {
 	.proc_show	= cifs_security_flags_proc_show,
-	.proc_write_buf	= cifs_security_flags_proc_write,
+	.proc_write	= cifs_security_flags_proc_write,
 };
 
 /* To make it easier to debug, can help to show mount params */
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index a6aa05430b15..8b5781e828b4 100644
--- a/fs/smb/client/dfs_cache.c
+++ b/fs/smb/client/dfs_cache.c
@@ -208,7 +208,7 @@ static ssize_t dfscache_proc_write(struct file *file, char *buffer,
 
 const struct proc_ops dfscache_proc_ops = {
 	.proc_show	= dfscache_proc_show,
-	.proc_write_buf	= dfscache_proc_write,
+	.proc_write	= dfscache_proc_write,
 };
 
 #ifdef CONFIG_CIFS_DEBUG2
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
index 4db79f1ab97e..c27def365beb 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -35,26 +35,26 @@ enum {
 	PROC_ENTRY_proc_show		= 1U << 5,
 
 	PROC_ENTRY_FORCE_LOOKUP		= 1U << 7,
-	PROC_ENTRY_proc_write_buf	= 1U << 8,
+	PROC_ENTRY_proc_write	= 1U << 8,
 
 	/*
 	 * Writes larger than ->proc_write_size come up short instead of
 	 * failing with -EINVAL.
 	 */
 	PROC_ENTRY_SHORT_WRITES		= 1U << 9,
-	PROC_ENTRY_proc_read_buf	= 1U << 10,
+	PROC_ENTRY_proc_read	= 1U << 10,
 
 	/*
 	 * Reads larger than ->proc_read_size are filled in pieces instead of
 	 * coming up short.
 	 */
 	PROC_ENTRY_FULL_READS		= 1U << 11,
-	PROC_ENTRY_proc_ioctl_buf	= 1U << 12,
+	PROC_ENTRY_proc_ioctl	= 1U << 12,
 };
 
 /*
  * How many bytes of the argument of @cmd procfs copies in before calling
- * ->proc_ioctl_buf() and copies back out after it. A command that copies
+ * ->proc_ioctl() and copies back out after it. A command that copies
  * neither gets its argument as is.
  */
 struct proc_ioctl_arg {
@@ -77,7 +77,7 @@ struct proc_ops {
 	int	(*proc_open)(struct inode *, struct file *);
 	/*
 	 * These two are only for PROC_ENTRY_PERMANENT entries. Everything else
-	 * uses ->proc_read_buf().
+	 * uses ->proc_read().
 	 */
 	ssize_t	(*proc_permanent_read)(struct file *, char __user *, size_t,
 				       loff_t *);
@@ -108,26 +108,26 @@ struct proc_ops {
 	unsigned int proc_state_size;
 	/*
 	 * procfs copies what is written into a kernel buffer and calls
-	 * ->proc_write_buf() with it. The buffer is NUL-terminated and holds
+	 * ->proc_write() with it. The buffer is NUL-terminated and holds
 	 * at most ->proc_write_size bytes, PAGE_SIZE - 1 if that's 0. Larger
 	 * writes fail with -EINVAL unless the entry asks for
 	 * PROC_ENTRY_SHORT_WRITES. The position is NULL for a stream, like
 	 * for ->write().
 	 */
-	ssize_t	(*proc_write_buf)(struct file *, char *, size_t, loff_t *);
+	ssize_t	(*proc_write)(struct file *, char *, size_t, loff_t *);
 	unsigned int proc_write_size;
 	/*
-	 * procfs calls ->proc_read_buf() to fill a kernel buffer of at most
+	 * procfs calls ->proc_read() to fill a kernel buffer of at most
 	 * ->proc_read_size bytes, PAGE_SIZE if that's 0, and copies it to
 	 * userspace afterwards. Larger reads come up short unless the entry
 	 * asks for PROC_ENTRY_FULL_READS. The position the entry moves to only
 	 * sticks if the copy works.
 	 */
-	ssize_t	(*proc_read_buf)(struct file *, char *, size_t, loff_t *);
+	ssize_t	(*proc_read)(struct file *, char *, size_t, loff_t *);
 	unsigned int proc_read_size;
 	/*
 	 * procfs looks the command up in ->proc_ioctl_args, copies in what it
-	 * passes before pinning the entry, calls ->proc_ioctl_buf() with that
+	 * passes before pinning the entry, calls ->proc_ioctl() with that
 	 * copy and copies out what it returns after unpinning the entry.
 	 * Commands that aren't in the table fail with -ENOTTY. 32-bit tasks
 	 * go through the same table. The copy is zeroed and as large as the
@@ -135,7 +135,7 @@ struct proc_ops {
 	 * succeeded, and a command that copies neither gets its argument as
 	 * is.
 	 */
-	long	(*proc_ioctl_buf)(struct file *, unsigned int, void *);
+	long	(*proc_ioctl)(struct file *, unsigned int, void *);
 	const struct proc_ioctl_arg *proc_ioctl_args;
 	unsigned int proc_nr_ioctl_args;
 } __randomize_layout;
diff --git a/kernel/configs.c b/kernel/configs.c
index 05b12c440168..6ce19fa7ad44 100644
--- a/kernel/configs.c
+++ b/kernel/configs.c
@@ -49,7 +49,7 @@ ikconfig_read_current(struct file *file, char *buf,
 
 static const struct proc_ops config_gz_proc_ops = {
 	.proc_flags	= PROC_ENTRY_FULL_READS,
-	.proc_read_buf	= ikconfig_read_current,
+	.proc_read	= ikconfig_read_current,
 	.proc_lseek	= default_llseek,
 };
 
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index fe683457a088..4caf10e05e03 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -194,13 +194,13 @@ static ssize_t irq_affinity_list_proc_write(struct file *file,
 
 static const struct proc_ops irq_affinity_proc_ops = {
 	.proc_show	= irq_affinity_proc_show,
-	.proc_write_buf	= irq_affinity_proc_write,
+	.proc_write	= irq_affinity_proc_write,
 	.proc_write_size = IRQ_AFFINITY_WRITE_SIZE,
 };
 
 static const struct proc_ops irq_affinity_list_proc_ops = {
 	.proc_show	= irq_affinity_list_proc_show,
-	.proc_write_buf	= irq_affinity_list_proc_write,
+	.proc_write	= irq_affinity_list_proc_write,
 	.proc_write_size = IRQ_AFFINITY_WRITE_SIZE,
 };
 
@@ -255,7 +255,7 @@ out:
 
 static const struct proc_ops default_affinity_proc_ops = {
 	.proc_show	= default_affinity_show,
-	.proc_write_buf	= default_affinity_write,
+	.proc_write	= default_affinity_write,
 	.proc_write_size = IRQ_AFFINITY_WRITE_SIZE,
 };
 
diff --git a/kernel/latencytop.c b/kernel/latencytop.c
index 35f73ca07656..e90b1801f9b4 100644
--- a/kernel/latencytop.c
+++ b/kernel/latencytop.c
@@ -276,7 +276,7 @@ lstats_write(struct file *file, char *buf, size_t count, loff_t *offs)
 
 static const struct proc_ops lstats_proc_ops = {
 	.proc_show	= lstats_show,
-	.proc_write_buf	= lstats_write,
+	.proc_write	= lstats_write,
 };
 
 static int __init init_lstats_procfs(void)
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index 2a66ea09c2aa..0a95786bba88 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -699,7 +699,7 @@ static int lock_stat_release(struct inode *inode, struct file *file)
 static const struct proc_ops lock_stat_proc_ops = {
 	.proc_seq_ops	= &lockstat_ops,
 	.proc_open	= lock_stat_open,
-	.proc_write_buf	= lock_stat_write,
+	.proc_write	= lock_stat_write,
 	.proc_release	= lock_stat_release,
 };
 #endif /* CONFIG_LOCK_STAT */
diff --git a/kernel/profile.c b/kernel/profile.c
index 72e30bb30396..ff1377f39e9c 100644
--- a/kernel/profile.c
+++ b/kernel/profile.c
@@ -208,7 +208,7 @@ static ssize_t write_profile(struct file *file, char *buf, size_t count,
 static const struct proc_ops profile_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_permanent_read = read_profile,
-	.proc_write_buf	= write_profile,
+	.proc_write	= write_profile,
 	.proc_lseek	= default_llseek,
 };
 
diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index 9811c3b705eb..68a57058b98a 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1624,7 +1624,7 @@ static int psi_fop_release(struct inode *inode, struct file *file)
 static const struct proc_ops psi_io_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_io_show,
-	.proc_write_buf	= psi_io_write,
+	.proc_write	= psi_io_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
@@ -1632,7 +1632,7 @@ static const struct proc_ops psi_io_proc_ops = {
 static const struct proc_ops psi_memory_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_memory_show,
-	.proc_write_buf	= psi_memory_write,
+	.proc_write	= psi_memory_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
@@ -1640,7 +1640,7 @@ static const struct proc_ops psi_memory_proc_ops = {
 static const struct proc_ops psi_cpu_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_cpu_show,
-	.proc_write_buf	= psi_cpu_write,
+	.proc_write	= psi_cpu_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
@@ -1660,7 +1660,7 @@ static ssize_t psi_irq_write(struct file *file, char *buf, size_t nbytes,
 static const struct proc_ops psi_irq_proc_ops = {
 	.proc_flags	= PROC_ENTRY_PERMANENT,
 	.proc_show	= psi_irq_show,
-	.proc_write_buf	= psi_irq_write,
+	.proc_write	= psi_irq_write,
 	.proc_poll	= psi_fop_poll,
 	.proc_release	= psi_fop_release,
 };
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 1d6012c9eb41..1f5076b05242 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1205,7 +1205,7 @@ static const struct file_operations ddebug_proc_fops = {
 static const struct proc_ops proc_fops = {
 	.proc_seq_ops = &ddebug_proc_seqops,
 	.proc_state_size = sizeof(struct ddebug_iter),
-	.proc_write_buf = ddebug_proc_write_buf,
+	.proc_write = ddebug_proc_write_buf,
 	.proc_write_size = USER_BUF_PAGE - 1,
 };
 
diff --git a/mm/alloc_tag.c b/mm/alloc_tag.c
index 1734705e2990..45be07af102f 100644
--- a/mm/alloc_tag.c
+++ b/mm/alloc_tag.c
@@ -460,7 +460,7 @@ static const struct proc_ops allocinfo_proc_ops = {
 	.proc_state_size	= sizeof(struct allocinfo_private),
 	.proc_open		= allocinfo_open,
 	.proc_release		= allocinfo_release,
-	.proc_ioctl_buf		= allocinfo_ioctl,
+	.proc_ioctl		= allocinfo_ioctl,
 	.proc_ioctl_args	= allocinfo_ioctl_args,
 	.proc_nr_ioctl_args	= ARRAY_SIZE(allocinfo_ioctl_args),
 };
diff --git a/net/atm/proc.c b/net/atm/proc.c
index cea930b4b840..032adae4bf75 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -34,7 +34,7 @@ static ssize_t proc_dev_atm_read(struct file *file, char *buf,
 				 size_t count, loff_t *pos);
 
 static const struct proc_ops atm_dev_proc_ops = {
-	.proc_read_buf	= proc_dev_atm_read,
+	.proc_read	= proc_dev_atm_read,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 84f4db86a5b0..ac17a15102e3 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -545,7 +545,7 @@ static ssize_t pgctrl_write(struct file *file, char *buf, size_t count,
 
 static const struct proc_ops pktgen_proc_ops = {
 	.proc_show	= pgctrl_show,
-	.proc_write_buf	= pgctrl_write,
+	.proc_write	= pgctrl_write,
 };
 
 static int pktgen_if_show(struct seq_file *seq, void *v)
@@ -1829,7 +1829,7 @@ static ssize_t pktgen_if_write(struct file *file, char *user_buffer,
 
 static const struct proc_ops pktgen_if_proc_ops = {
 	.proc_show	= pktgen_if_show,
-	.proc_write_buf	= pktgen_if_write,
+	.proc_write	= pktgen_if_write,
 };
 
 static int pktgen_thread_show(struct seq_file *seq, void *v)
@@ -1958,7 +1958,7 @@ out:
 
 static const struct proc_ops pktgen_thread_proc_ops = {
 	.proc_show	= pktgen_thread_show,
-	.proc_write_buf	= pktgen_thread_write,
+	.proc_write	= pktgen_thread_write,
 };
 
 /* Think find or remove for NN */
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index 306557f25daf..fa25500dc897 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -623,7 +623,7 @@ static const struct proc_ops recent_mt_proc_ops = {
 	.proc_seq_ops	= &recent_seq_ops,
 	.proc_state_size = sizeof(struct recent_iter_state),
 	.proc_open	= recent_seq_open,
-	.proc_write_buf	= recent_mt_proc_write,
+	.proc_write	= recent_mt_proc_write,
 };
 
 static int __net_init recent_proc_net_init(struct net *net)
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 67e086a7127c..e21c4d326384 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1456,8 +1456,8 @@ static ssize_t read_gssp(struct file *file, char *buf,
 
 static const struct proc_ops use_gss_proxy_proc_ops = {
 	.proc_open	= nonseekable_open,
-	.proc_write_buf	= write_gssp,
-	.proc_read_buf	= read_gssp,
+	.proc_write	= write_gssp,
+	.proc_read	= read_gssp,
 };
 
 static int create_use_gss_proxy_proc_entry(struct net *net)
@@ -1511,7 +1511,7 @@ static ssize_t read_gss_krb5_enctypes(struct file *file, char *buf,
 
 static const struct proc_ops gss_krb5_enctypes_proc_ops = {
 	.proc_open	= nonseekable_open,
-	.proc_read_buf	= read_gss_krb5_enctypes,
+	.proc_read	= read_gss_krb5_enctypes,
 };
 
 static int create_krb5_enctypes_proc_entry(struct net *net)
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index d242b6f33cbc..6229a3fec413 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1611,8 +1611,8 @@ static int cache_release_procfs(struct inode *inode, struct file *filp)
 }
 
 static const struct proc_ops cache_channel_proc_ops = {
-	.proc_read_buf	= cache_read_procfs,
-	.proc_write_buf	= cache_write_procfs,
+	.proc_read	= cache_read_procfs,
+	.proc_write	= cache_write_procfs,
 	.proc_write_size = CACHE_DOWNCALL_MAX,
 	.proc_poll	= cache_poll_procfs,
 	.proc_open	= cache_open_procfs,
@@ -1681,8 +1681,8 @@ static ssize_t write_flush_procfs(struct file *filp, char *buf, size_t count,
 
 static const struct proc_ops cache_flush_proc_ops = {
 	.proc_open	= open_flush_procfs,
-	.proc_read_buf	= read_flush_procfs,
-	.proc_write_buf	= write_flush_procfs,
+	.proc_read	= read_flush_procfs,
+	.proc_write	= write_flush_procfs,
 	.proc_write_size = CACHE_FLUSH_MAX,
 	.proc_release	= release_flush_procfs,
 };
diff --git a/samples/kfifo/bytestream-example.c b/samples/kfifo/bytestream-example.c
index 214e46a64cc1..efb7ee0a3c56 100644
--- a/samples/kfifo/bytestream-example.c
+++ b/samples/kfifo/bytestream-example.c
@@ -141,8 +141,8 @@ static ssize_t fifo_read(struct file *file, char *buf,
 }
 
 static const struct proc_ops fifo_proc_ops = {
-	.proc_read_buf	= fifo_read,
-	.proc_write_buf	= fifo_write,
+	.proc_read	= fifo_read,
+	.proc_write	= fifo_write,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/samples/kfifo/inttype-example.c b/samples/kfifo/inttype-example.c
index 8384d7f8de6c..ce9111f59986 100644
--- a/samples/kfifo/inttype-example.c
+++ b/samples/kfifo/inttype-example.c
@@ -136,8 +136,8 @@ static ssize_t fifo_read(struct file *file, char *buf,
 }
 
 static const struct proc_ops fifo_proc_ops = {
-	.proc_read_buf	= fifo_read,
-	.proc_write_buf	= fifo_write,
+	.proc_read	= fifo_read,
+	.proc_write	= fifo_write,
 	.proc_lseek	= noop_llseek,
 };
 
diff --git a/samples/kfifo/record-example.c b/samples/kfifo/record-example.c
index 29d08e90a63d..399dc07d9438 100644
--- a/samples/kfifo/record-example.c
+++ b/samples/kfifo/record-example.c
@@ -148,8 +148,8 @@ static ssize_t fifo_read(struct file *file, char *buf,
 }
 
 static const struct proc_ops fifo_proc_ops = {
-	.proc_read_buf	= fifo_read,
-	.proc_write_buf	= fifo_write,
+	.proc_read	= fifo_read,
+	.proc_write	= fifo_write,
 	.proc_lseek	= noop_llseek,
 	/* kfifo_from_user() cut a record to what its length byte holds */
 	.proc_flags	= PROC_ENTRY_SHORT_WRITES,
diff --git a/sound/core/info.c b/sound/core/info.c
index 639554bb17c8..089d169a9d39 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -267,8 +267,8 @@ static const struct proc_ops snd_info_entry_operations =
 {
 	.proc_flags	= PROC_ENTRY_SHORT_WRITES | PROC_ENTRY_FULL_READS,
 	.proc_lseek	= snd_info_entry_llseek,
-	.proc_read_buf	= snd_info_entry_read,
-	.proc_write_buf	= snd_info_entry_write,
+	.proc_read	= snd_info_entry_read,
+	.proc_write	= snd_info_entry_write,
 	.proc_write_size = PAGE_SIZE,
 	.proc_poll	= snd_info_entry_poll,
 	.proc_mmap	= snd_info_entry_mmap,
@@ -393,7 +393,7 @@ static const struct proc_ops snd_info_text_entry_ops =
 	.proc_show	= snd_info_seq_show,
 	.proc_open	= snd_info_text_entry_open,
 	.proc_release	= snd_info_text_entry_release,
-	.proc_write_buf	= snd_info_text_entry_write,
+	.proc_write	= snd_info_text_entry_write,
 	.proc_write_size = SND_INFO_TEXT_WRITE_MAX,
 };
 

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 03/27] lockd: let procfs copy in writes to nlm_end_grace
  2026-11-02 15:07   ` [PATCH RFC 03/27] lockd: let procfs copy in writes to nlm_end_grace Christian Brauner
@ 2026-11-02 15:23     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 15:23 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers



On Mon, Nov 2, 2026, at 10:07 AM, Christian Brauner wrote:
> Implement ->proc_write_buf() so procfs copies in what is written before
> it pins the entry. simple_transaction_get() was only used to get a copy
> of what was written, so look at procfs' copy instead and drop the
> transaction. A second write to the same file doesn't fail with -EBUSY
> anymore. Writes larger than PAGE_SIZE - 1 bytes fail with -EINVAL from
> procfs instead of -EFBIG from simple_transaction_get(), which refused a
> few bytes less.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>


-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 04/27] lockd: let procfs copy out reads of nlm_end_grace
  2026-11-02 15:07   ` [PATCH RFC 04/27] lockd: let procfs copy out reads of nlm_end_grace Christian Brauner
@ 2026-11-02 15:24     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 15:24 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers



On Mon, Nov 2, 2026, at 10:07 AM, Christian Brauner wrote:
> Implement ->proc_read_buf() so procfs copies what is read to userspace
> after it unpinned the entry.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>


-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses
  2026-11-02 14:21   ` [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses Christian Brauner
@ 2026-11-02 17:58     ` Linus Torvalds
  0 siblings, 0 replies; 160+ messages in thread
From: Linus Torvalds @ 2026-11-02 17:58 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-fsdevel, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Al Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390, linux-scsi,
	linux-um, linux-wireless, linuxppc-dev, netdev, ntfs3,
	platform-driver-x86, coreteam, kexec, linux-pci, linux-sound,
	netfilter-devel, dri-devel, ibm-acpi-devel, jfs-discussion,
	linux-alpha, linux-arm-kernel, linux-cifs, linux-fbdev,
	linux-hwmon, linux-mm, linux-sh, linux-usb, samba-technical,
	sparclinux, linux-m68k, linux-doc, linux-serial,
	linux-atm-general, Takashi Iwai, linux-input, linux-raid,
	linux-ext4, linux-modules, linux-debuggers, Chuck Lever, stable

On Mon, 2 Nov 2026 at 06:22, Christian Brauner <brauner@kernel.org> wrote:
>
> proc_bus_pci_write() truncates the file position to an int. A pwrite()
> at an offset like 0xfffff000 turns it negative [..]

I think this would actually be better with rw_verify_area() just
checking a per-file maxbytes.

We already have that logic for lseek and friends - using that fairly
hacky combination of s_maxbytes and !unsigned_offsets().

We should combine s_maxbytes and unsigned_offsets() at file open time
into f_maxbytes and check that not just at lseek but in
rw_verify_area.

And then things like proc_bus_pci could set it's size limit in one single place.

Hmm?

                   Linus

^ permalink raw reply	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 06/11] SUNRPC: let procfs own the seq_file of the cache content file
  2026-11-02 15:07   ` [PATCH RFC 06/11] SUNRPC: let procfs own the seq_file of the cache content file Christian Brauner
@ 2026-11-02 21:11     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 21:11 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers



On Mon, Nov 2, 2026, at 10:07 AM, Christian Brauner wrote:
> Declare the seq_operations and let procfs open and free the seq_file of
> /proc/net/rpc/<cache>/content. ->proc_open() and ->proc_release() only
> take and drop the module reference and set up ->private now. The
> rpc_pipefs file is unchanged. Reading the procfs file then no longer
> holds up its removal when the network namespace goes away.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>


-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 09/11] SUNRPC: drop the ioctl of the procfs channel files
  2026-11-02 15:07   ` [PATCH RFC 09/11] SUNRPC: drop the ioctl of the procfs channel files Christian Brauner
@ 2026-11-02 21:11     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 21:11 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers



On Mon, Nov 2, 2026, at 10:07 AM, Christian Brauner wrote:
> do_vfs_ioctl() answers FIONREAD for regular files with i_size - f_pos
> before it gets to the file's ->unlocked_ioctl(). The channel files are
> regular files, so cache_ioctl_procfs() has never seen FIONREAD and only
> ever failed other commands with -EINVAL. Drop it. Other commands fail
> with -ENOTTY now.
>
> The rpc_pipefs channel files are regular files as well.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>


-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 05/27] nfsd: factor out setting up the exports seq_file
  2026-11-02 15:07   ` [PATCH RFC 05/27] nfsd: factor out setting up the exports seq_file Christian Brauner
@ 2026-11-02 21:14     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 21:14 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers



On Mon, Nov 2, 2026, at 10:07 AM, Christian Brauner wrote:
> Move setting up ->private and taking the reference to the network
> namespace into exports_net_init() and dropping it into
> exports_net_put(). The procfs file needs them without the seq_open()
> and seq_release() around them.
>
> No functional changes.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>

-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

* Re: [PATCH RFC 06/27] nfsd: let procfs own the seq_file of /proc/fs/nfs/exports
  2026-11-02 15:07   ` [PATCH RFC 06/27] nfsd: let procfs own the seq_file of /proc/fs/nfs/exports Christian Brauner
@ 2026-11-02 21:14     ` Chuck Lever
  0 siblings, 0 replies; 160+ messages in thread
From: Chuck Lever @ 2026-11-02 21:14 UTC (permalink / raw)
  To: Christian Brauner, linux-fsdevel
  Cc: Linus Torvalds, Jann Horn, Josef Bacik, Chris Mason, Jeff Layton,
	NeilBrown, Eric Dumazet, Thomas Gleixner, Alexander Viro,
	Jonathan Corbet, Christoph Hellwig, Greg Kroah-Hartman,
	Joel Granados, Kees Cook, Jan Kara, Jan Kara, linux-kernel,
	linux-nfs, linux-acpi, linux-kselftest, linux-s390,
	linux-scsi@vger.kernel.org, linux-um, linux-wireless,
	Linuxppc Dev, netdev, ntfs3, platform-driver-x86, coreteam, kexec,
	linux-pci, linux-sound, netfilter-devel, dri-devel,
	ibm-acpi-devel, jfs-discussion, linux-alpha, linux-arm-kernel,
	linux-cifs, linux-fbdev, linux-hwmon, linux-mm, linux-sh,
	linux-usb, samba-technical, sparclinux, linux-m68k, linux-doc,
	linux-serial, linux-atm-general, Takashi Iwai, linux-input,
	linux-raid, linux-ext4, linux-modules, linux-debuggers



On Mon, Nov 2, 2026, at 10:07 AM, Christian Brauner wrote:
> Declare the seq_operations and let procfs open and free the seq_file.
> ->proc_open() and ->proc_release() only set up ->private and take and
> drop the reference to the network namespace now. Reading the file then
> no longer holds up its removal.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>

Acked-by: Chuck Lever <cel@kernel.org>


-- 
Chuck Lever (Come to NFS bake-a-thon! https://nfsv4bat.org)

^ permalink raw reply	[flat|nested] 160+ messages in thread

end of thread, other threads:[~2026-11-02 21:15 UTC | newest]

Thread overview: 160+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02 14:19 [PATCH RFC 0/7] proc: move procfs to what kernfs does Christian Brauner
2026-11-02 14:21 ` [PATCH RFC 00/38] proc: fix a few old bugs in /proc handlers Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses Christian Brauner
2026-11-02 17:58     ` Linus Torvalds
2026-11-02 14:21   ` [PATCH RFC 02/38] PNP: isapnp: don't read past the end of the buffer on a large pread() Christian Brauner
2026-11-02 14:51     ` Rafael J. Wysocki (Intel)
2026-11-02 14:21   ` [PATCH RFC 03/38] netfilter: xt_recent: don't remove the proc entries under a spinlock Christian Brauner
2026-11-02 14:41     ` Florian Westphal
2026-11-02 14:21   ` [PATCH RFC 04/38] s390/dasd: don't look in front of the buffer on an empty write Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 05/38] platform/x86: toshiba_acpi: reject malformed brightness and fan writes Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 06/38] xtensa: simdisk: don't read the file name without the lock Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 07/38] powerpc/rtas_flash: don't read past the validate message Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 08/38] powerpc/rtasd: zero the error log buffer Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 09/38] ALSA: info: don't write past the end of a data entry Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 10/38] scsi: BusLogic: don't return 0 from writes to the proc file Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 11/38] scsi: ips: " Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 12/38] wifi: libipw: don't return 0 from writes to debug_level Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 13/38] scsi: core: NUL-terminate what is written to the host proc files Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 14/38] um: NUL-terminate what is written to /proc/exitcode Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 15/38] ntfs3: don't warn when a huge label is written Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 16/38] proc: don't walk through a non-directory when resolving a name Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 17/38] net: pktgen: reject device names with a '/' Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 18/38] proc: don't WARN about a name that is already registered Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 19/38] proc: resume readdir after the last name it returned Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 20/38] selftests/proc: check a /proc listing while entries come and go Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 21/38] proc: let proc_remove() remove the entry it was given Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 22/38] proc: warn about entries left in /proc/net of a dying namespace Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 23/38] proc: don't let NOMMU map procfs files Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 24/38] proc/vmcore: don't let vmcore_cleanup() run twice at once Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 25/38] powerpc/crash_dump: check ioremap_cache() in copy_oldmem_page() Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 26/38] powerpc/crash_dump: recover from a machine check " Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 27/38] sunrpc: make queue_wait a global wait queue again Christian Brauner
2026-11-02 15:12     ` Chuck Lever
2026-11-02 14:21   ` [PATCH RFC 28/38] scsi: esas2r: bound the lengths of the HBA, SMP and firmware ioctls Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 29/38] powerpc/pseries: bound the lengths in the ofdt property parser Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 30/38] powerpc/pseries/lparcfg: return h_get_ppp()'s status as a long Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 31/38] proc: sysctl: resume readdir after the last name it returned Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 32/38] selftests/proc: check a sysctl listing while entries come and go Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 33/38] proc: sysctl: copy to and from userspace without holding the table Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 34/38] proc: sysctl: look at the table under sysctl_lock when creating an inode Christian Brauner
2026-11-02 14:21   ` [PATCH RFC 35/38] proc: sysctl: don't hold the tables while readdir copies to userspace Christian Brauner
2026-11-02 14:43   ` [PATCH RFC 36/38] selftests/proc: check that faulting sysctl accesses don't hold up removal Christian Brauner
2026-11-02 14:43   ` [PATCH RFC 37/38] proc: don't pin a symlink's entry while readlink() copies it out Christian Brauner
2026-11-02 14:43   ` [PATCH RFC 38/38] selftests/proc: check that a faulting readlink() doesn't hold up removal Christian Brauner
2026-11-02 14:48 ` [PATCH RFC 00/16] proc: let procfs own the data path Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 01/16] proc: drop the compat file operations Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 02/16] proc: add pde_pin() and pde_unpin() Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 03/16] proc: make room for more entry flags Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 04/16] proc: let procfs own the seq_file of seq_file entries Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 05/16] proc: let procfs own the seq_file of proc_create_seq() and friends Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 06/16] proc: let procfs own the seq_file of DEFINE_PROC_SHOW_ATTRIBUTE() files Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 07/16] treewide: let procfs own the seq_file of simple proc entries Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 08/16] selftests/proc: check that a faulting reader doesn't hold up removal Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 09/16] seq_file: add seq_preallocate() Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 10/16] proc: let procfs own the seq_file of /proc/stat Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 11/16] proc: let procfs copy in writes before pinning the entry Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 12/16] proc: copy in writes to the network namespace entries before pinning Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 13/16] proc: let procfs copy out reads that don't go through seq_file Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 14/16] fs/proc/vmcore: let procfs copy out reads Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 15/16] proc: let procfs copy ioctl arguments in and out Christian Brauner
2026-11-02 14:48   ` [PATCH RFC 16/16] Documentation: seq_file: describe how a /proc file uses seq_file Christian Brauner
2026-11-02 15:01 ` [PATCH RFC 00/22] proc: convert the arch and platform entries Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 01/22] alpha: srm_env: let procfs copy in writes Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 02/22] ARM: alignment: " Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 03/22] ARM: atags: let procfs copy out reads of /proc/atags Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 04/22] m68k/bootinfo: let procfs copy out reads of /proc/bootinfo Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 05/22] powerpc/rtas: let procfs copy in writes Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 06/22] powerpc/rtas: let procfs copy out reads Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 07/22] powerpc/pseries: let procfs copy in writes Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 08/22] powerpc/procfs: let procfs copy out reads of /proc/powerpc/systemcfg Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 09/22] sh: alignment: let procfs copy in writes Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 10/22] sparc: led: " Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 11/22] um: " Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 12/22] x86/mtrr: let procfs own the seq_file of /proc/mtrr Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 13/22] x86/mtrr: let procfs copy in writes to /proc/mtrr Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 14/22] x86/mtrr: let procfs copy the ioctl arguments of /proc/mtrr Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 15/22] xtensa: simdisk: let procfs copy in writes Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 16/22] xtensa: simdisk: let procfs copy out reads Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 17/22] ACPI: proc: let procfs copy in writes to /proc/acpi/wakeup Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 18/22] macintosh/via-pmu: let procfs copy in writes to the options file Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 19/22] platform/x86: thinkpad_acpi: let procfs copy in writes Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 20/22] platform/x86: toshiba_acpi: " Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 21/22] PNP: pnpbios: " Christian Brauner
2026-11-02 15:01   ` [PATCH RFC 22/22] PNP: isapnp: let procfs copy out reads Christian Brauner
2026-11-02 15:07 ` [PATCH RFC 00/23] proc: convert the driver entries Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 01/23] nubus: let procfs own the seq_file of the resource files Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 02/23] zorro: let procfs copy out reads Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 03/23] PCI/proc: let procfs copy in config space writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 04/23] PCI/proc: let procfs copy out config space reads Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 05/23] PCI/proc: let procfs handle the ioctls Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 06/23] md: let procfs own the seq_file of /proc/mdstat Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 07/23] s390/dasd: let procfs copy in writes to /proc/dasd/statistics Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 08/23] s390/cio: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 09/23] scsi: let procfs own the seq_file of the host files Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 10/23] scsi: core: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 11/23] selftests/proc: check that a faulting writer doesn't hold up removal Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 12/23] scsi: sg: let procfs own the seq_file of allow_dio and def_reserved_size Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 13/23] scsi: sg: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 14/23] scsi: esas2r: let procfs copy the ioctl argument of the ATTONode file Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 15/23] hwmon: (dell-smm) let procfs copy the i8k ioctl arguments Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 16/23] fbdev: viafb: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 17/23] usb: gadget: function: rndis: " Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 18/23] wifi: ipw2x00: let procfs copy in writes to the debug level Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 19/23] tty: sysrq: let procfs copy in writes to /proc/sysrq-trigger Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 20/23] ALSA: info: let procfs own the seq_file of text entries Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 21/23] ALSA: info: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 22/23] ALSA: info: let procfs copy out reads of data entries Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 23/23] ALSA: info: drop the ioctl hook " Christian Brauner
2026-11-02 15:07 ` [PATCH RFC 00/27] proc: convert the fs, kernel and mm entries Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 01/27] jbd2: let procfs own the seq_file of the info file Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 02/27] jfs: let procfs copy in writes to the loglevel file Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 03/27] lockd: let procfs copy in writes to nlm_end_grace Christian Brauner
2026-11-02 15:23     ` Chuck Lever
2026-11-02 15:07   ` [PATCH RFC 04/27] lockd: let procfs copy out reads of nlm_end_grace Christian Brauner
2026-11-02 15:24     ` Chuck Lever
2026-11-02 15:07   ` [PATCH RFC 05/27] nfsd: factor out setting up the exports seq_file Christian Brauner
2026-11-02 21:14     ` Chuck Lever
2026-11-02 15:07   ` [PATCH RFC 06/27] nfsd: let procfs own the seq_file of /proc/fs/nfs/exports Christian Brauner
2026-11-02 21:14     ` Chuck Lever
2026-11-02 15:07   ` [PATCH RFC 07/27] ntfs3: let procfs copy in writes to the label file Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 08/27] smb: client: let procfs own the seq_file of the smbdirect files Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 09/27] smb: client: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 10/27] genirq/proc: let procfs copy in the affinity masks Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 11/27] latencytop: let procfs copy in writes to /proc/latency_stats Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 12/27] locking/lockdep: let procfs own the seq_file of /proc/lock_stat Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 13/27] locking/lockdep: let procfs copy in writes to /proc/lock_stat Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 14/27] profiling: let procfs copy in writes to /proc/profile Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 15/27] profiling: mark /proc/profile permanent Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 16/27] sched/psi: let procfs own the seq_file of the pressure files Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 17/27] sched/psi: let procfs copy in trigger writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 18/27] sched/psi: mark the pressure files permanent Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 19/27] kallsyms: let procfs own the seq_file of /proc/kallsyms Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 20/27] module: procfs: let procfs own the seq_file of /proc/modules Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 21/27] dyndbg: let procfs copy in writes to /proc/dynamic_debug/control Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 22/27] ipc: let procfs own the seq_file of the sysvipc files Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 23/27] alloc_tag: let procfs own the seq_file of /proc/allocinfo Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 24/27] alloc_tag: let procfs copy the ioctl arguments " Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 25/27] mm/swapfile: let procfs own the seq_file of /proc/swaps Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 26/27] kernel/configs: let procfs copy out reads of /proc/config.gz Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 27/27] selftests/proc: check that unloading configs doesn't wait for a reader Christian Brauner
2026-11-02 15:07 ` [PATCH RFC 00/11] proc: convert the net entries and the kfifo samples Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 01/11] atm: let procfs copy out reads of the device files Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 02/11] l2tp: let procfs own the seq_file of /proc/net/pppol2tp Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 03/11] net: pktgen: let procfs copy in commands Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 04/11] netfilter: xt_recent: let procfs own the seq_file Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 05/11] netfilter: xt_recent: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 06/11] SUNRPC: let procfs own the seq_file of the cache content file Christian Brauner
2026-11-02 21:11     ` Chuck Lever
2026-11-02 15:07   ` [PATCH RFC 07/11] SUNRPC: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 08/11] SUNRPC: let procfs copy out reads Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 09/11] SUNRPC: drop the ioctl of the procfs channel files Christian Brauner
2026-11-02 21:11     ` Chuck Lever
2026-11-02 15:07   ` [PATCH RFC 10/11] samples/kfifo: let procfs copy in writes Christian Brauner
2026-11-02 15:07   ` [PATCH RFC 11/11] samples/kfifo: let procfs copy out reads Christian Brauner
2026-11-02 15:15 ` [PATCH RFC 0/5] proc: drop the __user methods Christian Brauner
2026-11-02 15:15   ` [PATCH RFC 1/5] proc: drop ->proc_write() Christian Brauner
2026-11-02 15:15   ` [PATCH RFC 2/5] proc: drop ->proc_ioctl() and ->proc_compat_ioctl() Christian Brauner
2026-11-02 15:15   ` [PATCH RFC 3/5] proc: only let permanent entries read straight into userspace Christian Brauner
2026-11-02 15:15   ` [PATCH RFC 4/5] proc: rename ->proc_read() and ->proc_read_iter() Christian Brauner
2026-11-02 15:15   ` [PATCH RFC 5/5] proc: drop the _buf suffix from the new methods Christian Brauner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox