Register_chrdev_region function source code analysis

Source: Internet
Author: User

This article is reproduced in: http://edsionte.com/techblog/archives/1393

How to find an effective starting point to thoroughly analyze the kernel source code is a deeply thought-provoking problem. Functions that are not described in the previous sections are the starting point for in-depth analysis of the Code in the char_dev.c file. If you already have the C language basics and some data structure basics, what are you waiting? Let's go!

In the character device driver analysis article, we mentioned that the function of register_chrdev_region is to apply for a set of consecutive device numbers when the start device number is known. However, most driver books do not describe this function in depth, probably because it encapsulates _ register_chrdev_region (unsigned int major, unsigned int baseminor, int minorct, const char * name) the reason for the function. However, we don't need to worry about it. This prompts us to analyze this function.
int register_chrdev_region(dev_t from, unsigned count, const char *name)

{

       struct char_device_struct *cd;

       dev_t to = from + count;

       dev_t n, next;  

       for (n = from; n <\ to; n = next)

       {

          next = MKDEV(MAJOR(n)+1, 0);

           if (next >\ to)

                next = to;
                cd = __register_chrdev_region(MAJOR(n), MINOR(n), next - n, name);
                if (IS_ERR(cd))

                     goto fail;

       }
           return 0;

fail: to = n;

       for (n = from; n <\ to; n = next)

       {

            next = MKDEV(MAJOR(n)+1, 0);
            kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
       }

       return PTR_ERR(cd);
}

First, it is worth noting that each function is assigned a group of device numbers. Here, the from parameter is the start device number of this set of Continuous Device numbers, and the count parameter is the size of this set of device numbers (also the number of sub-device numbers). The name parameter processes the driver name of this set of devices. In addition, when the number of device numbers is too large (excessive count), the number of device numbers may overflow to the next primary device. Therefore, we can see in the for statement that, first obtain the next master device number (in fact, it is also a device number, but the next device number is 0 at this time) and store it in next. Then, determine whether the additional count device has exceeded to the next master device number based on the from. If there is no overflow (next is less than to), the entire for statement will only execute the _ register_chrdev_region function once; otherwise, when the device number overflows, the range of the currently exceeded device numbers is divided into several small ranges and the _ register_chrdev_region function is called respectively.

If _ register_chrdev_region fails to be called in a small range, all previously assigned device numbers will be released.

In fact, the register_chrdev_region function does not completely describe the specific process of clearing the device number allocation, because the specific small range of device numbers are completed by the _ register_chrdev_region function. You may have noticed that the struct char_device_struct structure appears in the register_chrdev_region function source code. Let's first look at this struct:
static struct char_device_struct {
        struct char_device_struct *next;

        unsigned int major;
        unsigned int baseminor;

        int minorct;

        char name[64];

        struct cdev *cdev;              /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

In the register_chrdev_region function, a pointer of the struct char_device_struct type is returned when the _ register_chrdev_region function is called within a small range of device numbers of each character. Therefore, we can know that the struct char_device_struct type corresponds to not every character device, but a group of character devices with Continuous Device numbers. The internal fields of this struct can also be seen that the main device number of this set of consecutive device numbers is major, the sub-device number starts with baseminor, and the sub-device number range is minorct, the device driver name corresponding to this set of device numbers is name, And cdev is the pointer to this character device driver.

Note that all assigned character device numbers in the kernel are recorded in a chrdevs hash. Each element in the hash is a char_device_struct structure. The size of the hash is 255 (CHRDEV_MAJOR_HASH_SIZE), because the system shields the first four digits of the 12-bit master device number. When it comes to the hash list, there will certainly be conflicts, so the next field is the pointer to the next element in the conflicting linked list.

Next we will analyze the _ register_chrdev_region function in detail. First, allocate memory for the cd variable and fill it with zero (that is why kzarloc is used instead of kmalloc ). Then, P is used to make the statements to be executed in the critical section.

static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)

