[PATCH RESEND v2 4/6] proc: drop ->proc_read() and ->proc_read_iter()

Christian Brauner brauner at kernel.org
Wed Nov 4 13:16:16 PST 2026


Nothing implements them anymore. Every entry that can be removed fills
procfs' buffer through ->proc_read_buf() or is a seq_file entry, and
the permanent ones that still read straight into userspace have
file_operations of their own now.

With them go the flag, the registration checks that kept them apart
from ->proc_read_buf() and the seq_file entries, proc_reg_read(), and
two of the three sets of file_operations for regular files:
proc_reg_llseek() and proc_reg_read_iter() hand a seq_file entry to
seq_lseek() and seq_read_iter() themselves now, like
kernfs_fop_read_iter() does, so one set serves every entry that has
proc_ops. That set has ->splice_read(), so a splice() from an entry
that can't be read at all, a write-only one, fails with -EIO now
instead of -EINVAL.

Nothing in struct proc_ops takes a __user pointer anymore.

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

diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 4e0142fec582..2515df3a0110 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -454,8 +454,7 @@ 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 ||
-				 proc_ops->proc_lseek))
+		if (WARN_ON_ONCE(proc_ops->proc_lseek))
 			return -EINVAL;
 		/* and frees it, see pde_seq_release() */
 		if (WARN_ON_ONCE(proc_ops->proc_release == seq_release ||
@@ -463,11 +462,8 @@ static int pde_set_flags(struct proc_dir_entry *pde)
 				 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;
+	} else if (proc_ops->proc_lseek) {
+		pde->flags |= PROC_ENTRY_proc_lseek;
 	}
 	if (proc_ops->proc_poll)
 		pde->flags |= PROC_ENTRY_proc_poll;
@@ -482,8 +478,8 @@ 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))
+		/* procfs reads through the seq_file itself */
+		if (WARN_ON_ONCE(pde_is_seq_file(pde)))
 			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 49ae30b6e515..9719c1b89de3 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -413,6 +413,9 @@ 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;
 
+	/* procfs seeks through the seq_file itself, see pde_seq_start() */
+	if (pde_is_seq_file(pde))
+		return seq_lseek(file, offset, whence);
 	if (!pde_pin(pde))
 		return -EINVAL;
 	rv = pde->proc_ops->proc_lseek(file, offset, whence);
@@ -477,37 +480,15 @@ 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;
 
+	/* procfs reads through the seq_file itself, see pde_seq_start() */
+	if (pde_is_seq_file(pde))
+		return seq_read_iter(iocb, iter);
 	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;
 }
 
-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))
-		return -EIO;
-	rv = pde_read(pde, file, buf, count, ppos);
-	pde_unpin(pde);
-	return rv;
-}
-
 /*
  * 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().
@@ -923,41 +904,7 @@ static int proc_reg_release(struct inode *inode, struct file *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,
-	.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 = {
 	.llseek		= proc_reg_llseek,
 	.read_iter	= proc_reg_read_iter,
 	.write		= proc_reg_write,
@@ -1026,10 +973,6 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
 		inode->i_op = de->proc_iops;
 		if (pde_has_proc_fops(de))
 			inode->i_fop = de->proc_fops;
-		else 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))
-			inode->i_fop = &proc_iter_file_ops;
 		else
 			inode->i_fop = &proc_reg_file_ops;
 	} else if (S_ISDIR(inode->i_mode)) {
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 84c5c13651aa..0a22237e6778 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -103,11 +103,6 @@ static inline bool pde_has_proc_fops(const struct proc_dir_entry *pde)
 	return pde->flags & PROC_ENTRY_proc_fops;
 }
 
-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_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 05e0d6e4f918..9c09766b8f72 100644
--- a/include/linux/proc_fs.h
+++ b/include/linux/proc_fs.h
@@ -17,6 +17,7 @@ struct seq_operations;
 enum {
 	/*
 	 * All /proc entries using this ->proc_ops instance are never removed.
+	 * procfs calls into them without pinning them.
 	 *
 	 * If in doubt, ignore this flag.
 	 */
@@ -26,7 +27,6 @@ enum {
 	PROC_ENTRY_PERMANENT		= 1U << 0,
 #endif
 
-	PROC_ENTRY_proc_read_iter	= 1U << 1,
 	PROC_ENTRY_proc_fops		= 1U << 2,
 	PROC_ENTRY_proc_lseek		= 1U << 3,
 	PROC_ENTRY_proc_seq_ops		= 1U << 4,
@@ -76,8 +76,6 @@ struct proc_ioctl_arg {
 struct proc_ops {
 	unsigned int proc_flags;
 	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 *);
 	/*
 	 * mandatory unless nonseekable_open() or equivalent is used, procfs
 	 * seeks seq_file entries itself

-- 
2.53.0




More information about the linux-um mailing list