summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErnesto A. Fernández <ernesto.mnd.fernandez@gmail.com>2018-08-22 00:59:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-08-22 13:52:50 -0400
commit31651c607151f1034cfb57e5a78678bea54c362b (patch)
treeb8868dcd8dd0330edeec238bb83716be61b3d314
parent7464726cb5998846306ed0a7d6714afb2e37b25d (diff)
hfsplus: avoid deadlock on file truncation
After an extent is removed from the extent tree, the corresponding bits are also cleared from the block allocation file. This is currently done without releasing the tree lock. The problem is that the allocation file has extents of its own; if it is fragmented enough, some of them may be in the extent tree as well, and hfsplus_get_block() will try to take the lock again. To avoid deadlock, only hold the extent tree lock during the actual tree operations. Link: http://lkml.kernel.org/r/20180709202549.auxwkb6memlegb4a@eaf Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com> Reported-by: Anatoly Trosinenko <anatoly.trosinenko@gmail.com> Cc: Viacheslav Dubeyko <slava@dubeyko.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/hfsplus/extents.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index e8770935ce6d..8e0f59767694 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -336,6 +336,9 @@ static int hfsplus_free_extents(struct super_block *sb,
336 int i; 336 int i;
337 int err = 0; 337 int err = 0;
338 338
339 /* Mapping the allocation file may lock the extent tree */
340 WARN_ON(mutex_is_locked(&HFSPLUS_SB(sb)->ext_tree->tree_lock));
341
339 hfsplus_dump_extent(extent); 342 hfsplus_dump_extent(extent);
340 for (i = 0; i < 8; extent++, i++) { 343 for (i = 0; i < 8; extent++, i++) {
341 count = be32_to_cpu(extent->block_count); 344 count = be32_to_cpu(extent->block_count);
@@ -415,11 +418,13 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,
415 if (res) 418 if (res)
416 break; 419 break;
417 start = be32_to_cpu(fd.key->ext.start_block); 420 start = be32_to_cpu(fd.key->ext.start_block);
418 hfsplus_free_extents(sb, ext_entry,
419 total_blocks - start,
420 total_blocks);
421 hfs_brec_remove(&fd); 421 hfs_brec_remove(&fd);
422
423 mutex_unlock(&fd.tree->tree_lock);
424 hfsplus_free_extents(sb, ext_entry, total_blocks - start,
425 total_blocks);
422 total_blocks = start; 426 total_blocks = start;
427 mutex_lock(&fd.tree->tree_lock);
423 } while (total_blocks > blocks); 428 } while (total_blocks > blocks);
424 hfs_find_exit(&fd); 429 hfs_find_exit(&fd);
425 430
@@ -576,15 +581,20 @@ void hfsplus_file_truncate(struct inode *inode)
576 } 581 }
577 while (1) { 582 while (1) {
578 if (alloc_cnt == hip->first_blocks) { 583 if (alloc_cnt == hip->first_blocks) {
584 mutex_unlock(&fd.tree->tree_lock);
579 hfsplus_free_extents(sb, hip->first_extents, 585 hfsplus_free_extents(sb, hip->first_extents,
580 alloc_cnt, alloc_cnt - blk_cnt); 586 alloc_cnt, alloc_cnt - blk_cnt);
581 hfsplus_dump_extent(hip->first_extents); 587 hfsplus_dump_extent(hip->first_extents);
582 hip->first_blocks = blk_cnt; 588 hip->first_blocks = blk_cnt;
589 mutex_lock(&fd.tree->tree_lock);
583 break; 590 break;
584 } 591 }
585 res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt); 592 res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
586 if (res) 593 if (res)
587 break; 594 break;
595 hfs_brec_remove(&fd);
596
597 mutex_unlock(&fd.tree->tree_lock);
588 start = hip->cached_start; 598 start = hip->cached_start;
589 hfsplus_free_extents(sb, hip->cached_extents, 599 hfsplus_free_extents(sb, hip->cached_extents,
590 alloc_cnt - start, alloc_cnt - blk_cnt); 600 alloc_cnt - start, alloc_cnt - blk_cnt);
@@ -596,7 +606,7 @@ void hfsplus_file_truncate(struct inode *inode)
596 alloc_cnt = start; 606 alloc_cnt = start;
597 hip->cached_start = hip->cached_blocks = 0; 607 hip->cached_start = hip->cached_blocks = 0;
598 hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW); 608 hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
599 hfs_brec_remove(&fd); 609 mutex_lock(&fd.tree->tree_lock);
600 } 610 }
601 hfs_find_exit(&fd); 611 hfs_find_exit(&fd);
602 612