X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fgfs2%2Fbmap.c;h=0db0cd92a38d92f7dcf882c2d942b037c632da17;hb=1a0eae8848cde6e0734360f6456496c995ee1e23;hp=f570d8caef6893ca7db83eb760469c82db2ed22b;hpb=08bc2dbc7327e89b9d5b9c8ef9401d1df2622fca;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index f570d8c..0db0cd9 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1,46 +1,44 @@ /* * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. - * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU General Public License v.2. + * of the GNU General Public License version 2. */ -#include #include #include #include #include #include #include -#include #include "gfs2.h" -#include "lm_interface.h" #include "incore.h" #include "bmap.h" #include "glock.h" #include "inode.h" #include "meta_io.h" -#include "page.h" #include "quota.h" #include "rgrp.h" #include "trans.h" #include "dir.h" #include "util.h" +#include "trace_gfs2.h" /* This doesn't need to be that large as max 64 bit pointers in a 4k * block is 512, so __u16 is fine for that. It saves stack space to * keep it small. */ struct metapath { + struct buffer_head *mp_bh[GFS2_MAX_META_HEIGHT]; __u16 mp_list[GFS2_MAX_META_HEIGHT]; }; typedef int (*block_call_t) (struct gfs2_inode *ip, struct buffer_head *dibh, - struct buffer_head *bh, uint64_t *top, - uint64_t *bottom, unsigned int height, + struct buffer_head *bh, __be64 *top, + __be64 *bottom, unsigned int height, void *data); struct strip_mine { @@ -49,35 +47,65 @@ struct strip_mine { }; /** - * @gfs2_unstuffer_sync - Synchronously unstuff a dinode - * @ip: - * @dibh: - * @block: - * @private: - * - * Cheat and use a metadata buffer instead of a data page. + * gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page + * @ip: the inode + * @dibh: the dinode buffer + * @block: the block number that was allocated + * @private: any locked page held by the caller process * * Returns: errno */ -#if 0 -int gfs2_unstuffer_sync(struct gfs2_inode *ip, struct buffer_head *dibh, - uint64_t block, void *private) + +static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, + u64 block, struct page *page) { + struct inode *inode = &ip->i_inode; struct buffer_head *bh; - int error; + int release = 0; - bh = gfs2_meta_new(ip->i_gl, block); + if (!page || page->index) { + page = grab_cache_page(inode->i_mapping, 0); + if (!page) + return -ENOMEM; + release = 1; + } - gfs2_buffer_copy_tail(bh, 0, dibh, sizeof(struct gfs2_dinode)); + if (!PageUptodate(page)) { + void *kaddr = kmap(page); + u64 dsize = i_size_read(inode); + + if (dsize > (dibh->b_size - sizeof(struct gfs2_dinode))) + dsize = dibh->b_size - sizeof(struct gfs2_dinode); - set_buffer_dirty(bh); - error = sync_dirty_buffer(bh); + memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize); + memset(kaddr + dsize, 0, PAGE_CACHE_SIZE - dsize); + kunmap(page); - brelse(bh); + SetPageUptodate(page); + } - return error; + if (!page_has_buffers(page)) + create_empty_buffers(page, 1 << inode->i_blkbits, + (1 << BH_Uptodate)); + + bh = page_buffers(page); + + if (!buffer_mapped(bh)) + map_bh(bh, inode->i_sb, block); + + set_buffer_uptodate(bh); + if (!gfs2_is_jdata(ip)) + mark_buffer_dirty(bh); + if (!gfs2_is_writeback(ip)) + gfs2_trans_add_bh(ip->i_gl, bh, 0); + + if (release) { + unlock_page(page); + page_cache_release(page); + } + + return 0; } -#endif /* 0 */ /** * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big @@ -91,11 +119,11 @@ int gfs2_unstuffer_sync(struct gfs2_inode *ip, struct buffer_head *dibh, * Returns: errno */ -int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer, - void *private) +int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page) { struct buffer_head *bh, *dibh; - uint64_t block = 0; + struct gfs2_dinode *di; + u64 block = 0; int isdir = gfs2_is_dir(ip); int error; @@ -104,25 +132,25 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer, error = gfs2_meta_inode_buffer(ip, &dibh); if (error) goto out; - - if (ip->i_di.di_size) { + + if (ip->i_disksize) { /* Get a free block, fill it with the stuffed data, and write it out to disk */ + unsigned int n = 1; + error = gfs2_alloc_block(ip, &block, &n); + if (error) + goto out_brelse; if (isdir) { - block = gfs2_alloc_meta(ip); - + gfs2_trans_add_unrevoke(GFS2_SB(&ip->i_inode), block, 1); error = gfs2_dir_get_new_buffer(ip, block, &bh); if (error) goto out_brelse; - gfs2_buffer_copy_tail(bh, - sizeof(struct gfs2_meta_header), + gfs2_buffer_copy_tail(bh, sizeof(struct gfs2_meta_header), dibh, sizeof(struct gfs2_dinode)); brelse(bh); } else { - block = gfs2_alloc_data(ip); - - error = unstuffer(ip, dibh, block, private); + error = gfs2_unstuffer_page(ip, dibh, block, page); if (error) goto out_brelse; } @@ -131,143 +159,32 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer, /* Set up the pointer to the new block */ gfs2_trans_add_bh(ip->i_gl, dibh, 1); - + di = (struct gfs2_dinode *)dibh->b_data; gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - if (ip->i_di.di_size) { - *(uint64_t *)(dibh->b_data + sizeof(struct gfs2_dinode)) = - cpu_to_be64(block); - ip->i_di.di_blocks++; + if (ip->i_disksize) { + *(__be64 *)(di + 1) = cpu_to_be64(block); + gfs2_add_inode_blocks(&ip->i_inode, 1); + di->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode)); } - ip->i_di.di_height = 1; + ip->i_height = 1; + di->di_height = cpu_to_be16(1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - - out_brelse: +out_brelse: brelse(dibh); - - out: +out: up_write(&ip->i_rw_mutex); - return error; } -/** - * calc_tree_height - Calculate the height of a metadata tree - * @ip: The GFS2 inode - * @size: The proposed size of the file - * - * Work out how tall a metadata tree needs to be in order to accommodate a - * file of a particular size. If size is less than the current size of - * the inode, then the current size of the inode is used instead of the - * supplied one. - * - * Returns: the height the tree should be - */ - -static unsigned int calc_tree_height(struct gfs2_inode *ip, uint64_t size) -{ - struct gfs2_sbd *sdp = ip->i_sbd; - uint64_t *arr; - unsigned int max, height; - - if (ip->i_di.di_size > size) - size = ip->i_di.di_size; - - if (gfs2_is_dir(ip)) { - arr = sdp->sd_jheightsize; - max = sdp->sd_max_jheight; - } else { - arr = sdp->sd_heightsize; - max = sdp->sd_max_height; - } - - for (height = 0; height < max; height++) - if (arr[height] >= size) - break; - - return height; -} - -/** - * build_height - Build a metadata tree of the requested height - * @ip: The GFS2 inode - * @height: The height to build to - * - * This routine makes sure that the metadata tree is tall enough to hold - * "size" bytes of data. - * - * Returns: errno - */ - -static int build_height(struct gfs2_inode *ip, int height) -{ - struct gfs2_sbd *sdp = ip->i_sbd; - struct buffer_head *bh, *dibh; - uint64_t block = 0, *bp; - unsigned int x; - int new_block; - int error; - - while (ip->i_di.di_height < height) { - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - return error; - - new_block = 0; - bp = (uint64_t *)(dibh->b_data + sizeof(struct gfs2_dinode)); - for (x = 0; x < sdp->sd_diptrs; x++, bp++) - if (*bp) { - new_block = 1; - break; - } - - if (new_block) { - /* Get a new block, fill it with the old direct - pointers, and write it out */ - - block = gfs2_alloc_meta(ip); - - bh = gfs2_meta_new(ip->i_gl, block); - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_metatype_set(bh, - GFS2_METATYPE_IN, - GFS2_FORMAT_IN); - gfs2_buffer_copy_tail(bh, - sizeof(struct gfs2_meta_header), - dibh, sizeof(struct gfs2_dinode)); - - brelse(bh); - } - - /* Set up the new direct pointer and write it out to disk */ - - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); - - if (new_block) { - *(uint64_t *)(dibh->b_data + - sizeof(struct gfs2_dinode)) = - cpu_to_be64(block); - ip->i_di.di_blocks++; - } - - ip->i_di.di_height++; - - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); - } - - return 0; -} /** * find_metapath - Find path through the metadata tree - * @ip: The inode pointer + * @sdp: The superblock * @mp: The metapath to return the result in * @block: The disk block to look up + * @height: The pre-calculated height of the metadata tree * * This routine returns a struct metapath structure that defines a path * through the metadata of inode "ip" to get to block "block". @@ -322,21 +239,25 @@ static int build_height(struct gfs2_inode *ip, int height) * */ -static void find_metapath(struct gfs2_inode *ip, uint64_t block, - struct metapath *mp) +static void find_metapath(const struct gfs2_sbd *sdp, u64 block, + struct metapath *mp, unsigned int height) { - struct gfs2_sbd *sdp = ip->i_sbd; - uint64_t b = block; unsigned int i; - for (i = ip->i_di.di_height; i--;) - mp->mp_list[i] = (__u16)do_div(b, sdp->sd_inptrs); + for (i = height; i--;) + mp->mp_list[i] = do_div(block, sdp->sd_inptrs); + +} +static inline unsigned int metapath_branch_start(const struct metapath *mp) +{ + if (mp->mp_list[0] == 0) + return 2; + return 1; } /** * metapointer - Return pointer to start of metadata in a buffer - * @bh: The buffer * @height: The metadata height (0 = dinode) * @mp: The metapath * @@ -345,174 +266,406 @@ static void find_metapath(struct gfs2_inode *ip, uint64_t block, * metadata tree. */ -static inline uint64_t *metapointer(struct buffer_head *bh, - unsigned int height, struct metapath *mp) +static inline __be64 *metapointer(unsigned int height, const struct metapath *mp) { + struct buffer_head *bh = mp->mp_bh[height]; unsigned int head_size = (height > 0) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode); - - return ((uint64_t *)(bh->b_data + head_size)) + mp->mp_list[height]; + return ((__be64 *)(bh->b_data + head_size)) + mp->mp_list[height]; } /** - * lookup_block - Get the next metadata block in metadata tree - * @ip: The GFS2 inode - * @bh: Buffer containing the pointers to metadata blocks - * @height: The height of the tree (0 = dinode) + * lookup_metapath - Walk the metadata tree to a specific point + * @ip: The inode * @mp: The metapath - * @create: Non-zero if we may create a new meatdata block - * @new: Used to indicate if we did create a new metadata block - * @block: the returned disk block number * - * Given a metatree, complete to a particular height, checks to see if the next - * height of the tree exists. If not the next height of the tree is created. - * The block number of the next height of the metadata tree is returned. + * Assumes that the inode's buffer has already been looked up and + * hooked onto mp->mp_bh[0] and that the metapath has been initialised + * by find_metapath(). * + * If this function encounters part of the tree which has not been + * allocated, it returns the current height of the tree at the point + * at which it found the unallocated block. Blocks which are found are + * added to the mp->mp_bh[] list. + * + * Returns: error or height of metadata tree */ -static void lookup_block(struct gfs2_inode *ip, struct buffer_head *bh, - unsigned int height, struct metapath *mp, int create, - int *new, uint64_t *block) +static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp) { - uint64_t *ptr = metapointer(bh, height, mp); + unsigned int end_of_metadata = ip->i_height - 1; + unsigned int x; + __be64 *ptr; + u64 dblock; + int ret; - if (*ptr) { - *block = be64_to_cpu(*ptr); - return; + for (x = 0; x < end_of_metadata; x++) { + ptr = metapointer(x, mp); + dblock = be64_to_cpu(*ptr); + if (!dblock) + return x + 1; + + ret = gfs2_meta_indirect_buffer(ip, x+1, dblock, 0, &mp->mp_bh[x+1]); + if (ret) + return ret; } - *block = 0; - - if (!create) - return; - - if (height == ip->i_di.di_height - 1 && - !gfs2_is_dir(ip)) - *block = gfs2_alloc_data(ip); - else - *block = gfs2_alloc_meta(ip); - - gfs2_trans_add_bh(ip->i_gl, bh, 1); + return ip->i_height; +} - *ptr = cpu_to_be64(*block); - ip->i_di.di_blocks++; +static inline void release_metapath(struct metapath *mp) +{ + int i; - *new = 1; + for (i = 0; i < GFS2_MAX_META_HEIGHT; i++) { + if (mp->mp_bh[i] == NULL) + break; + brelse(mp->mp_bh[i]); + } } /** - * gfs2_block_map - Map a block from an inode to a disk block - * @ip: The GFS2 inode - * @lblock: The logical block number - * @new: Value/Result argument (1 = may create/did create new blocks) - * @dblock: the disk block number of the start of an extent - * @extlen: the size of the extent + * gfs2_extent_length - Returns length of an extent of blocks + * @start: Start of the buffer + * @len: Length of the buffer in bytes + * @ptr: Current position in the buffer + * @limit: Max extent length to return (0 = unlimited) + * @eob: Set to 1 if we hit "end of block" * - * Find the block number on the current device which corresponds to an - * inode's block. If the block had to be created, "new" will be set. + * If the first block is zero (unallocated) it will return the number of + * unallocated blocks in the extent, otherwise it will return the number + * of contiguous blocks in the extent. * - * Returns: errno + * Returns: The length of the extent (minimum of one block) */ -int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new, - uint64_t *dblock, uint32_t *extlen) +static inline unsigned int gfs2_extent_length(void *start, unsigned int len, __be64 *ptr, unsigned limit, int *eob) { - struct gfs2_sbd *sdp = ip->i_sbd; - struct buffer_head *bh; - struct metapath mp; - int create = *new; - unsigned int bsize; - unsigned int height; - unsigned int end_of_metadata; - unsigned int x; - int error = 0; - - *new = 0; - *dblock = 0; - if (extlen) - *extlen = 0; + const __be64 *end = (start + len); + const __be64 *first = ptr; + u64 d = be64_to_cpu(*ptr); + + *eob = 0; + do { + ptr++; + if (ptr >= end) + break; + if (limit && --limit == 0) + break; + if (d) + d++; + } while(be64_to_cpu(*ptr) == d); + if (ptr >= end) + *eob = 1; + return (ptr - first); +} +static inline void bmap_lock(struct gfs2_inode *ip, int create) +{ if (create) down_write(&ip->i_rw_mutex); else down_read(&ip->i_rw_mutex); +} - if (gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip))) - goto out; - - bsize = (gfs2_is_dir(ip)) ? sdp->sd_jbsize : sdp->sd_sb.sb_bsize; +static inline void bmap_unlock(struct gfs2_inode *ip, int create) +{ + if (create) + up_write(&ip->i_rw_mutex); + else + up_read(&ip->i_rw_mutex); +} - height = calc_tree_height(ip, (lblock + 1) * bsize); - if (ip->i_di.di_height < height) { - if (!create) - goto out; +static inline __be64 *gfs2_indirect_init(struct metapath *mp, + struct gfs2_glock *gl, unsigned int i, + unsigned offset, u64 bn) +{ + __be64 *ptr = (__be64 *)(mp->mp_bh[i - 1]->b_data + + ((i > 1) ? sizeof(struct gfs2_meta_header) : + sizeof(struct gfs2_dinode))); + BUG_ON(i < 1); + BUG_ON(mp->mp_bh[i] != NULL); + mp->mp_bh[i] = gfs2_meta_new(gl, bn); + gfs2_trans_add_bh(gl, mp->mp_bh[i], 1); + gfs2_metatype_set(mp->mp_bh[i], GFS2_METATYPE_IN, GFS2_FORMAT_IN); + gfs2_buffer_clear_tail(mp->mp_bh[i], sizeof(struct gfs2_meta_header)); + ptr += offset; + *ptr = cpu_to_be64(bn); + return ptr; +} - error = build_height(ip, height); - if (error) - goto out; - } +enum alloc_state { + ALLOC_DATA = 0, + ALLOC_GROW_DEPTH = 1, + ALLOC_GROW_HEIGHT = 2, + /* ALLOC_UNSTUFF = 3, TBD and rather complicated */ +}; - find_metapath(ip, lblock, &mp); - end_of_metadata = ip->i_di.di_height - 1; +/** + * gfs2_bmap_alloc - Build a metadata tree of the requested height + * @inode: The GFS2 inode + * @lblock: The logical starting block of the extent + * @bh_map: This is used to return the mapping details + * @mp: The metapath + * @sheight: The starting height (i.e. whats already mapped) + * @height: The height to build to + * @maxlen: The max number of data blocks to alloc + * + * In this routine we may have to alloc: + * i) Indirect blocks to grow the metadata tree height + * ii) Indirect blocks to fill in lower part of the metadata tree + * iii) Data blocks + * + * The function is in two parts. The first part works out the total + * number of blocks which we need. The second part does the actual + * allocation asking for an extent at a time (if enough contiguous free + * blocks are available, there will only be one request per bmap call) + * and uses the state machine to initialise the blocks in order. + * + * Returns: errno on error + */ - error = gfs2_meta_inode_buffer(ip, &bh); - if (error) - goto out; +static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, + struct buffer_head *bh_map, struct metapath *mp, + const unsigned int sheight, + const unsigned int height, + const unsigned int maxlen) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct buffer_head *dibh = mp->mp_bh[0]; + u64 bn, dblock = 0; + unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0; + unsigned dblks = 0; + unsigned ptrs_per_blk; + const unsigned end_of_metadata = height - 1; + int eob = 0; + enum alloc_state state; + __be64 *ptr; + __be64 zero_bn = 0; + + BUG_ON(sheight < 1); + BUG_ON(dibh == NULL); - for (x = 0; x < end_of_metadata; x++) { - lookup_block(ip, bh, x, &mp, create, new, dblock); - brelse(bh); - if (!*dblock) - goto out; + gfs2_trans_add_bh(ip->i_gl, dibh, 1); - error = gfs2_meta_indirect_buffer(ip, x+1, *dblock, *new, &bh); - if (error) - goto out; + if (height == sheight) { + struct buffer_head *bh; + /* Bottom indirect block exists, find unalloced extent size */ + ptr = metapointer(end_of_metadata, mp); + bh = mp->mp_bh[end_of_metadata]; + dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, + &eob); + BUG_ON(dblks < 1); + state = ALLOC_DATA; + } else { + /* Need to allocate indirect blocks */ + ptrs_per_blk = height > 1 ? sdp->sd_inptrs : sdp->sd_diptrs; + dblks = min(maxlen, ptrs_per_blk - mp->mp_list[end_of_metadata]); + if (height == ip->i_height) { + /* Writing into existing tree, extend tree down */ + iblks = height - sheight; + state = ALLOC_GROW_DEPTH; + } else { + /* Building up tree height */ + state = ALLOC_GROW_HEIGHT; + iblks = height - ip->i_height; + branch_start = metapath_branch_start(mp); + iblks += (height - branch_start); + } } - lookup_block(ip, bh, end_of_metadata, &mp, create, new, dblock); + /* start of the second part of the function (state machine) */ - if (extlen && *dblock) { - *extlen = 1; - - if (!*new) { - uint64_t tmp_dblock; - int tmp_new; - unsigned int nptrs; + blks = dblks + iblks; + i = sheight; + do { + int error; + n = blks - alloced; + error = gfs2_alloc_block(ip, &bn, &n); + if (error) + return error; + alloced += n; + if (state != ALLOC_DATA || gfs2_is_jdata(ip)) + gfs2_trans_add_unrevoke(sdp, bn, n); + switch (state) { + /* Growing height of tree */ + case ALLOC_GROW_HEIGHT: + if (i == 1) { + ptr = (__be64 *)(dibh->b_data + + sizeof(struct gfs2_dinode)); + zero_bn = *ptr; + } + for (; i - 1 < height - ip->i_height && n > 0; i++, n--) + gfs2_indirect_init(mp, ip->i_gl, i, 0, bn++); + if (i - 1 == height - ip->i_height) { + i--; + gfs2_buffer_copy_tail(mp->mp_bh[i], + sizeof(struct gfs2_meta_header), + dibh, sizeof(struct gfs2_dinode)); + gfs2_buffer_clear_tail(dibh, + sizeof(struct gfs2_dinode) + + sizeof(__be64)); + ptr = (__be64 *)(mp->mp_bh[i]->b_data + + sizeof(struct gfs2_meta_header)); + *ptr = zero_bn; + state = ALLOC_GROW_DEPTH; + for(i = branch_start; i < height; i++) { + if (mp->mp_bh[i] == NULL) + break; + brelse(mp->mp_bh[i]); + mp->mp_bh[i] = NULL; + } + i = branch_start; + } + if (n == 0) + break; + /* Branching from existing tree */ + case ALLOC_GROW_DEPTH: + if (i > 1 && i < height) + gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[i-1], 1); + for (; i < height && n > 0; i++, n--) + gfs2_indirect_init(mp, ip->i_gl, i, + mp->mp_list[i-1], bn++); + if (i == height) + state = ALLOC_DATA; + if (n == 0) + break; + /* Tree complete, adding data blocks */ + case ALLOC_DATA: + BUG_ON(n > dblks); + BUG_ON(mp->mp_bh[end_of_metadata] == NULL); + gfs2_trans_add_bh(ip->i_gl, mp->mp_bh[end_of_metadata], 1); + dblks = n; + ptr = metapointer(end_of_metadata, mp); + dblock = bn; + while (n-- > 0) + *ptr++ = cpu_to_be64(bn++); + break; + } + } while ((state != ALLOC_DATA) || !dblock); + + ip->i_height = height; + gfs2_add_inode_blocks(&ip->i_inode, alloced); + gfs2_dinode_out(ip, mp->mp_bh[0]->b_data); + map_bh(bh_map, inode->i_sb, dblock); + bh_map->b_size = dblks << inode->i_blkbits; + set_buffer_new(bh_map); + return 0; +} - nptrs = (end_of_metadata) ? sdp->sd_inptrs : - sdp->sd_diptrs; +/** + * gfs2_block_map - Map a block from an inode to a disk block + * @inode: The inode + * @lblock: The logical block number + * @bh_map: The bh to be mapped + * @create: True if its ok to alloc blocks to satify the request + * + * Sets buffer_mapped() if successful, sets buffer_boundary() if a + * read of metadata will be required before the next block can be + * mapped. Sets buffer_new() if new blocks were allocated. + * + * Returns: errno + */ - while (++mp.mp_list[end_of_metadata] < nptrs) { - lookup_block(ip, bh, end_of_metadata, &mp, - 0, &tmp_new, &tmp_dblock); +int gfs2_block_map(struct inode *inode, sector_t lblock, + struct buffer_head *bh_map, int create) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + unsigned int bsize = sdp->sd_sb.sb_bsize; + const unsigned int maxlen = bh_map->b_size >> inode->i_blkbits; + const u64 *arr = sdp->sd_heightsize; + __be64 *ptr; + u64 size; + struct metapath mp; + int ret; + int eob; + unsigned int len; + struct buffer_head *bh; + u8 height; - if (*dblock + *extlen != tmp_dblock) - break; + BUG_ON(maxlen == 0); - (*extlen)++; - } - } + memset(mp.mp_bh, 0, sizeof(mp.mp_bh)); + bmap_lock(ip, create); + clear_buffer_mapped(bh_map); + clear_buffer_new(bh_map); + clear_buffer_boundary(bh_map); + trace_gfs2_bmap(ip, bh_map, lblock, create, 1); + if (gfs2_is_dir(ip)) { + bsize = sdp->sd_jbsize; + arr = sdp->sd_jheightsize; } - brelse(bh); + ret = gfs2_meta_inode_buffer(ip, &mp.mp_bh[0]); + if (ret) + goto out; - if (*new) { - error = gfs2_meta_inode_buffer(ip, &bh); - if (!error) { - gfs2_trans_add_bh(ip->i_gl, bh, 1); - gfs2_dinode_out(&ip->i_di, bh->b_data); - brelse(bh); - } + height = ip->i_height; + size = (lblock + 1) * bsize; + while (size > arr[height]) + height++; + find_metapath(sdp, lblock, &mp, height); + ret = 1; + if (height > ip->i_height || gfs2_is_stuffed(ip)) + goto do_alloc; + ret = lookup_metapath(ip, &mp); + if (ret < 0) + goto out; + if (ret != ip->i_height) + goto do_alloc; + ptr = metapointer(ip->i_height - 1, &mp); + if (*ptr == 0) + goto do_alloc; + map_bh(bh_map, inode->i_sb, be64_to_cpu(*ptr)); + bh = mp.mp_bh[ip->i_height - 1]; + len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob); + bh_map->b_size = (len << inode->i_blkbits); + if (eob) + set_buffer_boundary(bh_map); + ret = 0; +out: + release_metapath(&mp); + trace_gfs2_bmap(ip, bh_map, lblock, create, ret); + bmap_unlock(ip, create); + return ret; + +do_alloc: + /* All allocations are done here, firstly check create flag */ + if (!create) { + BUG_ON(gfs2_is_stuffed(ip)); + ret = 0; + goto out; } - out: - if (create) - up_write(&ip->i_rw_mutex); - else - up_read(&ip->i_rw_mutex); + /* At this point ret is the tree depth of already allocated blocks */ + ret = gfs2_bmap_alloc(inode, lblock, bh_map, &mp, ret, height, maxlen); + goto out; +} - return error; +/* + * Deprecated: do not use in new code + */ +int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen) +{ + struct buffer_head bh = { .b_state = 0, .b_blocknr = 0 }; + int ret; + int create = *new; + + BUG_ON(!extlen); + BUG_ON(!dblock); + BUG_ON(!new); + + bh.b_size = 1 << (inode->i_blkbits + (create ? 0 : 5)); + ret = gfs2_block_map(inode, lblock, &bh, create); + *extlen = bh.b_size >> inode->i_blkbits; + *dblock = bh.b_blocknr; + if (buffer_new(&bh)) + *new = 1; + else + *new = 0; + return ret; } /** @@ -534,13 +687,13 @@ int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new, static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, struct metapath *mp, unsigned int height, - uint64_t block, int first, block_call_t bc, + u64 block, int first, block_call_t bc, void *data) { - struct gfs2_sbd *sdp = ip->i_sbd; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head *bh = NULL; - uint64_t *top, *bottom; - uint64_t bn; + __be64 *top, *bottom; + u64 bn; int error; int mh_size = sizeof(struct gfs2_meta_header); @@ -550,26 +703,24 @@ static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, return error; dibh = bh; - top = (uint64_t *)(bh->b_data + sizeof(struct gfs2_dinode)) + - mp->mp_list[0]; - bottom = (uint64_t *)(bh->b_data + sizeof(struct gfs2_dinode)) + - sdp->sd_diptrs; + top = (__be64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + mp->mp_list[0]; + bottom = (__be64 *)(bh->b_data + sizeof(struct gfs2_dinode)) + sdp->sd_diptrs; } else { error = gfs2_meta_indirect_buffer(ip, height, block, 0, &bh); if (error) return error; - top = (uint64_t *)(bh->b_data + mh_size) + - ((first) ? mp->mp_list[height] : 0); + top = (__be64 *)(bh->b_data + mh_size) + + (first ? mp->mp_list[height] : 0); - bottom = (uint64_t *)(bh->b_data + mh_size) + sdp->sd_inptrs; + bottom = (__be64 *)(bh->b_data + mh_size) + sdp->sd_inptrs; } error = bc(ip, dibh, bh, top, bottom, height, data); if (error) goto out; - if (height < ip->i_di.di_height - 1) + if (height < ip->i_height - 1) for (; top < bottom; top++, first = 0) { if (!*top) continue; @@ -582,9 +733,8 @@ static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, break; } - out: +out: brelse(bh); - return error; } @@ -602,15 +752,15 @@ static int recursive_scan(struct gfs2_inode *ip, struct buffer_head *dibh, */ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, - struct buffer_head *bh, uint64_t *top, uint64_t *bottom, + struct buffer_head *bh, __be64 *top, __be64 *bottom, unsigned int height, void *data) { - struct strip_mine *sm = (struct strip_mine *)data; - struct gfs2_sbd *sdp = ip->i_sbd; + struct strip_mine *sm = data; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_rgrp_list rlist; - uint64_t bn, bstart; - uint32_t blen; - uint64_t *p; + u64 bn, bstart; + u32 blen; + __be64 *p; unsigned int rg_blocks = 0; int metadata; unsigned int revokes = 0; @@ -628,11 +778,11 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, sm->sm_first = 0; } - metadata = (height != ip->i_di.di_height - 1); + metadata = (height != ip->i_height - 1); if (metadata) revokes = (height) ? sdp->sd_inptrs : sdp->sd_diptrs; - error = gfs2_rindex_hold(sdp, &ip->i_alloc.al_ri_gh); + error = gfs2_rindex_hold(sdp, &ip->i_alloc->al_ri_gh); if (error) return error; @@ -662,12 +812,12 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, else goto out; /* Nothing to do */ - gfs2_rlist_alloc(&rlist, LM_ST_EXCLUSIVE, 0); + gfs2_rlist_alloc(&rlist, LM_ST_EXCLUSIVE); for (x = 0; x < rlist.rl_rgrps; x++) { struct gfs2_rgrpd *rgd; rgd = rlist.rl_ghs[x].gh_gl->gl_object; - rg_blocks += rgd->rd_ri.ri_length; + rg_blocks += rgd->rd_length; } error = gfs2_glock_nq_m(rlist.rl_rgrps, rlist.rl_ghs); @@ -709,9 +859,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, } *p = 0; - if (!ip->i_di.di_blocks) - gfs2_consist_inode(ip); - ip->i_di.di_blocks--; + gfs2_add_inode_blocks(&ip->i_inode, -1); } if (bstart) { if (metadata) @@ -720,23 +868,20 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, gfs2_free_data(ip, bstart, blen); } - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; - gfs2_dinode_out(&ip->i_di, dibh->b_data); + gfs2_dinode_out(ip, dibh->b_data); up_write(&ip->i_rw_mutex); gfs2_trans_end(sdp); - out_rg_gunlock: +out_rg_gunlock: gfs2_glock_dq_m(rlist.rl_rgrps, rlist.rl_ghs); - - out_rlist: +out_rlist: gfs2_rlist_free(&rlist); - - out: - gfs2_glock_dq_uninit(&ip->i_alloc.al_ri_gh); - +out: + gfs2_glock_dq_uninit(&ip->i_alloc->al_ri_gh); return error; } @@ -750,24 +895,21 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh, * Returns: errno */ -static int do_grow(struct gfs2_inode *ip, uint64_t size) +static int do_grow(struct gfs2_inode *ip, u64 size) { - struct gfs2_sbd *sdp = ip->i_sbd; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_alloc *al; struct buffer_head *dibh; - unsigned int h; int error; al = gfs2_alloc_get(ip); + if (!al) + return -ENOMEM; - error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_lock_check(ip); if (error) goto out; - error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid); - if (error) - goto out_gunlock_q; - al->al_requested = sdp->sd_max_height + RES_DATA; error = gfs2_inplace_reserve(ip); @@ -775,64 +917,122 @@ static int do_grow(struct gfs2_inode *ip, uint64_t size) goto out_gunlock_q; error = gfs2_trans_begin(sdp, - sdp->sd_max_height + al->al_rgd->rd_ri.ri_length + + sdp->sd_max_height + al->al_rgd->rd_length + RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA, 0); if (error) goto out_ipres; + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + goto out_end_trans; + if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { if (gfs2_is_stuffed(ip)) { - error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page, - NULL); + error = gfs2_unstuff_dinode(ip, NULL); if (error) - goto out_end_trans; - } - - h = calc_tree_height(ip, size); - if (ip->i_di.di_height < h) { - down_write(&ip->i_rw_mutex); - error = build_height(ip, h); - up_write(&ip->i_rw_mutex); - if (error) - goto out_end_trans; + goto out_brelse; } } - ip->i_di.di_size = size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - - error = gfs2_meta_inode_buffer(ip, &dibh); - if (error) - goto out_end_trans; - + ip->i_disksize = size; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - brelse(dibh); + gfs2_dinode_out(ip, dibh->b_data); - out_end_trans: +out_brelse: + brelse(dibh); +out_end_trans: gfs2_trans_end(sdp); - - out_ipres: +out_ipres: gfs2_inplace_release(ip); - - out_gunlock_q: +out_gunlock_q: gfs2_quota_unlock(ip); - - out: +out: gfs2_alloc_put(ip); - return error; } -static int trunc_start(struct gfs2_inode *ip, uint64_t size) + +/** + * gfs2_block_truncate_page - Deal with zeroing out data for truncate + * + * This is partly borrowed from ext3. + */ +static int gfs2_block_truncate_page(struct address_space *mapping) +{ + struct inode *inode = mapping->host; + struct gfs2_inode *ip = GFS2_I(inode); + loff_t from = inode->i_size; + unsigned long index = from >> PAGE_CACHE_SHIFT; + unsigned offset = from & (PAGE_CACHE_SIZE-1); + unsigned blocksize, iblock, length, pos; + struct buffer_head *bh; + struct page *page; + int err; + + page = grab_cache_page(mapping, index); + if (!page) + return 0; + + blocksize = inode->i_sb->s_blocksize; + length = blocksize - (offset & (blocksize - 1)); + iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); + + if (!page_has_buffers(page)) + create_empty_buffers(page, blocksize, 0); + + /* Find the buffer that contains "offset" */ + bh = page_buffers(page); + pos = blocksize; + while (offset >= pos) { + bh = bh->b_this_page; + iblock++; + pos += blocksize; + } + + err = 0; + + if (!buffer_mapped(bh)) { + gfs2_block_map(inode, iblock, bh, 0); + /* unmapped? It's a hole - nothing to do */ + if (!buffer_mapped(bh)) + goto unlock; + } + + /* Ok, it's mapped. Make sure it's up-to-date */ + if (PageUptodate(page)) + set_buffer_uptodate(bh); + + if (!buffer_uptodate(bh)) { + err = -EIO; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + /* Uhhuh. Read error. Complain and punt. */ + if (!buffer_uptodate(bh)) + goto unlock; + err = 0; + } + + if (!gfs2_is_writeback(ip)) + gfs2_trans_add_bh(ip->i_gl, bh, 0); + + zero_user(page, offset, length); + mark_buffer_dirty(bh); +unlock: + unlock_page(page); + page_cache_release(page); + return err; +} + +static int trunc_start(struct gfs2_inode *ip, u64 size) { - struct gfs2_sbd *sdp = ip->i_sbd; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head *dibh; int journaled = gfs2_is_jdata(ip); int error; error = gfs2_trans_begin(sdp, - RES_DINODE + ((journaled) ? RES_JDATA : 0), 0); + RES_DINODE + (journaled ? RES_JDATA : 0), 0); if (error) return error; @@ -841,48 +1041,50 @@ static int trunc_start(struct gfs2_inode *ip, uint64_t size) goto out; if (gfs2_is_stuffed(ip)) { - ip->i_di.di_size = size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); + u64 dsize = size + sizeof(struct gfs2_inode); + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + size); + gfs2_dinode_out(ip, dibh->b_data); + if (dsize > dibh->b_size) + dsize = dibh->b_size; + gfs2_buffer_clear_tail(dibh, dsize); error = 1; - } else { - if (size & (uint64_t)(sdp->sd_sb.sb_bsize - 1)) - error = gfs2_block_truncate_page(ip->i_vnode->i_mapping); + if (size & (u64)(sdp->sd_sb.sb_bsize - 1)) + error = gfs2_block_truncate_page(ip->i_inode.i_mapping); if (!error) { - ip->i_di.di_size = size; - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - ip->i_di.di_flags |= GFS2_DIF_TRUNC_IN_PROG; + ip->i_disksize = size; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; + ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG; gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); + gfs2_dinode_out(ip, dibh->b_data); } } brelse(dibh); - out: +out: gfs2_trans_end(sdp); - return error; } -static int trunc_dealloc(struct gfs2_inode *ip, uint64_t size) +static int trunc_dealloc(struct gfs2_inode *ip, u64 size) { - unsigned int height = ip->i_di.di_height; - uint64_t lblock; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + unsigned int height = ip->i_height; + u64 lblock; struct metapath mp; int error; if (!size) lblock = 0; else - lblock = (size - 1) >> ip->i_sbd->sd_sb.sb_bsize_shift; + lblock = (size - 1) >> sdp->sd_sb.sb_bsize_shift; - find_metapath(ip, lblock, &mp); - gfs2_alloc_get(ip); + find_metapath(sdp, lblock, &mp, ip->i_height); + if (!gfs2_alloc_get(ip)) + return -ENOMEM; error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); if (error) @@ -900,14 +1102,14 @@ static int trunc_dealloc(struct gfs2_inode *ip, uint64_t size) gfs2_quota_unhold(ip); - out: +out: gfs2_alloc_put(ip); return error; } static int trunc_end(struct gfs2_inode *ip) { - struct gfs2_sbd *sdp = ip->i_sbd; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head *dibh; int error; @@ -921,25 +1123,21 @@ static int trunc_end(struct gfs2_inode *ip) if (error) goto out; - if (!ip->i_di.di_size) { - ip->i_di.di_height = 0; - ip->i_di.di_goal_meta = - ip->i_di.di_goal_data = - ip->i_num.no_addr; + if (!ip->i_disksize) { + ip->i_height = 0; + ip->i_goal = ip->i_no_addr; gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode)); } - ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds(); - ip->i_di.di_flags &= ~GFS2_DIF_TRUNC_IN_PROG; + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; + ip->i_diskflags &= ~GFS2_DIF_TRUNC_IN_PROG; gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(&ip->i_di, dibh->b_data); + gfs2_dinode_out(ip, dibh->b_data); brelse(dibh); - out: +out: up_write(&ip->i_rw_mutex); - gfs2_trans_end(sdp); - return error; } @@ -954,7 +1152,7 @@ static int trunc_end(struct gfs2_inode *ip) * Returns: errno */ -static int do_shrink(struct gfs2_inode *ip, uint64_t size) +static int do_shrink(struct gfs2_inode *ip, u64 size) { int error; @@ -971,6 +1169,33 @@ static int do_shrink(struct gfs2_inode *ip, uint64_t size) return error; } +static int do_touch(struct gfs2_inode *ip, u64 size) +{ + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + struct buffer_head *dibh; + int error; + + error = gfs2_trans_begin(sdp, RES_DINODE, 0); + if (error) + return error; + + down_write(&ip->i_rw_mutex); + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + goto do_touch_out; + + ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + +do_touch_out: + up_write(&ip->i_rw_mutex); + gfs2_trans_end(sdp); + return error; +} + /** * gfs2_truncatei - make a file a given size * @ip: the inode @@ -982,17 +1207,20 @@ static int do_shrink(struct gfs2_inode *ip, uint64_t size) * Returns: errno */ -int gfs2_truncatei(struct gfs2_inode *ip, uint64_t size) +int gfs2_truncatei(struct gfs2_inode *ip, u64 size) { int error; - if (gfs2_assert_warn(ip->i_sbd, S_ISREG(ip->i_di.di_mode))) + if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), S_ISREG(ip->i_inode.i_mode))) return -EINVAL; - if (size > ip->i_di.di_size) + if (size > ip->i_disksize) error = do_grow(ip, size); - else + else if (size < ip->i_disksize) error = do_shrink(ip, size); + else + /* update time stamps */ + error = do_touch(ip, size); return error; } @@ -1000,7 +1228,7 @@ int gfs2_truncatei(struct gfs2_inode *ip, uint64_t size) int gfs2_truncatei_resume(struct gfs2_inode *ip) { int error; - error = trunc_dealloc(ip, ip->i_di.di_size); + error = trunc_dealloc(ip, ip->i_disksize); if (!error) error = trunc_end(ip); return error; @@ -1012,35 +1240,6 @@ int gfs2_file_dealloc(struct gfs2_inode *ip) } /** - * gfs2_write_calc_reserv - calculate number of blocks needed to write to a file - * @ip: the file - * @len: the number of bytes to be written to the file - * @data_blocks: returns the number of data blocks required - * @ind_blocks: returns the number of indirect blocks required - * - */ - -void gfs2_write_calc_reserv(struct gfs2_inode *ip, unsigned int len, - unsigned int *data_blocks, unsigned int *ind_blocks) -{ - struct gfs2_sbd *sdp = ip->i_sbd; - unsigned int tmp; - - if (gfs2_is_dir(ip)) { - *data_blocks = DIV_ROUND_UP(len, sdp->sd_jbsize) + 2; - *ind_blocks = 3 * (sdp->sd_max_jheight - 1); - } else { - *data_blocks = (len >> sdp->sd_sb.sb_bsize_shift) + 3; - *ind_blocks = 3 * (sdp->sd_max_height - 1); - } - - for (tmp = *data_blocks; tmp > sdp->sd_diptrs;) { - tmp = DIV_ROUND_UP(tmp, sdp->sd_inptrs); - *ind_blocks += tmp; - } -} - -/** * gfs2_write_alloc_required - figure out if a write will require an allocation * @ip: the file being written to * @offset: the offset to write to @@ -1050,14 +1249,14 @@ void gfs2_write_calc_reserv(struct gfs2_inode *ip, unsigned int len, * Returns: errno */ -int gfs2_write_alloc_required(struct gfs2_inode *ip, uint64_t offset, +int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, unsigned int len, int *alloc_required) { - struct gfs2_sbd *sdp = ip->i_sbd; - uint64_t lblock, lblock_stop, dblock; - uint32_t extlen; - int new = 0; - int error = 0; + struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); + struct buffer_head bh; + unsigned int shift; + u64 lblock, lblock_stop, size; + u64 end_of_file; *alloc_required = 0; @@ -1071,29 +1270,27 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, uint64_t offset, return 0; } - if (gfs2_is_dir(ip)) { - unsigned int bsize = sdp->sd_jbsize; - lblock = offset; - do_div(lblock, bsize); - lblock_stop = offset + len + bsize - 1; - do_div(lblock_stop, bsize); - } else { - unsigned int shift = sdp->sd_sb.sb_bsize_shift; - lblock = offset >> shift; - lblock_stop = (offset + len + sdp->sd_sb.sb_bsize - 1) >> shift; - } - - for (; lblock < lblock_stop; lblock += extlen) { - error = gfs2_block_map(ip, lblock, &new, &dblock, &extlen); - if (error) - return error; + *alloc_required = 1; + shift = sdp->sd_sb.sb_bsize_shift; + BUG_ON(gfs2_is_dir(ip)); + end_of_file = (ip->i_disksize + sdp->sd_sb.sb_bsize - 1) >> shift; + lblock = offset >> shift; + lblock_stop = (offset + len + sdp->sd_sb.sb_bsize - 1) >> shift; + if (lblock_stop > end_of_file) + return 0; - if (!dblock) { - *alloc_required = 1; + size = (lblock_stop - lblock) << shift; + do { + bh.b_state = 0; + bh.b_size = size; + gfs2_block_map(&ip->i_inode, lblock, &bh, 0); + if (!buffer_mapped(&bh)) return 0; - } - } + size -= bh.b_size; + lblock += (bh.b_size >> ip->i_inode.i_blkbits); + } while(size > 0); + *alloc_required = 0; return 0; }