Chinaunix首页 | 论坛 | 博客
  • 博客访问: 96861
  • 博文数量: 29
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-01 19:43
文章分类
文章存档

2017年(4)

2015年(24)

2014年(1)

我的朋友

分类: LINUX

2015-07-11 13:39:14

原文地址:ll_rw_block解析Ⅰ 作者:zixin

参考分析对块设备驱动程序的读写请求过程一文

原文见:

 

在这里具体解析了ll_rw_block、submit_bh、generic_make_request

而make_request_fn方法实现函数__make_request还没有看完。

 

问题:

————————

前者generic_make_request针对请求队列描述符

后者__make_request针对请求描述符

加密地方还可以斟酌一下。到底在哪个函数中更为合适。

因为在generic_make_request有一个do-while循环。在do-while之前做?

出错机制或者是一个设备到另一个设备需要循环做的情况如何处理?有影响么?

 

可否在__make_request中,创建读写请求结构(请求描述符)之前加密buffer中的数据?

————————

 

    ll_rw_block() [drives/block/ll_rw_blk.c中定义]请求块设备驱动程序将多个物理块读出或写入块设备,块设备的读写都在块缓冲内进行.

    块设备的请求函数由blk_dev[]数组描述,在调用块设备的请求函数之前,需要为每个缓冲块安装相应的bh->b_end_io函数指针,当设备驱动程序读写完成时,将会调用b_end_io()来通知自已读写完成的情况.

 

    其中要写的bh对应的记录块不一定是连续的,但必须是同一设备的。

    每种设备都有一个操作请求队列,队列头部是一个request_queque_t数据结构。即请求队列描述符[include/linux/blkdev.h定义]

struct request_queue

{

/*  the queue request freelist, one for reads and one for writes */

struct request_list rq;

struct list_head queue_head;

elevator_t elevator;


request_fn_proc * request_fn;  [
指针对应结构blkdev.h定义]

merge_request_fn * back_merge_fn;

merge_request_fn * front_merge_fn;

merge_requests_fn * merge_requests_fn;

make_request_fn * make_request_fn;

plug_device_fn * plug_device_fn;


void * queuedata;

struct tq_struct plug_tq;

char plugged;

char head_active;

spinlock_t queue_lock;

 

wait_queue_head_t wait_for_request
};

 

typedef struct request_queue request_queue_t

当内核初始化一个驱程时,为驱程所处理的每个请求队列都创建一个请求队列描述符并填充它。

请求队列是一个双向链表,其元素就是请求描述符(request数据结构,其结构的组后一个元素为指向请求队列描述符的指针request_queue_t * q)

 

为了把各种块设备的操作请求队列有效的组这起来,内核中还设置了一个结构数组blk_dev[],driver/block/ll_rw_blk.c

struct blk_dev_struct blk_dev[MAX_BLKDEV];

 

这个数组以主设备号为下标,数组中的每个元素都是一个blk_dev_struct数据结构,定义在include/linux/blkdev.h:

struct blk_dev_struct {

/*

*主体为操作请求队列request_queue

*/

    request_queue_t     request_queue;

   /*

*queue0时调用此函数来找到具体设备的请求队列

*针对同一主设备号的多项设备而设

*/

    queue_proc      *queue;

/*

*data提供辅助性信息,queue会利用到

*/

    void            *data;

};

 

所有块设备的描述符放在blk_dev表中,由块设备的主设备号索引。

 

解析ll_rw_block函数

void ll_rw_block(int rw, int nr, struct buffer_head * bhs[])

