首页 > 编程学习 > Linux 块设备之bio结构体

Linux 块设备之bio结构体

发布时间:2022/1/17 12:25:45

当文件系统准备好数据之后,就会调用submit_bio提交一个IO请求,这里主要介绍bio数据管理和分配.

1. bio数据结构


这里列举重要的数据结构:

struct bio {

        sector_t                bi_sector;    /*磁盘起始扇区号 */

        struct block_device     *bi_bdev;/* bio操作的块设备 */

        unsigned long           bi_flags;       /* 状态标志位 */

        unsigned long           bi_rw;        /*读写 */

        unsigned short          bi_vcnt;        /*  bio_vec's 个数*/

        unsigned short          bi_idx;         /* 当前bvl_vec 数组的index*/

        unsigned int            bi_size;        /* 整个bio的大小:所有bi_io_vec->len之和 */

        bio_end_io_t            *bi_end_io;/* bio完成时调用*/

        void                    *bi_private;/*bio私有数据 */

        unsigned int            bi_max_vecs;    /* bio携带的最大bio_vec数量(实际使用的bio_vec由bi_vcnt表示) */

        atomic_t                bi_cnt;         /* bio引用计数*/

        struct bio_vec          *bi_io_vec;   /* bio_vec数组 */  

        struct bio_set          *bi_pool;/*bio_set维护了若干不同大小的bio slab */

        struct bio_vec          bi_inline_vecs[0];/* bio 内嵌的bio_vec*/

};
这里需要注意的是bi_io_vec和bi_inline_vecs两个变量:
bio需要携带的bio_vec数量不超过BIO_INLINE_VECS(4)时,bi_io_vec指向bi_inline_vecs,如果超过BIO_INLINE_VECS,
则从bio_vec专有的slab中分配,此时bi_inline_vecs的空间就被浪费了.

bio_vec表示一段连续的内存数据,最大为一个page
一般情况下,bio_vec与page一一对应. 所有bio_vec就组成 了一个bio携带的所有数据

struct bio_vec {

        struct page     *bv_page;/*数据所属的page */
        unsigned int    bv_len;/*数据大小 */
        unsigned int    bv_offset;/*数据在page内的偏移 */
};

2.bio的分配

在分析bio分配之前,先要弄清楚bio slab和bio_vec slab的初始化

2.1 bio slab初始化

内核定义了两个全局变量

static struct bio_slab *bio_slabs/*包含不同大小bio的slab */

#define BV(x) { .nr_vecs = x, .name = "biovec-"__stringify(x) }
/*包含大小1到BIO_MAX_PAGES个bio_vec的slab*/
static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = {

        BV(1), BV(4), BV(16), BV(64), BV(128), BV(BIO_MAX_PAGES),

};

static int __init init_bio(void)
{/*bio slab数组初始化 */bio_slab_max = 2; bio_slab_nr = 0; /*默认分配两个,后续会动态创建不同大小的bio slab */bio_slabs = kzalloc(bio_slab_max * sizeof(struct bio_slab), GFP_KERNEL);if (!bio_slabs)panic("bio: can't allocate bios\n");/*bio_vec slab 数组初始化 */biovec_init_slabs();/*创建bioset 内存池 */fs_bio_set = bioset_create(BIO_POOL_SIZE, 0);if (!fs_bio_set)panic("bio: can't allocate bios\n");return 0;
}

bio_vec slab初始化
 

static void __init biovec_init_slabs(void)
{int i;for (i = 0; i < BIOVEC_NR_POOLS; i++) {int size;struct biovec_slab *bvs = bvec_slabs + i;/*小于BIO_INLINE_VECS的直接使用bio内嵌的bio_vec */if (bvs->nr_vecs <= BIO_INLINE_VECS) {bvs->slab = NULL;continue;}/*创建bio_vec slab */size = bvs->nr_vecs * sizeof(struct bio_vec);bvs->slab = kmem_cache_create(bvs->name, size, 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);}
}

bio_set 初始化: 建立bio和bio_vec内存池

struct bio_set *bioset_create(unsigned int pool_size, unsigned int front_pad)
{unsigned int back_pad = BIO_INLINE_VECS * sizeof(struct bio_vec);struct bio_set *bs;bs = kzalloc(sizeof(*bs), GFP_KERNEL);if (!bs)return NULL;/*front_pad可以携带私有数据 */bs->front_pad = front_pad;spin_lock_init(&bs->rescue_lock);bio_list_init(&bs->rescue_list);INIT_WORK(&bs->rescue_work, bio_alloc_rescue);/*查找or创建带inline bio_vec的bio slab */bs->bio_slab = bio_find_or_create_slab(front_pad + back_pad);if (!bs->bio_slab) {kfree(bs);return NULL;}/*创建bio 内存池 */bs->bio_pool = mempool_create_slab_pool(pool_size, bs->bio_slab);if (!bs->bio_pool)goto bad;/*创建biovec 内存池 */bs->bvec_pool = biovec_create_pool(bs, pool_size);if (!bs->bvec_pool)goto bad;return bs;
}

