Backward Compatibility

Much has changed with the block device layer, and most of those changes happened between the 2.2 and 2.4 stable releases. Here is a quick summary of what was different before. As always, you can look at the drivers in the sample source, which work on 2.0, 2.2, and 2.4, to see how the portability challenges have been handled.

The block_device_operations structure did not exist in Linux 2.2. Instead, block drivers used a file_operations structure just like char drivers. The check_media_change and revalidate methods used to be a part of that structure. The kernel also provided a set of generic functions—block_read, block_write, and block_fsync—which most drivers used in their file_operations structures. A typical 2.2 or 2.0 file_operations initialization looked like this:

struct file_operations sbull_bdops = {
    read:       block_read,
    write:      block_write,
    ioctl:      sbull_ioctl,
    open:       sbull_open,
    release:    sbull_release,
    fsync:      block_fsync,
    check_media_change: sbull_check_change,
    revalidate: sbull_revalidate
};

Note that block drivers are subject to the same changes in the file_operations prototypes between 2.0 and 2.2 as char drivers.

In 2.2 and previous kernels, the request function was stored in the blk_dev global array. Initialization required a line like

blk_dev[major].request_fn = sbull_request;

Because this method allows for only one queue per major number, the multiqueue capability of 2.4 kernels is not present in earlier releases. Because there was only one queue, the request function did not need the queue as an argument, so it took none. Its prototype was as follows:

void (*request) (void);

Also, all queues had active heads, so blk_queue_headactive did not exist.

There was no blk_ioctl function in 2.2 and prior releases. There was, however, a macro called RO_IOCTLS, which could be inserted in a switch statement to implement BLKROSET and BLKROGET. sysdep.h in the sample source includes an implementation of blk_ioctl that uses RO_IOCTLS and implements a few other of the standard ioctl commands as well:

#ifdef RO_IOCTLS
static inline int blk_ioctl(kdev_t dev, unsigned int cmd,
                            unsigned long arg)
{
    int err;

    switch (cmd) {
      case BLKRAGET: /* return the read-ahead value */
        if (!arg)  return -EINVAL;
        err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));
        if (err) return -EFAULT;
        PUT_USER(read_ahead[MAJOR(dev)],(long *) arg);
        return 0;

      case BLKRASET: /* set the read-ahead value */
        if (!capable(CAP_SYS_ADMIN)) return -EACCES;
        if (arg > 0xff) return -EINVAL; /* limit it */
        read_ahead[MAJOR(dev)] = arg;
        return 0;

      case BLKFLSBUF: /* flush */
        if (! capable(CAP_SYS_ADMIN)) return -EACCES; /* only root */
        fsync_dev(dev);
        invalidate_buffers(dev);
        return 0;

        RO_IOCTLS(dev, arg);
    }
    return -ENOTTY;
}
#endif  /* RO_IOCTLS */

The BLKFRAGET, BLKFRASET, BLKSECTGET, BLKSECTSET, BLKELVGET, and BLKELVSET commands were added with Linux 2.2, and BLKPG was added in 2.4.

Linux 2.0 did not have the max_readahead array. The max_segments array, instead, existed and was used in Linux 2.0 and 2.2, but device drivers did not normally need to set it.

Finally, register_disk did not exist until Linux 2.4. There was, instead, a function called resetup_one_dev, which performed a similar function:

resetup_one_dev(struct gendisk *gd, int drive);

register_disk is emulated in sysdep.h with the following code:

static inline void register_disk(struct gendisk *gdev, kdev_t dev,
                unsigned minors, struct file_operations *ops, long size)
{
    if (! gdev)
        return;
    resetup_one_dev(gdev, MINOR(dev) >> gdev->minor_shift);
}

Linux 2.0 was different, of course, in not supporting any sort of fine-grained SMP. Thus, there was no io_request_lock and much less need to worry about concurrent access to the I/O request queue.

One final thing worth keeping in mind: although nobody really knows what will happen in the 2.5 development series, a major block device overhaul is almost certain. Many people are unhappy with the design of this layer, and there is a lot of pressure to redo it.

Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.