[RFC 2/5] inode reservation v0.1 (ext4 kernel patch)

coly
2007-05-23T18:06:33Z

The patch is generated based on 2.6.20-ext4-2 branch. you can find the
benchmark from other email.

DO NOT waste time on reading the patch :-) I post this patch here is to
show that I really spent time on it and the patch can work (even not
well).

diff --git a/Makefile b/Makefile
index 7e2750f..21d21e4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,7 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 20
-EXTRAVERSION =
-NAME = Homicidal Dwarf Hamster
+EXTRAVERSION = inores

# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index 11e93c1..daf88b4 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -30,3 +30,29 @@ unsigned long ext4_count_free (struct buffer_head *
map, unsigned int numchars)

#endif /* EXT4FS_DEBUG */

+/*
+ * Read the inode allocation bitmap for a given block_group, reading
+ * into the specified slot in the superblock's bitmap cache.
+ *
+ * Return buffer_head of bitmap on success or NULL.
+ */
+struct buffer_head *
+read_inode_bitmap(struct super_block * sb, unsigned long block_group)
+{
+ struct ext4_group_desc *desc;
+ struct buffer_head *bh = NULL;
+
+ desc = ext4_get_group_desc(sb, block_group, NULL);
+ if (!desc)
+ goto error_out;
+
+ bh = sb_bread(sb, ext4_inode_bitmap(sb, desc));
+ if (!bh)
+ ext4_error(sb, "read_inode_bitmap",
+ "Cannot read inode bitmap - "
+ "block_group = %lu, inode_bitmap = %llu",
+ block_group, ext4_inode_bitmap(sb, desc));
+error_out:
+ return bh;
+}
+
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 427f830..bb83112 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -45,32 +45,6 @@

/*
- * Read the inode allocation bitmap for a given block_group, reading
- * into the specified slot in the superblock's bitmap cache.
- *
- * Return buffer_head of bitmap on success or NULL.
- */
-static struct buffer_head *
-read_inode_bitmap(struct super_block * sb, unsigned long block_group)
-{
- struct ext4_group_desc *desc;
- struct buffer_head *bh = NULL;
-
- desc = ext4_get_group_desc(sb, block_group, NULL);
- if (!desc)
- goto error_out;
-
- bh = sb_bread(sb, ext4_inode_bitmap(sb, desc));
- if (!bh)
- ext4_error(sb, "read_inode_bitmap",
- "Cannot read inode bitmap - "
- "block_group = %lu, inode_bitmap = %llu",
- block_group, ext4_inode_bitmap(sb, desc));
-error_out:
- return bh;
-}
-
-/*
* NOTE! When we get the inode, we're the only people
* that have access to it, and as such there are no
* race conditions we have to worry about. The inode
@@ -288,6 +262,12 @@ static int find_group_orlov(struct super_block *sb,
struct inode *parent)
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext4_get_group_desc (sb, group, &bh);
+ if (test_opt(sb, INORES) &&
+ (ext4_unreserved_inodes(sb, group) <
+ EXT4_INIT_RESERVE_INODES)) {
+ printk(KERN_DEBUG "no enough reserved inodes in group %d\n", group);
+ continue;
+ }
if (!desc || !desc->bg_free_inodes_count)
continue;
if (le16_to_cpu(desc->bg_used_dirs_count) >= best_ndir)
@@ -323,6 +303,12 @@ static int find_group_orlov(struct super_block *sb,
struct inode *parent)
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext4_get_group_desc (sb, group, &bh);
+ if (test_opt(sb, INORES) &&
+ (ext4_unreserved_inodes(sb, group) <
+ EXT4_INIT_RESERVE_INODES)) {
+ printk(KERN_DEBUG "no enough reserved inodes in group %d\n", group);
+ continue;
+ }
if (!desc || !desc->bg_free_inodes_count)
continue;
if (le16_to_cpu(desc->bg_used_dirs_count) >= max_dirs)
@@ -335,6 +321,9 @@ static int find_group_orlov(struct super_block *sb,
struct inode *parent)
}

fallback:
+ printk(KERN_DEBUG "reach fallback, disable INORES\n");
+ return -1; /* for test */
+ clear_opt(sbi->s_mount_opt, INORES);
for (i = 0; i < ngroups; i++) {
group = (parent_group + i) % ngroups;
desc = ext4_get_group_desc (sb, group, &bh);
@@ -414,6 +403,598 @@ static int find_group_other(struct super_block
*sb, struct inode *parent)
return -1;
}

