aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorThomas Betker <thomas.betker@freenet.de>2012-10-17 16:59:30 -0400
committerArtem Bityutskiy <artem.bityutskiy@linux.intel.com>2012-11-09 10:02:50 -0500
commit5ffd3412ae5536a4c57469cb8ea31887121dcb2e (patch)
tree33c9d89eabf70c49e683a36be800be3b4582a358 /fs
parent0131950ebd146b5e31508233352d6f4625af25b1 (diff)
jffs2: Fix lock acquisition order bug in jffs2_write_begin
jffs2_write_begin() first acquires the page lock, then f->sem. This causes an AB-BA deadlock with jffs2_garbage_collect_live(), which first acquires f->sem, then the page lock: jffs2_garbage_collect_live mutex_lock(&f->sem) (A) jffs2_garbage_collect_dnode jffs2_gc_fetch_page read_cache_page_async do_read_cache_page lock_page(page) (B) jffs2_write_begin grab_cache_page_write_begin find_lock_page lock_page(page) (B) mutex_lock(&f->sem) (A) We fix this by restructuring jffs2_write_begin() to take f->sem before the page lock. However, we make sure that f->sem is not held when calling jffs2_reserve_space(), as this is not permitted by the locking rules. The deadlock above was observed multiple times on an SoC with a dual ARMv7 (Cortex-A9), running the long-term 3.4.11 kernel; it occurred when using scp to copy files from a host system to the ARM target system. The fix was heavily tested on the same target system. Cc: stable@vger.kernel.org Signed-off-by: Thomas Betker <thomas.betker@rohde-schwarz.com> Acked-by: Joakim Tjernlund <Joakim.Tjernlund@transmode.se> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/jffs2/file.c39
1 files changed, 21 insertions, 18 deletions
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index 60ef3fb707ff..1506673c087e 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -138,33 +138,39 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
138 struct page *pg; 138 struct page *pg;
139 struct inode *inode = mapping->host; 139 struct inode *inode = mapping->host;
140 struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); 140 struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
141 struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
142 struct jffs2_raw_inode ri;
143 uint32_t alloc_len = 0;
141 pgoff_t index = pos >> PAGE_CACHE_SHIFT; 144 pgoff_t index = pos >> PAGE_CACHE_SHIFT;
142 uint32_t pageofs = index << PAGE_CACHE_SHIFT; 145 uint32_t pageofs = index << PAGE_CACHE_SHIFT;
143 int ret = 0; 146 int ret = 0;
144 147
148 jffs2_dbg(1, "%s()\n", __func__);
149
150 if (pageofs > inode->i_size) {
151 ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
152 ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
153 if (ret)
154 return ret;
155 }
156
157 mutex_lock(&f->sem);
145 pg = grab_cache_page_write_begin(mapping, index, flags); 158 pg = grab_cache_page_write_begin(mapping, index, flags);
146 if (!pg) 159 if (!pg) {
160 if (alloc_len)
161 jffs2_complete_reservation(c);
162 mutex_unlock(&f->sem);
147 return -ENOMEM; 163 return -ENOMEM;
164 }
148 *pagep = pg; 165 *pagep = pg;
149 166
150 jffs2_dbg(1, "%s()\n", __func__); 167 if (alloc_len) {
151
152 if (pageofs > inode->i_size) {
153 /* Make new hole frag from old EOF to new page */ 168 /* Make new hole frag from old EOF to new page */
154 struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
155 struct jffs2_raw_inode ri;
156 struct jffs2_full_dnode *fn; 169 struct jffs2_full_dnode *fn;
157 uint32_t alloc_len;
158 170
159 jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", 171 jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
160 (unsigned int)inode->i_size, pageofs); 172 (unsigned int)inode->i_size, pageofs);
161 173
162 ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
163 ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
164 if (ret)
165 goto out_page;
166
167 mutex_lock(&f->sem);
168 memset(&ri, 0, sizeof(ri)); 174 memset(&ri, 0, sizeof(ri));
169 175
170 ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); 176 ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -191,7 +197,6 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
191 if (IS_ERR(fn)) { 197 if (IS_ERR(fn)) {
192 ret = PTR_ERR(fn); 198 ret = PTR_ERR(fn);
193 jffs2_complete_reservation(c); 199 jffs2_complete_reservation(c);
194 mutex_unlock(&f->sem);
195 goto out_page; 200 goto out_page;
196 } 201 }
197 ret = jffs2_add_full_dnode_to_inode(c, f, fn); 202 ret = jffs2_add_full_dnode_to_inode(c, f, fn);
@@ -206,12 +211,10 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
206 jffs2_mark_node_obsolete(c, fn->raw); 211 jffs2_mark_node_obsolete(c, fn->raw);
207 jffs2_free_full_dnode(fn); 212 jffs2_free_full_dnode(fn);
208 jffs2_complete_reservation(c); 213 jffs2_complete_reservation(c);
209 mutex_unlock(&f->sem);
210 goto out_page; 214 goto out_page;
211 } 215 }
212 jffs2_complete_reservation(c); 216 jffs2_complete_reservation(c);
213 inode->i_size = pageofs; 217 inode->i_size = pageofs;
214 mutex_unlock(&f->sem);
215 } 218 }
216 219
217 /* 220 /*
@@ -220,18 +223,18 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
220 * case of a short-copy. 223 * case of a short-copy.
221 */ 224 */
222 if (!PageUptodate(pg)) { 225 if (!PageUptodate(pg)) {
223 mutex_lock(&f->sem);
224 ret = jffs2_do_readpage_nolock(inode, pg); 226 ret = jffs2_do_readpage_nolock(inode, pg);
225 mutex_unlock(&f->sem);
226 if (ret) 227 if (ret)
227 goto out_page; 228 goto out_page;
228 } 229 }
230 mutex_unlock(&f->sem);
229 jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags); 231 jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags);
230 return ret; 232 return ret;
231 233
232out_page: 234out_page:
233 unlock_page(pg); 235 unlock_page(pg);
234 page_cache_release(pg); 236 page_cache_release(pg);
237 mutex_unlock(&f->sem);
235 return ret; 238 return ret;
236} 239}
237 240