/*
 * The ext4_get_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_get_blocks(),
 * Otherwise, call with ext4_ind_get_blocks() to handle indirect mapping
 * based files
 *
 * On success, it returns the number of blocks being mapped or allocate.
 * if create==0 and the blocks are pre-allocated and uninitialized block,
 * the result buffer head is unmapped. If the create ==1, it will make sure
 * the buffer head is mapped.
 *
 * It returns 0 if plain look up failed (blocks have not been allocated), in
 * that casem, buffer head is unmapped
 *
 * It returns the error in case of allocation failure.
 */
int ext4_get_blocks(handle_t *handle, struct inode *inode, sector_t block,
            unsigned int max_blocks, struct buffer_head *bh,
            int flags)
{
    int retval;

    clear_buffer_mapped(bh);
    clear_buffer_unwritten(bh);

    /*
     * 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_I(inode)->i_flags & EXT4_EXTENTS_FL) {
        retval =  ext4_ext_get_blocks(handle, inode, block, max_blocks,
                bh, 0);
    } else {
        retval = ext4_ind_get_blocks(handle, inode, block, max_blocks,
                         bh, 0);
    }
    up_read((&EXT4_I(inode)->i_data_sem));

    if (retval > 0 && buffer_mapped(bh)) {
        int ret = check_block_validity(inode, block,
                           bh->b_blocknr, retval);
        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 th create = 0
     * with buffer head unmapped.
     */
    if (retval > 0 && buffer_mapped(bh))
        return retval;

    /*
     * When we call get_blocks without the create flag, the
     * BH_Unwritten flag could have gotten set if the blocks
     * requested were part of a uninitialized extent.  We need to
     * clear this flag now that we are committed to convert all or
     * part of the uninitialized extent to be an initialized
     * extent.  This is because we need to avoid the combination
     * of BH_Unwritten and BH_Mapped flags being simultaneously
     * set on the buffer_head.
     */
    clear_buffer_unwritten(bh);

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

    /*
     * if the caller is from delayed allocation writeout path
     * we have already reserved fs blocks for allocation
     * let the underlying get_block() function know to
     * avoid double accounting
     */
    if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
        EXT4_I(inode)->i_delalloc_reserved_flag = 1;
    /*
     * We need to check for EXT4 here because migrate
     * could have changed the inode type in between
     */
    if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
        retval =  ext4_ext_get_blocks(handle, inode, block, max_blocks,
                          bh, flags);
    } else {
        retval = ext4_ind_get_blocks(handle, inode, block,
                         max_blocks, bh, flags);

        if (retval > 0 && buffer_new(bh)) {
            /*
             * We allocated new blocks which will result in
             * i_data's format changing.  Force the migrate
             * to fail by clearing migrate flags
             */
            EXT4_I(inode)->i_flags = EXT4_I(inode)->i_flags &
                            ~EXT4_EXT_MIGRATE;
        }
    }

    if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
        EXT4_I(inode)->i_delalloc_reserved_flag = 0;

    /*
     * Update reserved blocks/metadata blocks after successful
     * block allocation which had been deferred till now.
     */
    if ((retval > 0) && (flags & EXT4_GET_BLOCKS_UPDATE_RESERVE_SPACE))
        ext4_da_update_reserve_space(inode, retval);

    up_write((&EXT4_I(inode)->i_data_sem));
    if (retval > 0 && buffer_mapped(bh)) {
        int ret = check_block_validity(inode, block,
                           bh->b_blocknr, retval);
        if (ret != 0)
            return ret;
    }
    return retval;
}

/* Maximum number of blocks we map for direct IO at once. */
#define DIO_MAX_BLOCKS 4096

int ext4_get_block(struct inode *inode, sector_t iblock,
           struct buffer_head *bh_result, int create)
{
    handle_t *handle = ext4_journal_current_handle();
    int ret = 0, started = 0;
    unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
    int dio_credits;

    if (create && !handle) {
        /* Direct IO write... */
        if (max_blocks > DIO_MAX_BLOCKS)
            max_blocks = DIO_MAX_BLOCKS;
        dio_credits = ext4_chunk_trans_blocks(inode, max_blocks);
        handle = ext4_journal_start(inode, dio_credits);
        if (IS_ERR(handle)) {
            ret = PTR_ERR(handle);
            goto out;
        }
        started = 1;
    }

    ret = ext4_get_blocks(handle, inode, iblock, max_blocks, bh_result,
                  create ? EXT4_GET_BLOCKS_CREATE : 0);
    if (ret > 0) {
        bh_result->b_size = (ret << inode->i_blkbits);
        ret = 0;
    }
    if (started)
        ext4_journal_stop(handle);
out:
    return ret;
}

/*
 * `handle' can be NULL if create is zero
 */
struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
                ext4_lblk_t block, int create, int *errp)
{
    struct buffer_head dummy;
    int fatal = 0, err;
    int flags = 0;

    J_ASSERT(handle != NULL || create == 0);

    dummy.b_state = 0;
    dummy.b_blocknr = -1000;
    buffer_trace_init(&dummy.b_history);
    if (create)
        flags |= EXT4_GET_BLOCKS_CREATE;
    err = ext4_get_blocks(handle, inode, block, 1, &dummy, flags);
    /*
     * ext4_get_blocks() returns number of blocks mapped. 0 in
     * case of a HOLE.
     */
    if (err > 0) {
        if (err > 1)
            WARN_ON(1);
        err = 0;
    }
    *errp = err;
    if (!err && buffer_mapped(&dummy)) {
        struct buffer_head *bh;
        bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
        if (!bh) {
            *errp = -EIO;
            goto err;
        }
        if (buffer_new(&dummy)) {
            J_ASSERT(create != 0);
            J_ASSERT(handle != NULL);

            /*
             * Now that we do not always journal data, we should
             * keep in mind whether this should always journal the
             * new buffer as metadata.  For now, regular file
             * writes use ext4_get_block instead, so it's not a
             * problem.
             */
            lock_buffer(bh);
            BUFFER_TRACE(bh, "call get_create_access");
            fatal = ext4_journal_get_create_access(handle, bh);
            if (!fatal && !buffer_uptodate(bh)) {
                memset(bh->b_data, 0, inode->i_sb->s_blocksize);
                set_buffer_uptodate(bh);
            }
            unlock_buffer(bh);
            BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
            err = ext4_handle_dirty_metadata(handle, inode, bh);
            if (!fatal)
                fatal = err;
        } else {
            BUFFER_TRACE(bh, "not a new buffer");
        }
        if (fatal) {
            *errp = fatal;
            brelse(bh);
            bh = NULL;
        }
        return bh;
    }
err:
    return NULL;
}

Reply via email to