mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 10:31:33 -04:00
f2fs: embed f2fs_gc_kthread in f2fs_sb_info
Instead of allocating f2fs_gc_kthread dynamically, embed it in f2fs_sb_info. This simplifies lifetime management and prepares for fixing race conditions during teardown. - __sbi_store - remount|shutdown - f2fs_stop_gc_thread - access sbi->gc_thread - sbi->gc_thread = NULL - access sbi->gc_thread->f2fs_gc_task Fixes:52190933c3("f2fs: sysfs: introduce critical_task_priority") Fixes:7950e9ac63("f2fs: stop gc/discard thread after fs shutdown") Cc: stable@kernel.org Signed-off-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
@@ -352,10 +352,6 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||
get_cache:
|
||||
si->cache_mem = 0;
|
||||
|
||||
/* build gc */
|
||||
if (sbi->gc_thread)
|
||||
si->cache_mem += sizeof(struct f2fs_gc_kthread);
|
||||
|
||||
/* build merge flush thread */
|
||||
if (SM_I(sbi)->fcc_info)
|
||||
si->cache_mem += sizeof(struct flush_cmd_control);
|
||||
|
||||
@@ -1748,6 +1748,33 @@ struct decompress_io_ctx {
|
||||
#define MAX_COMPRESS_LOG_SIZE 8
|
||||
#define MAX_COMPRESS_WINDOW_SIZE(log_size) ((PAGE_SIZE) << (log_size))
|
||||
|
||||
struct f2fs_gc_kthread {
|
||||
struct task_struct *f2fs_gc_task;
|
||||
wait_queue_head_t gc_wait_queue_head;
|
||||
|
||||
/* for gc sleep time */
|
||||
unsigned int urgent_sleep_time;
|
||||
unsigned int min_sleep_time;
|
||||
unsigned int max_sleep_time;
|
||||
unsigned int no_gc_sleep_time;
|
||||
|
||||
/* for changing gc mode */
|
||||
bool gc_wake;
|
||||
|
||||
/* for GC_MERGE mount option */
|
||||
wait_queue_head_t fggc_wq; /*
|
||||
* caller of f2fs_balance_fs()
|
||||
* will wait on this wait queue.
|
||||
*/
|
||||
|
||||
/* for gc control for zoned devices */
|
||||
unsigned int no_zoned_gc_percent;
|
||||
unsigned int boost_zoned_gc_percent;
|
||||
unsigned int valid_thresh_ratio;
|
||||
unsigned int boost_gc_multiple;
|
||||
unsigned int boost_gc_greedy;
|
||||
};
|
||||
|
||||
struct f2fs_sb_info {
|
||||
struct super_block *sb; /* pointer to VFS super block */
|
||||
struct proc_dir_entry *s_proc; /* proc entry */
|
||||
@@ -1883,7 +1910,7 @@ struct f2fs_sb_info {
|
||||
* semaphore for GC, avoid
|
||||
* race between GC and GC or CP
|
||||
*/
|
||||
struct f2fs_gc_kthread *gc_thread; /* GC thread */
|
||||
struct f2fs_gc_kthread gc_thread; /* GC thread */
|
||||
struct atgc_management am; /* atgc management */
|
||||
unsigned int cur_victim_sec; /* current victim section num */
|
||||
unsigned int gc_mode; /* current GC state */
|
||||
|
||||
36
fs/f2fs/gc.c
36
fs/f2fs/gc.c
@@ -31,9 +31,9 @@ static unsigned int count_bits(const unsigned long *addr,
|
||||
static int gc_thread_func(void *data)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = data;
|
||||
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
||||
wait_queue_head_t *fggc_wq = &sbi->gc_thread->fggc_wq;
|
||||
struct f2fs_gc_kthread *gc_th = &sbi->gc_thread;
|
||||
wait_queue_head_t *wq = &sbi->gc_thread.gc_wait_queue_head;
|
||||
wait_queue_head_t *fggc_wq = &sbi->gc_thread.fggc_wq;
|
||||
unsigned int wait_ms;
|
||||
struct f2fs_gc_control gc_control = {
|
||||
.victim_segno = NULL_SEGNO,
|
||||
@@ -193,13 +193,9 @@ static int gc_thread_func(void *data)
|
||||
|
||||
int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_gc_kthread *gc_th;
|
||||
struct f2fs_gc_kthread *gc_th = &sbi->gc_thread;
|
||||
dev_t dev = sbi->sb->s_bdev->bd_dev;
|
||||
|
||||
gc_th = f2fs_kmalloc(sbi, sizeof(struct f2fs_gc_kthread), GFP_KERNEL);
|
||||
if (!gc_th)
|
||||
return -ENOMEM;
|
||||
|
||||
gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME;
|
||||
gc_th->valid_thresh_ratio = DEF_GC_THREAD_VALID_THRESH_RATIO;
|
||||
gc_th->boost_gc_multiple = BOOST_GC_MULTIPLE;
|
||||
@@ -221,16 +217,14 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
|
||||
|
||||
gc_th->gc_wake = false;
|
||||
|
||||
sbi->gc_thread = gc_th;
|
||||
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
||||
init_waitqueue_head(&sbi->gc_thread->fggc_wq);
|
||||
sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
|
||||
init_waitqueue_head(&gc_th->gc_wait_queue_head);
|
||||
init_waitqueue_head(&gc_th->fggc_wq);
|
||||
gc_th->f2fs_gc_task = kthread_run(gc_thread_func, sbi,
|
||||
"f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev));
|
||||
if (IS_ERR(gc_th->f2fs_gc_task)) {
|
||||
int err = PTR_ERR(gc_th->f2fs_gc_task);
|
||||
|
||||
kfree(gc_th);
|
||||
sbi->gc_thread = NULL;
|
||||
gc_th->f2fs_gc_task = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -241,14 +235,14 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
|
||||
|
||||
void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
||||
struct f2fs_gc_kthread *gc_th = &sbi->gc_thread;
|
||||
|
||||
if (!gc_th)
|
||||
if (!gc_th->f2fs_gc_task)
|
||||
return;
|
||||
|
||||
kthread_stop(gc_th->f2fs_gc_task);
|
||||
gc_th->f2fs_gc_task = NULL;
|
||||
wake_up_all(&gc_th->fggc_wq);
|
||||
kfree(gc_th);
|
||||
sbi->gc_thread = NULL;
|
||||
}
|
||||
|
||||
static int select_gc_type(struct f2fs_sb_info *sbi, int gc_type)
|
||||
@@ -796,7 +790,7 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
|
||||
if (one_time) {
|
||||
p.one_time_gc = one_time;
|
||||
if (has_enough_free_secs(sbi, 0, NR_PERSISTENT_LOG))
|
||||
valid_thresh_ratio = sbi->gc_thread->valid_thresh_ratio;
|
||||
valid_thresh_ratio = sbi->gc_thread.valid_thresh_ratio;
|
||||
}
|
||||
|
||||
retry:
|
||||
@@ -1807,9 +1801,9 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
|
||||
if (f2fs_sb_has_blkzoned(sbi) &&
|
||||
!has_enough_free_blocks(sbi,
|
||||
sbi->gc_thread->boost_zoned_gc_percent))
|
||||
sbi->gc_thread.boost_zoned_gc_percent))
|
||||
window_granularity *=
|
||||
sbi->gc_thread->boost_gc_multiple;
|
||||
sbi->gc_thread.boost_gc_multiple;
|
||||
|
||||
end_segno = start_segno + window_granularity;
|
||||
}
|
||||
|
||||
27
fs/f2fs/gc.h
27
fs/f2fs/gc.h
@@ -45,32 +45,7 @@
|
||||
|
||||
#define NR_GC_CHECKPOINT_SECS (3) /* data/node/dentry sections */
|
||||
|
||||
struct f2fs_gc_kthread {
|
||||
struct task_struct *f2fs_gc_task;
|
||||
wait_queue_head_t gc_wait_queue_head;
|
||||
|
||||
/* for gc sleep time */
|
||||
unsigned int urgent_sleep_time;
|
||||
unsigned int min_sleep_time;
|
||||
unsigned int max_sleep_time;
|
||||
unsigned int no_gc_sleep_time;
|
||||
|
||||
/* for changing gc mode */
|
||||
bool gc_wake;
|
||||
|
||||
/* for GC_MERGE mount option */
|
||||
wait_queue_head_t fggc_wq; /*
|
||||
* caller of f2fs_balance_fs()
|
||||
* will wait on this wait queue.
|
||||
*/
|
||||
|
||||
/* for gc control for zoned devices */
|
||||
unsigned int no_zoned_gc_percent;
|
||||
unsigned int boost_zoned_gc_percent;
|
||||
unsigned int valid_thresh_ratio;
|
||||
unsigned int boost_gc_multiple;
|
||||
unsigned int boost_gc_greedy;
|
||||
};
|
||||
|
||||
struct gc_inode_list {
|
||||
struct list_head ilist;
|
||||
@@ -197,6 +172,6 @@ static inline bool need_to_boost_gc(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
return !has_enough_free_blocks(sbi,
|
||||
sbi->gc_thread->boost_zoned_gc_percent);
|
||||
sbi->gc_thread.boost_zoned_gc_percent);
|
||||
return has_enough_invalid_blocks(sbi);
|
||||
}
|
||||
|
||||
@@ -452,15 +452,14 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
||||
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) {
|
||||
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread.f2fs_gc_task) {
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait,
|
||||
prepare_to_wait(&sbi->gc_thread.fggc_wq, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
wake_up(&sbi->gc_thread->gc_wait_queue_head);
|
||||
wake_up(&sbi->gc_thread.gc_wait_queue_head);
|
||||
io_schedule();
|
||||
finish_wait(&sbi->gc_thread->fggc_wq, &wait);
|
||||
finish_wait(&sbi->gc_thread.fggc_wq, &wait);
|
||||
} else {
|
||||
struct f2fs_gc_control gc_control = {
|
||||
.victim_segno = NULL_SEGNO,
|
||||
|
||||
@@ -2943,11 +2943,11 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
|
||||
if ((flags & SB_RDONLY) ||
|
||||
(F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF &&
|
||||
!test_opt(sbi, GC_MERGE))) {
|
||||
if (sbi->gc_thread) {
|
||||
if (sbi->gc_thread.f2fs_gc_task) {
|
||||
f2fs_stop_gc_thread(sbi);
|
||||
need_restart_gc = true;
|
||||
}
|
||||
} else if (!sbi->gc_thread) {
|
||||
} else if (!sbi->gc_thread.f2fs_gc_task) {
|
||||
err = f2fs_start_gc_thread(sbi);
|
||||
if (err)
|
||||
goto restore_opts;
|
||||
|
||||
@@ -75,7 +75,7 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||
static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type)
|
||||
{
|
||||
if (struct_type == GC_THREAD)
|
||||
return (unsigned char *)sbi->gc_thread;
|
||||
return (unsigned char *)&sbi->gc_thread;
|
||||
else if (struct_type == SM_INFO)
|
||||
return (unsigned char *)SM_I(sbi);
|
||||
else if (struct_type == DCC_INFO)
|
||||
@@ -664,20 +664,20 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
|
||||
sbi->gc_mode = GC_NORMAL;
|
||||
} else if (t == 1) {
|
||||
sbi->gc_mode = GC_URGENT_HIGH;
|
||||
if (sbi->gc_thread) {
|
||||
sbi->gc_thread->gc_wake = true;
|
||||
if (sbi->gc_thread.f2fs_gc_task) {
|
||||
sbi->gc_thread.gc_wake = true;
|
||||
wake_up_interruptible_all(
|
||||
&sbi->gc_thread->gc_wait_queue_head);
|
||||
&sbi->gc_thread.gc_wait_queue_head);
|
||||
wake_up_discard_thread(sbi, true);
|
||||
}
|
||||
} else if (t == 2) {
|
||||
sbi->gc_mode = GC_URGENT_LOW;
|
||||
} else if (t == 3) {
|
||||
sbi->gc_mode = GC_URGENT_MID;
|
||||
if (sbi->gc_thread) {
|
||||
sbi->gc_thread->gc_wake = true;
|
||||
if (sbi->gc_thread.f2fs_gc_task) {
|
||||
sbi->gc_thread.gc_wake = true;
|
||||
wake_up_interruptible_all(
|
||||
&sbi->gc_thread->gc_wait_queue_head);
|
||||
&sbi->gc_thread.gc_wait_queue_head);
|
||||
}
|
||||
} else {
|
||||
return -EINVAL;
|
||||
@@ -934,14 +934,14 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
|
||||
if (!strcmp(a->attr.name, "gc_boost_gc_multiple")) {
|
||||
if (t < 1 || t > SEGS_PER_SEC(sbi))
|
||||
return -EINVAL;
|
||||
sbi->gc_thread->boost_gc_multiple = (unsigned int)t;
|
||||
sbi->gc_thread.boost_gc_multiple = (unsigned int)t;
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "gc_boost_gc_greedy")) {
|
||||
if (t > GC_GREEDY)
|
||||
return -EINVAL;
|
||||
sbi->gc_thread->boost_gc_greedy = (unsigned int)t;
|
||||
sbi->gc_thread.boost_gc_greedy = (unsigned int)t;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -989,8 +989,8 @@ static ssize_t __sbi_store(struct f2fs_attr *a,
|
||||
if (sbi->cprc_info.f2fs_issue_ckpt)
|
||||
set_user_nice(sbi->cprc_info.f2fs_issue_ckpt,
|
||||
PRIO_TO_NICE(sbi->critical_task_priority));
|
||||
if (sbi->gc_thread && sbi->gc_thread->f2fs_gc_task)
|
||||
set_user_nice(sbi->gc_thread->f2fs_gc_task,
|
||||
if (sbi->gc_thread.f2fs_gc_task)
|
||||
set_user_nice(sbi->gc_thread.f2fs_gc_task,
|
||||
PRIO_TO_NICE(sbi->critical_task_priority));
|
||||
return count;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user