块设备驱动

注册块IO设备

为了注册块IO设备,register_blkdev()被使用。取消注册时使用unregister_blkdev()方法。自从4.9版本的内核开始,对register_blkdev()的调用是可选的。

下面是一个典型的场景。

#include <linux/fs.h>

#define MY_BLOCK_MAJOR           240
#define MY_BLKDEV_NAME          "mybdev"

static int my_block_init(void)
{
    int status;

    status = register_blkdev(MY_BLOCK_MAJOR, MY_BLKDEV_NAME);
    if (status < 0) {
             printk(KERN_ERR "unable to register mybdev block device\n");
             return -EBUSY;
     }
     //...
}

static void my_block_exit(void)
{
     //...
     unregister_blkdev(MY_BLOCK_MAJOR, MY_BLKDEV_NAME);
}
注册一个磁盘

尽管register_blkdev()方法获取了major,但是它没有为系统提供设备(磁盘)。alloc_disk()方法用于分配磁盘,del_gendisk()用于取消分配。使用add_disk()来添加磁盘。

alloc_disk()和add_disk()被用于模块的初始化。

#include <linux/fs.h>
#include <linux/genhd.h>

#define MY_BLOCK_MINORS       1

static struct my_block_dev {
    struct gendisk *gd;
    //...
} dev;

static int create_block_device(struct my_block_dev *dev)
{
    dev->gd = alloc_disk(MY_BLOCK_MINORS);
    //...
    add_disk(dev->gd);
}

static int my_block_init(void)
{
    //...
    create_block_device(&dev);
}

static void delete_block_device(struct my_block_dev *dev)
{
    if (dev->gd)
        del_gendisk(dev->gd);
    //...
}

static void my_block_exit(void)
{
    delete_block_device(&dev);
    //...
}

就想是字符设备一样,我们建议使用my_block_dev结构来存储描述块设备的重要元素。

It can be noticed that the basic structure in working with block devices (disks) is the struct gendisk structure.

struct gendisk structure

struct gendisk存储着有关磁盘的信息。这个结构来自于alloc_disk()。在调用add_disk()之前,各个字段被填充了。
struct gendisk结构拥有下面几个重要的字段

  • major, first_minor, minor, describing the identifiers used by the disk; a disk must have at least one minor; if the disk allows the partitioning operation, a minor must be allocated for each possible partition
  • disk_name, which represents the disk name as it appears in /proc/partitions and in sysfs (/sys/block)
  • fops, representing operations associated with the disk
  • queue, which represents the queue of requests
  • capacity, which is disk capacity in 512 byte sectors; it is initialized using the set_capacity() function
  • private_data, which is a pointer to private data

struct gendisk结构如下所示

#include <linux/genhd.h>
#include <linux/fs.h>
#include <linux/blkdev.h>

#define NR_SECTORS                   1024

#define KERNEL_SECTOR_SIZE           512

static struct my_block_dev {
    //...
    spinlock_t lock;                /* For mutual exclusion */
    struct request_queue *queue;    /* The device request queue */
    struct gendisk *gd;             /* The gendisk structure */
    //...
} dev;

static int create_block_device(struct my_block_dev *dev)
{
    ...
    /* Initialize the gendisk structure */
    dev->gd = alloc_disk(MY_BLOCK_MINORS);
    if (!dev->gd) {
        printk (KERN_NOTICE "alloc_disk failure\n");
        return -ENOMEM;
    }

    dev->gd->major = MY_BLOCK_MAJOR;
    dev->gd->first_minor = 0;
    dev->gd->fops = &my_block_ops;
    dev->gd->queue = dev->queue;
    dev->gd->private_data = dev;
    snprintf (dev->gd->disk_name, 32, "myblock");
    set_capacity(dev->gd, NR_SECTORS);

    add_disk(dev->gd);

    return 0;
}

static int my_block_init(void)
{
    int status;
    //...
    status = create_block_device(&dev);
    if (status < 0)
        return status;
    //...
}

static void delete_block_device(struct my_block_dev *dev)
{
    if (dev->gd) {
        del_gendisk(dev->gd);
    }
    //...
}

static void my_block_exit(void)
{
    delete_block_device(&dev);
    //...
}

就像之前所说的那样,内核把磁盘看作是512字节大小扇区的向量。现实当中,设备可能具有不同大小的扇区。通过内核产生的所有请求将会是扇区大小的倍数并且会被对齐。

struct block_device_operations structure

如果只是字符设备,那么struct file_operations 的操作是完整的,对于块设备而言,struct block_device_operations的操作是完整的。

struct block_device_operations结构如下所示

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 *);
    int (*revalidate_disk) (struct gendisk *);
    int (*getgeo)(struct block_device *, struct hd_geometry *);
    struct module *owner;
}

open()和release()操作被直接从用户空间调用。可以分区,文件系统的创建,文件系统的验证。
对open函数的调用有两种情况,一种是从内核空间进行调用,另一种是从用户空间进行调用,块设备驱动程序不能够对这两种情况进行区分.

#include <linux/fs.h>
#include <linux/genhd.h>

static struct my_block_dev {
    //...
    struct gendisk * gd;
    //...
} dev;

