Commit 3d0518f4 authored by Wei Yongjun's avatar Wei Yongjun Committed by Theodore Ts'o

ext4: New rec_len encoding for very large blocksizes

The rec_len field in the directory entry is 16 bits, so to encode
blocksizes larger than 64k becomes problematic.  This patch allows us
to supprot block sizes up to 256k, by using the low 2 bits to extend
the range of rec_len to 2**18-1 (since valid rec_len sizes must be a
multiple of 4).  We use the convention that a rec_len of 0 or 65535
means the filesystem block size, for compatibility with older kernels.

It's unlikely we'll see VM pages of up to 256k, but at some point we
might find that the Linux VM has been enhanced to support filesystem
block sizes > than the VM page size, at which point it might be useful
for some applications to allow very large filesystem block sizes.
Signed-off-by: default avatarWei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 8bad4597
...@@ -67,7 +67,8 @@ int ext4_check_dir_entry(const char *function, struct inode *dir, ...@@ -67,7 +67,8 @@ int ext4_check_dir_entry(const char *function, struct inode *dir,
unsigned int offset) unsigned int offset)
{ {
const char *error_msg = NULL; const char *error_msg = NULL;
const int rlen = ext4_rec_len_from_disk(de->rec_len); const int rlen = ext4_rec_len_from_disk(de->rec_len,
dir->i_sb->s_blocksize);
if (rlen < EXT4_DIR_REC_LEN(1)) if (rlen < EXT4_DIR_REC_LEN(1))
error_msg = "rec_len is smaller than minimal"; error_msg = "rec_len is smaller than minimal";
...@@ -178,10 +179,11 @@ static int ext4_readdir(struct file *filp, ...@@ -178,10 +179,11 @@ static int ext4_readdir(struct file *filp,
* least that it is non-zero. A * least that it is non-zero. A
* failure will be detected in the * failure will be detected in the
* dirent test below. */ * dirent test below. */
if (ext4_rec_len_from_disk(de->rec_len) if (ext4_rec_len_from_disk(de->rec_len,
< EXT4_DIR_REC_LEN(1)) sb->s_blocksize) < EXT4_DIR_REC_LEN(1))
break; break;
i += ext4_rec_len_from_disk(de->rec_len); i += ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize);
} }
offset = i; offset = i;
filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
...@@ -203,7 +205,8 @@ static int ext4_readdir(struct file *filp, ...@@ -203,7 +205,8 @@ static int ext4_readdir(struct file *filp,
ret = stored; ret = stored;
goto out; goto out;
} }
offset += ext4_rec_len_from_disk(de->rec_len); offset += ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize);
if (le32_to_cpu(de->inode)) { if (le32_to_cpu(de->inode)) {
/* We might block in the next section /* We might block in the next section
* if the data destination is * if the data destination is
...@@ -225,7 +228,8 @@ static int ext4_readdir(struct file *filp, ...@@ -225,7 +228,8 @@ static int ext4_readdir(struct file *filp,
goto revalidate; goto revalidate;
stored++; stored++;
} }
filp->f_pos += ext4_rec_len_from_disk(de->rec_len); filp->f_pos += ext4_rec_len_from_disk(de->rec_len,
sb->s_blocksize);
} }
offset = 0; offset = 0;
brelse(bh); brelse(bh);
......
...@@ -855,24 +855,6 @@ struct ext4_dir_entry_2 { ...@@ -855,24 +855,6 @@ struct ext4_dir_entry_2 {
~EXT4_DIR_ROUND) ~EXT4_DIR_ROUND)
#define EXT4_MAX_REC_LEN ((1<<16)-1) #define EXT4_MAX_REC_LEN ((1<<16)-1)
static inline unsigned ext4_rec_len_from_disk(__le16 dlen)
{
unsigned len = le16_to_cpu(dlen);
if (len == EXT4_MAX_REC_LEN || len == 0)
return 1 << 16;
return len;
}
static inline __le16 ext4_rec_len_to_disk(unsigned len)
{
if (len == (1 << 16))
return cpu_to_le16(EXT4_MAX_REC_LEN);
else if (len > (1 << 16))
BUG();
return cpu_to_le16(len);
}
/* /*
* Hash Tree Directory indexing * Hash Tree Directory indexing
* (c) Daniel Phillips, 2001 * (c) Daniel Phillips, 2001
...@@ -1097,7 +1079,10 @@ extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long); ...@@ -1097,7 +1079,10 @@ extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
/* migrate.c */ /* migrate.c */
extern int ext4_ext_migrate(struct inode *); extern int ext4_ext_migrate(struct inode *);
/* namei.c */ /* namei.c */
extern unsigned int ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize);
extern __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize);
extern int ext4_orphan_add(handle_t *, struct inode *); extern int ext4_orphan_add(handle_t *, struct inode *);
extern int ext4_orphan_del(handle_t *, struct inode *); extern int ext4_orphan_del(handle_t *, struct inode *);
extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
......
This diff is collapsed.
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