Re: [PATCH 1/6] seq_file: add seq_read_iter

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, Nov 15, 2020 at 11:38:14PM +0000, Al Viro wrote:
> On Sun, Nov 15, 2020 at 02:41:25PM -0700, Nathan Chancellor wrote:
> > Hi Al,
> > 
> > Apologies for the delay.
> > 
> > On Sun, Nov 15, 2020 at 03:53:55PM +0000, Al Viro wrote:
> > > On Sat, Nov 14, 2020 at 08:50:00PM +0000, Al Viro wrote:
> > > 
> > > OK, I think I understand what's going on.  Could you check if
> > > reverting the variant in -next and applying the following instead
> > > fixes what you are seeing?
> > 
> > The below diff on top of d4d50710a8b46082224376ef119a4dbb75b25c56 does
> > not fix my issue unfortunately.
> 
> OK...  Now that I have a reproducer[1], I think I've sorted it out.
> And yes, it had been too subtle for its own good ;-/
> 
> [1] I still wonder what the hell in the userland has come up with the
> idea of reading through a file with readv(), each time with 2-element
> iovec array, the first element covering 0 bytes and the second one - 1024.
> AFAICS, nothing is systemd git appears to be _that_ weird...  Makes for
> a useful testcase, though...
> 
> Anyway, could you test this replacement?

Looks good to me on top of d4d50710a8b46082224376ef119a4dbb75b25c56,
thanks for quickly looking into this!

Tested-by: Nathan Chancellor <natechancellor@xxxxxxxxx>

> diff --git a/fs/seq_file.c b/fs/seq_file.c
> index 3b20e21604e7..c0dfe2861b35 100644
> --- a/fs/seq_file.c
> +++ b/fs/seq_file.c
> @@ -168,12 +168,14 @@ EXPORT_SYMBOL(seq_read);
>  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 n;
>  	void *p;
>  	int err = 0;
>  
> +	if (!iov_iter_count(iter))
> +		return 0;
> +
>  	mutex_lock(&m->lock);
>  
>  	/*
> @@ -208,34 +210,32 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>  	}
>  	/* if not empty - flush it first */
>  	if (m->count) {
> -		n = min(m->count, size);
> -		if (copy_to_iter(m->buf + m->from, n, iter) != n)
> -			goto Efault;
> +		n = copy_to_iter(m->buf + m->from, m->count, iter);
>  		m->count -= n;
>  		m->from += n;
> -		size -= n;
>  		copied += n;
> -		if (!size)
> +		if (m->count)	// hadn't managed to copy everything
>  			goto Done;
>  	}
> -	/* we need at least one record in buffer */
> +	/* we need at least one non-empty record in the buffer */
>  	m->from = 0;
>  	p = m->op->start(m, &m->index);
>  	while (1) {
>  		err = PTR_ERR(p);
> -		if (!p || IS_ERR(p))
> +		if (!p || IS_ERR(p))	// EOF or an error
>  			break;
>  		err = m->op->show(m, p);
> -		if (err < 0)
> +		if (err < 0)		// hard error
>  			break;
> -		if (unlikely(err))
> +		if (unlikely(err))	// ->show() says "skip it"
>  			m->count = 0;
> -		if (unlikely(!m->count)) {
> +		if (unlikely(!m->count)) { // empty record
>  			p = m->op->next(m, p, &m->index);
>  			continue;
>  		}
> -		if (m->count < m->size)
> +		if (!seq_has_overflowed(m)) // got it
>  			goto Fill;
> +		// need a bigger buffer
>  		m->op->stop(m, p);
>  		kvfree(m->buf);
>  		m->count = 0;
> @@ -244,11 +244,14 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>  			goto Enomem;
>  		p = m->op->start(m, &m->index);
>  	}
> +	// EOF or an error
>  	m->op->stop(m, p);
>  	m->count = 0;
>  	goto Done;
>  Fill:
> -	/* they want more? let's try to get some more */
> +	// one non-empty record is in the buffer; if they want more,
> +	// try to fit more in, but in any case we need to advance
> +	// the iterator at least once.
>  	while (1) {
>  		size_t offs = m->count;
>  		loff_t pos = m->index;
> @@ -259,11 +262,9 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>  					    m->op->next);
>  			m->index++;
>  		}
> -		if (!p || IS_ERR(p)) {
> -			err = PTR_ERR(p);
> +		if (!p || IS_ERR(p))	// no next record for us
>  			break;
> -		}
> -		if (m->count >= size)
> +		if (m->count >= iov_iter_count(iter))
>  			break;
>  		err = m->op->show(m, p);
>  		if (seq_has_overflowed(m) || err) {
> @@ -273,16 +274,14 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>  		}
>  	}
>  	m->op->stop(m, p);
> -	n = min(m->count, size);
> -	if (copy_to_iter(m->buf, n, iter) != n)
> -		goto Efault;
> +	n = copy_to_iter(m->buf, m->count, iter);
>  	copied += n;
>  	m->count -= n;
>  	m->from = n;
>  Done:
> -	if (!copied)
> -		copied = err;
> -	else {
> +	if (unlikely(!copied)) {
> +		copied = m->count ? -EFAULT : err;
> +	} else {
>  		iocb->ki_pos += copied;
>  		m->read_pos += copied;
>  	}
> @@ -291,9 +290,6 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter)
>  Enomem:
>  	err = -ENOMEM;
>  	goto Done;
> -Efault:
> -	err = -EFAULT;
> -	goto Done;
>  }
>  EXPORT_SYMBOL(seq_read_iter);
>  



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux