2007-10-18 10:29:50

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC][PATCH 1/6] ext4: Code cleanup

Introduce ext4_update_*_feature and use them instead
of opencoding.


Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/ialloc.c | 11 +++-----
fs/ext4/super.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/ext4_fs.h | 6 ++++
3 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 1777d9d..de40401 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -747,13 +747,10 @@ got:
if (test_opt(sb, EXTENTS)) {
EXT4_I(inode)->i_flags |= EXT4_EXTENTS_FL;
ext4_ext_tree_init(handle, inode);
- if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
- err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
- if (err) goto fail;
- EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS);
- BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "call ext4_journal_dirty_metadata");
- err = ext4_journal_dirty_metadata(handle, EXT4_SB(sb)->s_sbh);
- }
+ err = ext4_update_incompat_feature(handle, sb,
+ EXT4_FEATURE_INCOMPAT_EXTENTS);
+ if (err)
+ goto fail;
}

ext4_debug("allocating inode %lu\n", inode->i_ino);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6b4695c..e2bed20 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -373,6 +373,66 @@ void ext4_update_dynamic_rev(struct super_block *sb)
*/
}

+int ext4_update_compat_feature(handle_t *handle,
+ struct super_block *sb, __u32 compat)
+{
+ int err = 0;
+ if (!EXT4_HAS_COMPAT_FEATURE(sb, compat)) {
+ err = ext4_journal_get_write_access(handle,
+ EXT4_SB(sb)->s_sbh);
+ if (err)
+ return err;
+ EXT4_SET_COMPAT_FEATURE(sb, compat);
+ sb->s_dirt = 1;
+ handle->h_sync = 1;
+ BUFFER_TRACE(EXT4_SB(sb)->s_sbh,
+ "call ext4_journal_dirty_met adata");
+ err = ext4_journal_dirty_metadata(handle,
+ EXT4_SB(sb)->s_sbh);
+ }
+ return err;
+}
+
+int ext4_update_rocompat_feature(handle_t *handle,
+ struct super_block *sb, __u32 rocompat)
+{
+ int err = 0;
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, rocompat)) {
+ err = ext4_journal_get_write_access(handle,
+ EXT4_SB(sb)->s_sbh);
+ if (err)
+ return err;
+ EXT4_SET_RO_COMPAT_FEATURE(sb, rocompat);
+ sb->s_dirt = 1;
+ handle->h_sync = 1;
+ BUFFER_TRACE(EXT4_SB(sb)->s_sbh,
+ "call ext4_journal_dirty_met adata");
+ err = ext4_journal_dirty_metadata(handle,
+ EXT4_SB(sb)->s_sbh);
+ }
+ return err;
+}
+
+int ext4_update_incompat_feature(handle_t *handle,
+ struct super_block *sb, __u32 incompat)
+{
+ int err = 0;
+ if (!EXT4_HAS_INCOMPAT_FEATURE(sb, incompat)) {
+ err = ext4_journal_get_write_access(handle,
+ EXT4_SB(sb)->s_sbh);
+ if (err)
+ return err;
+ EXT4_SET_INCOMPAT_FEATURE(sb, incompat);
+ sb->s_dirt = 1;
+ handle->h_sync = 1;
+ BUFFER_TRACE(EXT4_SB(sb)->s_sbh,
+ "call ext4_journal_dirty_met adata");
+ err = ext4_journal_dirty_metadata(handle,
+ EXT4_SB(sb)->s_sbh);
+ }
+ return err;
+}
+
/*
* Open the external journal device
*/
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index 9c99090..f6d4b4c 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -1039,6 +1039,12 @@ extern void ext4_abort (struct super_block *, const char *, const char *, ...)
extern void ext4_warning (struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
extern void ext4_update_dynamic_rev (struct super_block *sb);
+extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb,
+ __u32 compat);
+extern int ext4_update_rocompat_feature(handle_t *handle, struct super_block *sb,
+ __u32 rocompat);
+extern int ext4_update_incompat_feature(handle_t *handle, struct super_block *sb,
+ __u32 incompat);
extern ext4_fsblk_t ext4_block_bitmap(struct super_block *sb,
struct ext4_group_desc *bg);
extern ext4_fsblk_t ext4_inode_bitmap(struct super_block *sb,
--
1.5.3.4.206.g58ba4-dirty


2007-10-18 10:29:51

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC][PATCH 2/6] ext4: code cleanup

