LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH] Add seq_file howto to Documentation/
@ 2007-07-23 19:45 Martin Bligh
  2007-07-23 20:09 ` Jonathan Corbet
  0 siblings, 1 reply; 4+ messages in thread
From: Martin Bligh @ 2007-07-23 19:45 UTC (permalink / raw)
  To: LKML; +Cc: Randy Dunlap

[-- Attachment #1: Type: text/plain, Size: 140 bytes --]

Add seq_file howto to Documentation/

Signed-off-by: Martin J. Bligh <mbligh@google.com>

Taken from kernelnewbies with Randy's permission.

[-- Attachment #2: 2.6.22-seq_file_doc --]
[-- Type: text/plain, Size: 9925 bytes --]

diff -aurpN -X /home/mbligh/.diff.exclude linux-2.6.22/Documentation/seq_file_howto.txt 2.6.22-seq_file_doc/Documentation/seq_file_howto.txt
--- linux-2.6.22/Documentation/seq_file_howto.txt	1969-12-31 16:00:00.000000000 -0800
+++ 2.6.22-seq_file_doc/Documentation/seq_file_howto.txt	2007-07-23 12:41:24.000000000 -0700
@@ -0,0 +1,246 @@
+Linux kernel seq_file HOWTO
+Randy Dunlap  <rddunlap@osdl.org>
+v0: 2003-02-22
+v1: 2003-03-14
+
+Parts of this seq_file HOWTO were contributed by Andries Brouwer
+(aeb%win!tue!nl).
+
+[Another seq_file reference is "Driver porting: The seq_file interface"
+at <http://lwn.net/Articles/22355/>, which is part of the
+LWN.net series "Porting Drivers to 2.5" that is located at
+<http://lwn.net/Articles/driver-porting/>.]
+
+======================================================================
+
+* Introduction
+
+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 and easily handles procfs files
+that are larger than one page.  It also provides methods for
+traversing a list of kernel items and iterating on that list.
+It provides procfs output facilities that are less error-prone than
+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.
+
+======================================================================
+
+* seq_file creation and data structures
+
+A seq_file-type /proc file is created by using create_proc_entry() and
+setting the resulting proc_dir_entry->proc_fops pointer to the
+desired struct file_operations.  E.g. (from linux/mm/swapfile.c):
+
+static int __init procswaps_init(void)
+{
+	struct proc_dir_entry *entry;
+
+	entry = create_proc_entry("swaps", 0, NULL);
+	if (entry)
+		entry->proc_fops = &proc_swaps_operations;
+	return 0;
+}
+
+struct file_operations for the seq_file-type proc file describes 4
+I/O methods, e.g.:
+
+static struct file_operations proc_swaps_operations = {
+	.open		= swaps_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+The last 3 are reusable seq_file-supplied methods.  The open method
+is what must be supplied for each proc file, and that open()
+function only needs to call seq_open() with a pointer to a struct
+seq_operations descriptor.  E.g. (still from linux/mm/swapfile.c):
+
+static int swaps_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &swaps_op);
+}
+
+struct seq_file seq_operations swaps_op supplies 4 methods for
+producing seq_file output:  start(), next(), stop(), and show().
+These are described below.  The structure typically looks like:
+
+static struct seq_operations swaps_op = {
+	.start =	swap_start,
+	.next =		swap_next,
+	.stop =		swap_stop,
+	.show =		swap_show
+};
+
+======================================================================
+
+* seq_file methods
+
+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.  In return
+for this, you (we) get much safer /proc output methods.  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);
+};
+
+The .start method 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.  This function should lock
+whatever needs to be locked for safety and return an entry by number
+(0 for the first entry).  This method should also honor file offset
+semantics by using the "loff_t *pos" (second) parameter.
+The "entry number" value is passed to the stop, next, and show
+methods as the "void *v" parameter.
+In case of error, return ERR_PTR(error_code).
+
+If you need to show a header line or something, then return
+SEQ_START_TOKEN in your start() and recognise that in next() and
+show(). IOW, pos == 0 will be the header line, and pos == 1
+will correspond to the first actual item on your list, and so on.
+See net/netlink/af_netlink.c for a simple example.
+
+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.
+
+struct seq_file contains a "void *private" that can be used by the
+struct seq_operations functions to hold any private data that needs
+to be available to all of these related methods.  For example, the
+.start method might allocate some memory and save its address in
+seq_file.private so that the .next and .show methods can use it,
+then the .stop method would free that memory.
+
+The .stop method is called after the .next method has nothing more to
+do.  This method is used for cleanups, unlocking, freeing resources, 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.
+
+The .next method is the iterator for the items (list, array, table, etc.)
+that is being traversed for /proc file output.  It advances to the next
+item of interest to be shown in the /proc output file and indicates
+when there are no more items by returning NULL or an error (like -ENOMEM
+or -EACCES).  If there are more items to be shown, it returns the next
+element (entry) of the sequence by entry number.
+
+The .show method is used to show an entry (write output to the /proc
+file) by using seq_...() as you would use stdio functions.  It can
+write static headings or variable data into the seq_file output buffer.
+It uses seq_{putc, puts, printf, ...} to format the output (see below).
+In case of error, return a negative error_code; otherwise return 0.
+
+======================================================================
+
+* seq_file output routines
+
+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);
+
+======================================================================
+
+* Simplified seq_file methods
+
+If you only need a single function entry (call) to produce all the
+desired proc-fs output, 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
+
+###

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add seq_file howto to Documentation/ 
  2007-07-23 19:45 [PATCH] Add seq_file howto to Documentation/ Martin Bligh
@ 2007-07-23 20:09 ` Jonathan Corbet
  2008-02-29 19:18   ` Randy Dunlap
  0 siblings, 1 reply; 4+ messages in thread
From: Jonathan Corbet @ 2007-07-23 20:09 UTC (permalink / raw)
  To: Martin Bligh; +Cc: Randy Dunlap, linux-kernel

> +[Another seq_file reference is "Driver porting: The seq_file interface"
> +at <http://lwn.net/Articles/22355/>, which is part of the
> +LWN.net series "Porting Drivers to 2.5" that is located at
> +<http://lwn.net/Articles/driver-porting/>.]

Funny thing, that...I had sent in a version of that document for
consideration back in 2003.  Guess that was still the Good Old Days when
the patch management system was rather lossier, so I never heard back.
Here it is again just in case anybody's interested.

In general, I'm more than happy to have anything I put on LWN go into
Documentation/ if there's interest - just say the word.

jon


To: linux-kernel@vger.kernel.org
Subject: [PATCH] seq_file documentation
cc: torvalds@osdl.org
From: Jonathan Corbet <corbet@lwn.net>
Date: Thu, 13 Nov 2003 10:16:31 -0700
Sender: corbet

I went ahead and packaged up my seq_file document as a basic text file, in
the interests of getting something out there.  

jon

Jonathan Corbet
Executive editor, LWN.net
corbet@lwn.net


diff -urN test9-vanilla/Documentation/filesystems/seq_file.txt test9/Documentation/filesystems/seq_file.txt
--- test9-vanilla/Documentation/filesystems/seq_file.txt	Wed Dec 31 17:00:00 1969
+++ test9/Documentation/filesystems/seq_file.txt	Fri Nov 14 01:03:58 2003
@@ -0,0 +1,268 @@
+The seq_file interface
+
+	Copyright 2003 Jonathan Corbet <corbet@lwn.net>
+	This file is originally from the LWN.net Driver Porting series at
+	http://lwn.net/Articles/driver-porting/
+
+
+There are numerous ways for a device driver (or other kernel component) to
+provide information to the user or system administrator.  One very useful
+technique is the creation of virtual files, in /proc or elsewhere. Virtual
+files can provide human-readable output that is easy to get at without any
+special utility programs; they can also make life easier for script
+writers. It is not surprising that the use of virtual files has grown over
+the years.
+
+Creating those files correctly has always been a bit of a challenge,
+however. It is not that hard to make a /proc file which returns a
+string. But life gets trickier if the output is long - anything greater
+than an application is likely to read in a single operation.  Handling
+multiple reads (and seeks) requires careful attention to the reader's
+position within the virtual file - that position is, likely as not, in the
+middle of a line of output. The kernel is full of /proc file
+implementations that get this wrong.
+
+The 2.6 kernel contains a set of functions (implemented by Alexander Viro)
+which are designed to make it easy for virtual file creators to get it
+right. This interface (called "seq_file") is not strictly a 2.6 feature -
+it was also merged into 2.4.15.
+
+The seq_file interface is available via <linux/seq_file.h>. There are
+three aspects to seq_file:
+
+     * An iterator interface which lets a virtual file implementation
+       step through the objects it is presenting.
+
+     * Some utility functions for formatting objects for output without
+       needing to worry about things like output buffers.
+
+     * A set of canned file_operations which implement most operations on
+       the virtual file.
+
+We'll look at the seq_file interface via an extremely simple example: a
+loadable module which creates a file called /proc/sequence. The file, when
+read, simply produces a set of increasing integer values, one per line. The
+sequence will continue until the user loses patience and finds something
+better to do. The file is seekable, in that one can do something like the
+following:
+
+    dd if=/proc/sequence of=out1 count=1
+    dd if=/proc/sequence skip=1 out=out2 count=1
+
+Then concatenate the output files out1 and out2 and get the right
+result. Yes, it is a thoroughly useless module, but the point is to show
+how the mechanism works without getting lost in other details.  (Those
+wanting to see the full source for this module can find it at
+http://lwn.net/Articles/22359/).
+
+
+The iterator interface
+
+Modules implementing a virtual file with seq_file must implement a simple
+iterator object that allows stepping through the data of
+interest. Iterators must be able to move to a specific position - like the
+file they implement - but the interpretation of that position is up to the
+iterator itself. A seq_file implementation that is formatting firewall
+rules, for example, could interpret position N as the Nth rule in the
+chain. Positioning can thus be done in whatever way makes the most sense
+for the generator of the data, which need not be aware of how a position
+translates to an offset in the virtual file. The one obvious exception is
+that a position of zero should indicate the beginning of the file.
+
+The /proc/sequence iterator just uses the count of the next number it
+will output as its position.
+
+Four functions must be implemented to make the iterator work. The first,
+called start() takes a position as an argument and returns an iterator
+which will start reading at that position. For our simple sequence example,
+the start() function looks like:
+
+	static void *ct_seq_start(struct seq_file *s, loff_t *pos)
+	{
+	        loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL);
+	        if (! spos)
+	                return NULL;
+	        *spos = *pos;
+	        return spos;
+	}
+
+The entire data structure for this iterator is a single loff_t value
+holding the current position. There is no upper bound for the sequence
+iterator, but that will not be the case for most other seq_file
+implementations; in most cases the start() function should check for a
+"past end of file" condition and return NULL if need be.
+
+For more complicated applications, the private field of the seq_file
+structure can be used. There is also a special value whch can be returned
+by the start() function called SEQ_START_TOKEN; it can be used if you wish
+to instruct your show() function (described below) to print a header at the
+top of the output. SEQ_START_TOKEN should only be used if the offset is
+zero, however.
+
+The next function to implement is called, amazingly, next(); its job is to
+move the iterator forward to the next position in the sequence.  The
+example module can simply increment the position by one; more useful
+modules will do what is needed to step through some data structure. The
+next() function returns a new iterator, or NULL if the sequence is
+complete. Here's the example version:
+
+	static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
+	{
+	        loff_t *spos = (loff_t *) v;
+	        *pos = ++(*spos);
+	        return spos;
+	}
+
+The stop() function is called when iteration is complete; its job, of
+course, is to clean up. If dynamic memory is allocated for the iterator,
+stop() is the place to return it.
+
+	static void ct_seq_stop(struct seq_file *s, void *v)
+	{
+	        kfree (v);
+	}
+
+Finally, the show() function should format the object currently pointed to
+by the iterator for output. It should return zero, or an error code if
+something goes wrong. The example module's show() function is:
+
+	static int ct_seq_show(struct seq_file *s, void *v)
+	{
+	        loff_t *spos = (loff_t *) v;
+	        seq_printf(s, "%Ld\n", *spos);
+	        return 0;
+	}
+
+We will look at seq_printf() in a moment. But first, the definition of the
+seq_file iterator is finished by creating a seq_operations structure with
+the four functions we have just defined:
+
+	static struct seq_operations ct_seq_ops = {
+	        .start = ct_seq_start,
+	        .next  = ct_seq_next,
+	        .stop  = ct_seq_stop,
+	        .show  = ct_seq_show
+	};
+
+This structure will be needed to tie our iterator to the /proc file in
+a little bit.
+
+It's worth noting that the interator value returned by start() and
+manipulated by the other functions is considered to be completely opaque by
+the seq_file code. It can thus be anything that is useful in stepping
+through the data to be output. Counters can be useful, but it could also be
+a direct pointer into an array or linked list. Anything goes, as long as
+the programmer is aware that things can happen between calls to the
+iterator function. However, the seq_file code (by design) will not sleep
+between the calls to start() and stop(), so holding a lock during that time
+is a reasonable thing to do. The seq_file code will also avoid taking any
+other locks while the iterator is active.
+
+
+Formatted output
+
+The seq_file code manages positioning within the output created by the
+iterator and getting it into the user's buffer. But, for that to work, that
+output must be passed to the seq_file code. Some utility functions have
+been defined which make this task easy.
+
+Most code will simply use seq_printf(), which works pretty much like
+printk(), but which requires the seq_file pointer as an argument. It is
+common to ignore the return value from seq_printf(), but a function
+producing complicated output may want to check that value and quit if
+something non-zero is returned; an error return means that the seq_file
+buffer has been filled and further output will be discarded.
+
+For straight character output, the following functions may be used:
+
+	int seq_putc(struct seq_file *m, char c);
+	int seq_puts(struct seq_file *m, const char *s);
+	int seq_escape(struct seq_file *m, const char *s, const char *esc);
+
+The first two output a single character and a string, just like one would
+expect. seq_escape() is like seq_puts(), except that any character in s
+which is in the string esc will be represented in octal form in the output.
+
+There is also a function for printing filenames:
+
+	int seq_path(struct seq_file *m, struct vfsmount *mnt,
+	             struct dentry *dentry, char *esc);
+
+Here, mnt and dentry indicate the file of interest, and esc is a set of
+characters which should be escaped in the output. 
+
+
+Making it all work
+
+So far, we have a nice set of functions which can produce output within the
+seq_file system, but we have not yet turned them into a file that a user
+can see. Creating a file within the kernel requires, of course, the
+creation of a set of file_operations which implement the operations on that
+file. The seq_file interface provides a set of canned operations which do
+most of the work. The virtual file author still must implement the open()
+method, however, to hook everything up. The open function is often a single
+line, as in the example module:
+
+	static int ct_open(struct inode *inode, struct file *file)
+	{
+		return seq_open(file, &ct_seq_ops);
+	};
+
+Here, the call to seq_open() takes the seq_operations structure we created
+before, and gets set up to iterate through the virtual file.
+
+On a successful open, seq_open() stores the struct seq_file pointer in
+file->private_data. If you have an application where the same iterator can
+be used for more than one file, you can store an arbitrary pointer in the
+private field of the seq_file structure; that value can then be retrieved
+by the iterator functions.
+
+The other operations of interest - read(), llseek(), and release() - are
+all implemented by the seq_file code itself. So a virtual file's
+file_operations structure will look like:
+
+	static struct file_operations ct_file_ops = {
+	        .owner   = THIS_MODULE,
+	        .open    = ct_open,
+	        .read    = seq_read,
+	        .llseek  = seq_lseek,
+	        .release = seq_release
+	};
+
+There is also a seq_release_private() which passes the contents of the
+seq_file private field to kfree() before releasing the structure.
+
+The final step is the creation of the /proc file itself. In the example
+code, that is done in the initialization code in the usual way:
+
+	static int ct_init(void)
+	{
+	        struct proc_dir_entry *entry;
+
+	        entry = create_proc_entry("sequence", 0, NULL);
+	        if (entry)
+	                entry->proc_fops = &ct_file_ops;
+	        return 0;
+	}
+
+	module_init(ct_init);
+
+And that is pretty much it.
+
+
+The extra-simple version
+
+For extremely simple virtual files, there is an even easier interface.  A
+module can define only the show() function, which should create all the
+output that the virtual file will contain. The file's open() method then
+calls:
+
+	int single_open(struct file *file,
+	                int (*show)(struct seq_file *m, void *p),
+	                void *data);
+
+When output time comes, the show() function will be called once. The data
+value given to single_open() can be found in the private field of the
+seq_file structure. When using single_open(), the programmer should use
+single_release() instead of seq_release() in the file_operations structure
+to avoid a memory leak.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add seq_file howto to Documentation/
  2007-07-23 20:09 ` Jonathan Corbet
