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).