mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 22:24:11 +08: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
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;
|
||||
|
||||
/*
|
||||
* 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 *);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)) {
|
||||
/*
|
||||
* 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;
|
||||
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_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);
|
||||
int i, n = 0;
|
||||
int err = 0;
|
||||
struct buffer_head *bh;
|
||||
int num;
|
||||
ext2_fsblk_t new_blocks[4];
|
||||
ext2_fsblk_t current_block;
|
||||
|
||||
branch[0].key = cpu_to_le32(parent);
|
||||
if (parent) for (n = 1; n < num; n++) {
|
||||
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);
|
||||
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]);
|
||||
/*
|
||||
* 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
|
||||
* the pointer to new one, then send parent to disk.
|
||||
* 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, parent);
|
||||
if (!bh) {
|
||||
err = -EIO;
|
||||
break;
|
||||
}
|
||||
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,77 +491,68 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* ext2_splice_branch - splice the allocated branch onto inode.
|
||||
* @inode: owner
|
||||
* @block: (logical) number of block we are adding
|
||||
* @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
|
||||
* ext2_splice_branch - splice the allocated branch onto inode.
|
||||
* @inode: owner
|
||||
* @block: (logical) number of block we are adding
|
||||
* @chain: chain of indirect blocks (with a missing link - see
|
||||
* ext2_alloc_branch)
|
||||
* @where: location of missing link
|
||||
* @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
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 (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)
|
||||
goto changed;
|
||||
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);
|
||||
|
||||
goal = 0;
|
||||
if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0)
|
||||
goto changed;
|
||||
|
||||
left = (chain + depth) - partial;
|
||||
err = ext2_alloc_branch(inode, left, goal,
|
||||
offsets+(partial-chain), partial);
|
||||
if (err)
|
||||
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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
@ -317,8 +320,9 @@ struct ext2_inode {
|
||||
#define EXT2_MOUNT_XATTR_USER 0x004000 /* Extended user attributes */
|
||||
#define EXT2_MOUNT_POSIX_ACL 0x008000 /* POSIX Access Control Lists */
|
||||
#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_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 */
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user