sbull虚拟的磁盘驱动的编写

原理指导:我们通过vmalloc在内存中开辟一部分空间,作为一个虚拟的磁盘,然后我们以块设备的方式来访问这片内存,例如这个sbull模型。

sbull(Simple Block Utility for Loading Localities),该驱动程序实现了一个使用系统内存的块设备,从本质上讲,属于一种 RAM 磁盘驱动程序。字符设备的IO操作则是直接不绕弯的,块设备的IO操作会配对和整合。驱动的任务是处理请求,对于这些请求的排队和整合的工作有IO调度算法处理。所以块设备的核心工作就是:请求处理函数或者是制造请求。Block_devices_operations结构体中没有读写一类的成员函数,而只是包含打开、释放和IO控制等函数。

块设备的流程:

(1)先把模块模型搭建好

MODULE_LICENSE("Dual BSD/GPL");

static struct block_device_operations sbull_ops = {

.owner           = THIS_MODULE,

.open           = sbull_open,

.release   = sbull_release,

.media_changed   = sbull_media_changed,

.revalidate_disk = sbull_revalidate,

.ioctl          = sbull_ioctl,

.getgeo = sbull_getgeo,

};

module_init(sbull_init);

module_exit(sbull_exit);

(2)定义一个我们用内存虚拟出来的块设备sbull_dev

struct sbull_dev {

        int size;                       /* Device size in sectors */

        u8 *data;                       /* The data array */

        short users;                    /* How many users */

        short media_change;             /* Flag a media change? */

        spinlock_t lock;                /* For mutual exclusion */

        struct request_queue *queue;    /* The device request queue */

        struct gendisk *gd;             /* The gendisk structure */

        struct timer_list timer;        /* For simulated media changes */

};

这个设备结构体是我们工作的核心,也许你不知道需要哪些成员,不要紧,还是那句话,编写驱动的时候,需要设备表现出那些性质和功能,相应的添加上就OK了。

(3)设备的初始化 

sbull_init(  ){

一、sbull_major = register_blkdev(sbull_major, "sbull");

二、Static struct sbull_dev *Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);//在这里可以对Kmalloc的结果判断下,然后进一步处理...

三、setup_device()跳到我们设备注册函数中去

}//初始化函数结束

(4)开始注册我们的这个设备

 setup_device(  ){  //安装(注册)一个设备前,一定要保证它被初始化了

这些成员都得出初始化好:

    一、    int size;                       /* Device size in sectors */

        u8 *data;                       /* The data array */

        short users;                    /* How many users */

        spinlock_t lock;                /* For mutual exclusion */

        struct request_queue *queue;    /* The device request queue */

        struct gendisk *gd;             /* The gendisk structure */

    二、add_disk(dev->gd);//这个重要的注册函数 }//函数结束

(5)扇区大小、data数组、user、锁lock的初始化:

一、dev->size = nsectors*hardsect_size;

dev->data = vmalloc(dev->size);

spin_lock_init(&dev->lock);

二、//dev->queue = blk_alloc_queue(GFP_KERNEL);           //RM_NOQUEUE

//dev->queue = blk_init_queue(sbull_full_request, &dev->lock);     //RM_FULL

dev->queue = blk_init_queue(sbull_request, &dev->lock);          //RM_SIMPLE

(6)告知内核硬件扇区尺寸、和timer模拟的初始化

blk_queue_logical_block_size(dev->queue, hardsect_size);

init_timer(&dev->timer);

dev->timer.data = (unsigned long) dev;

dev->timer.function = sbull_invalidate;gendisk初始化 

一、初始化gendisk:

dev->gd = alloc_disk(SBULL_MINORS);

二、 初始化gendisk的成员

dev->gd->major = sbull_major;

dev->gd->first_minor = which*SBULL_MINORS;

dev->gd->fops = &sbull_ops;

dev->gd->queue = dev->queue;

dev->gd->private_data = dev;

(7)设置gendisk容量为xxx_size个扇区大小 

set_capacity(dev->gd  , nsectors*(hardsect_size / KERNEL_SECTOR_SIZE));

(8)剩下的就是为devices_operation结构体里声明的那些接口进行实现:

.open  = sbull_open,

.release  = sbull_release,

.media_changed   = sbull_media_changed,

.revalidate_disk  = sbull_revalidate,

.ioctl = sbull_ioctl,

.getgeo = sbull_getgeo,

一、sbull_open(struct block_device *bdev,fmode_t mode )

二、sbull_release(struct gendisk *bd_disk, fmode_t mode)

三、sbull_media_changed(struct gendisk *gd)

四、sbull_revalidate(struct gendisk *gd)

五、sbull_invalidate(unsigned long ldev)

六、sbull_ioctl (struct block_device *bdev, fmode_t mode,

                   unsigned int cmd, unsigned long arg)

七、sbull_getgeo(struct block_device *bdev, struct hd_geometry *geo)

以下是代码:

/*
 * Sample disk driver for 2.6.35.
 */

//#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/sched.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>		/* everything... */
#include <linux/errno.h>	/* error codes */
#include <linux/timer.h>
#include <linux/types.h>	/* size_t */
#include <linux/fcntl.h>	/* O_ACCMODE */
#include <linux/hdreg.h>	/* HDIO_GETGEO */
#include <linux/kdev_t.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>	/* invalidate_bdev */
#include <linux/bio.h>

MODULE_LICENSE("Dual BSD/GPL");

static int sbull_major = 0;
module_param(sbull_major, int, 0);
static int hardsect_size = 512;
module_param(hardsect_size, int, 0);
static int nsectors = 25600;	/* How big the drive is */
module_param(nsectors, int, 0);
static int ndevices = 1;
module_param(ndevices, int, 0);

/*
 * The different "request modes" we can use.
 */
enum {
	RM_SIMPLE  = 0,	/* The extra-simple request function */
	RM_FULL    = 1,	/* The full-blown version */
	RM_NOQUEUE = 2,	/* Use make_request */
};
//static int request_mode = RM_FULL;
//static int request_mode = RM_SIMPLE;
static int request_mode = RM_NOQUEUE;
module_param(request_mode, int, 0);

/*
 * Minor number and partition management.
 */
#define SBULL_MINORS	16
#define MINOR_SHIFT	4
#define DEVNUM(kdevnum)	(MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT

/*
 * We can tweak our hardware sector size, but the kernel talks to us
 * in terms of small sectors, always.
 */
#define KERNEL_SECTOR_SIZE	512

/*
 * After this much idle time, the driver will simulate a media change.
 */
#define INVALIDATE_DELAY	60*HZ

/*
 * The internal representation of our device.
 */
struct sbull_dev {
        int size;                       /* Device size in sectors */
        u8 *data;                       /* The data array */
        short users;                    /* How many users */
        short media_change;             /* Flag a media change? */
        spinlock_t lock;                /* For mutual exclusion */
        struct request_queue *queue;    /* The device request queue */
        struct gendisk *gd;             /* The gendisk structure */
        struct timer_list timer;        /* For simulated media changes */
};

static struct sbull_dev *Devices = NULL;

/*
 * Handle an I/O request.
 */
static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
		unsigned long nsect, char *buffer, int write)
{
	unsigned long offset = sector*KERNEL_SECTOR_SIZE;
	unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
	//printk("<0>""in %s offset=%d  nbytes=%d write=%d\n",__FUNCTION__,offset,nbytes,write);
	//buffer[10]='\0';
	//printk(buffer);
	//printk("\n");
	if ((offset + nbytes) > dev->size) {
		printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
		return;
	}
	if (write)
		memcpy(dev->data + offset, buffer, nbytes);
	else
		memcpy(buffer, dev->data + offset, nbytes);
}

/*The simple form of the request function.*/

static void sbull_request(struct request_queue *q)
{
	struct request *req;

	req = blk_fetch_request(q);
	while (req  != NULL) {
		struct sbull_dev *dev = req->rq_disk->private_data;
		if (! blk_fs_request(req)) {
			printk (KERN_NOTICE "Skip non-fs request\n");
			__blk_end_request_all(req, -EIO);
			continue;
		}
    //    	printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
    //    			dev - Devices, rq_data_dir(req),
    //    			req->sector, req->current_nr_sectors,
    //    			req->flags);
	//	printk("sectors=%d\n",req->current_nr_sectors);
		sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req),
				req->buffer, rq_data_dir(req));
		if ( ! __blk_end_request_cur(req, 0) ) {
			req = NULL;
		}
	}
}


