Hi, Neil, This is a kind of urgent issue, and I suggest going with the "m->index++" one in both traverse() and seq_read_iter() first. Once you have a better fix, you can follow up after. Sounds good? On Fri, Jan 29, 2021 at 2:57 PM Xin Long <lucien.xin@xxxxxxxxx> wrote: > > Hi, Neil, > > Thanks for reviewing, more below. > > On Fri, Jan 29, 2021 at 6:56 AM NeilBrown <neilb@xxxxxxx> wrote: > > > > On Fri, Jan 22 2021, Xin Long wrote: > > > > > In commit 1f4aace60b0e ("fs/seq_file.c: simplify seq_file iteration code > > > and interface"), it broke a behavior: op show() is always called when op > > > next() returns an available obj. > > > > Interesting. I was not aware that some callers assumed this guarantee. > > If we are going to support it (which seems reasonable) we should add a > > statement of this guarantee to the documentation - > > Documentation/filesystems/seq_file.rst. > > Maybe a new paragraph after "Finally, the show() function ..." > > > > Note that show() will *always* be called after a successful start() > > or next() call, so that it can release any resources (such as > > ref-counts) that was acquired by those calls. > OK, that's good, will add it. > > > > > > > > > > This caused a refcnt leak in net/sctp/proc.c, as of the seq_operations > > > sctp_assoc_ops, transport obj is held in op next() and released in op > > > show(). > > > > > > Here fix it by moving count check against iov_iter_count after calling > > > op show() so that op show() can still be called when op next() returns > > > an available obj. > > > > > > Note that m->index needs to increase so that op start() could go fetch > > > the next obj in the next round. > > > > This is certainly wrong. > > As the introduction in my patch said: > > > > A large part of achieving this is to *always* call ->next after ->show > > has successfully stored all of an entry in the buffer. Never just > > increment the index instead. > Understand. > > > > > Incrementing ->index in common seq_file code is wrong. > > > > As we are no longer calling ->next after a successful ->show, we need to > > make that ->show appear unsuccessful so that it will be retried. This > > is done be setting "m->count = offs". > > So the moved code below becomes > > > > if (m->count >= iov_iter_count(iter)) { > > /* That record is more than we want, so discard it */ > > m->count = offs; > > break; > > } > But I'm not sure if this's a better way, as discarding it means the last > show() call is just a waste, next time it has to call show() for that > obj again. Note that this is a different case from [1] (show() call > actually failed) and [2](the buffer overflowed), and it makes sense > to call show() again due to [1] and [2] next time. > > if (err > 0) { <---[1] > m->count = offs; > } else if (err || seq_has_overflowed(m)) { <--- [2] > m->count = offs; > break; > } > if (m->count >= iov_iter_count(iter)) { <---[3] > > But for this one [3], all it needs is just enter into seq_read again and > do the copying, no need to discard it. > > > > > Possibly that can be merged into the preceding 'if'. > > > > Also the traverse() function contains a call to ->next that is not > > reliably followed by a call to ->show, even when successful. That needs > > to be fixed too. > Right, But I don't see a way here other than Incrementing m->index in > traverse(): > > @@ -114,16 +114,19 @@ static int traverse(struct seq_file *m, loff_t offset) > } > if (seq_has_overflowed(m)) > goto Eoverflow; > - p = m->op->next(m, p, &m->index); > if (pos + m->count > offset) { > m->from = offset - pos; > m->count -= m->from; > + m->index++; > break; > } > pos += m->count; > m->count = 0; > - if (pos == offset) > + if (pos == offset) { > + m->index++; > break; > + } > + p = m->op->next(m, p, &m->index); > } > > > > > Thanks, > > NeilBrown > > > > > > > > > > > > Fixes: 1f4aace60b0e ("fs/seq_file.c: simplify seq_file iteration code and interface") > > > Reported-by: Prijesh <prpatel@xxxxxxxxxx> > > > Signed-off-by: Xin Long <lucien.xin@xxxxxxxxx> > > > --- > > > fs/seq_file.c | 6 ++++-- > > > 1 file changed, 4 insertions(+), 2 deletions(-) > > > > > > diff --git a/fs/seq_file.c b/fs/seq_file.c > > > index 03a369c..da304f7 100644 > > > --- a/fs/seq_file.c > > > +++ b/fs/seq_file.c > > > @@ -264,8 +264,6 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) > > > } > > > if (!p || IS_ERR(p)) // no next record for us > > > break; > > > - if (m->count >= iov_iter_count(iter)) > > > - break; > > > err = m->op->show(m, p); > > > if (err > 0) { // ->show() says "skip it" > > > m->count = offs; > > > @@ -273,6 +271,10 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) > > > m->count = offs; > > > break; > > > } > > > + if (m->count >= iov_iter_count(iter)) { > > > + m->index++; > > > + break; > > > + } > > > } > > > m->op->stop(m, p); > > > n = copy_to_iter(m->buf, m->count, iter); > > > -- > > > 2.1.0