ext2 reservations

Val's cross-port of the ext3 reservations code into ext2.

[mbligh@mbligh.org: Small type error for printk
[akpm@linux-foundation.org: fix types, sync with ext3]
[mbligh@mbligh.org: Bring ext2 reservations code in line with latest ext3]
[akpm@linux-foundation.org: kill noisy printk]
[akpm@linux-foundation.org: remember to dirty the gdp's block]
[akpm@linux-foundation.org: cross-port the missed 5dea5176e5]
[akpm@linux-foundation.org: cross-port e6022603b9]
[akpm@linux-foundation.org: Port the omitted 08fb306fe6]
[akpm@linux-foundation.org: Backport the missed 20acaa18d0]
[akpm@linux-foundation.org: fixes]
[cmm@us.ibm.com: fix reservation extension]
[bunk@stusta.de: make ext2_get_blocks() static]
[hugh@veritas.com: fix hang]
[hugh@veritas.com: ext2_new_blocks should reset the reservation window size]
[hugh@veritas.com: ext2 balloc: fix off-by-one against rsv_end]
[hugh@veritas.com: grp_goal 0 is a genuine goal (unlike -1), so ext2_try_to_allocate_with_rsv should treat it as such]
[hugh@veritas.com: rbtree usage cleanup]
[pbadari@us.ibm.com: Fix for ext2 reservation]
[bunk@kernel.org: remove fs/ext2/balloc.c:reserve_blocks()]
[hugh@veritas.com: ext2 balloc: use io_error label]
Cc: "Martin J. Bligh" <mbligh@mbligh.org>
Cc: Valerie Henson <val_henson@linux.intel.com>
Cc: Mingming Cao <cmm@us.ibm.com>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com>
Signed-off-by: Adrian Bunk <bunk@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Martin J. Bligh 2007-10-16 23:30:46 -07:00 committed by Linus Torvalds
parent 369f2389e7
commit a686cd898b
10 changed files with 1569 additions and 500 deletions

File diff suppressed because it is too large Load Diff

View File

@ -33,22 +33,9 @@ struct ext2_inode_info {
*/
__u32 i_block_group;
/*
* i_next_alloc_block is the logical (file-relative) number of the
* most-recently-allocated block in this file. Yes, it is misnamed.
* We use this for detecting linearly ascending allocation requests.
*/
__u32 i_next_alloc_block;
/* block reservation info */
struct ext2_block_alloc_info *i_block_alloc_info;
/*
* i_next_alloc_goal is the *physical* companion to i_next_alloc_block.
* it the the physical block number of the block which was most-recently
* allocated to this file. This give us the goal (target) for the next
* allocation when we detect linearly ascending requests.
*/
__u32 i_next_alloc_goal;
__u32 i_prealloc_block;
__u32 i_prealloc_count;
__u32 i_dir_start_lookup;
#ifdef CONFIG_EXT2_FS_XATTR
/*
@ -65,7 +52,16 @@ struct ext2_inode_info {
struct posix_acl *i_default_acl;
#endif
rwlock_t i_meta_lock;
/*
* truncate_mutex is for serialising ext2_truncate() against
* ext2_getblock(). It also protects the internals of the inode's
* reservation data structures: ext2_reserve_window and
* ext2_reserve_window_node.
*/
struct mutex truncate_mutex;
struct inode vfs_inode;
struct list_head i_orphan; /* unlinked but open inodes */
};
/*
@ -91,8 +87,9 @@ static inline struct ext2_inode_info *EXT2_I(struct inode *inode)
/* balloc.c */
extern int ext2_bg_has_super(struct super_block *sb, int group);
extern unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
extern int ext2_new_block (struct inode *, unsigned long,
__u32 *, __u32 *, int *);
extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *);
extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long,
unsigned long *, int *);
extern void ext2_free_blocks (struct inode *, unsigned long,
unsigned long);
extern unsigned long ext2_count_free_blocks (struct super_block *);
@ -101,6 +98,10 @@ extern void ext2_check_blocks_bitmap (struct super_block *);
extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
unsigned int block_group,
struct buffer_head ** bh);
extern void ext2_discard_reservation (struct inode *);
extern int ext2_should_retry_alloc(struct super_block *sb, int *retries);
extern void ext2_init_block_alloc_info(struct inode *);
extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv);
/* dir.c */
extern int ext2_add_link (struct dentry *, struct inode *);
@ -128,7 +129,6 @@ extern int ext2_write_inode (struct inode *, int);
extern void ext2_put_inode (struct inode *);
extern void ext2_delete_inode (struct inode *);
extern int ext2_sync_inode (struct inode *);
extern void ext2_discard_prealloc (struct inode *);
extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern void ext2_truncate (struct inode *);
extern int ext2_setattr (struct dentry *, struct iattr *);

