ext4 節點映射塊

/*
 * The ext4_map_blocks() function tries to look up the requested blocks,
 * and returns if the blocks are already mapped.
 *
 * Otherwise it takes the write lock of the i_data_sem and allocate blocks
 * and store the allocated blocks in the result buffer head and mark it
 * mapped.
 *
 * If file type is extents based, it will call ext4_ext_map_blocks(),
 * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping
 * based files
 *
 * On success, it returns the number of blocks being mapped or allocated.  if
 * create==0 and the blocks are pre-allocated and unwritten, the resulting @map
 * is marked as unwritten. If the create == 1, it will mark @map as mapped.
 *
 * It returns 0 if plain look up failed (blocks have not been allocated), in
 * that case, @map is returned as unmapped but we still do fill map->m_len to
 * indicate the length of a hole starting at map->m_lblk.
 *
 * It returns the error in case of allocation failure.
 */
int ext4_map_blocks(handle_t *handle, struct inode *inode,
            struct ext4_map_blocks *map, int flags)
{
    struct extent_status es;
    int retval;
    int ret = 0;
#ifdef ES_AGGRESSIVE_TEST
    struct ext4_map_blocks orig_map;

    memcpy(&orig_map, map, sizeof(*map));
#endif

    map->m_flags = 0;
    ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u,"
          "logical block %lu\n", inode->i_ino, flags, map->m_len,
          (unsigned long) map->m_lblk);

    /*
     * ext4_map_blocks returns an int, and m_len is an unsigned int
     */
    if (unlikely(map->m_len > INT_MAX))
        map->m_len = INT_MAX;

    /* We can handle the block number less than EXT_MAX_BLOCKS */
    if (unlikely(map->m_lblk >= EXT_MAX_BLOCKS))
        return -EFSCORRUPTED;

    /* Lookup extent status tree firstly */
    if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) {
        if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) {
            map->m_pblk = ext4_es_pblock(&es) +
                    map->m_lblk - es.es_lblk;
            map->m_flags |= ext4_es_is_written(&es) ?
                    EXT4_MAP_MAPPED : EXT4_MAP_UNWRITTEN;
            retval = es.es_len - (map->m_lblk - es.es_lblk);
            if (retval > map->m_len)
                retval = map->m_len;
            map->m_len = retval;
        } else if (ext4_es_is_delayed(&es) || ext4_es_is_hole(&es)) {
            map->m_pblk = 0;
            retval = es.es_len - (map->m_lblk - es.es_lblk);
            if (retval > map->m_len)
                retval = map->m_len;
            map->m_len = retval;
            retval = 0;
        } else {
            BUG_ON(1);
        }
#ifdef ES_AGGRESSIVE_TEST
        ext4_map_blocks_es_recheck(handle, inode, map,
                       &orig_map, flags);
#endif
        goto found;
    }

    /*
     * Try to see if we can get the block without requesting a new
     * file system block.
     */
    down_read(&EXT4_I(inode)->i_data_sem);
    if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
        retval = ext4_ext_map_blocks(handle, inode, map, flags & EXT4_GET_BLOCKS_KEEP_SIZE);
    } else {
        retval = ext4_ind_map_blocks(handle, inode, map, flags & EXT4_GET_BLOCKS_KEEP_SIZE);
    }
    if (retval > 0) {
        unsigned int status;

        if (unlikely(retval != map->m_len)) {
            ext4_warning(inode->i_sb,
                     "ES len assertion failed for inode "
                     "%lu: retval %d != map->m_len %d",
                     inode->i_ino, retval, map->m_len);
            WARN_ON(1);
        }

        status = map->m_flags & EXT4_MAP_UNWRITTEN ?
                EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
        if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
            !(status & EXTENT_STATUS_WRITTEN) &&
            ext4_find_delalloc_range(inode, map->m_lblk,
                         map->m_lblk + map->m_len - 1))
            status |= EXTENT_STATUS_DELAYED;
        ret = ext4_es_insert_extent(inode, map->m_lblk,
                        map->m_len, map->m_pblk, status);
        if (ret < 0)
            retval = ret;
    }
    up_read((&EXT4_I(inode)->i_data_sem));

