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 67e2471bd3..29308e3b44 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 +- fs/ext4/ext4_common.h | 2 +- fs/ext4/ext4_write.c | 11 +++++------ include/ext4fs.h | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 29308e3b44..e9123785f1 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; diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h index 1ee81ab7ce..4dff1914d9 100644 --- a/fs/ext4/ext4_common.h +++ b/fs/ext4/ext4_common.h @@ -72,7 +72,7 @@ int ext4fs_iget(int inode_no, struct ext2_inode *inode); void ext4fs_allocate_blocks(struct ext2_inode *file_inode, unsigned int total_remaining_blocks, unsigned int *total_no_of_block); -void put_ext4(uint64_t off, void *buf, uint32_t size); +void put_ext4(uint64_t off, const void *buf, uint32_t size); struct ext2_block_group *ext4fs_get_group_descriptor (const struct ext_filesystem *fs, uint32_t bg_idx); uint64_t ext4fs_bg_get_block_id(const struct ext2_block_group *bg, diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c index 95ffa3dfad..b5b7ee8133 100644 --- a/fs/ext4/ext4_write.c +++ b/fs/ext4/ext4_write.c @@ -752,7 +752,7 @@ void ext4fs_deinit(void) * contigous sectors as ext4fs_read_file */ static int ext4fs_write_file(struct ext2_inode *file_inode, - int pos, unsigned int len, char *buf) + int pos, unsigned int len, const char *buf) { int i; int blockcnt; @@ -764,7 +764,7 @@ static int ext4fs_write_file(struct ext2_inode *file_inode, int delayed_start = 0; int delayed_extent = 0; int delayed_next = 0; - char *delayed_buf = NULL; + const char *delayed_buf = NULL; /* Adjust len so it we can't read past the end of the file. */ if (len > filesize) @@ -816,7 +816,6 @@ static int ext4fs_write_file(struct ext2_inode *file_inode, (uint32_t) delayed_extent); previous_block_number = -1; } - memset(buf, 0, fs->blksz - skipfirst); } buf += fs->blksz - skipfirst; } @@ -830,8 +829,8 @@ static int ext4fs_write_file(struct ext2_inode *file_inode, return len; } -int ext4fs_write(const char *fname, unsigned char *buffer, - unsigned long sizebytes) +int ext4fs_write(const char *fname, const char *buffer, + unsigned long sizebytes) { int ret = 0; struct ext2_inode *file_inode = NULL; @@ -949,7 +948,7 @@ int ext4fs_write(const char *fname, unsigned char *buffer, if (ext4fs_put_metadata(temp_ptr, itable_blkno)) goto fail; /* copy the file content into data blocks */ - if (ext4fs_write_file(file_inode, 0, sizebytes, (char *)buffer) == -1) { + if (ext4fs_write_file(file_inode, 0, sizebytes, buffer) == -1) { printf("Error in copying content\n"); /* FIXME: Deallocate data blocks */ goto fail; diff --git a/include/ext4fs.h b/include/ext4fs.h index 4b5de6e7b6..7d48b2bc46 100644 --- a/include/ext4fs.h +++ b/include/ext4fs.h @@ -134,7 +134,7 @@ extern int gindex; int ext4fs_init(void); void ext4fs_deinit(void); int ext4fs_filename_unlink(char *filename); -int ext4fs_write(const char *fname, unsigned char *buffer, +int ext4fs_write(const char *fname, const char *buffer, unsigned long sizebytes); int ext4_write_file(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actwrite); -- 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 --- env/ext4.c | 2 +- fs/ext4/ext4_common.c | 2 +- fs/ext4/ext4_write.c | 51 +++++++++++++++++++++++++++++++++++++++++++-------- include/ext4fs.h | 3 ++- 4 files changed, 47 insertions(+), 11 deletions(-) (limited to 'fs/ext4/ext4_common.c') diff --git a/env/ext4.c b/env/ext4.c index 09c5e4a491..388474a11c 100644 --- a/env/ext4.c +++ b/env/ext4.c @@ -60,7 +60,7 @@ static int env_ext4_save(void) } err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)&env_new, - sizeof(env_t)); + sizeof(env_t), FILETYPE_REG); ext4fs_close(); if (err == -1) { diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index e9123785f1..59ad6c8f8c 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)); diff --git a/fs/ext4/ext4_write.c b/fs/ext4/ext4_write.c index b5b7ee8133..504d23a895 100644 --- a/fs/ext4/ext4_write.c +++ b/fs/ext4/ext4_write.c @@ -465,6 +465,15 @@ static int ext4fs_delete_file(int inodeno) if (le32_to_cpu(inode.size) % fs->blksz) no_blocks++; + /* + * special case for symlinks whose target are small enough that + *it fits in struct ext2_inode.b.symlink: no block had been allocated + */ + if ((le16_to_cpu(inode.mode) & S_IFLNK) && + le32_to_cpu(inode.size) <= sizeof(inode.b.symlink)) { + no_blocks = 0; + } + if (le32_to_cpu(inode.flags) & EXT4_EXTENTS_FL) { /* FIXME delete extent index blocks, i.e. eh_depth >= 1 */ struct ext4_extent_header *eh = @@ -830,7 +839,7 @@ static int ext4fs_write_file(struct ext2_inode *file_inode, } int ext4fs_write(const char *fname, const char *buffer, - unsigned long sizebytes) + unsigned long sizebytes, int type) { int ret = 0; struct ext2_inode *file_inode = NULL; @@ -853,8 +862,12 @@ int ext4fs_write(const char *fname, const char *buffer, struct ext2_block_group *bgd = NULL; struct ext_filesystem *fs = get_fs(); ALLOC_CACHE_ALIGN_BUFFER(char, filename, 256); + bool store_link_in_inode = false; memset(filename, 0x00, 256); + if (type != FILETYPE_REG && type != FILETYPE_SYMLINK) + return -1; + g_parent_inode = zalloc(fs->inodesz); if (!g_parent_inode) goto fail; @@ -892,8 +905,16 @@ int ext4fs_write(const char *fname, const char *buffer, if (ret) goto fail; } - /* calucalate how many blocks required */ - bytes_reqd_for_file = sizebytes; + + /* calculate how many blocks required */ + if (type == FILETYPE_SYMLINK && + sizebytes <= sizeof(file_inode->b.symlink)) { + store_link_in_inode = true; + bytes_reqd_for_file = 0; + } else { + bytes_reqd_for_file = sizebytes; + } + blks_reqd_for_file = lldiv(bytes_reqd_for_file, fs->blksz); if (do_div(bytes_reqd_for_file, fs->blksz) != 0) { blks_reqd_for_file++; @@ -906,7 +927,7 @@ int ext4fs_write(const char *fname, const char *buffer, goto fail; } - inodeno = ext4fs_update_parent_dentry(filename, FILETYPE_REG); + inodeno = ext4fs_update_parent_dentry(filename, type); if (inodeno == -1) goto fail; /* prepare file inode */ @@ -914,14 +935,23 @@ int ext4fs_write(const char *fname, const char *buffer, if (!inode_buffer) goto fail; file_inode = (struct ext2_inode *)inode_buffer; - file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU | - S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH); + file_inode->size = cpu_to_le32(sizebytes); + if (type == FILETYPE_SYMLINK) { + file_inode->mode = cpu_to_le16(S_IFLNK | S_IRWXU | S_IRWXG | + S_IRWXO); + if (store_link_in_inode) { + strncpy(file_inode->b.symlink, buffer, sizebytes); + sizebytes = 0; + } + } else { + file_inode->mode = cpu_to_le16(S_IFREG | S_IRWXU | S_IRGRP | + S_IROTH | S_IXGRP | S_IXOTH); + } /* ToDo: Update correct time */ file_inode->mtime = cpu_to_le32(timestamp); file_inode->atime = cpu_to_le32(timestamp); file_inode->ctime = cpu_to_le32(timestamp); file_inode->nlinks = cpu_to_le16(1); - file_inode->size = cpu_to_le32(sizebytes); /* Allocate data blocks */ ext4fs_allocate_blocks(file_inode, blocks_remaining, @@ -1013,7 +1043,7 @@ int ext4_write_file(const char *filename, void *buf, loff_t offset, return -1; } - ret = ext4fs_write(filename, buf, len); + ret = ext4fs_write(filename, buf, len, FILETYPE_REG); if (ret) { printf("** Error ext4fs_write() **\n"); goto fail; @@ -1028,3 +1058,8 @@ fail: return -1; } + +int ext4fs_create_link(const char *target, const char *fname) +{ + return ext4fs_write(fname, target, strlen(target), FILETYPE_SYMLINK); +} diff --git a/include/ext4fs.h b/include/ext4fs.h index 7d48b2bc46..34585d407d 100644 --- a/include/ext4fs.h +++ b/include/ext4fs.h @@ -135,9 +135,10 @@ int ext4fs_init(void); void ext4fs_deinit(void); int ext4fs_filename_unlink(char *filename); int ext4fs_write(const char *fname, const char *buffer, - unsigned long sizebytes); + unsigned long sizebytes, int type); int ext4_write_file(const char *filename, void *buf, loff_t offset, loff_t len, loff_t *actwrite); +int ext4fs_create_link(const char *target, const char *fname); #endif struct ext_filesystem *get_fs(void); -- 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 59ad6c8f8c..feffbfa9a9 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 feffbfa9a9..464c33d0d7 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