Message ID | 1509113891-17584-2-git-send-email-c.ronco@kerlink.fr |
---|---|
State | Deferred |
Delegated to: | Tom Rini |
Headers | show |
Series | [U-Boot] ext4: cache first extent index blocks (#06343) | expand |
On Fri, Oct 27, 2017 at 04:18:11PM +0200, Christophe Ronco wrote: > Extent index blocks are read many times when reading completely a big file. > This is time costly if underlying driver is slow. > Caching the first index blocks cost a bit of RAM but speed up file reading a lot. > --- > fs/ext4/ext4_common.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++--- > fs/ext4/ext4_common.h | 16 +++++++++ > fs/ext4/ext4fs.c | 16 +++++++-- > 3 files changed, 117 insertions(+), 8 deletions(-) > > diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c > index cab5465..68ab2e9 100644 > --- a/fs/ext4/ext4_common.c > +++ b/fs/ext4/ext4_common.c > @@ -44,6 +44,7 @@ int ext4fs_indir3_size; > int ext4fs_indir3_blkno = -1; > struct ext2_inode *g_parent_inode; > static int symlinknest; > +struct ext4_extent_blocs ext4fs_extents_blocs; > > #if defined(CONFIG_EXT4_WRITE) > uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n) > @@ -1413,6 +1414,7 @@ static struct ext4_extent_header *ext4fs_get_extent_block > unsigned long long block; > int blksz = EXT2_BLOCK_SIZE(data); > int i; > + int found; > > while (1) { > index = (struct ext4_extent_idx *)(ext_block + 1); > @@ -1435,11 +1437,24 @@ static struct ext4_extent_header *ext4fs_get_extent_block > block = le16_to_cpu(index[i].ei_leaf_hi); > block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); > > - if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, blksz, > - buf)) > - ext_block = (struct ext4_extent_header *)buf; > - else > - return 0; > + /* look in saved extent blocks */ > + found = 0; > + for (i = 0; i < ext4fs_extents_blocs.bloc_nb; i++) { > + if (ext4fs_extents_blocs.ext_blocs[i].block == block) { > + memcpy(buf, ext4fs_extents_blocs.ext_blocs[i].ext_header, > + blksz); > + ext_block = (struct ext4_extent_header *)buf; > + found = 1; > + } > + } > + > + if (found == 0) { > + if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, > + blksz, buf)) > + ext_block = (struct ext4_extent_header *)buf; > + else > + return 0; > + } > } > } > > @@ -1465,6 +1480,74 @@ static int ext4fs_blockgroup > (char *)blkgrp); > } > > +void ext4fs_init_extents_blocks_global(void) > +{ > + memset(&ext4fs_extents_blocs, 0, sizeof(struct ext4_extent_blocs)); > +} > + > +int ext4fs_read_extents_blocks(struct ext2_inode *inode) > +{ > + int blksz = EXT2_BLOCK_SIZE(ext4fs_root); > + int log2_blksz; > + struct ext4_extent_header *ext_block; > + struct ext4_extent_idx *index; > + unsigned long long block; > + int entries; > + char *buf; > + int i; > + > + ext4fs_extents_blocs.bloc_nb = 0; > + log2_blksz = LOG2_BLOCK_SIZE(ext4fs_root) > + - get_fs()->dev_desc->log2blksz; > + > + if ((le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) == 0) > + return 0; > + > + ext_block = (struct ext4_extent_header *)inode->b.blocks.dir_blocks; > + > + if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC) > + return 0; > + if (ext_block->eh_depth == 0) > + return 0; > + > + entries = le16_to_cpu(ext_block->eh_entries); > + index = (struct ext4_extent_idx *)(ext_block + 1); > + > + for (i = 0; (i < entries) && (i < MAX_EXTENT_BLOCS); i++) { > + /* bloc number */ > + block = le16_to_cpu(index[i].ei_leaf_hi); > + block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); > + /* bloc data */ > + buf = zalloc(blksz); > + if (!buf) > + break; > + if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, blksz, > + buf) == 0) { > + free(buf); > + break; > + } > + ext4fs_extents_blocs.ext_blocs[i].block = block; > + ext4fs_extents_blocs.ext_blocs[i].ext_header = > + (struct ext4_extent_header *)buf; > + } > + ext4fs_extents_blocs.bloc_nb = i; > + return i; > +} > + > +void ext4fs_free_extents_blocks(void) > +{ > + int i; > + > + for (i = 0; i < ext4fs_extents_blocs.bloc_nb; i++) { > + if (ext4fs_extents_blocs.ext_blocs[i].ext_header) { > + free(ext4fs_extents_blocs.ext_blocs[i].ext_header); > + ext4fs_extents_blocs.ext_blocs[i].ext_header = NULL; > + ext4fs_extents_blocs.ext_blocs[i].block = 0; > + } > + } > + ext4fs_extents_blocs.bloc_nb = 0; > +} > + > int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) > { > struct ext2_block_group blkgrp; > diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h > index 48fd2ac..0f80856 100644 > --- a/fs/ext4/ext4_common.h > +++ b/fs/ext4/ext4_common.h > @@ -41,6 +41,18 @@ > #define SUPERBLOCK_SIZE 1024 > #define F_FILE 1 > > +#define MAX_EXTENT_BLOCS 10 > + > +struct ext4_extent_bloc { > + unsigned long long block; > + struct ext4_extent_header *ext_header; > +}; > + > +struct ext4_extent_blocs { > + int bloc_nb; > + struct ext4_extent_bloc ext_blocs[MAX_EXTENT_BLOCS]; > +}; > + > static inline void *zalloc(size_t size) > { > void *p = memalign(ARCH_DMA_MINALIGN, size); > @@ -57,6 +69,10 @@ int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode, > int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, > struct ext2fs_node **fnode, int *ftype); > > +void ext4fs_init_extents_blocks_global(void); > +int ext4fs_read_extents_blocks(struct ext2_inode *inode); > +void ext4fs_free_extents_blocks(void); > + > #if defined(CONFIG_EXT4_WRITE) > uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n); > int ext4fs_checksum_update(unsigned int i); > diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c > index 258b937..88db1fb 100644 > --- a/fs/ext4/ext4fs.c > +++ b/fs/ext4/ext4fs.c > @@ -70,6 +70,12 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, > > blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize); > > + ext4fs_init_extents_blocks_global(); > + if (blockcnt > 100) { > + /* Big reading: read extent blocks tree if any */ > + ext4fs_read_extents_blocks(&(node->inode)); > + } > + > for (i = lldiv(pos, blocksize); i < blockcnt; i++) { > lbaint_t blknr; > int blockoff = pos - (blocksize * i); > @@ -77,7 +83,7 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, > int skipfirst = 0; > blknr = read_allocated_block(&(node->inode), i); > if (blknr < 0) > - return -1; > + goto error; > > blknr = blknr << log2_fs_blocksize; > > @@ -134,7 +140,7 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, > delayed_extent, > delayed_buf); > if (status == 0) > - return -1; > + goto error; > previous_block_number = -1; > } > memset(buf, 0, blocksize - skipfirst); > @@ -147,12 +153,16 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, > delayed_skipfirst, delayed_extent, > delayed_buf); > if (status == 0) > - return -1; > + goto error; > previous_block_number = -1; > } > > *actread = len; > + ext4fs_free_extents_blocks(); > return 0; > +error: > + ext4fs_free_extents_blocks(); > + return -1; > } > > int ext4fs_ls(const char *dirname) Is this still needed with: commit ecdfb4195b20eb2dcde3c4083170016c13c69e8b Author: Ian Ray <ian.ray@ge.com> Date: Wed Nov 8 15:35:10 2017 +0000 ext4: recover from filesystem corruption when reading Some fixes when reading EXT files and directory entries were identified after using e2fuzz to corrupt an EXT3 filesystem: - Stop reading directory entries if the offset becomes badly aligned. - Avoid overwriting memory by clamping the length used to zero the buffer in ext4fs_read_file. Also sanity check blocksize. Signed-off-by: Ian Ray <ian.ray@ge.com> Signed-off-by: Martyn Welch <martyn.welch@collabora.co.uk> Reviewed-by: Stefano Babic <sbabic@denx.de> Applied? Thanks!
diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index cab5465..68ab2e9 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -44,6 +44,7 @@ int ext4fs_indir3_size; int ext4fs_indir3_blkno = -1; struct ext2_inode *g_parent_inode; static int symlinknest; +struct ext4_extent_blocs ext4fs_extents_blocs; #if defined(CONFIG_EXT4_WRITE) uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n) @@ -1413,6 +1414,7 @@ static struct ext4_extent_header *ext4fs_get_extent_block unsigned long long block; int blksz = EXT2_BLOCK_SIZE(data); int i; + int found; while (1) { index = (struct ext4_extent_idx *)(ext_block + 1); @@ -1435,11 +1437,24 @@ static struct ext4_extent_header *ext4fs_get_extent_block block = le16_to_cpu(index[i].ei_leaf_hi); block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); - if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, blksz, - buf)) - ext_block = (struct ext4_extent_header *)buf; - else - return 0; + /* look in saved extent blocks */ + found = 0; + for (i = 0; i < ext4fs_extents_blocs.bloc_nb; i++) { + if (ext4fs_extents_blocs.ext_blocs[i].block == block) { + memcpy(buf, ext4fs_extents_blocs.ext_blocs[i].ext_header, + blksz); + ext_block = (struct ext4_extent_header *)buf; + found = 1; + } + } + + if (found == 0) { + if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, + blksz, buf)) + ext_block = (struct ext4_extent_header *)buf; + else + return 0; + } } } @@ -1465,6 +1480,74 @@ static int ext4fs_blockgroup (char *)blkgrp); } +void ext4fs_init_extents_blocks_global(void) +{ + memset(&ext4fs_extents_blocs, 0, sizeof(struct ext4_extent_blocs)); +} + +int ext4fs_read_extents_blocks(struct ext2_inode *inode) +{ + int blksz = EXT2_BLOCK_SIZE(ext4fs_root); + int log2_blksz; + struct ext4_extent_header *ext_block; + struct ext4_extent_idx *index; + unsigned long long block; + int entries; + char *buf; + int i; + + ext4fs_extents_blocs.bloc_nb = 0; + log2_blksz = LOG2_BLOCK_SIZE(ext4fs_root) + - get_fs()->dev_desc->log2blksz; + + if ((le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) == 0) + return 0; + + ext_block = (struct ext4_extent_header *)inode->b.blocks.dir_blocks; + + if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC) + return 0; + if (ext_block->eh_depth == 0) + return 0; + + entries = le16_to_cpu(ext_block->eh_entries); + index = (struct ext4_extent_idx *)(ext_block + 1); + + for (i = 0; (i < entries) && (i < MAX_EXTENT_BLOCS); i++) { + /* bloc number */ + block = le16_to_cpu(index[i].ei_leaf_hi); + block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); + /* bloc data */ + buf = zalloc(blksz); + if (!buf) + break; + if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, blksz, + buf) == 0) { + free(buf); + break; + } + ext4fs_extents_blocs.ext_blocs[i].block = block; + ext4fs_extents_blocs.ext_blocs[i].ext_header = + (struct ext4_extent_header *)buf; + } + ext4fs_extents_blocs.bloc_nb = i; + return i; +} + +void ext4fs_free_extents_blocks(void) +{ + int i; + + for (i = 0; i < ext4fs_extents_blocs.bloc_nb; i++) { + if (ext4fs_extents_blocs.ext_blocs[i].ext_header) { + free(ext4fs_extents_blocs.ext_blocs[i].ext_header); + ext4fs_extents_blocs.ext_blocs[i].ext_header = NULL; + ext4fs_extents_blocs.ext_blocs[i].block = 0; + } + } + ext4fs_extents_blocs.bloc_nb = 0; +} + int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) { struct ext2_block_group blkgrp; diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h index 48fd2ac..0f80856 100644 --- a/fs/ext4/ext4_common.h +++ b/fs/ext4/ext4_common.h @@ -41,6 +41,18 @@ #define SUPERBLOCK_SIZE 1024 #define F_FILE 1 +#define MAX_EXTENT_BLOCS 10 + +struct ext4_extent_bloc { + unsigned long long block; + struct ext4_extent_header *ext_header; +}; + +struct ext4_extent_blocs { + int bloc_nb; + struct ext4_extent_bloc ext_blocs[MAX_EXTENT_BLOCS]; +}; + static inline void *zalloc(size_t size) { void *p = memalign(ARCH_DMA_MINALIGN, size); @@ -57,6 +69,10 @@ int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode, int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, struct ext2fs_node **fnode, int *ftype); +void ext4fs_init_extents_blocks_global(void); +int ext4fs_read_extents_blocks(struct ext2_inode *inode); +void ext4fs_free_extents_blocks(void); + #if defined(CONFIG_EXT4_WRITE) uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n); int ext4fs_checksum_update(unsigned int i); diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c index 258b937..88db1fb 100644 --- a/fs/ext4/ext4fs.c +++ b/fs/ext4/ext4fs.c @@ -70,6 +70,12 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, blockcnt = lldiv(((len + pos) + blocksize - 1), blocksize); + ext4fs_init_extents_blocks_global(); + if (blockcnt > 100) { + /* Big reading: read extent blocks tree if any */ + ext4fs_read_extents_blocks(&(node->inode)); + } + for (i = lldiv(pos, blocksize); i < blockcnt; i++) { lbaint_t blknr; int blockoff = pos - (blocksize * i); @@ -77,7 +83,7 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, int skipfirst = 0; blknr = read_allocated_block(&(node->inode), i); if (blknr < 0) - return -1; + goto error; blknr = blknr << log2_fs_blocksize; @@ -134,7 +140,7 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, delayed_extent, delayed_buf); if (status == 0) - return -1; + goto error; previous_block_number = -1; } memset(buf, 0, blocksize - skipfirst); @@ -147,12 +153,16 @@ int ext4fs_read_file(struct ext2fs_node *node, loff_t pos, delayed_skipfirst, delayed_extent, delayed_buf); if (status == 0) - return -1; + goto error; previous_block_number = -1; } *actread = len; + ext4fs_free_extents_blocks(); return 0; +error: + ext4fs_free_extents_blocks(); + return -1; } int ext4fs_ls(const char *dirname)