Writing Device Drivers

Data Transfers

Most block drivers use the strategy(9F) entry point to transfer data.


int xxstrategy(struct buf *bp)

The strategy(9E) entry point is used to read and write data buffers to and from a block device. The name strategy refers to the fact that this entry point may implement some optimal strategy for ordering requests to the device.

strategy(9E) can be written to process one request at a time (synchronous transfer), or to queue multiple requests to the device (asynchronous transfer). When choosing a method, the abilities and limitations of the device should be taken into account.

The strategy(9E) routine is passed a pointer to a buf(9S) structure. This structure describes the transfer request, and contains status information on return. buf(9S) and strategy(9E) are the focus of block device operations.

buf Structure

The following buf structure members are important to block drivers:

	   int				 	b_flags;			/* Buffer Status */
 	struct buf			*av_forw;		/* Driver work list link */
 	struct buf			*av_back;		/* Driver work lists link */
 	size_t				b_bcount;		/* # of bytes to transfer */
 	union {
 		caddr_t			b_addr;			/* Buffer's virtual address */
 	} b_un;
 	daddr_t				b_blkno;			/* Block number on device */
 	diskaddr_t			b_lblkno;		/* Expanded block number on device */
 	size_t				b_resid;			/* # of bytes not transferred */
 								        		/* after error */
 	int				   b_error;			/* Expanded error field */
 	void				   *b_private;		/* "opaque" driver private area */
 	dev_t				   b_edev;			/* expanded dev field */

b_flags contains status and transfer attributes of the buf structure. If B_READ is set, the buf structure indicates a transfer from the device to memory, otherwise it indicates a transfer from memory to the device. If the driver encounters an error during data transfer, it should set the B_ERROR field in the b_flags member and provide a more specific error value in b_error. Drivers should use bioerror(9F) rather than setting B_ERROR.

Caution - Caution -

Drivers should never clear b_flags.

av_forw and av_back are pointers that the driver can use to manage a list of buffers by the driver. See "Asynchronous Data Transfers" for a discussion of the av_forw and av_back pointers.

b_bcount specifies the number of bytes to be transferred by the device.

b_un.b_addr is the kernel virtual address of the data buffer.

b_blkno is the starting 32-bit logical block number on the device for the data transfer, expressed in DEV_BSIZE (512 bytes) units. The driver should use either b_blkno or b_lblkno, but not both.

b_lblkno is the starting 64-bit logical block number on the device for the data transfer, expressed in DEV_BSIZE (512 bytes) units. The driver should use either b_blkno or b_lblkno, but not both.

b_resid is set by the driver to indicate the number of bytes that were not transferred because of an error. See Example 10-8for an example of setting b_resid. The b_resid member is overloaded: it is also used by disksort(9F).

b_error is set to an error number by the driver when a transfer error occurs. It is set in conjunction with the b_flags B_ERROR bit. See Intro(9E) for details regarding error values. Drivers should use bioerror(9F) rather than setting b_error directly.

b_private is for exclusive use by the driver to store driver-private data.

b_edev contains the device number of the device involved in the transfer.


When a buf structure pointer is passed into the device driver's strategy(9E) routine, the data buffer referred to by b_un.b_addr is not necessarily mapped in the kernel's address space. This means that the driver cannot directly access the data. Most block-oriented devices have DMA capability, and therefore do not need to access the data buffer directly. Instead, they use the DMA mapping routines to allow the device's DMA engine to do the data transfer. For details about using DMA, see Chapter 7, DMA.

If a driver needs to directly access the data buffer (as opposed to having the device access the data), it must first map the buffer into the kernel's address space using bp_mapin(9F). bp_mapout(9F) should be used when the driver no longer needs to access the data directly.

Caution - Caution -

bp_mapout(9F) should only be called on buffers which have been allocated and are owned by the device driver. It must not be called on buffers passed to the driver through the strategy(9E) entry point (for example a filesystem). Because bp_mapin(9F) does not keep a reference count, bp_mapout(9F) will remove any kernel mapping that a layer above the device driver might rely on.