Linux Kernel abstract Resource Management

Source: Internet
Author: User

From: http://zhanglinbao.bokee.com/5408080.html

Note: This document is released in the spirit of free software open source code. Anyone can obtain, use, and re-release it for free, but you have no right to restrict others from re-publishing your content. The purpose of this article is to hope that it can be useful to readers, but it does not have any guarantees, or even implicit guarantees suitable for a specific purpose. For more details, see the GNU General Public License (GPL) and the GNU Free Documentation protocol (GFDL ). Almost every type of peripherals is performed by reading and writing registers on the device. A peripheral register, also known as an "I/O port", usually includes three categories: control registers, status registers, and data registers, and a peripheral register is usually continuously edited. The CPU can address the physical address of the peripheral I/O port in two ways: I/O ing (I/O-mapped ), another method is memory-mapped ). The specific method depends on the CPU architecture. Some architecture CPUs (such as PowerPC and m68k) generally only implement one physical address space (RAM ). In this case, the physical address of the peripheral I/O port is mapped to a single physical address space of the CPU and becomes a part of the memory. In this case, the CPU can access the peripheral I/O port as it accesses a memory unit, without the need to set up a dedicated peripheral I/O command. This is the so-called memory ing method (memory-mapped ). In addition, some architecture CPUs (typically x86) provide a dedicated address space for peripherals, it is called "I/O address space" or "I/O port space ". This is an address space different from the physical address space of the CPU. All the peripheral I/O ports are located in this space. The CPU uses dedicated I/O commands (such as x86 In and out commands) to access the address units (I/O Ports) in the space ). This is the so-called "I/O ing mode" (I/O-mapped ). Compared with the ram physical address space, I/O address space is usually relatively small, for example, x86 cpu I/O space is only 64 KB (0-0xffff ). This is a major drawback of the "I/O ing method. In Linux, I/O Ports Based on I/O ing or memory ing are called "I/O Region" (I/O Region ). Before discussing the management of the I/O Region, let's first analyze how Linux implements the abstract concept of "I/O resources. 3.1 Linux description of I/O resources Linux designs a general data structure resource to describe various I/O resources (such: i/O port, peripheral memory, DMA and IRQ ). This structure is defined in include/Linux/ioport. h header file: struct resource {const char * Name; unsigned long start, end; unsigned long flags; struct resource * parent, * sibling, * child ;}; the meaning of each member is as follows: 1. name Pointer: indicates the name of the resource. 2. start and end: start and end physical addresses of resources. They determine the resource range, that is, a closed interval [start, end]. 3. Flags: The identifier that describes the resource attribute (see the following ). 4. pointer parent, sibling, and child: pointer to parent, sibling, and sub-resource respectively. The flags attribute is a 32-bit flag value of the unsigned long type to describe the attributes of a resource. For example, the type of the resource, whether it is read-only, whether it can be cached, and whether it is occupied. The following is a definition of some common attribute flags (ioport. h):/** Io resources have these defined flags. */# define ioresource_bits 0x000000ff/* bus-specific bits */# define ioresource_io 0x00000100/* resource type */# define ioresource_mem 0x00000200 # define limit 0x00000400 # define ioresource_dma 0x00000800 # define ioresource_prefetch 0x00001000/* No side effects */# define ioresource_readonly 0x00002000 # define ioresource _ Cacheable 0x00004000 # define limit 0x00008000 # define limit 0x00010000 # define limit 0x00080000 # define ioresource_unset 0x20000000 # define ioresource_auto 0x40000000 # define ioresource_busy 0 x 80000000/* driver has marked this resource busy */pointer parent, sibling, and child are set to manage various I/O resources in the form of a tree. 3.2 Linux manages I/O resources. Linux uses an inverted tree structure to manage each type of I/O resources (such as I/O Ports, peripheral memory, DMA, and IRQ). Each type of I/O resources corresponds to an inverted Resource Tree. each node in the tree is a resource structure, while the root node of the tree is the whole resource space of the class resources. Based on the above idea, Linux implements operations such as resource application, release, and search in the kernel/resource. c file. 3.2.1 I/O resource application assume that a certain type of resource has the following Resource Tree: node root, R1, R2, and R3 are actually a resource structure type. The sub-resources R1, R2, and R3 are linked to a one-way non-circular linked list through the sibling pointer. The table header is defined by the Child pointer in the root node. Therefore, it is also called the sub-resource linked list of the parent resource. The parent pointers of R1, R2, and R3 all point to their parent resource node, which is the root node in the figure. Assume that you want to allocate an I/O resource to the root node (represented by the shadow area in the figure ). The request_resource () function implements this function. It has two parameters: ① root pointer, indicating the resource root node to be allocated; ② new pointer, pointing to the resource to be allocated (that is, the shadow area in the figure) resource structure. The source code of this function is as follows (kernel/resource. c): int request_resource (struct resource * root, struct resource * New) {struct resource * Conflict; write_lock (& resource_lock); Conflict = _ request_resource (root, new ); write_unlock (& resource_lock); Return conflict? -Ebusy: 0;} note the preceding functions as follows: ① resource lock resource_lock protects the read and write of all resource trees, any code segment must hold the lock before accessing a Resource Tree. The definition is as follows (kernel/resource. c): static rwlock_t resource_lock = rw_lock_unlocked; ② we can see that the function actually allocates resources by calling the internal static function _ request_resource. If this function returns a non-null pointer, a resource conflict exists. Otherwise, if null is returned, the allocation is successful. ③ Finally, if the conflict pointer is null, The request_resource () function returns 0 to indicate success; otherwise, return-ebusy to indicate that the resource to be allocated is occupied. Function _ request_resource () completes the actual resource allocation. If some or all of the resources described by the new parameter are occupied by other nodes, the function returns a pointer to the resource structure in conflict with the new parameter. Otherwise, null is returned. The source code of this function is as follows (kernel/resource. c):/* return the conflict entry if you can't request it */static struct resource * _ request_resource (struct resource * root, struct resource * New) {unsigned long start = new-> Start; unsigned long end = new-> end; struct resource * TMP, ** P; If (end <start) return root; if (start <root-> Start) return root; If (end> root-> end) return root; P = & root-> child; for (;) {TMP = * P; If (! TMP | TMP-> Start> end) {New-> sibling = TMP; * P = new; New-> parent = root; return NULL ;} P = & TMP-> sibling; If (TMP-> end <start) continue; return TMP ;}} Note of the function: ① The first three if statements determine whether the resource range described by new is included in the root, and whether it is a valid Resource (because the end must be greater than start ). Otherwise, the root pointer is returned, indicating a conflict with the root node. ② Next we will use a for loop to traverse the root node's root child linked list to check whether there are resource conflicts, insert new to the appropriate position in the Child linked list (the Child linked list is arranged in the ascending order of the physical addresses of I/O resources ). Therefore, it uses the TMP pointer to point to the currently being scanned resource structure, and the pointer P to the sibling pointer member variable of the previous resource structure. The initial value of P is to root-> sibling. The steps for executing the for loop body are as follows: l point TMP to the resource structure currently being scanned (TMP = * P ). L determine whether the TMP pointer is null (if the TMP pointer is null, it indicates that the entire child linked list has been traversed), or whether the start position of the currently scanned node is larger than the end position of the new node. As long as one of the two conditions is true, there is no resource conflict, so we can link new to the Child linked list: ① set the new sibling pointer to the node TMP (New-> sibling = TMP) currently being scanned ); ② The sibling pointer of the previous brother node of the current node TMP is changed to point to the new node (* P = new); ③ The new parent pointer is set to point to the root node. Then the function can return (Return Value null indicates there is no resource conflict ). L if neither of the preceding conditions is true, it indicates that the resource domains of the currently scanned nodes may conflict with new (in fact, there is an intersection between the two closed intervals), so further judgment is required. For this reason, it first modifies the pointer P and points it to TMP-> sibling, so as to continue scanning the child linked list. Then, judge whether TMP-> end is smaller than New-> start. If it is smaller than, it indicates that the current node TMP and new have no resource conflicts. Therefore, execute the continue statement to continue scanning the child linked list. Otherwise, if TMP-> end is greater than or equal to new-> start, there is an intersection between TMP-> [start, end] and new-> [start, end. Therefore, the pointer TMP of the current node is returned, indicating a resource conflict. 3.2.2 The resource release function release_resource () is used to release I/O resources. This function has only one parameter, that is, the pointer old, pointing to the resource to be released. Source code: int release_resource (struct resource * Old) {int retval; write_lock (& resource_lock); retval = _ release_resource (old); write_unlock (& resource_lock ); return retval;} it can be seen that it actually completes the actual resource release by calling the internal static function _ release_resource. The main task of function _ release_resource () is to remove the resource region old (if it already exists) from its parent resource's child linked list. Its source code is as follows: static int _ release_resource (struct resource * Old) {struct resource * TMP, ** P; P = & old-> parent-> child; (;;) {TMP = * P; If (! TMP) break; If (TMP = old) {* P = TMP-> sibling; old-> parent = NULL; return 0;} p = & TMP-> sibling ;} return-einval;} the note for the above function code is as follows: similar to function _ request_resource (), this function also traverses the child linked list of the parent resource through a for loop. Therefore, it directs the TMP pointer to the currently scanned resource, while the pointer P points to the sibling member of the previous node of the current node (the initial value of P is the Child pointer pointing to the parent resource ). The steps for the loop body are as follows: ① first, let the TMP pointer point to the currently scanned node (TMP = * P ). ② If the TMP pointer is null, it indicates that the entire child linked list has been traversed. Therefore, the break statement is executed to launch the for loop. Because the resource node specified by the old parameter is not found in the Child linked list during the traversal process, the error value-einval is returned, indicating that the old parameter is an invalid value. ③ Next, determine whether the currently scanned node is the resource node specified by the parameter old. If yes, remove old from the Child linked list, that is, let the sibling pointer of the previous brother node of the current node TMP point to the next node of TMP, set the old-> parent pointer to null. The return value 0 indicates that the execution is successful. ④ If the node being scanned is not the resource old, scan the next element in the Child linked list. Therefore, point the pointer P to the TMP-> sibling member. 3.2.3 check whether the resource is in use. The check_resource () function is used to check whether an I/O resource is in use. The source code is as follows: int check_resource (struct resource * root, unsigned long start, unsigned long Len) {struct resource * Conflict, TMP; TMP. start = start; TMP. end = start + len-1; write_lock (& resource_lock); Conflict = _ request_resource (root, & TMP); If (! Conflict) _ release_resource (& TMP); write_unlock (& resource_lock); Return conflict? -Ebusy: 0;} the note for this function is as follows: ① construct a temporary resource TMP, indicating the resource to be checked [start, start + end-1]. ② Call the _ request_resource () function to request resources represented by TMP from the root node root. If the resource described by TMP is still used, the function returns NULL; otherwise, a non-null pointer is returned. Therefore, when conflict is null, call _ release_resource () to release the requested resource. ③ Return the value of-ebusy or 0 based on whether conflict is null. 3.2.4 The find_resource () function for searching available resources is used to find unused resources in a Resource Tree that meet the given conditions (that is, the resource length is size, and in [min,. The source code of the function is as follows:/** find empty slot in the Resource Tree given range and alignment. */static int find_resource (struct resource * root, struct resource * New, unsigned long size, unsigned long min, unsigned long Max, unsigned long align, void (* alignf) (void *, struct resource *, unsigned long), void * alignf_data) {struct resource * This = root-> child; New-> Start = root-> start; for (;) {If (this) New-> end = This-> Start; else new-> end = root-> end; If (New-> Start <min) New-> Start = min; if (New-> end> MAX) New-> end = max; New-> Start = (New-> Start + align-1 )&~ (Align-1); If (alignf) alignf (alignf_data, new, size ); if (New-> Start <New-> end & New-> end-New-> Start + 1> = size) {New-> end = new-> Start + size-1; return 0 ;}if (! This) break; New-> Start = This-> end + 1; this = This-> sibling;} return-ebusy;} the note for this function is as follows, this function also traverses the root child linked list to find unused resource holes. Therefore, it allows this pointer to indicate the child resource node being scanned. Its initial value is equal to root-> child, that is, pointing to the first node in the Child linked list, and make the initial value of new-> Start equal to root-> start, and then use a for loop to start scanning the child linked list. For each node to be scanned, perform the following operations on the body: ① first, determines whether the this pointer is null. If it is not blank, set new-> end to this-> start, that is, set resource new to indicate the unused resource range in the previous section of the current resource node "this. ② If this pointer is null, make new-> end equal to root-> end. This has two meanings: The first case is that the child pointer of the root node is null (that is, the root node does not have any sub-resources ). Therefore, we need to temporarily put new-> end to the maximum value. The second case is that the entire child linked list has been traversed, So let new indicate the unused resource range next to the last sub-resource. ③ Modify the value of new-> [start, end] based on the min and Max parameters so that the resource new is included in the [min, Max] area. ④ Perform the alignment operation next. ⑤ Then, determine whether the resource region new formed by the above steps is a valid Resource (end must be greater than or equal to start ), the length of the resource area must meet the size parameter (end-start + 1> = size ). If both conditions are met, we have found a resource hole that meets the conditions. Therefore, after modifying the value of new-> end, you can return the result (return value 0 indicates that the result is successful ). ⑥ If the two conditions cannot be met at the same time, it indicates that they have not been found. Therefore, you must continue to scan the linked list. Before continuing the scan, we still need to determine whether this pointer is null. If it is null, it indicates that a complete child linked list has been scanned. Therefore, a for loop can be launched. Otherwise, the value of new-> Start is changed to this-> end + 1, and this is directed to the next sibling resource node, so as to continue scanning the next subresource node in the linked list. 3.2.5 allocation interface allocate_resource () based on the find_resource () function, the function allocate_resource () is implemented: allocate a specified size in a Resource Tree and include it in the specified region [min, the unused resource area in Max. The source code is as follows:/** allocate empty slot in the Resource Tree given range and alignment. */INT allocate_resource (struct resource * root, struct resource * New, unsigned long size, unsigned long min, unsigned long Max, unsigned long align, void (* alignf) (void *, struct resource *, unsigned long), void * alignf_data) {int err; write_lock (& resource_lock); err = find_resource (root, new, size, Min, Max, align, Ali Gnf, alignf_data); If (ERR> = 0 & _ request_resource (root, new) Err =-ebusy; write_unlock (& resource_lock); Return err ;} 3.2.6 The get_resource_list () function is used to obtain the name list of sub-resources of the root node. This function is mainly used to support/proc/file systems (such as proc/ioports files and/proc/iomem files ). The source code is as follows: int get_resource_list (struct resource * root, char * Buf, int size) {char * FMT; int retval; FMt = "% 08lx-% 08lx: % s "; if (root-> end <0x10000) FMt = "% 04lx-% 04lx: % s"; read_lock (& resource_lock); retval = do_resource_list (root-> child, FMT, 8, Buf, BUF + size)-Buf; read_unlock (& resource_lock); Return retval;} we can see that this function is mainly called by calling the internal static function do_resource_list () the source code is as follows:/** this generates reports For/proc/ioports and/proc/iomem */static char * do_resource_list (struct resource * entry, const char * FMT, int offset, char * Buf, char * end) {If (offset <0) offset = 0; while (entry) {const char * name = entry-> name; unsigned long from, to; If (INT) (end-BUF) <80) return Buf; from = entry-> Start; To = entry-> end; If (! Name) name = ""; BUF + = sprintf (BUF, FMT + offset, from, to, name); If (Entry-> child) buf = do_resource_list (Entry-> child, FMT, offset-2, Buf, end); entry = entry-> sibling;} return Buf;} function do_resource_list () it is implemented through a while {} loop and recursive nested call, which is relatively simple and will not be explained in detail here.

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.