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; __u32 i_block_group;
/* /* block reservation info */
* i_next_alloc_block is the logical (file-relative) number of the struct ext2_block_alloc_info *i_block_alloc_info;
* 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;
/*
* 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; __u32 i_dir_start_lookup;
#ifdef CONFIG_EXT2_FS_XATTR #ifdef CONFIG_EXT2_FS_XATTR
/* /*
@ -65,7 +52,16 @@ struct ext2_inode_info {
struct posix_acl *i_default_acl; struct posix_acl *i_default_acl;
#endif #endif
rwlock_t i_meta_lock; 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 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 */ /* balloc.c */
extern int ext2_bg_has_super(struct super_block *sb, int group); 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 unsigned long ext2_bg_num_gdb(struct super_block *sb, int group);
extern int ext2_new_block (struct inode *, unsigned long, extern ext2_fsblk_t ext2_new_block(struct inode *, unsigned long, int *);
__u32 *, __u32 *, int *); extern ext2_fsblk_t ext2_new_blocks(struct inode *, unsigned long,
unsigned long *, int *);
extern void ext2_free_blocks (struct inode *, unsigned long, extern void ext2_free_blocks (struct inode *, unsigned long,
unsigned long); unsigned long);
extern unsigned long ext2_count_free_blocks (struct super_block *); 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, extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
unsigned int block_group, unsigned int block_group,
struct buffer_head ** bh); 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 */ /* dir.c */
extern int ext2_add_link (struct dentry *, struct inode *); 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_put_inode (struct inode *);
extern void ext2_delete_inode (struct inode *); extern void ext2_delete_inode (struct inode *);
extern int ext2_sync_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 int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern void ext2_truncate (struct inode *); extern void ext2_truncate (struct inode *);
extern int ext2_setattr (struct dentry *, struct iattr *); 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) static int ext2_release_file (struct inode * inode, struct file * filp)
{ {
if (filp->f_mode & FMODE_WRITE) if (filp->f_mode & FMODE_WRITE) {
ext2_discard_prealloc (inode); mutex_lock(&EXT2_I(inode)->truncate_mutex);
ext2_discard_reservation(inode);
mutex_unlock(&EXT2_I(inode)->truncate_mutex);
}
return 0; return 0;
} }

View file

