Linux Management of I/O port resources (3)

Source: Internet
Author: User

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.4 search for available resources

The find_resource () function is used to find unused and meet the given conditions in a Resource Tree (that is, the resource length is size and is within the range of [min, Max). The function source code 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),/* 2.6.32 alignf function has a parameter void (* alignf) (void *, struct resource *,
Resource_size_t size, resource_size_t align )*/

Void * alignf_data)
{
Struct resource * This = root-> child;

New-> Start = root-> start;

/*
* Skip past an allocated resource that starts at 0, since the assignment
* Of this-> Start-1 to new-> end below wowould cause an underflow.
*/

If (this & this-> Start = 0 ){
New-> Start = This-> end + 1;
This = This-> sibling;
}

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

Note the function as follows:

Similarly, this function also needs to traverse the root child linked list to find unused resource holes. Therefore, it allows this pointer to indicate the child resource node being scanned, and its initial value is equal
Root-> child, that is, point 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 starts scanning the child linked list. For each node to be scanned, perform the following operations:

① First, judge 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.
New indicates 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), and the length of the resource region 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 the end value is modified, the return value can be returned (the return value 0 indicates that the operation 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
After scanning a complete child linked list, you can launch a for loop. Otherwise, change the value of new-> start to this-> end + 1 and set this
To the next sibling resource node 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 allocate_resource () function is used to 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),/* 2.6.32 alignf function has a parameter void (* alignf) (void *, struct resource *,
Resource_size_t size, resource_size_t align )*/

Void * alignf_data)
{
Int err;

Write_lock (& resource_lock );
Err = find_resource (root, new, size, Min, Max, align, alignf,
Alignf_data );
If (ERR> = 0 & _ request_resource (root, new ))
Err =-ebusy;
Write_unlock (& resource_lock );
Return err;
}

3.2.6 obtain the Resource Name List

The get_resource_list () function is used to obtain the list of sub-resource names 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:
This function has been deleted in 2.6.32.

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

It can be seen that this function is mainly implemented by calling the internal static function do_resource_list (). Its 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,;

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

The do_resource_list () function is implemented through a while {} loop and recursive nested call. It is relatively simple and will not be explained in detail here.

3.3 managing I/O Region Resources

In Linux, I/O ing-based I/O Ports and memory ing-based I/O port resources are collectively referred to as "I/O Region" (I/O
Region ). I/O
Region is still an I/O resource, so it can still be described using the resource structure type. Next let's take a look at how Linux manages I/O
Region.

3.3.1 distribution of I/O Region

Based on function _ request_resource (), Linux implements the function _ request_region () used to allocate the I/O region, as follows:

Struct resource * _ request_region (struct resource * parent,
Unsigned long start, unsigned long N, const char * Name, int flags
) // A flags is added to the new kernel.

{
Struct resource * res = kmalloc (sizeof (* res), gfp_kernel );

Struct resource * res = kzarloc (sizeof (* res), gfp_kernel );

If (! Res)
Return NULL;

If (RES ){
Memset (Res, 0, sizeof (* res ));
Res-> name = Name;
Res-> Start = start;
Res-> end = start + n-1;
Res-> flags = ioresource_busy;

Res-> flags | = flags;

Write_lock (& resource_lock );

For (;;){
Struct resource * Conflict;

Conflict = _ request_resource (parent, Res );
If (! Conflict)
Break;
If (conflict! = Parent ){
Parent = conflict;
If (! (Conflict-> flags & ioresource_busy ))
Continue;
}

/* Uhhuh, that didn't work out ..*/
Kfree (RES );
Res = NULL;
Break;
}
Write_unlock (& resource_lock );
}
Return res;
}

Note:

① First, call the kmalloc () function to allocate a resource structure in the slab distributor cache.

② Then, initialize the allocated resource structure based on the parameter value. Note! The flags member is initialized as ioresource_busy.

③ Next, use a for loop to start resource allocation. The steps of the loop body are as follows:

L
First, call the _ request_resource () function to allocate resources. If null is returned, the allocation is successful. Therefore, the break statement is executed to launch the for loop. the pointer to the allocated resource structure is returned, and the function ends successfully.