@ 2008-02-29 19:18   ` Randy Dunlap
  2008-03-03 21:11     ` Jonathan Corbet
  0 siblings, 1 reply; 4+ messages in thread
From: Randy Dunlap @ 2008-02-29 19:18 UTC (permalink / raw)
  To: Jonathan Corbet; +Cc: Martin Bligh, linux-kernel

On Mon, 23 Jul 2007 14:09:08 -0600 Jonathan Corbet wrote:

> > +[Another seq_file reference is "Driver porting: The seq_file interface"
> > +at <http://lwn.net/Articles/22355/>, which is part of the
> > +LWN.net series "Porting Drivers to 2.5" that is located at
> > +<http://lwn.net/Articles/driver-porting/>.]
> 
> Funny thing, that...I had sent in a version of that document for
> consideration back in 2003.  Guess that was still the Good Old Days when
> the patch management system was rather lossier, so I never heard back.
> Here it is again just in case anybody's interested.
> 
> In general, I'm more than happy to have anything I put on LWN go into
> Documentation/ if there's interest - just say the word.
> 
> jon

Jon,
Can we get this merged??  or does it need to be updated first?


> To: linux-kernel@vger.kernel.org
> Subject: [PATCH] seq_file documentation
> cc: torvalds@osdl.org
> From: Jonathan Corbet <corbet@lwn.net>
> Date: Thu, 13 Nov 2003 10:16:31 -0700
> Sender: corbet
> 
> I went ahead and packaged up my seq_file document as a basic text file, in
> the interests of getting something out there.  
> 
> jon
> 
> Jonathan Corbet
> Executive editor, LWN.net
> corbet@lwn.net
> 
> 
> diff -urN test9-vanilla/Documentation/filesystems/seq_file.txt test9/Documentation/filesystems/seq_file.txt
> --- test9-vanilla/Documentation/filesystems/seq_file.txt	Wed Dec 31 17:00:00 1969
> +++ test9/Documentation/filesystems/seq_file.txt	Fri Nov 14 01:03:58 2003
> @@ -0,0 +1,268 @@
> +The seq_file interface
> +
> +	Copyright 2003 Jonathan Corbet <corbet@lwn.net>
> +	This file is originally from the LWN.net Driver Porting series at
> +	http://lwn.net/Articles/driver-porting/
> +
> +
> +There are numerous ways for a device driver (or other kernel component) to
> +provide information to the user or system administrator.  One very useful
> +technique is the creation of virtual files, in /proc or elsewhere. Virtual
> +files can provide human-readable output that is easy to get at without any
> +special utility programs; they can also make life easier for script
> +writers. It is not surprising that the use of virtual files has grown over
> +the years.
> +
> +Creating those files correctly has always been a bit of a challenge,
> +however. It is not that hard to make a /proc file which returns a
> +string. But life gets trickier if the output is long - anything greater
> +than an application is likely to read in a single operation.  Handling
> +multiple reads (and seeks) requires careful attention to the reader's
> +position within the virtual file - that position is, likely as not, in the
> +middle of a line of output. The kernel is full of /proc file
> +implementations that get this wrong.
> +
> +The 2.6 kernel contains a set of functions (implemented by Alexander Viro)
> +which are designed to make it easy for virtual file creators to get it
> +right. This interface (called "seq_file") is not strictly a 2.6 feature -
> +it was also merged into 2.4.15.
> +
> +The seq_file interface is available via <linux/seq_file.h>. There are
> +three aspects to seq_file:
> +
> +     * An iterator interface which lets a virtual file implementation
> +       step through the objects it is presenting.
> +
> +     * Some utility functions for formatting objects for output without
> +       needing to worry about things like output buffers.
> +
> +     * A set of canned file_operations which implement most operations on
> +       the virtual file.
> +
> +We'll look at the seq_file interface via an extremely simple example: a
> +loadable module which creates a file called /proc/sequence. The file, when
> +read, simply produces a set of increasing integer values, one per line. The
> +sequence will continue until the user loses patience and finds something
> +better to do. The file is seekable, in that one can do something like the
> +following:
> +
> +    dd if=/proc/sequence of=out1 count=1
> +    dd if=/proc/sequence skip=1 out=out2 count=1
> +
> +Then concatenate the output files out1 and out2 and get the right
> +result. Yes, it is a thoroughly useless module, but the point is to show
> +how the mechanism works without getting lost in other details.  (Those
> +wanting to see the full source for this module can find it at
> +http://lwn.net/Articles/22359/).
> +
> +
> +The iterator interface
> +
> +Modules implementing a virtual file with seq_file must implement a simple
> +iterator object that allows stepping through the data of
> +interest. Iterators must be able to move to a specific position - like the
> +file they implement - but the interpretation of that position is up to the
> +iterator itself. A seq_file implementation that is formatting firewall
> +rules, for example, could interpret position N as the Nth rule in the
> +chain. Positioning can thus be done in whatever way makes the most sense
> +for the generator of the data, which need not be aware of how a position
> +translates to an offset in the virtual file. The one obvious exception is
> +that a position of zero should indicate the beginning of the file.
> +
> +The /proc/sequence iterator just uses the count of the next number it
> +will output as its position.
> +
> +Four functions must be implemented to make the iterator work. The first,
> +called start() takes a position as an argument and returns an iterator
> +which will start reading at that position. For our simple sequence example,
> +the start() function looks like:
> +
> +	static void *ct_seq_start(struct seq_file *s, loff_t *pos)
> +	{
> +	        loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL);
> +	        if (! spos)
> +	                return NULL;
> +	        *spos = *pos;
> +	        return spos;
> +	}
> +
> +The entire data structure for this iterator is a single loff_t value
> +holding the current position. There is no upper bound for the sequence
> +iterator, but that will not be the case for most other seq_file
> +implementations; in most cases the start() function should check for a
> +"past end of file" condition and return NULL if need be.
> +
> +For more complicated applications, the private field of the seq_file
> +structure can be used. There is also a special value whch can be returned
> +by the start() function called SEQ_START_TOKEN; it can be used if you wish
> +to instruct your show() function (described below) to print a header at the
> +top of the output. SEQ_START_TOKEN should only be used if the offset is
> +zero, however.
> +
> +The next function to implement is called, amazingly, next(); its job is to
> +move the iterator forward to the next position in the sequence.  The
> +example module can simply increment the position by one; more useful
> +modules will do what is needed to step through some data structure. The
> +next() function returns a new iterator, or NULL if the sequence is
> +complete. Here's the example version:
> +
> +	static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
> +	{
> +	        loff_t *spos = (loff_t *) v;
> +	        *pos = ++(*spos);
> +	        return spos;
> +	}
> +
> +The stop() function is called when iteration is complete; its job, of
> +course, is to clean up. If dynamic memory is allocated for the iterator,
> +stop() is the place to return it.
> +
> +	static void ct_seq_stop(struct seq_file *s, void *v)
> +	{
> +	        kfree (v);
> +	}
> +
> +Finally, the show() function should format the object currently pointed to
> +by the iterator for output. It should return zero, or an error code if
> +something goes wrong. The example module's show() function is:
> +
> +	static int ct_seq_show(struct seq_file *s, void *v)
> +	{
> +	        loff_t *spos = (loff_t *) v;
> +	        seq_printf(s, "%Ld\n", *spos);
> +	        return 0;
> +	}
> +
> +We will look at seq_printf() in a moment. But first, the definition of the
> +seq_file iterator is finished by creating a seq_operations structure with
> +the four functions we have just defined:
> +
> +	static struct seq_operations ct_seq_ops = {
> +	        .start = ct_seq_start,
> +	        .next  = ct_seq_next,
> +	        .stop  = ct_seq_stop,
> +	        .show  = ct_seq_show
> +	};
> +
> +This structure will be needed to tie our iterator to the /proc file in
> +a little bit.
> +
> +It's worth noting that the interator value returned by start() and
> +manipulated by the other functions is considered to be completely opaque by
> +the seq_file code. It can thus be anything that is useful in stepping
> +through the data to be output. Counters can be useful, but it could also be
> +a direct pointer into an array or linked list. Anything goes, as long as
> +the programmer is aware that things can happen between calls to the
> +iterator function. However, the seq_file code (by design) will not sleep
> +between the calls to start() and stop(), so holding a lock during that time
> +is a reasonable thing to do. The seq_file code will also avoid taking any
> +other locks while the iterator is active.
> +
> +
> +Formatted output
> +
> +The seq_file code manages positioning within the output created by the
> +iterator and getting it into the user's buffer. But, for that to work, that
> +output must be passed to the seq_file code. Some utility functions have
> +been defined which make this task easy.
> +
> +Most code will simply use seq_printf(), which works pretty much like
> +printk(), but which requires the seq_file pointer as an argument. It is
> +common to ignore the return value from seq_printf(), but a function
> +producing complicated output may want to check that value and quit if
> +something non-zero is returned; an error return means that the seq_file
> +buffer has been filled and further output will be discarded.
> +
> +For straight character output, the following functions may be used:
> +
> +	int seq_putc(struct seq_file *m, char c);
> +	int seq_puts(struct seq_file *m, const char *s);
> +	int seq_escape(struct seq_file *m, const char *s, const char *esc);
> +
> +The first two output a single character and a string, just like one would
> +expect. seq_escape() is like seq_puts(), except that any character in s
> +which is in the string esc will be represented in octal form in the output.
> +
> +There is also a function for printing filenames:
> +
> +	int seq_path(struct seq_file *m, struct vfsmount *mnt,
> +	             struct dentry *dentry, char *esc);
> +
> +Here, mnt and dentry indicate the file of interest, and esc is a set of
> +characters which should be escaped in the output. 
> +
> +
> +Making it all work
> +
> +So far, we have a nice set of functions which can produce output within the
> +seq_file system, but we have not yet turned them into a file that a user
> +can see. Creating a file within the kernel requires, of course, the
> +creation of a set of file_operations which implement the operations on that
> +file. The seq_file interface provides a set of canned operations which do
> +most of the work. The virtual file author still must implement the open()
> +method, however, to hook everything up. The open function is often a single
> +line, as in the example module:
> +
> +	static int ct_open(struct inode *inode, struct file *file)
> +	{
> +		return seq_open(file, &ct_seq_ops);
> +	};
> +
> +Here, the call to seq_open() takes the seq_operations structure we created
> +before, and gets set up to iterate through the virtual file.
> +
> +On a successful open, seq_open() stores the struct seq_file pointer in
> +file->private_data. If you have an application where the same iterator can
> +be used for more than one file, you can store an arbitrary pointer in the
> +private field of the seq_file structure; that value can then be retrieved
> +by the iterator functions.
> +
> +The other operations of interest - read(), llseek(), and release() - are
> +all implemented by the seq_file code itself. So a virtual file's
> +file_operations structure will look like:
> +
> +	static struct file_operations ct_file_ops = {
> +	        .owner   = THIS_MODULE,
> +	        .open    = ct_open,
> +	        .read    = seq_read,
> +	        .llseek  = seq_lseek,
> +	        .release = seq_release
> +	};
> +
> +There is also a seq_release_private() which passes the contents of the
> +seq_file private field to kfree() before releasing the structure.
> +
> +The final step is the creation of the /proc file itself. In the example
> +code, that is done in the initialization code in the usual way:
> +
> +	static int ct_init(void)
> +	{
> +	        struct proc_dir_entry *entry;
> +
> +	        entry = create_proc_entry("sequence", 0, NULL);
> +	        if (entry)
> +	                entry->proc_fops = &ct_file_ops;
> +	        return 0;
> +	}
> +
> +	module_init(ct_init);
> +
> +And that is pretty much it.
> +
> +
> +The extra-simple version
> +
> +For extremely simple virtual files, there is an even easier interface.  A
> +module can define only the show() function, which should create all the
> +output that the virtual file will contain. The file's open() method then
> +calls:
> +
> +	int single_open(struct file *file,
> +	                int (*show)(struct seq_file *m, void *p),
> +	                void *data);
> +
> +When output time comes, the show() function will be called once. The data
> +value given to single_open() can be found in the private field of the
> +seq_file structure. When using single_open(), the programmer should use
> +single_release() instead of seq_release() in the file_operations structure
> +to avoid a memory leak.

---
~Randy

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add seq_file howto to Documentation/ 
  2008-02-29 19:18   ` Randy Dunlap
@ 2008-03-03 21:11     ` Jonathan Corbet
  0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Corbet @ 2008-03-03 21:11 UTC (permalink / raw)
  To: Randy Dunlap; +Cc: Martin Bligh, linux-kernel

Randy Dunlap <randy.dunlap@oracle.com> wrote:

> Can we get this merged??  or does it need to be updated first?

Hey, it's only been a couple years or so, what's the rush?

Lemme do a review pass, I've not verified the contents for some time.
Then I'll make another try to get it in.

Thanks,

jon

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2008-03-03 21:11 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-07-23 19:45 [PATCH] Add seq_file howto to Documentation/ Martin Bligh
2007-07-23 20:09 ` Jonathan Corbet
2008-02-29 19:18   ` Randy Dunlap
2008-03-03 21:11     ` Jonathan Corbet

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).