static int my_block_open(struct block_device *bdev, fmode_t mode)
{
    //...

    return 0;
}

static int my_block_release(struct gendisk *gd, fmode_t mode)
{
    //...

    return 0;
}

struct block_device_operations my_block_ops = {
    .owner = THIS_MODULE,
    .open = my_block_open,
    .release = my_block_release
};

static int create_block_device(struct my_block_dev *dev)
{
    //....
    dev->gd->fops = &my_block_ops;
    dev->gd->private_data = dev;
    //...
}

请注意到这里面没有涉及到read和write操作,这些操作通过request()函数来执行,在请求队列的参与下进行的.下面讲介绍请求队列.

请求队列

请求队列被称为struct request_queue的结构代表.
块设备的驱动使用的是队列来存储块IO请求。请求队列通过双链表链接在一起,其中还包含着控制信息。只要请求队列不空,那么与该队列相关的设备驱动将会从请求队列中取出第一个请求,并且将该请求和对应的块设备相关联.请求队列当中的每一个请求都是struct request结构。

请求队列实现了接口,使得能够使用multiple I/O schedulers. 为了最大化性能的发挥,scheduler立刻对请求进行排序并且将他们提供给驱动程序.调度器也对相关联的请求进行处理,即对磁盘中相邻扇区的请求访问.

创建和删除请求队列

A request queue is created with the blk_init_queue() function and is deleted using the blk_cleanup_queue() function.

#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>

static struct my_block_dev {
    //...
    struct request_queue *queue;
    //...
} dev;

static void my_block_request(struct request_queue *q);
//...

static int create_block_device(struct my_block_dev *dev)
{
    /* Initialize the I/O queue */
    spin_lock_init(&dev->lock);//来保证对请求队列的互斥访问.
    dev->queue = blk_init_queue(my_block_request, &dev->lock);
    if (dev->queue == NULL)
        goto out_err;
    blk_queue_logical_block_size(dev->queue, KERNEL_SECTOR_SIZE);
    dev->queue->queuedata = dev;//作为请求队列初始化的一部分,queuedata和在其他结构中的private_data字段是起着相等的作用.
    //...

out_err:
    return -ENOMEM;
}

static int my_block_init(void)
{
    int status;
    //...
    status = create_block_device(&dev);
    if (status < 0)
        return status;
    //...
}

static void delete_block_device(struct block_dev *dev)
{
    //...
    if (dev->queue)
        blk_cleanup_queue(dev->queue);
}

static void my_block_exit(void)
{
    delete_block_device(&dev);
    //...
}

The blk_init_queue() 的第一个参数是指向某个函数的指针,而这个函数正是用来处理对设备的请求的.这个函数是my_block_request().锁是一个自旋锁,它是驱动创建的,并且内核使用它,在调用request()的时候,这样来确保对queue的互斥访问.这个锁同样可以用于其他的设备驱动方法,用来在request()方法中对共享的数据进行保护.

处理请求队列的有用的函数

request_fn_proc方法用来处理对块设备的请求.这个方法和read以及write方法是同等重要的.这个方法接收请求队列作为参数,并且可以从请求队列中找出许多处理请求的方法.

请求队列中的用来处理请求的方法如下所示:

  • blk_peek_request() 从请求队列中取出第一个请求,每个请求的开始执行都需要调用blk_start_request()方法
  • blk_start_request() 从队列中取出请求并开始执行.通常上,这个方法接收一个指针作为对blk_peek_request返回的request的引用.
  • blk_fetch_request() 从队列中取出一个请求并且开始执行.相当与先是执行了blk_peek_request然后执行了blk_start_request.
  • blk_requeue_request() 来重新进入队列.

在调用上面4个方法的任意一个之前,和队列相关联的spinlock都需要被获取到.如果通过request_fn_proc类型的方法来进行调用,难么spinlock就已经被满足了.

请求块设备

对块设备的请求可以通过sturct request结构来进行描述.
struct request结构的字段包括下面的这些.

  • cmd_flags: 一个包含方向的一个系列的flag,方向指的是读或者写.为了确定方向,将会使用宏定义rq_data_dir.返回0代表read请求,返回1代表write请求.
  • __sector: 请求的第一个扇区.如果设备扇区有不同的大小,那么需要进行合适的转换.为了访问这个字段,使用blk_rq_pos宏.
  • __data_len: 传输的字节的总数目.为了访问这个字段,需要使用blk_rq_bytes宏.
  • 通常上来说,当前的struct bio中的数据将会被传输,使用blk_rq_cur_bytes宏来获取size.
  • bio, 是struct bio结构的动态列表,也是和request相关的一系列buffer的集合.如果只有一个buffer,那么使用bio_data宏定义来访问这个字段,如果有多个buffer,那么使用rq_for_each_segment宏定义来访问这个字段.
  • bio_data: 和request相关的buffer的地址.关于struct bio结构和与他有关的操作,将会在bio_structure章节中讨论.
创建请求

通过高于内核I/O子系统的代码层可以创建读写请求.创建对块设备的读写请求的子系统是文件管理子系统.IO子系统相当于是文件管理子系统和块设备驱动之间的接口.IO子系统的主要的职责就是向对特定的块设备的请求的队列中添加请求,并且根据性能的考虑对request进行排序和合并.