Fix sparse warnings.

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/extents.c | 6 +++---
fs/ext4/inode.c | 14 ++++++++------
include/linux/ext4_fs.h | 2 ++
3 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 392b76e..ce58d45 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1230,7 +1230,7 @@ static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode,
* then we have to correct all indexes above.
* TODO: do we need to correct tree in all cases?
*/
-int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
+static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
{
struct ext4_extent_header *eh;
@@ -1677,7 +1677,7 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
* It's used in truncate case only, thus all requests are for
* last index in the block only.
*/
-int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
+static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
struct ext4_ext_path *path)
{
struct buffer_head *bh;
@@ -1947,7 +1947,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
return 1;
}

-int ext4_ext_remove_space(struct inode *inode, unsigned long start)
+static int ext4_ext_remove_space(struct inode *inode, unsigned long start)
{
struct super_block *sb = inode->i_sb;
int depth = ext_depth(inode);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index a557c5c..8908ab3 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2159,11 +2159,11 @@ static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
for (p = first; p < last; p++) {
u32 nr = le32_to_cpu(*p);
if (nr) {
- struct buffer_head *bh;
+ struct buffer_head *tbh;

*p = 0;
- bh = sb_find_get_block(inode->i_sb, nr);
- ext4_forget(handle, 0, inode, bh, nr);
+ tbh = sb_find_get_block(inode->i_sb, nr);
+ ext4_forget(handle, 0, inode, tbh, nr);
}
}

@@ -2431,8 +2431,10 @@ void ext4_truncate(struct inode *inode)
return;
}

- if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
- return ext4_ext_truncate(inode, page);
+ if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
+ ext4_ext_truncate(inode, page);
+ return;
+ }

