based on the Sbull program in the Linux device driver book to Linux Block device driver summary analysis.
Start by understanding the core data structures in this block device:
struct Sbull_dev {
int size; /* Device Size in sectors */
U8 *data; /* The data array */
Short users; /* How many users * *
Short Media_change; /* Flag a media change? */
spinlock_t lock; /* for mutual exclusion */
struct Request_queue *queue; /* The device request queue */
struct Gendisk *gd; /* The GENDISK structure */
struct Timer_list timer; /* For simulated media changes */
};
In this structure, struct request_queue and struct gendiskis an important member of this structure,
is also an important structure in the block device, most of the elaboration in this article is based on the operation of these two structures.
I. Process:
Block device drivers also start with the Init function, so the analysis starts here as well.
The first step:
Register_blkdev (sbull_major, "sbull");
First, register the block device, the first parameter is the device number, for 0 The dynamic allocation of the table, the second is a device name.
Step Two:
Devices = Kmalloc (ndevices*sizeof (struct Sbull_dev), gfp_kernel);
Creating the core data structure of this block device, which is the object entity of this block device, creates a Ndevice
The entity.
Step Three:
Setup_device (Devices + i, i);
To put it bluntly, initialize the entity and add it to the block layer of the system. This step is very important and itFinish
into the following actions:
1. Initialize a spin lock.
Spin_lock_init (&dev->lock);
2. Assign a request queue and use the spin lock in 1 to control access to the queue.
Dev->queue = Blk_init_queue (Sbull_full_request, &dev->lock);
3. Assign, initialize and install the corresponding gendisk structure.
DEV->GD = Alloc_disk (sbull_minors);
if (! Dev->gd) {
PRINTK (kern_notice "Alloc_disk failure/n");
Goto Out_vfree;
}
Dev->gd->major = Sbull_major;
Dev->gd->first_minor = which*sbull_minors;
Dev->gd->fops = &sbull_ops;
Dev->gd->queue = dev->queue;
Dev->gd->private_data = Dev;
snprintf (Dev->gd->disk_name, +, "sbull%c", which + ' a ');
Set_capacity (DEV->GD, nsectors* (hardsect_size/kernel_sector_size));
4. Finally add_disk the entire initialization process, this step must be called at the end of initialization, because
After Add_disk, the operation function of the disk may be called, assuming that initialization is not complete and error occurs.
Two. Block equipment operation struct BLOCK_DEVICE_OPERATIONS structure analysis:
The structure in the Sbull module:
static struct Block_device_operations Sbull_ops = {
. Owner = This_module,
. open = Sbull_open,
. Release = Sbull_release,
. media_changed = sbull_media_changed,
. Revalidate_disk = Sbull_revalidate,
. IOCTL = Sbull_ioctl
};
Open and release these two functions are no longer detailed analysis, they have an important function is to add user count and
Reduce the user count. Media_changed and Revalidata_disk are support for removable media, such as U-disk
When these removable, plug-and-play devices should implement both functions. Media_changed is to check that the media is
No change, the prosperity is returned non-zero, revalidate_disk that the media is changed after the run. How do they contact me?
We do not care, we mainly implement the function of the entity can be.
The IOCTL function, the function of the IOCTL function is also simplified, but most of the actual disk devices are mainly implemented on disk
The search for interest.
Three. Request processing.
The core of the block device driver is the request processing part, which is the difficulty of the block device driver. The design is good whether directly off
To the performance of the device.
We look at the initialization of a request queue when the Block device entity is installed:
Dev->queue = Blk_init_queue (Sbull_full_request, &dev->lock);
This operation is to bind the generated request queue Dev->queue with the request function sbull_full_request in a
From The request function in Sbull:
static void Sbull_full_request (request_queue_t *q)
{
struct request *req;
int sectors_xferred;
struct Sbull_dev *dev = q->queuedata;
while ((req = elv_next_request (q)) = NULL) {
if (! blk_fs_request (req)) {
PRINTK (kern_notice "Skip non-fs request/n");
End_request (req, 0);
Continue
}
sectors_xferred = Sbull_xfer_request (dev, req);
if (! End_that_request_first (req, 1, sectors_xferred)) {
Blkdev_dequeue_request (req);
End_that_request_last (req);
}
}
}
req = elv_next_request (q) Gets the first outstanding request in the queue, two calls without
The same result is obtained when executing end_that_request_last or end_request, because it does not delete
The request in the queue. Only the end of the request will get the next request. Sbull_xfer_request is here
is the actual transfer data.
An actual block device request processing It's much more complicated to understand the request structure, the bio-knot
structure, queue structures, and so on. But here we don't go into the discussion.
Analysis of block device drivers in Linux