{
       struct char_device_struct *cd, **cp;
       int ret = 0;

       int i;
       cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
       if (cd == NULL)

            return ERR_PTR(-ENOMEM);  
            mutex_lock(&chrdevs_lock);

If the major is 0, that is, no specific master device number is specified, it needs to be dynamically allocated. Then, the if statement finds the appropriate location for the group of devices in the entire hash, that is, the chrdevs [I] is empty from the end of the hash. If it is found, I represents not only the master device number of this set of devices, but also the keywords in the scattered list. Of course, if the primary device number has been specified, ignore this part of code.

       if (major == 0)

       {

           for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i—)

           {
                if (chrdevs[i] == NULL)

                    break;
           }  
           if (i == 0)

           {

                ret = -EBUSY;
                goto out;
           }

           major = i;
           ret = major;
        }

Then, assign the values in the parameters to the corresponding fields of the cd variable in sequence. If the master device number is non-zero, that is, if it is known in advance, it must be calculated by using the maxjor_to_index function. Therefore, the range of keywords in the entire hash table is 0 ~ 254.

        cd->major = major;

        cd->baseminor = baseminor;
        cd->minorct = minorct;
        strlcpy(cd->name, name, sizeof(cd->name));  
        i = major_to_index(major);

At this point, we will get a valid master device number through the above Code (if you can continue to execute the following code), then the allocation will not continue. As you know, conflicts in a hash are inevitable. Therefore, after we get the major value, we need to facilitate the conflicting linked list and find the correct position for the char_device_struct variable cd. More importantly, we need to check the current device number range, that is, baseminor ~ Baseminor + minorct: whether the range of the baseminor + minorct is the same as that of the previously assigned sub-device number (provided that the major is the same.

The following for loop is the position of the for statement in the conflicting linked list. When the following three conditions occur, the for statement will stop.

(1) If the master device number (* (cp)-> major) of the node being traversed in the conflict table is greater than the master device number (major) allocated by us ), then you can jump out of the for statement and stop searching. In this case, it should be said that the device number is allocated successfully, so the cd node only needs to be inserted into the conflicting linked list (* Before the cp node ).

(2) If the master device number of the (* cp) node is the same as that of the cd node, but the start point of the secondary device number of the former node is larger than that of the cd node, the for statement is popped out, wait for the next detection of overlapping ranges.

(3) If the master device number of the (* cp) node is the same as that of the cd node, but the start point of the secondary device Number of the cd node is smaller than the end point of the secondary device number of the (* cp) node, then the for statement will pop out. In this case, the device numbers of the two ranges may overlap.

From the above analysis, we can see that the conflict table is arranged in the ascending order of device numbers.

        for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major > major || ((*cp)->major == major && (((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor))))
            break;

Next, check whether the range of the secondary device overlaps when the primary device number is the same. First, calculate the range of the New and Old device numbers, and then determine the range. The first statement is to check whether the end point of the new range is between the old range, and the second statement is to check whether the new range starts from the old range.

        /* Check for overlapping minor ranges.  */

       if (*cp && (*cp)->major == major)

       {
            int old_min = (*cp)->baseminor;
            int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
            int new_min = baseminor;
            int new_max = baseminor + minorct - 1;

            /* New driver overlaps from the left.  */
            if (new_max >= old_min && new_max <= old_max)

            {
                 ret = -EBUSY;
                 goto out;
            }
            /* New driver overlaps from the right.  */

            if (new_min <= old_max && new_min >= old_min)

            {
                 ret = -EBUSY;
                 goto out;
            }

        }

When everything works, insert the char_device_struct descriptor into the linked list. So far, a small range of device numbers are allocated successfully. In this case, exit the critical section and perform the V operation. If any failure occurs in the above process, the system jumps to the out and returns an error message.

        cd->next = *cp;

        *cp = cd;
        mutex_unlock(&chrdevs_lock);
        return cd;
out:    mutex_unlock(&chrdevs_lock);
        kfree(cd);
        return ERR_PTR(ret);
}

So far, we have analyzed the character device number allocation function.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.