Skip to content

Commit 5ffd341

Browse files
tbetkerdedekind
authored andcommitted
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: [email protected] Signed-off-by: Thomas Betker <[email protected]> Acked-by: Joakim Tjernlund <[email protected]> Signed-off-by: Artem Bityutskiy <[email protected]>
1 parent 0131950 commit 5ffd341

File tree

1 file changed

+21
-18
lines changed

1 file changed

+21
-18
lines changed

fs/jffs2/file.c

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -138,33 +138,39 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
138138
struct page *pg;
139139
struct inode *inode = mapping->host;
140140
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;
141144
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
142145
uint32_t pageofs = index << PAGE_CACHE_SHIFT;
143146
int ret = 0;
144147

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);
145158
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);
147163
return -ENOMEM;
164+
}
148165
*pagep = pg;
149166

150-
jffs2_dbg(1, "%s()\n", __func__);
151-
152-
if (pageofs > inode->i_size) {
167+
if (alloc_len) {
153168
/* 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;
156169
struct jffs2_full_dnode *fn;
157-
uint32_t alloc_len;
158170

159171
jffs2_dbg(1, "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
160172
(unsigned int)inode->i_size, pageofs);
161173

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);
168174
memset(&ri, 0, sizeof(ri));
169175

170176
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -191,7 +197,6 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
191197
if (IS_ERR(fn)) {
192198
ret = PTR_ERR(fn);
193199
jffs2_complete_reservation(c);
194-
mutex_unlock(&f->sem);
195200
goto out_page;
196201
}
197202
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,
206211
jffs2_mark_node_obsolete(c, fn->raw);
207212
jffs2_free_full_dnode(fn);
208213
jffs2_complete_reservation(c);
209-
mutex_unlock(&f->sem);
210214
goto out_page;
211215
}
212216
jffs2_complete_reservation(c);
213217
inode->i_size = pageofs;
214-
mutex_unlock(&f->sem);
215218
}
216219

217220
/*
@@ -220,18 +223,18 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
220223
* case of a short-copy.
221224
*/
222225
if (!PageUptodate(pg)) {
223-
mutex_lock(&f->sem);
224226
ret = jffs2_do_readpage_nolock(inode, pg);
225-
mutex_unlock(&f->sem);
226227
if (ret)
227228
goto out_page;
228229
}
230+
mutex_unlock(&f->sem);
229231
jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags);
230232
return ret;
231233

232234
out_page:
233235
unlock_page(pg);
234236
page_cache_release(pg);
237+
mutex_unlock(&f->sem);
235238
return ret;
236239
}
237240

0 commit comments

Comments
 (0)