L
If the _ request_resource () function fails to be allocated, the system further checks whether the returned conflicting resource node is the parent resource node parent. If not, the row is allocated.
To lower the level, try to allocate resources in the conflicting Resource Nodes (only when the conflicting resource node does not set ioresource_busy ).
The parent pointer is equal to conflict and executed when conflict-> flags & ioresource_busy is 0
The continue statement continues the for loop.

L
Otherwise, if the conflicting resource node is the parent node parent or the conflicting resource node sets the ioresource_busy flag, the allocation fails. Call the kfree () function to release the allocated resource structure, set the res pointer to null, and use the break statement to launch the for loop.

④ Return the pointer to the allocated resource structure.

3.3.2 release of I/O Region

Function _ release_region () is used to release the specified range of I/O in a parent resource node.
Region. In fact, the implementation idea of this function is similar to _ release_resource. The source code is as follows:

Void _ release_region (struct resource * parent,
Unsigned long start, unsigned long N)
{
Struct resource ** P;
Unsigned long end;

P = & parent-> child;
End = start + n-1;

Write_lock (& resource_lock );

For (;;){
Struct resource * res = * P;

If (! Res)
Break;
If (res-> Start <= Start & res-> end> = end)
{
If (! (Res-> flags & ioresource_busy )){
P = & res-> child;
Continue;
}
If (res-> Start! = Start ''res-> end! = End)
Break;
* P = res-> sibling;

Write_unlock (& resource_lock );

Kfree (RES );
Return;
}
P = & res-> sibling;
}

Write_unlock (& resource_lock );

Printk ("trying to free nonexistent Resource
<% 08lx-% 08lx>
", Start, end );
}

Similarly, this function uses a for loop to traverse the child linked list of the parent resource parent. Therefore, it directs the pointer res to the Child resource node being scanned, And the pointer P to forward
The sibling member variable of a sub-Resource Node. The initial value of P is to point to parent-> child. The steps for the For Loop body are as follows:

① Point the res pointer to the currently scanned sub-Resource Node (RES = * P ).

② If the res pointer is null, it indicates that a complete child linked list has been scanned, so the for loop is exited.

③ If the res pointer is not null, check whether the specified I/O region range is fully included in the current resource node, that is, whether [start, start + n-1] is a package.
Included in res-> [start, end. If not, point P to the sibling member of the current resource node and continue the for loop. If yes, perform the following steps:
Steps:

L
First, check whether the ioresource_busy flag is set for the current resource node. If this flag is not set, it indicates that there may be subnodes under the Resource Node. Therefore, the scanning process is downgraded to a level, so the P pointer is modified to point to res-> child, then execute the continue statement to continue the for loop.

L
If the ioresource_busy flag is set. Make sure that the current resource node is the specified I/O region, and then remove the current resource node from its parent resource's child linked list
Division. This can be achieved by pointing the sibling pointer of the previous sibling resource node to the next sibling Resource Node of the current resource node (that is, * P = res-> sibling ),
Then, call the kfree () function to release the resource structure of the current resource node. Then the function can return successfully.

3.3.3 check whether the specified I/O region is in use

Function _ check_region () checks the specified I/O
Whether the region is in use. The source code is as follows:

Int _ check_region (struct resource * parent, unsigned long start,
Unsigned long N)
{
Struct resource * res;

Res = _ request_region (parent, start, N, "Check-region ");
If (! Res)
Return-ebusy;

Release_resource (RES );
Kfree (RES );
Return 0;
}

The implementation of this function is similar to the implementation of _ check_resource. First, it tries to allocate the specified I/O in the parent resource parent by calling the _ request_region () function.
Region. If the allocation fails, null is returned. Therefore, the function returns an error value-ebusy indicating the specified I/O.
Region is in use. If the res pointer is not null, the specified I/O
Region is not in use. Therefore, call the _ release_resource () function to release the allocated Resource (Instead, remove the res structure from the Child linked list of the parent), and then call kfree () the function releases the memory occupied by the res structure. Finally, return 0 to indicate the specified I/O
Region is not in use.

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.