从这里可以看出,一个bio内存由4部分组成 front_pad + sizeof(bio) + BIO_INLINE_VECS * sizeof(struct bio_vec) + sizeof(struct bio_aux)

bio_find_or_create_slab会查找符合大小的bio slab,或者重新分配bio slab

static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)
{
unsigned int sz = sizeof(struct bio) + sizeof(struct bio_aux) + extra_size;while (i < bio_slab_nr) {bslab = &bio_slabs[i];/*找到大小符合的bio slab */else if (bslab->slab_size == sz) {slab = bslab->slab;bslab->slab_ref++;break;}i++;}if (slab)goto out_unlock;/*重新分配bio slab数组 */if (bio_slab_nr == bio_slab_max && entry == -1) {new_bio_slab_max = bio_slab_max << 1;new_bio_slabs = krealloc(bio_slabs,new_bio_slab_max * sizeof(struct bio_slab),GFP_KERNEL);if (!new_bio_slabs)goto out_unlock;bio_slab_max = new_bio_slab_max;bio_slabs = new_bio_slabs;}/*创建新的bio slab */snprintf(bslab->name, sizeof(bslab->name), "bio-%d", entry);slab = kmem_cache_create(bslab->name, sz, ARCH_KMALLOC_MINALIGN,SLAB_HWCACHE_ALIGN, NULL);if (!slab)goto out_unlock;bslab->slab = slab;bslab->slab_ref = 1;bslab->slab_size = sz;return slab;
}

2.2 bio 分配

bio分配主要有以下几个函数
struct bio *bio_alloc(gfp_t gfp_mask, unsigned int nr_iovecs)/*默认使用fs_bio_set 相关内存池分配 */
struct bio *bio_kmalloc(gfp_t gfp_mask, unsigned int nr_iovecs)/*直接使用kmalloc来分配bio和bio_vec内存 */
struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)/*bio_alloc和bio_kmalloc最终调用的函数 */

分配bio和对应数量的bio_vec

 1. 从bio_set slab中分配或者直接用kmalloc分配

 2. nr_iovecs大于inline bio_vec个数时,需要重新分配bio_vec

 

struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs) 
{/*没有bio_set,直接用kmalloc分配 */if (!bs) {if (nr_iovecs > UIO_MAXIOV)return NULL;p = kmalloc(sizeof(struct bio) + sizeof(struct bio_aux) +nr_iovecs * sizeof(struct bio_vec),gfp_mask);front_pad = 0; inline_vecs = nr_iovecs;} else {/* 从bio slab中分配bio */p = mempool_alloc(bs->bio_pool, gfp_mask);if (!p && gfp_mask != saved_gfp) {punt_bios_to_rescuer(bs);gfp_mask = saved_gfp;p = mempool_alloc(bs->bio_pool, gfp_mask);}    front_pad = bs->front_pad;inline_vecs = BIO_INLINE_VECS;}    if (unlikely(!p))return NULL;/*初始化bio和bio_aux */bio = p + front_pad;bio_init(bio);bio_aux = p + front_pad +sizeof(struct bio) + (inline_vecs * sizeof(struct bio_vec));bio_init_aux(bio, bio_aux);/*如果需要的iovecs大于inline vec,则需要重新分配bio_vec */if (nr_iovecs > inline_vecs) {bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, bs->bvec_pool);if (!bvl && gfp_mask != saved_gfp) {punt_bios_to_rescuer(bs);gfp_mask = saved_gfp;bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx, bs->bvec_pool);}if (unlikely(!bvl))goto err_free;/*表示需要显示释放bio_vec */bio->bi_flags |= 1 << BIO_OWNS_VEC;} else if (nr_iovecs) {/*使用inline bio_vec */bvl = bio->bi_inline_vecs;}bio->bi_pool = bs;/*idx表示bio_vec的slab数组下标 */bio->bi_flags |= idx << BIO_POOL_OFFSET;bio->bi_max_vecs = nr_iovecs;bio->bi_io_vec = bvl;return bio;
}

3. bio其他操作函数

bio_endio:bio 操作完成回调函数
bio_split:    bio分隔成两个bio
bio_trim:    截取bio某段数据
bio_add_page:建立bio与page联系


本文链接:https://www.ngui.cc/51cto/show-317.html
Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000