diff options
author | Eric Sandeen <sandeen@redhat.com> | 2011-02-03 14:33:15 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2011-02-03 14:33:15 -0500 |
commit | 8f1f745331c1b560f53c0d60e55a4f4f43f7cce5 (patch) | |
tree | d8db8e0e1a067012795372e38fce09dec6bc575a /fs/ext4 | |
parent | 1bae4ce27c9c90344f23c65ea6966c50ffeae2f5 (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.c | 27 |
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); |
78 | static void ext4_destroy_lazyinit_thread(void); | 78 | static void ext4_destroy_lazyinit_thread(void); |
79 | static void ext4_unregister_li_request(struct super_block *sb); | 79 | static void ext4_unregister_li_request(struct super_block *sb); |
80 | static 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) |
82 | static struct file_system_type ext3_fs_type = { | 83 | static 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 | ||
2720 | static 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 | ||
2789 | exit_thread: | 2796 | exit_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 | ||
2831 | static int ext4_run_lazyinit_thread(void) | 2839 | static 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 | ||
3000 | static int ext4_fill_super(struct super_block *sb, void *data, int silent) | 3001 | static int ext4_fill_super(struct super_block *sb, void *data, int silent) |