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)