运行平台: Ubuntu 14.04
jiwey@jiwey-Ideapad-Z460:~$ uname -a
Linux jiwey-Ideapad-Z460 3.13.0-36-generic #63-Ubuntu SMP Wed Sep 3 21:30:45 UTC 2014 i686 i686 i686 GNU/Linux
(补充于2014.10.18 02:08 : 第一份代码是错的,主要是 __blk_end_request_all的用法不对;第二份代码做了修正。
关于 __blk_end_request 函数的简单解析参见:__blk_end_request源码分析)
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/blkdev.h> #include <linux/spinlock_types.h> #include <linux/hdreg.h> #define MEMBLKA_SIZE (8*1024*1024) // 内存块设备的大小 8M #define MEMBLKA_MAJOR 0 // memblk 内存块设备的主设备号 #define MEMBLKA_NAME "memblkA" static int major = MEMBLKA_MAJOR ; static spinlock_t queue_lock ; static uint8_t memblkA_buf [ MEMBLKA_SIZE ] ; static struct request_queue *queue = NULL ; static struct gendisk* memblkA_disk = NULL ; static int getgeo ( struct block_device *dev, struct hd_geometry *geo ) { // 由于只是用内存模拟块设备,所以磁盘信息可以随意一点,我们这里返回固定数值 geo->heads = 1 ; geo->sectors = 32 ; geo->cylinders = (MEMBLKA_SIZE>>9) / ( geo->heads * geo->sectors ) ; return 0 ; } static struct block_device_operations fops = { .owner = THIS_MODULE , .getgeo = getgeo }; static void do_request ( struct request_queue *q ) { struct request *req ; // " while((req=elv_next_request(q))!=NULL) " is replaced with the line below : while ( (req = blk_peek_request(q) ) != NULL ) { // 调用 elv_next_request 得到 req 之后,req 并不会自动从 // 队列中移除,所以在调用end_request之前不能重复调用elv_next_request, // 否则会返回与上一次相同的结果,导致同一个请求被重复执行。 // 在调用了 end_request 之后,req才离开队列,可参考end_request // 的实现代码,其中有一句 blkdev_dequeue_reuest // "if(!blk_fs_request(req))" is replaced with the line below : if ( req->cmd_type != REQ_TYPE_FS ) { printk (KERN_NOTICE MEMBLKA_NAME ": Skip non-fs request\n"); // "end_request(req, 0)" is replaced with the code block below : { blk_start_request(req); __blk_end_request_all(req, -EIO); } continue; } int data_offset ; int data_size ; // data_offset = req->sector << 9 ; // data_size = req->current_nr_sectors << 9 ; // these two lines are replaced with the two lines below : data_offset = blk_rq_pos(req) << 9 ; // 参考 drivers/block/z2ram.c data_size = blk_rq_cur_bytes(req) ; if ( data_offset + data_size > MEMBLKA_SIZE ) { printk ( KERN_ERR MEMBLKA_NAME ": bad request: block=%llu, count=%u\n", (unsigned long long)(data_offset>>9), data_size>>9 ) ; // "end_request(req, 0)" is replaced with the code block below : { blk_start_request(req); __blk_end_request_all(req, -EIO); } continue ; } switch ( rq_data_dir(req) ) { case READ : /* code with old API memcpy ( req->buffer , memblkA_buf+data_offset , data_size ) ; end_request(req,1); break ; */ // code with new API blk_start_request(req); memcpy ( req->buffer , memblkA_buf+data_offset , data_size ) ; __blk_end_request_all(req, 0); break ; case WRITE : /* code with old API memcpy ( memblkA_buf+data_offset , req->buffer , data_size ) ; end_request(req,1); break ; */ // code with new API blk_start_request(req); memcpy ( memblkA_buf+data_offset , req->buffer , data_size ) ; __blk_end_request_all(req, 0); break ; default : break ; } } } /*设备驱动模块加载函数*/ int memblkA_init ( void ) { int ret ; // register the device (major) ret = register_blkdev ( major , MEMBLKA_NAME ) ; if ( ret < 0 ) { // device regitered failed . major = 0 ; printk ( KERN_WARNING MEMBLKA_NAME ": failed to register the device memblkA\n" ) ; return -EBUSY ; } if ( major == 0 ) { // if we didn't select a valid major beforehand ( i.e. we set major to 0 ) , // we should use the return value of register_blkdev as the final major . major = ret ; } printk ( KERN_WARNING MEMBLKA_NAME ": successfully register the device memblkA\n" ) ; // 请求队列的初始化 spin_lock_init ( &queue_lock ) ; queue = blk_init_queue ( do_request , &queue_lock ) ; if ( queue == NULL ) { printk ( KERN_WARNING MEMBLKA_NAME ": failed to alloc request_queue\n" ) ; return -ENOMEM ; } printk ( KERN_WARNING MEMBLKA_NAME ": successfully alloc request_queue\n" ) ; // memblkA_disk = alloc_disk (64) ; if ( memblkA_disk == NULL ) { printk ( KERN_WARNING MEMBLKA_NAME ": failed to alloc gendisk\n" ) ; return -ENOMEM; } printk ( KERN_WARNING MEMBLKA_NAME ": successfully alloc gendisk\n" ) ; strcpy(memblkA_disk->disk_name, MEMBLKA_NAME); memblkA_disk->major = major ; memblkA_disk->first_minor = 0 ; memblkA_disk->fops = &fops; memblkA_disk->queue = queue; set_capacity(memblkA_disk, MEMBLKA_SIZE>>9); printk ( KERN_INFO MEMBLKA_NAME ": prepare to add disk\n" ) ; printk ( KERN_INFO MEMBLKA_NAME ": queue_max_hw_sectors is %d \n", queue_max_hw_sectors(queue) ) ; add_disk(memblkA_disk); printk ( KERN_INFO MEMBLKA_NAME ": memblkA module has been installed\n" ) ; return 0 ; } /*模块卸载函数*/ void memblkA_exit(void) { if ( memblkA_disk ) { // gendisk 是一个引用计数结构,"通常"对del_gendisk的调用会删除gendisk中的最终计数, // 但并没有机制能"保证"这一点,因此当调用del_gendisk后该结构可能继续存在。 del_gendisk(memblkA_disk); put_disk(memblkA_disk); } if ( queue ) { // 把请求队列返回给系统。调用该函数后,驱动程序将不会再得到这个队列中的请求 blk_cleanup_queue(queue) ; } if ( major ) { unregister_blkdev(major,MEMBLKA_NAME); } printk(KERN_INFO MEMBLKA_NAME ": memblkA module has been removed\n"); } MODULE_AUTHOR("NewThinker_Jiwey"); MODULE_LICENSE("Dual BSD/GPL"); module_param(major, int, S_IRUGO); module_init(memblkA_init); module_exit(memblkA_exit);
(补充于2014.10.18 02:08 : 新代码如下)
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/blkdev.h> #include <linux/spinlock_types.h> #include <linux/hdreg.h> #define MEMBLK_NAME "memblkA" #define MEMBLK_SIZE (8*1024*1024) // 内存块设备的大小 8M #define MEMBLK_MAJOR 0 // memblk 内存块设备的主设备号 static int major = MEMBLK_MAJOR ; static spinlock_t queue_lock ; static uint8_t memblk_buf [ MEMBLK_SIZE ] ; static struct request_queue *queue = NULL ; static struct gendisk* memblk_disk = NULL ; static int getgeo ( struct block_device *dev, struct hd_geometry *geo ) { // 由于只是用内存模拟块设备,所以磁盘信息可以随意一点,我们这里返回固定数值 geo->heads = 1 ; geo->sectors = 32 ; geo->cylinders = (MEMBLK_SIZE>>9) / ( geo->heads * geo->sectors ) ; return 0 ; } static struct block_device_operations fops = { .owner = THIS_MODULE , .getgeo = getgeo }; static void print_request(struct request*req,int num) { int new_bio_flag ; int bio_index ; int bio_vec_index ; struct req_iterator iter ; struct bio_vec* bvec ; new_bio_flag = 1 ; bio_index = -1 ; printk (KERN_INFO MEMBLK_NAME ": ****************** request No.%d ***************** \n", num); printk (KERN_INFO MEMBLK_NAME ": request%d->__sector (blk_rq_pos): %d\n", num ,blk_rq_pos(req)); printk (KERN_INFO MEMBLK_NAME ": request%d->__data_len (blk_rq_bytes): %d\n", num ,blk_rq_bytes(req)); printk (KERN_INFO MEMBLK_NAME ": request%d->blk_rq_cur_bytes: %d\n", num ,blk_rq_cur_bytes(req)); printk (KERN_INFO MEMBLK_NAME ": request%d->bio: \n",num ); rq_for_each_segment ( bvec , req , iter ) { struct bio* bio ; bio = iter.bio ; if ( new_bio_flag ) { new_bio_flag = 0 ; bio_vec_index = -1 ; bio_index ++ ; printk (KERN_INFO MEMBLK_NAME ": ********** bio No.%d ******** \n", bio_index); printk (KERN_INFO MEMBLK_NAME ": bio%d->bi_sector: %d \n", bio_index,bio->bi_sector); printk (KERN_INFO MEMBLK_NAME ": bio%d->bi_size: %d \n", bio_index,bio->bi_size); printk (KERN_INFO MEMBLK_NAME ": bio%d->bi_io_vec: \n", bio_index); } bio_vec_index ++ ; printk (KERN_INFO MEMBLK_NAME ": \t bio_vec%d->bv_len : %d\n", bio_vec_index , bvec->bv_len); if ( iter.i == bio->bi_vcnt-1 ) { printk (KERN_INFO MEMBLK_NAME ": -------- bio No.%d end ------- \n", bio_index); new_bio_flag = 1 ; } } printk (KERN_INFO MEMBLK_NAME ": ----------------- request No.%d end --------------- \n", num); } static void do_request ( struct request_queue *q ) { struct request *req ; static int queue_loops = 0 ; static int request_loops = 0 ; static int local_request_loops = 0 ; local_request_loops = 0 ; queue_loops ++ ; printk (KERN_INFO MEMBLK_NAME ": queue num : %d ...... \n", queue_loops); // " while((req=elv_next_request(q))!=NULL) " is replaced with the line below : while ( (req = blk_peek_request(q) ) != NULL ) { int data_offset ; int data_size ; request_loops ++ ; local_request_loops ++ ; printk (KERN_INFO MEMBLK_NAME ": local request num: %d , global request num: %d \n", local_request_loops, request_loops); //if ( blk_rq_bytes(req) != blk_rq_cur_bytes(req) ) print_request(req,request_loops);//只打印有多个bio或bio_vec的请求 blk_start_request(req); // 调用 elv_next_request 得到 req 之后,req 并不会自动从 // 队列中移除,所以在调用end_request之前不能重复调用elv_next_request, // 否则会返回与上一次相同的结果,导致同一个请求被重复执行。 // 在调用了 end_request 之后,req才离开队列,可参考end_request // 的实现代码,其中有一句 blkdev_dequeue_reuest // "if(!blk_fs_request(req))" is replaced with the line below : if ( req->cmd_type != REQ_TYPE_FS ) { printk (KERN_NOTICE MEMBLK_NAME ": Skip non-fs request\n"); // "end_request(req, 0)" is replaced with the code block below : { __blk_end_request_all(req, -EIO); } continue; } do_request_loop: // data_offset = req->sector << 9 ; // data_size = req->current_nr_sectors << 9 ; // these two lines are replaced with the two lines below : data_offset = blk_rq_pos(req) << 9 ; // 参考 drivers/block/z2ram.c data_size = blk_rq_cur_bytes(req) ; if ( data_offset + data_size > MEMBLK_SIZE ) { printk ( KERN_ERR MEMBLK_NAME ": bad request: block=%llu, count=%u\n", (unsigned long long)(data_offset>>9), data_size>>9 ) ; // "end_request(req, 0)" is replaced with the code block below : { __blk_end_request_all(req, -EIO); } continue ; } switch ( rq_data_dir(req) ) { case READ : /* code with old API memcpy ( req->buffer , memblk_buf+data_offset , data_size ) ; end_request(req,1); break ; */ // code with new API memcpy ( req->buffer , memblk_buf+data_offset , data_size ) ; if ( __blk_end_request(req, 0,data_size) ) { printk ( KERN_ERR MEMBLK_NAME ": <<<<<< has met a multi-bio request >>>>>\n" ) ; goto do_request_loop ; } printk ( KERN_ERR MEMBLK_NAME ": a request has been handled \n" ) ; break ; case WRITE : /* code with old API memcpy ( memblk_buf+data_offset , req->buffer , data_size ) ; end_request(req,1); break ; */ // code with new API memcpy ( memblk_buf+data_offset , req->buffer , data_size ) ; if ( __blk_end_request(req, 0, data_size) ) { printk ( KERN_ERR MEMBLK_NAME ": <<<<<< has met a multi-bio request >>>>>\n" ) ; goto do_request_loop ; } printk ( KERN_ERR MEMBLK_NAME ": a request has been handled \n" ) ; break ; default : break ; } } } /*设备驱动模块加载函数*/ int memblk_init ( void ) { int ret ; // register the device (major) ret = register_blkdev ( major , MEMBLK_NAME ) ; if ( ret < 0 ) { // device regitered failed . major = 0 ; printk ( KERN_WARNING MEMBLK_NAME ": failed to register the device %s\n", MEMBLK_NAME ) ; return -EBUSY ; } if ( major == 0 ) { // if we didn't select a valid major beforehand ( i.e. we set major to 0 ) , // we should use the return value of register_blkdev as the final major . major = ret ; } printk ( KERN_WARNING MEMBLK_NAME ": successfully register the device %s\n" , MEMBLK_NAME) ; // 请求队列的初始化 spin_lock_init ( &queue_lock ) ; queue = blk_init_queue ( do_request , &queue_lock ) ; if ( queue == NULL ) { printk ( KERN_WARNING MEMBLK_NAME ": failed to alloc request_queue\n" ) ; return -ENOMEM ; } printk ( KERN_WARNING MEMBLK_NAME ": successfully alloc request_queue\n" ) ; // memblk_disk = alloc_disk (64) ; if ( memblk_disk == NULL ) { printk ( KERN_WARNING MEMBLK_NAME ": failed to alloc gendisk\n" ) ; return -ENOMEM; } printk ( KERN_WARNING MEMBLK_NAME ": successfully alloc gendisk\n" ) ; strcpy(memblk_disk->disk_name, MEMBLK_NAME); memblk_disk->major = major ; memblk_disk->first_minor = 0 ; memblk_disk->fops = &fops; memblk_disk->queue = queue; set_capacity(memblk_disk, MEMBLK_SIZE>>9); printk ( KERN_INFO MEMBLK_NAME ": prepare to add disk\n" ) ; printk ( KERN_INFO MEMBLK_NAME ": queue_max_hw_sectors is %d \n", queue_max_hw_sectors(queue) ) ; add_disk(memblk_disk); printk ( KERN_INFO MEMBLK_NAME ": %s module has been installed\n",MEMBLK_NAME ) ; return 0 ; } /*模块卸载函数*/ void memblk_exit(void) { if ( memblk_disk ) { // gendisk 是一个引用计数结构,"通常"对del_gendisk的调用会删除gendisk中的最终计数, // 但并没有机制能"保证"这一点,因此当调用del_gendisk后该结构可能继续存在。 del_gendisk(memblk_disk); put_disk(memblk_disk); } if ( queue ) { // 把请求队列返回给系统。调用该函数后,驱动程序将不会再得到这个队列中的请求 blk_cleanup_queue(queue) ; } if ( major ) { unregister_blkdev(major,MEMBLK_NAME); } printk(KERN_INFO MEMBLK_NAME ": %s module has been removed\n",MEMBLK_NAME); } MODULE_AUTHOR("NewThinker_Jiwey"); MODULE_LICENSE("Dual BSD/GPL"); module_param(major, int, S_IRUGO); module_init(memblk_init); module_exit(memblk_exit);