Skip to content

Commit 31651c6

Browse files
eafertorvalds
authored andcommitted
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>
1 parent 7464726 commit 31651c6

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

fs/hfsplus/extents.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ static int hfsplus_free_extents(struct super_block *sb,
336336
int i;
337337
int err = 0;
338338

339+
/* Mapping the allocation file may lock the extent tree */
340+
WARN_ON(mutex_is_locked(&HFSPLUS_SB(sb)->ext_tree->tree_lock));
341+
339342
hfsplus_dump_extent(extent);
340343
for (i = 0; i < 8; extent++, i++) {
341344
count = be32_to_cpu(extent->block_count);
@@ -415,11 +418,13 @@ int hfsplus_free_fork(struct super_block *sb, u32 cnid,
415418
if (res)
416419
break;
417420
start = be32_to_cpu(fd.key->ext.start_block);
418-
hfsplus_free_extents(sb, ext_entry,
419-
total_blocks - start,
420-
total_blocks);
421421
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);
422426
total_blocks = start;
427+
mutex_lock(&fd.tree->tree_lock);
423428
} while (total_blocks > blocks);
424429
hfs_find_exit(&fd);
425430

@@ -576,15 +581,20 @@ void hfsplus_file_truncate(struct inode *inode)
576581
}
577582
while (1) {
578583
if (alloc_cnt == hip->first_blocks) {
584+
mutex_unlock(&fd.tree->tree_lock);
579585
hfsplus_free_extents(sb, hip->first_extents,
580586
alloc_cnt, alloc_cnt - blk_cnt);
581587
hfsplus_dump_extent(hip->first_extents);
582588
hip->first_blocks = blk_cnt;
589+
mutex_lock(&fd.tree->tree_lock);
583590
break;
584591
}
585592
res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
586593
if (res)
587594
break;
595+
hfs_brec_remove(&fd);
596+
597+
mutex_unlock(&fd.tree->tree_lock);
588598
start = hip->cached_start;
589599
hfsplus_free_extents(sb, hip->cached_extents,
590600
alloc_cnt - start, alloc_cnt - blk_cnt);
@@ -596,7 +606,7 @@ void hfsplus_file_truncate(struct inode *inode)
596606
alloc_cnt = start;
597607
hip->cached_start = hip->cached_blocks = 0;
598608
hip->extent_state &= ~(HFSPLUS_EXT_DIRTY | HFSPLUS_EXT_NEW);
599-
hfs_brec_remove(&fd);
609+
mutex_lock(&fd.tree->tree_lock);
600610
}
601611
hfs_find_exit(&fd);
602612

0 commit comments

Comments
 (0)