LDD3示例代码sbull编译问题

编译块设备驱动程序实例代码sbull遇到的问题

示例代码版本为 2.6.10,编译时的内核版本为2.6.39。内核中许多函数和结构发生变化,造成了编译问题,记录如下:


  • error: unknown type name ‘request_queue_t’
    原因:新版内核中已经没有request_queue_t的定义
    解决方法:可在代码所在文件添加如下定义:
    typedef struct request_queue request_queue_t;

  • error: implicit declaration of function ‘elv_next_request’
    原因:在新版本中已经将elv_next_requestblk_fetch_request替代。参考网址
    解决方法:在相应代码处替换即可。

  • error: implicit declaration of function ‘blk_fs_request’
    原因:在 新版本中已经没有blk_fs_request函数来判断命令类型。
    解决方法:可将判断语句改为:req->cmd_type != REQ_TYPE_FS

  • error: implicit declaration of function end_request
    原因:新版本内核中void end_request(struct request *req, int succeeded)已不存在。
    解决方法:可用void __blk_end_request_all(struct request *rq, int error);替代。

  • error: ‘struct request’ has no member named ‘sector’
    error: ‘struct request’ has no member named ‘current_nr_sectors’
    原因:新版本内核,struct request中删除了成员sectorcurrent_nr_sectors
    解决方法 :采用如下两个函数作相应代换:blk_rq_pos(req)blk_rq_cur_sectors(req)

  • error: implicit declaration of function ‘bio_cur_sectors’
    原因:新版本内核中 ‘bio_cur_sectors’已经删除。
    解决方法:与 2.610和2.6.39代码对照可知,可使用bio_cur_bytes(bio) / KERNEL_SECTOR_SIZE代替该函数。

  • error: implicit declaration of function ‘rq_for_each_bio’
    原因:新版本中改为了__rq_for_each_bio
    解决方法:修改之。

  • error: implicit declaration of function ‘end_that_request_first’
    error: implicit declaration of function ‘blkdev_dequeue_request’
    error: implicit declaration of function ‘end_that_request_last’
    原因:新版本中以上三个函数已经不存在。
    解决方法:参考网址代替将
sectors_xferred = sbull_xfer_request(dev, req);
if (! end_that_request_first(req, 1, sectors_xferred)) {
     
   blkdev_dequeue_request(req);
   end_that_request_last(req);
}

改为

sbull_xfer_request(dev, req);
if (!__blk_end_request_cur(req, 0)) {
     
	blk_start_request(req);
 	blk_fetch_request(q);
}

但是在我这边运行不了,以下为亲测可用代码,只是可以正常运行,不知其中原理 :

sbull_xfer_request(dev, req);
__blk_end_request_all(req, 0);

  • error: too many arguments to function ‘bio_endio’
    原因:新版本内核该函数参数改为两个,少了中间那个参数。
    解决方法:将中间那个参数去掉。

  • error: implicit declaration of function ‘blk_queue_hardsect_size’
    原因:与 2.610和2.6.39代码对照可知, 可由blk_queue_logical_block_size代替。
    解决方法:修改之。

  • 编译之后,有一个warning,"blk_put_queue" [/root/examples/sbull/sbull.ko] undefined!
    原因:新版本未定义该函数,可以使用kobject_put(&(dev->queue)->kobj);代替。
    解决方法:修改之 。

  • 修改上述错误后,便可编译成功,但是在安装模块时会提示killed。
  • 原因:观察编译时的输出:
    LDD3示例代码sbull编译问题_第1张图片
    warning: initialization from incompatible pointer type [enabled by default] 警告:从不兼容的指针类型初始化[默认启用],有好几个函数指针类型均不匹配。查看新的内核版本代码struct block_device_operations结构:
struct block_device_operations {
     
 int (*open) (struct block_device *, fmode_t);
 int (*release) (struct gendisk *, fmode_t);
 int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
 int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
 int (*direct_access) (struct block_device *, sector_t,
      void **, unsigned long *);
 unsigned int (*check_events) (struct gendisk *disk,
          unsigned int clearing);
 /* ->media_changed() is DEPRECATED, use ->check_events() instead */
 int (*media_changed) (struct gendisk *);
 void (*unlock_native_capacity) (struct gendisk *);
 int (*revalidate_disk) (struct gendisk *);
 int (*getgeo)(struct block_device *, struct hd_geometry *);
 /* this callback is with swap_lock and sometimes page table lock held */
 void (*swap_slot_free_notify) (struct block_device *, unsigned long);
 struct module *owner;
};