handle = start_transaction(inode);
if (IS_ERR(handle)) {
@@ -3283,7 +3285,7 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
* Expand an inode by new_extra_isize bytes.
* Returns 0 on success or negative error number on failure.
*/
-int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize,
+static int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize,
struct ext4_iloc iloc, handle_t *handle)
{
struct ext4_inode *raw_inode;
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index f6d4b4c..514464d 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -928,6 +928,8 @@ extern ext4_fsblk_t ext4_new_block (handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, int *errp);
extern ext4_fsblk_t ext4_new_blocks (handle_t *handle, struct inode *inode,
ext4_fsblk_t goal, unsigned long *count, int *errp);
+extern ext4_fsblk_t ext4_new_blocks_old (handle_t *handle, struct inode *inode,
+ ext4_fsblk_t goal, unsigned long *count, int *errp);
extern void ext4_free_blocks (handle_t *handle, struct inode *inode,
ext4_fsblk_t block, unsigned long count, int metadata);
extern void ext4_free_blocks_sb (handle_t *handle, struct super_block *sb,
--
1.5.3.4.206.g58ba4-dirty

2007-10-18 10:29:55

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC][PATCH 3/6] ext4: Code cleanup

rename i_file_acl to i_file_acl_lo

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/inode.c | 4 ++--
include/linux/ext4_fs.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 8908ab3..ef073df 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2825,7 +2825,7 @@ void ext4_read_inode(struct inode * inode)
}
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
ei->i_flags = le32_to_cpu(raw_inode->i_flags);
- ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+ ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo);
if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
cpu_to_le32(EXT4_OS_HURD))
ei->i_file_acl |=
@@ -2980,7 +2980,7 @@ static int ext4_do_update_inode(handle_t *handle,
cpu_to_le32(EXT4_OS_HURD))
raw_inode->i_file_acl_high =
cpu_to_le16(ei->i_file_acl >> 32);
- raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
+ raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl);
if (!S_ISREG(inode->i_mode)) {
raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
} else {
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index 514464d..6464ff9 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -325,7 +325,7 @@ struct ext4_inode {
} osd1; /* OS dependent 1 */
__le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
- __le32 i_file_acl; /* File ACL */
+ __le32 i_file_acl_lo; /* File ACL */
__le32 i_dir_acl; /* Directory ACL */
__le32 i_obso_faddr; /* Obsoleted fragment address */
union {
--
1.5.3.4.206.g58ba4-dirty

2007-10-18 10:30:03

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC][PATCH 4/6] ext4: code cleanup

rename ext4_inode.i_dir_acl to i_size_high
drop ext4_inode_info.i_dir_acl as it is not used

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/ialloc.c | 1 -
fs/ext4/inode.c | 55 ++++++++++++++++++---------------------------
include/linux/ext4_fs.h | 15 +++++++++--
include/linux/ext4_fs_i.h | 1 -
4 files changed, 34 insertions(+), 38 deletions(-)

diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index de40401..d775170 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -708,7 +708,6 @@ got:
if (!S_ISDIR(mode))
ei->i_flags &= ~EXT4_DIRSYNC_FL;
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;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ef073df..39859cf 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2801,7 +2801,6 @@ void ext4_read_inode(struct inode * inode)
inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
- inode->i_size = le32_to_cpu(raw_inode->i_size);

ei->i_state = 0;
ei->i_dir_start_lookup = 0;
@@ -2827,15 +2826,11 @@ void ext4_read_inode(struct inode * inode)
ei->i_flags = le32_to_cpu(raw_inode->i_flags);
ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo);
if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
- cpu_to_le32(EXT4_OS_HURD))
+ cpu_to_le32(EXT4_OS_HURD)) {
ei->i_file_acl |=
((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
- if (!S_ISREG(inode->i_mode)) {
- ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
- } else {
- inode->i_size |=
- ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
}
+ inode->i_size = ext4_isize(raw_inode);
ei->i_disksize = inode->i_size;
inode->i_generation = le32_to_cpu(raw_inode->i_generation);
ei->i_block_group = iloc.block_group;
@@ -2966,7 +2961,6 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_gid_high = 0;
}
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
- raw_inode->i_size = cpu_to_le32(ei->i_disksize);

EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode);
EXT4_INODE_SET_XTIME(i_mtime, inode, raw_inode);
@@ -2981,32 +2975,27 @@ static int ext4_do_update_inode(handle_t *handle,
raw_inode->i_file_acl_high =
cpu_to_le16(ei->i_file_acl >> 32);
raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl);
- if (!S_ISREG(inode->i_mode)) {
- raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
- } else {
- raw_inode->i_size_high =
- cpu_to_le32(ei->i_disksize >> 32);
- if (ei->i_disksize > 0x7fffffffULL) {
- struct super_block *sb = inode->i_sb;
- if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
- EXT4_FEATURE_RO_COMPAT_LARGE_FILE) ||
- EXT4_SB(sb)->s_es->s_rev_level ==
- cpu_to_le32(EXT4_GOOD_OLD_REV)) {
- /* If this is the first large file
- * created, add a flag to the superblock.
- */
- err = ext4_journal_get_write_access(handle,
- EXT4_SB(sb)->s_sbh);
- if (err)
- goto out_brelse;
- ext4_update_dynamic_rev(sb);
- EXT4_SET_RO_COMPAT_FEATURE(sb,
+ ext4_isize_set(raw_inode, ei->i_disksize);
+ if (ei->i_disksize > 0x7fffffffULL) {
+ struct super_block *sb = inode->i_sb;
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+ EXT4_FEATURE_RO_COMPAT_LARGE_FILE) ||
+ EXT4_SB(sb)->s_es->s_rev_level ==
+ cpu_to_le32(EXT4_GOOD_OLD_REV)) {
+ /* If this is the first large file
+ * created, add a flag to the superblock.
+ */
+ err = ext4_journal_get_write_access(handle,
+ EXT4_SB(sb)->s_sbh);
+ if (err)
+ goto out_brelse;
+ ext4_update_dynamic_rev(sb);
+ EXT4_SET_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
- sb->s_dirt = 1;
- handle->h_sync = 1;
- err = ext4_journal_dirty_metadata(handle,
- EXT4_SB(sb)->s_sbh);
- }
+ sb->s_dirt = 1;
+ handle->h_sync = 1;
+ err = ext4_journal_dirty_metadata(handle,
+ EXT4_SB(sb)->s_sbh);
}
}
raw_inode->i_generation = cpu_to_le32(inode->i_generation);
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index 6464ff9..5e972ba 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -303,7 +303,7 @@ struct ext4_mount_options {
struct ext4_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
- __le32 i_size; /* Size in bytes */
+ __le32 i_size_lo; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Inode Change time */
__le32 i_mtime; /* Modification time */
@@ -326,7 +326,7 @@ struct ext4_inode {
__le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
__le32 i_file_acl_lo; /* File ACL */
- __le32 i_dir_acl; /* Directory ACL */
+ __le32 i_size_high;
__le32 i_obso_faddr; /* Obsoleted fragment address */
union {
struct {
@@ -359,7 +359,6 @@ struct ext4_inode {
__le32 i_version_hi; /* high 32 bits for 64-bit version */
};

-#define i_size_high i_dir_acl

#define EXT4_EPOCH_BITS 2
#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
@@ -1099,7 +1098,17 @@ static inline void ext4_r_blocks_count_set(struct ext4_super_block *es,
es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32);
}

+static inline loff_t ext4_isize(struct ext4_inode *raw_inode)
+{
+ return ((loff_t)le32_to_cpu(raw_inode->i_size_high) << 32) |
+ le32_to_cpu(raw_inode->i_size_lo);
+}

+static inline void ext4_isize_set(struct ext4_inode *raw_inode, loff_t i_size)
+{
+ raw_inode->i_size_lo = cpu_to_le32(i_size);
+ raw_inode->i_size_high = cpu_to_le32(i_size >> 32);
+}

#define ext4_std_error(sb, errno) \
do { \
diff --git a/include/linux/ext4_fs_i.h b/include/linux/ext4_fs_i.h
index 94baea0..16249b9 100644
--- a/include/linux/ext4_fs_i.h
+++ b/include/linux/ext4_fs_i.h
@@ -85,7 +85,6 @@ struct ext4_inode_info {
__le32 i_data[15]; /* unconverted */
__u32 i_flags;
ext4_fsblk_t i_file_acl;
- __u32 i_dir_acl;
__u32 i_dtime;

/*
--
1.5.3.4.206.g58ba4-dirty

2007-10-18 10:30:20

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC][PATCH 6/6] ext4: Support large files

This patch converts ext4_inode i_blocks to represent total
blocks occupied by the inode in file system block size.
Earlier the variable used to represent this in 512 byte
block size. This actually limited the total size of the file.

The feature is enabled transparently when we write an inode
whose i_blocks cannot be represnted as 512 byte units in a
48 bit variable.

inode flag EXT4_HUGE_FILE_FL

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/inode.c | 34 ++++++++++++++++++++++++++--------
fs/ext4/super.c | 9 ++++++---
include/linux/ext4_fs.h | 3 ++-
3 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 98dbcb7..479d9b1 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2778,14 +2778,20 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
struct ext4_inode_info *ei)
{
blkcnt_t i_blocks ;
- struct super_block *sb = ei->vfs_inode.i_sb;
+ struct inode *inode = &(ei->vfs_inode);
+ struct super_block *sb = inode->i_sb;

if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
/* we are using combined 48 bit field */
i_blocks = ((u64)le16_to_cpu(raw_inode->i_blocks_high)) << 32 |
le32_to_cpu(raw_inode->i_blocks_lo);
- return i_blocks;
+ if (ei->i_flags & EXT4_HUGE_FILE_FL) {
+ /* i_blocks represent file system block size */
+ return i_blocks << (inode->i_blkbits - 9);
+ } else {
+ return i_blocks;
+ }
} else {
return le32_to_cpu(raw_inode->i_blocks_lo);
}
@@ -2943,8 +2949,9 @@ static int ext4_inode_blocks_set(handle_t *handle,
* i_blocks can be represnted in a 32 bit variable
* as multiple of 512 bytes
*/
- raw_inode->i_blocks_lo = cpu_to_le32((u32)i_blocks);
+ raw_inode->i_blocks_lo = cpu_to_le32(i_blocks);
raw_inode->i_blocks_high = 0;
+ ei->i_flags &= ~EXT4_HUGE_FILE_FL;
} else if (i_blocks <= 0xffffffffffffULL) {
/*
* i_blocks can be represented in a 48 bit variable
@@ -2955,12 +2962,23 @@ static int ext4_inode_blocks_set(handle_t *handle,
if (err)
goto err_out;
/* i_block is stored in the split 48 bit fields */
- raw_inode->i_blocks_lo = cpu_to_le32((u32)i_blocks);
+ raw_inode->i_blocks_lo = cpu_to_le32(i_blocks);
+ raw_inode->i_blocks_high = cpu_to_le16(i_blocks >> 32);
+ ei->i_flags &= ~EXT4_HUGE_FILE_FL;
+ } else {
+ /*
+ * i_blocks should be represented in a 48 bit variable
+ * as multiple of file system block size
+ */
+ err = ext4_update_rocompat_feature(handle, sb,
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
+ if (err)
+ goto err_out;
+ ei->i_flags |= EXT4_HUGE_FILE_FL;
+ /* i_block is stored in file system block size */
+ i_blocks = i_blocks >> (inode->i_blkbits - 9);
+ raw_inode->i_blocks_lo = cpu_to_le32(i_blocks);
raw_inode->i_blocks_high = cpu_to_le16(i_blocks >> 32);
- }else {
- ext4_error(sb, __FUNCTION__,
- "Wrong inode i_blocks count %llu\n",
- (unsigned long long)inode->i_blocks);
}
err_out:
return err;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index fee3bd7..801c499 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1660,11 +1660,14 @@ static loff_t ext4_max_size(int bits)
upper_limit >>= (bits - 9);

} else {
- /* We use 48 bit ext4_inode i_blocks */
+ /*
+ * We use 48 bit ext4_inode i_blocks
+ * With EXT4_HUGE_FILE_FL set the i_blocks
+ * represent total number of blocks in
+ * file system block size
+ */
upper_limit = (1LL << 48) - 1;

- /* total blocks in file system block size */
- upper_limit >>= (bits - 9);
}

/* indirect blocks */
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index 8d55c16..71f0569 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -205,8 +205,9 @@ struct ext4_group_desc
#define EXT4_NOTAIL_FL 0x00008000 /* file tail should not be merged */
#define EXT4_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */
#define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
-#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
+#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
+#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */

#define EXT4_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */
#define EXT4_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */
--
1.5.3.4.206.g58ba4-dirty

2007-10-18 10:32:47

by Aneesh Kumar K.V

[permalink] [raw]
Subject: [RFC][PATCH 5/6] ext4: Add support for 48 bit inode i_blocks.

use the __le16 l_i_reserved1 field of the linux2
struct of ext4_inode to represet the higher 16
bits for i_blocks. With this change max_file size becomes
(2**48 -1 )* 512 bytes.

We add a RO_COMPAT feature to the super
block to indicate that inode have i_blocks represented as
a split 48 bits. Super block with this feature set cannot
be mounted read write on a kernel with CONFIG_LSF disabled.

Super block flag EXT4_FEATURE_RO_COMPAT_HUGE_FILE

Signed-off-by: Aneesh Kumar K.V <[email protected]>
---
fs/ext4/inode.c | 58 ++++++++++++++++++++++++++++++++++++++++++-
fs/ext4/super.c | 62 ++++++++++++++++++++++++++++++++++++++++++----
include/linux/ext4_fs.h | 10 +++++--
3 files changed, 119 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 39859cf..98dbcb7 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2774,6 +2774,22 @@ void ext4_get_inode_flags(struct ext4_inode_info *ei)
if (flags & S_DIRSYNC)
ei->i_flags |= EXT4_DIRSYNC_FL;
}
+static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
+ struct ext4_inode_info *ei)
+{
+ blkcnt_t i_blocks ;
+ struct super_block *sb = ei->vfs_inode.i_sb;
+
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
+ /* we are using combined 48 bit field */
+ i_blocks = ((u64)le16_to_cpu(raw_inode->i_blocks_high)) << 32 |
+ le32_to_cpu(raw_inode->i_blocks_lo);
+ return i_blocks;
+ } else {
+ return le32_to_cpu(raw_inode->i_blocks_lo);
+ }
+}

void ext4_read_inode(struct inode * inode)
{
@@ -2822,8 +2838,8 @@ void ext4_read_inode(struct inode * inode)
* recovery code: that's fine, we're about to complete
* the process of deleting those. */
}
- inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
ei->i_flags = le32_to_cpu(raw_inode->i_flags);
+ inode->i_blocks = ext4_inode_blocks(raw_inode, ei);
ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo);
if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
cpu_to_le32(EXT4_OS_HURD)) {
@@ -2913,6 +2929,43 @@ bad_inode:
return;
}

