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 ecartis@xxxxxxxxxxxx Please read the FAQ at http://kernelnewbies.org/FAQ