View File

@ -30,8 +30,11 @@
*/
static int ext2_release_file (struct inode * inode, struct file * filp)
{
if (filp->f_mode & FMODE_WRITE)
ext2_discard_prealloc (inode);
if (filp->f_mode & FMODE_WRITE) {
mutex_lock(&EXT2_I(inode)->truncate_mutex);
ext2_discard_reservation(inode);
mutex_unlock(&EXT2_I(inode)->truncate_mutex);
}
return 0;
}

View File

@ -581,11 +581,8 @@ got:
ei->i_file_acl = 0;
ei->i_dir_acl = 0;
ei->i_dtime = 0;
ei->i_block_alloc_info = NULL;
ei->i_block_group = group;
ei->i_next_alloc_block = 0;
ei->i_next_alloc_goal = 0;
ei->i_prealloc_block = 0;
ei->i_prealloc_count = 0;
ei->i_dir_start_lookup = 0;
ei->i_state = EXT2_STATE_NEW;
ext2_set_inode_flags(inode);

View File

@ -53,19 +53,6 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode)
inode->i_blocks - ea_blocks == 0);
}
/*
* Called at each iput().
*
* The inode may be "bad" if ext2_read_inode() saw an error from
* ext2_get_inode(), so we need to check that to avoid freeing random disk
* blocks.
*/
void ext2_put_inode(struct inode *inode)
{
if (!is_bad_inode(inode))
ext2_discard_prealloc(inode);
}
/*
* Called at the last iput() if i_nlink is zero.
*/
@ -89,61 +76,6 @@ no_delete:
clear_inode(inode); /* We must guarantee clearing of inode... */
}
void ext2_discard_prealloc (struct inode * inode)
{
#ifdef EXT2_PREALLOCATE
struct ext2_inode_info *ei = EXT2_I(inode);
write_lock(&ei->i_meta_lock);
if (ei->i_prealloc_count) {
unsigned short total = ei->i_prealloc_count;
unsigned long block = ei->i_prealloc_block;
ei->i_prealloc_count = 0;
ei->i_prealloc_block = 0;
write_unlock(&ei->i_meta_lock);
ext2_free_blocks (inode, block, total);
return;
} else
write_unlock(&ei->i_meta_lock);
#endif
}
static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
{
#ifdef EXT2FS_DEBUG
static unsigned long alloc_hits, alloc_attempts;
#endif
unsigned long result;
#ifdef EXT2_PREALLOCATE
struct ext2_inode_info *ei = EXT2_I(inode);
write_lock(&ei->i_meta_lock);
if (ei->i_prealloc_count &&
(goal == ei->i_prealloc_block || goal + 1 == ei->i_prealloc_block))
{
result = ei->i_prealloc_block++;
ei->i_prealloc_count--;
write_unlock(&ei->i_meta_lock);
ext2_debug ("preallocation hit (%lu/%lu).\n",
++alloc_hits, ++alloc_attempts);
} else {
write_unlock(&ei->i_meta_lock);
ext2_discard_prealloc (inode);
ext2_debug ("preallocation miss (%lu/%lu).\n",
alloc_hits, ++alloc_attempts);
if (S_ISREG(inode->i_mode))
result = ext2_new_block (inode, goal,
&ei->i_prealloc_count,
&ei->i_prealloc_block, err);
else
result = ext2_new_block(inode, goal, NULL, NULL, err);
}
#else
result = ext2_new_block (inode, goal, 0, 0, err);
#endif
return result;
}
typedef struct {
__le32 *p;
__le32 key;
@ -228,7 +160,8 @@ static int ext2_block_to_path(struct inode *inode,
ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");
}
if (boundary)
*boundary = (i_block & (ptrs - 1)) == (final - 1);
*boundary = final - 1 - (i_block & (ptrs - 1));
return n;
}
@ -355,39 +288,129 @@ static unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
* @block: block we want
* @chain: chain of indirect blocks
* @partial: pointer to the last triple within a chain
* @goal: place to store the result.
*
* Normally this function find the prefered place for block allocation,
* stores it in *@goal and returns zero. If the branch had been changed
* under us we return -EAGAIN.
* Returns preferred place for a block (the goal).
*/
static inline int ext2_find_goal(struct inode *inode,
long block,
Indirect chain[4],
Indirect *partial,
unsigned long *goal)
Indirect *partial)
{
struct ext2_inode_info *ei = EXT2_I(inode);
write_lock(&ei->i_meta_lock);
if ((block == ei->i_next_alloc_block + 1) && ei->i_next_alloc_goal) {
ei->i_next_alloc_block++;
ei->i_next_alloc_goal++;
}
if (verify_chain(chain, partial)) {
struct ext2_block_alloc_info *block_i;
block_i = EXT2_I(inode)->i_block_alloc_info;
/*
* try the heuristic for sequential allocation,
* failing that at least try to get decent locality.
*/
if (block == ei->i_next_alloc_block)
*goal = ei->i_next_alloc_goal;
if (!*goal)
*goal = ext2_find_near(inode, partial);
write_unlock(&ei->i_meta_lock);
return 0;
if (block_i && (block == block_i->last_alloc_logical_block + 1)
&& (block_i->last_alloc_physical_block != 0)) {
return block_i->last_alloc_physical_block + 1;
}
write_unlock(&ei->i_meta_lock);
return -EAGAIN;
return ext2_find_near(inode, partial);
}
/**
* ext2_blks_to_allocate: Look up the block map and count the number
* of direct blocks need to be allocated for the given branch.
*
* @branch: chain of indirect blocks
* @k: number of blocks need for indirect blocks
* @blks: number of data blocks to be mapped.
* @blocks_to_boundary: the offset in the indirect block
*
* return the total number of blocks to be allocate, including the
* direct and indirect blocks.
*/
static int
ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks,
int blocks_to_boundary)
{
unsigned long count = 0;
/*
* Simple case, [t,d]Indirect block(s) has not allocated yet
* then it's clear blocks on that path have not allocated
*/
if (k > 0) {
/* right now don't hanel cross boundary allocation */
if (blks < blocks_to_boundary + 1)
count += blks;
else
count += blocks_to_boundary + 1;
return count;
}
count++;
while (count < blks && count <= blocks_to_boundary
&& le32_to_cpu(*(branch[0].p + count)) == 0) {
count++;
}
return count;
}
/**
* ext2_alloc_blocks: multiple allocate blocks needed for a branch
* @indirect_blks: the number of blocks need to allocate for indirect
* blocks
*
* @new_blocks: on return it will store the new block numbers for
* the indirect blocks(if needed) and the first direct block,
* @blks: on return it will store the total number of allocated
* direct blocks
*/
static int ext2_alloc_blocks(struct inode *inode,
ext2_fsblk_t goal, int indirect_blks, int blks,
ext2_fsblk_t new_blocks[4], int *err)
{
int target, i;
unsigned long count = 0;
int index = 0;
ext2_fsblk_t current_block = 0;
int ret = 0;
/*
* Here we try to allocate the requested multiple blocks at once,
* on a best-effort basis.
* To build a branch, we should allocate blocks for
* the indirect blocks(if not allocated yet), and at least
* the first direct block of this branch. That's the
* minimum number of blocks need to allocate(required)
*/
target = blks + indirect_blks;
while (1) {
count = target;
/* allocating blocks for indirect blocks and direct blocks */
current_block = ext2_new_blocks(inode,goal,&count,err);
if (*err)
goto failed_out;
target -= count;
/* allocate blocks for indirect blocks */
while (index < indirect_blks && count) {
new_blocks[index++] = current_block++;
count--;
}
if (count > 0)
break;
}
/* save the new block number for the first direct block */
new_blocks[index] = current_block;
/* total number of blocks allocated for direct blocks */
ret = count;
*err = 0;
return ret;
failed_out:
for (i = 0; i <index; i++)
ext2_free_blocks(inode, new_blocks[i], 1);
return ret;
}
/**
@ -416,39 +439,49 @@ static inline int ext2_find_goal(struct inode *inode,
*/
static int ext2_alloc_branch(struct inode *inode,
int num,
unsigned long goal,
int *offsets,
Indirect *branch)
int indirect_blks, int *blks, ext2_fsblk_t goal,
int *offsets, Indirect *branch)
{
int blocksize = inode->i_sb->s_blocksize;
int n = 0;
int err;
int i;
int parent = ext2_alloc_block(inode, goal, &err);
branch[0].key = cpu_to_le32(parent);
if (parent) for (n = 1; n < num; n++) {
int i, n = 0;
int err = 0;
struct buffer_head *bh;
/* Allocate the next block */
int nr = ext2_alloc_block(inode, parent, &err);
if (!nr)
break;
branch[n].key = cpu_to_le32(nr);
int num;
ext2_fsblk_t new_blocks[4];
ext2_fsblk_t current_block;
num = ext2_alloc_blocks(inode, goal, indirect_blks,
*blks, new_blocks, &err);
if (err)
return err;
branch[0].key = cpu_to_le32(new_blocks[0]);
/*
* Get buffer_head for parent block, zero it out and set
* the pointer to new one, then send parent to disk.
* metadata blocks and data blocks are allocated.
*/
bh = sb_getblk(inode->i_sb, parent);
if (!bh) {
err = -EIO;
break;
}
for (n = 1; n <= indirect_blks; n++) {
/*
* Get buffer_head for parent block, zero it out
* and set the pointer to new one, then send
* parent to disk.
*/
bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
branch[n].bh = bh;
lock_buffer(bh);
memset(bh->b_data, 0, blocksize);
branch[n].bh = bh;
branch[n].p = (__le32 *) bh->b_data + offsets[n];
branch[n].key = cpu_to_le32(new_blocks[n]);
*branch[n].p = branch[n].key;
if ( n == indirect_blks) {
current_block = new_blocks[n];
/*
* End of chain, update the last new metablock of
* the chain to point to the new allocated
* data blocks numbers
*/
for (i=1; i < num; i++)
*(branch[n].p + i) = cpu_to_le32(++current_block);
}
set_buffer_uptodate(bh);
unlock_buffer(bh);
mark_buffer_dirty_inode(bh, inode);
@ -458,16 +491,8 @@ static int ext2_alloc_branch(struct inode *inode,
*/
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode))
sync_dirty_buffer(bh);
parent = nr;
}
if (n == num)
return 0;
/* Allocation failed, free what we already allocated */
for (i = 1; i < n; i++)
bforget(branch[i].bh);
for (i = 0; i < n; i++)
ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1);
*blks = num;
return err;
}
@ -478,57 +503,56 @@ static int ext2_alloc_branch(struct inode *inode,
* @chain: chain of indirect blocks (with a missing link - see
* ext2_alloc_branch)
* @where: location of missing link
* @num: number of blocks we are adding
* @num: number of indirect blocks we are adding
* @blks: number of direct blocks we are adding
*
* This function verifies that chain (up to the missing link) had not
* changed, fills the missing link and does all housekeeping needed in
* This function fills the missing link and does all housekeeping needed in
* inode (->i_blocks, etc.). In case of success we end up with the full
* chain to new block and return 0. Otherwise (== chain had been changed)
* we free the new blocks (forgetting their buffer_heads, indeed) and
* return -EAGAIN.
* chain to new block and return 0.
*/
static inline int ext2_splice_branch(struct inode *inode,
long block,
Indirect chain[4],
Indirect *where,
int num)
static void ext2_splice_branch(struct inode *inode,
long block, Indirect *where, int num, int blks)
{
struct ext2_inode_info *ei = EXT2_I(inode);
int i;
struct ext2_block_alloc_info *block_i;
ext2_fsblk_t current_block;
/* Verify that place we are splicing to is still there and vacant */
write_lock(&ei->i_meta_lock);
if (!verify_chain(chain, where-1) || *where->p)
goto changed;
block_i = EXT2_I(inode)->i_block_alloc_info;
/* XXX LOCKING probably should have i_meta_lock ?*/
/* That's it */
*where->p = where->key;
ei->i_next_alloc_block = block;
ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
write_unlock(&ei->i_meta_lock);
/*
* Update the host buffer_head or inode to point to more just allocated
* direct blocks blocks
*/
if (num == 0 && blks > 1) {
current_block = le32_to_cpu(where->key) + 1;
for (i = 1; i < blks; i++)
*(where->p + i ) = cpu_to_le32(current_block++);
}
/*
* update the most recently allocated logical & physical block
* in i_block_alloc_info, to assist find the proper goal block for next
* allocation
*/
if (block_i) {
block_i->last_alloc_logical_block = block + blks - 1;
block_i->last_alloc_physical_block =
le32_to_cpu(where[num].key) + blks - 1;
}
/* We are done with atomic stuff, now do the rest of housekeeping */
inode->i_ctime = CURRENT_TIME_SEC;
/* had we spliced it onto indirect block? */
if (where->bh)
mark_buffer_dirty_inode(where->bh, inode);
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
return 0;
changed:
write_unlock(&ei->i_meta_lock);
for (i = 1; i < num; i++)
bforget(where[i].bh);
for (i = 0; i < num; i++)
ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1);
return -EAGAIN;
}
/*
@ -542,64 +566,99 @@ changed:
* That has a nice additional property: no special recovery from the failed
* allocations is needed - we simply release blocks and do not touch anything
* reachable from inode.
*
* `handle' can be NULL if create == 0.
*
* The BKL may not be held on entry here. Be sure to take it early.
* return > 0, # of blocks mapped or allocated.
* return = 0, if plain lookup failed.
* return < 0, error case.
*/
int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
static int ext2_get_blocks(struct inode *inode,
sector_t iblock, unsigned long maxblocks,
struct buffer_head *bh_result,
int create)
{
int err = -EIO;
int offsets[4];
Indirect chain[4];
Indirect *partial;
unsigned long goal;
int left;
int boundary = 0;
int depth = ext2_block_to_path(inode, iblock, offsets, &boundary);
ext2_fsblk_t goal;
int indirect_blks;
int blocks_to_boundary = 0;
int depth;
struct ext2_inode_info *ei = EXT2_I(inode);
int count = 0;
ext2_fsblk_t first_block = 0;
depth = ext2_block_to_path(inode,iblock,offsets,&blocks_to_boundary);
if (depth == 0)
goto out;
return (err);
reread:
partial = ext2_get_branch(inode, depth, offsets, chain, &err);
/* Simplest case - block found, no allocation needed */
if (!partial) {
got_it:
map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));
if (boundary)
set_buffer_boundary(bh_result);
/* Clean up and exit */
partial = chain+depth-1; /* the whole chain */
goto cleanup;
first_block = le32_to_cpu(chain[depth - 1].key);
clear_buffer_new(bh_result); /* What's this do? */
count++;
/*map more blocks*/
while (count < maxblocks && count <= blocks_to_boundary) {
ext2_fsblk_t blk;
if (!verify_chain(chain, partial)) {
/*
* Indirect block might be removed by
* truncate while we were reading it.
* Handling of that case: forget what we've
* got now, go to reread.
*/
count = 0;
goto changed;
}
blk = le32_to_cpu(*(chain[depth-1].p + count));
if (blk == first_block + count)
count++;
else
break;
}
goto got_it;
}
/* Next simple case - plain lookup or failed read of indirect block */
if (!create || err == -EIO) {
cleanup:
while (partial > chain) {
brelse(partial->bh);
partial--;
}
out:
return err;
}
if (!create || err == -EIO)
goto cleanup;
mutex_lock(&ei->truncate_mutex);
/*
* Indirect block might be removed by truncate while we were
* reading it. Handling of that case (forget what we've got and
* reread) is taken out of the main path.
* Okay, we need to do block allocation. Lazily initialize the block
* allocation info here if necessary
*/
if (err == -EAGAIN)
goto changed;
if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info))
ext2_init_block_alloc_info(inode);
goal = 0;
if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0)
goto changed;
goal = ext2_find_goal(inode, iblock, chain, partial);
left = (chain + depth) - partial;
err = ext2_alloc_branch(inode, left, goal,
offsets+(partial-chain), partial);
if (err)
/* the number of blocks need to allocate for [d,t]indirect blocks */
indirect_blks = (chain + depth) - partial - 1;
/*
* Next look up the indirect map to count the totoal number of
* direct blocks to allocate for this branch.
*/
count = ext2_blks_to_allocate(partial, indirect_blks,
maxblocks, blocks_to_boundary);
/*
* XXX ???? Block out ext2_truncate while we alter the tree
*/
err = ext2_alloc_branch(inode, indirect_blks, &count, goal,
offsets + (partial - chain), partial);
if (err) {
mutex_unlock(&ei->truncate_mutex);
goto cleanup;
}
if (ext2_use_xip(inode->i_sb)) {
/*
@ -607,16 +666,28 @@ out:
*/
err = ext2_clear_xip_target (inode,
le32_to_cpu(chain[depth-1].key));
if (err)
if (err) {
mutex_unlock(&ei->truncate_mutex);
goto cleanup;
}
}
if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)
goto changed;
ext2_splice_branch(inode, iblock, partial, indirect_blks, count);
mutex_unlock(&ei->truncate_mutex);
set_buffer_new(bh_result);
goto got_it;
got_it:
map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));
if (count > blocks_to_boundary)
set_buffer_boundary(bh_result);
err = count;
/* Clean up and exit */
partial = chain + depth - 1; /* the whole chain */
cleanup:
while (partial > chain) {
brelse(partial->bh);
partial--;
}
return err;
changed:
while (partial > chain) {
brelse(partial->bh);
@ -625,6 +696,19 @@ changed:
goto reread;
}
int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
{
unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;
int ret = ext2_get_blocks(inode, iblock, max_blocks,
bh_result, create);
if (ret > 0) {
bh_result->b_size = (ret << inode->i_blkbits);
ret = 0;
}
return ret;
}
static int ext2_writepage(struct page *page, struct writeback_control *wbc)
{
return block_write_full_page(page, ext2_get_block, wbc);
@ -913,9 +997,10 @@ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int de
ext2_free_data(inode, p, q);
}
void ext2_truncate (struct inode * inode)
void ext2_truncate(struct inode *inode)
{
__le32 *i_data = EXT2_I(inode)->i_data;
struct ext2_inode_info *ei = EXT2_I(inode);
int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int offsets[4];
Indirect chain[4];
@ -933,8 +1018,6 @@ void ext2_truncate (struct inode * inode)
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return;
ext2_discard_prealloc(inode);
blocksize = inode->i_sb->s_blocksize;
iblock = (inode->i_size + blocksize-1)
>> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
@ -952,6 +1035,12 @@ void ext2_truncate (struct inode * inode)
if (n == 0)
return;
/*
* From here we block out all ext2_get_block() callers who want to
* modify the block allocation tree.
*/
mutex_lock(&ei->truncate_mutex);
if (n == 1) {
ext2_free_data(inode, i_data+offsets[0],
i_data + EXT2_NDIR_BLOCKS);
@ -1004,6 +1093,10 @@ do_indirects:
case EXT2_TIND_BLOCK:
;
}
ext2_discard_reservation(inode);
mutex_unlock(&ei->truncate_mutex);
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
if (inode_needs_sync(inode)) {
sync_mapping_buffers(inode->i_mapping);
@ -1104,6 +1197,8 @@ void ext2_read_inode (struct inode * inode)
ei->i_acl = EXT2_ACL_NOT_CACHED;
ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif
ei->i_block_alloc_info = NULL;
if (IS_ERR(raw_inode))
goto bad_inode;
@ -1145,9 +1240,6 @@ void ext2_read_inode (struct inode * inode)
ei->i_dtime = 0;
inode->i_generation = le32_to_cpu(raw_inode->i_generation);
ei->i_state = 0;
ei->i_next_alloc_block = 0;
ei->i_next_alloc_goal = 0;
ei->i_prealloc_count = 0;
ei->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
ei->i_dir_start_lookup = 0;

View File

@ -22,6 +22,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
{
struct ext2_inode_info *ei = EXT2_I(inode);
unsigned int flags;
unsigned short rsv_window_size;
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
@ -83,6 +84,50 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
return 0;
case EXT2_IOC_GETRSVSZ:
if (test_opt(inode->i_sb, RESERVATION)
&& S_ISREG(inode->i_mode)
&& ei->i_block_alloc_info) {
rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
return put_user(rsv_window_size, (int __user *)arg);
}
return -ENOTTY;
case EXT2_IOC_SETRSVSZ: {
if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))
return -ENOTTY;
if (IS_RDONLY(inode))
return -EROFS;
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
return -EACCES;
if (get_user(rsv_window_size, (int __user *)arg))
return -EFAULT;
if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS)
rsv_window_size = EXT2_MAX_RESERVE_BLOCKS;
/*
* need to allocate reservation structure for this inode
* before set the window size
*/
/*
* XXX What lock should protect the rsv_goal_size?
* Accessed in ext2_get_block only. ext3 uses i_truncate.
*/
mutex_lock(&ei->truncate_mutex);
if (!ei->i_block_alloc_info)
ext2_init_block_alloc_info(inode);
if (ei->i_block_alloc_info){
struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
rsv->rsv_goal_size = rsv_window_size;
}
mutex_unlock(&ei->truncate_mutex);
return 0;
}
default:
return -ENOTTY;
}

