Driver for Block devices in Linux (1)

Source: Internet
Author: User

The driver of a block device is more difficult than that of a character device, because the connection between the driver and the kernel of the block device is further increased, however, the basic structure and characters of block device access are similar.

Remember one sentence: For storage devices (hard disks ~~ (With mechanical operations), adjusting the read/write order has a huge effect, because the sequential read/write sector is faster than the separated sector.

But at the same time: SD card and USB flash disk are not mechanically limited, so it is unnecessary to adjust the continuous sector as mentioned above.

 

Let's talk about simple drivers for hard disks.

In the Linux kernel, The gendisk structure is used to represent an independent disk device or partition. This structure contains the disk's primary device number, secondary device number, and device name.

During the process of inserting data into the country, the filling of the gendisk struct is completed in the simp_blkdev_init function. Before filling the gendisk structure, allocate space for it. The Code is as follows:

simp_blkdev_disk = alloc_disk(1);        if (!simp_blkdev_disk) {                ret = -ENOMEM;                goto err_alloc_disk;        }

The alloc_disk function is implemented in the kernel. Parameter 1 after it represents the number of sub-device numbers used, which cannot be modified.

After the space for gendisk is allocated, the members in gendisk are filled. The Code is as follows:

Strcpy (simp_blkdev_disk-> disk_name, category); // macro definition simp_blkdev simp_blkdev_disk-> major = snapshot; // master device number simp_blkdev_disk-> first_minor = 0; // times the device number simp_blkdev_disk-> fops = & bytes; // main structure simp_blkdev_disk-> queue = bytes; set_capacity (simp_blkdev_disk, simp_blkdev_bytes> 9 ); // macro definition (16*1024*1024) is actually the struct.

After the gendisk structure is filled, register the disk device with the kernel. The Code is as follows:

add_disk(simp_blkdev_disk);

In LDD, the device that you want to register in the kernel must be filled in with the gendisk struct. This is also the case when we used character devices, I don't know why LDD emphasizes this here.

If you do not need a disk, you need to release the gendisk. The code for releasing the disk is implemented in the simp_blkdev_exit function. The specific release code is as follows:

del_gendisk(simp_blkdev_disk);

Put_disk (simp_blkdev_disk) also exists in simp_blkdev_exit, which is used to operate the reference count of gendisk. Simp_blkdev_exit also implements the blk_cleanup_queue function to clear the Request queue. Finally speaking of the Request queue.

 

Before waiting for a queue, you must clarify several concepts:

① What a user wants to do with hard disk data is called a request. This request is the same as the IO request, so the IO request comes from the upper layer.
② Each Io request corresponds to a bio structure in the kernel.

③ The IO scheduling algorithm can combine continuous bio (that is, users' requests to adjacent clusters of Hard Disk Data) into a request.
④ Multiple requests are a request queue, which is used by the driver to respond to user requirements.

Simp_blkdev_queue of the Request queue in the program embedded in the country

Next, let's talk about hard drives with mechanical storage devices.

In this type of driver, the user's IO requests correspond to the clusters on the hard disk may be continuous, and may be discontinuous. Continuous is of course good. If it is not continuous, then the IO scheduler will sort these bio items (for example, the elevator scheduling algorithm mentioned by Xie), merge them into a request, then receive the request, and then merge it into a request, after multiple requests, our request queue is formed, and then we can submit the request to the driver.

In a storage device such as a hard disk, the request queue initialization code is as follows:

simp_blkdev_queue = blk_init_queue(simp_blkdev_do_request, NULL);

In this case, the make_requst function in the kernel is called first, and then the customized simp_blkdev_do_request is called. After checking the kernel code, you will find the make_requst kernel code as follows:

static int make_request(struct request_queue *q, struct bio * bio)

 

The specific function of make_request is to use the IO scheduler to optimize and adjust the access sequence of Multiple bio databases and merge them into one request. That is, after the function is executed, the request queue of the kernel is officially executed.

The merged request is actually a structure used to characterize IO requests. This structure is defined in the kernel.

A request is a structure, and a request queue is also a structure. The structure of this request queue in the kernel is defined as follows:

struct request_queue{/* * Together with queue_head for cacheline sharing */struct list_headqueue_head;struct request*last_merge;struct elevator_queue*elevator;/* * the queue request freelist, one for reads and one for writes */struct request_listrq;request_fn_proc*request_fn;make_request_fn*make_request_fn;prep_rq_fn*prep_rq_fn;unplug_fn*unplug_fn;merge_bvec_fn*merge_bvec_fn;prepare_flush_fn*prepare_flush_fn;softirq_done_fn*softirq_done_fn;rq_timed_out_fn*rq_timed_out_fn;dma_drain_needed_fn*dma_drain_needed;lld_busy_fn*lld_busy_fn;/* * Dispatch queue sorting */sector_tend_sector;struct request*boundary_rq;/* * Auto-unplugging state */struct timer_listunplug_timer;intunplug_thresh;/* After this many requests */unsigned longunplug_delay;/* After this many jiffies */struct work_structunplug_work;struct backing_dev_infobacking_dev_info;/* * The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it. */void*queuedata;/* * queue needs bounce pages for pages above this limit */gfp_tbounce_gfp;/* * various queue flags, see QUEUE_* below */unsigned longqueue_flags;/* * protects queue structures from reentrancy. ->__queue_lock should * _never_ be used directly, it is queue private. always use * ->queue_lock. */spinlock_t__queue_lock;spinlock_t*queue_lock;/* * queue kobject */struct kobject kobj;/* * queue settings */unsigned longnr_requests;/* Max # of requests */unsigned intnr_congestion_on;unsigned intnr_congestion_off;unsigned intnr_batching;void*dma_drain_buffer;unsigned intdma_drain_size;unsigned intdma_pad_mask;unsigned intdma_alignment;struct blk_queue_tag*queue_tags;struct list_headtag_busy_list;unsigned intnr_sorted;unsigned intin_flight[2];unsigned intrq_timeout;struct timer_listtimeout;struct list_headtimeout_list;struct queue_limitslimits;/* * sg stuff */unsigned intsg_timeout;unsigned intsg_reserved_size;intnode;#ifdef CONFIG_BLK_DEV_IO_TRACEstruct blk_trace*blk_trace;#endif/* * reserved for flush operations */unsigned intordered, next_ordered, ordseq;intorderr, ordcolor;struct requestpre_flush_rq, bar_rq, post_flush_rq;struct request*orig_bar_rq;struct mutexsysfs_lock;#if defined(CONFIG_BLK_DEV_BSG)struct bsg_class_device bsg_dev;#endif};

LDD said that the request queue implements an insert interface, which allows multiple I/O schedulers and most I/O schedulers to accumulate I/O requests in batches, and submit them to the driver in ascending or descending order.

Multiple continuous bio will be merged into one request, and multiple requests will become a request queue. In this way, Bio is a direct and basic request. The structure of Bio is defined as follows:

struct bio { sector_t            bi_sector;       struct bio          *bi_next;    /* request queue link */       struct block_device *bi_bdev;/* target device */       unsigned long       bi_flags;    /* status, command, etc */ unsigned long       bi_rw;       /* low bits: r/w, high: priority */       unsigned intbi_vcnt;     /* how may bio_vec's */       unsigned intbi_idx;/* current index into bio_vec array */       unsigned intbi_size;     /* total size in bytes */       unsigned short bi_phys_segments; /* segments after physaddr coalesce*/ unsigned shortbi_hw_segments; /* segments after DMA remapping */ unsigned intbi_max;     /* max bio_vecs we can hold used as index into pool */ struct bio_vec   *bi_io_vec;  /* the actual vec list */       bio_end_io_t*bi_end_io;  /* bi_end_io (bio) */       atomic_tbi_cnt;     /* pin count: free when it hits zero */ void             *bi_private;       bio_destructor_t *bi_destructor; /* bi_destructor (bio) */ };

Note that the most important part in the bio structure is the bio. VEC structure. At the same time, there are many macro operations on bio, which are implemented by the kernel.

Implementation of Request queue:

First use while (req = elv_next_request (q ))! = NULL) checks the loop to see what the I/O requests are.

Then, determine the Read and Write areas:

If (req-> sector + req-> current_nr_sectors) <9> simp_blkdev_bytes) {printk (kern_err simp_blkdev_diskname ": Bad request: block = % LlU, count = % u \ n ", (unsigned long) req-> sector, req-> current_nr_sectors); // end the request. End_request (req, 0); continue ;}

Many Linux programming habits are involved in determining the Read and Write areas.

Sector indicates the first sector to be accessed.

Current_nr_sectors indicates the expected number of access sectors.

Here, the nine digits left are multiplied by 512.

In this way, (req-> sector + req-> current_nr_sectors) <9 calculates the expected size of the sector to be accessed. A judgment was made.

If the above judgment does not exceed the range, you can perform operations on this part of the requested block device.

simp_blkdev_disk = alloc_disk(1);        if (!simp_blkdev_disk) {                ret = -ENOMEM;                goto err_alloc_disk;        }        strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);        simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;        simp_blkdev_disk->first_minor = 0;        simp_blkdev_disk->fops = &simp_blkdev_fops;        simp_blkdev_disk->queue = simp_blkdev_queue;        set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);        add_disk(simp_blkdev_disk);        return 0;

The memory allocation and member filling of the gendisk structure are the same as those of Hard Disk Block devices.

Because the SD card and USB flash drive are a type of non-mechanical equipment, we do not need such a complicated scheduling algorithm, that is, we do not need to sort IO requests, therefore, we need to allocate a request queue for ourselves. The Code is as follows:

 

simp_blkdev_queue = blk_alloc_queue(GFP_KERNEL);

Note that in the drive of a hardware block device, the prototype of this function is blk_init_queue (simp_blkdev_do_request, null );

In this case, kernel make_request is not actually called (the function of this function is described above). That is to say, the simp_blkdev_make_request function to be bound is the same level as the make_request function. There is no issue concerning algorithm scheduling.

Then, you need to bind the manufacturing request function and request queue. The Code is as follows:

blk_queue_make_request(simp_blkdev_queue, simp_blkdev_make_request);

After adding the structure members of gendisk, You can execute add_disk (simp_blkdev_disk). This function adds this partition to the kernel.

Lists the functions of the manufacturing request.

// This condition is used to determine the current kernel version. # If linux_version_code <kernel_version (2, 6, 24) bio_endio (Bio, 0,-EIO); # else bio_endio (Bio,-EIO); # endif return 0 ;} dsk_mem = simp_blkdev_data + (bio-> bi_sector <9); // traverse bio_for_each_segment (bvec, bio, I) {void * iovec_mem; Switch (bio_rw (bio )) {Case read: Case reada: iovec_mem = kmap (bvec-> bv_page) + bvec-> bv_offset; memcpy (iovec_mem, dsk_mem, bvec-> bv_len ); kunmap (bvec-> bv_page); break; Case write: iovec_mem = kmap (bvec-> bv_page) + bvec-> bv_offset; memcpy (dsk_mem, iovec_mem, bvec-> bv_len ); kunmap (bvec-> bv_page); break; default: printk (kern_err simp_blkdev_diskname ": unknown value of bio_rw: % lu \ n", bio_rw (bio )); # If linux_version_code <kernel_version (2, 6, 24) bio_endio (Bio, 0,-EIO); # else bio_endio (Bio,-EIO); # endif return 0 ;} dsk_mem + = bvec-> bv_len;} # If linux_version_code <kernel_version (2, 6, 24) bio_endio (Bio, bio-> bi_size, 0); # else bio_endio (Bio, 0); # endif return 0 ;}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.