Figure 167 Code flow diagram for the bufferrelated operations of blockreadfullpage
block_read_full_page reads a full page in three steps:
1. The buffers are set up and their state is checked.
2. The buffers are locked to rule out interference by other kernel threads in the next step.
3. The data are transferred to the buffers.
The first step involves checking whether buffers are already attached to the page as this is not always the case. If not, buffers are created using the create_empty_buffers function discussed a few sections
11I/O operations are usually more efficient if data are read or written in pages. This was the main reason for introducing the BIO layer that has replaced the old concept based on buffer heads.
back. Thereafter, the buffers — whether just created or already in existence — are identified using page_buffers before they are handled as described below. page_buffers simply translates the private element of the page into a buffer_head pointer by means of pointer conversion because, by convention, private points to the first buffer if buffers are attached to a page.
The main work of the kernel is to find out which buffers are current (their data match that on the block device or may even be more up-to-date) and therefore need not be read, and which buffers hold invalid data. To do this, the kernel makes use of the BH_Mapping and BH_Uptodate state bits, both of which may be set or unset.
It iterates over all buffers attached to the page and performs the following checks:
1. If the buffer contents are up-to-date (this can be checked with buffer_uptodate), the kernel continues to process the next buffer. In this case, the data in the page cache and on the block device match, and an additional read operation is not required.
2. If there is no mapping (BH_Mapping is not set), get_block is invoked to determine the position of the block on the block storage medium.
ext2_get_block and ext3_get_block, respectively, are used for this purpose on Ext2/Ext3 filesystems. Other filesystems use functions with similar names. Common to all alternatives is that the buffer_head structure is modified so that it can be used to locate the desired block in the filesystem. Essentially, this involves setting the b_bdev and b_blocknr fields because they identify the desired block.
Was this article helpful?