2003-02-23 03:55:25

by Randy.Dunlap

[permalink] [raw]
Subject: [RFC] seq_file_howto

The "seq_file" interface to the /proc filesystem was introduced in
Linux 2.4.15-pre3 and Linux 2.4.13-ac8. It provides a safer interface
to the /proc filesystem than previous procfs methods because it protects
against overflow of the output buffer. It also provides methods for
traversing a list of kernel items and iterating on that list.
It attempts to provide facilities that are less error-prone that the
previous procfs interfaces.

Overview: seq_file operates by using "pull" methods, pulling or asking
for data from seq_file operations methods, whereas the previous procfs
methods pushed data into output buffers.

The seq_file routines never take any locks between the ->open() and
->stop() functions, so seq_file callers are free to use anything --
spinlocks, etc.

The seq_file interface does require more data structures to be setup
to point to methods that are used during seq_file access. These four
methods are in struct seq_operations:

struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};

.start: lock whatever you need to lock and return an entry by number;
sets the iterator up and returns the first element of sequence::

.start is used to initialize data for walking through a list of kernel
items. This list can be an array, a linked list, a hash table, etc.
Its actual data type doesn't matter.

If there is any locking that needs to be done to iterate through the
kernel list, the lock(s) can be acquired in the .start method. However,
if the .show method is very time-consuming and the .show method lends
itself to locking there, that may be a better place for it.

.stop: done with seq_file, unlock or free resources::

The .stop method is called after the .next method has nothing more to
do. This method is used for cleanups and unlocking etc.
The .stop method is always called if the .start method was called, even
if the .start method fails, so that all cleanups can be done in .stop.

.next: returns the next element (entry) of sequence::

The .next method is the kernel item iterator. It advances to the next
item of interest to be shown and indicates when there are no more such
items by returning NULL or an error (like -ENOMEM or -EACCES).

.show: show an entry, using seq_...() as you would use stdio functions::

The .show method is used to put data (static headings or variable
data) into the seq_file output buffer. It uses seq_{putc, puts, printf, ...}
to format the output.


The seq_file output methods are:

/*
* seq_putc:
* print one character to the seq_file output buffer
* returns 0 for success or -1 for error
*/
int seq_putc(struct seq_file *m, char c);

/*
* seq_puts:
* print a null-terminated string to the seq_file output buffer
* returns 0 for success or -1 for error
*/
int seq_puts(struct seq_file *m, const char *s);

/*
* seq_printf:
* print a formatted string and variable data to the seq_file output buffer
* returns 0 for success or -1 for error
*/
int seq_printf(struct seq_file *m, const char *f, ...);

/*
* seq_open: initialize sequential file
* @file: file to initialize
* @op: method table describing the sequence
*
* seq_open() sets @file, associating it with a sequence described
* by @op. @op->start() sets the iterator up and returns the first
* element of sequence. @op->stop() shuts it down. @op->next()
* returns the next element of sequence. @op->show() prints element
* into the buffer. In case of error ->start() and ->next() return
* ERR_PTR(error). In the end of sequence they return %NULL. ->show()
* returns 0 in case of success and negative number in case of error.
*/
int seq_open(struct file *file, struct seq_operations *op);

/*
* seq_read: ->read() method for sequential files.
* @file, @buf, @size, @ppos: see file_operations method
*
* Ready-made ->f_op->read()
*/
ssize_t seq_read(struct file *file, char *buf, size_t size, loff_t *ppos);

/*
* seq_lseek: ->llseek() method for sequential files.
* @file, @offset, @origin: see file_operations method
*
* Ready-made ->f_op->llseek()
*/
loff_t seq_lseek(struct file *file, loff_t offset, int origin);

/*
* seq_release: free the structures associated with sequential file.
* @file: file in question
* @inode: file->f_dentry->d_inode
*
* Frees the structures associated with sequential file; can be used
* as ->f_op->release() if you don't have private data to destroy.
*/
int seq_release(struct inode *inode, struct file *file);

/*
* seq_escape: print string into buffer, escaping some characters
* @m: target buffer
* @s: string
* @esc: set of characters that need escaping
*
* Puts string into buffer, replacing each occurence of character from
* @esc with usual octal escape. Returns 0 in case of success
* or -1 in case of overflow.
*/
int seq_escape(struct seq_file *m, const char *s, const char *esc);


If you only need a single function entry, just use single_open() and
single_release(). TBD: FIXME/MORE.

###


Attachments:
seq_file_howto.txt (4.98 kB)

2003-02-23 04:35:39