结束请求

当设备驱动已经讲请求的各个扇区传送完成,那么它需要调用blk_end_request来通知IO子系统.如果已经获取了和请求队列相关的锁,那么可以使用__blk_end_request()方法.

如果设备驱动在没有完成所有扇区的传送的同时想要关闭请求,那么它可以调用blk_end_request_all()或者__blk_end_request_all()方法.

处理请求

块设备驱动的核心部分是request_fn_proc函数类型.在之前的例子中,实现了这个角色的方法是my_block_request(),通过调用blk_init_queue()方法来访问,但是并不会强制结束它的运行,因为请求可以被驱动程序的其他部分结束.

当创建请求队列是的锁参数,是当执行请求的方法的时候内核持有的自旋锁.由于这个因素,请求的方法运行在原子上下文中并且必须遵守原子代码的准则(它不需要调用可能会导致sleep的方法).这个锁也会确保当前请求正在执行的时候对该设备的其他的请求不会添加到这个设备的请求队列中.

由于任何用户空间进程的行为不确定以及各自的函数需要运行在那个进程中,所以对处理请求的函数的调用是异步的.当然,我们也不应当假定request提供的buffer是来自内核空间还是来自用户空间,任何访问用户空间的操作可能会导致错误.
下面提供了一个request_fn_proc类型的最简单的方法.

static void my_block_request(struct request_queue *q)
{
    struct request *rq;
    struct my_block_dev *dev = q->queuedata;

    while (1) {
        rq = blk_fetch_request(q);
        if (rq == NULL)
            break;

        if (blk_rq_is_passthrough(rq)) {
            printk (KERN_NOTICE "Skip non-fs request\n");
            __blk_end_request_all(rq, -EIO);
           continue;
        }

        /* do work */
        ...

        __blk_end_request_all(rq, 0);
    }
}

my_block_request方法中包含了一个while 循环,来对请求队列中的每一个请求进行遍历并作为参数.在这个循环内执行的操作如下所示:

  • 使用blk_fetch_request方法从请求队列中读取第一个请求.获取并且开始执行.
  • 如果方法返回NULL,那么已经走到了请求队列的末尾,证明请求队列中不存在待处理的请求了.
  • 块设备可以接收不传输任何数据块的调用,例如在磁盘上的低等级的操作.大多数的设备不知道如何处理这些请求并返回error.
  • 为了返回error,__blk_end_request_all()方法会被调用,-EIO是第二个参数.
  • 根据涉及到的设备的需要,请求被处理.
struct bio 结构

每个struct request结构都是IO块的请求,但是可能会从来自更高层的请求集成在一起.将要被reques传送的扇区将会分散在主存当中,但是这些扇区是设备中的连续的扇区.一系列的segments组成了请求,每个segments都和主存中的buffer相对应.内核寻求相邻扇区的请求集成在一起,但是不会将写请求和读请求集成在一个单独的struct request结构当中.

struct request结构通过一个struct bio组成的链表实现的.集成了information的结构使得驱动能够在处理请求的时候记得当前的位置.

sturct bio结构块IO请求的低级的描述.

struct bio {
    //...
    struct gendisk          *bi_disk;
    unsigned int            bi_opf;         /* bottom bits req flags, top bits REQ_OP. Use accessors. */请求的类型
    //...
    struct bio_vec          *bi_io_vec;     /* the actual vec list */
    //...
    struct bvec_iter        bi_iter;
    /...
    void                    *bi_private;
    //...
};

反过来,struct bio结构包含一个struct bio_vec结构的向量(vector):bi_io_vec.它由物理内存中独立的页,页面内的偏移和buffer的大小所组成.

/*
 * was unsigned short, but we might as well be ready for > 64kB I/O pages
 */
struct bio_vec {
	struct page	*bv_page;
	unsigned int	bv_len;
	unsigned int	bv_offset;
};

为了遍历struct bio结构,我们需要遍历向量:struct bio_vec,并且从每个物理页中传送数据.为了简化向量的遍历,需要使用struct bvec_iter结构.

struct bvec_iter {
	sector_t		bi_sector;	/* device address in 512 byte sectors */
	unsigned int		bi_size;	/* residual I/O count */
	unsigned int		bi_idx;		/* current index into bvl_vec */
	unsigned int            bi_bvec_done;	/* number of bytes completed in current bvec */
};

这个结构保留了关于有多少buffer和sector诶消费了的信息.请求的类型被编入了bi_opf字段当中.为了确定它,使用bio_data_dir()方法.

创建struct bio结构

两种方法可以被用来创建struct bio结构.

  • bio_alloc(): 为新的结构分配空间;这个结构必须被初始化.
  • bio_clone(): 对现存的struct bio结构进行克隆;新克隆的结构被老的struct bio结构中字段的值进行初始化.buffers和被克隆的struct bio结构共享,从而对buffers的访问需要谨慎处理,这样才能避免从两个clones中访问相同的内存.

两个方法都返回struct bio结构.

提交struct bio结构

