Based on the original author's analysis on linux2.4.0, the author's current kernel is 2.6.32. I will mark the difference in red as a supplement to the original article.
3.2 Linux I/O Resource Management
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 resource type 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 unidirectional non-cyclic linked list through the sibling pointer,
Its 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.
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
Number: ① root pointer, indicating the resource root node to be allocated; ② new pointer, pointing to the resource structure that describes the resource to be allocated (that is, the shadow area in the figure. Source of the Function
The Code 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:
① The resource lock resource_lock is used to read and write all resource trees. Any code segment must hold the lock before accessing a Resource Tree. Its definition is as follows (kernel/resource. C ):
Static rwlock_t resource_lock = rw_lock_unlocked;
2.6.32 is changed to static define_rwlock (resource_lock );
② We can see that the function actually completes the actual resource allocation 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 for 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, so as to check whether there are resource conflicts and insert new into the Child linked list.
Tables are arranged in the ascending order of the physical addresses of I/O resources ). Therefore, it uses the TMP pointer to point to the resource structure currently being scanned, And the pointer P to forward a resource
The sibling pointer member variable. 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 greater than the end position of new
Large. If 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 Section currently being scanned.
Point TMP (New-> sibling = TMP); ② the sibling pointer of the previous brother node of the current node TMP is changed to point to the new node
(* P = new); ③ set the new parent pointer to point to the root. 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 domain of the currently scanned node may conflict with the new one (in fact, there is an intersection between the two closed intervals), so further judgment is required. First, it modifies the pointer.
P. Point it to TMP-> sibling to continue scanning the child linked list. Then, determine whether TMP-> end is smaller than New-> Start, as shown in figure
If the value is less than, it indicates that no resource conflict exists between the current node TMP and new. Therefore, execute the continue statement to continue scanning the child linked list. Otherwise, if TMP-> end is greater
Or equal to new-> start, it means there is an intersection between TMP-> [start, end] and new-> [start, end. So return to the current section
Point pointer TMP, indicating a resource conflict.
3.2.2 release of resources
The release_resource () function 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. The source code is as follows:
Int release_resource (struct resource * old)
{
Int retval;
Write_lock (& resource_lock );
Retval = _ release_resource (old );
Write_unlock (& resource_lock );
Return retval;
}
We can see that it actually calls the internal static function _ release_resource () to release the actual 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;
For (;;){
TMP = * P;
If (! TMP)
Break;
If (TMP = old ){
* P = TMP-> sibling;
Old-> parent = NULL;
Return 0;
}
P = & TMP-> sibling;
}
Return-einval;
}
Note the preceding Function Code 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 current
The scanned resource, and 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 to the parent resource ). The steps for loop body are as follows:
① First, let the TMP pointer point to the node currently being scanned (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 previous brother of the current node TMP
The sibling pointer of the node points to the next node of TMP, and then sets 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 resources are occupied,
2.6.32 deleted this function
The check_resource () function is used to check whether a certain segment of I/O resources are occupied. 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;
}
Note the function 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.