/* * 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. */ /*这个函数试图读取文件中的一个page大小的数据,最理想的情况下就是这个page大小 的数据都是在连续的物理磁盘上面的,然后函数只需要提交一个bio请求就可以获取 所有的数据,这个函数大部分工作在检查page上所有的物理块是否连续,检查的方法 就是调用文件系统提供的get_block函数,如果不连续,需要调用block_read_full_page 函数采用buffer 缓冲区的形式来逐个块获取数据*/ /* 1、调用get_block函数检查page中是不是所有的物理块都连续 2、如果连续调用mpage_bio_submit函数请求整个page的数据 3、如果不连续调用block_read_full_page逐个block读取 */ 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; const unsigned blkbits = inode->i_blkbits; const unsigned blocks_per_page = PAGE_CACHE_SIZE >> blkbits; 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; if (page_has_buffers(page)) goto confused; /* block_in_file 本page中的第一个block number last_block 本page中最后一个block 的大小 last_block_in_file 文件大小求出文件的最后一个block 大小*/ block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = block_in_file + nr_pages *blocks_per_page; last_block_in_file = (i_size_read(inode) + blocksize - 1) >> blkbits; /*last_block 最后等于本次对这个page操作的最后一个block大小*/ 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. */ nblocks = map_bh->b_size >> blkbits; /*对于普通情况mpage_readpage调用下,map_bh只是一个临时变量不会走到 下面的分支*/ 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; /*这个循环是比较关键的路径,理解这个函数至关重要 1、page_block从0开始循环,它表示在这个page内的block大小 2、调用get_block 函数查找对应逻辑块的物理块号是多少 3、如果遇到了文件空洞、page上的物理块不连续就会跳转到confused 4、将这个page中每个逻辑块对应的物理块都保存到临时的数组blocks[] 中*/ while (page_block < blocks_per_page) { map_bh->b_state = 0; map_bh->b_size = 0; if (block_in_file < last_block) { map_bh->b_size = (last_block - block_in_file) << blkbits; if (get_block(inode, block_in_file, map_bh, 0)) goto confused; *first_logical_block = block_in_file; } if (!buffer_mapped(map_bh)) { fully_mapped = 0; if (first_hole == blocks_per_page) first_hole = page_block; page_block++; block_in_file++; 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; } if (first_hole != blocks_per_page) goto confused; /* hole -> non-hole */ /* Contiguous blocks? */ if (page_block && blocks[page_block-1] != map_bh->b_blocknr - 1) goto confused; nblocks = map_bh->b_size >> blkbits; for (relative_block = 0; ; relative_block++) { if (relative_block == nblocks) { clear_buffer_mapped(map_bh); break; } else if (page_block == blocks_per_page) break; blocks[page_block] = map_bh->b_blocknr + relative_block; page_block++; block_in_file++; } bdev = map_bh->b_bdev; } /*如果发现文件中有洞,将整个page清0,因为文件洞的区域 物理层不会真的去磁盘上读取,必须在这里主动清零,否则 文件洞区域内容可能随机*/ if (first_hole != blocks_per_page) { zero_user_segment(page, first_hole << blkbits, PAGE_CACHE_SIZE); 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,目前分析的场景可以跳过去*/ if (bio && (*last_block_in_bio != blocks[0] - 1)) bio = mpage_bio_submit(READ, bio); alloc_new: if (bio == NULL) { /*重新分配一个bio结构体 blocks[0] << (blkbits - 9) 这个是page中第一个逻辑块的物理块号, 转换成物理扇区号*/ 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; } relative_block = block_in_file - *first_logical_block; nblocks = map_bh->b_size >> blkbits; if ((buffer_boundary(map_bh) && relative_block == nblocks) || (first_hole != blocks_per_page)) bio = mpage_bio_submit(READ, bio); else *last_block_in_bio = blocks[blocks_per_page - 1]; out: /*一切顺利,整个page中的物理块是相连的,返回一个bio*/ return bio; confused: if (bio) bio = mpage_bio_submit(READ, bio); /*page 中的物理块不相连,没有办法一个一个buffer去读取出来 */ if (!PageUptodate(page)) block_read_full_page(page, get_block); else unlock_page(page); goto out; } |