通常,struct bio结构被更高等级的内核创建.因此该结构被创建后就被传送到IO子系统当中,并收集更多的struct bio结构进入到request当中.

为了将struct bio结构提交给相关的IO设备驱动,需要使用submit_bio方法.这个方法接受一个初始化过了的struct bio结构作为参数,将struct bio从一个设备的请求队列中添加到request当中.所以在这个过程中,IO设备驱动可以使用特殊的方法来处理这个struct bio.

等待struct bio结构的完成

Submitting a struct bio structure to a driver has the effect of adding it to a request from the request queue from where it will be further processed. Thus, when the submit_bio() function returns, it is not guaranteed that the processing of the structure has finished. If you want to wait for the processing of the request to be finished, use the submit_bio_wait() function.

To be notified when the processing of a struct bio structure ends (when we do not use submit_bio_wait() function), the bi_end_io field of the structure should be used. This field specifies the function that will be called at the end of the struct bio structure processing. You can use the bi_private field of the structure to pass information to the function.
在这里插入图片描述

初始化struct bio结构

Once a struct bio structure has been allocated and before being transmitted, it must be initialized.

Initializing the structure involves filling in its important fields. As mentioned above, the bi_end_io field is used to specify the function called when the processing of the structure is finished. The bi_private field is used to store useful data that can be accessed in the function pointed by bi_end_io.

The bi_opf field specifies the type of operation. Use the bio_set_op_attrs to initialize the type of operation.
在这里插入图片描述

struct bio *bio = bio_alloc(GFP_NOIO, 1);
//...
bio->bi_disk = bdev->bd_disk;
bio->bi_iter.bi_sector = sector;
bio_set_op_attrs(bio, REQ_OP_READ, 0);
bio_add_page(bio, page, size, offset);
//...

In the code snippet above we specified the block device to which we sent the following: struct bio structure, startup sector, operation (REQ_OP_READ or REQ_OP_WRITE) and content. The content of a struct bio structure is a buffer described by: a physical page, the offset in the page and the size of the bufer. A page can be assigned using the alloc_page() call.

如何使用struct bio结构的内容

To use the content of a struct bio structure, the structure’s support pages must be mapped to the kernel address space from where they can be accessed. For mapping /unmapping, use the kmap_atomic and the kunmap_atomic macros.
在这里插入图片描述

static void my_block_transfer(struct my_block_dev *dev, size_t start,
                              size_t len, char *buffer, int dir);


static int my_xfer_bio(struct my_block_dev *dev, struct bio *bio)
{
    int i;
    struct bio_vec bvec;
    struct bvec_iter i;
    int dir = bio_data_dir(bio);

    /* Do each segment independently. */
    bio_for_each_segment(bvec, bio, i) {
        sector_t sector = i.bi_sector;
        char *buffer = kmap_atomic(bvec.bv_page);
        unsigned long offset = bvec.bv_offset;
        size_t len = bvec.bv_len;

        /* process mapped buffer */
        my_block_transfer(dev, sector, len, buffer + offset, dir);

        kunmap_atomic(buffer);
    }

    return 0;
}

As it can be seen from the example above, iterating through a struct bio requires iterating through all of its segments. A segment (struct bio_vec) is defined by the physical address page, the offset in the page and its size.
在这里插入图片描述
To simplify the processing of a struct bio, use the bio_for_each_segment macrodefinition. It will iterate through all segments, and will also update global information stored in an iterator (struct bvec_iter) such as the current sector as well as other internal information (segment vector index, number of bytes left to be processed, etc.) .

You can store information in the mapped buffer, or extract information.

In case request queues are used and you needed to process the requests at struct bio level, use the rq_for_each_segment macrodefinition instead of the bio_for_each_segment macrodefinition. This macrodefinition iterates through each segment of each struct bio structure of a struct request structure and updates a struct req_iterator structure. The struct req_iterator contains the current struct bio structure and the iterator that traverses its segments.
在这里插入图片描述

struct bio_vec bvec;
struct req_iterator iter;

rq_for_each_segment(bvec, req, iter) {
    sector_t sector = iter.iter.bi_sector;
    char *buffer = kmap_atomic(bvec.bv_page);
    unsigned long offset = bvec.bv_offset;
    size_t len = bvec.bv_len;
    int dir = bio_data_dir(iter.bio);

    my_block_transfer(dev, sector, len, buffer + offset, dir);

    kunmap_atomic(buffer);
}
Free a struct bio structure

Once a kernel subsystem uses a struct bio structure, it will have to release the reference to it. This is done by calling bio_put() function.

在bio等级设置请求队列

The function blk_init_queue() may specify a function to be used to process requests sent to the driver. The function receives as argument the request queue as queries and carries out processing at struct request level.
blk_init_queue在struct request等级执行.
If, for flexibility reasons, it is needed to specify a function that carries out processing at struct bio structure level, the function blk_queue_make_request() in conjunction with the blk_alloc_queue() function should be used.

Below is a typical example of initializing a function that carries out processing at struct bio structure level:

// the declaration of the function that carries out processing
// :c:type:`struct bio` structures
static void my_make_request(struct request_queue *q, struct bio *bio);


