Block devices differ from char devices and normal files in that they
can be mounted on the computer’s filesystem. Mounting provides a
level of indirection not seen with char devices, which are accessed
through a struct file
pointer that is held by a
specific process. When a filesystem is mounted, there is no process
holding that file
structure.
When the kernel mounts a device in the filesystem, it invokes the
normal open method to access the driver.
However, in this case both the filp
and
inode
arguments to open are
dummy variables. In the file
structure, only the
f_mode
and f_flags
fields hold
anything meaningful; in the inode
structure only
i_rdev
may be used. The remaining fields hold
random values and should not be used. The value of
f_mode
tells the driver whether the device is to be
mounted read-only (f_mode == FMODE_READ
) or
read/write (f_mode == (FMODE_READ|FMODE_WRITE)
).
This interface may seem a little strange; it is done this way for
two reasons. First is that the open method can
still be called normally by a process that accesses the device
directly—the mkfs utility, for
example. The other reason is a historical artifact: block devices
once used the same file_operations
structure as
char devices, and thus had to conform to the same interface.
Other than the limitations on the arguments to the open method, the driver does not really see anything unusual when a filesystem is mounted. The device is opened, and then the request method is invoked to transfer blocks back and forth. The driver cannot really tell the difference between operations that happen in response to an individual process (such as fsck) and those that originate in the filesystem layers of the kernel.
As far as umount is concerned, it just flushes
the buffer cache and calls the release driver
method. Since there is no meaningful filp
to pass
to the release method, the kernel uses
NULL
. Since the release
implementation of a block driver can’t use
filp->private_data
to access device information,
it uses inode->i_rdev
to differentiate between
devices instead. This is how sbull
implements release:
int sbull_release (struct inode *inode, struct file *filp) { Sbull_Dev *dev = sbull_devices + MINOR(inode->i_rdev); spin_lock(&dev->lock); dev->usage--; MOD_DEC_USE_COUNT; spin_unlock(&dev->lock); return 0; }
Other driver functions are not affected by the “missing
filp
” problem because they aren’t involved with
mounted filesystems. For example, ioctl is
issued only by processes that explicitly open the
device.