塊設備(二)

塊設備驅動()

塊設備數據訪問流程:

generic_make_request () ------> __genenric_make_request() -----> q->make_request_fn(q, bio)

當有用戶需求來的時候,在通用塊層使用generic_make_request () 形成一個請求,然後調用

__genenric_make_request(bio)形成一個bio,再調用塊設備請求隊列的q->make_request_fn(q, bio),每個塊設備請求隊列request_queue都有一個函數指針,

struct request_queue {

.......................

make_request_fn  *make_request_fn ;

......................

}

struct request_queue *blk_init_queue(request_fn_proc *rfnspinlock *lock) {

return blk_init_queue_node (rfnlock-1)

}

blk_init_queue_node (request_fn_proc *rfnspinlock *lock) {

......................

blk_queue_make_request (q,  __make_request) ; //把等待隊列q和函數__make_request關聯,__make_request負責製造struct request,每個請求由struct request描述

.........................

}

void blk_queue_make_request (struct request_queue *qmake_request_fn  *mfn) {

.........................

q->make_request_fn = mfn ;

.........................

}

 make_request製造請求元素是Bio1struct Bio代表一次塊設備I/O請求,IO調度器可將連續的bio合併成一個請求struct request,對每個扇區的訪問就是一個biorequest把連續的訪問合在一起,通過__make_reques按照調度算法訪問磁盤合併bio,提高訪問效率。

struct bio {

secotor_t  bi_sector ; //要訪問的第一個扇區

unsigned int bi_size ; //以字節爲單位傳輸數據的大小

struct bio_vec *bi_io_vec ; //實際的vec列表

...........................

};

 struct  bio_vec {

struct page *bv_page ; //頁指針

unsigned int bv_len ; //傳輸的數據長度

unsigned int bv_offset ; //偏移量

}; //反應用戶信息,從磁盤讀取數據的存放位置,存放在哪一頁以及偏移

有的塊設備需要調度算法去支撐,比如磁盤的磁頭要移動,但是有的設備沒有這種移動,比如內存、U盤,就不要這種調度器,不用製作make_request,直接依次處理bio,這樣就不會形成register。要直接處理bio就不許內核提供的處理函數__make_request,自己實現請求隊列裏的__make_request_fn

自己實現:

request_queue_t *blk_alloc_queue (int gfp_mask) 分配一個請求隊列。

void blk_queue_make_request (request_queue_t *q,  make_request_fn *mfn)

把自己實現的bio請求處理函數mfn賦值給請求隊列q裏的成員q->make_request_fn = mfn

 

塊設備驅動程序(不使用IO調度器)
#define SIMP_BLKDEV_DEVICEMAJOR        COMPAQ_SMART2_MAJOR
#define SIMP_BLKDEV_DISKNAME        "simp_blkdev"
#define SIMP_BLKDEV_BYTES        (16*1024*1024)

static struct request_queue *simp_blkdev_queue;
static struct gendisk *simp_blkdev_disk;
unsigned char simp_blkdev_data[SIMP_BLKDEV_BYTES];

static int simp_blkdev_make_request(struct request_queue *q, struct bio *bio)
//在simple-blk.c傳給request_queue,從裏取出request處理,這裏傳給request和bio
{
        struct bio_vec *bvec;
        int i;
        void *dsk_mem;

        if ((bio->bi_sector << 9) + bio->bi_size > SIMP_BLKDEV_BYTES) {
  //訪問數據量是否超出磁盤範圍
                printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                        ": bad request: block=%llu, count=%u\n",
                        (unsigned long long)bio->bi_sector, bio->bi_size);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)//程序的兼容性
                bio_endio(bio, 0, -EIO);
#else
                bio_endio(bio, -EIO);
#endif
                return 0;
        }

        dsk_mem = simp_blkdev_data + (bio->bi_sector << 9);

  bio_for_each_segment(bvec, bio, i) {//bio由一個一個段構成,對於每個段要去進行相應處理
                void *iovec_mem;

                switch (bio_rw(bio)) {  //判斷bio方向
                case READ:
                case READA:
                        iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
  //kmap把頁指針轉換成地址加上偏移得到數據位於地址。
                        memcpy(iovec_mem, dsk_mem, bvec->bv_len);
                        kunmap(bvec->bv_page);
                        break;
                case WRITE:
                        iovec_mem = kmap(bvec->bv_page) + bvec->bv_offset;
                        memcpy(dsk_mem, iovec_mem, bvec->bv_len);
                        kunmap(bvec->bv_page);
                        break;
                default:
                        printk(KERN_ERR SIMP_BLKDEV_DISKNAME
                                ": unknown value of bio_rw: %lu\n",
                                bio_rw(bio));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
                        bio_endio(bio, 0, -EIO);
#else
                        bio_endio(bio, -EIO);
#endif
                        return 0;
                }
                dsk_mem += bvec->bv_len;
        }

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
        bio_endio(bio, bio->bi_size, 0);
#else
        bio_endio(bio, 0);
#endif
        return 0;
}//沒有request,直接拿到處理bio,繞過調度算法

struct block_device_operations simp_blkdev_fops = {
        .owner                = THIS_MODULE,
};

static int __init simp_blkdev_init(void)
{
        int ret;

        simp_blkdev_queue = blk_alloc_queue(GFP_KERNEL);//分配一個請求隊列
        if (!simp_blkdev_queue) {
                ret = -ENOMEM;
                goto err_alloc_queue;
        }
        blk_queue_make_request(simp_blkdev_queue, simp_blkdev_make_request);
  //綁定make_request和請求隊列,在simple-blk.c當有請求後,首先調用內核__make_request,然後調用自己提供的simp_blkdev_do_request,在本程序沒有使用__make_rwquest有bio時,直接交給simp_blkdev_make_request,自己要實現其他調度算法,把bio按照自己方法處理形成一個request。
        simp_blkdev_disk = alloc_disk(1);
        if (!simp_blkdev_disk) {
                ret = -ENOMEM;
                goto err_alloc_disk;
        }

        strcpy(simp_blkdev_disk->disk_name, SIMP_BLKDEV_DISKNAME);
        simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
        simp_blkdev_disk->first_minor = 0;
        simp_blkdev_disk->fops = &simp_blkdev_fops;
        simp_blkdev_disk->queue = simp_blkdev_queue;
        set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
        add_disk(simp_blkdev_disk);
  //填充gendisk並註冊到內核
        return 0;

err_alloc_disk:
        blk_cleanup_queue(simp_blkdev_queue);
err_alloc_queue:
        return ret;
}


static void __exit simp_blkdev_exit(void)
{
        del_gendisk(simp_blkdev_disk);
        put_disk(simp_blkdev_disk);
        blk_cleanup_queue(simp_blkdev_queue);
}

module_init(simp_blkdev_init);
module_exit(simp_blkdev_exit);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章