next up previous contents
Next: Linux Device Driver Issues Up: Network Block Device Driver Previous: Defer Server   Contents

Loading Block device drivers

There are no read or write operations provided in the block_device_operations structure. All I/O to block devices is normally buffered by the system (the only exception is with raw devices); user processes do not perform direct I/O to these devices. User-mode access to block devices usually is implicit in filesystem operations they perform, and those operations clearly benefit from I/O buffering. However, even direct I/O to a block device, such as when a filesystem is created, goes through the Linux buffer cache. As a result, the kernel provides a single set of read and write functions for block devices, and drivers do not need to worry about them. Clearly, a block driver must eventually provide some mechanism for actually doing block I/O to a device. In Linux, the method used for these I/O operations is called request; it is the equivalent of the strategy function found on many Unix systems. The request method handles both read and write operations and can be somewhat complex. For the purposes of block device registration, however, we must tell the kernel where our request method is. This method is not kept in the block_device_operations structure, for both historical and performance reasons; instead, it is associated with the queue of pending I/O operations for the device. By default, there is one such queue for each major number. A block driver must initialize that queue with blk_init_queue. The request_queue member contains the I/O request queue that we have just initialized. As we have seen, block drivers need not be aware of clustering at all, Linux transparently splits each clustered request into its component pieces. In many cases, however, a driver can do better by explicitly acting on clustering. To take advantage of clustering, a block driver must look directly at the list of buffer_head structures attached to the request. This list is pointed to by CURRENT->bh, subsequent buffers can be found by following the b_reqnext pointers in each buffer_head structure A driver performing clustered I/O should follow roughly this sequence of operations with each buffer in the cluster:
  1. Arrange to transfer the data block at address bh->b_data, of size bh->b_size bytes. The direction of the data transfer is CURRENT->cmd (i.e., either READ or WRITE).
  2. Retrieve the next buffer head in the list: bh->b_reqnext. Then detach the buffer just transferred from the list, by zeroing its b_reqnext--the pointer to the new buffer you just retrieved.
  3. Update the request structure to reflect the I/O done with the buffer that has just been removed. Both CURRENT->hard_nr_sectors and CURRENT->nr_sectors should be decremented by the number of sectors (not blocks) transferred from the buffer. The sector numbers CURRENT->hard_sector and CURRENT->sector should be incremented by the same amount. Performing these operations keeps the request structure consistent.
  4. Loop back to the beginning to transfer the next adjacent block. When the I/O on each buffer completes, your driver should notify the kernel by calling the buffer's I/O completion routine

next up previous contents
Next: Linux Device Driver Issues Up: Network Block Device Driver Previous: Defer Server   Contents
Sudhindra Rao 2003-11-22