6.6 About Block Device Drivers

Block device drivers support devices that handle fixed rather than variable amounts of data and access physically addressable storage media or support file system access. Hard disks and flash memory are common types of block devices.

The implementation of block device drivers is likely to differ in many respects between Linux and UNIX operating systems, with most differences arising from the functions that the driver uses to handle block requests, the preferred use of the getgeo() (get geometry) method instead of an ioctl() method to support partitioning tools, and the use of the kernel block device interface (blkdev) to handle block I/O operations. The driver_strategy() routine that UNIX block device drivers use to handle the reading or writing of data from or to a device is named driver_request() on Linux systems.

The block_device_operations structure for a block device, defined in <linux/blkdev.h>, contains a set of method pointers that specify how the system interacts with the device via the device files under /dev when using system calls such as open() and ioctl().

struct block_device_operations {
        int (*open) (struct block_device *, fmode_t);
        int (*release) (struct gendisk *, fmode_t);
        int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
        int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
        int (*direct_access) (struct block_device *, sector_t,
                                                void **, unsigned long *);
        unsigned int (*check_events) (struct gendisk *disk,
                                      unsigned int clearing);
        /* ->media_changed() is DEPRECATED, use ->check_events() instead */
        int (*media_changed) (struct gendisk *);
        void (*unlock_native_capacity) (struct gendisk *);
        int (*revalidate_disk) (struct gendisk *);
        int (*getgeo)(struct block_device *, struct hd_geometry *);
        /* this callback is with swap_lock and sometimes page table lock held */
        void (*swap_slot_free_notify) (struct block_device *, unsigned long);
        struct module *owner;
};

A simple block device driver might only need to implement a subset of the methods defined for this structure, for example:

struct block_device_operations driver_blkdevops = {
    .owner   = THIS_MODULE,
    .open    = driver_open,
    .release = driver_release,
    .ioctl   = driver_ioctl,
    .getgeo  = driver_getgeo,
};

As well as the driver_request() and other driver methods, it is usual to define module_init() initialization and module_exit() cleanup routines for the driver that are called when a driver is loaded and unloaded. These routines should call register_blkdev() and unregister_blkdev() to register and unregister the device major number for the driver, and alloc_gendisk() and del_gendisk() to allocate and delete the kernel's representation of a generic disk (gendisk) device. To make the disk available to the system, an initialization routine usually calls add_disk() as its final action.