seq_file: add seq_read_iter

iov_iter based variant for reading a seq_file.  seq_read is
reimplemented on top of the iter variant.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Tested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Christoph Hellwig 2020-11-04 09:27:33 +01:00 committed by Linus Torvalds
parent 3cea11cd5e
commit d4d50710a8
2 changed files with 33 additions and 13 deletions

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);