// ...
// queue creation
dev->queue = blk_alloc_queue (GFP_KERNEL);
if (dev->queue == NULL) {
    printk(KERN_ERR "cannot allocate block device queue\n");
    return -ENOMEM;
}
// the registration of the function that carries out processing
// :c:type:`struct bio` structures
blk_queue_make_request(dev->queue, my_make_request);
dev->queue->queuedata = dev;

struct bio结构

/*
 * main unit of I/O for the block layer and lower layers (ie drivers and
 * stacking drivers)
 */
struct bio {
	struct bio		*bi_next;	/* request queue link */
	struct block_device	*bi_bdev;
	unsigned long		bi_flags;	/* status, command, etc */
	unsigned long		bi_rw;		/* bottom bits READ/WRITE,
						 * top bits priority
						 */

	struct bvec_iter	bi_iter;

	/* Number of segments in this BIO after
	 * physical address coalescing is performed.
	 */
	unsigned int		bi_phys_segments;

	/*
	 * To keep track of the max segment size, we account for the
	 * sizes of the first and last mergeable segments in this bio.
	 */
	unsigned int		bi_seg_front_size;
	unsigned int		bi_seg_back_size;

	atomic_t		bi_remaining;

	bio_end_io_t		*bi_end_io;

	void			*bi_private;
#ifdef CONFIG_BLK_CGROUP
	/*
	 * Optional ioc and css associated with this bio.  Put on bio
	 * release.  Read comment on top of bio_associate_current().
	 */
	struct io_context	*bi_ioc;
	struct cgroup_subsys_state *bi_css;
#endif
#if defined(CONFIG_BLK_DEV_INTEGRITY)
	struct bio_integrity_payload *bi_integrity;  /* data integrity */
#endif

	unsigned short		bi_vcnt;	/* how many bio_vec's */

	/*
	 * Everything starting with bi_max_vecs will be preserved by bio_reset()
	 */

	unsigned short		bi_max_vecs;	/* max bvl_vecs we can hold */

	atomic_t		bi_cnt;		/* pin count */

	struct bio_vec		*bi_io_vec;	/* the actual vec list */

	struct bio_set		*bi_pool;

	/*
	 * We can inline a number of vecs at the end of the bio, to avoid
	 * double allocations for a small number of bio_vecs. This member
	 * MUST obviously be kept at the very end of the bio.
	 */
	struct bio_vec		bi_inline_vecs[0];
};

struct bio_vec结构

/*
 * was unsigned short, but we might as well be ready for > 64kB I/O pages
 */
struct bio_vec {
	struct page	*bv_page;
	unsigned int	bv_len;
	unsigned int	bv_offset;
};

bio_for_each_segment

#define bio_for_each_segment(bvl, bio, iter)				\
	__bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)


#define __bio_for_each_segment(bvl, bio, iter, start)			\
	for (iter = (start);						\
	     (iter).bi_size &&						\
		((bvl = bio_iter_iovec((bio), (iter))), 1);		\
	     bio_advance_iter((bio), &(iter), (bvl).bv_len))

struct gendisk

struct gendisk {
	/* major, first_minor and minors are input parameters only,
	 * don't use directly.  Use disk_devt() and disk_max_parts().
	 */
	int major;			/* major number of driver */
	int first_minor;
	int minors;                     /* maximum number of minors, =1 for
                                         * disks that can't be partitioned. */

	char disk_name[DISK_NAME_LEN];	/* name of major driver */
	char *(*devnode)(struct gendisk *gd, umode_t *mode);

	unsigned int events;		/* supported events */
	unsigned int async_events;	/* async events, subset of all */

	/* Array of pointers to partitions indexed by partno.
	 * Protected with matching bdev lock but stat and other
	 * non-critical accesses use RCU.  Always access through
	 * helpers.
	 */
	struct disk_part_tbl __rcu *part_tbl;
	struct hd_struct part0;

	const struct block_device_operations *fops;
	struct request_queue *queue;
	void *private_data;

	int flags;
	struct device *driverfs_dev;  // FIXME: remove
	struct kobject *slave_dir;

	struct timer_rand_state *random;
	atomic_t sync_io;		/* RAID */
	struct disk_events *ev;
#ifdef  CONFIG_BLK_DEV_INTEGRITY
	struct blk_integrity *integrity;
#endif
	int node_id;
};

struct block_device_operations

struct block_device_operations {
	int (*open) (struct block_device *, fmode_t);
	void (*release) (struct gendisk *, fmode_t);
	int (*rw_page)(struct block_device *, sector_t, struct page *, int rw);
	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;
};

测试程序