@ -581,11 +581,8 @@ struct inode *ext2_new_inode(struct inode *dir, int mode)
ei->i_file_acl = 0; ei->i_file_acl = 0;
ei->i_dir_acl = 0; ei->i_dir_acl = 0;
ei->i_dtime = 0; ei->i_dtime = 0;
ei->i_block_alloc_info = NULL;
ei->i_block_group = group; 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_dir_start_lookup = 0;
ei->i_state = EXT2_STATE_NEW; ei->i_state = EXT2_STATE_NEW;
ext2_set_inode_flags(inode); 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); 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. * Called at the last iput() if i_nlink is zero.
*/ */
@ -89,61 +76,6 @@ void ext2_delete_inode (struct inode * inode)
clear_inode(inode); /* We must guarantee clearing of inode... */ 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 { typedef struct {
__le32 *p; __le32 *p;
__le32 key; __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"); ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");
} }
if (boundary) if (boundary)
*boundary = (i_block & (ptrs - 1)) == (final - 1); *boundary = final - 1 - (i_block & (ptrs - 1));
return n; return n;
} }
@ -355,39 +288,129 @@ static unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
* @block: block we want * @block: block we want
* @chain: chain of indirect blocks * @chain: chain of indirect blocks
* @partial: pointer to the last triple within a chain * @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, * Returns preferred place for a block (the goal).
* stores it in *@goal and returns zero. If the branch had been changed
* under us we return -EAGAIN.
*/ */
static inline int ext2_find_goal(struct inode *inode, static inline int ext2_find_goal(struct inode *inode,
long block, long block,
Indirect chain[4], Indirect chain[4],
Indirect *partial, Indirect *partial)
unsigned long *goal)
{ {
struct ext2_inode_info *ei = EXT2_I(inode); struct ext2_block_alloc_info *block_i;
write_lock(&ei->i_meta_lock);
if ((block == ei->i_next_alloc_block + 1) && ei->i_next_alloc_goal) { block_i = EXT2_I(inode)->i_block_alloc_info;
ei->i_next_alloc_block++;
ei->i_next_alloc_goal++; /*
} * try the heuristic for sequential allocation,
if (verify_chain(chain, partial)) { * failing that at least try to get decent locality.
/* */
* try the heuristic for sequential allocation, if (block_i && (block == block_i->last_alloc_logical_block + 1)
* failing that at least try to get decent locality. && (block_i->last_alloc_physical_block != 0)) {
*/ return block_i->last_alloc_physical_block + 1;
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;
} }
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, static int ext2_alloc_branch(struct inode *inode,
int num, int indirect_blks, int *blks, ext2_fsblk_t goal,
unsigned long goal, int *offsets, Indirect *branch)
int *offsets,
Indirect *branch)
{ {
int blocksize = inode->i_sb->s_blocksize; int blocksize = inode->i_sb->s_blocksize;
int n = 0; int i, n = 0;
int err; int err = 0;
int i; struct buffer_head *bh;
int parent = ext2_alloc_block(inode, goal, &err); int num;
ext2_fsblk_t new_blocks[4];
ext2_fsblk_t current_block;
branch[0].key = cpu_to_le32(parent); num = ext2_alloc_blocks(inode, goal, indirect_blks,
if (parent) for (n = 1; n < num; n++) { *blks, new_blocks, &err);
struct buffer_head *bh; if (err)
/* Allocate the next block */ return err;
int nr = ext2_alloc_block(inode, parent, &err);
if (!nr) branch[0].key = cpu_to_le32(new_blocks[0]);
break; /*
branch[n].key = cpu_to_le32(nr); * metadata blocks and data blocks are allocated.
*/
for (n = 1; n <= indirect_blks; n++) {
/* /*
* Get buffer_head for parent block, zero it out and set * Get buffer_head for parent block, zero it out
* the pointer to new one, then send parent to disk. * and set the pointer to new one, then send
* parent to disk.
*/ */
bh = sb_getblk(inode->i_sb, parent); bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
if (!bh) { branch[n].bh = bh;
err = -EIO;
break;
}
lock_buffer(bh); lock_buffer(bh);
memset(bh->b_data, 0, blocksize); memset(bh->b_data, 0, blocksize);
branch[n].bh = bh;
branch[n].p = (__le32 *) bh->b_data + offsets[n]; branch[n].p = (__le32 *) bh->b_data + offsets[n];
branch[n].key = cpu_to_le32(new_blocks[n]);
*branch[n].p = branch[n].key; *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); set_buffer_uptodate(bh);
unlock_buffer(bh); unlock_buffer(bh);
mark_buffer_dirty_inode(bh, inode); mark_buffer_dirty_inode(bh, inode);
@ -458,77 +491,68 @@ static int ext2_alloc_branch(struct inode *inode,
*/ */
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode))
sync_dirty_buffer(bh); sync_dirty_buffer(bh);
parent = nr;
} }
if (n == num) *blks = 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);
return err; return err;
} }
/** /**
* ext2_splice_branch - splice the allocated branch onto inode. * ext2_splice_branch - splice the allocated branch onto inode.
* @inode: owner * @inode: owner
* @block: (logical) number of block we are adding * @block: (logical) number of block we are adding
* @chain: chain of indirect blocks (with a missing link - see * @chain: chain of indirect blocks (with a missing link - see
* ext2_alloc_branch) * ext2_alloc_branch)
* @where: location of missing link * @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 * This function fills the missing link and does all housekeeping needed in
* changed, fills the missing link and does all housekeeping needed in * inode (->i_blocks, etc.). In case of success we end up with the full
* inode (->i_blocks, etc.). In case of success we end up with the full * chain to new block and return 0.
* 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.
*/ */
static void ext2_splice_branch(struct inode *inode,
static inline int ext2_splice_branch(struct inode *inode, long block, Indirect *where, int num, int blks)
long block,
Indirect chain[4],
Indirect *where,
int num)
{ {
struct ext2_inode_info *ei = EXT2_I(inode);
int i; 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 */ block_i = EXT2_I(inode)->i_block_alloc_info;
write_lock(&ei->i_meta_lock);
if (!verify_chain(chain, where-1) || *where->p)
goto changed;
/* XXX LOCKING probably should have i_meta_lock ?*/
/* That's it */ /* That's it */
*where->p = where->key; *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 */ /* 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? */ /* had we spliced it onto indirect block? */
if (where->bh) if (where->bh)
mark_buffer_dirty_inode(where->bh, inode); mark_buffer_dirty_inode(where->bh, inode);
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode); 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 @@ static inline int ext2_splice_branch(struct inode *inode,
* That has a nice additional property: no special recovery from the failed * That has a nice additional property: no special recovery from the failed
* allocations is needed - we simply release blocks and do not touch anything * allocations is needed - we simply release blocks and do not touch anything
* reachable from inode. * 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.
*/ */
static int ext2_get_blocks(struct inode *inode,
int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) sector_t iblock, unsigned long maxblocks,
struct buffer_head *bh_result,
int create)
{ {
int err = -EIO; int err = -EIO;
int offsets[4]; int offsets[4];
Indirect chain[4]; Indirect chain[4];
Indirect *partial; Indirect *partial;
unsigned long goal; ext2_fsblk_t goal;
int left; int indirect_blks;
int boundary = 0; int blocks_to_boundary = 0;
int depth = ext2_block_to_path(inode, iblock, offsets, &boundary); 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) if (depth == 0)
goto out; return (err);
reread: reread:
partial = ext2_get_branch(inode, depth, offsets, chain, &err); partial = ext2_get_branch(inode, depth, offsets, chain, &err);
/* Simplest case - block found, no allocation needed */ /* Simplest case - block found, no allocation needed */
if (!partial) { if (!partial) {
got_it: first_block = le32_to_cpu(chain[depth - 1].key);
map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key)); clear_buffer_new(bh_result); /* What's this do? */
if (boundary) count++;
set_buffer_boundary(bh_result); /*map more blocks*/
/* Clean up and exit */ while (count < maxblocks && count <= blocks_to_boundary) {
partial = chain+depth-1; /* the whole chain */ ext2_fsblk_t blk;
goto cleanup;
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 */ /* Next simple case - plain lookup or failed read of indirect block */
if (!create || err == -EIO) { if (!create || err == -EIO)
cleanup: goto cleanup;
while (partial > chain) {
brelse(partial->bh); mutex_lock(&ei->truncate_mutex);
partial--;
}
out:
return err;
}
/* /*
* Indirect block might be removed by truncate while we were * Okay, we need to do block allocation. Lazily initialize the block
* reading it. Handling of that case (forget what we've got and * allocation info here if necessary
* reread) is taken out of the main path. */
if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info))
ext2_init_block_alloc_info(inode);
goal = ext2_find_goal(inode, iblock, chain, partial);
/* 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.
*/ */
if (err == -EAGAIN) count = ext2_blks_to_allocate(partial, indirect_blks,
goto changed; 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);
goal = 0; if (err) {
if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0) mutex_unlock(&ei->truncate_mutex);
goto changed;
left = (chain + depth) - partial;
err = ext2_alloc_branch(inode, left, goal,
offsets+(partial-chain), partial);
if (err)
goto cleanup; goto cleanup;
}
if (ext2_use_xip(inode->i_sb)) { if (ext2_use_xip(inode->i_sb)) {
/* /*
@ -607,16 +666,28 @@ int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_
*/ */
err = ext2_clear_xip_target (inode, err = ext2_clear_xip_target (inode,
le32_to_cpu(chain[depth-1].key)); le32_to_cpu(chain[depth-1].key));
if (err) if (err) {
mutex_unlock(&ei->truncate_mutex);
goto cleanup; goto cleanup;
}
} }
if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0) ext2_splice_branch(inode, iblock, partial, indirect_blks, count);
goto changed; mutex_unlock(&ei->truncate_mutex);
set_buffer_new(bh_result); 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: changed:
while (partial > chain) { while (partial > chain) {
brelse(partial->bh); brelse(partial->bh);
@ -625,6 +696,19 @@ int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_
goto reread; 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) static int ext2_writepage(struct page *page, struct writeback_control *wbc)
{ {
return block_write_full_page(page, ext2_get_block, 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); 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; __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 addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
int offsets[4]; int offsets[4];
Indirect chain[4]; Indirect chain[4];
@ -933,8 +1018,6 @@ void ext2_truncate (struct inode * inode)
if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return; return;
ext2_discard_prealloc(inode);
blocksize = inode->i_sb->s_blocksize; blocksize = inode->i_sb->s_blocksize;
iblock = (inode->i_size + blocksize-1) iblock = (inode->i_size + blocksize-1)
>> EXT2_BLOCK_SIZE_BITS(inode->i_sb); >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
@ -952,6 +1035,12 @@ void ext2_truncate (struct inode * inode)
if (n == 0) if (n == 0)
return; 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) { if (n == 1) {
ext2_free_data(inode, i_data+offsets[0], ext2_free_data(inode, i_data+offsets[0],
i_data + EXT2_NDIR_BLOCKS); i_data + EXT2_NDIR_BLOCKS);
@ -1004,6 +1093,10 @@ void ext2_truncate (struct inode * inode)
case EXT2_TIND_BLOCK: case EXT2_TIND_BLOCK:
; ;
} }
ext2_discard_reservation(inode);
mutex_unlock(&ei->truncate_mutex);
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
if (inode_needs_sync(inode)) { if (inode_needs_sync(inode)) {
sync_mapping_buffers(inode->i_mapping); 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_acl = EXT2_ACL_NOT_CACHED;
ei->i_default_acl = EXT2_ACL_NOT_CACHED; ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif #endif
ei->i_block_alloc_info = NULL;
if (IS_ERR(raw_inode)) if (IS_ERR(raw_inode))
goto bad_inode; goto bad_inode;
@ -1145,9 +1240,6 @@ void ext2_read_inode (struct inode * inode)
ei->i_dtime = 0; ei->i_dtime = 0;
inode->i_generation = le32_to_cpu(raw_inode->i_generation); inode->i_generation = le32_to_cpu(raw_inode->i_generation);
ei->i_state = 0; 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_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
ei->i_dir_start_lookup = 0; 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); struct ext2_inode_info *ei = EXT2_I(inode);
unsigned int flags; unsigned int flags;
unsigned short rsv_window_size;
ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); 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; inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode); mark_inode_dirty(inode);
return 0; 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: default:
return -ENOTTY; 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_acl = EXT2_ACL_NOT_CACHED;
ei->i_default_acl = EXT2_ACL_NOT_CACHED; ei->i_default_acl = EXT2_ACL_NOT_CACHED;
#endif #endif
ei->i_block_alloc_info = NULL;
ei->vfs_inode.i_version = 1; ei->vfs_inode.i_version = 1;
return &ei->vfs_inode; return &ei->vfs_inode;
} }
@ -166,6 +167,7 @@ static void init_once(struct kmem_cache * cachep, void *foo)
#ifdef CONFIG_EXT2_FS_XATTR #ifdef CONFIG_EXT2_FS_XATTR
init_rwsem(&ei->xattr_sem); init_rwsem(&ei->xattr_sem);
#endif #endif
mutex_init(&ei->truncate_mutex);
inode_init_once(&ei->vfs_inode); inode_init_once(&ei->vfs_inode);
} }
@ -188,6 +190,7 @@ static void destroy_inodecache(void)
static void ext2_clear_inode(struct inode *inode) 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 #ifdef CONFIG_EXT2_FS_POSIX_ACL
struct ext2_inode_info *ei = EXT2_I(inode); 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; ei->i_default_acl = EXT2_ACL_NOT_CACHED;
} }
#endif #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) 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, .destroy_inode = ext2_destroy_inode,
.read_inode = ext2_read_inode, .read_inode = ext2_read_inode,
.write_inode = ext2_write_inode, .write_inode = ext2_write_inode,
.put_inode = ext2_put_inode,
.delete_inode = ext2_delete_inode, .delete_inode = ext2_delete_inode,
.put_super = ext2_put_super, .put_super = ext2_put_super,
.write_super = ext2_write_super, .write_super = ext2_write_super,
@ -379,7 +385,7 @@ enum {
Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,
Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, 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_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 = { static match_table_t tokens = {
@ -411,6 +417,8 @@ static match_table_t tokens = {
{Opt_ignore, "noquota"}, {Opt_ignore, "noquota"},
{Opt_quota, "quota"}, {Opt_quota, "quota"},
{Opt_usrquota, "usrquota"}, {Opt_usrquota, "usrquota"},
{Opt_reservation, "reservation"},
{Opt_noreservation, "noreservation"},
{Opt_err, NULL} {Opt_err, NULL}
}; };
@ -543,6 +551,14 @@ static int parse_options (char * options,
break; break;
#endif #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: case Opt_ignore:
break; break;
default: 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_resuid = le16_to_cpu(es->s_def_resuid);
sbi->s_resgid = le16_to_cpu(es->s_def_resgid); sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
set_opt(sbi->s_mount_opt, RESERVATION);
if (!parse_options ((char *) data, sbi)) if (!parse_options ((char *) data, sbi))
goto failed_mount; 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)); get_random_bytes(&sbi->s_next_generation, sizeof(u32));
spin_lock_init(&sbi->s_next_gen_lock); 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, err = percpu_counter_init(&sbi->s_freeblocks_counter,
ext2_count_free_blocks(sb)); ext2_count_free_blocks(sb));
if (!err) { 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; tmp_bh.b_state = 0;
err = ext2_get_block(inode, blk, &tmp_bh, 0); err = ext2_get_block(inode, blk, &tmp_bh, 0);
if (err) if (err < 0)
return err; return err;
if (!buffer_mapped(&tmp_bh)) /* A hole? */ if (!buffer_mapped(&tmp_bh)) /* A hole? */
memset(data, 0, tocopy); 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; tmp_bh.b_state = 0;
err = ext2_get_block(inode, blk, &tmp_bh, 1); err = ext2_get_block(inode, blk, &tmp_bh, 1);
if (err) if (err < 0)
goto out; goto out;
if (offset || tocopy != EXT2_BLOCK_SIZE(sb)) if (offset || tocopy != EXT2_BLOCK_SIZE(sb))
bh = sb_bread(sb, tmp_bh.b_blocknr); 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) + s_first_data_block) +
EXT2_I(inode)->i_block_group * EXT2_I(inode)->i_block_group *
EXT2_BLOCKS_PER_GROUP(sb); EXT2_BLOCKS_PER_GROUP(sb);
int block = ext2_new_block(inode, goal, int block = ext2_new_block(inode, goal, &error);
NULL, NULL, &error);
if (error) if (error)
goto cleanup; goto cleanup;
ea_idebug(inode, "creating block %d", block); ea_idebug(inode, "creating block %d", block);

