summaryrefslogtreecommitdiff
path: root/fs/f2fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/f2fs')
-rw-r--r--fs/f2fs/checkpoint.c10
-rw-r--r--fs/f2fs/compress.c4
-rw-r--r--fs/f2fs/data.c33
-rw-r--r--fs/f2fs/f2fs.h30
-rw-r--r--fs/f2fs/gc.c50
-rw-r--r--fs/f2fs/inode.c17
-rw-r--r--fs/f2fs/node.c21
-rw-r--r--fs/f2fs/segment.c14
-rw-r--r--fs/f2fs/super.c4
-rw-r--r--fs/f2fs/xattr.c6
10 files changed, 147 insertions, 42 deletions
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index c27509d4b6ef..66d3714dda32 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -1330,7 +1330,7 @@ void f2fs_wait_on_all_pages(struct f2fs_sb_info *sbi, int type)
f2fs_submit_merged_write(sbi, DATA);
prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE);
- io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
}
finish_wait(&sbi->cp_wait, &wait);
}
@@ -1685,7 +1685,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
goto out;
}
- trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops");
+ trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_START_BLOCK_OPS);
err = block_operations(sbi);
if (err)
@@ -1693,7 +1693,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
stat_cp_time(cpc, CP_TIME_OP_LOCK);
- trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");
+ trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_FINISH_BLOCK_OPS);
f2fs_flush_merged_writes(sbi);
@@ -1759,7 +1759,7 @@ stop:
/* update CP_TIME to trigger checkpoint periodically */
f2fs_update_time(sbi, CP_TIME);
- trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint");
+ trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_FINISH_CHECKPOINT);
out:
if (cpc->reason != CP_RESIZE)
f2fs_up_write(&sbi->cp_global_sem);
@@ -1986,7 +1986,7 @@ void f2fs_flush_ckpt_thread(struct f2fs_sb_info *sbi)
/* Let's wait for the previous dispatched checkpoint. */
while (atomic_read(&cprc->queued_ckpt))
- io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
}
void f2fs_init_ckpt_req_control(struct f2fs_sb_info *sbi)
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
index ae36bb1f9351..0399db98fccf 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1057,7 +1057,7 @@ static void cancel_cluster_writeback(struct compress_ctx *cc,
f2fs_submit_merged_write(F2FS_I_SB(cc->inode), DATA);
while (atomic_read(&cic->pending_pages) !=
(cc->valid_nr_cpages - submitted + 1))
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
}
/* Cancel writeback and stay locked. */
@@ -1582,7 +1582,7 @@ continue_unlock:
*/
if (IS_NOQUOTA(cc->inode))
goto out;
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
goto retry_write;
}
goto out;
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 89105b22a024..872169b91913 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -898,6 +898,35 @@ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
}
}
+void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi)
+{
+ struct bio_entry *be, *tmp;
+ struct f2fs_bio_info *io;
+ enum temp_type temp;
+
+ for (temp = HOT; temp < NR_TEMP_TYPE; temp++) {
+ LIST_HEAD(list);
+
+ io = sbi->write_io[DATA] + temp;
+
+ /* A lockless list_empty() check is safe here: any bios from
+ * other kworkers that we miss will be submitted by those
+ * kworkers accordingly.
+ */
+ if (list_empty(&io->bio_list))
+ continue;
+
+ f2fs_down_write(&io->bio_list_lock);
+ list_splice_init(&io->bio_list, &list);
+ f2fs_up_write(&io->bio_list_lock);
+
+ list_for_each_entry_safe(be, tmp, &list, list) {
+ f2fs_submit_write_bio(sbi, be->bio, DATA);
+ del_bio_entry(be);
+ }
+ }
+}
+
int f2fs_merge_page_bio(struct f2fs_io_info *fio)
{
struct bio *bio = *fio->bio;
@@ -3185,8 +3214,8 @@ result:
} else if (ret == -EAGAIN) {
ret = 0;
if (wbc->sync_mode == WB_SYNC_ALL) {
- f2fs_io_schedule_timeout(
- DEFAULT_IO_TIMEOUT);
+ f2fs_schedule_timeout(
+ DEFAULT_SCHEDULE_TIMEOUT);
goto retry_write;
}
goto next;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a77858bd3877..f1d7a22293b3 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -319,6 +319,12 @@ struct cp_control {
struct cp_stats stats;
};
+enum f2fs_cp_phase {
+ CP_PHASE_START_BLOCK_OPS,
+ CP_PHASE_FINISH_BLOCK_OPS,
+ CP_PHASE_FINISH_CHECKPOINT,
+};
+
/*
* indicate meta/data type
*/
@@ -665,8 +671,8 @@ enum {
#define DEFAULT_RETRY_IO_COUNT 8 /* maximum retry read IO or flush count */
-/* congestion wait timeout value, default: 20ms */
-#define DEFAULT_IO_TIMEOUT (msecs_to_jiffies(20))
+/* IO/non-IO congestion wait timeout value, default: 20ms */
+#define DEFAULT_SCHEDULE_TIMEOUT (msecs_to_jiffies(20))
/* timeout value injected, default: 1000ms */
#define DEFAULT_FAULT_TIMEOUT (msecs_to_jiffies(1000))
@@ -1527,6 +1533,7 @@ enum node_type {
NODE_TYPE_INODE,
NODE_TYPE_XATTR,
NODE_TYPE_NON_INODE,
+ NODE_TYPE_NON_IXNODE, /* non inode and xnode */
};
@@ -4084,6 +4091,7 @@ void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi,
nid_t ino, enum page_type type);
void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
struct bio **bio, struct folio *folio);
+void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi);
void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi);
int f2fs_submit_page_bio(struct f2fs_io_info *fio);
int f2fs_merge_page_bio(struct f2fs_io_info *fio);
@@ -4939,22 +4947,30 @@ static inline bool f2fs_block_unit_discard(struct f2fs_sb_info *sbi)
return F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_BLOCK;
}
-static inline void f2fs_io_schedule_timeout(long timeout)
+static inline void __f2fs_schedule_timeout(long timeout, bool io)
{
set_current_state(TASK_UNINTERRUPTIBLE);
- io_schedule_timeout(timeout);
+ if (io)
+ io_schedule_timeout(timeout);
+ else
+ schedule_timeout(timeout);
}
+#define f2fs_io_schedule_timeout(timeout) \
+ __f2fs_schedule_timeout(timeout, true)
+#define f2fs_schedule_timeout(timeout) \
+ __f2fs_schedule_timeout(timeout, false)
+
static inline void f2fs_io_schedule_timeout_killable(long timeout)
{
while (timeout) {
if (fatal_signal_pending(current))
return;
set_current_state(TASK_UNINTERRUPTIBLE);
- io_schedule_timeout(DEFAULT_IO_TIMEOUT);
- if (timeout <= DEFAULT_IO_TIMEOUT)
+ io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
+ if (timeout <= DEFAULT_SCHEDULE_TIMEOUT)
return;
- timeout -= DEFAULT_IO_TIMEOUT;
+ timeout -= DEFAULT_SCHEDULE_TIMEOUT;
}
}
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 3d9f326ae840..3d41c6348b3c 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -1208,8 +1208,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static int ra_data_block(struct inode *inode, pgoff_t index)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct dnode_of_data dn;
struct folio *folio, *efolio;
struct f2fs_io_info fio = {
@@ -1224,9 +1224,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
};
int err = 0;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
folio = f2fs_grab_cache_folio(mapping, index, true);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
+ goto out_iput;
+ }
if (f2fs_lookup_read_extent_cache_block(inode, index,
&dn.data_blkaddr)) {
@@ -1287,11 +1300,16 @@ got_it:
f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE);
f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE);
+ if (atomic_inode)
+ iput(atomic_inode);
return 0;
put_encrypted_page:
f2fs_put_page(fio.encrypted_page, 1);
put_folio:
f2fs_folio_put(folio, true);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
@@ -1302,8 +1320,8 @@ put_folio:
static int move_data_block(struct inode *inode, block_t bidx,
int gc_type, unsigned int segno, int off)
{
- struct address_space *mapping = f2fs_is_cow_file(inode) ?
- F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
+ struct address_space *mapping = inode->i_mapping;
+ struct inode *atomic_inode = NULL;
struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(inode),
.ino = inode->i_ino,
@@ -1325,10 +1343,23 @@ static int move_data_block(struct inode *inode, block_t bidx,
(fio.sbi->gc_mode != GC_URGENT_HIGH) ?
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
+ f2fs_down_read(&F2FS_I(inode)->i_sem);
+ if (f2fs_is_cow_file(inode)) {
+ atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
+ if (!atomic_inode) {
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+ return -EBUSY;
+ }
+ mapping = atomic_inode->i_mapping;
+ }
+ f2fs_up_read(&F2FS_I(inode)->i_sem);
+
/* do not read out */
folio = f2fs_grab_cache_folio(mapping, bidx, false);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
+ if (IS_ERR(folio)) {
+ err = PTR_ERR(folio);
+ goto out_iput;
+ }
if (!check_valid_map(F2FS_I_SB(inode), segno, off)) {
err = -ENOENT;
@@ -1457,6 +1488,9 @@ put_out:
f2fs_put_dnode(&dn);
out:
f2fs_folio_put(folio, true);
+out_iput:
+ if (atomic_inode)
+ iput(atomic_inode);
return err;
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 171cd4172025..90cc20232f76 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -318,9 +318,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio)
}
if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
- f2fs_has_inline_xattr(inode) &&
- (fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE ||
- fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
+ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
+ (f2fs_has_inline_xattr(inode) &&
+ fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu",
__func__, inode->i_ino, fi->i_inline_xattr_size,
MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
@@ -853,10 +853,15 @@ void f2fs_evict_inode(struct inode *inode)
f2fs_abort_atomic_write(inode, true);
if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
- clear_inode_flag(fi->cow_inode, FI_COW_FILE);
- F2FS_I(fi->cow_inode)->atomic_inode = NULL;
- iput(fi->cow_inode);
+ struct inode *cow_inode = fi->cow_inode;
+
+ f2fs_down_write(&F2FS_I(cow_inode)->i_sem);
+ clear_inode_flag(cow_inode, FI_COW_FILE);
+ F2FS_I(cow_inode)->atomic_inode = NULL;
fi->cow_inode = NULL;
+ f2fs_up_write(&F2FS_I(cow_inode)->i_sem);
+
+ iput(cow_inode);
}
trace_f2fs_evict_inode(inode);
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index feb152c21d2e..391802155331 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1504,20 +1504,33 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
struct folio *folio, pgoff_t nid,
enum node_type ntype, bool in_irq)
{
+ bool is_inode, is_xnode;
+
if (unlikely(nid != nid_of_node(folio)))
goto out_err;
+ is_inode = IS_INODE(folio);
+ is_xnode = f2fs_has_xattr_block(ofs_of_node(folio));
+
switch (ntype) {
+ case NODE_TYPE_REGULAR:
+ if (is_inode && is_xnode)
+ goto out_err;
+ break;
case NODE_TYPE_INODE:
- if (!IS_INODE(folio))
+ if (!is_inode || is_xnode)
goto out_err;
break;
case NODE_TYPE_XATTR:
- if (!f2fs_has_xattr_block(ofs_of_node(folio)))
+ if (is_inode || !is_xnode)
goto out_err;
break;
case NODE_TYPE_NON_INODE:
- if (IS_INODE(folio))
+ if (is_inode)
+ goto out_err;
+ break;
+ case NODE_TYPE_NON_IXNODE:
+ if (is_inode || is_xnode)
goto out_err;
break;
default:
@@ -1613,7 +1626,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start)
struct f2fs_sb_info *sbi = F2FS_F_SB(parent);
nid_t nid = get_nid(parent, start, false);
- return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR);
+ return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE);
}
static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index a7cf8627d888..5e2789cd96b5 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -234,7 +234,7 @@ retry:
err = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE);
if (err) {
if (err == -ENOMEM) {
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ memalloc_retry_wait(GFP_NOFS);
goto retry;
}
return err;
@@ -443,6 +443,13 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
if (has_enough_free_secs(sbi, 0, 0))
return;
+ /*
+ * Submit all cached OPU/IPU DATA bios before triggering
+ * foreground GC to avoid potential deadlocks.
+ */
+ f2fs_submit_merged_write(sbi, DATA);
+ f2fs_submit_all_merged_ipu_writes(sbi);
+
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
sbi->gc_thread->f2fs_gc_task) {
DEFINE_WAIT(wait);
@@ -461,6 +468,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
.should_migrate_blocks = false,
.err_gc_skipped = false,
.nr_free_secs = 1 };
+
f2fs_down_write(&sbi->gc_lock);
stat_inc_gc_call_count(sbi, FOREGROUND);
f2fs_gc(sbi, &gc_control);
@@ -750,7 +758,7 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi)
do {
ret = __submit_flush_wait(sbi, FDEV(i).bdev);
if (ret)
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
} while (ret && --count);
if (ret) {
@@ -3472,7 +3480,7 @@ next:
blk_finish_plug(&plug);
mutex_unlock(&dcc->cmd_lock);
trimmed += __wait_all_discard_cmd(sbi, NULL);
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ f2fs_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
goto next;
}
skip:
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 9154f702e170..f6b75ce11d1c 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -2656,7 +2656,7 @@ static int f2fs_enable_checkpoint(struct f2fs_sb_info *sbi)
/* we should flush all the data to keep data consistency */
while (get_pages(sbi, F2FS_DIRTY_DATA)) {
writeback_inodes_sb_nr(sbi->sb, nr_pages, WB_REASON_SYNC);
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ f2fs_io_schedule_timeout(DEFAULT_SCHEDULE_TIMEOUT);
if (f2fs_time_over(sbi, ENABLE_TIME))
break;
@@ -3128,7 +3128,7 @@ retry:
&folio, &fsdata);
if (unlikely(err)) {
if (err == -ENOMEM) {
- f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
+ memalloc_retry_wait(GFP_NOFS);
goto retry;
}
set_sbi_flag(F2FS_SB(sb), SBI_QUOTA_NEED_REPAIR);
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index b4e5c406632f..8cb0de2a5bcf 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -581,8 +581,6 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
size_t prefix_len;
size_t size;
- prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
-
if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
f2fs_err(F2FS_I_SB(inode), "list inode (%lu) has corrupted xattr",
@@ -590,9 +588,11 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
f2fs_handle_error(F2FS_I_SB(inode),
ERROR_CORRUPTED_XATTR);
- break;
+ error = -EFSCORRUPTED;
+ goto cleanup;
}
+ prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
if (!prefix)
continue;