/*
 * SO2 - Block device driver (#8)
 * Test suite for exercise #3 (RAM Disk)
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>

#define NR_SECTORS	128
#define SECTOR_SIZE	512

#define DEVICE_NAME	"/dev/myblock"
#define MODULE_NAME	"ram-disk"
#define MY_BLOCK_MAJOR	"240"
#define MY_BLOCK_MINOR	"0"


#define max_elem_value(elem)	\
	(1 << 8*sizeof(elem))

static unsigned char buffer[SECTOR_SIZE];
static unsigned char buffer_copy[SECTOR_SIZE];

static void test_sector(int fd, size_t sector)
{
	int i;

	for (i = 0; i < sizeof(buffer) / sizeof(buffer[0]); i++)//初始化buffer数组
		buffer[i] = rand() % max_elem_value(buffer[0]);

	lseek(fd, sector * SECTOR_SIZE, SEEK_SET);
	write(fd, buffer, sizeof(buffer));//将buffer的内容写入到块设备中

	fsync(fd);

	lseek(fd, sector * SECTOR_SIZE, SEEK_SET);
	read(fd, buffer_copy, sizeof(buffer_copy));//将块设备中的数据读取到buffer_copy数组中

	printf("test sector %3d ... ", sector);
	if (memcmp(buffer, buffer_copy, sizeof(buffer_copy)) == 0)
		printf("passed\n");
	else
		printf("failed\n");
}

int main(void)
{
	int fd;
	size_t i;
	int back_errno;

	printf("insmod ../kernel/" MODULE_NAME ".ko\n");
	system("insmod ../kernel/" MODULE_NAME ".ko\n");
	sleep(1);

	printf("mknod " DEVICE_NAME " b " MY_BLOCK_MAJOR " " MY_BLOCK_MINOR "\n");
	system("mknod " DEVICE_NAME " b " MY_BLOCK_MAJOR " " MY_BLOCK_MINOR "\n");
	sleep(1);

	fd = open(DEVICE_NAME, O_RDWR);
	if (fd < 0) {
		back_errno = errno;
		perror("open");
		fprintf(stderr, "errno is %d\n", back_errno);
		exit(EXIT_FAILURE);
	}

	srand(time(NULL));
	for (i = 0; i < NR_SECTORS; i++)
		test_sector(fd, i);

	close(fd);

	sleep(1);
	printf("rmmod " MODULE_NAME "\n");
	system("rmmod " MODULE_NAME "\n");

	return 0;
}
/*
 * SO2 - Block device drivers lab (#7)
 * Linux - Exercise #1, #2, #3, #6 (RAM Disk)
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/genhd.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/vmalloc.h>

MODULE_DESCRIPTION("Simple RAM Disk");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");


#define KERN_LOG_LEVEL		KERN_ALERT

#define MY_BLOCK_MAJOR		240
#define MY_BLKDEV_NAME		"mybdev"
#define MY_BLOCK_MINORS		1
#define NR_SECTORS		128

#define KERNEL_SECTOR_SIZE	512

/* TODO 6: use bios for read/write requests */
#define USE_BIO_TRANSFER	0


static struct my_block_dev {
	spinlock_t lock;
	struct request_queue *queue;
	struct gendisk *gd;
	u8 *data;
	size_t size;
} g_dev;

static int my_block_open(struct block_device *bdev, fmode_t mode)
{
	return 0;
}

static void my_block_release(struct gendisk *gd, fmode_t mode)
{
}

static const struct block_device_operations my_block_ops = {
	.owner = THIS_MODULE,
	.open = my_block_open,
	.release = my_block_release
};

static void my_block_transfer(struct my_block_dev *dev, sector_t sector,
		unsigned long len, char *buffer, int dir)
{
	unsigned long offset = sector * KERNEL_SECTOR_SIZE;

	/* check for read/write beyond end of block device */
	if ((offset + len) > dev->size)
		return;

	/* TODO 3: read/write to dev buffer depending on dir */
    if(dir == 1)
        memcpy(dev->data + offset, buffer, len);
    else
        memcpy(buffer, dev->data + offset, len);
}

/* to transfer data using bio structures enable USE_BIO_TRANFER */
#if USE_BIO_TRANSFER == 1
static void my_xfer_request(struct my_block_dev *dev, struct request *req)
{
	/* TODO 6: iterate segments */
    struct bio_vec bvec;
    struct req_iterator iter;

    rq_for_each_segment(bvec,req,iter){
        sector_t sector = iter.iter.bi_sector;
        unsigned long offset = bvec.bv_offset;
        size_t len = bvec.bv_len;
        int dir = bio_data_dir(iter.bio);
        char *buffer = kmap_atomic(bvec.bv_page);
        printk(KERN_LOG_LEVEL "%s: buf %8p offset %lu len %u dir %d\n",__func__,buffer,offset,len,dir);
        my_block_transfer(dev,sector,len,buffer+offset,dir);
        kunmap_atomic(buffer);
    }
		/* TODO 6: copy bio data to device buffer */
}
#endif

static void my_block_request(struct request_queue *q)
{
	struct request *rq;
	struct my_block_dev *dev = q->queuedata;

	while (1) {

		/* TODO 2: fetch request */
        rq = blk_fetch_request(q);
        if(rq == NULL)
            break;
		/* TODO 2: check fs request */
        if(blk_rq_is_passthrough(rq)){
            printk(KERN_NOTICE "Skip non-fs request\n");
            __blk_end_request_all(rq,-EIO);
            continue;
        }
		/* TODO 2: print request information */
        printk(KERN_LOG_LEVEL "request received: pos=%llu bytes=%u cur_bytes=%u dir=%c\n",(unsigned long long)blk_rq_pos(rq),blk_rq_bytes(rq),blk_rq_cur_bytes(rq),rq_data_dir(rq)? 'W' : 'R');
#if USE_BIO_TRANSFER == 1
        my_xfer_request(dev,rq);
		/* TODO 6: process the request by calling my_xfer_request */
#else
		/* TODO 3: process the request by calling my_block_transfer */
        my_block_transfer(dev,blk_rq_pos(rq),blk_rq_bytes(rq),bio_data(rq->bio),rq_data_dir(rq));
#endif

		/* TODO 2: end request successfully */
        __blk_end_request_all(rq,0);
	}
}

