]>
Commit | Line | Data |
---|---|---|
4d1e5b62 AF |
1 | From: Jan Kara <jack@suse.cz> |
2 | Subject: ocfs2: Fix deadlock on umount | |
3 | References: bnc#531716 | |
4 | Patch-mainline: 2.6.32 | |
5 | ||
6 | In patch patches.fixes/ocfs2-push-out-dropping-of-dentry-lock-to-ocfs2_wq.patch | |
7 | we moved the dentry lock put process into ocfs2_wq. This causes problems during | |
8 | umount because ocfs2_wq can drop references to inodes while they are being | |
9 | invalidated by invalidate_inodes() causing all sorts of nasty things | |
10 | (invalidate_inodes() ending in an infinite loop, "Busy inodes after umount" | |
11 | messages etc.). | |
12 | ||
13 | We fix the problem by stopping ocfs2_wq from doing any further releasing of | |
14 | inode references on the superblock being unmounted, wait until it finishes | |
15 | the current round of releasing and finally cleaning up all the references in | |
16 | dentry_lock_list from ocfs2_put_super(). | |
17 | ||
18 | The issue was tracked down by Tao Ma <tao.ma@oracle.com>. | |
19 | ||
20 | Signed-off-by: Jan Kara <jack@suse.cz> | |
21 | Signed-off-by: Joel Becker <joel.becker@oracle.com> | |
22 | ||
23 | diff -rupX /home/jack/.kerndiffexclude linux-2.6.27-SLE11_BRANCH/fs/ocfs2/dcache.c linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/dcache.c | |
24 | --- linux-2.6.27-SLE11_BRANCH/fs/ocfs2/dcache.c 2009-08-18 15:39:32.000000000 +0200 | |
25 | +++ linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/dcache.c 2009-08-18 15:44:51.000000000 +0200 | |
26 | @@ -295,22 +295,19 @@ out_attach: | |
27 | return ret; | |
28 | } | |
29 | ||
30 | -static DEFINE_SPINLOCK(dentry_list_lock); | |
31 | +DEFINE_SPINLOCK(dentry_list_lock); | |
32 | ||
33 | /* We limit the number of dentry locks to drop in one go. We have | |
34 | * this limit so that we don't starve other users of ocfs2_wq. */ | |
35 | #define DL_INODE_DROP_COUNT 64 | |
36 | ||
37 | /* Drop inode references from dentry locks */ | |
38 | -void ocfs2_drop_dl_inodes(struct work_struct *work) | |
39 | +static void __ocfs2_drop_dl_inodes(struct ocfs2_super *osb, int drop_count) | |
40 | { | |
41 | - struct ocfs2_super *osb = container_of(work, struct ocfs2_super, | |
42 | - dentry_lock_work); | |
43 | struct ocfs2_dentry_lock *dl; | |
44 | - int drop_count = DL_INODE_DROP_COUNT; | |
45 | ||
46 | spin_lock(&dentry_list_lock); | |
47 | - while (osb->dentry_lock_list && drop_count--) { | |
48 | + while (osb->dentry_lock_list && (drop_count < 0 || drop_count--)) { | |
49 | dl = osb->dentry_lock_list; | |
50 | osb->dentry_lock_list = dl->dl_next; | |
51 | spin_unlock(&dentry_list_lock); | |
52 | @@ -318,11 +315,32 @@ void ocfs2_drop_dl_inodes(struct work_st | |
53 | kfree(dl); | |
54 | spin_lock(&dentry_list_lock); | |
55 | } | |
56 | - if (osb->dentry_lock_list) | |
57 | + spin_unlock(&dentry_list_lock); | |
58 | +} | |
59 | + | |
60 | +void ocfs2_drop_dl_inodes(struct work_struct *work) | |
61 | +{ | |
62 | + struct ocfs2_super *osb = container_of(work, struct ocfs2_super, | |
63 | + dentry_lock_work); | |
64 | + | |
65 | + __ocfs2_drop_dl_inodes(osb, DL_INODE_DROP_COUNT); | |
66 | + /* | |
67 | + * Don't queue dropping if umount is in progress. We flush the | |
68 | + * list in ocfs2_dismount_volume | |
69 | + */ | |
70 | + spin_lock(&dentry_list_lock); | |
71 | + if (osb->dentry_lock_list && | |
72 | + !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED)) | |
73 | queue_work(ocfs2_wq, &osb->dentry_lock_work); | |
74 | spin_unlock(&dentry_list_lock); | |
75 | } | |
76 | ||
77 | +/* Flush the whole work queue */ | |
78 | +void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb) | |
79 | +{ | |
80 | + __ocfs2_drop_dl_inodes(osb, -1); | |
81 | +} | |
82 | + | |
83 | /* | |
84 | * ocfs2_dentry_iput() and friends. | |
85 | * | |
86 | @@ -353,7 +371,8 @@ static void ocfs2_drop_dentry_lock(struc | |
87 | /* We leave dropping of inode reference to ocfs2_wq as that can | |
88 | * possibly lead to inode deletion which gets tricky */ | |
89 | spin_lock(&dentry_list_lock); | |
90 | - if (!osb->dentry_lock_list) | |
91 | + if (!osb->dentry_lock_list && | |
92 | + !ocfs2_test_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED)) | |
93 | queue_work(ocfs2_wq, &osb->dentry_lock_work); | |
94 | dl->dl_next = osb->dentry_lock_list; | |
95 | osb->dentry_lock_list = dl; | |
96 | diff -rupX /home/jack/.kerndiffexclude linux-2.6.27-SLE11_BRANCH/fs/ocfs2/dcache.h linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/dcache.h | |
97 | --- linux-2.6.27-SLE11_BRANCH/fs/ocfs2/dcache.h 2009-08-18 15:39:32.000000000 +0200 | |
98 | +++ linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/dcache.h 2009-08-18 15:44:51.000000000 +0200 | |
99 | @@ -49,10 +49,13 @@ struct ocfs2_dentry_lock { | |
100 | int ocfs2_dentry_attach_lock(struct dentry *dentry, struct inode *inode, | |
101 | u64 parent_blkno); | |
102 | ||
103 | +extern spinlock_t dentry_list_lock; | |
104 | + | |
105 | void ocfs2_dentry_lock_put(struct ocfs2_super *osb, | |
106 | struct ocfs2_dentry_lock *dl); | |
107 | ||
108 | void ocfs2_drop_dl_inodes(struct work_struct *work); | |
109 | +void ocfs2_drop_all_dl_inodes(struct ocfs2_super *osb); | |
110 | ||
111 | struct dentry *ocfs2_find_local_alias(struct inode *inode, u64 parent_blkno, | |
112 | int skip_unhashed); | |
113 | diff -rupX /home/jack/.kerndiffexclude linux-2.6.27-SLE11_BRANCH/fs/ocfs2/ocfs2.h linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/ocfs2.h | |
114 | --- linux-2.6.27-SLE11_BRANCH/fs/ocfs2/ocfs2.h 2009-08-18 15:39:32.000000000 +0200 | |
115 | +++ linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/ocfs2.h 2009-08-18 15:44:51.000000000 +0200 | |
116 | @@ -201,10 +201,12 @@ enum ocfs2_mount_options | |
117 | OCFS2_MOUNT_GRPQUOTA = 1 << 10, /* We support group quotas */ | |
118 | }; | |
119 | ||
120 | -#define OCFS2_OSB_SOFT_RO 0x0001 | |
121 | -#define OCFS2_OSB_HARD_RO 0x0002 | |
122 | -#define OCFS2_OSB_ERROR_FS 0x0004 | |
123 | -#define OCFS2_DEFAULT_ATIME_QUANTUM 60 | |
124 | +#define OCFS2_OSB_SOFT_RO 0x0001 | |
125 | +#define OCFS2_OSB_HARD_RO 0x0002 | |
126 | +#define OCFS2_OSB_ERROR_FS 0x0004 | |
127 | +#define OCFS2_OSB_DROP_DENTRY_LOCK_IMMED 0x0008 | |
128 | + | |
129 | +#define OCFS2_DEFAULT_ATIME_QUANTUM 60 | |
130 | ||
131 | struct ocfs2_journal; | |
132 | struct ocfs2_slot_info; | |
133 | @@ -400,6 +402,18 @@ static inline void ocfs2_set_osb_flag(st | |
134 | spin_unlock(&osb->osb_lock); | |
135 | } | |
136 | ||
137 | + | |
138 | +static inline unsigned long ocfs2_test_osb_flag(struct ocfs2_super *osb, | |
139 | + unsigned long flag) | |
140 | +{ | |
141 | + unsigned long ret; | |
142 | + | |
143 | + spin_lock(&osb->osb_lock); | |
144 | + ret = osb->osb_flags & flag; | |
145 | + spin_unlock(&osb->osb_lock); | |
146 | + return ret; | |
147 | +} | |
148 | + | |
149 | static inline void ocfs2_set_ro_flag(struct ocfs2_super *osb, | |
150 | int hard) | |
151 | { | |
152 | diff -rupX /home/jack/.kerndiffexclude linux-2.6.27-SLE11_BRANCH/fs/ocfs2/super.c linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/super.c | |
153 | --- linux-2.6.27-SLE11_BRANCH/fs/ocfs2/super.c 2009-08-18 15:39:33.000000000 +0200 | |
154 | +++ linux-2.6.27-SLE11_BRANCH-1-dentry_lock_drop//fs/ocfs2/super.c 2009-08-18 15:46:56.000000000 +0200 | |
155 | @@ -1014,14 +1014,27 @@ static int ocfs2_get_sb(struct file_syst | |
156 | mnt); | |
157 | } | |
158 | ||
159 | +static void ocfs2_kill_sb(struct super_block *sb) | |
160 | +{ | |
161 | + struct ocfs2_super *osb = OCFS2_SB(sb); | |
162 | + | |
163 | + /* Prevent further queueing of inode drop events */ | |
164 | + spin_lock(&dentry_list_lock); | |
165 | + ocfs2_set_osb_flag(osb, OCFS2_OSB_DROP_DENTRY_LOCK_IMMED); | |
166 | + spin_unlock(&dentry_list_lock); | |
167 | + /* Wait for work to finish and/or remove it */ | |
168 | + cancel_work_sync(&osb->dentry_lock_work); | |
169 | + | |
170 | + kill_block_super(sb); | |
171 | +} | |
172 | + | |
173 | static struct file_system_type ocfs2_fs_type = { | |
174 | .owner = THIS_MODULE, | |
175 | .name = "ocfs2", | |
176 | .get_sb = ocfs2_get_sb, /* is this called when we mount | |
177 | * the fs? */ | |
178 | - .kill_sb = kill_block_super, /* set to the generic one | |
179 | - * right now, but do we | |
180 | - * need to change that? */ | |
181 | + .kill_sb = ocfs2_kill_sb, | |
182 | + | |
183 | .fs_flags = FS_REQUIRES_DEV|FS_RENAME_DOES_D_MOVE, | |
184 | .next = NULL | |
185 | }; | |
186 | @@ -1621,6 +1634,12 @@ static void ocfs2_dismount_volume(struct | |
187 | osb = OCFS2_SB(sb); | |
188 | BUG_ON(!osb); | |
189 | ||
190 | + /* | |
191 | + * Flush inode dropping work queue so that deletes are | |
192 | + * performed while the filesystem is still working | |
193 | + */ | |
194 | + ocfs2_drop_all_dl_inodes(osb); | |
195 | + | |
196 | ocfs2_disable_quotas(osb); | |
197 | ||
198 | ocfs2_shutdown_local_alloc(osb); |