/*
 * Transfer a single BIO.
 */
static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
{
	int i;
	struct bio_vec *bvec;
	sector_t sector = bio->bi_sector;

	/* Do each segment independently. */
	bio_for_each_segment(bvec, bio, i) {
		char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
		sbull_transfer(dev, sector, bio_cur_bytes(bio)>>9 ,
				buffer, bio_data_dir(bio) == WRITE);
		sector += bio_cur_bytes(bio)>>9;
		__bio_kunmap_atomic(bio, KM_USER0);
	}
	return 0; /* Always "succeed" */
}

/*
 * Transfer a full request.
 */
static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
{
	struct bio *bio;
	int nsect = 0;
    
	__rq_for_each_bio(bio, req) {
		sbull_xfer_bio(dev, bio);
		nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
	}
	return nsect;
}



/*
 * Smarter request function that "handles clustering".*/
static void sbull_full_request(struct request_queue *q)
{

	struct request *req;
	int nsect;
	struct sbull_dev *dev ;
	req = blk_fetch_request(q);
	dev = req->rq_disk->private_data;
	while (req  != NULL) {
		if (! blk_fs_request(req)) {
			printk (KERN_NOTICE "Skip non-fs request\n");
			__blk_end_request_all(req, -EIO);
			continue;
		}
    //    	printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
    //    			dev - Devices, rq_data_dir(req),
    //    			req->sector, req->current_nr_sectors,
    //    			req->flags);
	//	printk("sectors=%d\n",req->current_nr_sectors);
		 nsect = sbull_xfer_request(dev, req);
       	 __blk_end_request(req, 0, (nsect<<9));
	//	req = blk_fetch_request(q);
		req = NULL;
	}

}



//The direct make request version
static int sbull_make_request(struct request_queue *q, struct bio *bio)
{
	struct sbull_dev *dev = q->queuedata;
	int status;

	status = sbull_xfer_bio(dev, bio);
	//bio_endio(bio, bio->bi_size, status);
	bio_endio(bio, status);
	return 0;
}


/*
 * Open and close.
 */

static int sbull_open(struct block_device *bdev,fmode_t mode )
{
	struct sbull_dev *dev = bdev->bd_disk->private_data;
	//printk("<0>" "fdfjdlksjfdlkj\n");	
	del_timer_sync(&dev->timer);
	spin_lock(&dev->lock);
	if (! dev->users) 
		check_disk_change(bdev);
	dev->users++;
	spin_unlock(&dev->lock);
	return 0;
}

static int sbull_release(struct gendisk *bd_disk, fmode_t mode)
{
	struct sbull_dev *dev = bd_disk->private_data;

	spin_lock(&dev->lock);
	dev->users--;

	if (!dev->users) {
		dev->timer.expires = jiffies + INVALIDATE_DELAY;
		add_timer(&dev->timer);
	}
	spin_unlock(&dev->lock);

	return 0;
}

/*
 * Look for a (simulated) media change.
 */
int sbull_media_changed(struct gendisk *gd)
{
	struct sbull_dev *dev = gd->private_data;
	
	return dev->media_change;
}

/*
 * Revalidate.  WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
 * with open.  That needs to be reevaluated.
 */
int sbull_revalidate(struct gendisk *gd)
{
	struct sbull_dev *dev = gd->private_data;
	
	if (dev->media_change) {
		dev->media_change = 0;
		memset (dev->data, 0, dev->size);
	}
	return 0;
}

/*
 * The "invalidate" function runs out of the device timer; it sets
 * a flag to simulate the removal of the media.
 */
void sbull_invalidate(unsigned long ldev)
{
	struct sbull_dev *dev = (struct sbull_dev *) ldev;

	spin_lock(&dev->lock);
	if (dev->users || !dev->data) 
		printk (KERN_WARNING "sbull: timer sanity check failed\n");
	else
		dev->media_change = 1;
	spin_unlock(&dev->lock);
}

/*
 * The ioctl() implementation
 */

int sbull_ioctl (struct block_device *bdev, fmode_t mode,
                 unsigned int cmd, unsigned long arg)
{
		return 0;
}