found:
    if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
        ret = check_block_validity(inode, map);
        if (ret != 0)
            return ret;
    }

    /* If it is only a block(s) look up */
    if ((flags & EXT4_GET_BLOCKS_CREATE) == 0)
        return retval;

    /*
     * Returns if the blocks have already allocated
     *
     * Note that if blocks have been preallocated
     * ext4_ext_get_block() returns the create = 0
     * with buffer head unmapped.
     */
    if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED)
        /*
         * If we need to convert extent to unwritten
         * we continue and do the actual work in
         * ext4_ext_map_blocks()
         */
        if (!(flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN))
            return retval;

    /*
     * Here we clear m_flags because after allocating an new extent,
     * it will be set again.
     */
    map->m_flags &= ~EXT4_MAP_FLAGS;

    /*
     * New blocks allocate and/or writing to unwritten extent
     * will possibly result in updating i_data, so we take
     * the write lock of i_data_sem, and call get_block()
     * with create == 1 flag.
     */
    down_write(&EXT4_I(inode)->i_data_sem);

    /*
     * We need to check for EXT4 here because migrate
     * could have changed the inode type in between
     */
    if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
        retval = ext4_ext_map_blocks(handle, inode, map, flags);
    } else {
        retval = ext4_ind_map_blocks(handle, inode, map, flags);

        if (retval > 0 && map->m_flags & EXT4_MAP_NEW) {
            /*
             * We allocated new blocks which will result in
             * i_data's format changing.  Force the migrate
             * to fail by clearing migrate flags
             */
            ext4_clear_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
        }

        /*
         * Update reserved blocks/metadata blocks after successful
         * block allocation which had been deferred till now. We don't
         * support fallocate for non extent files. So we can update
         * reserve space here.
         */
        if ((retval > 0) &&
            (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE))
            ext4_da_update_reserve_space(inode, retval, 1);
    }

    if (retval > 0) {
        unsigned int status;

        if (unlikely(retval != map->m_len)) {
            ext4_warning(inode->i_sb,
                     "ES len assertion failed for inode "
                     "%lu: retval %d != map->m_len %d",
                     inode->i_ino, retval, map->m_len);
            WARN_ON(1);
        }

        /*
         * We have to zeroout blocks before inserting them into extent
         * status tree. Otherwise someone could look them up there and
         * use them before they are really zeroed. We also have to
         * unmap metadata before zeroing as otherwise writeback can
         * overwrite zeros with stale data from block device.
         */
        if (flags & EXT4_GET_BLOCKS_ZERO &&
            map->m_flags & EXT4_MAP_MAPPED &&
            map->m_flags & EXT4_MAP_NEW) {
            ext4_lblk_t i;

            for (i = 0; i < map->m_len; i++) {
                unmap_underlying_metadata(inode->i_sb->s_bdev,
                              map->m_pblk + i);
            }
            ret = ext4_issue_zeroout(inode, map->m_lblk,
                         map->m_pblk, map->m_len);
            if (ret) {
                retval = ret;
                goto out_sem;
            }
        }

        /*
         * If the extent has been zeroed out, we don't need to update
         * extent status tree.
         */
        if ((flags & EXT4_GET_BLOCKS_PRE_IO) &&
            ext4_es_lookup_extent(inode, map->m_lblk, &es)) {
            if (ext4_es_is_written(&es))
                goto out_sem;
        }
        status = map->m_flags & EXT4_MAP_UNWRITTEN ?
                EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
        if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
            !(status & EXTENT_STATUS_WRITTEN) &&
            ext4_find_delalloc_range(inode, map->m_lblk,
                         map->m_lblk + map->m_len - 1))
            status |= EXTENT_STATUS_DELAYED;
        ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
                        map->m_pblk, status);
        if (ret < 0) {
            retval = ret;
            goto out_sem;
        }
    }

out_sem:
    up_write((&EXT4_I(inode)->i_data_sem));
    if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
        ret = check_block_validity(inode, map);
        if (ret != 0)
            return ret;

        /*
         * Inodes with freshly allocated blocks where contents will be
         * visible after transaction commit must be on transaction's
         * ordered data list.
         */
        if (map->m_flags & EXT4_MAP_NEW &&
            !(map->m_flags & EXT4_MAP_UNWRITTEN) &&
            !(flags & EXT4_GET_BLOCKS_ZERO) &&
            !IS_NOQUOTA(inode) &&
            ext4_should_order_data(inode)) {
            if (flags & EXT4_GET_BLOCKS_IO_SUBMIT)
                ret = ext4_jbd2_inode_add_wait(handle, inode);
            else
                ret = ext4_jbd2_inode_add_write(handle, inode);
            if (ret)
                return ret;
        }
    }
    return retval;
}


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