aboutsummaryrefslogtreecommitdiff
path: root/fs/ext4
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/Makefile5
-rw-r--r--fs/ext4/crypto.c9
-rw-r--r--fs/ext4/dir.c2
-rw-r--r--fs/ext4/ext4.h25
-rw-r--r--fs/ext4/ext4_extents.h12
-rw-r--r--fs/ext4/ext4_jbd2.c3
-rw-r--r--fs/ext4/extents-test.c68
-rw-r--r--fs/ext4/extents.c251
-rw-r--r--fs/ext4/extents_status.c28
-rw-r--r--fs/ext4/fast_commit.c114
-rw-r--r--fs/ext4/file.c18
-rw-r--r--fs/ext4/fsync.c21
-rw-r--r--fs/ext4/ialloc.c16
-rw-r--r--fs/ext4/indirect.c2
-rw-r--r--fs/ext4/inline.c24
-rw-r--r--fs/ext4/inode.c417
-rw-r--r--fs/ext4/ioctl.c4
-rw-r--r--fs/ext4/mballoc-test.c89
-rw-r--r--fs/ext4/mballoc.c166
-rw-r--r--fs/ext4/mballoc.h30
-rw-r--r--fs/ext4/migrate.c2
-rw-r--r--fs/ext4/move_extent.c24
-rw-r--r--fs/ext4/namei.c58
-rw-r--r--fs/ext4/orphan.c16
-rw-r--r--fs/ext4/page-io.c49
-rw-r--r--fs/ext4/readpage.c11
-rw-r--r--fs/ext4/super.c79
-rw-r--r--fs/ext4/symlink.c2
-rw-r--r--fs/ext4/sysfs.c10
-rw-r--r--fs/ext4/xattr.c16
30 files changed, 1014 insertions, 557 deletions
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 72206a292676..3baee4e7c1cf 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -14,7 +14,8 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
-ext4-inode-test-objs += inode-test.o
-obj-$(CONFIG_EXT4_KUNIT_TESTS) += ext4-inode-test.o
+ext4-test-objs += inode-test.o mballoc-test.o \
+ extents-test.o
+obj-$(CONFIG_EXT4_KUNIT_TESTS) += ext4-test.o
ext4-$(CONFIG_FS_VERITY) += verity.o
ext4-$(CONFIG_FS_ENCRYPTION) += crypto.o
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index cf0a0970c095..f41f320f4437 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -163,10 +163,17 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
*/
if (handle) {
+ /*
+ * Since the inode is new it is ok to pass the
+ * XATTR_CREATE flag. This is necessary to match the
+ * remaining journal credits check in the set_handle
+ * function with the credits allocated for the new
+ * inode.
+ */
res = ext4_xattr_set_handle(handle, inode,
EXT4_XATTR_INDEX_ENCRYPTION,
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
- ctx, len, 0);
+ ctx, len, XATTR_CREATE);
if (!res) {
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
ext4_clear_inode_state(inode,
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index 28b2a3deb954..17edd678fa87 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -535,7 +535,7 @@ static int call_filldir(struct file *file, struct dir_context *ctx,
struct super_block *sb = inode->i_sb;
if (!fname) {
- ext4_msg(sb, KERN_ERR, "%s:%d: inode #%lu: comm %s: "
+ ext4_msg(sb, KERN_ERR, "%s:%d: inode #%llu: comm %s: "
"called with null fname?!?", __func__, __LINE__,
inode->i_ino, current->comm);
return 0;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 293f698b7042..94283a991e5c 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -28,7 +28,6 @@
#include <linux/seqlock.h>
#include <linux/mutex.h>
#include <linux/timer.h>
-#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/blockgroup_lock.h>
#include <linux/percpu_counter.h>
@@ -92,7 +91,7 @@
*/
#ifdef CONFIG_EXT4_DEBUG
#define ext_debug(ino, fmt, ...) \
- pr_debug("[%s/%d] EXT4-fs (%s): ino %lu: (%s, %d): %s:" fmt, \
+ pr_debug("[%s/%d] EXT4-fs (%s): ino %llu: (%s, %d): %s:" fmt, \
current->comm, task_pid_nr(current), \
ino->i_sb->s_id, ino->i_ino, __FILE__, __LINE__, \
__func__, ##__VA_ARGS__)
@@ -1082,9 +1081,6 @@ struct ext4_inode_info {
spinlock_t i_raw_lock; /* protects updates to the raw inode */
- /* Fast commit wait queue for this inode */
- wait_queue_head_t i_fc_wait;
-
/*
* Protect concurrent accesses on i_fc_lblk_start, i_fc_lblk_len
* and inode's EXT4_FC_STATE_COMMITTING state bit.
@@ -1121,6 +1117,7 @@ struct ext4_inode_info {
struct rw_semaphore i_data_sem;
struct inode vfs_inode;
struct jbd2_inode *jinode;
+ struct mapping_metadata_bhs i_metadata_bhs;
/*
* File creation time. Its function is same as that of
@@ -1570,6 +1567,7 @@ struct ext4_sb_info {
struct proc_dir_entry *s_proc;
struct kobject s_kobj;
struct completion s_kobj_unregister;
+ struct mutex s_error_notify_mutex; /* protects sysfs_notify vs kobject_del */
struct super_block *s_sb;
struct buffer_head *s_mmp_bh;
@@ -2974,7 +2972,8 @@ void __ext4_fc_track_unlink(handle_t *handle, struct inode *inode,
void __ext4_fc_track_link(handle_t *handle, struct inode *inode,
struct dentry *dentry);
void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry);
-void ext4_fc_track_link(handle_t *handle, struct dentry *dentry);
+void ext4_fc_track_link(handle_t *handle, struct inode *inode,
+ struct dentry *dentry);
void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
struct dentry *dentry);
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry);
@@ -3099,8 +3098,9 @@ extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks);
extern int ext4_chunk_trans_extent(struct inode *inode, int nrblocks);
extern int ext4_meta_trans_blocks(struct inode *inode, int lblocks,
int pextents);
-extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
- loff_t lstart, loff_t lend);
+extern int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end);
+extern int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart,
+ loff_t length, bool *did_zero);
extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf);
extern qsize_t *ext4_get_reserved_space(struct inode *inode);
extern int ext4_get_projid(struct inode *inode, kprojid_t *projid);
@@ -3229,7 +3229,7 @@ extern void __dump_mmp_msg(struct super_block *, struct mmp_struct *mmp,
extern __printf(7, 8)
void __ext4_grp_locked_error(const char *, unsigned int,
struct super_block *, ext4_group_t,
- unsigned long, ext4_fsblk_t,
+ u64, ext4_fsblk_t,
const char *, ...);
#define EXT4_ERROR_INODE(inode, fmt, a...) \
@@ -3719,7 +3719,7 @@ extern int ext4_handle_dirty_dirblock(handle_t *handle, struct inode *inode,
extern int __ext4_unlink(struct inode *dir, const struct qstr *d_name,
struct inode *inode, struct dentry *dentry);
extern int __ext4_link(struct inode *dir, struct inode *inode,
- struct dentry *dentry);
+ const struct qstr *d_name, struct dentry *dentry);
#define S_SHIFT 12
static const unsigned char ext4_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = {
@@ -3944,6 +3944,11 @@ static inline bool ext4_inode_can_atomic_write(struct inode *inode)
extern int ext4_block_write_begin(handle_t *handle, struct folio *folio,
loff_t pos, unsigned len,
get_block_t *get_block);
+
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+#define EXPORT_SYMBOL_FOR_EXT4_TEST(sym) \
+ EXPORT_SYMBOL_FOR_MODULES(sym, "ext4-test")
+#endif
#endif /* __KERNEL__ */
#endif /* _EXT4_H */
diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h
index c484125d963f..ebaf7cc42430 100644
--- a/fs/ext4/ext4_extents.h
+++ b/fs/ext4/ext4_extents.h
@@ -264,5 +264,17 @@ static inline void ext4_idx_store_pblock(struct ext4_extent_idx *ix,
0xffff);
}
+extern int __ext4_ext_dirty(const char *where, unsigned int line,
+ handle_t *handle, struct inode *inode,
+ struct ext4_ext_path *path);
+extern int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex);
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+extern int ext4_ext_space_root_idx_test(struct inode *inode, int check);
+extern struct ext4_ext_path *ext4_split_convert_extents_test(
+ handle_t *handle, struct inode *inode,
+ struct ext4_map_blocks *map,
+ struct ext4_ext_path *path,
+ int flags, unsigned int *allocated);
+#endif
#endif /* _EXT4_EXTENTS */
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 05e5946ed9b3..9a8c225f2753 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -390,7 +390,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
}
} else {
if (inode)
- mark_buffer_dirty_inode(bh, inode);
+ mmb_mark_buffer_dirty(bh,
+ &EXT4_I(inode)->i_metadata_bhs);
else
mark_buffer_dirty(bh);
if (inode && inode_needs_sync(inode)) {
diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
index 7c4690eb7dad..6b53a3f39fcd 100644
--- a/fs/ext4/extents-test.c
+++ b/fs/ext4/extents-test.c
@@ -142,8 +142,14 @@ static struct file_system_type ext_fs_type = {
static void extents_kunit_exit(struct kunit *test)
{
- struct ext4_sb_info *sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info;
+ struct ext4_sb_info *sbi;
+ if (!k_ctx.k_ei)
+ return;
+
+ sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info;
+ ext4_es_unregister_shrinker(sbi);
+ deactivate_super(sbi->s_sb);
kfree(sbi);
kfree(k_ctx.k_ei);
kfree(k_ctx.k_data);
@@ -222,34 +228,38 @@ static int extents_kunit_init(struct kunit *test)
(struct kunit_ext_test_param *)(test->param_value);
int err;
- sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
- if (IS_ERR(sb))
- return PTR_ERR(sb);
-
- sb->s_blocksize = 4096;
- sb->s_blocksize_bits = 12;
-
sbi = kzalloc_obj(struct ext4_sb_info);
if (sbi == NULL)
return -ENOMEM;
+ sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
+ if (IS_ERR(sb)) {
+ kfree(sbi);
+ return PTR_ERR(sb);
+ }
+
sbi->s_sb = sb;
sb->s_fs_info = sbi;
+ sb->s_blocksize = 4096;
+ sb->s_blocksize_bits = 12;
+
if (!param || !param->disable_zeroout)
sbi->s_extent_max_zeroout_kb = 32;
+ err = ext4_es_register_shrinker(sbi);
+ if (err)
+ goto out_deactivate;
+
/* setup the mock inode */
k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info);
- if (k_ctx.k_ei == NULL)
- return -ENOMEM;
+ if (k_ctx.k_ei == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
ei = k_ctx.k_ei;
inode = &ei->vfs_inode;
- err = ext4_es_register_shrinker(sbi);
- if (err)
- return err;
-
ext4_es_init_tree(&ei->i_es_tree);
rwlock_init(&ei->i_es_lock);
INIT_LIST_HEAD(&ei->i_es_list);
@@ -264,8 +274,10 @@ static int extents_kunit_init(struct kunit *test)
inode->i_sb = sb;
k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL);
- if (k_ctx.k_data == NULL)
- return -ENOMEM;
+ if (k_ctx.k_data == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
/*
* set the data area to a junk value
@@ -280,8 +292,8 @@ static int extents_kunit_init(struct kunit *test)
eh->eh_depth = 0;
eh->eh_entries = cpu_to_le16(1);
eh->eh_magic = EXT4_EXT_MAGIC;
- eh->eh_max =
- cpu_to_le16(ext4_ext_space_root_idx(&k_ctx.k_ei->vfs_inode, 0));
+ eh->eh_max = cpu_to_le16(ext4_ext_space_root_idx_test(
+ &k_ctx.k_ei->vfs_inode, 0));
eh->eh_generation = 0;
/*
@@ -307,7 +319,23 @@ static int extents_kunit_init(struct kunit *test)
kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub);
kunit_activate_static_stub(test, ext4_issue_zeroout,
ext4_issue_zeroout_stub);
+ up_write(&sb->s_umount);
+
return 0;
+
+out:
+ kfree(k_ctx.k_ei);
+ k_ctx.k_ei = NULL;
+
+ kfree(k_ctx.k_data);
+ k_ctx.k_data = NULL;
+
+ ext4_es_unregister_shrinker(sbi);
+out_deactivate:
+ deactivate_locked_super(sb);
+ kfree(sbi);
+
+ return err;
}
/*
@@ -384,8 +412,8 @@ static void test_split_convert(struct kunit *test)
switch (param->type) {
case TEST_SPLIT_CONVERT:
- path = ext4_split_convert_extents(NULL, inode, &map, path,
- param->split_flags, NULL);
+ path = ext4_split_convert_extents_test(NULL, inode, &map,
+ path, param->split_flags, NULL);
break;
case TEST_CREATE_BLOCKS:
ext4_map_create_blocks_helper(test, inode, &map, param->split_flags);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ae3804f36535..125f628e738a 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -184,9 +184,9 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
* - ENOMEM
* - EIO
*/
-static int __ext4_ext_dirty(const char *where, unsigned int line,
- handle_t *handle, struct inode *inode,
- struct ext4_ext_path *path)
+int __ext4_ext_dirty(const char *where, unsigned int line,
+ handle_t *handle, struct inode *inode,
+ struct ext4_ext_path *path)
{
int err;
@@ -1736,6 +1736,13 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
err = ext4_ext_get_access(handle, inode, path + k);
if (err)
return err;
+ if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) {
+ EXT4_ERROR_INODE(inode,
+ "path[%d].p_idx %p > EXT_LAST_INDEX %p",
+ k, path[k].p_idx,
+ EXT_LAST_INDEX(path[k].p_hdr));
+ return -EFSCORRUPTED;
+ }
path[k].p_idx->ei_block = border;
err = ext4_ext_dirty(handle, inode, path + k);
if (err)
@@ -1748,6 +1755,14 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
err = ext4_ext_get_access(handle, inode, path + k);
if (err)
goto clean;
+ if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) {
+ EXT4_ERROR_INODE(inode,
+ "path[%d].p_idx %p > EXT_LAST_INDEX %p",
+ k, path[k].p_idx,
+ EXT_LAST_INDEX(path[k].p_hdr));
+ err = -EFSCORRUPTED;
+ goto clean;
+ }
path[k].p_idx->ei_block = border;
err = ext4_ext_dirty(handle, inode, path + k);
if (err)
@@ -3144,7 +3159,7 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex)
}
/* FIXME!! we need to try to merge to left or right after zero-out */
-static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
+int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
{
ext4_fsblk_t ee_pblock;
unsigned int ee_len;
@@ -3239,6 +3254,9 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
insert_err = PTR_ERR(path);
err = 0;
+ if (insert_err != -ENOSPC && insert_err != -EDQUOT &&
+ insert_err != -ENOMEM)
+ goto out_path;
/*
* Get a new path to try to zeroout or fix the extent length.
@@ -3255,13 +3273,20 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
goto out_path;
}
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ if (!ex) {
+ EXT4_ERROR_INODE(inode,
+ "bad extent address lblock: %lu, depth: %d pblock %llu",
+ (unsigned long)ee_block, depth, path[depth].p_block);
+ err = -EFSCORRUPTED;
+ goto out;
+ }
+
err = ext4_ext_get_access(handle, inode, path + depth);
if (err)
goto out;
- depth = ext_depth(inode);
- ex = path[depth].p_ext;
-
fix_extent_len:
ex->ee_len = orig_ex.ee_len;
err = ext4_ext_dirty(handle, inode, path + path->p_depth);
@@ -3363,7 +3388,7 @@ static int ext4_split_extent_zeroout(handle_t *handle, struct inode *inode,
ext4_ext_mark_initialized(ex);
- ext4_ext_dirty(handle, inode, path + depth);
+ err = ext4_ext_dirty(handle, inode, path + depth);
if (err)
return err;
@@ -4457,9 +4482,13 @@ got_allocated_blocks:
path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
if (IS_ERR(path)) {
err = PTR_ERR(path);
- if (allocated_clusters) {
+ /*
+ * Gracefully handle out of space conditions. If the filesystem
+ * is inconsistent, we'll just leak allocated blocks to avoid
+ * causing even more damage.
+ */
+ if (allocated_clusters && (err == -EDQUOT || err == -ENOSPC)) {
int fb_flags = 0;
-
/*
* free data blocks we just allocated.
* not a good idea to call discard here directly,
@@ -4542,30 +4571,30 @@ retry_remove_space:
return err;
}
-static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
- ext4_lblk_t len, loff_t new_size,
- int flags)
+static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len,
+ loff_t new_size, int flags)
{
struct inode *inode = file_inode(file);
handle_t *handle;
int ret = 0, ret2 = 0, ret3 = 0;
int retries = 0;
int depth = 0;
+ ext4_lblk_t len_lblk;
struct ext4_map_blocks map;
unsigned int credits;
- loff_t epos, old_size = i_size_read(inode);
+ loff_t epos = 0, old_size = i_size_read(inode);
unsigned int blkbits = inode->i_blkbits;
bool alloc_zero = false;
BUG_ON(!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS));
- map.m_lblk = offset;
- map.m_len = len;
+ map.m_lblk = offset >> blkbits;
+ map.m_len = len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits);
/*
* Don't normalize the request if it can fit in one extent so
* that it doesn't get unnecessarily split into multiple
* extents.
*/
- if (len <= EXT_UNWRITTEN_MAX_LEN)
+ if (len_lblk <= EXT_UNWRITTEN_MAX_LEN)
flags |= EXT4_GET_BLOCKS_NO_NORMALIZE;
/*
@@ -4582,16 +4611,23 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
/*
* credits to insert 1 extent into extent tree
*/
- credits = ext4_chunk_trans_blocks(inode, len);
+ credits = ext4_chunk_trans_blocks(inode, len_lblk);
depth = ext_depth(inode);
+ /* Zero to the end of the block containing i_size */
+ if (new_size > old_size) {
+ ret = ext4_block_zero_eof(inode, old_size, LLONG_MAX);
+ if (ret)
+ return ret;
+ }
+
retry:
- while (len) {
+ while (len_lblk) {
/*
* Recalculate credits when extent tree depth changes.
*/
if (depth != ext_depth(inode)) {
- credits = ext4_chunk_trans_blocks(inode, len);
+ credits = ext4_chunk_trans_blocks(inode, len_lblk);
depth = ext_depth(inode);
}
@@ -4603,7 +4639,7 @@ retry:
}
ret = ext4_map_blocks(handle, inode, &map, flags);
if (ret <= 0) {
- ext4_debug("inode #%lu: block %u: len %u: "
+ ext4_debug("inode #%llu: block %u: len %u: "
"ext4_ext_map_blocks returned %d",
inode->i_ino, map.m_lblk,
map.m_len, ret);
@@ -4611,50 +4647,60 @@ retry:
ext4_journal_stop(handle);
break;
}
+ ext4_update_inode_fsync_trans(handle, inode, 1);
+ ret = ext4_journal_stop(handle);
+ if (unlikely(ret))
+ break;
+
/*
* allow a full retry cycle for any remaining allocations
*/
retries = 0;
- epos = EXT4_LBLK_TO_B(inode, map.m_lblk + ret);
- inode_set_ctime_current(inode);
- if (new_size) {
- if (epos > new_size)
- epos = new_size;
- if (ext4_update_inode_size(inode, epos) & 0x1)
- inode_set_mtime_to_ts(inode,
- inode_get_ctime(inode));
- if (epos > old_size) {
- pagecache_isize_extended(inode, old_size, epos);
- ext4_zero_partial_blocks(handle, inode,
- old_size, epos - old_size);
- }
- }
- ret2 = ext4_mark_inode_dirty(handle, inode);
- ext4_update_inode_fsync_trans(handle, inode, 1);
- ret3 = ext4_journal_stop(handle);
- ret2 = ret3 ? ret3 : ret2;
- if (unlikely(ret2))
- break;
if (alloc_zero &&
(map.m_flags & (EXT4_MAP_MAPPED | EXT4_MAP_UNWRITTEN))) {
- ret2 = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk,
- map.m_len);
- if (likely(!ret2))
- ret2 = ext4_convert_unwritten_extents(NULL,
+ ret = ext4_issue_zeroout(inode, map.m_lblk, map.m_pblk,
+ map.m_len);
+ if (likely(!ret))
+ ret = ext4_convert_unwritten_extents(NULL,
inode, (loff_t)map.m_lblk << blkbits,
(loff_t)map.m_len << blkbits);
- if (ret2)
+ if (ret)
break;
}
- map.m_lblk += ret;
- map.m_len = len = len - ret;
+ map.m_lblk += map.m_len;
+ map.m_len = len_lblk = len_lblk - map.m_len;
+ epos = EXT4_LBLK_TO_B(inode, map.m_lblk);
}
+
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
goto retry;
- return ret > 0 ? ret2 : ret;
+ if (!epos || !new_size)
+ return ret;
+
+ /*
+ * Allocate blocks, update the file size to match the size of the
+ * already successfully allocated blocks.
+ */
+ if (epos > new_size)
+ epos = new_size;
+
+ handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
+ if (IS_ERR(handle))
+ return ret ? ret : PTR_ERR(handle);
+
+ ext4_update_inode_size(inode, epos);
+ ret2 = ext4_mark_inode_dirty(handle, inode);
+ ext4_update_inode_fsync_trans(handle, inode, 1);
+ ret3 = ext4_journal_stop(handle);
+ ret2 = ret3 ? ret3 : ret2;
+
+ if (epos > old_size)
+ pagecache_isize_extended(inode, old_size, epos);
+
+ return ret ? ret : ret2;
}
static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len);
@@ -4666,12 +4712,11 @@ static long ext4_zero_range(struct file *file, loff_t offset,
{
struct inode *inode = file_inode(file);
handle_t *handle = NULL;
- loff_t new_size = 0;
+ loff_t align_start, align_end, new_size = 0;
loff_t end = offset + len;
- ext4_lblk_t start_lblk, end_lblk;
unsigned int blocksize = i_blocksize(inode);
- unsigned int blkbits = inode->i_blkbits;
- int ret, flags, credits;
+ bool partial_zeroed = false;
+ int ret, flags;
trace_ext4_zero_range(inode, offset, len, mode);
WARN_ON_ONCE(!inode_is_locked(inode));
@@ -4691,11 +4736,8 @@ static long ext4_zero_range(struct file *file, loff_t offset,
flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
/* Preallocate the range including the unaligned edges */
if (!IS_ALIGNED(offset | end, blocksize)) {
- ext4_lblk_t alloc_lblk = offset >> blkbits;
- ext4_lblk_t len_lblk = EXT4_MAX_BLOCKS(len, offset, blkbits);
-
- ret = ext4_alloc_file_blocks(file, alloc_lblk, len_lblk,
- new_size, flags);
+ ret = ext4_alloc_file_blocks(file, offset, len, new_size,
+ flags);
if (ret)
return ret;
}
@@ -4710,18 +4752,17 @@ static long ext4_zero_range(struct file *file, loff_t offset,
return ret;
/* Zero range excluding the unaligned edges */
- start_lblk = EXT4_B_TO_LBLK(inode, offset);
- end_lblk = end >> blkbits;
- if (end_lblk > start_lblk) {
- ext4_lblk_t zero_blks = end_lblk - start_lblk;
-
+ align_start = round_up(offset, blocksize);
+ align_end = round_down(end, blocksize);
+ if (align_end > align_start) {
if (mode & FALLOC_FL_WRITE_ZEROES)
flags = EXT4_GET_BLOCKS_CREATE_ZERO | EXT4_EX_NOCACHE;
else
flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
EXT4_EX_NOCACHE);
- ret = ext4_alloc_file_blocks(file, start_lblk, zero_blks,
- new_size, flags);
+ ret = ext4_alloc_file_blocks(file, align_start,
+ align_end - align_start, new_size,
+ flags);
if (ret)
return ret;
}
@@ -4729,25 +4770,24 @@ static long ext4_zero_range(struct file *file, loff_t offset,
if (IS_ALIGNED(offset | end, blocksize))
return ret;
- /*
- * In worst case we have to writeout two nonadjacent unwritten
- * blocks and update the inode
- */
- credits = (2 * ext4_ext_index_trans_blocks(inode, 2)) + 1;
- if (ext4_should_journal_data(inode))
- credits += 2;
- handle = ext4_journal_start(inode, EXT4_HT_MISC, credits);
+ /* Zero out partial block at the edges of the range */
+ ret = ext4_zero_partial_blocks(inode, offset, len, &partial_zeroed);
+ if (ret)
+ return ret;
+ if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && partial_zeroed) {
+ ret = filemap_write_and_wait_range(inode->i_mapping, offset,
+ end - 1);
+ if (ret)
+ return ret;
+ }
+
+ handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
if (IS_ERR(handle)) {
ret = PTR_ERR(handle);
ext4_std_error(inode->i_sb, ret);
return ret;
}
- /* Zero out partial block at the edges of the range */
- ret = ext4_zero_partial_blocks(handle, inode, offset, len);
- if (ret)
- goto out_handle;
-
if (new_size)
ext4_update_inode_size(inode, new_size);
ret = ext4_mark_inode_dirty(handle, inode);
@@ -4755,7 +4795,7 @@ static long ext4_zero_range(struct file *file, loff_t offset,
goto out_handle;
ext4_update_inode_fsync_trans(handle, inode, 1);
- if (file->f_flags & O_SYNC)
+ if ((file->f_flags & O_SYNC) || IS_SYNC(inode))
ext4_handle_sync(handle);
out_handle:
@@ -4769,15 +4809,11 @@ static long ext4_do_fallocate(struct file *file, loff_t offset,
struct inode *inode = file_inode(file);
loff_t end = offset + len;
loff_t new_size = 0;
- ext4_lblk_t start_lblk, len_lblk;
int ret;
trace_ext4_fallocate_enter(inode, offset, len, mode);
WARN_ON_ONCE(!inode_is_locked(inode));
- start_lblk = offset >> inode->i_blkbits;
- len_lblk = EXT4_MAX_BLOCKS(len, offset, inode->i_blkbits);
-
/* We only support preallocation for extent-based files only. */
if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
ret = -EOPNOTSUPP;
@@ -4792,17 +4828,19 @@ static long ext4_do_fallocate(struct file *file, loff_t offset,
goto out;
}
- ret = ext4_alloc_file_blocks(file, start_lblk, len_lblk, new_size,
+ ret = ext4_alloc_file_blocks(file, offset, len, new_size,
EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT);
if (ret)
goto out;
- if (file->f_flags & O_SYNC && EXT4_SB(inode->i_sb)->s_journal) {
+ if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) &&
+ EXT4_SB(inode->i_sb)->s_journal) {
ret = ext4_fc_commit(EXT4_SB(inode->i_sb)->s_journal,
EXT4_I(inode)->i_sync_tid);
}
out:
- trace_ext4_fallocate_exit(inode, offset, len_lblk, ret);
+ trace_ext4_fallocate_exit(inode, offset,
+ EXT4_MAX_BLOCKS(len, offset, inode->i_blkbits), ret);
return ret;
}
@@ -4955,7 +4993,7 @@ int ext4_convert_unwritten_extents_atomic(handle_t *handle, struct inode *inode,
ret = ext4_map_blocks(handle, inode, &map, flags);
if (ret != max_blocks)
ext4_msg(inode->i_sb, KERN_INFO,
- "inode #%lu: block %u: len %u: "
+ "inode #%llu: block %u: len %u: "
"split block mapping found for atomic write, "
"ret = %d",
inode->i_ino, map.m_lblk,
@@ -4974,7 +5012,7 @@ int ext4_convert_unwritten_extents_atomic(handle_t *handle, struct inode *inode,
if (ret <= 0 || ret2)
ext4_warning(inode->i_sb,
- "inode #%lu: block %u: len %u: "
+ "inode #%llu: block %u: len %u: "
"returned %d or %d",
inode->i_ino, map.m_lblk,
map.m_len, ret, ret2);
@@ -5031,7 +5069,7 @@ int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode,
EXT4_EX_NOCACHE);
if (ret <= 0)
ext4_warning(inode->i_sb,
- "inode #%lu: block %u: len %u: "
+ "inode #%llu: block %u: len %u: "
"ext4_ext_map_blocks returned %d",
inode->i_ino, map.m_lblk,
map.m_len, ret);
@@ -5569,7 +5607,7 @@ static int ext4_collapse_range(struct file *file, loff_t offset, loff_t len)
goto out_handle;
ext4_update_inode_fsync_trans(handle, inode, 1);
- if (IS_SYNC(inode))
+ if ((file->f_flags & O_SYNC) || IS_SYNC(inode))
ext4_handle_sync(handle);
out_handle:
@@ -5693,7 +5731,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len)
goto out_handle;
ext4_update_inode_fsync_trans(handle, inode, 1);
- if (IS_SYNC(inode))
+ if ((file->f_flags & O_SYNC) || IS_SYNC(inode))
ext4_handle_sync(handle);
out_handle:
@@ -6238,6 +6276,33 @@ out:
return 0;
}
-#ifdef CONFIG_EXT4_KUNIT_TESTS
-#include "extents-test.c"
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+int ext4_ext_space_root_idx_test(struct inode *inode, int check)
+{
+ return ext4_ext_space_root_idx(inode, check);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_space_root_idx_test);
+
+struct ext4_ext_path *ext4_split_convert_extents_test(handle_t *handle,
+ struct inode *inode, struct ext4_map_blocks *map,
+ struct ext4_ext_path *path, int flags,
+ unsigned int *allocated)
+{
+ return ext4_split_convert_extents(handle, inode, map, path,
+ flags, allocated);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_split_convert_extents_test);
+
+EXPORT_SYMBOL_FOR_EXT4_TEST(__ext4_ext_dirty);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_zeroout);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_register_shrinker);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_unregister_shrinker);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_create_blocks);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_init_tree);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_lookup_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_insert_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_insert_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_find_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_issue_zeroout);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_query_blocks);
#endif
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index a1538bac51c6..6e4a191e8219 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -214,7 +214,7 @@ static void ext4_es_print_tree(struct inode *inode)
struct ext4_es_tree *tree;
struct rb_node *node;
- printk(KERN_DEBUG "status extents for inode %lu:", inode->i_ino);
+ printk(KERN_DEBUG "status extents for inode %llu:", inode->i_ino);
tree = &EXT4_I(inode)->i_es_tree;
node = rb_first(&tree->root);
while (node) {
@@ -703,7 +703,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode,
if (!ext4_es_is_written(es) && !ext4_es_is_unwritten(es)) {
if (in_range(es->es_lblk, ee_block, ee_len)) {
pr_warn("ES insert assertion failed for "