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 | |
| 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
| -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 | ||
