AD

块设备驱动注意事项

将ldd3(linxu device driver 3)中块设备驱动部分的代码在redhat6.3下编译时,会出现很多问题,主要是由于ldd3示例代码使用的内核版本较低(2.6.10版本),

对于块设备子系统,很多接口都已经发生了改变,

主要有如下变化:

1,elv_next_request已删除,使用如下方式从请求队列中获取请求

struct request *req;

req = blk_fetch_request (q);

while(req != 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;

}

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;

}

}

其中blk_peek_request从队列中获取请求,但是不会从队列中删除该请求。

从队列中删除请求需要调用blk_start_request(),blk_fetch_request 相当于调用blk_peek_request,和blk_start_request()

具体参考:**lwn**/Articles/333620/,

2,end_request,改成了blk_end_request_all

3,现在不能直接获取request结构的sector和current_nr_sector;而是通过函数blk_rq_pos和blk_rq_cur_sectors获得,所以将对用两处改为:

sbull_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), req->buffer, rq_data_dir(req));

4,bio_endio参数过多

5,blk_queue_hardsect_size定义已删除,使用blk_queue_logical_block_size(dev->queue, hardsect_size);

6,block_device_operation接口已经发生了非常大的变化

新接口如下:(该接口包含getgeo函数,从而不再需要在ioctl函数实现HDIO_GETGEO命令)

struct block_device_operations {

int (*open) (struct block_device *, fmode_t);

int (*release) (struct gendisk *, fmode_t);

int (*locked_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);

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 *);

int (*media_changed) (struct gendisk *);

unsigned long long (*set_capacity) (struct gendisk *,

unsigned long long);

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;

};

编译通过后就可以使用该设备,例如

创建分区:

fdisk /dev/sbulla

创建文件系统

mkfs.ext3 /dev/sbulla1

修改后的代码如下:

#include <linux/module.h>

#include <linux/moduleparam.h>

#include <linux/init.h>

#include <linux/sched.h>

#include <linux/kernel.h>

#include <linux/slab.h>

#include <linux/fs.h>

#include <linux/errno.h>

#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 = 1024;

module_param(nsectors,int,0);

static int ndevices = 4;

module_param(ndevices,int,0);

enum {

RM_SIMPLE = 0,

RM_FULL = 1,

RM_NOQUEUE = 2,

};

static int request_mode = RM_SIMPLE;

module_param(request_mode, int, 0);

#define SBULL_MINORS 16

#define KERNEL_SECTOR_SIZE 512

#define INVALIDATE_DELAY 30*HZ

struct sbull_dev{

int size;

u8 *data;

short users;

short media_change;

spinlock_t lock;

struct request_queue *queue;

struct gendisk *gd;

struct timer_list timer;

};

static struct sbull_dev *Devices = NULL;

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);

}

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(req->cmd_type != REQ_TYPE_FS){

printk(KERN_NOTICE "Skip non-fs request\n");

blk_end_request_all(req,-EIO);

continue;

}

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;

}

}

}

static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)

{

int i;

struct bio_vec *bvec;

sector_t sector = bio->bi_sector;

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;

}

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;

}

static void sbull_full_request(struct request_queue *q)

{

struct request *req;

struct sbull_dev *dev = q->queuedata;

req = blk_fetch_request(q);

while(req != 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)){

req = NULL;

}

}

}

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,status);

return 0;

}

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);

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;

}

int sbull_media_change(struct gendisk *gd)

{

struct sbull_dev *dev = gd->private_data;

return dev->media_change;

}

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;

}

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);

}

int sbull_ioctl(struct block_device *bd, fmode_t mode, unsigned 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;

size = dev->size *(hardsect_size / KERNEL_SECTOR_SIZE);

geo->cylinders = (size & ~0x3f) >> 6;

geo->heads = 4;

geo->sectors = 16;

geo->start = 4;

return 0;

}

static struct block_device_operations sbull_ops = {

.owner = THIS_MODULE,

.open = sbull_open,

.release = sbull_release,

.media_changed = sbull_media_change,

.revalidate_disk = sbull_revalidate,

.ioctl = sbull_ioctl,

.getgeo = sbull_getgeo

};

static void setup_device(struct sbull_dev *dev,int which)

{

printk(KERN_WARNING "sbull: step into into setup_device\n");

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);

init_timer(&dev->timer);

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

dev->timer.function = sbull_invalidate;

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;

case RM_SIMPLE:

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

if(dev->queue == NULL)

goto out_vfree;

break;

default:

printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);

}

blk_queue_logical_block_size(dev->queue,hardsect_size);

dev->queue->queuedata = dev;

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;

printk(KERN_WARNING "sbull: start init\n");

sbull_major = register_blkdev(sbull_major,"sbull");

if (sbull_major <= 0) {

printk(KERN_WARNING "sbull: unable to get major number\n");

return -EBUSY;

}

printk(KERN_WARNING "sbull: start kmalloc\n");

Devices = kmalloc(ndevices * sizeof(struct sbull_dev),GFP_KERNEL);

if(Devices == NULL)

goto out_unregister;

printk(KERN_WARNING "sbull: start setup_device\n");

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
时间: 2014-01-20

