diff options
author | Thomas Betker <thomas.betker@rohde-schwarz.com> | 2015-11-10 16:18:15 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2016-02-25 06:11:25 -0500 |
commit | 157078f64b8a9cd7011b6b900b2f2498df850748 (patch) | |
tree | 7bfe4c748232f41764dcaa80c1810abacd70bef4 /fs/jffs2/file.c | |
parent | 38714fbd299f992080a11b977f609136ec8530c2 (diff) |
Revert "jffs2: Fix lock acquisition order bug in jffs2_write_begin"
This reverts commit 5ffd3412ae55
("jffs2: Fix lock acquisition order bug in jffs2_write_begin").
The commit modified jffs2_write_begin() to remove a deadlock with
jffs2_garbage_collect_live(), but this introduced new deadlocks found
by multiple users. page_lock() actually has to be called before
mutex_lock(&c->alloc_sem) or mutex_lock(&f->sem) because
jffs2_write_end() and jffs2_readpage() are called with the page locked,
and they acquire c->alloc_sem and f->sem, resp.
In other words, the lock order in jffs2_write_begin() was correct, and
it is the jffs2_garbage_collect_live() path that has to be changed.
Revert the commit to get rid of the new deadlocks, and to clear the way
for a better fix of the original deadlock.
Reported-by: Deng Chao <deng.chao1@zte.com.cn>
Reported-by: Ming Liu <liu.ming50@gmail.com>
Reported-by: wangzaiwei <wangzaiwei@top-vision.cn>
Signed-off-by: Thomas Betker <thomas.betker@rohde-schwarz.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/jffs2/file.c')
-rw-r--r-- | fs/jffs2/file.c | 39 |
1 files changed, 18 insertions, 21 deletions
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index c5ac5944bc1b..cad86bac3453 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c | |||
@@ -137,39 +137,33 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, | |||
137 | struct page *pg; | 137 | struct page *pg; |
138 | struct inode *inode = mapping->host; | 138 | struct inode *inode = mapping->host; |
139 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); | 139 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); |
140 | struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); | ||
141 | struct jffs2_raw_inode ri; | ||
142 | uint32_t alloc_len = 0; | ||
143 | pgoff_t index = pos >> PAGE_CACHE_SHIFT; | 140 | pgoff_t index = pos >> PAGE_CACHE_SHIFT; |
144 | uint32_t pageofs = index << PAGE_CACHE_SHIFT; | 141 | uint32_t pageofs = index << PAGE_CACHE_SHIFT; |
145 | int ret = 0; | 142 | int ret = 0; |
146 | 143 | ||
147 | jffs2_dbg(1, "%s()\n", __func__); | ||
148 | |||
149 | if (pageofs > inode->i_size) { | ||
150 | ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, | ||
151 | ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); | ||
152 | if (ret) | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | mutex_lock(&f->sem); | ||
157 | pg = grab_cache_page_write_begin(mapping, index, flags); | 144 | pg = grab_cache_page_write_begin(mapping, index, flags); |
158 | if (!pg) { | 145 | if (!pg) |
159 | if (alloc_len) | ||
160 | jffs2_complete_reservation(c); | ||
161 | mutex_unlock(&f->sem); | ||
162 | return -ENOMEM; | 146 | return -ENOMEM; |
163 | } | ||
164 | *pagep = pg; | 147 | *pagep = pg; |
165 | 148 | ||
166 | if (alloc_len) { | 149 | jffs2_dbg(1, "%s()\n", __func__); |
150 | |||
151 | if (pageofs > inode->i_size) { | ||
167 | /* Make new hole frag from old EOF to new page */ | 152 | /* Make new hole frag from old EOF to new page */ |
153 | struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); | ||
154 | struct jffs2_raw_inode ri; | ||
168 | struct jffs2_full_dnode *fn; | 155 | struct jffs2_full_dnode *fn; |
156 | uint32_t alloc_len; | ||
169 | 157 | ||
170 | jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", | 158 | jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", |
171 | (unsigned int)inode->i_size, pageofs); | 159 | (unsigned int)inode->i_size, pageofs); |
172 | 160 | ||
161 | ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, | ||
162 | ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); | ||
163 | if (ret) | ||
164 | goto out_page; | ||
165 | |||
166 | mutex_lock(&f->sem); | ||
173 | memset(&ri, 0, sizeof(ri)); | 167 | memset(&ri, 0, sizeof(ri)); |
174 | 168 | ||
175 | ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); | 169 | ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); |
@@ -196,6 +190,7 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, | |||
196 | if (IS_ERR(fn)) { | 190 | if (IS_ERR(fn)) { |
197 | ret = PTR_ERR(fn); | 191 | ret = PTR_ERR(fn); |
198 | jffs2_complete_reservation(c); | 192 | jffs2_complete_reservation(c); |
193 | mutex_unlock(&f->sem); | ||
199 | goto out_page; | 194 | goto out_page; |
200 | } | 195 | } |
201 | ret = jffs2_add_full_dnode_to_inode(c, f, fn); | 196 | ret = jffs2_add_full_dnode_to_inode(c, f, fn); |
@@ -210,10 +205,12 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, | |||
210 | jffs2_mark_node_obsolete(c, fn->raw); | 205 | jffs2_mark_node_obsolete(c, fn->raw); |
211 | jffs2_free_full_dnode(fn); | 206 | jffs2_free_full_dnode(fn); |
212 | jffs2_complete_reservation(c); | 207 | jffs2_complete_reservation(c); |
208 | mutex_unlock(&f->sem); | ||
213 | goto out_page; | 209 | goto out_page; |
214 | } | 210 | } |
215 | jffs2_complete_reservation(c); | 211 | jffs2_complete_reservation(c); |
216 | inode->i_size = pageofs; | 212 | inode->i_size = pageofs; |
213 | mutex_unlock(&f->sem); | ||
217 | } | 214 | } |
218 | 215 | ||
219 | /* | 216 | /* |
@@ -222,18 +219,18 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping, | |||
222 | * case of a short-copy. | 219 | * case of a short-copy. |
223 | */ | 220 | */ |
224 | if (!PageUptodate(pg)) { | 221 | if (!PageUptodate(pg)) { |
222 | mutex_lock(&f->sem); | ||
225 | ret = jffs2_do_readpage_nolock(inode, pg); | 223 | ret = jffs2_do_readpage_nolock(inode, pg); |
224 | mutex_unlock(&f->sem); | ||
226 | if (ret) | 225 | if (ret) |
227 | goto out_page; | 226 | goto out_page; |
228 | } | 227 | } |
229 | mutex_unlock(&f->sem); | ||
230 | jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags); | 228 | jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags); |
231 | return ret; | 229 | return ret; |
232 | 230 | ||
233 | out_page: | 231 | out_page: |
234 | unlock_page(pg); | 232 | unlock_page(pg); |
235 | page_cache_release(pg); | 233 | page_cache_release(pg); |
236 | mutex_unlock(&f->sem); | ||
237 | return ret; | 234 | return ret; |
238 | } | 235 | } |
239 | 236 | ||