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).
18.118.200.86