by Randy.Dunlap

[permalink] [raw]
Subject: Re: [RFC] seq_file_howto

The "seq_file" interface to the /proc filesystem was introduced in
Linux 2.4.15-pre3 and Linux 2.4.13-ac8. It provides a safer interface
to the /proc filesystem than previous procfs methods because it protects
against overflow of the output buffer. It also provides methods for
traversing a list of kernel items and iterating on that list.
It attempts to provide facilities that are less error-prone that the
previous procfs interfaces.

Overview: seq_file operates by using "pull" methods, pulling or asking
for data from seq_file operations methods, whereas the previous procfs
methods pushed data into output buffers.

The seq_file routines never take any locks between the ->open() and
->stop() functions, so seq_file callers are free to use anything --
spinlocks, etc.

The seq_file interface does require more data structures to be setup
to point to methods that are used during seq_file access. These four
methods are in struct seq_operations:

struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};

.start: lock whatever you need to lock and return an entry by number;
sets the iterator up and returns the first element of sequence::

.start is used to initialize data for walking through a list of kernel
items. This list can be an array, a linked list, a hash table, etc.
Its actual data type doesn't matter.

If there is any locking that needs to be done to iterate through the
kernel list, the lock(s) can be acquired in the .start method. However,
if the .show method is very time-consuming and the .show method lends
itself to locking there, that may be a better place for it.

.stop: done with seq_file, unlock or free resources::

The .stop method is called after the .next method has nothing more to
do. This method is used for cleanups and unlocking etc.
The .stop method is always called if the .start method was called, even
if the .start method fails, so that all cleanups can be done in .stop.

.next: returns the next element (entry) of sequence::

The .next method is the kernel item iterator. It advances to the next
item of interest to be shown and indicates when there are no more such
items by returning NULL or an error (like -ENOMEM or -EACCES).

.show: show an entry, using seq_...() as you would use stdio functions::

The .show method is used to put data (static headings or variable
data) into the seq_file output buffer. It uses seq_{putc, puts, printf, ...}
to format the output.


The seq_file output methods are:

/*
* seq_putc:
* print one character to the seq_file output buffer
* returns 0 for success or -1 for error
*/
int seq_putc(struct seq_file *m, char c);

/*
* seq_puts:
* print a null-terminated string to the seq_file output buffer
* returns 0 for success or -1 for error
*/
int seq_puts(struct seq_file *m, const char *s);

/*
* seq_printf:
* print a formatted string and variable data to the seq_file output buffer
* returns 0 for success or -1 for error
*/
int seq_printf(struct seq_file *m, const char *f, ...);

/*
* seq_open: initialize sequential file
* @file: file to initialize
* @op: method table describing the sequence
*
* seq_open() sets @file, associating it with a sequence described
* by @op. @op->start() sets the iterator up and returns the first
* element of sequence. @op->stop() shuts it down. @op->next()
* returns the next element of sequence. @op->show() prints element
* into the buffer. In case of error ->start() and ->next() return
* ERR_PTR(error). In the end of sequence they return %NULL. ->show()
* returns 0 in case of success and negative number in case of error.
*/
int seq_open(struct file *file, struct seq_operations *op);

/*
* seq_read: ->read() method for sequential files.
* @file, @buf, @size, @ppos: see file_operations method
*
* Ready-made ->f_op->read()
*/
ssize_t seq_read(struct file *file, char *buf, size_t size, loff_t *ppos);

/*
* seq_lseek: ->llseek() method for sequential files.
* @file, @offset, @origin: see file_operations method
*
* Ready-made ->f_op->llseek()
*/
loff_t seq_lseek(struct file *file, loff_t offset, int origin);

/*
* seq_release: free the structures associated with sequential file.
* @file: file in question
* @inode: file->f_dentry->d_inode
*
* Frees the structures associated with sequential file; can be used
* as ->f_op->release() if you don't have private data to destroy.
*/
int seq_release(struct inode *inode, struct file *file);

/*
* seq_escape: print string into buffer, escaping some characters
* @m: target buffer
* @s: string
* @esc: set of characters that need escaping
*
* Puts string into buffer, replacing each occurence of character from
* @esc with usual octal escape. Returns 0 in case of success
* or -1 in case of overflow.
*/
int seq_escape(struct seq_file *m, const char *s, const char *esc);


If you only need a single function entry, just use single_open() and
single_release().