相关文章

  1. 译:块设备和 OpenStack

    libvirt 配置了 librbd 的 QEMU 接口,通过它可以在 OpenStack 中使用 Ceph 块设备镜像.Ceph 块设备镜像被当作集群对象,这意味着它比独立的服务器有更好的性能. 在 OpenStack ...
  2. Linux I2C核心.总线与设备驱动

    本章导读 I2C总线仅仅使用SCL.SDA两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和PCB板布线空间的占用.因此,I2C总线被非常广泛地应用在EEPROM.实时钟.小型LCD等设备与CPU的接口中. L ...
  3. linux中的块设备和字符设备

    系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的设备被称作块设备,这些数据片就称作块.最常见的块设备是硬盘,除此以外,还有软盘驱动器.CD-ROM驱动器和闪存等等许多其他块设备.注意,它们都是以安装文件 ...
  4. NBD-网络块设备[翻译]

    1997年4月,Pavel Machek 写了他的网络块设备代码,并被当时的Linux Kernel 2.1.55接受.Pavel 在随后的四个发行版(对应的内核版本为55.101.111.132)中维护并升级了他的代码 ...
  5. linux设备驱动第一篇:设备驱动程序简介

    首先,我们知道驱动是内核的一部分,那么驱动在内核中到底扮演了什么角色呢? 设备驱动程序在内核中的角色:他们是一个个独立的"黑盒子",使某个特定的硬件响应一个定义良好的内部编程接口,这些接口完全隐藏了设 ...
  6. linux设备驱动第三篇:写一个简单的字符设备驱动

    在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如 ...
  7. linux设备驱动第五篇:驱动中的并发与竟态

    综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被执行.而并发的执行单 ...
  8. 网络块设备存储系统 BlackHole Store

    BlackHole Store 网站 : http://vanheusden.com/java/BlackHole/ BlackHole 是一个无数据重复的网络块设备,支持镜像.快照以及多 LUNs 使用相同的存储. I ...
  9. 深入解析Windows 7的设备驱动管理

    是否能够对硬件提供良好的支持,是Windows 7面临的一个严峻考验,同时也是用户是否选择Windows 7的一个重要指标.所谓的硬件支持,说到底就是设备的驱动问题.Windows 7在硬件的驱动方面有哪些新的特性?如何 ...
  10. linux设备驱动之中断处理

    尽管有些设备仅通过它们的I/O寄存器就可以得到控制,但现实中的大部分设备却比这复杂一些.设备需要与外部世界打交道,如旋转的磁盘,绕卷的磁带,远距离连接的电缆等.这些设备的许多工作通常是在与处理器完全不同的时间周期内完成的 ...
  11. Windows下安装android设备驱动

    在windows下安装android设备驱动分为两种:一种是google标准的:一种是OEM的. 对于第一种,安装驱动很简单,通过SDK Manager下载Extras中的Google USB driver,然后通过设备 ...
  12. ceph中关于块设备的操作

    一.创建块设备 qemu-img create -f rbd rbd:资源池名/块设备名 文件大小 例如: qemu-img create -f rbd rbd:rbd/test 1G 说明:在资源池rbd下创建一个名为 ...
  13. 利用Trim/discard挂载rbd块设备

    在http://cephnotes.ksperis.com/blog/2014/12/18/use-discard-with-krbd-client-since-kernel-3-dot-18/有详细的描述,试验了一下, ...
  14. linux设备驱动第四篇:以oops信息定位代码行为例谈驱动调试方法

    上一篇我们大概聊了如何写一个简单的字符设备驱动,我们不是神,写代码肯定会出现问题,我们需要在编写代码的过程中不断调试.在普通的c应用程序中,我们经常使用printf来输出信息,或者使用gdb来调试程序,那么驱动程序如何调 ...
  15. linux设备驱动第二篇:构造和运行模块

    上一篇介绍了linux驱动的概念,以及linux下设备驱动的基本分类情况及其各个分类的依据和差异,这一篇我们来描述如何写一个类似hello world的简单测试驱动程序.而这个驱动的唯一功能就是输出hello world ...
  16. LDD3阅读笔记-字符设备驱动

    主要开发流程介绍 module_init宏和module_exit宏 当模块装载时需要调用module_init宏指定的函数,卸载时需要调用 module_exit宏指定的函数 以下是简单的init流程: 初始化设备 初 ...
  17. Linux设备驱动Hello World程序介绍

    by Valerie Henson 07/05/2007 (译者注:本文的例子是只能在linux的2.6内核下使用的,2.6以上的内核,译者没有做过实验,2.4是要修改make文件才能运行.) 原文出自:这里 译文来自: ...
  18. Linux设备驱动之阻塞与非阻塞IO

    阻塞是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作.被挂起的进程将进入休眠状态,被从调度器的运行队列移走,直到条件被满足.而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不 ...
  19. Linux设备驱动之API的实现

    Linux 下API的实现 作者: 韩大卫@ 吉林师范大学 驱动工程师工作内容之一就是向上层应用端提供API,这个API完成并封装了全部的与硬件芯片的I/O操作. 本问简单的说明了一个实现API函数的全部过程. 总体上看 ...