From 46891532370e862d6bddedef9e6ca22a59a51fa4 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 29 Mar 2010 13:55:39 +0200 Subject: ext2: Avoid loading bitmaps for full groups during block allocation There is no point in loading bitmap for groups which are completely full. This causes noticeable performance problems (and memory pressure) on small systems with large full filesystem (http://marc.info/?l=linux-ext4&m=126843108314310&w=2). Port of the same ext3 patch. Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs/ext2') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 3cf038c055d..e8766a39677 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1331,6 +1331,12 @@ retry_alloc: goto io_error; free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); + /* + * skip this group (and avoid loading bitmap) if there + * are no free blocks + */ + if (!free_blocks) + continue; /* * skip this group if the number of * free blocks is less than half of the reservation -- cgit v1.2.3-70-g09d2 From 524e4a1d102bdcee37297c0b763e945827b33ab8 Mon Sep 17 00:00:00 2001 From: Francis Moreau Date: Thu, 8 Apr 2010 11:35:17 +0200 Subject: ext2: remove useless call to brelse() in ext2_free_inode() This patch removes a useless call to brelse(bitmap_bh) since at that point bitmap_bh is NULL and slightly cleans up bitmap_bh handling. Signed-off-by: Francis Moreau Signed-off-by: Jan Kara --- fs/ext2/ialloc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index ad7d572ee8d..f0c5286f934 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -106,7 +106,7 @@ void ext2_free_inode (struct inode * inode) struct super_block * sb = inode->i_sb; int is_directory; unsigned long ino; - struct buffer_head *bitmap_bh = NULL; + struct buffer_head *bitmap_bh; unsigned long block_group; unsigned long bit; struct ext2_super_block * es; @@ -135,14 +135,13 @@ void ext2_free_inode (struct inode * inode) ino > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "ext2_free_inode", "reserved or nonexistent inode %lu", ino); - goto error_return; + return; } block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb); - brelse(bitmap_bh); bitmap_bh = read_inode_bitmap(sb, block_group); if (!bitmap_bh) - goto error_return; + return; /* Ok, now we can actually update the inode bitmaps.. */ if (!ext2_clear_bit_atomic(sb_bgl_lock(EXT2_SB(sb), block_group), @@ -154,7 +153,7 @@ void ext2_free_inode (struct inode * inode) mark_buffer_dirty(bitmap_bh); if (sb->s_flags & MS_SYNCHRONOUS) sync_dirty_buffer(bitmap_bh); -error_return: + brelse(bitmap_bh); } -- cgit v1.2.3-70-g09d2 From 2b8120efb2d41e2aefce3b06cf3fd085f71e9021 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:33 +0200 Subject: ext2: Use ext2_clear_super_error() in ext2_sync_fs() ext2_sync_fs() used to duplicate the code from ext2_clear_super_error(). Signed-off-by: Jan Blunck Signed-off-by: Jan Kara --- fs/ext2/super.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 42e4a303b67..8e8b675ac20 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1120,8 +1120,8 @@ static void ext2_clear_super_error(struct super_block *sb) * be remapped. Nothing we can do but to retry the * write and hope for the best. */ - printk(KERN_ERR "EXT2-fs: %s previous I/O error to " - "superblock detected", sb->s_id); + ext2_msg(sb, KERN_ERR, + "previous I/O error to superblock detected\n"); clear_buffer_write_io_error(sbh); set_buffer_uptodate(sbh); } @@ -1161,23 +1161,9 @@ static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es) static int ext2_sync_fs(struct super_block *sb, int wait) { struct ext2_super_block *es = EXT2_SB(sb)->s_es; - struct buffer_head *sbh = EXT2_SB(sb)->s_sbh; lock_kernel(); - if (buffer_write_io_error(sbh)) { - /* - * Oh, dear. A previous attempt to write the - * superblock failed. This could happen because the - * USB device was yanked out. Or it could happen to - * be a transient write error and maybe the block will - * be remapped. Nothing we can do but to retry the - * write and hope for the best. - */ - ext2_msg(sb, KERN_ERR, - "previous I/O error to superblock detected\n"); - clear_buffer_write_io_error(sbh); - set_buffer_uptodate(sbh); - } + ext2_clear_super_error(sb); if (es->s_state & cpu_to_le16(EXT2_VALID_FS)) { ext2_debug("setting valid to 0\n"); -- cgit v1.2.3-70-g09d2 From 269c8db30cf5b60f47a44bbceaac118b986895d8 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:34 +0200 Subject: ext2: Set the write time in ext2_sync_fs() This is probably a typo since the write time should actually be updated by ext2_sync_fs() instead of the mount time. Signed-off-by: Jan Blunck Signed-off-by: Jan Kara --- fs/ext2/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/ext2') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 8e8b675ac20..b2050032424 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1172,7 +1172,7 @@ static int ext2_sync_fs(struct super_block *sb, int wait) cpu_to_le32(ext2_count_free_blocks(sb)); es->s_free_inodes_count = cpu_to_le32(ext2_count_free_inodes(sb)); - es->s_mtime = cpu_to_le32(get_seconds()); + es->s_wtime = cpu_to_le32(get_seconds()); ext2_sync_super(sb, es); } else { ext2_commit_super(sb, es); -- cgit v1.2.3-70-g09d2 From 20da9baf4cf9c627aaf7b00d64ce0b2221bab9bf Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:35 +0200 Subject: ext2: Remove duplicate code from ext2_sync_fs() Depending in the state (valid or unchecked) of the filesystem either ext2_sync_super() or ext2_commit_super() is called. If the filesystem is currently valid (it is checked), we first mark it unchecked and afterwards duplicate the work that ext2_sync_super() is doing later. Therefore this patch removes the duplicate code and calls ext2_sync_super() directly after marking the filesystem unchecked. Signed-off-by: Jan Blunck Signed-off-by: Jan Kara --- fs/ext2/super.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index b2050032424..09a88bf0457 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1163,16 +1163,9 @@ static int ext2_sync_fs(struct super_block *sb, int wait) struct ext2_super_block *es = EXT2_SB(sb)->s_es; lock_kernel(); - ext2_clear_super_error(sb); - if (es->s_state & cpu_to_le16(EXT2_VALID_FS)) { ext2_debug("setting valid to 0\n"); es->s_state &= cpu_to_le16(~EXT2_VALID_FS); - es->s_free_blocks_count = - cpu_to_le32(ext2_count_free_blocks(sb)); - es->s_free_inodes_count = - cpu_to_le32(ext2_count_free_inodes(sb)); - es->s_wtime = cpu_to_le32(get_seconds()); ext2_sync_super(sb, es); } else { ext2_commit_super(sb, es); -- cgit v1.2.3-70-g09d2 From ee6921ebd04cb807dfe88b10ad80f1124813c673 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:36 +0200 Subject: ext2: Fold ext2_commit_super() into ext2_sync_super() Both function originally did similar things except that ext2_sync_super() is returning after the call to sync_dirty_buffer(sbh). Therefore this patch adds a wait flag to tell ext2_sync_super() if it has to call sync_dirty_buffer() to wait for in-progress I/O to finish. Signed-off-by: Jan Blunck Signed-off-by: Jan Kara --- fs/ext2/super.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 09a88bf0457..a304c544571 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -39,7 +39,7 @@ #include "xip.h" static void ext2_sync_super(struct super_block *sb, - struct ext2_super_block *es); + struct ext2_super_block *es, int wait); static int ext2_remount (struct super_block * sb, int * flags, char * data); static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf); static int ext2_sync_fs(struct super_block *sb, int wait); @@ -54,7 +54,7 @@ void ext2_error (struct super_block * sb, const char * function, if (!(sb->s_flags & MS_RDONLY)) { sbi->s_mount_state |= EXT2_ERROR_FS; es->s_state |= cpu_to_le16(EXT2_ERROR_FS); - ext2_sync_super(sb, es); + ext2_sync_super(sb, es, 1); } va_start(args, fmt); @@ -125,7 +125,7 @@ static void ext2_put_super (struct super_block * sb) struct ext2_super_block *es = sbi->s_es; es->s_state = cpu_to_le16(sbi->s_mount_state); - ext2_sync_super(sb, es); + ext2_sync_super(sb, es, 1); } db_count = sbi->s_gdb_count; for (i = 0; i < db_count; i++) @@ -1127,23 +1127,16 @@ static void ext2_clear_super_error(struct super_block *sb) } } -static void ext2_commit_super (struct super_block * sb, - struct ext2_super_block * es) -{ - ext2_clear_super_error(sb); - es->s_wtime = cpu_to_le32(get_seconds()); - mark_buffer_dirty(EXT2_SB(sb)->s_sbh); - sb->s_dirt = 0; -} - -static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es) +static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es, + int wait) { ext2_clear_super_error(sb); es->s_free_blocks_count = cpu_to_le32(ext2_count_free_blocks(sb)); es->s_free_inodes_count = cpu_to_le32(ext2_count_free_inodes(sb)); es->s_wtime = cpu_to_le32(get_seconds()); mark_buffer_dirty(EXT2_SB(sb)->s_sbh); - sync_dirty_buffer(EXT2_SB(sb)->s_sbh); + if (wait) + sync_dirty_buffer(EXT2_SB(sb)->s_sbh); sb->s_dirt = 0; } @@ -1166,11 +1159,8 @@ static int ext2_sync_fs(struct super_block *sb, int wait) if (es->s_state & cpu_to_le16(EXT2_VALID_FS)) { ext2_debug("setting valid to 0\n"); es->s_state &= cpu_to_le16(~EXT2_VALID_FS); - ext2_sync_super(sb, es); - } else { - ext2_commit_super(sb, es); } - sb->s_dirt = 0; + ext2_sync_super(sb, es, wait); unlock_kernel(); return 0; @@ -1268,7 +1258,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) if (!ext2_setup_super (sb, es, 0)) sb->s_flags &= ~MS_RDONLY; } - ext2_sync_super(sb, es); + ext2_sync_super(sb, es, 1); unlock_kernel(); return 0; restore_opts: -- cgit v1.2.3-70-g09d2 From 4c96a68bfc110d87b28bcee4c395a7b4d26ed67a Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:37 +0200 Subject: ext2: Move ext2_write_super() out of ext2_setup_super() Move ext2_write_super() out of ext2_setup_super() as a preparation for the next patch that adds a new lock for superblock fields. Signed-off-by: Jan Blunck Signed-off-by: Jan Kara --- fs/ext2/super.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index a304c544571..f28a7ad02af 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -606,7 +606,6 @@ static int ext2_setup_super (struct super_block * sb, if (!le16_to_cpu(es->s_max_mnt_count)) es->s_max_mnt_count = cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT); le16_add_cpu(&es->s_mnt_count, 1); - ext2_write_super(sb); if (test_opt (sb, DEBUG)) ext2_msg(sb, KERN_INFO, "%s, %s, bs=%lu, fs=%lu, gc=%lu, " "bpg=%lu, ipg=%lu, mo=%04lx]", @@ -1079,7 +1078,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_msg(sb, KERN_WARNING, "warning: mounting ext3 filesystem as ext2"); - ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY); + if (ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY)) + sb->s_flags |= MS_RDONLY; + ext2_write_super(sb); return 0; cantfind_ext2: @@ -1238,6 +1239,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) */ es->s_state = cpu_to_le16(sbi->s_mount_state); es->s_mtime = cpu_to_le32(get_seconds()); + ext2_sync_super(sb, es, 1); } else { __le32 ret = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP); @@ -1257,8 +1259,8 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) sbi->s_mount_state = le16_to_cpu(es->s_state); if (!ext2_setup_super (sb, es, 0)) sb->s_flags &= ~MS_RDONLY; + ext2_write_super(sb); } - ext2_sync_super(sb, es, 1); unlock_kernel(); return 0; restore_opts: -- cgit v1.2.3-70-g09d2 From c15271f4e74cd6dbdf461335d6d1450949c4b956 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:38 +0200 Subject: ext2: Add ext2_sb_info s_lock spinlock Add a spinlock that protects against concurrent modifications of s_mount_state, s_blocks_last, s_overhead_last and the content of the superblock's buffer pointed to by sbi->s_es. The spinlock is now used in ext2_xattr_update_super_block() which was setting the EXT2_FEATURE_COMPAT_EXT_ATTR flag on the superblock without protection before. Likewise the spinlock is used in ext2_show_options() to have a consistent view of the mount options. This is a preparation patch for removing the BKL from ext2 in the next patch. Signed-off-by: Jan Blunck Cc: Andi Kleen Cc: Jan Kara Cc: OGAWA Hirofumi Signed-off-by: Jan Kara --- fs/ext2/inode.c | 2 ++ fs/ext2/super.c | 27 ++++++++++++++++++++++++++- fs/ext2/xattr.c | 2 ++ include/linux/ext2_fs_sb.h | 9 +++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) (limited to 'fs/ext2') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index fc13cc119aa..5d15442abbd 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1407,9 +1407,11 @@ static int __ext2_write_inode(struct inode *inode, int do_sync) * created, add a flag to the superblock. */ lock_kernel(); + spin_lock(&EXT2_SB(sb)->s_lock); ext2_update_dynamic_rev(sb); EXT2_SET_RO_COMPAT_FEATURE(sb, EXT2_FEATURE_RO_COMPAT_LARGE_FILE); + spin_unlock(&EXT2_SB(sb)->s_lock); unlock_kernel(); ext2_write_super(sb); } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index f28a7ad02af..28f65609589 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -52,8 +52,10 @@ void ext2_error (struct super_block * sb, const char * function, struct ext2_super_block *es = sbi->s_es; if (!(sb->s_flags & MS_RDONLY)) { + spin_lock(&sbi->s_lock); sbi->s_mount_state |= EXT2_ERROR_FS; es->s_state |= cpu_to_le16(EXT2_ERROR_FS); + spin_unlock(&sbi->s_lock); ext2_sync_super(sb, es, 1); } @@ -84,6 +86,9 @@ void ext2_msg(struct super_block *sb, const char *prefix, va_end(args); } +/* + * This must be called with sbi->s_lock held. + */ void ext2_update_dynamic_rev(struct super_block *sb) { struct ext2_super_block *es = EXT2_SB(sb)->s_es; @@ -124,7 +129,9 @@ static void ext2_put_super (struct super_block * sb) if (!(sb->s_flags & MS_RDONLY)) { struct ext2_super_block *es = sbi->s_es; + spin_lock(&sbi->s_lock); es->s_state = cpu_to_le16(sbi->s_mount_state); + spin_unlock(&sbi->s_lock); ext2_sync_super(sb, es, 1); } db_count = sbi->s_gdb_count; @@ -209,6 +216,7 @@ static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) struct ext2_super_block *es = sbi->s_es; unsigned long def_mount_opts; + spin_lock(&sbi->s_lock); def_mount_opts = le32_to_cpu(es->s_default_mount_opts); if (sbi->s_sb_block != 1) @@ -281,6 +289,7 @@ static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) if (!test_opt(sb, RESERVATION)) seq_puts(seq, ",noreservation"); + spin_unlock(&sbi->s_lock); return 0; } @@ -766,6 +775,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; + spin_lock_init(&sbi->s_lock); + /* * See what the current blocksize for the device is, and * use that as the blocksize. Otherwise (or if the blocksize @@ -1132,9 +1143,12 @@ static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es, int wait) { ext2_clear_super_error(sb); + spin_lock(&EXT2_SB(sb)->s_lock); es->s_free_blocks_count = cpu_to_le32(ext2_count_free_blocks(sb)); es->s_free_inodes_count = cpu_to_le32(ext2_count_free_inodes(sb)); es->s_wtime = cpu_to_le32(get_seconds()); + /* unlock before we do IO */ + spin_unlock(&EXT2_SB(sb)->s_lock); mark_buffer_dirty(EXT2_SB(sb)->s_sbh); if (wait) sync_dirty_buffer(EXT2_SB(sb)->s_sbh); @@ -1151,16 +1165,18 @@ static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es, * may have been checked while mounted and e2fsck may have * set s_state to EXT2_VALID_FS after some corrections. */ - static int ext2_sync_fs(struct super_block *sb, int wait) { + struct ext2_sb_info *sbi = EXT2_SB(sb); struct ext2_super_block *es = EXT2_SB(sb)->s_es; lock_kernel(); + spin_lock(&sbi->s_lock); if (es->s_state & cpu_to_le16(EXT2_VALID_FS)) { ext2_debug("setting valid to 0\n"); es->s_state &= cpu_to_le16(~EXT2_VALID_FS); } + spin_unlock(&sbi->s_lock); ext2_sync_super(sb, es, wait); unlock_kernel(); @@ -1186,6 +1202,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) int err; lock_kernel(); + spin_lock(&sbi->s_lock); /* Store the old options */ old_sb_flags = sb->s_flags; @@ -1224,12 +1241,14 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) sbi->s_mount_opt |= old_mount_opt & EXT2_MOUNT_XIP; } if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) { + spin_unlock(&sbi->s_lock); unlock_kernel(); return 0; } if (*flags & MS_RDONLY) { if (le16_to_cpu(es->s_state) & EXT2_VALID_FS || !(sbi->s_mount_state & EXT2_VALID_FS)) { + spin_unlock(&sbi->s_lock); unlock_kernel(); return 0; } @@ -1239,6 +1258,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) */ es->s_state = cpu_to_le16(sbi->s_mount_state); es->s_mtime = cpu_to_le32(get_seconds()); + spin_unlock(&sbi->s_lock); ext2_sync_super(sb, es, 1); } else { __le32 ret = EXT2_HAS_RO_COMPAT_FEATURE(sb, @@ -1259,6 +1279,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) sbi->s_mount_state = le16_to_cpu(es->s_state); if (!ext2_setup_super (sb, es, 0)) sb->s_flags &= ~MS_RDONLY; + spin_unlock(&sbi->s_lock); ext2_write_super(sb); } unlock_kernel(); @@ -1268,6 +1289,7 @@ restore_opts: sbi->s_resuid = old_opts.s_resuid; sbi->s_resgid = old_opts.s_resgid; sb->s_flags = old_sb_flags; + spin_unlock(&sbi->s_lock); unlock_kernel(); return err; } @@ -1279,6 +1301,8 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) struct ext2_super_block *es = sbi->s_es; u64 fsid; + spin_lock(&sbi->s_lock); + if (test_opt (sb, MINIX_DF)) sbi->s_overhead_last = 0; else if (sbi->s_blocks_last != le32_to_cpu(es->s_blocks_count)) { @@ -1333,6 +1357,7 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) le64_to_cpup((void *)es->s_uuid + sizeof(u64)); buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL; buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL; + spin_unlock(&sbi->s_lock); return 0; } diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index e44dc92609b..3b96045a00c 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -345,7 +345,9 @@ static void ext2_xattr_update_super_block(struct super_block *sb) if (EXT2_HAS_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR)) return; + spin_lock(&EXT2_SB(sb)->s_lock); EXT2_SET_COMPAT_FEATURE(sb, EXT2_FEATURE_COMPAT_EXT_ATTR); + spin_unlock(&EXT2_SB(sb)->s_lock); sb->s_dirt = 1; mark_buffer_dirty(EXT2_SB(sb)->s_sbh); } diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h index 1cdb66367c9..db4d9f586bb 100644 --- a/include/linux/ext2_fs_sb.h +++ b/include/linux/ext2_fs_sb.h @@ -106,6 +106,15 @@ struct ext2_sb_info { spinlock_t s_rsv_window_lock; struct rb_root s_rsv_window_root; struct ext2_reserve_window_node s_rsv_window_head; + /* + * s_lock protects against concurrent modifications of s_mount_state, + * s_blocks_last, s_overhead_last and the content of superblock's + * buffer pointed to by sbi->s_es. + * + * Note: It is used in ext2_show_options() to provide a consistent view + * of the mount options. + */ + spinlock_t s_lock; }; static inline spinlock_t * -- cgit v1.2.3-70-g09d2 From e0a5cbac029db69032758000c67465c2ed7a5736 Mon Sep 17 00:00:00 2001 From: Jan Blunck Date: Wed, 14 Apr 2010 14:38:39 +0200 Subject: BKL: Remove BKL from ext2 filesystem The BKL is still used in ext2_put_super(), ext2_fill_super(), ext2_sync_fs() ext2_remount() and ext2_write_inode(). From these calls ext2_put_super(), ext2_fill_super() and ext2_remount() are protected against each other by the struct super_block s_umount rw semaphore. The call in ext2_write_inode() could only protect the modification of the ext2_sb_info through ext2_update_dynamic_rev() against concurrent ext2_sync_fs() or ext2_remount(). ext2_fill_super() and ext2_put_super() can be left out because you need a valid filesystem reference in all three cases, which you do not have when you are one of these functions. If the BKL is only protecting the modification of the ext2_sb_info it can safely be removed since this is protected by the struct ext2_sb_info s_lock. Signed-off-by: Jan Blunck Cc: Jan Kara Signed-off-by: Jan Kara --- fs/ext2/inode.c | 3 --- fs/ext2/super.c | 13 ------------- 2 files changed, 16 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 5d15442abbd..b90c3bf6e9b 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -22,7 +22,6 @@ * Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000 */ -#include #include #include #include @@ -1406,13 +1405,11 @@ static int __ext2_write_inode(struct inode *inode, int do_sync) /* If this is the first large file * created, add a flag to the superblock. */ - lock_kernel(); spin_lock(&EXT2_SB(sb)->s_lock); ext2_update_dynamic_rev(sb); EXT2_SET_RO_COMPAT_FEATURE(sb, EXT2_FEATURE_RO_COMPAT_LARGE_FILE); spin_unlock(&EXT2_SB(sb)->s_lock); - unlock_kernel(); ext2_write_super(sb); } } diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 28f65609589..71e9eb1fa69 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -120,8 +119,6 @@ static void ext2_put_super (struct super_block * sb) int i; struct ext2_sb_info *sbi = EXT2_SB(sb); - lock_kernel(); - if (sb->s_dirt) ext2_write_super(sb); @@ -147,8 +144,6 @@ static void ext2_put_super (struct super_block * sb) sb->s_fs_info = NULL; kfree(sbi->s_blockgroup_lock); kfree(sbi); - - unlock_kernel(); } static struct kmem_cache * ext2_inode_cachep; @@ -1170,7 +1165,6 @@ static int ext2_sync_fs(struct super_block *sb, int wait) struct ext2_sb_info *sbi = EXT2_SB(sb); struct ext2_super_block *es = EXT2_SB(sb)->s_es; - lock_kernel(); spin_lock(&sbi->s_lock); if (es->s_state & cpu_to_le16(EXT2_VALID_FS)) { ext2_debug("setting valid to 0\n"); @@ -1178,8 +1172,6 @@ static int ext2_sync_fs(struct super_block *sb, int wait) } spin_unlock(&sbi->s_lock); ext2_sync_super(sb, es, wait); - unlock_kernel(); - return 0; } @@ -1201,7 +1193,6 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) unsigned long old_sb_flags; int err; - lock_kernel(); spin_lock(&sbi->s_lock); /* Store the old options */ @@ -1242,14 +1233,12 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) } if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) { spin_unlock(&sbi->s_lock); - unlock_kernel(); return 0; } if (*flags & MS_RDONLY) { if (le16_to_cpu(es->s_state) & EXT2_VALID_FS || !(sbi->s_mount_state & EXT2_VALID_FS)) { spin_unlock(&sbi->s_lock); - unlock_kernel(); return 0; } /* @@ -1282,7 +1271,6 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) spin_unlock(&sbi->s_lock); ext2_write_super(sb); } - unlock_kernel(); return 0; restore_opts: sbi->s_mount_opt = old_opts.s_mount_opt; @@ -1290,7 +1278,6 @@ restore_opts: sbi->s_resgid = old_opts.s_resgid; sb->s_flags = old_sb_flags; spin_unlock(&sbi->s_lock); - unlock_kernel(); return err; } -- cgit v1.2.3-70-g09d2 From 12755627bdcddcdb30a1bfb9a09395a52b1d6838 Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Thu, 8 Apr 2010 22:04:20 +0400 Subject: quota: unify quota init condition in setattr Quota must being initialized if size or uid/git changes requested. But initialization performed in two different places: in case of i_size file system is responsible for dquot init , but in case of uid/gid init will be called internally in dquot_transfer(). This ambiguity makes code harder to understand. Let's move this logic to one common helper function. Signed-off-by: Dmitry Monakhov Signed-off-by: Jan Kara --- fs/ext2/inode.c | 2 +- fs/ext3/inode.c | 2 +- fs/ext4/inode.c | 2 +- fs/jfs/file.c | 2 +- fs/ocfs2/file.c | 4 ++-- fs/quota/dquot.c | 5 ++--- fs/reiserfs/inode.c | 3 ++- fs/udf/file.c | 2 +- fs/ufs/truncate.c | 8 ++++---- include/linux/quotaops.h | 8 ++++++++ 10 files changed, 23 insertions(+), 15 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index b90c3bf6e9b..527c46d9bc1 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1466,7 +1466,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) if (error) return error; - if (iattr->ia_valid & ATTR_SIZE) + if (is_quota_modification(inode, iattr)) dquot_initialize(inode); if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) { diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index ea33bdf0a30..735f0190ec2 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -3151,7 +3151,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) if (error) return error; - if (ia_valid & ATTR_SIZE) + if (is_quota_modification(inode, attr)) dquot_initialize(inode); if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 81d60541284..3e0f6af9d08 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5425,7 +5425,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (error) return error; - if (ia_valid & ATTR_SIZE) + if (is_quota_modification(inode, attr)) dquot_initialize(inode); if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 14ba982b3f2..85d9ec65922 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -98,7 +98,7 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr) if (rc) return rc; - if (iattr->ia_valid & ATTR_SIZE) + if (is_quota_modification(inode, iattr)) dquot_initialize(inode); if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) { diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index f74f1400ecc..e127c53ec2e 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -966,10 +966,10 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) if (status) return status; + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); size_change = S_ISREG(inode->i_mode) && attr->ia_valid & ATTR_SIZE; if (size_change) { - dquot_initialize(inode); - status = ocfs2_rw_lock(inode, 1); if (status < 0) { mlog_errno(status); diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index b1a5036560a..1056a21f030 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1822,10 +1822,9 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) mask |= 1 << GRPQUOTA; chid[GRPQUOTA] = iattr->ia_gid; } - if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) { - dquot_initialize(inode); + if (sb_any_quota_active(inode->i_sb) && !IS_NOQUOTA(inode)) return __dquot_transfer(inode, chid, mask); - } + return 0; } EXPORT_SYMBOL(dquot_transfer); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index dc2c65e0485..0f22fdaf54a 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3076,9 +3076,10 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID); depth = reiserfs_write_lock_once(inode->i_sb); - if (attr->ia_valid & ATTR_SIZE) { + if (is_quota_modification(inode, attr)) dquot_initialize(inode); + if (attr->ia_valid & ATTR_SIZE) { /* version 2 items will be caught by the s_maxbytes check ** done for us in vmtruncate */ diff --git a/fs/udf/file.c b/fs/udf/file.c index 4b6a46ccbf4..6ebc043f3a2 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -227,7 +227,7 @@ int udf_setattr(struct dentry *dentry, struct iattr *iattr) if (error) return error; - if (iattr->ia_valid & ATTR_SIZE) + if (is_quota_modification(inode, iattr)) dquot_initialize(inode); if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index ee8db3e77bf..f294c44577d 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -518,18 +518,18 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr) if (error) return error; + if (is_quota_modification(inode, attr)) + dquot_initialize(inode); + if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { error = dquot_transfer(inode, attr); if (error) return error; } - if (ia_valid & ATTR_SIZE && - attr->ia_size != i_size_read(inode)) { + if (ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { loff_t old_i_size = inode->i_size; - dquot_initialize(inode); - error = vmtruncate(inode, attr->ia_size); if (error) return error; diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 82c70c42d03..8a7818764a6 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -14,6 +14,14 @@ static inline struct quota_info *sb_dqopt(struct super_block *sb) return &sb->s_dquot; } +/* i_mutex must being held */ +static inline bool is_quota_modification(struct inode *inode, struct iattr *ia) +{ + return (ia->ia_valid & ATTR_SIZE && ia->ia_size != inode->i_size) || + (ia->ia_valid & ATTR_UID && ia->ia_uid != inode->i_uid) || + (ia->ia_valid & ATTR_GID && ia->ia_gid != inode->i_gid); +} + #if defined(CONFIG_QUOTA) /* -- cgit v1.2.3-70-g09d2 From 749c72efa4bd91dad3a8c2ac8bfaa7c45490ceef Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 13 May 2010 17:53:16 -0700 Subject: ext2: constify xattr_handler Signed-off-by: Stephen Hemminger Signed-off-by: Al Viro --- fs/ext2/acl.c | 4 ++-- fs/ext2/xattr.c | 10 +++++----- fs/ext2/xattr.h | 12 ++++++------ fs/ext2/xattr_security.c | 2 +- fs/ext2/xattr_trusted.c | 2 +- fs/ext2/xattr_user.c | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index a99e54318c3..ca7e2a0ed98 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -420,7 +420,7 @@ release_and_out: return error; } -struct xattr_handler ext2_xattr_acl_access_handler = { +const struct xattr_handler ext2_xattr_acl_access_handler = { .prefix = POSIX_ACL_XATTR_ACCESS, .flags = ACL_TYPE_ACCESS, .list = ext2_xattr_list_acl_access, @@ -428,7 +428,7 @@ struct xattr_handler ext2_xattr_acl_access_handler = { .set = ext2_xattr_set_acl, }; -struct xattr_handler ext2_xattr_acl_default_handler = { +const struct xattr_handler ext2_xattr_acl_default_handler = { .prefix = POSIX_ACL_XATTR_DEFAULT, .flags = ACL_TYPE_DEFAULT, .list = ext2_xattr_list_acl_default, diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 3b96045a00c..7c3915780b1 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -101,7 +101,7 @@ static void ext2_xattr_rehash(struct ext2_xattr_header *, static struct mb_cache *ext2_xattr_cache; -static struct xattr_handler *ext2_xattr_handler_map[] = { +static const struct xattr_handler *ext2_xattr_handler_map[] = { [EXT2_XATTR_INDEX_USER] = &ext2_xattr_user_handler, #ifdef CONFIG_EXT2_FS_POSIX_ACL [EXT2_XATTR_INDEX_POSIX_ACL_ACCESS] = &ext2_xattr_acl_access_handler, @@ -113,7 +113,7 @@ static struct xattr_handler *ext2_xattr_handler_map[] = { #endif }; -struct xattr_handler *ext2_xattr_handlers[] = { +const struct xattr_handler *ext2_xattr_handlers[] = { &ext2_xattr_user_handler, &ext2_xattr_trusted_handler, #ifdef CONFIG_EXT2_FS_POSIX_ACL @@ -126,10 +126,10 @@ struct xattr_handler *ext2_xattr_handlers[] = { NULL }; -static inline struct xattr_handler * +static inline const struct xattr_handler * ext2_xattr_handler(int name_index) { - struct xattr_handler *handler = NULL; + const struct xattr_handler *handler = NULL; if (name_index > 0 && name_index < ARRAY_SIZE(ext2_xattr_handler_map)) handler = ext2_xattr_handler_map[name_index]; @@ -298,7 +298,7 @@ bad_block: ext2_error(inode->i_sb, "ext2_xattr_list", /* list the attribute names */ for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry); entry = EXT2_XATTR_NEXT(entry)) { - struct xattr_handler *handler = + const struct xattr_handler *handler = ext2_xattr_handler(entry->e_name_index); if (handler) { diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index bf8175b2ced..a1a1c218461 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h @@ -55,11 +55,11 @@ struct ext2_xattr_entry { # ifdef CONFIG_EXT2_FS_XATTR -extern struct xattr_handler ext2_xattr_user_handler; -extern struct xattr_handler ext2_xattr_trusted_handler; -extern struct xattr_handler ext2_xattr_acl_access_handler; -extern struct xattr_handler ext2_xattr_acl_default_handler; -extern struct xattr_handler ext2_xattr_security_handler; +extern const struct xattr_handler ext2_xattr_user_handler; +extern const struct xattr_handler ext2_xattr_trusted_handler; +extern const struct xattr_handler ext2_xattr_acl_access_handler; +extern const struct xattr_handler ext2_xattr_acl_default_handler; +extern const struct xattr_handler ext2_xattr_security_handler; extern ssize_t ext2_listxattr(struct dentry *, char *, size_t); @@ -72,7 +72,7 @@ extern void ext2_xattr_put_super(struct super_block *); extern int init_ext2_xattr(void); extern void exit_ext2_xattr(void); -extern struct xattr_handler *ext2_xattr_handlers[]; +extern const struct xattr_handler *ext2_xattr_handlers[]; # else /* CONFIG_EXT2_FS_XATTR */ diff --git a/fs/ext2/xattr_security.c b/fs/ext2/xattr_security.c index b118c6383c6..3004e15d5da 100644 --- a/fs/ext2/xattr_security.c +++ b/fs/ext2/xattr_security.c @@ -67,7 +67,7 @@ ext2_init_security(struct inode *inode, struct inode *dir) return err; } -struct xattr_handler ext2_xattr_security_handler = { +const struct xattr_handler ext2_xattr_security_handler = { .prefix = XATTR_SECURITY_PREFIX, .list = ext2_xattr_security_list, .get = ext2_xattr_security_get, diff --git a/fs/ext2/xattr_trusted.c b/fs/ext2/xattr_trusted.c index 2a26d71f477..667e46a8d62 100644 --- a/fs/ext2/xattr_trusted.c +++ b/fs/ext2/xattr_trusted.c @@ -50,7 +50,7 @@ ext2_xattr_trusted_set(struct dentry *dentry, const char *name, value, size, flags); } -struct xattr_handler ext2_xattr_trusted_handler = { +const struct xattr_handler ext2_xattr_trusted_handler = { .prefix = XATTR_TRUSTED_PREFIX, .list = ext2_xattr_trusted_list, .get = ext2_xattr_trusted_get, diff --git a/fs/ext2/xattr_user.c b/fs/ext2/xattr_user.c index 3f6caf3684b..099d20f4716 100644 --- a/fs/ext2/xattr_user.c +++ b/fs/ext2/xattr_user.c @@ -54,7 +54,7 @@ ext2_xattr_user_set(struct dentry *dentry, const char *name, name, value, size, flags); } -struct xattr_handler ext2_xattr_user_handler = { +const struct xattr_handler ext2_xattr_user_handler = { .prefix = XATTR_USER_PREFIX, .list = ext2_xattr_user_list, .get = ext2_xattr_user_get, -- cgit v1.2.3-70-g09d2 From ffba102d75a2e79ac9754841c6cf4a6dd6892c42 Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Thu, 4 Mar 2010 17:31:49 +0300 Subject: ext2: replace inode uid,gid,mode init with helper Acked-by: Jan Kara Signed-off-by: Dmitry Monakhov Signed-off-by: Al Viro --- fs/ext2/ialloc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'fs/ext2') diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index f0c5286f934..938dbc739d0 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -549,16 +549,12 @@ got: sb->s_dirt = 1; mark_buffer_dirty(bh2); - inode->i_uid = current_fsuid(); - if (test_opt (sb, GRPID)) + if (test_opt(sb, GRPID)) { + inode->i_mode = mode; + inode->i_uid = current_fsuid(); inode->i_gid = dir->i_gid; - else if (dir->i_mode & S_ISGID) { - inode->i_gid = dir->i_gid; - if (S_ISDIR(mode)) - mode |= S_ISGID; } else - inode->i_gid = current_fsgid(); - inode->i_mode = mode; + inode_init_owner(inode, dir, mode); inode->i_ino = ino; inode->i_blocks = 0; -- cgit v1.2.3-70-g09d2