single_open() gets a parameter that is the "show" function for the data
that is to be written to /proc. The "show" function does everything
that is needed to write the data, all in one function call. This is
useful either for writing small amounts of data to /proc, for cases in
which the output is not iterative, or for cases in which recursion is
more appropriate, since the non-single methods don't fit well with
recursive techniques. Examples of appropriate uses of single_open()
"show" functions are:

linux/kernel/dma.c::proc_dma_show() : small quantity of data to write
linux/net/ipv4/proc.c::sockstat_seq_show() : non-iterative data
linux/net/sunrpc/rpc_pipe.c::rpc_show_info() : non-iterative data

###


Attachments:
seq_file_howto.txt (5.66 kB)

2003-02-23 10:02:07

by Andries Brouwer

[permalink] [raw]
Subject: Re: [RFC] seq_file_howto

On Sat, Feb 22, 2003 at 08:03:17PM -0800, Randy.Dunlap wrote:

> acme prodded me into doing this a few weeks (or months?) ago.
> It still needs some additional info for using single_open()
> and single_release(), but I'd like to get some comments on it
> and then add it to linux/Documentation/filesystems/ or post it
> on the web somewhere, like kernelnewbies.org.
>
> Comments, corrections?

By some coincidence I also wrote some text recently.
Take whatever you want from the below.
(For example, this mentions the use of private_data.)

Andries

<sect2>seqfiles<p>
Some infrastructure exists for producing generated proc files
that are larger than a single page. The call
<verb>
create_seq_entry("foo", mode, &amp;proc_foo_operations);
</verb>
will create a file <tt>/proc/foo</tt> with given mode
sich that opening it yields a file with <tt>proc_foo_operations</tt>
as struct file_operations. Typically one has something like
<verb>
static struct file_operations proc_foo_operations = {
.open = foo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
</verb>
where <tt>foo_open</tt> is defined as
<verb>
static int foo_open(struct inode *inode, struct file *file)
{
return seq_open(file, &amp;foo_op);
}
</verb>
and <tt>foo_op</tt> is a <tt>struct seq_operations</tt>:
<verb>
struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};
</verb>
<p>
The routines <tt>seq_open()</tt> etc. are defined in <tt>seq_file.c</tt>.
Here <tt>seq_open()</tt> initializes a <tt>struct seq_file</tt> and
attaches it to the <tt>private_data</tt> field of the file structure:
<verb>
struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
struct semaphore sem;
struct seq_operations *op;
void *private;
};
</verb>
(Use a buffer <tt>buf</tt> of size <tt>size</tt>. It still contains
<tt>count</tt> unread bytes, starting from buf offset <tt>from</tt>.
We return a sequence of items, and <tt>index</tt> is the current
serial number. To get a new item, call <tt>op->start()</tt>
followed by <tt>op->show()</tt>, then a number of times
<tt>op->next()</tt> followed by <tt>op->show</tt>, as long as more items
fit in the user-supplied buffer, and finally <tt>op->stop()</tt>.
Thus, the start routine can get locks or down semaphores, and the
stop routine can unlock or up them again.)

2003-02-24 00:40:05

by Randy.Dunlap

[permalink] [raw]
Subject: Re: [RFC] seq_file_howto

At 11:10 AM 2/23/2003 +0100, Andries Brouwer wrote:
>On Sat, Feb 22, 2003 at 08:03:17PM -0800, Randy.Dunlap wrote:
>
> > acme prodded me into doing this a few weeks (or months?) ago.
> > It still needs some additional info for using single_open()
> > and single_release(), but I'd like to get some comments on it
> > and then add it to linux/Documentation/filesystems/ or post it
> > on the web somewhere, like kernelnewbies.org.
> >
> > Comments, corrections?
>
>By some coincidence I also wrote some text recently.
>Take whatever you want from the below.
>(For example, this mentions the use of private_data.)
>
>Andries

Thanks, I'll merge them.
I was going to add some data structure info as well.

~Randy


2003-02-25 20:27:45

by Jonathan Corbet

[permalink] [raw]
Subject: Re: [RFC] seq_file_howto

> acme prodded me into doing this a few weeks (or months?) ago.
> It still needs some additional info for using single_open()
> and single_release(), but I'd like to get some comments on it
> and then add it to linux/Documentation/filesystems/ or post it
> on the web somewhere, like kernelnewbies.org.

Gee...I did one too, including a simple sample module which shows how it
all works; see http://lwn.net/Articles/22355/. It's part of the larger
"driver porting" series at http://lwn.net/Articles/driver-porting/; there's
a dozen articles there now, most of which have passed out of the
subscription period and are freely available.

jon

Jonathan Corbet
Executive editor, LWN.net
[email protected]