From d5aee659f217746395ff58adf3a863627ff02ec1 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 30 Jan 2019 12:58:05 -0700 Subject: fs: ext4: cache extent data When a file contains extents, U-Boot currently reads extent-related data for each block in the file, even if that data is located in the same block each time. This significantly slows down loading of files that use extents. Implement a very dumb cache to prevent repeatedly reading the same block. Files with extents now load as fast as files without. Note: There are many cases where read_allocated_block() is called. This patch only addresses one of those places; all others still read redundant data in any case they did before. This is a minimal patch to fix the load command; other cases aren't fixed. Signed-off-by: Stephen Warren --- fs/ext4/ext4_common.c | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 67e2471bd38..29308e3b447 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -510,7 +510,8 @@ restart: restart_read: /* read the block no allocated to a file */ - first_block_no_of_root = read_allocated_block(g_parent_inode, blk_idx); + first_block_no_of_root = read_allocated_block(g_parent_inode, blk_idx, + NULL); if (first_block_no_of_root <= 0) goto fail; @@ -646,7 +647,7 @@ static int search_dir(struct ext2_inode *parent_inode, char *dirname) /* get the block no allocated to a file */ for (blk_idx = 0; blk_idx < directory_blocks; blk_idx++) { - blknr = read_allocated_block(parent_inode, blk_idx); + blknr = read_allocated_block(parent_inode, blk_idx, NULL); if (blknr <= 0) goto fail; @@ -943,7 +944,7 @@ int ext4fs_filename_unlink(char *filename) /* read the block no allocated to a file */ for (blk_idx = 0; blk_idx < directory_blocks; blk_idx++) { - blknr = read_allocated_block(g_parent_inode, blk_idx); + blknr = read_allocated_block(g_parent_inode, blk_idx, NULL); if (blknr <= 0) break; inodeno = unlink_filename(filename, blknr); @@ -1522,7 +1523,7 @@ void ext4fs_allocate_blocks(struct ext2_inode *file_inode, #endif static struct ext4_extent_header *ext4fs_get_extent_block - (struct ext2_data *data, char *buf, + (struct ext2_data *data, struct ext_block_cache *cache, struct ext4_extent_header *ext_block, uint32_t fileblock, int log2_blksz) { @@ -1551,12 +1552,10 @@ 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 + block <<= log2_blksz; + if (!ext_cache_read(cache, (lbaint_t)block, blksz)) return NULL; + ext_block = (struct ext4_extent_header *)cache->buf; } } @@ -1613,7 +1612,8 @@ int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) return 1; } -long int read_allocated_block(struct ext2_inode *inode, int fileblock) +long int read_allocated_block(struct ext2_inode *inode, int fileblock, + struct ext_block_cache *cache) { long int blknr; int blksz; @@ -1630,20 +1630,26 @@ long int read_allocated_block(struct ext2_inode *inode, int fileblock) if (le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) { long int startblock, endblock; - char *buf = zalloc(blksz); - if (!buf) - return -ENOMEM; + struct ext_block_cache *c, cd; struct ext4_extent_header *ext_block; struct ext4_extent *extent; int i; + + if (cache) { + c = cache; + } else { + c = &cd; + ext_cache_init(c); + } ext_block = - ext4fs_get_extent_block(ext4fs_root, buf, + ext4fs_get_extent_block(ext4fs_root, c, (struct ext4_extent_header *) inode->b.blocks.dir_blocks, fileblock, log2_blksz); if (!ext_block) { printf("invalid extent block\n"); - free(buf); + if (!cache) + ext_cache_fini(c); return -EINVAL; } @@ -1655,19 +1661,22 @@ long int read_allocated_block(struct ext2_inode *inode, int fileblock) if (startblock > fileblock) { /* Sparse file */ - free(buf); + if (!cache) + ext_cache_fini(c); return 0; } else if (fileblock < endblock) { start = le16_to_cpu(extent[i].ee_start_hi); start = (start << 32) + le32_to_cpu(extent[i].ee_start_lo); - free(buf); + if (!cache) + ext_cache_fini(c); return (fileblock - startblock) + start; } } - free(buf); + if (!cache) + ext_cache_fini(c); return 0; } -- cgit v1.2.3 From b000180b0f467851525aae3d0dfb8ab3a9dbcf8f Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Wed, 13 Feb 2019 12:15:24 +0100 Subject: fs: ext4: constify the buffer passed to write functions There is no need to modify the buffer passed to ext4fs_write_file(). The memset() call is not required here and was likely copied from the equivalent part of the ext4fs_read_file() function where we do need it. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Tom Rini --- fs/ext4/ext4_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 29308e3b447..e9123785f11 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -190,7 +190,7 @@ uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n) return res; } -void put_ext4(uint64_t off, void *buf, uint32_t size) +void put_ext4(uint64_t off, const void *buf, uint32_t size) { uint64_t startblock; uint64_t remainder; -- cgit v1.2.3 From 5efc0686eebc0c0daabfbfc2c403f8251b468526 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Wed, 13 Feb 2019 12:15:25 +0100 Subject: fs: ext4: Add support for the creation of symbolic links Re-use the functions used to write/create a file, to support creation of a symbolic link. The difference with a regular file are small: - The inode mode is flagged with S_IFLNK instead of S_IFREG - The ext2_dirent's filetype is FILETYPE_SYMLINK instead of FILETYPE_REG - Instead of storing the content of a file in allocated blocks, the path to the target is stored. And if the target's path is short enough, no block is allocated and the target's path is stored in ext2_inode.b.symlink As with regulars files, if a file/symlink with the same name exits, it is unlinked first and then re-created. Signed-off-by: Jean-Jacques Hiblot Reviewed-by: Tom Rini [trini: Fix ext4 env code] Signed-off-by: Tom Rini --- fs/ext4/ext4_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index e9123785f11..59ad6c8f8c1 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -608,7 +608,7 @@ restart_read: dir->direntlen = cpu_to_le16(fs->blksz - totalbytes); dir->namelen = strlen(filename); - dir->filetype = FILETYPE_REG; /* regular file */ + dir->filetype = file_type; temp_dir = (char *)dir; temp_dir = temp_dir + sizeof(struct ext2_dirent); memcpy(temp_dir, filename, strlen(filename)); -- cgit v1.2.3 From 1c48fda3e5a88159130b4d4805fbdf367212afab Mon Sep 17 00:00:00 2001 From: Gero Schumacher Date: Tue, 26 Feb 2019 15:45:22 +0000 Subject: fs: ext4: Problem with ext4load and sparse files Hi, when I try to load a sparse file via ext4load, I am getting the error message 'invalid extent' After a deeper look in the code, it seems to be an issue in the function ext4fs_get_extent_block in fs/ext4/ext4_common.c: The file starts with 1k of zeros. The blocksize is 1024. So the first extend block contains the following information: eh_entries: 1 eh_depth: 1 ei_block 1 When the upper layer (ext4fs_read_file) asks for fileblock 0, we are running in the 'invalid extent' error message. For me it seems, that the code is not prepared for handling a sparse block at the beginning of the file. The following change, solved my problem: I am really not an expert in ext4 filesystems. Can somebody please have a look at this issue and give me a feedback, if I am totally wrong or not? --- fs/ext4/ext4_common.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 59ad6c8f8c1..feffbfa9a9e 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -1547,8 +1547,12 @@ static struct ext4_extent_header *ext4fs_get_extent_block break; } while (fileblock >= le32_to_cpu(index[i].ei_block)); - if (--i < 0) - return NULL; + /* + * If first logical block number is higher than requested fileblock, + * it is a sparse file. This is handled on upper layer. + */ + if (i > 0) + i--; block = le16_to_cpu(index[i].ei_leaf_hi); block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); -- cgit v1.2.3 From febbc583319b567fe3d83e521cc2ace9be8d1501 Mon Sep 17 00:00:00 2001 From: Benjamin Lim Date: Fri, 29 Mar 2019 07:29:45 -0400 Subject: Fix ext4 block group descriptor sizing Ext4 allows for arbitrarily sized block group descriptors when 64-bit addressing is enabled, which was previously not properly supported. This patch dynamically allocates a chunk of memory of the correct size. Signed-off-by: Benjamin Lim --- fs/ext4/ext4_common.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index feffbfa9a9e..464c33d0d74 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -1587,7 +1587,7 @@ static int ext4fs_blockgroup int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) { - struct ext2_block_group blkgrp; + struct ext2_block_group *blkgrp; struct ext2_sblock *sblock = &data->sblock; struct ext_filesystem *fs = get_fs(); int log2blksz = get_fs()->dev_desc->log2blksz; @@ -1595,17 +1595,28 @@ int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) long int blkno; unsigned int blkoff; + /* Allocate blkgrp based on gdsize (for 64-bit support). */ + blkgrp = zalloc(get_fs()->gdsize); + if (!blkgrp) + return 0; + /* It is easier to calculate if the first inode is 0. */ ino--; status = ext4fs_blockgroup(data, ino / le32_to_cpu - (sblock->inodes_per_group), &blkgrp); - if (status == 0) + (sblock->inodes_per_group), blkgrp); + if (status == 0) { + free(blkgrp); return 0; + } inodes_per_block = EXT2_BLOCK_SIZE(data) / fs->inodesz; - blkno = ext4fs_bg_get_inode_table_id(&blkgrp, fs) + + blkno = ext4fs_bg_get_inode_table_id(blkgrp, fs) + (ino % le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; blkoff = (ino % inodes_per_block) * fs->inodesz; + + /* Free blkgrp as it is no longer required. */ + free(blkgrp); + /* Read the inode. */ status = ext4fs_devread((lbaint_t)blkno << (LOG2_BLOCK_SIZE(data) - log2blksz), blkoff, -- cgit v1.2.3