版权声明:本文为博主原创文章,欢迎转载(文中有强调禁止转载的例外),但请说明出处。如用于培训等商业用途请先与博主联系并支付部分授权费 https://blog.csdn.net/NewThinker_wei/article/details/39911715
运行平台: 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);