Re: how does opening a char device set the cdev pointer in the inode?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux