Commit bca17d03 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ext3: avoid reading empty inode blocks

From: Alex Tomas <bzzz@tmi.comex.ru>

ext3_get_inode_loc() read inode's block only if:

  1) this inode has no copy in memory
  2) inode's block has another valid inode(s)

this optimization allows to avoid needless I/O in two cases:

1) just allocated inode is first valid in the inode's block

2) kernel wants to write inode, but buffer in which inode
   belongs to gets freed by VM
parent 12affe8f
...@@ -2337,26 +2337,114 @@ static unsigned long ext3_get_inode_block(struct super_block *sb, ...@@ -2337,26 +2337,114 @@ static unsigned long ext3_get_inode_block(struct super_block *sb,
} }
/* /*
* ext3_get_inode_loc returns with an extra refcount against the * ext3_get_inode_loc returns with an extra refcount against the inode's
* inode's underlying buffer_head on success. * underlying buffer_head on success. If `in_mem' is false then we're purely
* trying to determine the inode's location on-disk and no read need be
* performed.
*/ */
static int ext3_get_inode_loc(struct inode *inode,
int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc) struct ext3_iloc *iloc, int in_mem)
{ {
unsigned long block; unsigned long block;
struct buffer_head *bh;
block = ext3_get_inode_block(inode->i_sb, inode->i_ino, iloc); block = ext3_get_inode_block(inode->i_sb, inode->i_ino, iloc);
if (block) { if (!block)
struct buffer_head *bh = sb_bread(inode->i_sb, block); return -EIO;
if (bh) {
iloc->bh = bh; bh = sb_getblk(inode->i_sb, block);
return 0; if (!bh) {
}
ext3_error (inode->i_sb, "ext3_get_inode_loc", ext3_error (inode->i_sb, "ext3_get_inode_loc",
"unable to read inode block - " "unable to read inode block - "
"inode=%lu, block=%lu", inode->i_ino, block); "inode=%lu, block=%lu", inode->i_ino, block);
return -EIO;
}
if (!buffer_uptodate(bh)) {
lock_buffer(bh);
if (buffer_uptodate(bh)) {
/* someone brought it uptodate while we waited */
unlock_buffer(bh);
goto has_buffer;
}
/* we can't skip I/O if inode is on a disk only */
if (in_mem) {
struct buffer_head *bitmap_bh;
struct ext3_group_desc *desc;
int inodes_per_buffer;
int inode_offset, i;
int block_group;
int start;
/*
* If this is the only valid inode in the block we
* need not read the block.
*/
block_group = (inode->i_ino - 1) /
EXT3_INODES_PER_GROUP(inode->i_sb);
inodes_per_buffer = bh->b_size /
EXT3_INODE_SIZE(inode->i_sb);
inode_offset = ((inode->i_ino - 1) %
EXT3_INODES_PER_GROUP(inode->i_sb));
start = inode_offset & ~(inodes_per_buffer - 1);
/* Is the inode bitmap in cache? */
desc = ext3_get_group_desc(inode->i_sb,
block_group, NULL);
if (!desc)
goto make_io;
bitmap_bh = sb_getblk(inode->i_sb,
le32_to_cpu(desc->bg_inode_bitmap));
if (!bitmap_bh)
goto make_io;
/*
* If the inode bitmap isn't in cache then the
* optimisation may end up performing two reads instead
* of one, so skip it.
*/
if (!buffer_uptodate(bitmap_bh)) {
brelse(bitmap_bh);
goto make_io;
} }
for (i = start; i < start + inodes_per_buffer; i++) {
if (i == inode_offset)
continue;
if (ext3_test_bit(i, bitmap_bh->b_data))
break;
}
brelse(bitmap_bh);
if (i == start + inodes_per_buffer) {
/* all other inodes are free, so skip I/O */
memset(bh->b_data, 0, bh->b_size);
set_buffer_uptodate(bh);
unlock_buffer(bh);
goto has_buffer;
}
}
make_io:
/*
* There are another valid inodes in the buffer so we must
* read the block from disk
*/
get_bh(bh);
bh->b_end_io = end_buffer_io_sync;
submit_bh(READ, bh);
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
ext3_error(inode->i_sb, "ext3_get_inode_loc",
"unable to read inode block - "
"inode=%lu, block=%lu",
inode->i_ino, block);
brelse(bh);
return -EIO; return -EIO;
}
}
has_buffer:
iloc->bh = bh;
return 0;
} }
void ext3_set_inode_flags(struct inode *inode) void ext3_set_inode_flags(struct inode *inode)
...@@ -2376,7 +2464,6 @@ void ext3_set_inode_flags(struct inode *inode) ...@@ -2376,7 +2464,6 @@ void ext3_set_inode_flags(struct inode *inode)
inode->i_flags |= S_DIRSYNC; inode->i_flags |= S_DIRSYNC;
} }
void ext3_read_inode(struct inode * inode) void ext3_read_inode(struct inode * inode)
{ {
struct ext3_iloc iloc; struct ext3_iloc iloc;
...@@ -2389,7 +2476,7 @@ void ext3_read_inode(struct inode * inode) ...@@ -2389,7 +2476,7 @@ void ext3_read_inode(struct inode * inode)
ei->i_acl = EXT3_ACL_NOT_CACHED; ei->i_acl = EXT3_ACL_NOT_CACHED;
ei->i_default_acl = EXT3_ACL_NOT_CACHED; ei->i_default_acl = EXT3_ACL_NOT_CACHED;
#endif #endif
if (ext3_get_inode_loc(inode, &iloc)) if (ext3_get_inode_loc(inode, &iloc, 0))
goto bad_inode; goto bad_inode;
bh = iloc.bh; bh = iloc.bh;
raw_inode = ext3_raw_inode(&iloc); raw_inode = ext3_raw_inode(&iloc);
...@@ -2793,7 +2880,7 @@ ext3_reserve_inode_write(handle_t *handle, struct inode *inode, ...@@ -2793,7 +2880,7 @@ ext3_reserve_inode_write(handle_t *handle, struct inode *inode,
{ {
int err = 0; int err = 0;
if (handle) { if (handle) {
err = ext3_get_inode_loc(inode, iloc); err = ext3_get_inode_loc(inode, iloc, 1);
if (!err) { if (!err) {
BUFFER_TRACE(iloc->bh, "get_write_access"); BUFFER_TRACE(iloc->bh, "get_write_access");
err = ext3_journal_get_write_access(handle, iloc->bh); err = ext3_journal_get_write_access(handle, iloc->bh);
...@@ -2891,7 +2978,7 @@ ext3_pin_inode(handle_t *handle, struct inode *inode) ...@@ -2891,7 +2978,7 @@ ext3_pin_inode(handle_t *handle, struct inode *inode)
int err = 0; int err = 0;
if (handle) { if (handle) {
err = ext3_get_inode_loc(inode, &iloc); err = ext3_get_inode_loc(inode, &iloc, 1);
if (!err) { if (!err) {
BUFFER_TRACE(iloc.bh, "get_write_access"); BUFFER_TRACE(iloc.bh, "get_write_access");
err = journal_get_write_access(handle, iloc.bh); err = journal_get_write_access(handle, iloc.bh);
......
...@@ -721,7 +721,6 @@ extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, in ...@@ -721,7 +721,6 @@ extern int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, in
extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *); extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *); extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
extern int ext3_get_inode_loc (struct inode *, struct ext3_iloc *);
extern void ext3_read_inode (struct inode *); extern void ext3_read_inode (struct inode *);
extern void ext3_write_inode (struct inode *, int); extern void ext3_write_inode (struct inode *, int);
extern int ext3_setattr (struct dentry *, struct iattr *); extern int ext3_setattr (struct dentry *, struct iattr *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment