Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ext3: Replace lock/unlock_super() with an explicit lock for resizing

Use a separate lock to protect s_groups_count and the other block
group descriptors which get changed via an on-line resize operation,
so we can stop overloading the use of lock_super().

Port of ext4 commit 32ed5058ce90024efcd811254b4b1de0468099df by
Theodore Ts'o <tytso@mit.edu>.

CC: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Eric Sandeen and committed by
Jan Kara
96d2a495 b8a052d0

+20 -17
+18 -17
fs/ext3/resize.c
··· 209 209 if (IS_ERR(handle)) 210 210 return PTR_ERR(handle); 211 211 212 - lock_super(sb); 212 + mutex_lock(&sbi->s_resize_lock); 213 213 if (input->group != sbi->s_groups_count) { 214 214 err = -EBUSY; 215 215 goto exit_journal; ··· 324 324 brelse(bh); 325 325 326 326 exit_journal: 327 - unlock_super(sb); 327 + mutex_unlock(&sbi->s_resize_lock); 328 328 if ((err2 = ext3_journal_stop(handle)) && !err) 329 329 err = err2; 330 330 ··· 662 662 * important part is that the new block and inode counts are in the backup 663 663 * superblocks, and the location of the new group metadata in the GDT backups. 664 664 * 665 - * We do not need lock_super() for this, because these blocks are not 666 - * otherwise touched by the filesystem code when it is mounted. We don't 667 - * need to worry about last changing from sbi->s_groups_count, because the 668 - * worst that can happen is that we do not copy the full number of backups 669 - * at this time. The resize which changed s_groups_count will backup again. 665 + * We do not need take the s_resize_lock for this, because these 666 + * blocks are not otherwise touched by the filesystem code when it is 667 + * mounted. We don't need to worry about last changing from 668 + * sbi->s_groups_count, because the worst that can happen is that we 669 + * do not copy the full number of backups at this time. The resize 670 + * which changed s_groups_count will backup again. 670 671 */ 671 672 static void update_backups(struct super_block *sb, 672 673 int blk_off, char *data, int size) ··· 826 825 goto exit_put; 827 826 } 828 827 829 - lock_super(sb); 828 + mutex_lock(&sbi->s_resize_lock); 830 829 if (input->group != sbi->s_groups_count) { 831 830 ext3_warning(sb, __func__, 832 831 "multiple resizers run on filesystem!"); ··· 857 856 /* 858 857 * OK, now we've set up the new group. Time to make it active. 859 858 * 860 - * Current kernels don't lock all allocations via lock_super(), 859 + * We do not lock all allocations via s_resize_lock 861 860 * so we have to be safe wrt. concurrent accesses the group 862 861 * data. So we need to be careful to set all of the relevant 863 862 * group descriptor data etc. *before* we enable the group. ··· 901 900 * 902 901 * The precise rules we use are: 903 902 * 904 - * * Writers of s_groups_count *must* hold lock_super 903 + * * Writers of s_groups_count *must* hold s_resize_lock 905 904 * AND 906 905 * * Writers must perform a smp_wmb() after updating all dependent 907 906 * data and before modifying the groups count 908 907 * 909 - * * Readers must hold lock_super() over the access 908 + * * Readers must hold s_resize_lock over the access 910 909 * OR 911 910 * * Readers must perform an smp_rmb() after reading the groups count 912 911 * and before reading any dependent data. ··· 937 936 ext3_journal_dirty_metadata(handle, sbi->s_sbh); 938 937 939 938 exit_journal: 940 - unlock_super(sb); 939 + mutex_unlock(&sbi->s_resize_lock); 941 940 if ((err2 = ext3_journal_stop(handle)) && !err) 942 941 err = err2; 943 942 if (!err) { ··· 974 973 975 974 /* We don't need to worry about locking wrt other resizers just 976 975 * yet: we're going to revalidate es->s_blocks_count after 977 - * taking lock_super() below. */ 976 + * taking the s_resize_lock below. */ 978 977 o_blocks_count = le32_to_cpu(es->s_blocks_count); 979 978 o_groups_count = EXT3_SB(sb)->s_groups_count; 980 979 ··· 1046 1045 goto exit_put; 1047 1046 } 1048 1047 1049 - lock_super(sb); 1048 + mutex_lock(&EXT3_SB(sb)->s_resize_lock); 1050 1049 if (o_blocks_count != le32_to_cpu(es->s_blocks_count)) { 1051 1050 ext3_warning(sb, __func__, 1052 1051 "multiple resizers run on filesystem!"); 1053 - unlock_super(sb); 1052 + mutex_unlock(&EXT3_SB(sb)->s_resize_lock); 1054 1053 ext3_journal_stop(handle); 1055 1054 err = -EBUSY; 1056 1055 goto exit_put; ··· 1060 1059 EXT3_SB(sb)->s_sbh))) { 1061 1060 ext3_warning(sb, __func__, 1062 1061 "error %d on journal write access", err); 1063 - unlock_super(sb); 1062 + mutex_unlock(&EXT3_SB(sb)->s_resize_lock); 1064 1063 ext3_journal_stop(handle); 1065 1064 goto exit_put; 1066 1065 } 1067 1066 es->s_blocks_count = cpu_to_le32(o_blocks_count + add); 1068 1067 ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); 1069 - unlock_super(sb); 1068 + mutex_unlock(&EXT3_SB(sb)->s_resize_lock); 1070 1069 ext3_debug("freeing blocks %lu through "E3FSBLK"\n", o_blocks_count, 1071 1070 o_blocks_count + add); 1072 1071 ext3_free_blocks_sb(handle, sb, o_blocks_count, add, &freed_blocks);
+1
fs/ext3/super.c
··· 1929 1929 #endif 1930 1930 INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ 1931 1931 mutex_init(&sbi->s_orphan_lock); 1932 + mutex_init(&sbi->s_resize_lock); 1932 1933 1933 1934 sb->s_root = NULL; 1934 1935
+1
include/linux/ext3_fs_sb.h
··· 73 73 struct journal_s * s_journal; 74 74 struct list_head s_orphan; 75 75 struct mutex s_orphan_lock; 76 + struct mutex s_resize_lock; 76 77 unsigned long s_commit_interval; 77 78 struct block_device *journal_bdev; 78 79 #ifdef CONFIG_JBD_DEBUG