Merge branch 'hch' (patches from Christoph)

Merge procfs splice read fixes from Christoph Hellwig:
 "Greg reported a problem due to the fact that Android tests use procfs
  files to test splice, which stopped working with the changes for
  set_fs() removal.

  This series adds read_iter support for seq_file, and uses those for
  various proc files using seq_file to restore splice read support"

[ Side note: Christoph initially had a scripted "move everything over"
  patch, which looks fine, but I personally would prefer us to actively
  discourage splice() on random files.  So this does just the minimal
  basic core set of proc file op conversions.

  For completeness, and in case people care, that script was

     sed -i -e 's/\.proc_read\(\s*=\s*\)seq_read/\.proc_read_iter\1seq_read_iter/g'

  but I'll wait and see if somebody has a strong argument for using
  splice on random small /proc files before I'd run it on the whole
  kernel.   - Linus ]

* emailed patches from Christoph Hellwig <hch@lst.de>:
  proc "seq files": switch to ->read_iter
  proc "single files": switch to ->read_iter
  proc/stat: switch to ->read_iter
  proc/cpuinfo: switch to ->read_iter
  proc: wire up generic_file_splice_read for iter ops
  seq_file: add seq_read_iter
This commit is contained in:
Linus Torvalds 2020-11-08 10:11:31 -08:00
commit 6b2c4d52fd
6 changed files with 39 additions and 17 deletions

View File

@ -19,7 +19,7 @@ static int cpuinfo_open(struct inode *inode, struct file *file)
static const struct proc_ops cpuinfo_proc_ops = { static const struct proc_ops cpuinfo_proc_ops = {
.proc_flags = PROC_ENTRY_PERMANENT, .proc_flags = PROC_ENTRY_PERMANENT,
.proc_open = cpuinfo_open, .proc_open = cpuinfo_open,
.proc_read = seq_read, .proc_read_iter = seq_read_iter,
.proc_lseek = seq_lseek, .proc_lseek = seq_lseek,
.proc_release = seq_release, .proc_release = seq_release,
}; };

View File

@ -590,7 +590,7 @@ static int proc_seq_release(struct inode *inode, struct file *file)
static const struct proc_ops proc_seq_ops = { static const struct proc_ops proc_seq_ops = {
/* not permanent -- can call into arbitrary seq_operations */ /* not permanent -- can call into arbitrary seq_operations */
.proc_open = proc_seq_open, .proc_open = proc_seq_open,
.proc_read = seq_read, .proc_read_iter = seq_read_iter,
.proc_lseek = seq_lseek, .proc_lseek = seq_lseek,
.proc_release = proc_seq_release, .proc_release = proc_seq_release,
}; };
@ -621,7 +621,7 @@ static int proc_single_open(struct inode *inode, struct file *file)
static const struct proc_ops proc_single_ops = { static const struct proc_ops proc_single_ops = {
/* not permanent -- can call into arbitrary ->single_show */ /* not permanent -- can call into arbitrary ->single_show */
.proc_open = proc_single_open, .proc_open = proc_single_open,
.proc_read = seq_read, .proc_read_iter = seq_read_iter,
.proc_lseek = seq_lseek, .proc_lseek = seq_lseek,
.proc_release = single_release, .proc_release = single_release,
}; };

View File

