Skip to content

Commit 1f4aace

Browse files
NeilBrowntorvalds
authored andcommitted
fs/seq_file.c: simplify seq_file iteration code and interface
The documentation for seq_file suggests that it is necessary to be able to move the iterator to a given offset, however that is not the case. If the iterator is stored in the private data and is stable from one read() syscall to the next, it is only necessary to support first/next interactions. Implementing this in a client is a little clumsy. - if ->start() is given a pos of zero, it should go to start of sequence. - if ->start() is given the name pos that was given to the most recent next() or start(), it should restore the iterator to state just before that last call - if ->start is given another number, it should set the iterator one beyond the start just before the last ->start or ->next call. Also, the documentation says that the implementation can interpret the pos however it likes (other than zero meaning start), but seq_file increments the pos sometimes which does impose on the implementation. This patch simplifies the interface for first/next iteration and simplifies the code, while maintaining complete backward compatability. Now: - if ->start() is given a pos of zero, it should return an iterator placed at the start of the sequence - if ->start() is given a non-zero pos, it should return the iterator in the same state it was after the last ->start or ->next. This is particularly useful for interators which walk the multiple chains in a hash table, e.g. using rhashtable_walk*. See fs/gfs2/glock.c and drivers/staging/lustre/lustre/llite/vvp_dev.c 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. Also: - always pass &m->index to ->start() and ->next(), never a temp variable - don't clear ->from when ->count is zero, as ->from is dead when ->count is zero. Some ->next functions do not increment *pos when they return NULL. To maintain compatability with this, we still need to increment m->index in one place, if ->next didn't increment it. Note that such ->next functions are buggy and should be fixed. A simple demonstration is dd if=/proc/swaps bs=1000 skip=1 Choose any block size larger than the size of /proc/swaps. This will always show the whole last line of /proc/swaps. This patch doesn't work around buggy next() functions for this case. [[email protected]: ensure ->from is valid] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: NeilBrown <[email protected]> Acked-by: Jonathan Corbet <[email protected]> [docs] Tested-by: Jann Horn <[email protected]> Cc: Alexander Viro <[email protected]> Cc: Kees Cook <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 4cdfffc commit 1f4aace

File tree

2 files changed

+63
-54
lines changed

2 files changed

+63
-54
lines changed

Documentation/filesystems/seq_file.txt

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,39 @@ kernel 3.10. Current versions require the following update
6666

6767
The iterator interface
6868

69-
Modules implementing a virtual file with seq_file must implement a simple
70-
iterator object that allows stepping through the data of interest.
71-
Iterators must be able to move to a specific position - like the file they
72-
implement - but the interpretation of that position is up to the iterator
73-
itself. A seq_file implementation that is formatting firewall rules, for
74-
example, could interpret position N as the Nth rule in the chain.
75-
Positioning can thus be done in whatever way makes the most sense for the
76-
generator of the data, which need not be aware of how a position translates
77-
to an offset in the virtual file. The one obvious exception is that a
78-
position of zero should indicate the beginning of the file.
69+
Modules implementing a virtual file with seq_file must implement an
70+
iterator object that allows stepping through the data of interest
71+
during a "session" (roughly one read() system call). If the iterator
72+
is able to move to a specific position - like the file they implement,
73+
though with freedom to map the position number to a sequence location
74+
in whatever way is convenient - the iterator need only exist
75+
transiently during a session. If the iterator cannot easily find a
76+
numerical position but works well with a first/next interface, the
77+
iterator can be stored in the private data area and continue from one
78+
session to the next.
79+
80+
A seq_file implementation that is formatting firewall rules from a
81+
table, for example, could provide a simple iterator that interprets
82+
position N as the Nth rule in the chain. A seq_file implementation
83+
that presents the content of a, potentially volatile, linked list
84+
might record a pointer into that list, providing that can be done
85+
without risk of the current location being removed.
86+
87+
Positioning can thus be done in whatever way makes the most sense for
88+
the generator of the data, which need not be aware of how a position
89+
translates to an offset in the virtual file. The one obvious exception
90+
is that a position of zero should indicate the beginning of the file.
7991

8092
The /proc/sequence iterator just uses the count of the next number it
8193
will output as its position.
8294

83-
Four functions must be implemented to make the iterator work. The first,
84-
called start() takes a position as an argument and returns an iterator
85-
which will start reading at that position. For our simple sequence example,
95+
Four functions must be implemented to make the iterator work. The
96+
first, called start(), starts a session and takes a position as an
97+
argument, returning an iterator which will start reading at that
98+
position. The pos passed to start() will always be either zero, or
99+
the most recent pos used in the previous session.
100+
101+
For our simple sequence example,
86102
the start() function looks like:
87103

88104
static void *ct_seq_start(struct seq_file *s, loff_t *pos)
@@ -101,11 +117,12 @@ implementations; in most cases the start() function should check for a
101117
"past end of file" condition and return NULL if need be.
102118

103119
For more complicated applications, the private field of the seq_file
104-
structure can be used. There is also a special value which can be returned
105-
by the start() function called SEQ_START_TOKEN; it can be used if you wish
106-
to instruct your show() function (described below) to print a header at the
107-
top of the output. SEQ_START_TOKEN should only be used if the offset is
108-
zero, however.
120+
structure can be used to hold state from session to session. There is
121+
also a special value which can be returned by the start() function
122+
called SEQ_START_TOKEN; it can be used if you wish to instruct your
123+
show() function (described below) to print a header at the top of the
124+
output. SEQ_START_TOKEN should only be used if the offset is zero,
125+
however.
109126