View File

@ -149,6 +149,7 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
ei->i_acl = EXT2_ACL_NOT_CACHED;
ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif
ei->i_block_alloc_info = NULL;
ei->vfs_inode.i_version = 1;
return &ei->vfs_inode;
}
@ -166,6 +167,7 @@ static void init_once(struct kmem_cache * cachep, void *foo)
#ifdef CONFIG_EXT2_FS_XATTR
init_rwsem(&ei->xattr_sem);
#endif
mutex_init(&ei->truncate_mutex);
inode_init_once(&ei->vfs_inode);
}
@ -188,6 +190,7 @@ static void destroy_inodecache(void)
static void ext2_clear_inode(struct inode *inode)
{
struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info;
#ifdef CONFIG_EXT2_FS_POSIX_ACL
struct ext2_inode_info *ei = EXT2_I(inode);
@ -200,6 +203,10 @@ static void ext2_clear_inode(struct inode *inode)
ei->i_default_acl = EXT2_ACL_NOT_CACHED;
}
#endif
ext2_discard_reservation(inode);
EXT2_I(inode)->i_block_alloc_info = NULL;
if (unlikely(rsv))
kfree(rsv);
}
static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs)
@ -291,7 +298,6 @@ static const struct super_operations ext2_sops = {
.destroy_inode = ext2_destroy_inode,
.read_inode = ext2_read_inode,
.write_inode = ext2_write_inode,
.put_inode = ext2_put_inode,
.delete_inode = ext2_delete_inode,
.put_super = ext2_put_super,
.write_super = ext2_write_super,
@ -379,7 +385,7 @@ enum {
Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,
Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr,
Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota,
Opt_usrquota, Opt_grpquota
Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation
};
static match_table_t tokens = {
@ -411,6 +417,8 @@ static match_table_t tokens = {
{Opt_ignore, "noquota"},
{Opt_quota, "quota"},
{Opt_usrquota, "usrquota"},
{Opt_reservation, "reservation"},
{Opt_noreservation, "noreservation"},
{Opt_err, NULL}
};
@ -543,6 +551,14 @@ static int parse_options (char * options,
break;
#endif
case Opt_reservation:
set_opt(sbi->s_mount_opt, RESERVATION);
printk("reservations ON\n");
break;
case Opt_noreservation:
clear_opt(sbi->s_mount_opt, RESERVATION);
printk("reservations OFF\n");
break;
case Opt_ignore:
break;
default:
@ -784,6 +800,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
set_opt(sbi->s_mount_opt, RESERVATION);
if (!parse_options ((char *) data, sbi))
goto failed_mount;
@ -965,6 +983,21 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
spin_lock_init(&sbi->s_next_gen_lock);
/* per fileystem reservation list head & lock */
spin_lock_init(&sbi->s_rsv_window_lock);
sbi->s_rsv_window_root = RB_ROOT;
/*
* Add a single, static dummy reservation to the start of the
* reservation window list --- it gives us a placeholder for
* append-at-start-of-list which makes the allocation logic
* _much_ simpler.
*/
sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
sbi->s_rsv_window_head.rsv_alloc_hit = 0;
sbi->s_rsv_window_head.rsv_goal_size = 0;
ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
err = percpu_counter_init(&sbi->s_freeblocks_counter,
ext2_count_free_blocks(sb));
if (!err) {
@ -1260,7 +1293,7 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data,
tmp_bh.b_state = 0;
err = ext2_get_block(inode, blk, &tmp_bh, 0);
if (err)
if (err < 0)
return err;
if (!buffer_mapped(&tmp_bh)) /* A hole? */
memset(data, 0, tocopy);
@ -1299,7 +1332,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type,
tmp_bh.b_state = 0;
err = ext2_get_block(inode, blk, &tmp_bh, 1);
if (err)
if (err < 0)
goto out;
if (offset || tocopy != EXT2_BLOCK_SIZE(sb))
bh = sb_bread(sb, tmp_bh.b_blocknr);

View File

@ -664,8 +664,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh,
s_first_data_block) +
EXT2_I(inode)->i_block_group *
EXT2_BLOCKS_PER_GROUP(sb);
int block = ext2_new_block(inode, goal,
NULL, NULL, &error);
int block = ext2_new_block(inode, goal, &error);
if (error)
goto cleanup;
ea_idebug(inode, "creating block %d", block);

View File

@ -29,11 +29,12 @@
#undef EXT2FS_DEBUG
/*
* Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
* Define EXT2_RESERVATION to reserve data blocks for expanding files
*/
#define EXT2_PREALLOCATE
#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
#define EXT2_DEFAULT_RESERVE_BLOCKS 8
/*max window size: 1024(direct blocks) + 3([t,d]indirect blocks) */
#define EXT2_MAX_RESERVE_BLOCKS 1027
#define EXT2_RESERVE_WINDOW_NOT_ALLOCATED 0
/*
* The second extended file system version
*/
@ -200,6 +201,8 @@ struct ext2_group_desc
#define EXT2_IOC_SETFLAGS FS_IOC_SETFLAGS
#define EXT2_IOC_GETVERSION FS_IOC_GETVERSION
#define EXT2_IOC_SETVERSION FS_IOC_SETVERSION
#define EXT2_IOC_GETRSVSZ _IOR('f', 5, long)
#define EXT2_IOC_SETRSVSZ _IOW('f', 6, long)
/*
* ioctl commands in 32 bit emulation
@ -319,6 +322,7 @@ struct ext2_inode {
#define EXT2_MOUNT_XIP 0x010000 /* Execute in place */
#define EXT2_MOUNT_USRQUOTA 0x020000 /* user quota */
#define EXT2_MOUNT_GRPQUOTA 0x040000 /* group quota */
#define EXT2_MOUNT_RESERVATION 0x080000 /* Preallocation */
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
@ -558,4 +562,11 @@ enum {
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
static inline ext2_fsblk_t
ext2_group_first_block_no(struct super_block *sb, unsigned long group_no)
{
return group_no * (ext2_fsblk_t)EXT2_BLOCKS_PER_GROUP(sb) +
le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block);
}
#endif /* _LINUX_EXT2_FS_H */

View File

@ -18,6 +18,52 @@
#include <linux/blockgroup_lock.h>
#include <linux/percpu_counter.h>
#include <linux/rbtree.h>
/* XXX Here for now... not interested in restructing headers JUST now */
/* data type for block offset of block group */
typedef int ext2_grpblk_t;
/* data type for filesystem-wide blocks number */
typedef unsigned long ext2_fsblk_t;
#define E2FSBLK "%lu"
struct ext2_reserve_window {
ext2_fsblk_t _rsv_start; /* First byte reserved */
ext2_fsblk_t _rsv_end; /* Last byte reserved or 0 */
};
struct ext2_reserve_window_node {
struct rb_node rsv_node;
__u32 rsv_goal_size;
__u32 rsv_alloc_hit;
struct ext2_reserve_window rsv_window;
};
struct ext2_block_alloc_info {
/* information about reservation window */
struct ext2_reserve_window_node rsv_window_node;
/*
* was i_next_alloc_block in ext2_inode_info
* is the logical (file-relative) number of the
* most-recently-allocated block in this file.
* We use this for detecting linearly ascending allocation requests.
*/
__u32 last_alloc_logical_block;
/*
* Was i_next_alloc_goal in ext2_inode_info
* is the *physical* companion to i_next_alloc_block.
* it the the physical block number of the block which was most-recentl
* allocated to this file. This give us the goal (target) for the next
* allocation when we detect linearly ascending requests.
*/
ext2_fsblk_t last_alloc_physical_block;
};
#define rsv_start rsv_window._rsv_start
#define rsv_end rsv_window._rsv_end
/*
* second extended-fs super-block data in memory
@ -56,6 +102,10 @@ struct ext2_sb_info {
struct percpu_counter s_freeinodes_counter;
struct percpu_counter s_dirs_counter;
struct blockgroup_lock s_blockgroup_lock;
/* root of the per fs reservation window tree */
spinlock_t s_rsv_window_lock;
struct rb_root s_rsv_window_root;
struct ext2_reserve_window_node s_rsv_window_head;
};
#endif /* _LINUX_EXT2_FS_SB */