{

    unsigned int major;

    int correct_size;

    int i;

 

    if (!nr)

        return;

 

   /*通过bh的虚拟设备标识符求出主设备号*/

    major = MAJOR(bhs[0]->b_dev);

 

    /* Determine correct block size for this device. */

    correct_size = BLOCK_SIZE;

    if (blksize_size[major]) {

        i = blksize_size[major][MINOR(bhs[0]->b_dev)]; /*确定设备块长*/

        if (i)

            correct_size = i;

    }

 

    /* Verify requested block sizes. */

    /*扫描请求块描述符,其块长必须为设备块长的整数倍*/

    for (i = 0; i < nr; i++) {

        struct buffer_head *bh = bhs[ i ];

        if (bh->b_size % correct_size) {

            printk(KERN_NOTICE "ll_rw_block: device %s: "

                   "only %d-char blocks implemented (%u)\n",

                   kdevname(bhs[0]->b_dev),

                   correct_size, bh->b_size);

            goto sorry;

        }

    }

 

/*

*如果操作是write,确定块设备不是只读的

*内核中有个二维数组ro_bits[MAX_BLKDEV][8](drivers/block/ll_rw_blk.c中定义)

*每个设备在此数组中有标志位,通过iotcl可以将一个标志位设置为0/1

*is_read_only根据设备号就差这个数组中的标志位是否为1来检查读写允许情况

*/

    if ((rw & WRITE) && is_read_only(bhs[0]->b_dev)) {

        printk(KERN_NOTICE "Can't write to read-only device %s\n",

               kdevname(bh s[0]->b_dev));

        goto sorry;

    }

 

/*对于bhs数组中的每个bh*/

    for (i = 0; i < nr; i++) {

        struct buffer_head *bh = bhs[ i ];

 

        /*

         * don't lock any more buffers if we are above the high

         * water mark. instead start I/O on the queued stuff.

         */

        /*

*如果等待传送的块数量大于某个上限值

*则运行块设备驱动程序所生成的任务队列,直到低于某个下限

*/

 

        if (atomic_read(&queued_sectors) >= high_queued_sectors) {

            run_task_queue(&tq_disk);

            wait_event(blk_buffers_wait,

             atomic_read(&queued_sectors) < low_queued_sectors);

        }

 

        /* Only one thread can actually submit the I/O. */

/*

*BH_Lock标志置位锁定将要传送的块,返回原值

*如果其他内核线程设置了它则跳过

*/

        if (test_and_set_bit(BH_Lock, &bh->b_state))

            continue;

 

        /* We have the buffer lock */

/* IO操作结束则更新缓冲区首部 */

        bh->b_end_io = end_buffer_io_sync;

 

       

        switch(rw) {

 

        /*写操作*/

        case WRITE:

/*

* 清除bhBH_Dirty标志,并返回原值

* 如果原值为0则执行b_end_io方法,跳过此块,不用写了

*/

            if (!atomic_set_buffer_clean(bh))

 

                /* Hmmph! Nothing to write */

                goto end_io;

         

          /*

*在上面测试并清除bhBH_Dirty标志时,返回原值为1,则是需要要写

*既然要写了(逻辑上)把BH_Dirty置了0,而BH_Locked之前置1

*__mark_buffer_clean调用refile_buffer把bh转移到加锁bh的LRU队列中

*/

            __mark_buffer_clean(bh);

            break;

      

        /*预读为空操作*/

        case READA:

 

        /*读操作*/

        case READ:

/*

* 检查BH_Uptodate标志,如果要读的块数据已经被更新

* 则调用b_end_io跳过此块

*/

            if (buffer_uptodate(bh))

                /* Hmmph! Already have it */

                goto end_io;

            break;

 

        default:

            BUG();

    end_io:

        /* 调用b_end_io方法,继承BH_Uptodate状态 */

        bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state));

            continue;

        }

 

/* 向块设备驱动程序发出读写请求 */

        submit_bh(rw, bh);

    }

    return;

 

sorry:

    /* Make sure we don't get infinite dirty retries.. */

    for (i = 0; i < nr; i++)

        mark_buffer_clean(bhs[ i ]);

}

 

参见end_buffer_io_sync解析

static void end_buffer_io_sync(struct buffer_head *bh, int uptodate)

{

    mark_buffer_uptodate(bh, uptodate);

    unlock_buffer(bh);

    put_bh(bh);

}

 

void submit_bh(int rw, struct buffer_head * bh)

{

/* 检查BH_Lock字段,调用submit_bh之前缓冲块必须上锁 */

if (!test_bit(BH_Lock, &bh->b_state))

        BUG();

 

/*

* 设置BH_Req字段,表示块已被请求,正在执行IO请求,

* 互斥其他进程请求该块

*/

    set_bit(BH_Req, &bh->b_state);

 

    /*

     * First step, 'identity mapping' - RAID or LVM might

     * further remap this.

     */

/*用虚拟设备标识符bh->b_dev 初始bh->b_rdev实际设备标识符 */

    bh->b_rdev = bh->b_dev;

 

/* 求逻辑块的物理扇区号*/

    bh->b_rsector = bh->b_blocknr * (bh->b_size >> 9);

 

/*  向块设备发出读写请求 */

    generic_make_request(rw, bh);

 

    switch (rw) {

        case WRITE:

            kstat.pgpgout++;

            break;

        default:

            kstat.pgpgin++;

            break;

    }

}

 

 

 

阅读(1368) | 评论(0) | 转发(0) |
0

上一篇:linux内存寻址解析

下一篇:ll_rw_block解析Ⅱ

给主人留下些什么吧!~~