LWN.net Logo

Driver porting: The seq_file interface

Driver porting: The seq_file interface

Posted Jan 30, 2004 23:08 UTC (Fri) by gcc (guest, #19095)
Parent article: Driver porting: The seq_file interface

Users should be clear about the difference between a position and an iterator (this confused me for a while). The position is your means of communication with the seq_file interface, telling it where you are, and for it to tell you where to go.

The iterator is an opaque value, about which seq_file understands nothing, except that you return it from your start and next handlers, and it will give it back to you in next, stop and show.

Examples of iterators given in the article are a simple index (using the pointer as an integer, i.e. it doesn't really point to anything), or a pointer to a linked list entry or an array member. However, if you have to navigate some more complex structure, such as a hash table, it can be useful to have a struct which holds several values, for example:

    struct my_pos {              
    	my_hash_table *table;              
    	int hash;              
    	my_ht_entry *entry;              
    }              
    
This allows a single value (your iterator, void *v) to tell you everything you need to know in order to walk the hashtable. In this case, as in the /proc/sequence example, v remains the same throughout the walk.

On the other hand, the position, loff_t *pos, is a strange beast. If you start reading the /proc file from the beginning, then start will be called with position 0, 1, 2, etc. until it returns NULL. At this point the user gets EOF, and they can then seek around and start reading again, which results in your start handler being called again.

However, if the user skips more than the length of the file, (e.g. dd if=/proc/my_file bs=1G skip=1, then your code will be called with position 0 twice! The user doesn't get what they were expecting (an empty file), but the whole file, as if the seek never happened. strace shows that the seek appears to succeed. I think this behavious is wrong and constitutes a mental health hazard.

If you want to use the same set of operations for multiple files in /proc, you need a way for them to distinguish what object they operate on. The article mentions s->private, but you can only use this after you have called seq_open, because the seq_file object doesn't exist before that. On the 2.6 kernel, it appears that a good place to store this private data is in the struct inode *inode which is passed to your open handler:

    static int my_open (struct inode *inode, struct file          
    *file)
In particular, 2.6 has a function called PROC_I which converts your struct inode into a struct proc_inode, which has a member called pde, which is your struct proc_dir_entry.

So you can create the proc_dir_entry, initialise its data member with a pointer to your private data, and then in the open handler you can get to it from inode->pde. For example:

    // module init              
    static int __init init()              
    {              
    	my_proc_entry = create_proc_entry(filename, 0, NULL);              
    	/* ... check for failure ... */              
    	my_proc_entry->data = my_data;              
    }              
                  
    static int my_open (struct inode *inode, struct file *file)              
    {              
    	struct seq_file *s;       
    	result = seq_open(file, &seq_ops);       
    	/* ... check for failure ... */       
    	/* file->private_data was initialised by seq_open */       
    	s = (struct seq_file *)file->private_data;       
    	my_data = PROC_I(inode)->pde->data;              
    	s->private = my_data;              
    }              
                  
    static int my_seq_start (...) {              
    	my_data = s->private;              
    }              
    
I'm still looking for a good way to do this on 2.4, which lacks PROC_I and struct proc_inode. Can anyone help?


(Log in to post comments)

Copyright © 2008, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds