转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行)
1.3塊設(shè)備驅(qū)動(dòng)關(guān)鍵數(shù)據(jù)結(jié)構(gòu)及函數(shù)API詳細(xì)剖析
經(jīng)過(guò)上節(jié)四個(gè)步驟我們已經(jīng)熟悉并實(shí)戰(zhàn)了一個(gè)最基本的過(guò)濾塊設(shè)備驅(qū)動(dòng)的設(shè)計(jì)技巧,我們這一節(jié)先不繼續(xù)實(shí)戰(zhàn),我們本節(jié)把上節(jié)170行代碼中接觸到的塊設(shè)備核心數(shù)據(jù)結(jié)構(gòu)和API接口剖析一下,把這部分掌握和理解一下。
?
我們把上節(jié)涉及的六個(gè)數(shù)據(jù)結(jié)構(gòu)和相關(guān)API接口羅列一下:
?塊設(shè)備核心數(shù)據(jù)結(jié)構(gòu)
| gendisk | 塊設(shè)備倉(cāng)庫(kù) |
| hd_struct | 塊設(shè)備分區(qū) |
| block_device | 文件系統(tǒng)層使用的塊設(shè)備描述符 |
| request_queue | 倉(cāng)庫(kù)的關(guān)卡(請(qǐng)求隊(duì)列) |
| request | 包含多個(gè)bio的大請(qǐng)求 |
| bio | 單個(gè)請(qǐng)求 |
塊設(shè)備核心API接口
| register_blkdev | 注冊(cè)并申請(qǐng)門牌號(hào) |
| alloc_disk | 申請(qǐng)倉(cāng)庫(kù) |
| blk_alloc_queue | 申請(qǐng)倉(cāng)庫(kù)的關(guān)卡 |
| blk_queue_make_request | 注冊(cè)倉(cāng)庫(kù)的加工處理函數(shù) |
| add_disk | 將申請(qǐng)的倉(cāng)庫(kù)注冊(cè)到內(nèi)核中,成為合法倉(cāng)庫(kù) |
?
?
?
結(jié)合上節(jié)塊設(shè)備在Linux中的總體結(jié)構(gòu)圖來(lái)看,我們?cè)儋N一下這個(gè)圖,根據(jù)這個(gè)圖我們將請(qǐng)求從文件系統(tǒng)層構(gòu)建出bio開始,直到進(jìn)入到請(qǐng)求處理函數(shù),分析一下其過(guò)程,這個(gè)過(guò)程會(huì)擴(kuò)展描述到核心數(shù)據(jù)結(jié)構(gòu)中的幾個(gè)關(guān)鍵字段,大家先試著熟悉,然后我們會(huì)給出核心數(shù)據(jù)結(jié)構(gòu)的嵌套關(guān)系圖,讓大家更清楚的認(rèn)識(shí)一下各個(gè)核心數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系,最后我們會(huì)詳細(xì)剖析各個(gè)數(shù)據(jù)結(jié)構(gòu)和API接口功能。
?
?
?
上層文件系統(tǒng)發(fā)來(lái)I/O請(qǐng)求時(shí),我們?cè)趬K設(shè)備驅(qū)動(dòng)的請(qǐng)求處理函數(shù)make_request上接收到的是bio,每個(gè)bio結(jié)構(gòu)中都包含了一個(gè)bio_vec數(shù)組。bio_vec是用于記錄一段連續(xù)內(nèi)存空間位置信息的數(shù)據(jù)結(jié)構(gòu),包括描述這段內(nèi)存連續(xù)空間的頁(yè)指針描述符bv_page,數(shù)據(jù)長(zhǎng)度bv_len,數(shù)據(jù)在一個(gè)頁(yè)中的開始位置bv_offset。如此分析我們知道bio請(qǐng)求包含了一個(gè)bio_vec數(shù)組,意味著包含了一組內(nèi)存連續(xù)空間。
?
接下來(lái)文件系統(tǒng)層調(diào)用通用塊層的generic_make_request函數(shù),將請(qǐng)求插入到倉(cāng)庫(kù)的關(guān)卡即請(qǐng)求隊(duì)列上request_queue,如果隊(duì)列沒有使用,則繼續(xù)調(diào)用到我們注冊(cè)的請(qǐng)求處理函數(shù)make_request上。
?
請(qǐng)求隊(duì)列request_queue中的每一個(gè)元素是一個(gè)請(qǐng)求集合request,request包含了多個(gè)bio請(qǐng)求,同時(shí)多個(gè)request通過(guò)鏈表鏈接在一起,鏈表頭在request_queue上;同樣request中的多個(gè)bio也通過(guò)鏈表鏈接在一起。
?
磁盤描述符gendisk通過(guò)指向該磁盤的請(qǐng)求隊(duì)列的指針queue與其請(qǐng)求隊(duì)列關(guān)聯(lián)起來(lái)。內(nèi)核用結(jié)構(gòu)block_device代表一個(gè)塊設(shè)備對(duì)象,它是文件系統(tǒng)層使用的數(shù)據(jù)結(jié)構(gòu),如:整個(gè)硬盤或特定分區(qū)都是一個(gè)塊設(shè)備對(duì)象。如果該結(jié)構(gòu)代表一個(gè)分區(qū),則其成員bd_part指向設(shè)備的分區(qū)結(jié)構(gòu);如果該結(jié)構(gòu)代表設(shè)備,則其成員bd_disk指向設(shè)備的通用硬盤結(jié)構(gòu)gendisk。
?
?????? 根據(jù)上面的描述,我們把數(shù)據(jù)結(jié)構(gòu)關(guān)系畫一下,如下,大家可以更清楚的看一下各個(gè)結(jié)構(gòu)之間的關(guān)系,也更加能夠從整體上把握IO請(qǐng)求在操作系統(tǒng)內(nèi)核中的描述和處理。
?
?
?
?
?
根據(jù)上面這個(gè)圖,讓我們可以繼續(xù)總結(jié)一下,充分把握好它們的結(jié)構(gòu)關(guān)系。塊設(shè)備會(huì)有一個(gè)倉(cāng)庫(kù)描述gendisk,如果倉(cāng)庫(kù)有分區(qū),則分區(qū)由hd_struct描述,同時(shí)文件系統(tǒng)會(huì)對(duì)倉(cāng)庫(kù)及分區(qū)都用一個(gè)獨(dú)立的block_device進(jìn)行描述;文件系統(tǒng)產(chǎn)生bio請(qǐng)求,多個(gè)bio會(huì)組裝成一個(gè)request,多個(gè)request會(huì)組裝到request_queue請(qǐng)求隊(duì)列上。
?
好了,至此相信大家能夠很牢固的記住各結(jié)構(gòu)之間的關(guān)系了,并且能夠根據(jù)上圖從整體上把握好應(yīng)用層數(shù)據(jù)讀寫請(qǐng)求在操作系統(tǒng)內(nèi)核中的處理關(guān)系,下面我們?cè)敿?xì)剖析一下各個(gè)數(shù)據(jù)結(jié)構(gòu)及API的功能,大家可以作為一個(gè)參考,再后面實(shí)戰(zhàn)時(shí)可以繼續(xù)回來(lái)進(jìn)行查閱學(xué)習(xí)。
?
?
?? block_device關(guān)鍵成員剖析
| 類型 | 字段 | 說(shuō)明 |
| dev_t | bd_dev | 塊設(shè)備的主設(shè)備號(hào)和次設(shè)備號(hào) |
| struct inode* | bd_inode | 指向bdev文件系統(tǒng)中塊設(shè)備對(duì)應(yīng)的文件索引節(jié)點(diǎn)的指針 |
| int | bd_openers | 計(jì)數(shù)器,統(tǒng)計(jì)塊設(shè)備已經(jīng)被打開了多少次 |
| struct mutex | bd_mutex | 打開或關(guān)閉的互斥量 |
| struct list_head | bd_inodes | 已打開的塊設(shè)備文件的索引節(jié)點(diǎn)鏈表的首部 |
| void* | bd_holders | 塊設(shè)備描述符的當(dāng)前所有者 |
| struct block_device* | bd_contains | 如果塊設(shè)備是一個(gè)分區(qū),則指向整個(gè)磁盤的塊設(shè)備描述符;否則,指向該塊設(shè)備描述符 |
| unsigned | bd_block_size | 塊大小 |
| struct hd_struct* | bd_part | 指向分區(qū)描述符的指針(如果該塊設(shè)備不是一個(gè)分區(qū),則為NULL) |
| unsigned | bd_part_count | 計(jì)數(shù)器,統(tǒng)計(jì)包含在塊設(shè)備中的分區(qū)已經(jīng)被打開了多少次 |
| struct gendisk* | bd_disk | 指向塊設(shè)備中基本磁盤的gendisk結(jié)構(gòu)的指針 |
| struct list_head | bd_list | 用于塊設(shè)備描述符鏈表的指針 |
| unsigned long | bd_private | 指向塊設(shè)備持有者的私有數(shù)據(jù)的指針 |
?
?? hd_struct關(guān)鍵成員剖析
| 類型 | 字段 | 說(shuō)明 |
| sector_t | start_sect | 磁盤中分區(qū)的起始扇區(qū) |
| sector_t | nr_sects | 分區(qū)的長(zhǎng)度(總共的扇區(qū)數(shù)) |
| int | policy | 如果分區(qū)是只讀的,則置為1;否則為0 |
| int | partno | 磁盤中分區(qū)的相對(duì)索引 |
?
?? gendisk關(guān)鍵成員剖析
| 類型 | 字段 | 說(shuō)明 |
| int | major | 磁盤主設(shè)備號(hào), 每個(gè)塊設(shè)備都有唯一的主設(shè)備號(hào),在這個(gè)塊設(shè)備上建立的分區(qū)都使用這個(gè)相同的主設(shè)備號(hào)。具有相同主設(shè)備號(hào)的設(shè)備,使用相同的驅(qū)動(dòng)程序。 |
| int | first_minor | 與磁盤關(guān)聯(lián)的第一個(gè)次設(shè)備號(hào)。在某一個(gè)設(shè)備上首先創(chuàng)建的設(shè)備的初始次設(shè)備號(hào)為0,在名稱中不顯示,如sda;在這個(gè)設(shè)備上依次建立的其他設(shè)備,此設(shè)備號(hào)在0基礎(chǔ)上依次加1,并在名稱中顯示,如sda1,sda2。 |
| int | minors | 與磁盤關(guān)聯(lián)的次設(shè)備號(hào)范圍。規(guī)定了可以在這個(gè)設(shè)備上創(chuàng)建多少個(gè)分設(shè)備(分區(qū))。當(dāng)次設(shè)備號(hào)數(shù)量是1時(shí),表示這個(gè)設(shè)備不能被分區(qū)。 |
| char | disk_name | 磁盤的標(biāo)準(zhǔn)命名(通常是相應(yīng)設(shè)備文件的規(guī)范名稱) |
| struct hd_struct | part0 | 磁盤的分區(qū)信息 |
| const struct block_device_operations * | fops | 指向塊設(shè)備操作函數(shù)集的指針 |
| struct request_queue * | queue | 指向磁盤請(qǐng)求隊(duì)列的指針 |
| void * | private_data | 塊設(shè)備驅(qū)動(dòng)程序的私有數(shù)據(jù) |
| int | flags | 描述磁盤類型的標(biāo)志 |
?? 塊設(shè)備gendisk fops函數(shù)指針集
| 類型 | 方法 | 參數(shù) | 觸發(fā)事件 |
| int | (*open) | struct block_device*, fmode_t | 打開塊設(shè)備文件,增加引用計(jì)數(shù) |
| int | (*release) | struct gendisk*, fmode_t | 關(guān)閉對(duì)塊設(shè)備文件的最后一個(gè)引用,減少引用計(jì)數(shù) |
| int | (*ioctl) | struct block_device*, fmode_t, unsigned,unsigned long | 在塊設(shè)備文件上發(fā)出ioctl()系統(tǒng)調(diào)用 |
?? request_queue請(qǐng)求隊(duì)列描述符中的關(guān)鍵字段
| 類型 | 字段 | 說(shuō)明 |
| struct list_head | queue_head | 待處理請(qǐng)求的鏈表 |
| make_request_fn* | make_request_fn | 設(shè)備驅(qū)動(dòng)程序的請(qǐng)求處理函數(shù) |
?? request描述符的關(guān)鍵字段
| 類型 | 字段 | 說(shuō)明 |
| struct list_head | queuelist | 請(qǐng)求隊(duì)列鏈表的指針 |
| struct bio* | bio | 請(qǐng)求中第一個(gè)沒有完成傳送操作的bio,不能直接對(duì)該成員進(jìn)行訪問(wèn);而要使用rq_for_each_bio訪問(wèn) |
| struct bio* | biotail | 請(qǐng)求鏈表中末尾的bio |
?? bio結(jié)構(gòu)中的關(guān)鍵字段
| 類型 | 字段 | 說(shuō)明 |
| sector_t | bi_sector | 塊I/O操作的第一個(gè)磁盤扇區(qū) |
| struct bio* | bi_next | 鏈接到請(qǐng)求隊(duì)列中的下一個(gè)bio |
| struct block_device * | bi_bdev | 指向塊設(shè)備描述符的指針 |
| unsigned long | bi_flags | bio的狀態(tài)標(biāo)志 |
| unsigned long | bi_rw | I/O操作標(biāo)志 |
| unsigned short | bi_vcnt | bio的bio_vec數(shù)組中段的數(shù)目 |
| unsigned short | bi_idx | bio的bio_vec數(shù)組中段的當(dāng)前索引值 |
| unsigned int | bi_phys_segments | 合并之后bio中物理段的數(shù)目 |
| unsigned int | bi_size | 需要傳送的字節(jié)數(shù) |
| unsigned int | bi_seg_front_size | 第一個(gè)可合并的段大小 |
| unsigned int | bi_seg_back_size | 最后一個(gè)可合并的段大小 |
| unsigned int | bi_max_vecs | bio的bio_vec數(shù)組中允許的最大段數(shù) |
| struct bio_vec* | bi_io_vec | 指向bio的bio_vec數(shù)組中的段的指針 |
| atomic_t | bi_cnt | bio的引用計(jì)數(shù) |
| bio_end_io_t* | bi_end_io | bio的I/O操作結(jié)束時(shí)調(diào)用的方法 |
| void* | bi_private | 通用塊層和塊設(shè)備驅(qū)動(dòng)程序的I/O完成方法使用的指針 |
?? bio_vec結(jié)構(gòu)中的字段
| 類型 | 字段 | 說(shuō)明 |
| struct page* | bv_page | 指向段的頁(yè)框中頁(yè)描述符的指針 |
| unsigned int | bv_len | 段的字節(jié)長(zhǎng)度 |
| unsigned int | bv_offset | 頁(yè)框中中段數(shù)據(jù)的偏移量 |
?? 核心API函數(shù)
| 類型 | 函數(shù)名 | 輸入?yún)?shù) | 返回值 | 說(shuō)明 | ? |
| int | register_blkdev | unsigned int major, const char* name | 成功返回主設(shè)備號(hào),失敗返回一個(gè)負(fù)數(shù)。 | 向系統(tǒng)申請(qǐng)注冊(cè)一個(gè)名為“name”的主設(shè)備號(hào),當(dāng)主設(shè)備號(hào)設(shè)為0時(shí)由內(nèi)核自動(dòng)分配一個(gè)可用的主設(shè)備號(hào);若自己指定時(shí),需要確保不與已有設(shè)備沖突。 | ? |
| struct gendisk* | alloc_disk | int minors | 成功返回一個(gè)指向gendisk描述符的指針,失敗返回NULL | 分配一個(gè)gendisk結(jié)構(gòu),minors為次設(shè)備號(hào)的總數(shù),一般也就是磁盤分區(qū)的數(shù)量,為1時(shí)表示該設(shè)備不能被分區(qū),此后minors不能被修改 | ? |
| struct request_queue* | blk_alloc_queue | gfp_t gfp_mask | 成功返回一個(gè)指向request_queue的指針,失敗時(shí)返回NULL | 申請(qǐng)請(qǐng)求隊(duì)列,并給隊(duì)列分配空間,該隊(duì)列需要用戶自己去調(diào)用blk_queue_make_request函數(shù)進(jìn)行初始化,其中的make_request_fn函數(shù)也需要用戶自己實(shí)現(xiàn) | ? |
| void | blk_queue_make_request | struct request_queue* q, make_request_fn* mfn | 無(wú)返回 | 初始化一個(gè)設(shè)備的請(qǐng)求隊(duì)列,其參數(shù)make_request_fn需要我們自己實(shí)現(xiàn) | ? |
| void | add_disk | struct gendisk *disk | 無(wú)返回 | 將gendisk添加到系統(tǒng)中。調(diào)用該函數(shù)后,會(huì)在“/dev/”下顯示出塊設(shè)備名字,設(shè)備名字即是disk->disk_name的內(nèi)容。對(duì)add_disk()的調(diào)用必須發(fā)生在驅(qū)動(dòng)程序的初始化工作完成并能響應(yīng)磁盤的請(qǐng)求之后。 | ? |
| void | del_gendisk | struct gendisk* disk | 無(wú)返回 | 將gendisk從系統(tǒng)中刪除。gendisk是一個(gè)引用計(jì)數(shù)結(jié)構(gòu),通常對(duì)del_gendisk的調(diào)用會(huì)刪除gendisk中的最終計(jì)數(shù),但是沒有機(jī)制能保證其肯定發(fā)生。因此當(dāng)調(diào)用此函數(shù)后,該結(jié)構(gòu)可能繼續(xù)存在(而且內(nèi)核可能會(huì)調(diào)用我們提供的各種方法)。 | |
| void | put_disk | struct gendisk* disk | 無(wú)返回 | 釋放驅(qū)動(dòng)分配的gendisk結(jié)構(gòu) | |
| void | unregister_blkdev | unsigned int major, const char *name | 無(wú)返回 | 注銷驅(qū)動(dòng)程序,釋放申請(qǐng)的主設(shè)備號(hào) | |
| void | blk_cleanup_queue | struct request_queue *q | 無(wú)返回 | 釋放分配的請(qǐng)求隊(duì)列 | |
| void | bio_endio | struct bio *bio, int error | 無(wú)返回 | 返回對(duì)bio請(qǐng)求的處理結(jié)果,第一個(gè)參數(shù)即為被處理的bio指針,第二個(gè)參數(shù)成功時(shí)為0,失敗時(shí)為-ERRNO。 | |
?
?
?
1.4繼續(xù)完善170行代碼至200行 - 加入bio過(guò)濾功能
?
剛才我們已經(jīng)看到了一個(gè)真時(shí)的虛擬塊設(shè)備驅(qū)動(dòng),我們看看這個(gè)塊設(shè)備加載到系統(tǒng)中,linux內(nèi)核的IO棧發(fā)生了怎樣的變化。
?
?
?
看到有什么問(wèn)題嗎?對(duì)了,請(qǐng)求到我們這塊就結(jié)束了,為啥,通道被堵上了,那怎么打通呢?這是我們接下來(lái)增加30行代碼至200行,來(lái)把通道打開,注意僅僅是修改了make_request這個(gè)函數(shù),并且我們?cè)黾恿诵碌臇|東,請(qǐng)大家跟這我們進(jìn)一步學(xué)習(xí)。
?
?
?
??????
?????? 區(qū)別在于下半部,請(qǐng)求達(dá)到fbd_dev1和fbd_dev2后會(huì)繼續(xù)被過(guò)濾到更底層的塊設(shè)備/dev/sdb和/dev/sdc上,接下來(lái)我們看看這一步是如何在新增的30行代碼中做到的。
?
仍然是fbd_driver.c fbd_driver.h Makefile三個(gè)文件,Makefile文件內(nèi)容不變,我們不再貼出,然后頭文件我們先貼一下。
?
? 1#ifndef? _FBD_DRIVER_H
? 2#define? _FBD_DRIVER_H
? 3#include <linux/init.h>
? 4#include <linux/module.h>
? 5#include <linux/blkdev.h>
? 6#include <linux/bio.h>
? 7#include <linux/genhd.h>
? 8
? 9#define SECTOR_BITS???????????? (9)
?10#define DEV_NAME_LEN??????????? 32
?11
?12#define DRIVER_NAME????????????"filter driver"
?13
?14#define DEVICE1_NAME???????????"fbd1_dev"
?15#define DEVICE1_MINOR?????????? 0
?16#define DEVICE2_NAME???????????"fbd2_dev"
?17#define DEVICE2_MINOR?????????? 1
?18
?19struct fbd_dev {
?20???????? struct request_queue *queue;
?21????????struct gendisk *disk;
?22????????sector_t size;????????? /* devicesize in Bytes */
?23????????char lower_dev_name[DEV_NAME_LEN];
?24????????struct block_device *lower_bdev;
?25};
?26#endif
?
頭文件基本沒有變化,包括仍然包含5個(gè)基本頭文件,主要是新增加了數(shù)據(jù)結(jié)構(gòu)fbd_dev的三個(gè)成員,從22行到24行,如下:
?22???????? sector_t size;????????? /* device size in Bytes */
?23????????char lower_dev_name[DEV_NAME_LEN];
?24????????struct block_device *lower_bdev;
?
?????? size記錄將來(lái)要?jiǎng)?chuàng)建的fbd_dev設(shè)備的容量大小,在第一節(jié)中我們是定義了一個(gè)常量宏即512M,現(xiàn)在我們既然要加入BIO過(guò)濾功能,這個(gè)容量需要保持與fbd_dev底層設(shè)備大小一致,這個(gè)容量的獲取我們?cè)诤竺鏁?huì)分析到。然后是lower_dev_name記錄了底層設(shè)備的文件名字,lower_bdev則保存著底層設(shè)備的block_device描述符,這個(gè)指針的用處后面我們也會(huì)講解。
?
?
?
我們把完善后的fbd_driver.c再貼一下,來(lái)分析一下如果設(shè)計(jì)make_request函數(shù),讓請(qǐng)求可以通過(guò)我們的過(guò)濾塊設(shè)備后被提交到真正的底層設(shè)備上去,而不是直接在我們這一層返回退出。
?
? 1 /**
? 2 ?*?fbd-driver - filter block device driver
? 3?*? Author: Talk@studio
? 4 **/
? 5 #include "fbd_driver.h"
? 6
? 7 static int fbd_driver_major = 0;
? 8
? 9 static struct fbd_dev fbd_dev1 =
?10 {
?11????????.queue = NULL,
?12????????.disk = NULL,
?13 ????????.lower_dev_name = "/dev/sdb",
?14????????.lower_bdev = NULL,
?15????????.size = 0
?16 };
?17
?18 static struct fbd_dev fbd_dev2 =
?19 {
?20????????.queue = NULL,
?21????????.disk = NULL,
?22????????.lower_dev_name = "/dev/sdc",
?23????????.lower_bdev = NULL,
?24????????.size = 0
?25 };
?26
?27 static int fbddev_open(struct inode *inode,struct file *file);
?28 static int fbddev_close(struct inode*inode, struct file *file);
?29
?30 static struct block_device_operationsdisk_fops = {
?31?? ??????.open = fbddev_open,
?32????????.release = fbddev_close,
?33????????.owner = THIS_MODULE,
?34 };
?35
?36 static int fbddev_open(struct inode *inode,struct file *file)
37 {
?38????????printk("device is opened by:[%s]\n", current->comm);
?39????? ???return 0;
?40 }
?41
?42 static int fbddev_close(struct inode*inode, struct file *file)
?43 {
?44????????printk("device is closed by:[%s]\n", current->comm);
?45????????return 0;
?46 }
?47
?48 static int make_request(structrequest_queue *q, struct bio *bio)
?49 {
?50????????struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;
?51????????printk("device [%s] recevied [%s] io request, "
?52???????????????? "access on dev sector[%llu], length is [%u] sectors.\n",
?53???????????????? dev->disk->disk_name,
?54???????????????? bio_data_dir(bio) == READ ?"read" : "write",
?55???????????????? bio->bi_sector,
?56???????????????? bio_sectors(bio));
?57
?58????????bio->bi_bdev = dev->lower_bdev;
?59????????submit_bio(bio_rw(bio), bio);
?60????????return 0;
?61 }
?62
?63 static int dev_create(struct fbd_dev *dev,char *dev_name, int major, int minor)
?64 {
?65????????int ret = 0;
?66
?67????????/* init fbd_dev */
?68????????dev->disk = alloc_disk(1);
?69????????if (!dev->disk) {
?70???????????????? printk("alloc diskerror");
?71???????????????? ret = -ENOMEM;
?72???????????????? goto err_out1;
73???????? }
?74
?75????????dev->queue = blk_alloc_queue(GFP_KERNEL);
?76????????if (!dev->queue) {
?77???????????????? printk("alloc queueerror");
?78?? ??????????????ret = -ENOMEM;
?79???????????????? goto err_out2;
?80????????}
?81
?82????????/* init queue */
?83????????blk_queue_make_request(dev->queue, make_request);
?84????????dev->queue->queuedata = dev;
?85
?86????????/* init gendisk */
?87? ???????strncpy(dev->disk->disk_name,dev_name, DEV_NAME_LEN);
?88????????dev->disk->major = major;
?89????????dev->disk->first_minor = minor;
?90????????dev->disk->fops = &disk_fops;
91
?92????????dev->lower_bdev = open_bdev_excl(dev->lower_dev_name, FMODE_WRITE| FMODE_READ, dev->lower_bdev);
?93????????if (IS_ERR(dev->lower_bdev)) {
?94???????????????? printk("Open thedevice[%s]'s lower dev [%s] failed!\n", dev_name, dev->lower_dev_name);
?95???????????????? ret = -ENOENT;
?96???????????????? goto err_out3;
?97????????}
?98
?99????????dev->size = get_capacity(dev->lower_bdev->bd_disk) <<SECTOR_BITS;
100
101???????? set_capacity(dev->disk,(dev->size >> SECTOR_BITS));
102
103???????? /* bind queue to disk */
104???????? dev->disk->queue = dev->queue;
105
106???????? /* add disk to kernel */
107???????? add_disk(dev->disk);
108???????? return 0;
109err_out3:
110???????? blk_cleanup_queue(dev->queue);
111err_out2:
112???????? put_disk(dev->disk);
113err_out1:
114???????? return ret;
115 }
116
117 staticvoid dev_delete(struct fbd_dev *dev, char *name)
118 {
119???????? printk("delete the device[%s]!\n", name);
120???????? close_bdev_excl(dev->lower_bdev);
121
122???????? blk_cleanup_queue(dev->queue);
123???????? del_gendisk(dev->disk);
124 ????????put_disk(dev->disk);
125 }
126
127 staticint __init fbd_driver_init(void)
128 {
129???????? int ret;
130
131???????? /* register fbd driver, get the drivermajor number*/
132???????? fbd_driver_major =register_blkdev(fbd_driver_major, DRIVER_NAME);
133???????? if (fbd_driver_major < 0) {
134???????????????? printk("get majorfail");
135???????????????? ret = -EIO;
136???????????????? goto err_out1;
137???????? }
138
139? ???????/* create the first device */
140???????? ret = dev_create(&fbd_dev1,DEVICE1_NAME, fbd_driver_major, DEVICE1_MINOR);
141???????? if (ret) {
142???????????????? printk("create device[%s] failed!\n", DEVICE1_NAME);
143???????????????? goto err_out2;
144???????? }
145
146???????? /* create the second device */
147???????? ret = dev_create(&fbd_dev2,DEVICE2_NAME, fbd_driver_major, DEVICE2_MINOR);
148???????? if (ret) {
149???????????????? printk("create device[%s] failed!\n", DEVICE2_NAME);
150???? ????????????goto err_out3;
151???????? }
152???????? return ret;
153err_out3:
154???????? dev_delete(&fbd_dev1,DEVICE1_NAME);
155err_out2:
156???????? unregister_blkdev(fbd_driver_major,DRIVER_NAME);
157err_out1:
158???????? return ret;
159 }
160
161 staticvoid __exit fbd_driver_exit(void)
162 {
163???????? /* delete the two devices */
164???????? dev_delete(&fbd_dev2,DEVICE2_NAME);
165???????? dev_delete(&fbd_dev1,DEVICE1_NAME);
166
167???????? /* unregister fbd driver */
168???????? unregister_blkdev(fbd_driver_major,DRIVER_NAME);
169???????? printk("block device driver exitsuccessfuly!\n");
170 }
171
172module_init(fbd_driver_init);
173module_exit(fbd_driver_exit);
174MODULE_LICENSE("GPL");
?
?
?????? 為了迅速看出fbd_driver.c相對(duì)于第一個(gè)版本的差異,大家可以比較一下,可以用我們?cè)谏蟽?cè)內(nèi)核編譯時(shí)介紹的diff 命令對(duì)比一下第一節(jié)中的fbd_driver.c和本節(jié)的fbd_driver.c有什么差異,我們把patch文件也貼一下,并繼續(xù)分析一下fbd_driver.c代碼。
?
Patch文件:
---fbd_driver_stage1/fbd_driver.c?????2013-02-25 22:45:23.000000000 -0800
+++fbd_driver_stage2/fbd_driver.c?????2013-02-26 19:45:05.000000000 -0800
@@ -6,8 +6,23 @@
?
?static int fbd_driver_major = 0;
?
-staticstruct fbd_dev fbd_dev1 = {NULL};
-staticstruct fbd_dev fbd_dev2 = {NULL};
+staticstruct fbd_dev fbd_dev1 =
+{
+?????? .queue = NULL,
+?????? .disk = NULL,
+?????? .lower_dev_name = "/dev/sdb",
+?????? .lower_bdev = NULL,
+?????? .size = 0
+};
+
+staticstruct fbd_dev fbd_dev2 =
+{
+?????? .queue = NULL,
+?????? .disk = NULL,
+?????? .lower_dev_name = "/dev/sdc",
+?????? .lower_bdev = NULL,
+?????? .size = 0
+};
?
?static int fbddev_open(struct inode *inode,struct file *file);
?static int fbddev_close(struct inode *inode,struct file *file);
@@ -40,7 +55,8 @@
??????????????? bio->bi_sector,
??????????????? bio_sectors(bio));
?
-?????? bio_endio(bio, bio->bi_size, 0);
+?????? bio->bi_bdev = dev->lower_bdev;
+?????? submit_bio(bio_rw(bio), bio);??
??????? return 0;
}
?
@@ -49,7 +65,6 @@
??????? int ret = 0;
?
??????? /* init fbd_dev */
-?????? dev->size = DEV_SIZE;
??????? dev->disk = alloc_disk(1);
??????? if (!dev->disk) {
??????????????? printk("alloc diskerror");
@@ -73,6 +88,16 @@
??????? dev->disk->major = major;
??????? dev->disk->first_minor = minor;
??????? dev->disk->fops = &disk_fops;
+??????
+?????? dev->lower_bdev =open_bdev_excl(dev->lower_dev_name, FMODE_WRITE | FMODE_READ, dev->lower_bdev);
+?????? if (IS_ERR(dev->lower_bdev)) {
+?????????????? printk("Open thedevice[%s]'s lower dev [%s] failed!\n", dev_name, dev->lower_dev_name);
+?????????????? ret = -ENOENT;?
+?????????????? goto err_out3;
+?????? }
+
+?????? dev->size = get_capacity(dev->lower_bdev->bd_disk)<< SECTOR_BITS;
+??????
??????? set_capacity(dev->disk,(dev->size >> SECTOR_BITS));
?
??????? /* bind queue to disk */
@@ -81,6+106,8 @@
??????? /* add disk to kernel */
??????? add_disk(dev->disk);
??????? return 0;
+err_out3:
+?????? blk_cleanup_queue(dev->queue);
?err_out2:
??????? put_disk(dev->disk);
?err_out1:
@@ -90,6 +117,8 @@
?static void dev_delete(struct fbd_dev *dev,char *name)
?{
??????? printk("delete the device[%s]!\n", name);
+?????? close_bdev_excl(dev->lower_bdev);
+
??????? blk_cleanup_queue(dev->queue);
??????? del_gendisk(dev->disk);
??????? put_disk(dev->disk);
?
首先看一下9-25行代碼,這里我們將fbd_dev1和fbd_dev2兩個(gè)設(shè)備的描述符初始化了一下,與第一版本不同,我們針對(duì)每個(gè)成員都進(jìn)行了賦值,如下:
?
? 9 static struct fbd_dev fbd_dev1 =
?10 {
?11????????.queue = NULL,
?12????????.disk = NULL,
?13????????.lower_dev_name = "/dev/sdb",
?14????????.lower_bdev = NULL,
?15????????.size = 0
?16 };
?17
?18 static struct fbd_dev fbd_dev2 =
?19 {
?20????????.queue = NULL,
?21????????.disk = NULL,
?22????????.lower_dev_name = "/dev/sdc",
?23????????.lower_bdev = NULL,
?24????????.size = 0
?25 };
?
以fbd_dev1為例,成員queue和disk指針依然賦值為NULL,表示還沒有為它們分配好數(shù)據(jù)結(jié)構(gòu),然后lower-_dev_name指定為”/dev/sdb”,表示fbd_dev1這個(gè)塊設(shè)備底層的塊設(shè)備是sdb設(shè)備,由此我們會(huì)想到,后續(xù)進(jìn)入fbd_dev1設(shè)備的請(qǐng)求,我們會(huì)將其過(guò)濾轉(zhuǎn)發(fā)到sdb上,在此我們通過(guò)簡(jiǎn)單靜態(tài)賦值的方式實(shí)現(xiàn),在后面我們的項(xiàng)目實(shí)戰(zhàn)訓(xùn)練中,我們會(huì)帶領(lǐng)大家實(shí)現(xiàn)如何完善我們的代碼做到靈活的動(dòng)態(tài)指定底層設(shè)備;lower_bdev指針也被賦值為NULL,后面我們就會(huì)講到如何獲取sdb設(shè)備的block_device描述符,最后size初始化為0。同樣fbd_dev2也進(jìn)行了一樣的初始化操作。通過(guò)初始化我們將fbd_dev1和fbd_dev2兩個(gè)設(shè)備各自綁定了其底層的設(shè)備,這部分也是為后續(xù)真正在IO路徑上實(shí)現(xiàn)請(qǐng)求過(guò)濾轉(zhuǎn)發(fā)做好了基本準(zhǔn)備,但是大家一定會(huì)清楚,現(xiàn)在的準(zhǔn)備工作其實(shí)還很簡(jiǎn)單,fbd_dev設(shè)備還沒有真正與sd#設(shè)備建立聯(lián)系,我們繼續(xù)往下分析。
?
?
直接看92-97行,這一段代碼完成了fbd_dev中l(wèi)ower_bdev和size成員的最終賦值操作,我們看到lower-_bdev指針通過(guò)調(diào)用open_bdev_excl函數(shù)獲得,該函數(shù)是內(nèi)核用于打開指定路徑的塊設(shè)備并返回block_device數(shù)據(jù)結(jié)構(gòu)指針的函數(shù),通過(guò)該函數(shù)我們獲取到了底層/dev/sd#設(shè)備的block_device地址,最終與sd#設(shè)備真正的建立了聯(lián)系,然后99行,通過(guò)調(diào)用get_capacity函數(shù)我們獲取到了底層設(shè)備的容量大小,然后在101行把該容量也設(shè)置進(jìn)fbd_dev設(shè)備的gendisk描述符中,至此過(guò)濾功能的準(zhǔn)備工作徹底完成。以上也是fbd_dev設(shè)備創(chuàng)建過(guò)程中新增加的處理邏輯。
?
?
然而設(shè)備創(chuàng)建好,還需在make_request請(qǐng)求處理函數(shù)上重新進(jìn)行了一下設(shè)計(jì),才能真正做到在IO路徑上對(duì)請(qǐng)求進(jìn)行過(guò)濾轉(zhuǎn)發(fā),我們看48-61行的make_request函數(shù)發(fā)生了哪些改變,如下,我們?cè)儋N一下。
?
?48 static int make_request(structrequest_queue *q, struct bio *bio)
?49 {
?50????????struct fbd_dev *dev = (struct fbd_dev *)q->queuedata;
?51????????printk("device [%s] recevied [%s] io request, "
?52???????????????? "access on dev sector[%llu], length is [%u] sectors.\n",
?53???????????????? dev->disk->disk_name,
?54???? ????????????bio_data_dir(bio) == READ ?"read" : "write",
?55???????????????? bio->bi_sector,
?56???????????????? bio_sectors(bio));
?57
?58????????bio->bi_bdev = dev->lower_bdev;
?59????????submit_bio(bio_rw(bio), bio);
?60????????return 0;
?61 }
?
?
關(guān)鍵的地方是58-59行,不像第一節(jié)的代碼,我們這里的請(qǐng)求處理函數(shù)完成了一個(gè)重要的功能,它把傳入?yún)?shù)bio重新修飾了,58這一行中我們把bio->bi_bdev賦值為底層設(shè)備的指針lower_bdev,而lower_bdev就是我們剛才介紹的在設(shè)備初始化中通過(guò)調(diào)用open_bdev_excl函數(shù)獲得的,也就是說(shuō)我們告訴請(qǐng)求的下一站地址去哪,聯(lián)系我們舉的圖書館的例子,請(qǐng)求就這樣被一層一層傳遞下去了,最后59行,我們調(diào)用請(qǐng)求提交函數(shù)把我們修飾完的請(qǐng)求繼續(xù)提交給底層的塊設(shè)備驅(qū)動(dòng)了,這個(gè)函數(shù)是submit_bio,不同于第一個(gè)版本中調(diào)用bio_endio直接掐掉請(qǐng)求終止結(jié)束,這里通過(guò)submit_bio繼續(xù)把請(qǐng)求轉(zhuǎn)發(fā)下去了。
?
?????? 至此我們的過(guò)濾塊設(shè)備驅(qū)動(dòng)真正做到了轉(zhuǎn)發(fā)請(qǐng)求,我們的驅(qū)動(dòng)程序終于具備了一個(gè)正常的塊設(shè)備的基本功能了,我們把bio在內(nèi)核棧中的流動(dòng)過(guò)程再畫一張圖描述一下。
?
?
?????? 我們趕緊試試吧,make完成后,我們加載fbd_driver.ko模塊看看,然后在/dev/下找到我們的設(shè)備文件fbd_disk,然后dd一下該設(shè)備是否可以正常進(jìn)行讀寫操作了,至此一個(gè)基本的塊設(shè)備驅(qū)動(dòng)我們已經(jīng)完成,接下來(lái)我們繼續(xù)介紹請(qǐng)求轉(zhuǎn)發(fā)過(guò)濾后到請(qǐng)求真正處理完成后的回調(diào)處理過(guò)程,進(jìn)一步完整的走完請(qǐng)求處理全過(guò)程。
總結(jié)
以上是生活随笔為你收集整理的转载:谢谢原作者: 块设备驱动实战基础篇二 (继续完善170行过滤驱动代码至200行)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 转载:谢谢原作者:块设备驱动实战基础篇一
- 下一篇: 转载:谢谢原作者:块设备驱动实战基础篇三