static int create_block_device(struct my_block_dev *dev)
{
	int err;

	dev->size = NR_SECTORS * KERNEL_SECTOR_SIZE;
	dev->data = vmalloc(dev->size);
	if (dev->data == NULL) {
		printk(KERN_ERR "vmalloc: out of memory\n");
		err = -ENOMEM;
		goto out_vmalloc;
	}

	/* initialize the I/O queue */
	spin_lock_init(&dev->lock);
	dev->queue = blk_init_queue(my_block_request, &dev->lock);
	if (dev->queue == NULL) {
		printk(KERN_ERR "blk_init_queue: out of memory\n");
		err = -ENOMEM;
		goto out_blk_init;
	}
	blk_queue_logical_block_size(dev->queue, KERNEL_SECTOR_SIZE);
	dev->queue->queuedata = dev;

	/* initialize the gendisk structure */
	dev->gd = alloc_disk(MY_BLOCK_MINORS);
	if (!dev->gd) {
		printk(KERN_ERR "alloc_disk: failure\n");
		err = -ENOMEM;
		goto out_alloc_disk;
	}

	dev->gd->major = MY_BLOCK_MAJOR;
	dev->gd->first_minor = 0;
	dev->gd->fops = &my_block_ops;
	dev->gd->queue = dev->queue;
	dev->gd->private_data = dev;
	snprintf(dev->gd->disk_name, DISK_NAME_LEN, "myblock");
	set_capacity(dev->gd, NR_SECTORS);

	add_disk(dev->gd);

	return 0;

out_alloc_disk:
	blk_cleanup_queue(dev->queue);
out_blk_init:
	vfree(dev->data);
out_vmalloc:
	return err;
}

static int __init my_block_init(void)
{
	int err = 0;

	/* TODO 1: register block device */
    err = register_blkdev(MY_BLOCK_MAJOR,MY_BLKDEV_NAME);
    if(err < 0){
        printk(KERN_ERR "register_blkdev: unable to register\n");
        return err;
    }
	/* TODO 2: create block device using create_block_device */
    err = create_block_device(&g_dev);
    if(err < 0)
        {goto out;}

	return 0;

out:
	/* TODO 1: unregister block device in case of an error */
    unregister_blkdev(MY_BLOCK_MAJOR,MY_BLKDEV_NAME);
	return err;
}

static void delete_block_device(struct my_block_dev *dev)
{
	if (dev->gd) {
		del_gendisk(dev->gd);
		put_disk(dev->gd);
	}
	if (dev->queue)
		blk_cleanup_queue(dev->queue);
	if (dev->data)
		vfree(dev->data);
}

static void __exit my_block_exit(void)
{
	/* TODO 2: cleanup block device using delete_block_device */
    delete_block_device(&g_dev);
	/* TODO 1: unregister block device */
    unregister_blkdev(MY_BLOCK_MAJOR,MY_BLKDEV_NAME);
}

module_init(my_block_init);
module_exit(my_block_exit);

