memblkA 最简单的内存模拟块设备 源码


运行平台: 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);


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值