mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-24 09:13:20 -05:00
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 missed5dea5176e5
] [akpm@linux-foundation.org: cross-porte6022603b9
] [akpm@linux-foundation.org: Port the omitted08fb306fe6
] [akpm@linux-foundation.org: Backport the missed20acaa18d0
] [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:
parent
369f2389e7
commit
a686cd898b
10 changed files with 1569 additions and 500 deletions
1337
fs/ext2/balloc.c
1337
fs/ext2/balloc.c
File diff suppressed because it is too large
Load diff
|
@ -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 *);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -581,11 +581,8 @@ got:
|
||||||
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);
|
||||||
|
|
522
fs/ext2/inode.c
522
fs/ext2/inode.c
|
@ -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 @@ no_delete:
|
||||||
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 @@ changed:
|
||||||
* 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 @@ out:
|
||||||
*/
|
*/
|
||||||
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 @@ changed:
|
||||||
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 @@ do_indirects:
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Add table
Reference in a new issue