- Brief introduction
- A block device driver accesses a device primarily by transmitting a fixed-size random data
- Linux kernel video block devices are basic device types that differ from character devices
- The Linux block device driver interface allows the block device to perform its maximum effect, but its complex program is a problem that programmers must face
- A data block refers to fixed-size data, and the value of the size is determined by the kernel
- The size of the data block is typically 4,096 bytes, but can be changed based on the architecture and the file system being used
- The block corresponding to the chunk is a block of the size determined by the underlying hardware, and the size of the device sector processed by the kernel is 512 bytes
- If you want to use a different hardware sector size, the user must modify the number of sectors in the kernel accordingly
- Registered
- Registering block device drivers
- <linux/fs.h>
- int Register_blkdev (unsigned int major, const char *name);
- Assign a dynamic main device number if needed
- Create a entry in/proc/devices
- int Unregister_blkdev (unsigned int major, const char *name);
- Registering disks
- struct block_device_operations
- int (*open) (struct inode *inode, struct file *filp);
- Int (*release) (struct inode *inode, struct file *filp);
- Int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
- Int (*media_changed) (struct gendisk *gd);
- Int (*revalidate_disk) (struct gendisk *gd);
- struct module *owner;
- Gendisk structure
- <linux/genhd.h>
- struct Gendisk
- int major;
- int First_minor;
- int minors;
- Char Disk_name[32]
- Shown in/proc/partitions and SYSFS
- struct Block_device_operations *fops;
- struct Request_queue *queue;
- int flags;
- sector_t capacity;
- void *private_data;
- struct Gendisk *alloc_disk (int minors);
- void Del_gendisk (struct gendisk *gd);
- void Add_disk (struct gendisk *gd);
- Block device operation
- Open and Release functions
- For drivers that operate the actual hardware device, the open and release functions can set the status of the driver and the hardware. These actions include starting or stopping the disk, locking the silo door of removable media, and allocating the DMA cache, etc.
- There are some actions that allow the block device to be opened directly within the user space, including partitioning the disk, or creating a file system on the partition, or running a file system checker
- Support for removable media
- Call the Media_changed function to check if the media is changed
- The Revalideate function is called after the media has changed
- IOCTL function
- A high-level block device subsystem has intercepted a large number of commands before the driver obtains the IOCTL command
- In fact, in a modern driver, many IOCTL commands do not have to be implemented at all.
- Request Processing
- The core of each block device driver is its request function
- Any information that the driver needs to know about the request is contained in the structure passed to us through the request queue
- Introduction to the Request function
- void request (request_queue_t *queue);
- This function is called when the kernel requires the driver to handle read, write, and other operations on the device
- Each device has a request queue
- Dev->queue = Blk_init_queue (Test_request, &dev->lock);
- The call to the request function is completely asynchronous with the action in the user space process
- A simple request function
- struct Request * Elv_next_request (request_queue_t queue);
- void End_request (struct request *req, int succeeded);
- struct request
- sector_t SECOTR;
- unsigned long nr_sectors;
- Char *buffer
- Rq_data_dir (struct request *req);
- Request queue
- A block device request queue can be described as: sequence containing block device I/O requests
- Request queue Tracks I/O requests for incomplete block devices
- The request queue also implements the plug-in interface
- The I/O Scheduler is also responsible for merging neighboring requests
- Request queue has request_queue or request_queue_t struct type
- <linux/blkdev.h>
- Creation and deletion of queues
- request_queue_t *blk_init_queue (Request_fn_proc *request, spinlock_t *lock);
- void Blk_cleanup_queue (request_queue_t *queue);
- queue function
- struct request *elv_next_request (request_queue_t *queue);
- void blkdev_dequeue_request (struct request *req);
- void Elv_requeue_request (request_queue_t *queue, struct request *req);
- Queue control functions
- void Blk_stop_queue (request_queue_t *queue);
- void Blk_start_queue (request_queue_t *queue);
- void Blk_queue_bounce_limit (request_queue_t *queue, U64 dma_addr);
- void Blk_queue_max_sectors (request_queue_t *queue, unsigned short max);
- void Blk_queue_max_phys_segments (request_queue_t *queue, unsigned short max);
- void Blk_queue_max_hw_segments (request_queue_t *queue, unsigned short max);
- void Blk_queue_max_segment_size (request_queue_t *queue, unsigned short max);
- void Blk_queue_segment_boundary (request_queue_t *queue, unsigned long mask);
- void Blk_queue_dma_alignment (request_queue_t *queue, int mask);
- void Blk_queue_hardsect_size (request_queue_t *queue, unsigned short max);
- Profiling the request process
- Essentially, a request structure is implemented as a linked list of a bio structure.
- Bio structure
- The bio structure contains all the information about the driver execution request, not the process associated with initializing the requested user space
- <linux/bio.h>
- struct bio
- sector_t Bi_sector;
- unsigned int bi_size;
- The size of the data that needs to be transferred in bytes
- unsigned long bi_flags;
- unsigned short bio_phys_segments;
- unsigned short bio_hw_segments;
- struct Bio_vec *bi_io_vec
- struct BIO_VEC
- struct page *vb_page;
- unsigned int bv_len;
- unsigned int bv_offset;
- Example
- int Segno;
- struct Bio_vec *bvec;
- Bio_for_each_segment (Bvec, bio, Segno)
- {
- /* Use this section for certain operations */
- }
- Char *__bio_kmap_atomic (struct bio *bio, int i, enum km_type type);
- void __bio_kunmap_atomic (char *buffer, enum Km_type type):
- struct page *bio_page (struct bio *bio);
- int Bio_offset (struct bio *bio);
- int bio_cur_sectors (struct bio *bio);
- Char *bio_data (struct bio *bio);
- Char *bio_kmap_irq (struct bio *bio, unsigned long *flags);
- void Bio_kunmap_irq (char *buffer, unsigned long *flags);
- Request struct Member
- struct request
- sector_t Hard_sector;
- unsigned long hard_nr_sectors;
- unsigned int hard_cur_sectors;
- struct bio *bio;
- Char *buffer;
- unsigned short nr_phys_segments;
- struct List_head queuelist;
- Barrier Request
- The block device layer has re-assembled the request to improve I/O performance before the driver receives the request
- The driver can also regroup the request for the same purpose
- However, there is a problem with unlimited re-grouping requests: Some operations for some applications to be completed before other operations begin
- 2.6 version of block device layer use barrier (barrier) request to solve this problem
- If a request is set with the Req_hardbarrer flag, it must be written to the drive before other subsequent requests are initialized
- void Blk_queue_ordered (request_queue_t *queue, int flag);
- int Blk_barrier_rq (sruct request *req);
- If a non-0 value is returned, the request is a barrier request
- Do not retry requests
- int blk_noretry_request (struct request *req);
- Request Completion function
- int End_that_request_first (struct request *req, int success, int count);
- void End_that_request_last (struct request *req);
- Example
- void End_request (struct request *req, int uptodate)
- {
- if (!end_that_request (req, uptodate, req->hard_cur_sectors)
- {
- Add_disk_randomness (Req->rq_disk);
- Blkdev_dequeue_request (req);
- End_that_request_last (req);
- }
- }
- Using bio
- Example
- struct Request *req
- struct bio *bio;
- Rq_for_each_bio (bio, req)
- {
- /* Use the bio structure for a certain operation */
- }
- Block device requests and DMA
- int Blk_rq_map_sg (request_queue_t *queue, struct request *req, struct scatterlist *list);
- Clear_bit (Queue_flag_clear, &queue->queue_flags);
- Do not use the request queue
- typedef int (MAKE_REQUEST_FN) (request_queue_t *q, struct bio *bio);
- void Bio_endio (struct bio *bio, unsigned int bytes, int error);
- request_queue_t *blk_alloc_queue (int flags);
- Did not really create a queue to save the request
- void Blk_queue_make_request (request_queue_t *queue, Make_request_fn *func);
- Drivers/block/ll_rw_block.c
- Some other details
- Command preprocessing
- typedef int (PREP_RQ_FN) (request_queue_t *queue, struct request *req);
- The function can return one of the following values
- Blkprep_ok
- Blkprep_kill
- Blkprep_defer
- void Blk_queue_prep_rq (request_queue_t *queue, Prep_rq_fn *func);
- Flag Command Queue
- Hardware that has multiple active requests at the same time typically supports some form of tagged command queue (Tagged command queueing, TCQ)
- TCQ just adds an integer (token) technique for each request, so that when the drive finishes one of their requests, it can tell the driver what it is doing
- int Blk_queue_int_tags (request_queue_t *queue, int depth, struct blk_queue_tag *tags);
- int Blk_queue_resize_tags (request_queue_t *queue, int new_depth);
- int Blk_queue_start_tag (request_queue_t *queue, struct request *req);
- void Blk_queue_end_tag (request_queue_t *queue, struct request *req);
- struct Request *blk_queue_find_tag (request_queue_t *queue, int tag);
- void Blk_queue_invalidate_tags (request_queue_t *queue);
"Linux Device Drivers" chapter 16th block device driver--note