Block device driver analysis, based on sbull Before getting started, let's first understand the core data structure of 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 gendisk are important members in this structure, It is also an important structure of Block devices. Most of the descriptions in this article are based on these two structures. I. Process: The module driver also starts from the init function, so the analysis also starts from here. Step 1: Register_blkdev (sbull_major, "sbull "); Register a block device first. The first parameter is the device number, which is dynamically allocated to Table 0, and the second parameter is the device name. Step 2: Devices = kmalloc (ndevices * sizeof (struct sbull_dev), gfp_kernel ); Create the core data structure of the block device, that is, the object of the block device. . Step 3: Setup_device (devices + I, I ); To put it bluntly, it is to initialize the object and add it to the block layer of the system. This step is very important. To perform the following operations: 1. initialize a spin lock. Spin_lock_init (& Dev-> lock ); 2. Allocate 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. Allocate, 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, 32, "sbull % C", which + 'A '); Set_capacity (Dev-> GD, nsectors * (hardsect_size/kernel_sector_size )); 4. At last, add_disk completes the initialization process. This step must be called at the end of the initialization, because After add_disk, the disk operation function may be called. If the initialization is not completed, an error will occur. Ii. Structure Analysis of block device operation struct block_device_operations: This 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 }; The open and release functions are not analyzed in detail. They have an important function: Increase the user count and Reduce user count. Media_changed and revalidata_disk are the support for removable media, such as USB flash disks. And so on, the plug-and-play device should implement these two functions. Media_changed is used to check whether the media is If no value is changed, the log is returned as non-zero, and the revalidate_disk is executed after the media changes. How to contact me Don't worry, we can mainly implement the entity of this function. The ioctl function and ioctl function are also simplified. However, most of the actual disk devices mainly implement disk Information Information Query. 3. Request Processing. The core of the block device driver is the request processing part, which is the difficulty of the block device driver. Well designed, direct access Depends on the performance of the device. We initialize a request queue when installing the block device entity: Dev-> queue = blk_init_queue (sbull_full_request, & Dev-> lock ); This operation binds the generated Request queue Dev-> queue to the Request function sbull_full_request in . 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 unfinished request in the queue. When running end_that_request_last or end_request, the same result is obtained because it will not be deleted. Requests in the queue. The next request is received only after the request is completed. Sbull_xfer_request here is Is the actual data transmission. The actual block device request processing is much more complicated, so we need to have a better understanding of the Request structure. Structure, queue structure, and so on. However, we will not discuss it further here. |