Like char devices, block devices can be acted on by using the ioctl system call. The only relevant difference between block and char ioctl 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 in by the system call is a pointer to along
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.
-
BLKRRPART
Reread the partition table. This command is meaningful only for partitionable devices, introduced later in this chapter.
-
BLKRAGET
,BLKRASET
Used to get and change the current block-level read-ahead value (the one stored in the
read_ahead
array) for the device. ForGET
, the current value should be written to user space as along
item using the pointer passed to ioctl inarg
; forSET
, the new value is passed as an argument.-
BLKFRAGET
,BLKFRASET
Get and set the filesystem-level read-ahead value (the one stored in
max_readahead
) for this device.-
BLKROSET
,BLKROGET
These commands are used to change and check the read-only flag for the device.
-
BLKSECTGET
,BLKSECTSET
These commands retrieve and set the maximum number of sectors per request (as stored in
max_sectors
).-
BLKSSZGET
Returns the sector size of this block device in the integer variable pointed to by the caller; this size comes directly from the
hardsect_size
array.-
BLKPG
The
BLKPG
command allows user-mode programs to add and delete partitions. It is implemented by blk_ioctl (described shortly), and no drivers in the mainline kernel provide their own implementation.-
BLKELVGET
,BLKELVSET
These commands allow some control over how the elevator request sorting algorithm works. As with
BLKPG
, no driver implements them directly.-
HDIO_GETGEO
Defined in
<linux/hdreg.h>
and used to retrieve the disk geometry. The geometry should be written to user space in astruct hd_geometry
, which is declared inhdreg.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.
Almost all of these ioctl commands are
implemented in the same way for all block devices. The 2.4 kernel has
provided a function, blk_ioctl, that may be
called to implement the common commands; it is declared in
<linux/blkpg.h>
. Often the only ones that
must be implemented in the driver itself are
BLKGETSIZE
and HDIO_GETGEO
. The
driver can then safely pass any other commands to
blk_ioctl for handling.
The sbull device supports only the general commands just listed, because implementing device-specific commands is no different from the implementation of commands for char drivers. The ioctl implementation for sbull is as follows:
int sbull_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int err; long size; struct hd_geometry geo; PDEBUG("ioctl 0x%x 0x%lx\n", cmd, arg); switch(cmd) { case BLKGETSIZE: /* Return the device size, expressed in sectors */ if (!arg) return -EINVAL; /* NULL pointer: not valid */ err = ! access_ok (VERIFY_WRITE, arg, sizeof(long)); if (err) return -EFAULT; size = blksize*sbull_sizes[MINOR(inode->i_rdev)] / sbull_hardsects[MINOR(inode->i_rdev)]; if (copy_to_user((long *) arg, &size, sizeof (long))) return -EFAULT; return 0; case BLKRRPART: /* reread partition table: can't do it */ return -ENOTTY; case HDIO_GETGEO: /* * Get geometry: since we are a virtual device, we have to make * up something plausible. So we claim 16 sectors, four heads, * and calculate the corresponding number of cylinders. We set * the start of data at sector four. */ err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo)); if (err) return -EFAULT; size = sbull_size * blksize / sbull_hardsect; geo.cylinders = (size & ~0x3f) >> 6; geo.heads = 4; geo.sectors = 16; geo.start = 4; if (copy_to_user((void *) arg, &geo, sizeof(geo))) return -EFAULT; return 0; default: /* * For ioctls we don't understand, let the block layer * handle them. */ return blk_ioctl(inode->i_rdev, cmd, arg); } return -ENOTTY; /* 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.
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.