+static int ext4_inode_blocks_set(handle_t *handle,
+ struct ext4_inode *raw_inode,
+ struct ext4_inode_info *ei)
+{
+ struct inode *inode = &(ei->vfs_inode);
+ u64 i_blocks = inode->i_blocks;
+ struct super_block *sb = inode->i_sb;
+ int err = 0;
+
+ if (i_blocks <= ~0U) {
+ /*
+ * i_blocks can be represnted in a 32 bit variable
+ * as multiple of 512 bytes
+ */
+ raw_inode->i_blocks_lo = cpu_to_le32((u32)i_blocks);
+ raw_inode->i_blocks_high = 0;
+ } else if (i_blocks <= 0xffffffffffffULL) {
+ /*
+ * i_blocks can be represented in a 48 bit variable
+ * as multiple of 512 bytes
+ */
+ err = ext4_update_rocompat_feature(handle, sb,
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
+ if (err)
+ goto err_out;
+ /* i_block is stored in the split 48 bit fields */
+ raw_inode->i_blocks_lo = cpu_to_le32((u32)i_blocks);
+ raw_inode->i_blocks_high = cpu_to_le16(i_blocks >> 32);
+ }else {
+ ext4_error(sb, __FUNCTION__,
+ "Wrong inode i_blocks count %llu\n",
+ (unsigned long long)inode->i_blocks);
+ }
+err_out:
+ return err;
+}
+
/*
* Post the struct inode info into an on-disk inode location in the
* buffer-cache. This gobbles the caller's reference to the
@@ -2967,7 +3020,8 @@ static int ext4_do_update_inode(handle_t *handle,
EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);

- raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+ if (ext4_inode_blocks_set(handle, raw_inode, ei))
+ goto out_brelse;
raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
raw_inode->i_flags = cpu_to_le32(ei->i_flags);
if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index e2bed20..fee3bd7 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1632,17 +1632,50 @@ static void ext4_orphan_cleanup (struct super_block * sb,

/*
* Maximal file size. There is a direct, and {,double-,triple-}indirect
- * block limit, and also a limit of (2^32 - 1) 512-byte sectors in i_blocks.
- * We need to be 1 filesystem block less than the 2^32 sector limit.
+ * block limit, and also a limit of (2^48 - 1) 512-byte sectors in i_blocks.
+ * We need to be 1 filesystem block less than the 2^48 sector limit.
*/
static loff_t ext4_max_size(int bits)
{
loff_t res = EXT4_NDIR_BLOCKS;
- /* This constant is calculated to be the largest file size for a
- * dense, 4k-blocksize file such that the total number of
+ int meta_blocks;
+ loff_t upper_limit;
+ /* This is calculated to be the largest file size for a
+ * dense, file such that the total number of
* sectors in the file, including data and all indirect blocks,
- * does not exceed 2^32. */
- const loff_t upper_limit = 0x1ff7fffd000LL;
+ * does not exceed 2^48 -1
+ * __u32 i_blocks_lo and _u16 i_blocks_high representing the
+ * total number of 512 bytes blocks of the file
+ */
+
+ if (sizeof(blkcnt_t) < sizeof(u64)) {
+ /*
+ * CONFIG_LSF is not enabled implies the inode
+ * i_block represent total blocks in 512 bytes
+ * 32 == size of vfs inode i_blocks * 8
+ */
+ upper_limit = (1LL << 32) - 1;
+
+ /* total blocks in file system block size */
+ upper_limit >>= (bits - 9);
+
+ } else {
+ /* We use 48 bit ext4_inode i_blocks */
+ upper_limit = (1LL << 48) - 1;
+
+ /* total blocks in file system block size */
+ upper_limit >>= (bits - 9);
+ }
+
+ /* indirect blocks */
+ meta_blocks = 1;
+ /* double indirect blocks */
+ meta_blocks += 1 + (1LL << (bits-2));
+ /* tripple indirect blocks */
+ meta_blocks += 1 + (1LL << (bits-2)) + (1LL << (2*(bits-2)));
+
+ upper_limit -= meta_blocks;
+ upper_limit <<= bits;

res += 1LL << (bits-2);
res += 1LL << (2*(bits-2));
@@ -1650,6 +1683,10 @@ static loff_t ext4_max_size(int bits)
res <<= bits;
if (res > upper_limit)
res = upper_limit;
+
+ if (res > MAX_LFS_FILESIZE)
+ res = MAX_LFS_FILESIZE;
+
return res;
}

@@ -1815,6 +1852,19 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent)
sb->s_id, le32_to_cpu(features));
goto failed_mount;
}
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
+ /*
+ * Large file size enabled file system can only be
+ * mount if kernel is build with CONFIG_LSF
+ */
+ if (sizeof(root->i_blocks) < sizeof(u64) &&
+ !(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_ERR "EXT4-fs: %s: Filesystem with huge "
+ "files cannot be mounted read-write "
+ "without CONFIG_LSF.\n", sb->s_id);
+ goto failed_mount;
+ }
+ }
blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);