@ -597,6 +597,7 @@ static const struct file_operations proc_iter_file_ops = {
.llseek = proc_reg_llseek, .llseek = proc_reg_llseek,
.read_iter = proc_reg_read_iter, .read_iter = proc_reg_read_iter,
.write = proc_reg_write, .write = proc_reg_write,
.splice_read = generic_file_splice_read,
.poll = proc_reg_poll, .poll = proc_reg_poll,
.unlocked_ioctl = proc_reg_unlocked_ioctl, .unlocked_ioctl = proc_reg_unlocked_ioctl,
.mmap = proc_reg_mmap, .mmap = proc_reg_mmap,
@ -622,6 +623,7 @@ static const struct file_operations proc_reg_file_ops_compat = {
static const struct file_operations proc_iter_file_ops_compat = { static const struct file_operations proc_iter_file_ops_compat = {
.llseek = proc_reg_llseek, .llseek = proc_reg_llseek,
.read_iter = proc_reg_read_iter, .read_iter = proc_reg_read_iter,
.splice_read = generic_file_splice_read,
.write = proc_reg_write, .write = proc_reg_write,
.poll = proc_reg_poll, .poll = proc_reg_poll,
.unlocked_ioctl = proc_reg_unlocked_ioctl, .unlocked_ioctl = proc_reg_unlocked_ioctl,

View File

@ -226,7 +226,7 @@ static int stat_open(struct inode *inode, struct file *file)
static const struct proc_ops stat_proc_ops = { static const struct proc_ops stat_proc_ops = {
.proc_flags = PROC_ENTRY_PERMANENT, .proc_flags = PROC_ENTRY_PERMANENT,
.proc_open = stat_open, .proc_open = stat_open,
.proc_read = seq_read, .proc_read_iter = seq_read_iter,
.proc_lseek = seq_lseek, .proc_lseek = seq_lseek,
.proc_release = single_release, .proc_release = single_release,
}; };

View File

@ -18,6 +18,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/string_helpers.h> #include <linux/string_helpers.h>
#include <linux/uio.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/page.h> #include <asm/page.h>
@ -146,7 +147,28 @@ static int traverse(struct seq_file *m, loff_t offset)
*/ */
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{ {
struct seq_file *m = file->private_data; struct iovec iov = { .iov_base = buf, .iov_len = size};
struct kiocb kiocb;
struct iov_iter iter;
ssize_t ret;
init_sync_kiocb(&kiocb, file);
iov_iter_init(&iter, READ, &iov, 1, size);
kiocb.ki_pos = *ppos;
ret = seq_read_iter(&kiocb, &iter);
*ppos = kiocb.ki_pos;
return ret;
}
EXPORT_SYMBOL(seq_read);
/*
* Ready-made ->f_op->read_iter()
*/
ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct seq_file *m = iocb->ki_filp->private_data;
size_t size = iov_iter_count(iter);
size_t copied = 0; size_t copied = 0;
size_t n; size_t n;
void *p; void *p;
@ -158,14 +180,14 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
* if request is to read from zero offset, reset iterator to first * if request is to read from zero offset, reset iterator to first
* record as it might have been already advanced by previous requests * record as it might have been already advanced by previous requests
*/ */
if (*ppos == 0) { if (iocb->ki_pos == 0) {
m->index = 0; m->index = 0;
m->count = 0; m->count = 0;
} }
/* Don't assume *ppos is where we left it */ /* Don't assume ki_pos is where we left it */
if (unlikely(*ppos != m->read_pos)) { if (unlikely(iocb->ki_pos != m->read_pos)) {
while ((err = traverse(m, *ppos)) == -EAGAIN) while ((err = traverse(m, iocb->ki_pos)) == -EAGAIN)
; ;
if (err) { if (err) {
/* With prejudice... */ /* With prejudice... */
@ -174,7 +196,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
m->count = 0; m->count = 0;
goto Done; goto Done;
} else { } else {
m->read_pos = *ppos; m->read_pos = iocb->ki_pos;
} }
} }
@ -187,13 +209,11 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
/* if not empty - flush it first */ /* if not empty - flush it first */
if (m->count) { if (m->count) {
n = min(m->count, size); n = min(m->count, size);
err = copy_to_user(buf, m->buf + m->from, n); if (copy_to_iter(m->buf + m->from, n, iter) != n)
if (err)
goto Efault; goto Efault;
m->count -= n; m->count -= n;
m->from += n; m->from += n;
size -= n; size -= n;
buf += n;
copied += n; copied += n;
if (!size) if (!size)
goto Done; goto Done;
@ -254,8 +274,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
} }
m->op->stop(m, p); m->op->stop(m, p);
n = min(m->count, size); n = min(m->count, size);
err = copy_to_user(buf, m->buf, n); if (copy_to_iter(m->buf, n, iter) != n)
if (err)
goto Efault; goto Efault;
copied += n; copied += n;
m->count -= n; m->count -= n;
@ -264,7 +283,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
if (!copied) if (!copied)
copied = err; copied = err;
else { else {
*ppos += copied; iocb->ki_pos += copied;
m->read_pos += copied; m->read_pos += copied;
} }
mutex_unlock(&m->lock); mutex_unlock(&m->lock);
@ -276,7 +295,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
err = -EFAULT; err = -EFAULT;
goto Done; goto Done;
} }
EXPORT_SYMBOL(seq_read); EXPORT_SYMBOL(seq_read_iter);
/** /**
* seq_lseek - ->llseek() method for sequential files. * seq_lseek - ->llseek() method for sequential files.

View File

@ -107,6 +107,7 @@ void seq_pad(struct seq_file *m, char c);
char *mangle_path(char *s, const char *p, const char *esc); char *mangle_path(char *s, const char *p, const char *esc);
int seq_open(struct file *, const struct seq_operations *); int seq_open(struct file *, const struct seq_operations *);
ssize_t seq_read(struct file *, char __user *, size_t, loff_t *); ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter);
loff_t seq_lseek(struct file *, loff_t, int); loff_t seq_lseek(struct file *, loff_t, int);
int seq_release(struct inode *, struct file *); int seq_release(struct inode *, struct file *);
int seq_write(struct seq_file *seq, const void *data, size_t len); int seq_write(struct seq_file *seq, const void *data, size_t len);