2010-3-25 do_mpage_readpage 代码注释

来源:互联网 发布:小照域名授权源码 编辑:程序博客网 时间:2024/06/12 21:50

/*
 * This is the worker routine which does all the work of mapping the disk
 * blocks and constructs largest possible bios, submits them for IO if the
 * blocks are not contiguous on the disk.
 *
 * We pass a buffer_head back and forth and use its buffer_mapped() flag to
 * represent the validity of its disk mapping and to decide when to do the next
 * get_block() call.
 */
static struct bio *
do_mpage_readpage(struct bio *bio, struct page *page, unsigned nr_pages,
        sector_t *last_block_in_bio, struct buffer_head *map_bh,
        unsigned long *first_logical_block, get_block_t get_block)
{
    struct inode *inode = page->mapping->host;
    //将块的位数赋给blkbits
    const unsigned blkbits = inode->i_blkbits;
    //计算一个页面中的数据块数目
    const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits;
    //计算block 的大小
    const unsigned blocksize = 1 << blkbits;
    sector_t block_in_file;
    sector_t last_block;
    sector_t last_block_in_file;
    sector_t blocks[MAX_BUF_PER_PAGE];
    unsigned page_block;
    unsigned first_hole = blocks_per_page;
    struct block_device *bdev = NULL;
    int length;
    int fully_mapped = 1;
    unsigned nblocks;
    unsigned relative_block;


    //如果是一个缓存区页(PG_private被置位),跳转到confused
    //据目前的理解,PG_private置位意味着page中的缓冲区段在磁盘上不连续
    if (page_has_buffers(page))
        goto confused;

    //通过页的索引获得相对于文件头的首个块号
    block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits);
    //计算要传输的最后一个块号,这里nr_pages是1,即该页中的最后一块
    last_block = block_in_file + nr_pages * blocks_per_page;
    //计算文件的最后一块块号
    last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits;
    //应该传输的最后一块的块号
    if (last_block > last_block_in_file)
        last_block = last_block_in_file;
    page_block = 0;

    /*
     * Map blocks using the result from the previous get_blocks call first.
     */
    //nblock使用来干什么的啊?是指一个buffer_head所能指向的块数吗?
    //这么说来,buffer_head中的b_size所指的块大小和inode中的块大小不一样?
     nblocks = map_bh->b_size >> blkbits;
     
    //如果该缓冲区首部已经映射到磁盘(b_bdev、b_blocknr已经赋值),且……
    //注:首次传入的map_bh是经过clear_buffer_mapped()处理过的   
    if (buffer_mapped(map_bh) && block_in_file > *first_logical_block &&
            block_in_file < (*first_logical_block + nblocks)) {
           
        unsigned map_offset = block_in_file - *first_logical_block;
        unsigned last = nblocks - map_offset;

        for (relative_block = 0; ; relative_block++) {
            if (relative_block == last) {
                clear_buffer_mapped(map_bh);
                break;
            }
            if (page_block == blocks_per_page)
                break;
            blocks[page_block] = map_bh->b_blocknr + map_offset +
                        relative_block;
            page_block++;
            block_in_file++;
        }
        bdev = map_bh->b_bdev;
    }

    /*
     * Then do more get_blocks calls until we are done with this page.
     */
    map_bh->b_page = page;
   
    //page_block初值为0,block_per_page是一页中的块数,即循环次数为页中块数,即对页中所有块进行处理
    //即:一次调用get_block()获得的连续的磁盘的块数不够一页的话,就反复调用
    while (page_block < blocks_per_page) {
        map_bh->b_state = 0;
        map_bh->b_size = 0;

        if (block_in_file < last_block) {
            //设置buffer_head中的size,这个buffer_head指向连续的块的块数,是指期望获得的最大连续块数?
            //每次计算这个,是因为要反复使用这个结构来获取b_blocknr字段
            map_bh->b_size = (last_block-block_in_file) << blkbits;
           
            //get_block()函数将会buffer_head的映射和size的赋值
            if (get_block(inode, block_in_file, map_bh, 0))
                goto confused;
            *first_logical_block = block_in_file;
        }

        //??map_bh没有映射,这应该就对应文件洞了
        if (!buffer_mapped(map_bh)) {
            //设置页全部映射到磁盘的标志为0
            fully_mapped = 0;
            //将文件洞记录下来
            if (first_hole == blocks_per_page)
                first_hole = page_block;
            page_block++;
            block_in_file++;
            clear_buffer_mapped(map_bh);
            //继续处理下一个块
            continue;
        }

        /* some filesystems will copy data into the page during
         * the get_block call, in which case we don't want to
         * read it again.  map_buffer_to_page copies the data
         * we just collected from get_block into the page's buffers
         * so readpage doesn't have to repeat the get_block call
         */
        if (buffer_uptodate(map_bh)) {
            map_buffer_to_page(page, map_bh, page_block);
            goto confused;
        }
   
        //走到这步来了说明遇到了一个文件洞,但是之后的块又映射了,这时应该将遇到洞以前的块处理掉,故跳至confused
        if (first_hole != blocks_per_page)
            goto confused;        /* hole -> non-hole */

        /* Contiguous blocks? */
        //通过比较blocks数组中的上一个元素中的值和这次获得的(b_blocknr-1)比较,判断是否连续
        if (page_block && blocks[page_block-1] != map_bh->b_blocknr-1)
            goto confused;
        //计算出这个buffer_head指向的连续的块的块数
        nblocks = map_bh->b_size >> blkbits;
       
        //这个循环用来获取 块在磁盘上连续的页 中所有块 在磁盘中的块号
        //循环的结束条件是buffer_head所指向的块数减一至0 或 page中所有块在磁盘中的块号都以得出
        for (relative_block = 0; ; relative_block++) {
            if (relative_block == nblocks) {
                clear_buffer_mapped(map_bh);
                break;
            } else if (page_block == blocks_per_page)
                break;
            //为page中连续的块计算在磁盘中的编号,放入blocks数组中
            blocks[page_block] = map_bh->b_blocknr+relative_block;
            page_block++;
            block_in_file++;
        }
        bdev = map_bh->b_bdev;
    }

    if (first_hole != blocks_per_page) {
        char *kaddr = kmap_atomic(page, KM_USER0);
        memset(kaddr + (first_hole << blkbits), 0,
                PAGE_CACHE_SIZE - (first_hole << blkbits));
        flush_dcache_page(page);
        kunmap_atomic(kaddr, KM_USER0);
        if (first_hole == 0) {
            SetPageUptodate(page);
            unlock_page(page);
            goto out;
        }
    } else if (fully_mapped) {
        SetPageMappedToDisk(page);
    }

    /*
     * This page will go to BIO.  Do we need to send this BIO off first?
     */
    //首次调用时bio为NULL
    //如果bio不为空,而其中最后一个block与现在的不连续,则提交以前的bio
    if (bio && (*last_block_in_bio != blocks[0] - 1))
        bio = mpage_bio_submit(READ, bio);

alloc_new:
    if (bio == NULL) {
        //通过mpage_alloc创建新的bio
        // 2**9即512,blocks[0] << (blkbits - 9)得出首个扇区号
        //min_t(int, nr_pages, bio_get_nr_vecs(bdev))即,这个bio中的iovec个数
        bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
                  min_t(int, nr_pages, bio_get_nr_vecs(bdev)),
                GFP_KERNEL);
        if (bio == NULL)
            goto confused;
    }

    length = first_hole << blkbits;
    if (bio_add_page(bio, page, length, 0) < length) {
        bio = mpage_bio_submit(READ, bio);
        goto alloc_new;
    }

    if (buffer_boundary(map_bh) || (first_hole != blocks_per_page))
        bio = mpage_bio_submit(READ, bio);
    else
        *last_block_in_bio = blocks[blocks_per_page - 1];
out:
    return bio;

confused:
    if (bio)
        bio = mpage_bio_submit(READ, bio);
    if (!PageUptodate(page))
            block_read_full_page(page, get_block);
    else
        unlock_page(page);
    goto out;
}

原创粉丝点击