El Mon, Mar 31, 2008 at 10:10:41PM -0400 Robert P. J. Day ha dit:
>
> (a bit involved so bear with me, i really want to figure out how
> this works.)
>
> i'm following the sample code in the LDD3 book for the "scull"
> driver, and i *think* i know the answer to this but i just want to
> make sure. it has to do with how defining a new character device and
> opening it manages to set the i_cdev pointer back in the inode
> structure.
>
> first, from <linux/fs.h>, we have the definition of an inode, with
> only the relevant fields that i care about:
>
> struct inode {
> ...
> union {
> struct pipe_inode_info *i_pipe;
> struct block_device *i_bdev;
> struct cdev *i_cdev;
> };
> ...
>
> so, depending on the type of file, only one of those pointers will
> be valid. but, initially, if nothing has opened the file represented
> by this inode yet, none of those pointers will point at anything
> meaningful, right? so, in the beginning, the pointer i_cdev will not
> have a valid address. so far, so good? now consider how this relates
> to the "scull" example from LDD3.
>
> here's the structure for representing that new kind of character
> device:
>
> struct scull_dev {
> struct scull_qset *data; /* Pointer to first quantum set */
> int quantum; /* the current quantum size */
> int qset; /* the current array size */
> unsigned long size; /* amount of data stored here */
> unsigned int access_key; /* used by sculluid and scullpriv */
> struct semaphore sem; /* mutual exclusion semaphore */
> struct cdev cdev; /* Char device structure */
> };
>
> and there at the bottom is the embedded "struct cdev" you're supposed
> to build in to your new character device. the question is -- how does
> that structure get associated with the structure pointer back in the
> inode? moving on, here's the character device initialization routine
> for that sample "scull" char device:
>
> /*
> * Set up the char_dev structure for this device.
> */
> static void scull_setup_cdev(struct scull_dev *dev, int index)
> {
> int err, devno = MKDEV(scull_major, scull_minor + index);
>
> cdev_init(&dev->cdev, &scull_fops);
> dev->cdev.owner = THIS_MODULE;
> dev->cdev.ops = &scull_fops;
> err = cdev_add (&dev->cdev, devno, 1);
> /* Fail gracefully if need be */
> if (err)
> printk(KERN_NOTICE "Error %d adding scull%d", err, index);
> }
>
> so what does this code do? from fs/char_dev.c, here's the code for
> cdev_init():
>
> void cdev_init(struct cdev *cdev, const struct file_operations *fops)
> {
> memset(cdev, 0, sizeof *cdev);
> INIT_LIST_HEAD(&cdev->list);
> kobject_init(&cdev->kobj, &ktype_cdev_default);
> cdev->ops = fops;
> }
>
> that doesn't appear to link the cdev back to the inode, but it *does*
> create an initialized kobject for the character device. following
> that, cdev_add() is run:
>
> int cdev_add(struct cdev *p, dev_t dev, unsigned count)
> {
> p->dev = dev;
> p->count = count;
> return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock,
> p);
> }
>
> which appears to fill in the new kobject with information
> corresponding to this new character device, but *still*, there's no
> connection back to the inode for the device file. finally, it looks
> like it happens here:
>
> /*
> * Called every time a character special file is opened
> */
> static int chrdev_open(struct inode *inode, struct file *filp)
> {
> struct cdev *p;
> struct cdev *new = NULL;
> int ret = 0;
>
> spin_lock(&cdev_lock);
> p = inode->i_cdev;
> if (!p) {
> struct kobject *kobj;
> int idx;
> spin_unlock(&cdev_lock);
> kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
> if (!kobj)
> return -ENXIO;
> new = container_of(kobj, struct cdev, kobj);
> spin_lock(&cdev_lock);
> p = inode->i_cdev;
> if (!p) {
> inode->i_cdev = p = new;
> inode->i_cindex = idx;
> list_add(&inode->i_devices, &p->list);
> new = NULL;
> ... snip ...
>
> that routine might be called every time a char special file is opened
> but, at this point, it still seems that the inode "i_cdev" pointer
> isn't yet pointing to the "struct cdev" we've created and initialized.
> finally, it seems to happen in this routine, where a test is done to
> see if that pointer is set yet:
>
> p = inode->i_cdev;
> if (!p) {
>
> and if it isn't, then:
>
> kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
> if (!kobj)
> return -ENXIO;
> new = container_of(kobj, struct cdev, kobj);
>
> that *appears* to do a wide-ranging kobject lookup, looking for the
> kobject that matches the device ID (inode->i_rdev) and, if it finds
> it, then the struct cdev we're looking for is simply the container of
> that kobject. and *finally*,
>
> if (!p) {
> inode->i_cdev = p = new;
>
> the "i_cdev" struct cdev pointer back in the inode is set and can be
> used from now on. does all that make sense? in short, the very first
> time a character device file is opened and it's noticed that the
> inode->i_cdev pointer hasn't been set yet, a kobject-based search is
> done to find the corresponding "struct cdev" address to fill into the
> inode. does all this seem to follow?
>
> rday
>
> p.s. i am a bit confused as to why there are two tests for whether
> inode->i_cdev is non-NULL:
>
> p = inode->i_cdev;
> if (!p) { <-- first test of p
> struct kobject *kobj;
> int idx;
> spin_unlock(&cdev_lock);
> kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
> if (!kobj)
> return -ENXIO;
> new = container_of(kobj, struct cdev, kobj);
> spin_lock(&cdev_lock);
> p = inode->i_cdev;
> if (!p) { <-- second test of p
> inode->i_cdev = p = new;
>
>
> why is that first test being repeated further down? if p was NULL at
> that first test, how could it possibly have changed before that second
> test? isn't that second test redundant? or am i missing something?
if i understand that piece of code correctly inode->i_cdev could have
changed (probably by a concurrent invocation of the same function)
while cdev_lock is not hold and inode->i_cdev is reassigned to p.
--
Matthias Kaehlcke
Linux System Developer
Barcelona
Control over the use of one's ideas really constitutes control over other
people's lives; and it is usually used to make their lives more difficult.
(Richard Stallman)
.''`.
using free software / Debian GNU/Linux | http://debian.org : :' :
`. `'`
gpg --keyserver pgp.mit.edu --recv-keys 47D8E5D4 `-
--
To unsubscribe from this list: send an email with
"unsubscribe kernelnewbies" to [EMAIL PROTECTED]
Please read the FAQ at http://kernelnewbies.org/FAQ