2012年12月3日 星期一

Note about Linux Kernel char Device Driver

There are two ways to create a struct cdev:
  1. static way
    • declare
      • embedded in your device structure statically
        struct dummy_device {
            struct cdev cdev;
        };
    • initialize
      •  use cdev_init() function to initialize the cdev structure
        struct dummy_device dev;
        ...
        cdev_init(&dev.cdev, &dummy_fops);
    • remove
      • call cdev_del()
  2. dynamic way
    • declare
      • memory of cdev structure is allocated dynamically
        struct dummy_device {
            struct cdev *cdev;
        };
        ...
        struct dymmy_device dev;
        dev->cdev = cdev_alloc();
    • initialize
      • dev->cdev.fops = &dummy_fops;
    • remove
      • call cdev_del()
      • You don't need to call kfree() to free memory. Kernel releases the memory automatically. (talk this later)  
There is one thing you need to know about the dynamic way. 
If you use dynamic way to create a cdev structure, you should better not use cdev_init() to initialize the structure.

Let's check source code.

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;
}

struct cdev *cdev_alloc(void)
{

    /* cdev is allocated here! */
    struct cdev *p = kzalloc(sizeof(struct cdev),
                     GFP_KERNEL);
    if (p) {
        INIT_LIST_HEAD(&p->list);
        kobject_init(&p->kobj, &ktype_cdev_dynamic);
    }
    return p;
}

If you use dynamic way to create the cdev structure, ktype_cdev_dynamic() is used to release cdev kobject.
If you use cdev_init() to initialize cdev structure that is created in dynamic way, release function of kobject is replaced with ktype_cdev_default().
In this case, after you invoke cdev_del() to remove a cdev device from kernel, this causes a memory leak.
You need to free memory manually and it is not recommended.

static void cdev_default_release(struct kobject *kobj)
{
    struct cdev *p = container_of(kobj, struct cdev, kobj);
    cdev_purge(p);
}

static void cdev_dynamic_release(struct kobject *kobj)
{
    struct cdev *p = container_of(kobj, struct cdev, kobj);
    cdev_purge(p);
    kfree(p);    /* cdev is freed here! */
}

static struct kobj_type ktype_cdev_default = {
    .release        = cdev_default_release,
};

static struct kobj_type ktype_cdev_dynamic = {
    .release        = cdev_dynamic_release,
};

沒有留言:

張貼留言