root@qemux86:~/skels/block_device_drivers/1-2-3-6-ram-disk/user# ./ram-disk-test
insmod …/kernel/ram-disk.ko
mknod /dev/myblock b 240 0
mknod: /dev/myblock: File exists
request received: pos=0 bytes=4096 cur_bytes=4096 dir=R
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 0 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 1 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 2 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 3 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 4 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 5 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 6 … passed
request received: pos=0 bytes=4096 cur_bytes=4096 dir=W
test sector 7 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=R
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 8 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 9 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 10 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 11 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 12 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 13 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 14 … passed
request received: pos=8 bytes=4096 cur_bytes=4096 dir=W
test sector 15 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=R
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 16 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 17 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 18 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 19 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 20 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 21 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 22 … passed
request received: pos=16 bytes=4096 cur_bytes=4096 dir=W
test sector 23 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=R
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 24 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 25 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 26 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 27 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 28 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 29 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 30 … passed
request received: pos=24 bytes=4096 cur_bytes=4096 dir=W
test sector 31 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=R
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 32 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 33 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 34 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 35 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 36 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 37 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 38 … passed
request received: pos=32 bytes=4096 cur_bytes=4096 dir=W
test sector 39 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=R
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 40 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 41 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 42 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 43 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 44 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 45 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 46 … passed
request received: pos=40 bytes=4096 cur_bytes=4096 dir=W
test sector 47 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=R
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 48 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 49 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 50 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 51 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 52 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 53 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 54 … passed
request received: pos=48 bytes=4096 cur_bytes=4096 dir=W
test sector 55 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=R
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 56 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 57 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 58 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 59 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 60 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 61 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 62 … passed
request received: pos=56 bytes=4096 cur_bytes=4096 dir=W
test sector 63 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=R
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 64 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 65 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 66 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 67 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 68 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 69 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 70 … passed
request received: pos=64 bytes=4096 cur_bytes=4096 dir=W
test sector 71 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=R
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 72 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 73 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 74 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 75 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 76 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 77 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 78 … passed
request received: pos=72 bytes=4096 cur_bytes=4096 dir=W
test sector 79 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=R
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 80 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 81 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 82 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 83 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 84 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 85 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 86 … passed
request received: pos=80 bytes=4096 cur_bytes=4096 dir=W
test sector 87 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=R
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 88 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 89 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 90 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 91 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 92 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 93 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 94 … passed
request received: pos=88 bytes=4096 cur_bytes=4096 dir=W
test sector 95 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=R
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 96 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 97 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 98 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 99 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 100 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 101 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 102 … passed
request received: pos=96 bytes=4096 cur_bytes=4096 dir=W
test sector 103 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=R
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 104 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 105 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 106 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 107 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 108 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 109 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 110 … passed
request received: pos=104 bytes=4096 cur_bytes=4096 dir=W
test sector 111 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=R
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 112 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 113 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 114 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 115 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 116 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 117 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 118 … passed
request received: pos=112 bytes=4096 cur_bytes=4096 dir=W
test sector 119 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=R
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 120 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 121 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 122 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 123 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 124 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 125 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 126 … passed
request received: pos=120 bytes=4096 cur_bytes=4096 dir=W
test sector 127 … passed
rmmod ram-disk
root@qemux86:~/skels/block_device_drivers/1-2-3-6-ram-disk/user#

测试从磁盘中读取数据
/*
 * SO2 Lab - Block device drivers (#7)
 * Linux - Exercise #4, #5 (Relay disk - bio)
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>

MODULE_AUTHOR("SO2");
MODULE_DESCRIPTION("Relay disk");
MODULE_LICENSE("GPL");

#define KERN_LOG_LEVEL		KERN_ALERT

#define PHYSICAL_DISK_NAME	"/dev/vdb"
#define KERNEL_SECTOR_SIZE	512

#define BIO_WRITE_MESSAGE	"def"


/* pointer to physical device structure */
static struct block_device *phys_bdev;

static void send_test_bio(struct block_device *bdev, int dir)
{
	struct bio *bio = bio_alloc(GFP_NOIO, 1);
	struct page *page;
	char *buf;

	/* TODO 4: fill bio (bdev, sector, direction) */
    bio->bi_disk = bdev->bd_disk;
    bio->bi_iter.bi_sector = 0;
    bio_set_op_attrs(bio,dir,0);

	page = alloc_page(GFP_NOIO);
	bio_add_page(bio, page, KERNEL_SECTOR_SIZE, 0);

	/* TODO 5: write message to bio buffer if direction is write */
    if(dir == REQ_OP_WRITE){
        buf = kmap_atomic(page);
        memcpy(buf,BIO_WRITE_MESSAGE,strlen(BIO_WRITE_MESSAGE));
        kunmap_atomic(buf);
    }
	/* TODO 4: submit bio and wait for completion */
    printk(KERN_LOG_LEVEL "[send_test_bio] Submitting bio\n");
    submit_bio_wait(bio);
    printk(KERN_LOG_LEVEL "[send_test_bio] Done bio\n");

	/* TODO 4: read data (first 3 bytes) from bio buffer and print it */
    buf = kmap_atomic(page);
    printk(KERN_LOG_LEVEL "read %02x %02x %02x\n",buf[0],buf[1],buf[2]);
    kunmap_atomic(buf);

	bio_put(bio);
	__free_page(page);
}

static struct block_device *open_disk(char *name)
{
	struct block_device *bdev;

	/* TODO 4: get block device in exclusive mode */
    bdev = blkdev_get_by_path(name, FMODE_READ | FMODE_WRITE | FMODE_EXCL,THIS_MODULE);
    if(IS_ERR(bdev)){
        printk(KERN_ERR "blkdev_get_by_path\n");
        return NULL;
    }

	return bdev;
}

static int __init relay_init(void)
{
	phys_bdev = open_disk(PHYSICAL_DISK_NAME);
	if (phys_bdev == NULL) {
		printk(KERN_ERR "[relay_init] No such device\n");
		return -EINVAL;
	}

	send_test_bio(phys_bdev, REQ_OP_READ);

	return 0;
}

static void close_disk(struct block_device *bdev)
{
	/* TODO 4: put block device */
    blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
}

static void __exit relay_exit(void)
{
	/* TODO 5: send test write bio */
    send_test_bio(phys_bdev,REQ_OP_WRITE);

	close_disk(phys_bdev);
}

module_init(relay_init);
module_exit(relay_exit);

root@qemux86:~/skels/block_device_drivers/4-5-relay-disk# insmod relay-disk.ko
[send_test_bio] Submitting bio
[send_test_bio] Done bio
read 64 65 66
root@qemux86:~/skels/block_device_drivers/4-5-relay-disk#

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值