天天看點

do_mpage_readpage函數詳細分析

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 = (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; 

     if  (last_block > last_block_in_file) 

        last_block = last_block_in_file; 

    page_block =  0 ; 

    nblocks = map_bh->b_size >> blkbits; 

     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; 

    } 

    map_bh->b_page = page; 

     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 ; 

        } 

         if  (buffer_uptodate(map_bh)) 

        { 

            map_buffer_to_page(page, map_bh, page_block); 

             goto  confused; 

        } 

         if  (first_hole != blocks_per_page) 

             goto  confused;          

         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; 

    } 

     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); 

    } 

     if  (bio && (*last_block_in_bio != blocks[ 0 ] -  1 )) 

        bio = mpage_bio_submit(READ, bio); 

alloc_new: 

     if  (bio == NULL) 

    { 

        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: 

     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; 

繼續閱讀