+
+static int ext4_inores_newdir_ino(handle_t * handle,
+ struct inode * dir,
+ time_t ctime,
+ unsigned long * ino)
+{
+ struct super_block * sb;
+ struct ext4_sb_info * sbi;
+ int group;
+ struct buffer_head * bitmap_bh = NULL, * bh2;
+ unsigned long lastres_ino, start_ino, end_ino;
+ struct ext4_magic_inode * link_minode, * lastres_minode;
+ struct ext4_iloc link_iloc, lastres_iloc;
+ struct ext4_group_desc * gdp = NULL;
+ int itable_offset;
+ int ret = 0;
+
+ sb = dir->i_sb;
+ sbi = EXT4_SB(sb);
+
+find_group_again:
+ group = find_group_orlov(sb, dir);
+
+ if (group == -1) {
+ printk("no space in find_group_orlove.\n");
+ return -ENOSPC;
+ }
+ if (!test_opt (sb, INORES)) {
+ printk(KERN_DEBUG "INORES is not set, return 0.\n");
+ * ino = 0;
+ return 0;
+ }
+
+ /*
+ * the corresponded block is already loaded into memory in
+ * find_group_orlov(), this lock will not hurt performance
+ * in common case.
+ */
+ spin_lock(sb_bgl_lock(sbi, group));
+ if (ext4_unreserved_inodes(sb, group) < EXT4_INIT_RESERVE_INODES) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ goto find_group_again;
+ }
+
+ lastres_ino = ext4_get_group_lastres_ino(sb, group);
+ ret = ext4_get_magic_inode_loc(sb, lastres_ino, &lastres_iloc);
+ if (ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return -EFAULT;
+ }
+ lastres_minode = (struct ext4_magic_inode *)
+ ((char *)lastres_iloc.bh->b_data + lastres_iloc.offset);
+ if(!ext4_magic_inode(lastres_minode, EXT4_MINODE_TYPE_LASTRES)) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ brelse(lastres_iloc.bh);
+ return -EFAULT;
+ }
+ BUFFER_TRACE (lastres_iloc.bh, "call ext4_journal_get_write_access");
+ ret = ext4_journal_get_write_access(handle, lastres_iloc.bh);
+ if(ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ brelse(lastres_iloc.bh);
+ return -EFAULT;
+ }
+ start_ino = le32_to_cpu(lastres_minode->mi_lastres_ino) + 1;
+ printk("start_ino: %lu, in group %d\n", start_ino, group);
+ lastres_minode->mi_lastres_ino = cpu_to_le32(start_ino +
+ EXT4_INIT_RESERVE_INODES - 1);
+ BUFFER_TRACE(lastres_iloc.bh, "call ext4_journal_dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, lastres_iloc.bh);
+ if(ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ brelse(lastres_iloc.bh);
+ return -EFAULT;
+ }
+ brelse(lastres_iloc.bh);
+ end_ino = start_ino + EXT4_INIT_RESERVE_INODES - 1;
+
+ ret = ext4_get_magic_inode_loc(sb, end_ino, &link_iloc);
+ if (ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return -EFAULT;
+ }
+ link_minode = (struct ext4_magic_inode *)
+ ((char *)link_iloc.bh->b_data + link_iloc.offset);
+
+ bitmap_bh = read_inode_bitmap(sb, group);
+ if (!bitmap_bh) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+
+ itable_offset = (end_ino - 1) % EXT4_INODES_PER_GROUP(sb);
+ printk(KERN_DEBUG "itable_offset of group %d is: %d\n", group,
itable_offset);
+ if (ext4_test_bit(itable_offset, bitmap_bh->b_data)) {
+ if(!ext4_magic_inode(link_minode, EXT4_MINODE_TYPE_LINK)) {
+ printk(KERN_INFO "Allocated inode %lu is not a "
+ "EXT4_MINODE_TYPE_LINK magic inode, "
+ "Disable directory inode reservation "
+ "now.\n", end_ino);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ clear_opt(sbi->s_mount_opt, INORES);
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ * ino = 0;
+ return 0;
+ }
+ if(le32_to_cpu(link_minode->mi_parent_ino) != start_ino) {
+ printk(KERN_INFO "EXT4_MINODE_TYPE_LINK magic inode "
+ "%lu is allocated already and belongs to "
+ "a different directory inode %lu. Use this "
+ "magic inode for new directory inode %lu "
+ "with force now.\n",
+ end_ino,
+ (unsigned long)le32_to_cpu(link_minode->mi_parent_ino),
+ start_ino);
+ }
+ if(le32_to_cpu(link_minode->mi_parent_ctime) !=
+ ctime) {
+ printk(KERN_INFO "EXT4_MINODE_TYPE_LINK magic inode "
+ "%lu ctime does not match, which means it "
+ "belongs a removed directory with same inode "
+ "number. Use this magic inode for new directory "
+ "inode %lu with force now.\n",
+ end_ino,
+ (unsigned long)le32_to_cpu(link_minode->mi_parent_ino));
+ }
+ }
+ BUFFER_TRACE (bitmap_bh, "call ext4_journal_get_write_access");
+ ret = ext4_journal_get_write_access(handle, bitmap_bh);
+ if (ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ jbd2_journal_release_buffer(handle, link_iloc.bh);
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ return -EIO;
+ }
+
+ if (ext4_set_bit((start_ino - 1) % EXT4_INODES_PER_GROUP(sb),
+ bitmap_bh->b_data)) {
+ printk(KERN_ERR "inode %lu for new directory is already "
+ "set in bitmap of group %d\n", start_ino, group);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ jbd2_journal_release_buffer(handle, bitmap_bh);
+ jbd2_journal_release_buffer(handle, link_iloc.bh);
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+ if (ext4_set_bit((end_ino - 1) % EXT4_INODES_PER_GROUP(sb),
+ bitmap_bh->b_data)) {
+ printk(KERN_INFO "EXT4_MINODE_TYPE_LINK magic inode "
+ "%lu is already set in bitmap of group %d\n",
+ end_ino, group);
+ printk(KERN_INFO "Use inode %lu as EXT4_MINODE_TYPE_LINK magic "
+ "inode for directory inode %lu of group %d.\n",
+ end_ino, start_ino, group);
+ }
+ spin_unlock(sb_bgl_lock(sbi, group));
+
+ BUFFER_TRACE(link_iloc.bh, "call ext4_journal_get_write_access");
+ ret = ext4_journal_get_write_access(handle, link_iloc.bh);
+ if (ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+
+ ext4_init_magic_inode(link_minode, EXT4_MINODE_TYPE_LINK);
+ link_minode->mi_next_ino = cpu_to_le32(0);
+ link_minode->mi_parent_ino = cpu_to_le32(start_ino);
+ link_minode->mi_current_ressize =
cpu_to_le32(EXT4_INIT_RESERVE_INODES);
+ link_minode->mi_next_ressize = cpu_to_le32(EXT4_INIT_RESERVE_INODES *
2);
+ link_minode->mi_parent_ctime = cpu_to_le32(ctime);
+ BUFFER_TRACE (link_iloc.bh, "call ext4_journal_dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, link_iloc.bh);
+ if (ret) {
+ jbd2_journal_release_buffer(handle, bitmap_bh);
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+ brelse(link_iloc.bh);
+ BUFFER_TRACE (bitmap_bh, "call ext4_journal_dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, bitmap_bh);
+ if (ret) {
+ brelse(bitmap_bh);
+ return -EFAULT;
+ }
+ brelse(bitmap_bh);
+
+ gdp = ext4_get_group_desc(sb, group, &bh2);
+ if (!gdp)
+ return -EFAULT;
+ BUFFER_TRACE(bh2, "call ext4_journal_get_write_access");
+ ret = ext4_journal_get_write_access(handle, bh2);
+ if (ret) {
+ return -EFAULT;
+ }
+ spin_lock(sb_bgl_lock(sbi, group));
+ gdp->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ BUFFER_TRACE(bh2, "call ext4_journal_dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, bh2);
+ if (ret) {
+ return -EFAULT;
+ }
+
+ * ino = start_ino;
+ return 0;
+
+}
+
+static int ext4_new_reserve_area(handle_t * handle,
+ struct super_block *sb,
+ int group,
+ struct ext4_magic_inode * prev_link_minode,
+ struct buffer_head * prev_link_bh,
+ unsigned long prev_link_mino,
+ int new_ressize)
+{
+ struct buffer_head * bitmap_bh, * bh2;
+ struct ext4_iloc link_iloc, lastres_iloc;
+ struct ext4_magic_inode * lastres_minode, * link_minode;
+ struct ext4_group_desc * gdp;
+ unsigned long lastres_ino, start_ino, end_ino;
+ int itable_offset;
+ int ret;
+
+
+
+ lastres_ino = (group + 1) * EXT4_INODES_PER_GROUP(sb) - 1;
+ printk(KERN_DEBUG "lastres_ino %lu in group %d\n",
+ lastres_ino, group);
+ ret = ext4_get_magic_inode_loc(sb, lastres_ino, &lastres_iloc);
+ if (ret)
+ return -EIO;
+ lastres_minode = (struct ext4_magic_inode *)
+ ((char *)lastres_iloc.bh->b_data + lastres_iloc.offset);
+ if (!ext4_magic_inode(lastres_minode, EXT4_MINODE_TYPE_LASTRES)) {
+ printk(KERN_ERR "EXT4_MINODE_TYPE_LASTRES magic inode in "
+ "group %d corrupt.\n", group);
+ brelse(lastres_iloc.bh);
+ return -EFAULT;
+ }
+ start_ino = le32_to_cpu(lastres_minode->mi_lastres_ino) + 1;
+ printk(KERN_DEBUG "try start_ino %lu in group %d.\n",
+ start_ino, group);
+ BUFFER_TRACE(lastres_iloc.bh, "get_write_access");
+ ret = ext4_journal_get_write_access(handle, lastres_iloc.bh);
+ if (ret) {
+ brelse(lastres_iloc.bh);
+ return -EIO;
+ }
+ lastres_minode->mi_lastres_ino =
+ cpu_to_le32(le32_to_cpu(lastres_minode->mi_lastres_ino) +
+ new_ressize);
+ BUFFER_TRACE(lastres_iloc.bh, "dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, lastres_iloc.bh);
+ if (ret) {
+ brelse(lastres_iloc.bh);
+ return -EIO;
+ }
+ end_ino = le32_to_cpu(lastres_minode->mi_lastres_ino);
+ brelse(lastres_iloc.bh);
+
+ itable_offset = (end_ino - 1) % EXT4_INODES_PER_GROUP(sb);
+ bitmap_bh = read_inode_bitmap(sb, group);
+ if(!bitmap_bh) {
+ printk(KERN_ERR "Can not read bitmap for group %d.\n",
+ group);
+ return -EIO;
+ }
+ BUFFER_TRACE(bitmap_bh, "get_write_access");
+ ret = ext4_journal_get_write_access(handle, bitmap_bh);
+ if(ret) {
+ brelse(bitmap_bh);
+ return -EIO;
+ }
+ printk(KERN_DEBUG "end ino offset of new reserve area: %d\n",
itable_offset);
+ if (ext4_set_bit(itable_offset, bitmap_bh->b_data)) {
+ printk(KERN_INFO "inode %lu in group %d is allocated "
+ "already. Give up this group.\n",
+ end_ino, group);
+ jbd2_journal_release_buffer(handle, bitmap_bh);
+ brelse(bitmap_bh);
+ return -EFAULT;
+ }
+ BUFFER_TRACE(bitmap_bh, "dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, bitmap_bh);
+ brelse(bitmap_bh);
+ if(ret)
+ return -EFAULT;
+
+ gdp = ext4_get_group_desc(sb, group, &bh2);
+ if (!gdp) {
+ printk(KERN_ERR "can not get group descriptor of "
+ "group %d.\n", group);
+ return -EIO;
+ }
+ BUFFER_TRACE(bh2, "get_write_access");
+ ret = ext4_journal_get_write_access(handle, bh2);
+ if (ret)
+ return -EIO;
+ gdp->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
+ BUFFER_TRACE(bh2, "call dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, bh2);
+ if(ret)
+ return -EIO;
+ ret = ext4_get_magic_inode_loc(sb, end_ino, &link_iloc);
+ if(ret)
+ return -EIO;
+ ret = ext4_journal_get_write_access(handle, link_iloc.bh);
+ if (ret) {
+ brelse(link_iloc.bh);
+ return -EIO;
+ }
+ link_minode = (struct ext4_magic_inode *)
+ ((char *)link_iloc.bh->b_data + link_iloc.offset);
+ ext4_init_magic_inode(link_minode, EXT4_MINODE_TYPE_LINK);
+ link_minode->mi_next_ino = cpu_to_le32(0);
+ link_minode->mi_parent_ino = prev_link_minode->mi_parent_ino;
+ link_minode->mi_current_ressize = cpu_to_le32(new_ressize);
+ link_minode->mi_next_ressize = cpu_to_le32(0);
+ link_minode->mi_parent_ctime = prev_link_minode->mi_parent_ctime;
+ BUFFER_TRACE(link_iloc.bh, "call dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, link_iloc.bh);
+ if (ret) {
+ brelse(link_iloc.bh);
+ return -EIO;
+ }
+ brelse(link_iloc.bh);
+ ret = ext4_journal_get_write_access(handle, prev_link_bh);
+ if (ret)
+ return -EIO;
+ prev_link_minode->mi_next_ressize = cpu_to_le32(new_ressize);
+ prev_link_minode->mi_next_ino = start_ino;
+ ret = ext4_journal_dirty_metadata(handle, prev_link_bh);
+ if (ret)
+ return -EIO;
+
+ return 0;
+}
+
+static int ext4_reserve_inodes_area(handle_t * handle,
+ struct super_block * sb,
+ struct inode * dir,
+ struct ext4_magic_inode * prev_link_minode,
+ struct buffer_head * prev_link_bh,
+ unsigned long prev_link_mino)
+{
+ struct ext4_sb_info * sbi = EXT4_SB(sb);
+ int unreserved_inodes, new_ressize;
+ int group;
+ int i, ret;
+
+ spin_lock(dir->i_lock);
+ if (le32_to_cpu(prev_link_minode->mi_next_ino) != 0) {
+ printk(KERN_DEBUG "new reserve inodes area generated "
+ "by others. Nothing to do here.\n");
+ spin_unlock(dir->i_lock);
+ return 0;
+ }
+
+ group = (prev_link_mino - 1) / EXT4_INODES_PER_GROUP(sb);
+ new_ressize = le32_to_cpu(prev_link_minode->mi_current_ressize) * 2;
+ if (new_ressize > EXT4_INODES_PER_GROUP(sb))
+ new_ressize = new_ressize / 2;
+
+try_new_ressize:
+ for (i = 0; i < sbi->s_groups_count; i ++) {
+ printk(KERN_DEBUG "try reserv size %d in group %d\n",
+ new_ressize, group);
+ spin_lock(sb_bgl_lock(sbi, group));
+ unreserved_inodes = ext4_unreserved_inodes(sb, group);
+ printk("%d inodes unreserved in group %d\n", unreserved_inodes,
group);
+ if (unreserved_inodes >= new_ressize) {
+ printk(KERN_DEBUG "group %d has enough inodes to "
+ "reserve.\n", group);
+ ret = ext4_new_reserve_area(handle,
+ sb,
+ group,
+ prev_link_minode,
+ prev_link_bh,
+ prev_link_mino,
+ new_ressize);
+ if (ret) {
+ printk(KERN_DEBUG "failed to make new "
+ "reserved area in group %d\n",
+ group);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return ret;
+ }
+ printk(KERN_DEBUG "Success to make new reserved "
+ "inodes area in group %d\n", group);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return 0;
+ }
+ spin_unlock(sb_bgl_lock(sbi, group));
+ group = (group + 1) % sbi->s_groups_count;
+ }
+ new_ressize = new_ressize >> 1;
+ if(new_ressize >= EXT4_INIT_RESERVE_INODES)
+ goto try_new_ressize;
+ return -EFAULT;
+}
+
+static int ext4_inores_newfile_ino(handle_t * handle,
+ struct inode * dir,
+ unsigned long * ino)
+{
+ struct super_block * sb;
+ struct ext4_sb_info * sbi;
+ unsigned long start_ino, end_ino;
+ int itable_offset;
+ int parent_group, prev_group, group;
+ int bitmap_size;
+ struct buffer_head * bitmap_bh;
+ struct ext4_iloc link_iloc;
+ struct ext4_magic_inode * link_minode;
+ int ret;
+
+ start_ino = dir->i_ino;
+ if((start_ino != EXT4_ROOT_INO) &&
+ ((start_ino - 1) % EXT4_INIT_RESERVE_INODES) != 0) {
+ printk(KERN_WARNING "directory inode %lu is not "
+ "%d inodes aligned.\n",
+ start_ino, EXT4_INIT_RESERVE_INODES);
+ return -EFAULT;
+ }
+
+ sb = dir->i_sb;
+ sbi = EXT4_SB(sb);
+ group = parent_group = EXT4_I(dir)->i_block_group;
+ if (start_ino == EXT4_ROOT_INO)
+ end_ino = EXT4_INIT_RESERVE_INODES;
+ else
+ end_ino = start_ino + EXT4_INIT_RESERVE_INODES - 1;
+
+ if (unlikely(end_ino >
+ (parent_group + 1) * EXT4_INODES_PER_GROUP(sb))) {
+ printk(KERN_ERR "end_io %lu of directory inode %lu "
+ "exceeds inodes of group %d.\n",
+ end_ino, start_ino, group);
+ return -EFAULT;
+ }
+ if (unlikely(end_ino <= EXT4_FIRST_INO(sb))) {
+ printk(KERN_ERR "end_ino %lu is small than fs' first "
+ "inode %d.\n", end_ino, EXT4_FIRST_INO(sb));
+ return -EFAULT;
+ }
+
+ start_ino += 1;
+
+ prev_group = group;
+
+ /* loop_count should be removed after debugging */
+ unsigned long loop_count = 0;
+ while(1) {
+ printk(KERN_INFO "try group %d\n", group);
+ bitmap_bh = read_inode_bitmap(sb, group);
+ if (!bitmap_bh)
+ return -EIO;
+repeat_in_this_group:
+ loop_count += 1;
+ if (loop_count > 10000000){
+ brelse(bitmap_bh);
+ printk("too much time dead loop\n");
+ return -EIO;
+ }
+ itable_offset = (start_ino - 1) %
+ EXT4_INODES_PER_GROUP(sb);
+ bitmap_size = (end_ino - 1) % EXT4_INODES_PER_GROUP(sb) + 1;
+ /*
+ * should use a function here
+ */
+ printk("bitmap_size: %d, itable_offset: %d\n", bitmap_size,
itable_offset);
+ * ino = ext4_find_next_zero_bit((unsigned long *)
+ bitmap_bh->b_data, bitmap_size, itable_offset);
+// * ino = ext4_find_next_zero_bit((unsigned long *)
+// bitmap_bh->b_data, EXT4_INODES_PER_GROUP(sb), itable_offset);
+ printk("find offset %lu in group %d [%d - %d] inodes [%lu - %lu]\n",
+ * ino, group, itable_offset, bitmap_size - 1,
+ start_ino, end_ino);
+ if ((* ino) < bitmap_size) {
+ BUFFER_TRACE(bitmap_bh, "get_write_access");
+ ret = ext4_journal_get_write_access(handle, bitmap_bh);
+ if(ret) {
+ brelse(bitmap_bh);
+ return -EIO;
+ }
+ if(!ext4_set_bit_atomic(sb_bgl_lock(sbi, group),
+ * ino, bitmap_bh->b_data)) {
+ BUFFER_TRACE(bitmap_bh,
+ "call ext4_journal_dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle,
+ bitmap_bh);
+ if(ret) {
+ brelse (bitmap_bh);
+ return -EIO;
+ }
+ brelse(bitmap_bh);
+ * ino = group * EXT4_INODES_PER_GROUP(sb) +
+ (* ino) + 1;
+ return 0;
+ }
+ printk("offset %lu set in bitmap already.\n", * ino);
+ jbd2_journal_release_buffer(handle, bitmap_bh);
+ goto repeat_in_this_group;
+ }
+ ret = ext4_get_magic_inode_loc(sb, end_ino, &link_iloc);
+ if (ret) {
+ printk (KERN_ERR "failed to get magic inode %lu "
+ "from group %d\n", end_ino, group);
+ brelse(bitmap_bh);
+ return ret;
+ }
+ link_minode = (struct ext4_magic_inode *)
+ ((char *)link_iloc.bh->b_data + link_iloc.offset);
+ if(!ext4_magic_inode(link_minode, EXT4_MINODE_TYPE_LINK)) {
+ printk(KERN_ERR "inode %lu is not a EXT4_MINODE_TYPE_LINK "
+ "magic inode.\n", end_ino);
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+ printk("preextend, link_minode->mi_next_ino: %lu\n",
+ (unsigned long)le32_to_cpu(link_minode->mi_next_ino));
+ if (le32_to_cpu(link_minode->mi_next_ino) == 0) {
+ ret = ext4_reserve_inodes_area(handle,
+ sb,
+ dir,
+ link_minode,
+ link_iloc.bh,
+ end_ino);
+ if (ret) {
+ printk(KERN_ERR "get new reserve inodes area after "
+ "area [%lu - %lu] failed.\n",
+ start_ino, end_ino);
+ brelse(bitmap_bh);
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+ }
+ printk("afterextend, link_minode->mi_next_ino: %lu\n",
+ (unsigned long)le32_to_cpu(link_minode->mi_next_ino));
+ start_ino = le32_to_cpu(link_minode->mi_next_ino);
+ end_ino = start_ino +
+ le32_to_cpu(link_minode->mi_next_ressize) - 1;
+ brelse (link_iloc.bh);
+ group = (start_ino - 1) / EXT4_INODES_PER_GROUP(sb);
+ printk("prev_group: %d, group: %d, start_ino: %lu, end_ino: %lu\n",
+ prev_group, group, start_ino, end_ino);
+ if (group == prev_group) {
+ printk("try same group %d.\n", prev_group);
+ goto repeat_in_this_group;
+ }
+ printk("try new group %d.\n", group);
+ prev_group = group;
+ brelse(bitmap_bh);
+ }
+ printk(" ============= loop end ========= \n");
+ return -EINVAL;
+}
+
+static int ext4_find_inores_ino(handle_t * handle,
+ struct inode * dir,
+ int mode,
+ time_t ctime,
+ unsigned long * ino)
+{
+
+ struct super_block *sb;
+ int ret = -EINVAL;
+
+ sb = dir->i_sb;
+ if (!test_opt(sb, INORES))
+ return ret;
+
+ if (S_ISDIR(mode))
+ ret = ext4_inores_newdir_ino(handle, dir, ctime, ino);
+ else
+ ret = ext4_inores_newfile_ino(handle, dir, ino);
+
+ return ret;
+}
+
+
/*
* There are two policies for allocating an inode. If the new inode is
* a directory, then a forward search is made for a block group with
both
@@ -422,7 +1003,8 @@ static int find_group_other(struct super_block *sb,
struct inode *parent)
* directories already is chosen.
*
* For other inodes, search forward from the parent directory's block
- * group to find a free inode.
+ * group to find a free inode. When directory inode reservation is
enabled,
+ * inodes will be searched in the reserved inodes area firstly.
*/
struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int
mode)
{
@@ -436,6 +1018,7 @@ struct inode *ext4_new_inode(handle_t *handle,
struct inode * dir, int mode)
struct ext4_super_block * es;
struct ext4_inode_info *ei;
struct ext4_sb_info *sbi;
+ struct timespec ctime;
int err = 0;
struct inode *ret;
int i;
@@ -452,6 +1035,31 @@ struct inode *ext4_new_inode(handle_t *handle,
struct inode * dir, int mode)

sbi = EXT4_SB(sb);
es = sbi->s_es;
+
+ ctime = ext4_current_time(inode);
+ if (test_opt (sb, INORES)) {
+ err = ext4_find_inores_ino(handle, dir, mode, ctime.tv_sec, &ino);
+ if (err)
+// goto fail;
+ return ERR_PTR(-ENOSPC); /* for test now */
+ if (ino > 0) {
+ group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
+ gdp = ext4_get_group_desc(sb, group, &bh2);
+ if (!gdp)
+ goto fail;
+ printk("find ino %lu in group %d from ext4_find_inores_ino.\n",
+ ino, group);
+ goto inores_got;
+ }
+ printk(KERN_INFO "can not find inode from reserved inodes "
+ "area, disable inode reservation for "
+ "directory now.\n");
+ return ERR_PTR(-ENOSPC); /* for test now */
+ clear_opt (sbi->s_mount_opt, INORES);
+ }
+
+ return ERR_PTR(-ENOSPC);
+
if (S_ISDIR(mode)) {
if (test_opt (sb, OLDALLOC))
group = find_group_dir(sb, dir);
@@ -521,9 +1129,10 @@ repeat_in_this_group:

got:
ino += group * EXT4_INODES_PER_GROUP(sb) + 1;
+inores_got:
if (ino < EXT4_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
ext4_error (sb, "ext4_new_inode",
- "reserved inode or inode > inodes count - "
+ "reserved inode or inode > inodes count -- "
"block_group = %d, inode=%lu", group, ino);
err = -EIO;
goto fail;
@@ -564,7 +1173,7 @@ got:
/* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
- ext4_current_time(inode);
+ ctime;

memset(ei->i_data, 0, sizeof(ei->i_data));
ei->i_dir_start_lookup = 0;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f50c8cd..6929991 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3330,3 +3330,338 @@ int ext4_change_inode_journal_flag(struct inode
*inode, int val)

return err;
}
+
+int ext4_magic_inode(struct ext4_magic_inode * magic_inode,
+ int type)
+{
+ int i, sum;
+ if(le32_to_cpu(magic_inode->mi_zeropad) != 0)
+ return 0;
+ if(strncmp(magic_inode->mi_magic, EXT4_MINODE_MAGIC_STR,
+ EXT4_MINODE_MAGIC_LEN))
+ return 0;
+ sum = 0;
+ for(i = 0; i < EXT4_MINODE_MAGIC_LEN; i ++)
+ sum += magic_inode->mi_magic[i];
+ if(sum + le32_to_cpu(magic_inode->mi_checksum) != 0)
+ return 0;
+ if(le32_to_cpu(magic_inode->mi_type) != type)
+ return 0;
+ return 1;
+}
+
+void ext4_init_magic_inode(struct ext4_magic_inode * magic_inode,
+ int type)
+{
+ int i, sum;
+ memset(magic_inode, 0, sizeof(struct ext4_magic_inode));
+ memcpy(magic_inode->mi_magic, EXT4_MINODE_MAGIC_STR,
+ EXT4_MINODE_MAGIC_LEN);
+ sum = 0;
+ for(i = 0; i < EXT4_MINODE_MAGIC_LEN; i ++)
+ sum += magic_inode->mi_magic[i];
+ magic_inode->mi_checksum = cpu_to_le32(0 - sum);
+ magic_inode->mi_type = cpu_to_le32(type);
+}
+
+unsigned long ext4_get_group_lastres_ino(struct super_block * sb, int
group)
+{
+ unsigned long lastres_ino;
+ lastres_ino = (group + 1) * EXT4_INODES_PER_GROUP(sb) - 1;
+ return lastres_ino;
+}
+
+int ext4_get_magic_inode_loc(struct super_block * sb,
+ unsigned long ino,
+ struct ext4_iloc * iloc)
+{
+ unsigned long block_group, group_desc, desc;
+ unsigned long block, offset;
+ struct buffer_head * bh;
+ struct ext4_group_desc * gdp;
+
+ block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
+ if(block_group >= EXT4_SB(sb)->s_groups_count) {
+ ext4_error(sb, "ext4_get_magic_inode_loc",
+ "group >= groups count");
+ return -EINVAL;
+ }
+
+ group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
+ desc = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
+ bh = EXT4_SB(sb)->s_group_desc[group_desc];
+ if(!bh) {
+ ext4_error (sb, "ext4_get_magic_inode_loc",
+ "Descriptor not loaded");
+ return -EINVAL;
+ }
+ gdp = (struct ext4_group_desc *)
+ ((char *)bh->b_data + desc * EXT4_DESC_SIZE(sb));
+
+ offset = ((ino - 1) % EXT4_INODES_PER_GROUP(sb)) *
+ EXT4_INODE_SIZE(sb);
+ block = ext4_inode_table(sb, gdp) +
+ (offset >> EXT4_BLOCK_SIZE_BITS(sb));
+
+ bh = sb_bread(sb, block);
+ if(!bh) {
+ ext4_error (sb, "ext4_get_magic_inode_loc",
+ "unable to read inode block - "
+ "inode=%lu, block=%lu",
+ ino, block);
+ return -EIO;
+ }
+ offset = offset & (EXT4_BLOCK_SIZE(sb) - 1);
+ iloc->block_group = block_group;
+ iloc->offset = offset;
+ iloc->bh = bh;
+
+ return 0;
+}
+
+unsigned long ext4_unreserved_inodes(struct super_block *sb,
+ int group)
+{
+ unsigned long lastres_ino;
+ unsigned long unreserved_nr;
+ struct ext4_iloc iloc;
+ struct ext4_magic_inode * magic_inode;
+
+ lastres_ino = ext4_get_group_lastres_ino(sb, group);
+ if(ext4_get_magic_inode_loc(sb, lastres_ino, &iloc) < 0) {
+ ext4_error (sb, "ext4_unreserved_inodes",
+ "failed to load inode block - "
+ "inode %lu, group %d", lastres_ino, group);
+ return 0;
+ }
+ magic_inode = (struct ext4_magic_inode * )
+ ((char *)iloc.bh->b_data + iloc.offset);
+ if(!ext4_magic_inode(magic_inode, EXT4_MINODE_TYPE_LASTRES)) {
+ ext4_error (sb, "ext4_unreserved_inodes",
+ "inode %lu in group %d is not "
+ "EXT4_MINODE_TYPE_LASTRES magic inode",
+ lastres_ino, group);
+ brelse(iloc.bh);
+ return 0;
+ }
+ unreserved_nr = (group + 1) * EXT4_INODES_PER_GROUP(sb) -
+ le32_to_cpu(magic_inode->mi_lastres_ino);
+ brelse(iloc.bh);
+ return (unreserved_nr > 0) ? unreserved_nr : 0;
+}
+
+static int ext4_shrink_inores_ino(struct super_block * sb,
+ int group,
+ unsigned long link_ino,
+ handle_t * handle,
+ struct buffer_head * lastres_bh,
+ struct ext4_magic_inode * lastres_minode)
+{
+ struct ext4_sb_info * sbi;
+ struct buffer_head * bitmap_bh;
+ int lastres_mino_offset;
+ int len;
+ int prev_offset, offset;
+ int ret;
+
+ sbi = EXT4_SB(sb);
+ spin_lock(sb_bgl_lock(sbi, group));
+
+ if (link_ino != le32_to_cpu(lastres_minode->mi_lastres_ino)) {
+ printk(KERN_INFO "last reserved ino of group %d is not "
+ "%lu any more. Give up shrink last reserved ino.\n",
+ group, link_ino);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return 0;
+ }
+
+ bitmap_bh = read_inode_bitmap(sb, group);
+ if (!bitmap_bh) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return -EFAULT;
+ }
+
+ lastres_mino_offset =
+ (ext4_get_group_lastres_ino(sb, group) - 1) %
+ EXT4_INODES_PER_GROUP(sb);
+ len = (link_ino - 1) % EXT4_INODES_PER_GROUP(sb) + 1;
+
+ printk("lastres_mino_offset: %d, len: %d\n",
+ lastres_mino_offset, len);
+ for(prev_offset = 0, offset = 0; offset < len; offset ++) {
+ offset = find_next_bit((unsigned long *)bitmap_bh->b_data,
+ len, offset);
+ if (offset >= len)
+ break;
+ if (offset != lastres_mino_offset)
+ prev_offset = offset;
+ }
+ printk("offset: %d, prev_offset: %d\n", offset, prev_offset);
+ BUFFER_TRACE(lastres_bh, "call get_write_access");
+ ret = ext4_journal_get_write_access(handle, lastres_bh);
+ if (ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return -EFAULT;
+ }
+ if (prev_offset)
+ prev_offset += 1;
+ lastres_minode->mi_lastres_ino =
+ cpu_to_le32(group * EXT4_INODES_PER_GROUP(sb)
+ + prev_offset);
+ BUFFER_TRACE(lastres_bh, "call dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, lastres_bh);
+ if (ret) {
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return -EFAULT;
+ }
+ spin_unlock(sb_bgl_lock(sbi, group));
+ return 0;
+}
+
+
+int ext4_delete_link_magic_inodes(handle_t * handle, struct inode *
dir)
+{
+ struct super_block * sb;
+ struct ext4_sb_info * sbi;
+ unsigned long dir_ino, link_ino, next_ino;
+ unsigned long lastres_ino;
+ int next_ressize;
+ struct ext4_iloc link_iloc, lastres_iloc;
+ struct ext4_magic_inode * link_minode,* lastres_minode;
+ struct buffer_head * bitmap_bh, * bh2 ;
+ struct ext4_group_desc * gdp;
+ int group, bit;
+ int ret;
+
+ dir_ino = dir->i_ino;
+
+ if (dir_ino != EXT4_ROOT_INO &&
+ (dir_ino - 1) % EXT4_INIT_RESERVE_INODES != 0) {
+ printk(KERN_DEBUG "dir inode %lu is not %d aligned."
+ "Give up deleting EXT4_MINODE_TYPE_LINK magic "
+ "inodes of this dir inode.\n",
+ dir_ino, EXT4_INIT_RESERVE_INODES);
+ return 0;
+ }
+
+ sb = dir->i_sb;
+ sbi = EXT4_SB(sb);
+
+ if (dir_ino == EXT4_ROOT_INO)
+ link_ino = EXT4_INIT_RESERVE_INODES;
+ else
+ link_ino = dir_ino + EXT4_INIT_RESERVE_INODES - 1;
+
+ printk("at begining, dir_ino: %lu, link_ino: %lu.\n", dir_ino,
link_ino);
+
+ next_ino = dir_ino;
+ while (next_ino) {
+ ret = ext4_get_magic_inode_loc(sb, link_ino, &link_iloc);
+ if (ret)
+ return ret;
+ link_minode = (struct ext4_magic_inode *)
+ ((char *)link_iloc.bh->b_data + link_iloc.offset);
+ if(!ext4_magic_inode(link_minode, EXT4_MINODE_TYPE_LINK)) {
+ printk(KERN_WARNING "Inode %lu is not a "
+ "EXT4_MINODE_TYPE_LINK magic inode. "
+ "Give up removing other magic inodes.\n",
+ link_ino);
+ brelse(link_iloc.bh);
+ return -EFAULT;
+ }
+ next_ino = le32_to_cpu(link_minode->mi_next_ino);
+ next_ressize = le32_to_cpu(link_minode->mi_next_ressize);
+ brelse(link_iloc.bh);
+ group = (link_ino - 1) / EXT4_INODES_PER_GROUP(sb);
+ bit = (link_ino - 1) % EXT4_INODES_PER_GROUP(sb);
+ bitmap_bh = read_inode_bitmap(sb, group);
+ if (!bitmap_bh)
+ return -EIO;
+ BUFFER_TRACE(bitmap_bh, "call get_write_access");
+ ret = ext4_journal_get_write_access(handle, bitmap_bh);
+ if (ret) {
+ brelse(bitmap_bh);
+ return -EFAULT;
+ }
+ printk(KERN_DEBUG "clear magic inode %lu in bitmap of group %d.\n",
+ link_ino, group);
+ if (!ext4_clear_bit_atomic(sb_bgl_lock(sbi, group),
+ bit, bitmap_bh->b_data)) {
+ ext4_error(sb, "ext4_delete_link_magic_inodes",
+ "bit already cleared for inode %lu",
+ link_ino);
+ }
+ BUFFER_TRACE(bitmap_bh, "call dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, bitmap_bh);
+ if (ret) {
+ brelse(bitmap_bh);
+ return -EFAULT;
+ }
+ brelse(bitmap_bh);
+ gdp = ext4_get_group_desc(sb, group, &bh2);
+ if (!gdp) {
+ ext4_error(sb, "ext4_delete_link_magic_inodes",
+ "get group %d desc failed.",
+ group);
+ return -EFAULT;
+ }
+ BUFFER_TRACE(bh2, "call get_write_access");
+ ret = ext4_journal_get_write_access(handle, bh2);
+ if (ret)
+ return -EFAULT;
+ spin_lock(sb_bgl_lock(sbi, group));
+ gdp->bg_free_inodes_count =
+ cpu_to_le32(le32_to_cpu(gdp->bg_free_inodes_count) + 1);
+ spin_unlock(sb_bgl_lock(sbi, group));
+ BUFFER_TRACE(bh2, "call dirty_metadata");
+ ret = ext4_journal_dirty_metadata(handle, bh2);
+ if (ret)
+ return -EFAULT;
+
+ lastres_ino = ext4_get_group_lastres_ino(sb, group);
+ ret = ext4_get_magic_inode_loc(sb, lastres_ino, &lastres_iloc);
+ if (ret) {
+ ext4_error(sb, "ext4_delete_link_magic_inodes",
+ "read EXT4_MINODE_TYPE_LASTRES magic inode %lu"
+ "of group %d failed.", lastres_ino, group);
+ return -EFAULT;
+ }
+ lastres_minode = (struct ext4_magic_inode *)
+ ((char *)lastres_iloc.bh->b_data + lastres_iloc.offset);
+ if (!ext4_magic_inode(lastres_minode, EXT4_MINODE_TYPE_LASTRES)) {
+ ext4_error(sb, "ext4_delete_link_magic_inodes",
+ "inode %lu is not EXT4_MINODE_TYPE_LASTRES "
+ "magic inode of group %d.",
+ lastres_ino, group);
+ brelse(lastres_iloc.bh);
+ return -EFAULT;
+ }
+ printk("whether to shrink the last reserved ino? link_ino: %lu,
lastres_ino: %lu\n",
+ link_ino,
+ le32_to_cpu(lastres_minode->mi_lastres_ino));
+ if (link_ino == le32_to_cpu(lastres_minode->mi_lastres_ino)) {
+ ret = ext4_shrink_inores_ino( sb,
+ group,
+ link_ino,
+ handle,
+ lastres_iloc.bh,
+ lastres_minode);
+ if (ret) {
+ ext4_error(sb, "ext4_delete_link_magic_inodes",
+ "shrink last reserved ino of group "
+ "%d failed.", group);
+ brelse(lastres_iloc.bh);
+ return -EFAULT;
+ }
+ printk("shrink group %d last reserved ino to %lu.\n",
+ group, le32_to_cpu(lastres_minode->mi_lastres_ino));
+ }
+ brelse(lastres_iloc.bh);
+ link_ino = next_ino + next_ressize - 1;
+ printk(KERN_DEBUG "try next link_ino: %lu\n", link_ino);
+ }
+
+ return 0;
+}
+
+
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index f135b3b..c25f8b3 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2044,6 +2044,10 @@ static int ext4_rmdir (struct inode * dir, struct
dentry *dentry)
if (!empty_dir (inode))
goto end_rmdir;

+ retval = ext4_delete_link_magic_inodes(handle, inode);
+ if (retval)
+ goto end_rmdir;
+
retval = ext4_delete_entry(handle, dir, de, bh);
if (retval)
goto end_rmdir;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 9dd43d8..b385e2a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -729,7 +729,7 @@ static struct export_operations ext4_export_ops = {
enum {
Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
- Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov,
+ Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov,
Opt_inores,
Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, Opt_bh,
Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev,
@@ -759,6 +759,7 @@ static match_table_t tokens = {
{Opt_debug, "debug"},
{Opt_oldalloc, "oldalloc"},
{Opt_orlov, "orlov"},
+ {Opt_inores, "inode_reservation"},
{Opt_user_xattr, "user_xattr"},
{Opt_nouser_xattr, "nouser_xattr"},
{Opt_acl, "acl"},
@@ -894,6 +895,9 @@ static int parse_options (char *options, struct
super_block *sb,
case Opt_orlov:
clear_opt (sbi->s_mount_opt, OLDALLOC);
break;
+ case Opt_inores:
+ set_opt (sbi->s_mount_opt, INORES);
+ break;
#ifdef CONFIG_EXT4DEV_FS_XATTR
case Opt_user_xattr:
set_opt (sbi->s_mount_opt, XATTR_USER);
@@ -1303,6 +1307,119 @@ static int ext4_check_descriptors (struct
super_block * sb)
return 1;
}

+/* Called at mount-time, super-block is locked
+ * ext4_check_lastres_magic_inode() checks every
EXT4_MINODE_TYPE_LASTRES magic
+ * inode in each block group.
+ */
+int ext4_check_lastres_magic_inode(struct super_block * sb)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ struct ext4_group_desc *gdp = NULL;
+ struct buffer_head *bitmap_bh = NULL;
+ struct ext4_magic_inode * magic_inode;
+ struct ext4_iloc iloc;
+ int desc_block = 0;
+ unsigned long offset, prev_offset;
+ unsigned long itable_offset;
+ unsigned long lastres_ino;
+ int group;
+ int i;
+
+
+ ext4_debug("ext4_check_lastres_magic_inode");
+
+ for(group = 0; group < sbi->s_groups_count; group ++)
+ {
+ if((group % EXT4_DESC_PER_BLOCK(sb)) == 0)
+ gdp = (struct ext4_group_desc *)
+ sbi->s_group_desc[desc_block++]->b_data;
+
+ bitmap_bh = sb_bread(sb, ext4_inode_bitmap(sb, gdp));
+ if(!bitmap_bh) {
+ ext4_error (sb, "ext4_check_lastres_magic_inode",
+ "can not read inode bitmap for group %d",
+ group);
+ return 0;
+ }
+
+ lastres_ino = ext4_get_group_lastres_ino(sb, group);
+ itable_offset = (lastres_ino % EXT4_INODES_PER_GROUP(sb)) - 1;
+ if(ext4_test_bit(itable_offset, bitmap_bh->b_data)) {
+ if(ext4_get_magic_inode_loc(sb, lastres_ino, &iloc) < 0) {
+ ext4_error (sb, "ext4_check_lastres_magic_inode",
+ "failed to load inode block - inode %lu, "
+ "group %d", lastres_ino, group);
+ brelse(bitmap_bh);
+ return 0;
+ }
+ magic_inode = (struct ext4_magic_inode *)
+ ((char *)iloc.bh->b_data + iloc.offset);
+
+ if(!ext4_magic_inode(magic_inode, EXT4_MINODE_TYPE_LASTRES)) {
+ ext4_error(sb, "ext4_check_lastres_magic_inode",
+ "inode %lu in group %d is not "
+ "EXT4_MINODE_TYPE_LASTRES magic inode",
+ lastres_ino, group);
+ brelse(bitmap_bh);
+ brelse(iloc.bh);
+ return 0;
+ }
+ printk(KERN_DEBUG "group %d last reserved inode %lu.\n",
+ group, le32_to_cpu(magic_inode->mi_lastres_ino));
+ if(le32_to_cpu(magic_inode->mi_lastres_ino) >
+ ((group + 1) * EXT4_INODES_PER_GROUP(sb))) {
+ ext4_error(sb, "ext4_check_lastres_magic_inode",
+ "last reserved inode %d is not in inode "
+ "table of group %d",
+ (int)le32_to_cpu(magic_inode->mi_lastres_ino), group);
+ brelse(bitmap_bh);
+ brelse(iloc.bh);
+ return 0;
+ }
+ i = EXT4_INODES_PER_GROUP(sb) -
+ le32_to_cpu(gdp->bg_free_inodes_count);
+ for(prev_offset = 0, offset = 0; i > 0; i --, offset ++)
+ {
+ offset = find_next_bit((unsigned long *)bitmap_bh->b_data,
+ EXT4_INODES_PER_GROUP(sb), offset);
+ if (offset != itable_offset)
+ prev_offset = offset;
+ }
+ offset --;
+ if(offset == itable_offset)
+ offset = prev_offset;
+ if(offset > (le32_to_cpu(magic_inode->mi_lastres_ino) - 1) %
+ EXT4_INODES_PER_GROUP(sb)) {
+ printk(KERN_INFO "last reserved inode offset in "
+ "magic inode (group %d) does not match "
+ "in inode bitmap\n", group);
+ printk(KERN_INFO "set last reserved inode offset "
+ "from %d to %lu for group %d\n",
+ (int)le32_to_cpu(magic_inode->mi_lastres_ino),
+ group * EXT4_INODES_PER_GROUP(sb) + offset,
+ group);
+ magic_inode->mi_lastres_ino =
+ cpu_to_le32(group * EXT4_INODES_PER_GROUP(sb) +
+ offset);
+ mark_buffer_dirty(iloc.bh);
+ }
+ } else {
+ printk(KERN_INFO "can not find EXT4_MINODE_LASTRES magic "
+ "inode in group %d. Disable inode_reservaion now\n",
+ group);
+ clear_opt(sbi->s_mount_opt, INORES);
+ brelse(bitmap_bh);
+ return 1;
+ }
+ brelse(bitmap_bh);
+ brelse(iloc.bh);
+ gdp = (struct ext4_group_desc *)
+ ((__u8 *)gdp + EXT4_DESC_SIZE(sb));
+ }
+
+ return 1;
+}
+

/* ext4_orphan_cleanup() walks a singly-linked list of inodes (starting
at
* the superblock) which were deleted from all directories, but held
open by
@@ -1747,6 +1864,12 @@ static int ext4_fill_super (struct super_block
*sb, void *data, int silent)
printk(KERN_ERR "EXT4-fs: group descriptors corrupted!\n");
goto failed_mount2;
}
+ if(test_opt(sb, INORES) && !ext4_check_lastres_magic_inode(sb)) {
+ printk(KERN_ERR "EXT4-fs: EXT4_MINODE_TYPE_LASTRES "
+ "magic inodes correupted!\n");
+ goto failed_mount2;
+ }
+
sbi->s_gdb_count = db_count;
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
spin_lock_init(&sbi->s_next_gen_lock);
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index 19635a4..fb7ebfe 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -352,6 +352,34 @@ struct ext4_inode {
__le32 i_crtime_extra; /* extra File Creation time (nsec << 2 | epoch)
*/
};

+/*
+ * inode reservation for directories
+ */
+#define EXT4_INIT_RESERVE_INODES 16
+
+#define EXT4_MINODE_MAGIC_STR "ext_magic_inode\0"
+#define EXT4_MINODE_MAGIC_LEN 16
+
+#define EXT4_MINODE_TYPE_LASTRES 0x0001
+#define EXT4_MINODE_TYPE_LINK 0x0002
+
+struct ext4_magic_inode {
+ __le32 mi_zeropad; /* Zero pad */
+ __u8 mi_magic[EXT4_MINODE_MAGIC_LEN];/* Magic string */
+ __le32 mi_checksum; /* Checksum for magic string */
+ __le32 mi_type; /* Type of magic inode */
+ __le32 mi_lastres_ino; /* Offset in inode table, for */
+ /* EXT4_MINODE_TYPE_LASTRES magic inode */
+ __le32 mi_next_ino; /* Inode number for head inode of next */
+ /* reserved inodes area */
+ __le32 mi_parent_ino; /* Dir inode number */
+ __le32 mi_parent_ctime; /* Dir inode ctime */
+ __le32 mi_current_ressize; /* Reserved inodes size for current
reserved */
+ /* inodes area */
+ __le32 mi_next_ressize; /* Reserved inodes size for next reserved */
+ /* inodes area */
+};
+
#define i_size_high i_dir_acl

#define EXT4_EPOCH_BITS 2
@@ -459,6 +487,7 @@ do { \
#define EXT4_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */
#define EXT4_MOUNT_EXTENTS 0x400000 /* Extents support */
#define EXT4_MOUNT_DELAYED_ALLOC 0x1000000/* Delayed allocation support
*/
+#define EXT4_MOUNT_INORES 0x2000000/* Inode reservation support */

/* Compatibility, for having both ext2_fs.h and ext4_fs.h included at
once */
#ifndef _LINUX_EXT2_FS_H
@@ -926,6 +955,8 @@ extern int ext4_sync_inode (handle_t *, struct
inode *);
extern void ext4_discard_reservation (struct inode *);
extern void ext4_dirty_inode(struct inode *);
extern int ext4_change_inode_journal_flag(struct inode *, int);
+extern int ext4_magic_inode(struct ext4_magic_inode * , int);
+extern void ext4_init_magic_inode(struct ext4_magic_inode *, int);
extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *);
extern void ext4_truncate (struct inode *);
extern void ext4_set_inode_flags(struct inode *);
@@ -933,6 +964,13 @@ extern void ext4_set_aops(struct inode *inode);
extern int ext4_writepage_trans_blocks(struct inode *);
extern int ext4_block_truncate_page(handle_t *handle, struct page *page,
struct address_space *mapping, loff_t from);
+extern int ext4_magic_inode(struct ext4_magic_inode * magic_inode, int
type);
+extern void ext4_init_magic_inode(struct ext4_magic_inode *
magic_inode, int type);
+extern unsigned long ext4_get_group_lastres_ino(struct super_block *
sb, int group);
+extern int ext4_get_magic_inode_loc(struct super_block * sb,
+ unsigned long ino, struct ext4_iloc * iloc);
+extern unsigned long ext4_unreserved_inodes(struct super_block *sb, int
group);
+int ext4_delete_link_magic_inodes(handle_t * handle, struct inode *
dir);

/* ioctl.c */
extern int ext4_ioctl (struct inode *, struct file *, unsigned int,
@@ -952,6 +990,10 @@ extern int ext4_group_extend(struct super_block
*sb,
struct ext4_super_block *es,
ext4_fsblk_t n_blocks_count);

+/* bitmap.c */
+extern struct buffer_head *
+read_inode_bitmap(struct super_block * sb, unsigned long block_group);
+
/* super.c */
extern void ext4_error (struct super_block *, const char *, const char
*, ...)
__attribute__ ((format (printf, 3, 4)));