if (blocksize < EXT4_MIN_BLOCK_SIZE ||
diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h
index 5e972ba..8d55c16 100644
--- a/include/linux/ext4_fs.h
+++ b/include/linux/ext4_fs.h
@@ -310,7 +310,7 @@ struct ext4_inode {
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
- __le32 i_blocks; /* Blocks count */
+ __le32 i_blocks_lo; /* Blocks count */
__le32 i_flags; /* File flags */
union {
struct {
@@ -330,7 +330,7 @@ struct ext4_inode {
__le32 i_obso_faddr; /* Obsoleted fragment address */
union {
struct {
- __le16 l_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */
+ __le16 l_i_blocks_high; /* were l_i_reserved1 */
__le16 l_i_file_acl_high;
__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
@@ -435,6 +435,7 @@ do { \
#if defined(__KERNEL__) || defined(__linux__)
#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_file_acl_high osd2.linux2.l_i_file_acl_high
+#define i_blocks_high osd2.linux2.l_i_blocks_high
#define i_uid_low i_uid
#define i_gid_low i_gid
#define i_uid_high osd2.linux2.l_i_uid_high
@@ -705,6 +706,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
+#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
@@ -716,6 +718,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
#define EXT4_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200

#define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
@@ -730,7 +733,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
- EXT4_FEATURE_RO_COMPAT_BTREE_DIR)
+ EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\
+ EXT4_FEATURE_RO_COMPAT_HUGE_FILE)

/*
* Default values for user and/or group using reserved blocks
--
1.5.3.4.206.g58ba4-dirty