aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorEric Sandeen <sandeen@redhat.com>2011-02-03 14:33:15 -0500
committerTheodore Ts'o <tytso@mit.edu>2011-02-03 14:33:15 -0500
commit8f1f745331c1b560f53c0d60e55a4f4f43f7cce5 (patch)
treed8db8e0e1a067012795372e38fce09dec6bc575a /fs/ext4
parent1bae4ce27c9c90344f23c65ea6966c50ffeae2f5 (diff)
ext4: fix panic on module unload when stopping lazyinit thread
https://bugzilla.kernel.org/show_bug.cgi?id=27652 If the lazyinit thread is running, the teardown function ext4_destroy_lazyinit_thread() has problems: ext4_clear_request_list(); while (ext4_li_info->li_task) { wake_up(&ext4_li_info->li_wait_daemon); wait_event(ext4_li_info->li_wait_task, ext4_li_info->li_task == NULL); } Clearing the request list will cause the thread to exit and free ext4_li_info, so then we're waiting on something which is getting freed. Fix this up by making the thread respond to kthread_stop, and exit, without the need to wait for that exit in some other homegrown way. Cc: stable@kernel.org Reported-and-Tested-by: Tao Ma <boyu.mt@taobao.com> Signed-off-by: Eric Sandeen <sandeen@redhat.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/super.c27
1 files changed, 14 insertions, 13 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 48ce561fafac..3d8cf2cab379 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -77,6 +77,7 @@ static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
77 const char *dev_name, void *data); 77 const char *dev_name, void *data);
78static void ext4_destroy_lazyinit_thread(void); 78static void ext4_destroy_lazyinit_thread(void);
79static void ext4_unregister_li_request(struct super_block *sb); 79static void ext4_unregister_li_request(struct super_block *sb);
80static void ext4_clear_request_list(void);
80 81
81#if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23) 82#if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
82static struct file_system_type ext3_fs_type = { 83static struct file_system_type ext3_fs_type = {
@@ -2716,6 +2717,8 @@ static void ext4_unregister_li_request(struct super_block *sb)
2716 mutex_unlock(&ext4_li_info->li_list_mtx); 2717 mutex_unlock(&ext4_li_info->li_list_mtx);
2717} 2718}
2718 2719
2720static struct task_struct *ext4_lazyinit_task;
2721
2719/* 2722/*
2720 * This is the function where ext4lazyinit thread lives. It walks 2723 * This is the function where ext4lazyinit thread lives. It walks
2721 * through the request list searching for next scheduled filesystem. 2724 * through the request list searching for next scheduled filesystem.
@@ -2784,6 +2787,10 @@ cont_thread:
2784 if (time_before(jiffies, next_wakeup)) 2787 if (time_before(jiffies, next_wakeup))
2785 schedule(); 2788 schedule();
2786 finish_wait(&eli->li_wait_daemon, &wait); 2789 finish_wait(&eli->li_wait_daemon, &wait);
2790 if (kthread_should_stop()) {
2791 ext4_clear_request_list();
2792 goto exit_thread;
2793 }
2787 } 2794 }
2788 2795
2789exit_thread: 2796exit_thread:
@@ -2808,6 +2815,7 @@ exit_thread:
2808 wake_up(&eli->li_wait_task); 2815 wake_up(&eli->li_wait_task);
2809 2816
2810 kfree(ext4_li_info); 2817 kfree(ext4_li_info);
2818 ext4_lazyinit_task = NULL;
2811 ext4_li_info = NULL; 2819 ext4_li_info = NULL;
2812 mutex_unlock(&ext4_li_mtx); 2820 mutex_unlock(&ext4_li_mtx);
2813 2821
@@ -2830,11 +2838,10 @@ static void ext4_clear_request_list(void)
2830 2838
2831static int ext4_run_lazyinit_thread(void) 2839static int ext4_run_lazyinit_thread(void)
2832{ 2840{
2833 struct task_struct *t; 2841 ext4_lazyinit_task = kthread_run(ext4_lazyinit_thread,
2834 2842 ext4_li_info, "ext4lazyinit");
2835 t = kthread_run(ext4_lazyinit_thread, ext4_li_info, "ext4lazyinit"); 2843 if (IS_ERR(ext4_lazyinit_task)) {
2836 if (IS_ERR(t)) { 2844 int err = PTR_ERR(ext4_lazyinit_task);
2837 int err = PTR_ERR(t);
2838 ext4_clear_request_list(); 2845 ext4_clear_request_list();
2839 del_timer_sync(&ext4_li_info->li_timer); 2846 del_timer_sync(&ext4_li_info->li_timer);
2840 kfree(ext4_li_info); 2847 kfree(ext4_li_info);
@@ -2985,16 +2992,10 @@ static void ext4_destroy_lazyinit_thread(void)
2985 * If thread exited earlier 2992 * If thread exited earlier
2986 * there's nothing to be done. 2993 * there's nothing to be done.
2987 */ 2994 */
2988 if (!ext4_li_info) 2995 if (!ext4_li_info || !ext4_lazyinit_task)
2989 return; 2996 return;
2990 2997
2991 ext4_clear_request_list(); 2998 kthread_stop(ext4_lazyinit_task);
2992
2993 while (ext4_li_info->li_task) {
2994 wake_up(&ext4_li_info->li_wait_daemon);
2995 wait_event(ext4_li_info->li_wait_task,
2996 ext4_li_info->li_task == NULL);
2997 }
2998} 2999}
2999 3000
3000static int ext4_fill_super(struct super_block *sb, void *data, int silent) 3001static int ext4_fill_super(struct super_block *sb, void *data, int silent)