The ioctl Method

Like char devices, block devices can be acted on by using the ioctl system call. The only relevant difference between the two implementations is that block drivers share a number of common ioctl commands that most drivers are expected to support.

The commands that block drivers usually handle are the following, declared in <linux/fs.h>:

BLKGETSIZE

Retrieve the size of the current device, expressed as the number of sectors. The value of arg passed by the system call is a pointer to a long value and should be used to copy the size to a user-space variable. This ioctl command is used, for instance, by mkfs to know the size of the filesystem being created.

BLKFLSBUF

Literally, ``flush buffers.'' The implementation of this command is the same for every device and is shown later with the sample code for the whole ioctl method.

BLKRAGET

Used to get the current read-ahead value for the device. The current value should be written to user space as a long item using the pointer passed to ioctl in arg.

BLKRASET

Set the read-ahead value. The user process passes the new value in arg.

BLKRRPART

Reread the partition table. This command is meaningful only for partitionable devices, introduced later in Section 12.7.

BLKROSET , BLKROGET

These commands are used to change and check the read-only flag for the device. They are implemented by the macro RO_IOCTLS(kdev_t dev, unsigned long where) because the code is device-independent. The macro is defined in blk.h.

HDIO_GETGEO

Defined in <linux/hdreg.h> and used to retrieve the disk geometry. The geometry should be written to user space in a struct hd_geometry, which is declared in hdreg.h as well. sbull shows the general implementation for this command.

The HDIO_GETGEO command is the most commonly used of a series of HDIO_ commands, all defined in <linux/hdreg.h>. The interested reader can look in ide.c and hd.c for more information about these commands.

The major drawback to the commands just listed is that they are defined in the ``old'' way (see Section 5.1.1 in Chapter 5), and thus the macros for the bitfields can’t be used to simplify coding--each command should implement its own verify_area. However, if a driver needs to define its own commands to exploit particular features of the device, you are free to use the ``new'' way of defining commands.

The sbull device supports only the general commands above, because implementing device-specific commands is no different from the implementation of commands for char drivers. The ioctl implementation for sbull is shown below; it should help you understand the commands listed above.

int sbull_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
    int err, size;
    struct hd_geometry *geo = (struct hd_geometry *)arg;

    PDEBUG("ioctl 0x%x 0x%lx
", cmd, arg);
    switch(cmd) {

      case BLKGETSIZE:
        /* Return the device size, expressed in sectors */
        if (!arg) return -EINVAL; /* NULL pointer: not valid */
        err=verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));

        if (err) return err;
        put_user ( 1024 * sbull_sizes[MINOR(inode->i_rdev)]
                   / sbull_hardsects[MINOR(inode->i_rdev)],
                     (long *) arg);
        return 0;

      case BLKFLSBUF: /* flush */
        if (!suser()) return -EACCES; /* only root */
        fsync_dev(inode->i_rdev);
        invalidate_buffers(inode->i_rdev);
        return 0;

      case BLKRAGET: /* return the readahead value */
        if (!arg)  return -EINVAL;
        err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
        if (err) return err;
        put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
        return 0;

      case BLKRASET: /* set the readahead value */
        if (!suser()) return -EACCES;
        if (arg > 0xff) return -EINVAL; /* limit it */
        read_ahead[MAJOR(inode->i_rdev)] = arg;
        return 0;

      case BLKRRPART: /* re-read partition table: can't do it */
        return -EINVAL;

      RO_IOCTLS(inode->i_rdev, arg); /* the default RO operations */

      case HDIO_GETGEO:
        /*
         * get geometry: we have to fake one...  trim the size to a
         * multiple of 64 (32KB): tell we have 16 sectors, 4 heads,
         * whatever cylinders. Tell also that data starts at sector 4.
         */
        size = sbull_size * 1024 / sbull_hardsect;
        size &= ~0x3f; /* multiple of 64 */
        if (geo==NULL) return -EINVAL;
        err = verify_area(VERIFY_WRITE, geo, sizeof(*geo));
        if (err) return err;
        put_user(size >> 6, &geo->cylinders);
        put_user(        4, &geo->heads);
        put_user(       16, &geo->sectors);
        put_user(        4, &geo->start);
        return 0;
    }

    return -EINVAL; /* unknown command */
}

The PDEBUG statement at the beginning of the function has been left in so that when you compile the module, you can turn on debugging to see which ioctl commands are invoked on the device.

For example, with the ioctl commands shown, you can use fdisk on sbull. This is a sample short session I had on my own system:

morgana.root# fdisk /dev/sbull0

Command (m for help): p

Disk /dev/sbull0: 4 heads, 16 sectors, 64 cylinders
Units = cylinders of 64 * 512 bytes

      Device Boot  Begin   Start     End  Blocks   Id  System

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

The following messages appeared on my system log during the session:

Oct 29 10:22:08 morgana kernel: sbull: ioctl 0x301 0xbffffc74
Oct 29 10:22:15 morgana kernel: sbull: ioctl 0x125f 0x2
Oct 29 10:22:15 morgana kernel: sbull: revalidate for dev 0

The first ioctl is HDIO_GETGEO, invoked at fdisk startup, and the second is BLKRRPART. The sbull implementation for the latter command just calls the revalidate function, which in turn prints the last message in the printout just shown (see Section 12.6.2 later in this chapter).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.200.86