View file

@ -29,11 +29,12 @@
#undef EXT2FS_DEBUG #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_RESERVE_BLOCKS 8
#define EXT2_DEFAULT_PREALLOC_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 * The second extended file system version
*/ */
@ -200,6 +201,8 @@ struct ext2_group_desc
#define EXT2_IOC_SETFLAGS FS_IOC_SETFLAGS #define EXT2_IOC_SETFLAGS FS_IOC_SETFLAGS
#define EXT2_IOC_GETVERSION FS_IOC_GETVERSION #define EXT2_IOC_GETVERSION FS_IOC_GETVERSION
#define EXT2_IOC_SETVERSION FS_IOC_SETVERSION #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 * ioctl commands in 32 bit emulation
@ -317,8 +320,9 @@ struct ext2_inode {
#define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */ #define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */
#define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */ #define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */
#define EXT2_MOUNT_XIP 0x010000 /* Execute in place */ #define EXT2_MOUNT_XIP 0x010000 /* Execute in place */
#define EXT2_MOUNT_USRQUOTA 0x020000 /* user quota */ #define EXT2_MOUNT_USRQUOTA 0x020000 /* user quota */
#define EXT2_MOUNT_GRPQUOTA 0x040000 /* group quota */ #define EXT2_MOUNT_GRPQUOTA 0x040000 /* group quota */
#define EXT2_MOUNT_RESERVATION 0x080000 /* Preallocation */
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #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) & \ #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~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 */ #endif /* _LINUX_EXT2_FS_H */

View file

@ -18,6 +18,52 @@
#include <linux/blockgroup_lock.h> #include <linux/blockgroup_lock.h>
#include <linux/percpu_counter.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 * 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_freeinodes_counter;
struct percpu_counter s_dirs_counter; struct percpu_counter s_dirs_counter;
struct blockgroup_lock s_blockgroup_lock; 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 */ #endif /* _LINUX_EXT2_FS_SB */