static int sbull_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
	unsigned long size;
	struct sbull_dev *pdev = bdev->bd_disk->private_data;

	size = pdev->size;
	geo->cylinders = (size & ~0x3f) >> 6;
	geo->heads	= 4;
	geo->sectors = 16;
	geo->start = 0;
	return 0;
}


/*
 * The device operations structure.
 */
static struct block_device_operations sbull_ops = {
	.owner           = THIS_MODULE,
	.open 	         = sbull_open,
	.release 	 = sbull_release,
	.media_changed   = sbull_media_changed,
	.revalidate_disk = sbull_revalidate,
	.ioctl	         = sbull_ioctl,
	.getgeo			= sbull_getgeo,
};


/*
 * Set up our internal device.
 */
static void setup_device(struct sbull_dev *dev, int which)
{
	/*
	 * Get some memory.
	 */
	memset (dev, 0, sizeof (struct sbull_dev));
	dev->size = nsectors*hardsect_size;
	dev->data = vmalloc(dev->size);
	if (dev->data == NULL) {
		printk (KERN_NOTICE "vmalloc failure.\n");
		return;
	}
	spin_lock_init(&dev->lock);
	
	/*
	 * The timer which "invalidates" the device.
	 */
	init_timer(&dev->timer);
	dev->timer.data = (unsigned long) dev;
	dev->timer.function = sbull_invalidate;
	
	/*
	 * The I/O queue, depending on whether we are using our own
	 * make_request function or not.
	 */
	switch (request_mode) {
	    case RM_NOQUEUE:
		dev->queue = blk_alloc_queue(GFP_KERNEL);
		if (dev->queue == NULL)
			goto out_vfree;
		blk_queue_make_request(dev->queue, sbull_make_request);
		break;

	    case RM_FULL:
		dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
		if (dev->queue == NULL)
			goto out_vfree;
		break;

	    default:
		printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
        	/* fall into.. */
	
	    case RM_SIMPLE:
		dev->queue = blk_init_queue(sbull_request, &dev->lock);
		if (dev->queue == NULL)
			goto out_vfree;
		break;
	}
//	blk_queue_hardsect_size(dev->queue, hardsect_size);
	blk_queue_logical_block_size(dev->queue, hardsect_size);
	dev->queue->queuedata = dev;
	/*
	 * And the gendisk structure.
	 */
	dev->gd = alloc_disk(SBULL_MINORS);
	if (! dev->gd) {
		printk (KERN_NOTICE "alloc_disk failure\n");
		goto out_vfree;
	}
	dev->gd->major = sbull_major;
	dev->gd->first_minor = which*SBULL_MINORS;
	dev->gd->fops = &sbull_ops;
	dev->gd->queue = dev->queue;
	dev->gd->private_data = dev;
	snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
	set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
	add_disk(dev->gd);
	return;

  out_vfree:
	if (dev->data)
		vfree(dev->data);
}



static int __init sbull_init(void)
{
	int i;
	/*
	 * Get registered.
	 */
	//	printk("<0>" "add by lht\n");
	sbull_major = register_blkdev(sbull_major, "sbull");
	if (sbull_major <= 0) {
		printk(KERN_WARNING "sbull: unable to get major number\n");
		return -EBUSY;
	}
	/*
	 * Allocate the device array, and initialize each one.
	 */
	Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
	if (Devices == NULL)
		goto out_unregister;
	for (i = 0; i < ndevices; i++) 
		setup_device(Devices + i, i);
    
	return 0;

  out_unregister:
	unregister_blkdev(sbull_major, "sbd");
	return -ENOMEM;
}

static void sbull_exit(void)
{
	int i;

	for (i = 0; i < ndevices; i++) {
		struct sbull_dev *dev = Devices + i;

		del_timer_sync(&dev->timer);
		if (dev->gd) {
			del_gendisk(dev->gd);
			put_disk(dev->gd);
		}
		if (dev->queue) {
			if (request_mode == RM_NOQUEUE)
			//	blk_put_queue(dev->queue);
			kobject_put(&(dev->queue)->kobj);
			else
				blk_cleanup_queue(dev->queue);
		}
		if (dev->data)
			vfree(dev->data);
	}
	unregister_blkdev(sbull_major, "sbull");
	kfree(Devices);
}
	
module_init(sbull_init);
module_exit(sbull_exit);



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值