解决方法:将代码中相关函数改为对应定义的类型,并修改代码。 并且新版本结构中国包含getgeo函数,从而不再需要在ioctl函数实现HDIO_GETGEO命令。具体修改方法可见所附代码。


接下来即可进行测试。

  • ll /dev | grep sbull
  • 分区:fdisk /dev/sbull
  • 格式化,指定文件系统:mkfs.ext4 /dev/sbull
  • 挂载:mount /dev/sbull /mnt
  • 查看磁盘物理分区信息:cat /proc/partitions
  • 查看磁盘分区占用情况:df -ahT

参考网址:

  • Block layer request queue API changes
  • sbull在新内核编译
  • LDD3中sbull源码编译问题梳理
  • 块设备驱动注意事项
  • sbull驱动在vmware上测试

sbull.c:

/*
 * Sample disk driver, from the beginning.
 */

//#include 
#include 
#include 
#include 

#include 
#include 	/* printk() */
#include 		/* kmalloc() */
#include 		/* everything... */
#include 	/* error codes */
#include 
#include 	/* size_t */
#include 	/* O_ACCMODE */
#include 	/* HDIO_GETGEO */
#include 
#include 
#include 
#include 
#include 	/* invalidate_bdev */
#include 

typedef struct request_queue request_queue_t;

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 = 1024;	/* How big the drive is */
module_param(nsectors, int, 0);
static int ndevices = 4;
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_SIMPLE;
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	30*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;

	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(request_queue_t *q)
{
     
	struct request *req;

	while ((req = blk_fetch_request(q)) != NULL) {
     
		struct sbull_dev *dev = req->rq_disk->private_data;
		if (req->cmd_type != REQ_TYPE_FS) {
     
			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);
		sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req),
				req->buffer, rq_data_dir(req));
		__blk_end_request_all(req, 0);
		//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)/KERNEL_SECTOR_SIZE, buffer, bio_data_dir(bio) == WRITE);
		sector += bio_cur_bytes(bio)/KERNEL_SECTOR_SIZE;
		__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(request_queue_t *q)
{
     
	struct request *req;
	//int sectors_xferred;
	struct sbull_dev *dev = q->queuedata;

	while ((req = blk_fetch_request(q)) != NULL) {
     
		if (req->cmd_type != REQ_TYPE_FS) {
     
			printk (KERN_NOTICE "Skip non-fs request\n");
			__blk_end_request_all(req, -EIO);
			continue;
		}
		sbull_xfer_request(dev, req);
		//if (!__blk_end_request_cur(req, 0)) {
     
		//	blk_start_request(req);
		//	blk_fetch_request(q);
		//}
		__blk_end_request_all(req, 0);
		//if(!blk_end_request_cur(req, 0))
		//	req = NULL;
	}
}



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

	status = sbull_xfer_bio(dev, bio);
	bio_endio(bio, status);
	return 0;
}


/*
 * Open and close.
 */

static int sbull_open(struct block_device *bd, fmode_t mode)
{
     
	struct sbull_dev *dev = bd->bd_disk->private_data;

	del_timer_sync(&dev->timer);
	//filp->private_data = dev;
	spin_lock(&dev->lock);
	if (! dev->users)
		check_disk_change(bd);
	dev->users++;
	spin_unlock(&dev->lock);
	return 0;
}

static int sbull_release(struct gendisk *gd, fmode_t mode)
{
     
	struct sbull_dev *dev = gd->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 *bd, fmode_t mode,
                 unsigned int cmd, unsigned long arg)
{
     
	return 0;
}

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

        /*
	 * Get geometry: since we are a virtual device, we have to make
	 * up something plausible.  So we claim 16 sectors, four heads,
	 * and calculate the corresponding number of cylinders.  We set the
	 * start of data at sector four.
	 */
	size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
	geo->cylinders = (size & ~0x3f) >> 6;
	geo->heads = 4;
	geo->sectors = 16;
	geo->start = 4;
	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_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.
	 */
	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)
				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);

你可能感兴趣的:(Linux,块设备,设备驱动,LDD)