Block devices transfer information one block at a time, while process address spaces (or to be more precise, memory regions allocated to the process) are defined as sets of pages. This mismatch can be hidden to some extent by using page I/O operations. They may be activated in the following cases:
• A process issues a read( ) or write( ) system call on a file (see Chapter 15).
• A process reads a location of a page that maps a file in memory (see Chapter 15).
• The kernel flushes some dirty pages related to a file memory mapping to disk (see Section 15.2.5).
• When swapping in or swapping out, the kernel loads from or saves to disk the contents of whole page frames (see Chapter 16).
Page I/O operations can be activated by several kernel functions. In this section, we'll present the brw_page( ) function used to read or write swap pages (see Chapter 16). Other functions that start page I/O operations are discussed in Chapter 15.
The brw_page( ) function receives the following parameters:
Type of I/O operation (read, write, or reada)
Address of a page descriptor dev
Block device number (major and minor numbers)
Array of logical block numbers size
The page descriptor refers to the page involved in the page I/O operation. It must already be locked (PG_locked flag on) before invoking brw_page( ) so that no other kernel control path can access it. The page is considered as split into 4096/size buffers; the i th buffer in the page is associated with the block b[i] of device dev.
The function performs the following operations:
1. Checks the page->buffers field; if it is NULL, invokes create_empty_buffers( ) to allocate temporary buffer heads for all buffers included in the page (such buffer heads are called asynchronous; they are discussed in Section 14.2.1). The address of the buffer head for the first buffer in the page is stored in the page->buffers field. The b_this_page field of each buffer head points to the buffer head of the next buffer in the page.
Conversely, if the page->buffers field is not null, the kernel does not need to allocate temporary buffer heads. In fact, in this case, the page stores some buffers already included in the buffer cache, presumably because some of them were previously involved in block I/O operations (see Section 14.2.2 for further details).
2. For each buffer head in the page, performs the following substeps:
a. Sets the BH_Lock (locks the buffer for the I/O data transfer) and the BH_Mapped (the buffer maps a file on disk) flags of the buffer head.
b. Stores in the b_blocknr field the value of the corresponding element of the array b.
c. Since it is an asynchronous buffer head, sets the BH_Async flag, and in the b_end_io field, stores a pointer to end_buffer_io_async( ) (described next).
3. For each buffer head in the page, invokes submit_bh( ) to request the buffer (see Section 13.4.6 earlier in this chapter.)
The submit_bh( ) function activates the device driver of the block device being accessed. As described in the earlier section Section 13.4.7, the device driver performs the actual data transfer and then invokes the b_end_io method of all asynchronous buffer heads that have been transferred. The b_end_io field points to the end_buffer_io_async( ) function, which performs the following operations:
1. Sets the BH_Uptodate flag of the asynchronous buffer head according to the result of the I/O operation.
2. If the BH_Uptodate flag is off, sets the PG_error flag of the page descriptor because an error occurred while transferring the block. The function gets the page descriptor address from the b_page field of the buffer head.
3. Gets the page_update_lock spin lock.
4. Clears both the BH_Async and the BH_Lock flags of the buffer head, and awakens each process waiting for the buffer.
5. If any of the buffer heads in the page are still locked (i.e., the I/O data transfer is not yet terminated), releases the page_update_lock spin lock and returns.
6. Otherwise, releases the page_update_lock spin lock and checks the PG_error flag of the page descriptor. If it is cleared, then all data transfers on the page have successfully completed, so the function sets the PG_uptodate flag of the page descriptor.
7. Unlocks the page, clears the PG_locked flag, and wakes any process sleeping on page->wait wait queue.
Notice that once the page I/O operation terminates, the temporary buffer heads allocated by create_empty_buffers( ) are not automatically released. As we shall see in Chapter 16, the temporary buffer heads are released only when the kernel tries to reclaim some memory.
I [email protected] RuBoard
Was this article helpful?