110127
The next function to implement is called, amazingly, next(); its job is to
111128
move the iterator forward to the next position in the sequence. The
@@ -121,9 +138,13 @@ complete. Here's the example version:
121138
return spos;
122139
}
123140

124-
The stop() function is called when iteration is complete; its job, of
125-
course, is to clean up. If dynamic memory is allocated for the iterator,
126-
stop() is the place to free it.
141+
The stop() function closes a session; its job, of course, is to clean
142+
up. If dynamic memory is allocated for the iterator, stop() is the
143+
place to free it; if a lock was taken by start(), stop() must release
144+
that lock. The value that *pos was set to by the last next() call
145+
before stop() is remembered, and used for the first start() call of
146+
the next session unless lseek() has been called on the file; in that
147+
case next start() will be asked to start at position zero.
127148

128149
static void ct_seq_stop(struct seq_file *s, void *v)
129150
{

fs/seq_file.c

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,22 @@ EXPORT_SYMBOL(seq_open);
9090

9191
static int traverse(struct seq_file *m, loff_t offset)
9292
{
93-
loff_t pos = 0, index;
93+
loff_t pos = 0;
9494
int error = 0;
9595
void *p;
9696

9797
m->version = 0;
98-
index = 0;
98+
m->index = 0;
9999
m->count = m->from = 0;
100-
if (!offset) {
101-
m->index = index;
100+
if (!offset)
102101
return 0;
103-
}
102+
104103
if (!m->buf) {
105104
m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
106105
if (!m->buf)
107106
return -ENOMEM;
108107
}
109-
p = m->op->start(m, &index);
108+
p = m->op->start(m, &m->index);
110109
while (p) {
111110
error = PTR_ERR(p);
112111
if (IS_ERR(p))
@@ -123,20 +122,15 @@ static int traverse(struct seq_file *m, loff_t offset)
123122
if (pos + m->count > offset) {
124123
m->from = offset - pos;
125124
m->count -= m->from;
126-
m->index = index;
127125
break;
128126
}
129127
pos += m->count;
130128
m->count = 0;
131-
if (pos == offset) {
132-
index++;
133-
m->index = index;
129+
p = m->op->next(m, p, &m->index);
130+
if (pos == offset)
134131
break;
135-
}
136-
p = m->op->next(m, p, &index);
137132
}
138133
m->op->stop(m, p);
139-
m->index = index;
140134
return error;
141135

142136
Eoverflow:
@@ -160,7 +154,6 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
160154
{
161155
struct seq_file *m = file->private_data;
162156
size_t copied = 0;
163-
loff_t pos;
164157
size_t n;
165158
void *p;
166159
int err = 0;
@@ -223,16 +216,12 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
223216
size -= n;
224217
buf += n;
225218
copied += n;
226-
if (!m->count) {
227-
m->from = 0;
228-
m->index++;
229-
}
230219
if (!size)
231220
goto Done;
232221
}
233222
/* we need at least one record in buffer */
234-
pos = m->index;
235-
p = m->op->start(m, &pos);
223+
m->from = 0;
224+
p = m->op->start(m, &m->index);
236225
while (1) {
237226
err = PTR_ERR(p);
238227
if (!p || IS_ERR(p))
@@ -243,8 +232,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
243232
if (unlikely(err))
244233
m->count = 0;
245234
if (unlikely(!m->count)) {
246-
p = m->op->next(m, p, &pos);
247-
m->index = pos;
235+
p = m->op->next(m, p, &m->index);
248236
continue;
249237
}
250238
if (m->count < m->size)
@@ -256,29 +244,33 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
256244
if (!m->buf)
257245
goto Enomem;
258246
m->version = 0;
259-
pos = m->index;
260-
p = m->op->start(m, &pos);
247+
p = m->op->start(m, &m->index);
261248
}
262249
m->op->stop(m, p);
263250
m->count = 0;
264251
goto Done;
265252
Fill:
266253
/* they want more? let's try to get some more */
267-
while (m->count < size) {
254+
while (1) {
268255
size_t offs = m->count;
269-
loff_t next = pos;
270-
p = m->op->next(m, p, &next);
256+
loff_t pos = m->index;
257+
258+
p = m->op->next(m, p, &m->index);
259+
if (pos == m->index)
260+
/* Buggy ->next function */
261+
m->index++;
271262
if (!p || IS_ERR(p)) {
272263
err = PTR_ERR(p);
273264
break;
274265
}
266+
if (m->count >= size)
267+
break;
275268
err = m->op->show(m, p);
276269
if (seq_has_overflowed(m) || err) {
277270
m->count = offs;
278271
if (likely(err <= 0))
279272
break;
280273
}
281-
pos = next;
282274
}
283275
m->op->stop(m, p);
284276
n = min(m->count, size);
@@ -287,11 +279,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
287279
goto Efault;
288280
copied += n;
289281
m->count -= n;
290-
if (m->count)
291-
m->from = n;
292-
else
293-
pos++;
294-
m->index = pos;
282+
m->from = n;
295283
Done:
296284
if (!copied)
297285
copied = err;

0 commit comments

Comments
 (0)