Patch from Alex Tomas hi! here is the new version of the patch. changes since the last one: 1) new primitives ext2_set_bit_atomic and ext2_clear_bit_atomic have been introduced. primitives have additional parameter spinlock *, defined for every arch. each arch should use atomic test_and_set_bit/test_and_clear_bit or use ext2_set_bit and ext2_clear_bit serialized by this lock 2) each group has own spinlock, which is used for group counter modifications and may be used to implement ext2_set_bit_atomic/ext2_clear_bit_atomic 3) sb->s_free_blocks_count isn't used any more. ext2_statfs() and find_group_orlov() loop over groups to count free blocks 4) sb->s_free_blocks_count is recalculated at mount/umount/sync_super time in order to check consistency and to avoid fsck warnings 5) reserved blocks are distributed over all groups at mount time 6) ext2_new_block() tries to use non-reserved blocks and if it fails then tries to use reserved blocks 7) ext2_new_block() and ext2_free_blocks do not modify sb->s_free_blocks, therefore they do not call mark_buffer_dirty() for superblock's buffer_head. I think it may reduce I/O a bit fs/ext2/balloc.c | 183 +++++++++++++++++++++-------------------- fs/ext2/ialloc.c | 16 +-- fs/ext2/super.c | 56 ++++++++++-- include/asm-alpha/bitops.h | 2 include/asm-arm/bitops.h | 4 include/asm-cris/bitops.h | 2 include/asm-i386/bitops.h | 4 include/asm-ia64/bitops.h | 2 include/asm-m68k/bitops.h | 18 ++++ include/asm-m68knommu/bitops.h | 18 ++++ include/asm-mips/bitops.h | 20 ++++ include/asm-mips64/bitops.h | 20 ++++ include/asm-parisc/bitops.h | 4 include/asm-ppc/bitops.h | 2 include/asm-ppc64/bitops.h | 19 ++++ include/asm-s390/bitops.h | 4 include/asm-s390x/bitops.h | 4 include/asm-sh/bitops.h | 18 ++++ include/asm-sparc/bitops.h | 19 ++++ include/asm-sparc64/bitops.h | 2 include/asm-v850/bitops.h | 2 include/asm-x86_64/bitops.h | 4 include/linux/ext2_fs_sb.h | 8 + 23 files changed, 326 insertions(+), 105 deletions(-) diff -puN fs/ext2/balloc.c~ext2-no-lock_super fs/ext2/balloc.c --- 25/fs/ext2/balloc.c~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/fs/ext2/balloc.c 2003-03-16 14:50:59.000000000 -0800 @@ -94,69 +94,69 @@ error_out: return bh; } -static inline int reserve_blocks(struct super_block *sb, int count) +/* + * Set sb->s_dirt here because the superblock was "logically" altered. We + * need to recalculate its free blocks count and flush it out. + */ +static int +group_reserve_blocks(struct super_block *sb, struct ext2_bg_info *bgi, + struct ext2_group_desc *desc, struct buffer_head *bh, + int count, int use_reserve) { - struct ext2_sb_info * sbi = EXT2_SB(sb); - struct ext2_super_block * es = sbi->s_es; - unsigned free_blocks = le32_to_cpu(es->s_free_blocks_count); - unsigned root_blocks = le32_to_cpu(es->s_r_blocks_count); + struct ext2_sb_info *sbi = EXT2_SB(sb); + unsigned free_blocks; + unsigned root_blocks; + + spin_lock(&bgi->balloc_lock); + free_blocks = le16_to_cpu(desc->bg_free_blocks_count); if (free_blocks < count) count = free_blocks; + root_blocks = bgi->reserved; + + if (free_blocks < bgi->reserved && !use_reserve) { + /* don't use reserved blocks */ + spin_unlock(&bgi->balloc_lock); + return 0; + } - if (free_blocks < root_blocks + count && !capable(CAP_SYS_RESOURCE) && - sbi->s_resuid != current->fsuid && - (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { + if (free_blocks < bgi->reserved + count && + !capable(CAP_SYS_RESOURCE) && + sbi->s_resuid != current->fsuid && + (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { /* * We are too close to reserve and we are not privileged. * Can we allocate anything at all? */ - if (free_blocks > root_blocks) - count = free_blocks - root_blocks; - else + if (free_blocks > bgi->reserved) { + count = free_blocks - bgi->reserved; + } else { + spin_unlock(&bgi->balloc_lock); return 0; + } } + desc->bg_free_blocks_count = cpu_to_le16(free_blocks - count); - es->s_free_blocks_count = cpu_to_le32(free_blocks - count); - mark_buffer_dirty(sbi->s_sbh); + spin_unlock(&bgi->balloc_lock); sb->s_dirt = 1; + mark_buffer_dirty(bh); return count; } -static inline void release_blocks(struct super_block *sb, int count) +static void group_release_blocks(struct super_block *sb, + struct ext2_bg_info *bgi, struct ext2_group_desc *desc, + struct buffer_head *bh, int count) { if (count) { - struct ext2_sb_info * sbi = EXT2_SB(sb); - struct ext2_super_block * es = sbi->s_es; - unsigned free_blocks = le32_to_cpu(es->s_free_blocks_count); - es->s_free_blocks_count = cpu_to_le32(free_blocks + count); - mark_buffer_dirty(sbi->s_sbh); - sb->s_dirt = 1; - } -} - -static inline int group_reserve_blocks(struct ext2_group_desc *desc, - struct buffer_head *bh, int count) -{ - unsigned free_blocks; + unsigned free_blocks; - if (!desc->bg_free_blocks_count) - return 0; + spin_lock(&bgi->balloc_lock); - free_blocks = le16_to_cpu(desc->bg_free_blocks_count); - if (free_blocks < count) - count = free_blocks; - desc->bg_free_blocks_count = cpu_to_le16(free_blocks - count); - mark_buffer_dirty(bh); - return count; -} - -static inline void group_release_blocks(struct ext2_group_desc *desc, - struct buffer_head *bh, int count) -{ - if (count) { - unsigned free_blocks = le16_to_cpu(desc->bg_free_blocks_count); + free_blocks = le16_to_cpu(desc->bg_free_blocks_count); desc->bg_free_blocks_count = cpu_to_le16(free_blocks + count); + + spin_unlock(&bgi->balloc_lock); + sb->s_dirt = 1; mark_buffer_dirty(bh); } } @@ -172,12 +172,11 @@ void ext2_free_blocks (struct inode * in unsigned long i; unsigned long overflow; struct super_block * sb = inode->i_sb; + struct ext2_sb_info * sbi = EXT2_SB(sb); struct ext2_group_desc * desc; - struct ext2_super_block * es; + struct ext2_super_block * es = sbi->s_es; unsigned freed = 0, group_freed; - lock_super (sb); - es = EXT2_SB(sb)->s_es; if (block < le32_to_cpu(es->s_first_data_block) || block + count < block || block + count > le32_to_cpu(es->s_blocks_count)) { @@ -215,16 +214,17 @@ do_more: if (in_range (le32_to_cpu(desc->bg_block_bitmap), block, count) || in_range (le32_to_cpu(desc->bg_inode_bitmap), block, count) || in_range (block, le32_to_cpu(desc->bg_inode_table), - EXT2_SB(sb)->s_itb_per_group) || + sbi->s_itb_per_group) || in_range (block + count - 1, le32_to_cpu(desc->bg_inode_table), - EXT2_SB(sb)->s_itb_per_group)) + sbi->s_itb_per_group)) ext2_error (sb, "ext2_free_blocks", "Freeing blocks in system zones - " "Block = %lu, count = %lu", block, count); for (i = 0, group_freed = 0; i < count; i++) { - if (!ext2_clear_bit(bit + i, bitmap_bh->b_data)) + if (!ext2_clear_bit_atomic(&sbi->s_bgi[block_group].balloc_lock, + bit + i, (void *) bitmap_bh->b_data)) ext2_error (sb, "ext2_free_blocks", "bit already cleared for block %lu", block + i); @@ -236,7 +236,8 @@ do_more: if (sb->s_flags & MS_SYNCHRONOUS) sync_dirty_buffer(bitmap_bh); - group_release_blocks(desc, bh2, group_freed); + group_release_blocks(sb, &sbi->s_bgi[block_group], + desc, bh2, group_freed); freed += group_freed; if (overflow) { @@ -246,18 +247,18 @@ do_more: } error_return: brelse(bitmap_bh); - release_blocks(sb, freed); - unlock_super (sb); DQUOT_FREE_BLOCK(inode, freed); } -static int grab_block(char *map, unsigned size, int goal) +static int grab_block(spinlock_t *lock, char *map, unsigned size, int goal) { int k; char *p, *r; if (!ext2_test_bit(goal, map)) goto got_it; + +repeat: if (goal) { /* * The goal was occupied; search forward for a free @@ -297,7 +298,8 @@ static int grab_block(char *map, unsigne } return -1; got_it: - ext2_set_bit(goal, map); + if (ext2_set_bit_atomic(lock, goal, (void *) map)) + goto repeat; return goal; } @@ -309,17 +311,17 @@ got_it: * bitmap, and then for any free bit if that fails. * This function also updates quota and i_blocks field. */ -int ext2_new_block (struct inode * inode, unsigned long goal, - u32 * prealloc_count, u32 * prealloc_block, int * err) +int ext2_new_block(struct inode *inode, unsigned long goal, + u32 *prealloc_count, u32 *prealloc_block, int *err) { struct buffer_head *bitmap_bh = NULL; struct buffer_head *gdp_bh; /* bh2 */ struct ext2_group_desc *desc; int group_no; /* i */ int ret_block; /* j */ - int bit; /* k */ + int bit; /* k */ int target_block; /* tmp */ - int block = 0; + int block = 0, use_reserve = 0; struct super_block *sb = inode->i_sb; struct ext2_sb_info *sbi = EXT2_SB(sb); struct ext2_super_block *es = sbi->s_es; @@ -341,14 +343,7 @@ int ext2_new_block (struct inode * inode prealloc_goal--; dq_alloc = prealloc_goal + 1; - - lock_super (sb); - - es_alloc = reserve_blocks(sb, dq_alloc); - if (!es_alloc) { - *err = -ENOSPC; - goto out_unlock; - } + es_alloc = dq_alloc; ext2_debug ("goal=%lu.\n", goal); @@ -360,7 +355,8 @@ int ext2_new_block (struct inode * inode if (!desc) goto io_error; - group_alloc = group_reserve_blocks(desc, gdp_bh, es_alloc); + group_alloc = group_reserve_blocks(sb, &sbi->s_bgi[group_no], + desc, gdp_bh, es_alloc, 0); if (group_alloc) { ret_block = ((goal - le32_to_cpu(es->s_first_data_block)) % group_size); @@ -371,11 +367,13 @@ int ext2_new_block (struct inode * inode ext2_debug("goal is at %d:%d.\n", group_no, ret_block); - ret_block = grab_block(bitmap_bh->b_data, + ret_block = grab_block(&sbi->s_bgi[group_no].balloc_lock, + bitmap_bh->b_data, group_size, ret_block); if (ret_block >= 0) goto got_block; - group_release_blocks(desc, gdp_bh, group_alloc); + group_release_blocks(sb, &sbi->s_bgi[group_no], + desc, gdp_bh, group_alloc); group_alloc = 0; } @@ -385,6 +383,7 @@ int ext2_new_block (struct inode * inode * Now search the rest of the groups. We assume that * i and desc correctly point to the last group visited. */ +repeat: for (bit = 0; !group_alloc && bit < sbi->s_groups_count; bit++) { group_no++; @@ -393,7 +392,16 @@ int ext2_new_block (struct inode * inode desc = ext2_get_group_desc(sb, group_no, &gdp_bh); if (!desc) goto io_error; - group_alloc = group_reserve_blocks(desc, gdp_bh, es_alloc); + group_alloc = group_reserve_blocks(sb, &sbi->s_bgi[group_no], + desc, gdp_bh, es_alloc, use_reserve); + } + if (!use_reserve) { + /* first time we did not try to allocate + * reserved blocks. now it looks like + * no more non-reserved blocks left. we + * will try to allocate reserved blocks -bzzz */ + use_reserve = 1; + goto repeat; } if (!group_alloc) { *err = -ENOSPC; @@ -404,13 +412,11 @@ int ext2_new_block (struct inode * inode if (!bitmap_bh) goto io_error; - ret_block = grab_block(bitmap_bh->b_data, group_size, 0); + ret_block = grab_block(&sbi->s_bgi[group_no].balloc_lock, + bitmap_bh->b_data, group_size, 0); if (ret_block < 0) { - ext2_error (sb, "ext2_new_block", - "Free blocks count corrupted for block group %d", - group_no); group_alloc = 0; - goto io_error; + goto repeat; } got_block: @@ -452,7 +458,9 @@ got_block: unsigned n; for (n = 0; n < group_alloc && ++ret_block < group_size; n++) { - if (ext2_set_bit(ret_block, bitmap_bh->b_data)) + if (ext2_set_bit_atomic(&sbi->s_bgi[group_no].balloc_lock, + ret_block, + (void*) bitmap_bh->b_data)) break; } *prealloc_block = block + 1; @@ -471,10 +479,8 @@ got_block: *err = 0; out_release: - group_release_blocks(desc, gdp_bh, group_alloc); - release_blocks(sb, es_alloc); -out_unlock: - unlock_super (sb); + group_release_blocks(sb, &sbi->s_bgi[group_no], + desc, gdp_bh, group_alloc); DQUOT_FREE_BLOCK(inode, dq_alloc); out: brelse(bitmap_bh); @@ -487,11 +493,11 @@ io_error: unsigned long ext2_count_free_blocks (struct super_block * sb) { -#ifdef EXT2FS_DEBUG - struct ext2_super_block * es; - unsigned long desc_count, bitmap_count, x; struct ext2_group_desc * desc; + unsigned long desc_count = 0; int i; +#ifdef EXT2FS_DEBUG + unsigned long bitmap_count, x; lock_super (sb); es = EXT2_SB(sb)->s_es; @@ -519,13 +525,18 @@ unsigned long ext2_count_free_blocks (st unlock_super (sb); return bitmap_count; #else - return le32_to_cpu(EXT2_SB(sb)->s_es->s_free_blocks_count); + for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { + desc = ext2_get_group_desc (sb, i, NULL); + if (!desc) + continue; + desc_count += le16_to_cpu(desc->bg_free_blocks_count); + } + return desc_count; #endif } -static inline int block_in_use (unsigned long block, - struct super_block * sb, - unsigned char * map) +static inline int +block_in_use(unsigned long block, struct super_block *sb, unsigned char *map) { return ext2_test_bit ((block - le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb), map); diff -puN fs/ext2/ialloc.c~ext2-no-lock_super fs/ext2/ialloc.c --- 25/fs/ext2/ialloc.c~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/fs/ext2/ialloc.c 2003-03-16 14:50:11.000000000 -0800 @@ -278,7 +278,8 @@ static int find_group_orlov(struct super int ngroups = sbi->s_groups_count; int inodes_per_group = EXT2_INODES_PER_GROUP(sb); int avefreei = le32_to_cpu(es->s_free_inodes_count) / ngroups; - int avefreeb = le32_to_cpu(es->s_free_blocks_count) / ngroups; + int free_blocks = ext2_count_free_blocks(sb); + int avefreeb = free_blocks / ngroups; int blocks_per_dir; int ndirs = sbi->s_dir_count; int max_debt, max_dirs, min_blocks, min_inodes; @@ -320,8 +321,7 @@ static int find_group_orlov(struct super goto fallback; } - blocks_per_dir = (le32_to_cpu(es->s_blocks_count) - - le32_to_cpu(es->s_free_blocks_count)) / ndirs; + blocks_per_dir = (le32_to_cpu(es->s_blocks_count) - free_blocks) / ndirs; max_dirs = ndirs / ngroups + inodes_per_group / 16; min_inodes = avefreei - inodes_per_group / 4; @@ -340,7 +340,7 @@ static int find_group_orlov(struct super desc = ext2_get_group_desc (sb, group, &bh); if (!desc || !desc->bg_free_inodes_count) continue; - if (sbi->s_debts[group] >= max_debt) + if (sbi->s_bgi[group].debts >= max_debt) continue; if (le16_to_cpu(desc->bg_used_dirs_count) >= max_dirs) continue; @@ -501,11 +501,11 @@ repeat: cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1); if (S_ISDIR(mode)) { - if (EXT2_SB(sb)->s_debts[group] < 255) - EXT2_SB(sb)->s_debts[group]++; + if (EXT2_SB(sb)->s_bgi[group].debts < 255) + EXT2_SB(sb)->s_bgi[group].debts++; } else { - if (EXT2_SB(sb)->s_debts[group]) - EXT2_SB(sb)->s_debts[group]--; + if (EXT2_SB(sb)->s_bgi[group].debts) + EXT2_SB(sb)->s_bgi[group].debts--; } mark_buffer_dirty(EXT2_SB(sb)->s_sbh); diff -puN fs/ext2/super.c~ext2-no-lock_super fs/ext2/super.c --- 25/fs/ext2/super.c~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/fs/ext2/super.c 2003-03-16 14:50:11.000000000 -0800 @@ -141,7 +141,7 @@ static void ext2_put_super (struct super if (sbi->s_group_desc[i]) brelse (sbi->s_group_desc[i]); kfree(sbi->s_group_desc); - kfree(sbi->s_debts); + kfree(sbi->s_bgi); brelse (sbi->s_sbh); sb->s_fs_info = NULL; kfree(sbi); @@ -464,8 +464,11 @@ static int ext2_check_descriptors (struc int i; int desc_block = 0; struct ext2_sb_info *sbi = EXT2_SB(sb); - unsigned long block = le32_to_cpu(sbi->s_es->s_first_data_block); + struct ext2_super_block * es = sbi->s_es; + unsigned long block = le32_to_cpu(es->s_first_data_block); struct ext2_group_desc * gdp = NULL; + unsigned int total_free = 0, free; + unsigned int reserved = le32_to_cpu(es->s_r_blocks_count); ext2_debug ("Checking group descriptors"); @@ -504,6 +507,27 @@ static int ext2_check_descriptors (struc block += EXT2_BLOCKS_PER_GROUP(sb); gdp++; } + + /* restore free blocks counter in SB -bzzz */ + es->s_free_blocks_count = total_free = ext2_count_free_blocks(sb); + + /* distribute reserved blocks over groups -bzzz */ + for(i = sbi->s_groups_count-1; reserved && total_free && i >= 0; i--) { + gdp = ext2_get_group_desc (sb, i, NULL); + if (!gdp) { + ext2_error (sb, "ext2_check_descriptors", + "cant get descriptor for group %d", i); + return 0; + } + + free = le16_to_cpu(gdp->bg_free_blocks_count); + if (free > reserved) + free = reserved; + sbi->s_bgi[i].reserved = free; + reserved -= free; + total_free -= free; + } + return 1; } @@ -769,13 +793,17 @@ static int ext2_fill_super(struct super_ printk ("EXT2-fs: not enough memory\n"); goto failed_mount; } - sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->s_debts), + sbi->s_bgi = kmalloc(sbi->s_groups_count*sizeof(struct ext2_bg_info), GFP_KERNEL); - if (!sbi->s_debts) { + if (!sbi->s_bgi) { printk ("EXT2-fs: not enough memory\n"); goto failed_mount_group_desc; } - memset(sbi->s_debts, 0, sbi->s_groups_count * sizeof(*sbi->s_debts)); + for (i = 0; i < sbi->s_groups_count; i++) { + sbi->s_bgi[i].debts = 0; + sbi->s_bgi[i].reserved = 0; + spin_lock_init(&sbi->s_bgi[i].balloc_lock); + } for (i = 0; i < db_count; i++) { block = descriptor_loc(sb, logic_sb_block, i); sbi->s_group_desc[i] = sb_bread(sb, block); @@ -820,8 +848,8 @@ failed_mount2: brelse(sbi->s_group_desc[i]); failed_mount_group_desc: kfree(sbi->s_group_desc); - if (sbi->s_debts) - kfree(sbi->s_debts); + if (sbi->s_bgi) + kfree(sbi->s_bgi); failed_mount: brelse(bh); failed_sbi: @@ -840,6 +868,7 @@ static void ext2_commit_super (struct su static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es) { + es->s_free_blocks_count = cpu_to_le32(ext2_count_free_blocks(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); @@ -868,6 +897,7 @@ void ext2_write_super (struct super_bloc ext2_debug ("setting valid to 0\n"); es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT2_VALID_FS); + es->s_free_blocks_count = cpu_to_le32(ext2_count_free_blocks(sb)); es->s_mtime = cpu_to_le32(get_seconds()); ext2_sync_super(sb, es); } else @@ -929,7 +959,8 @@ static int ext2_remount (struct super_bl static int ext2_statfs (struct super_block * sb, struct statfs * buf) { struct ext2_sb_info *sbi = EXT2_SB(sb); - unsigned long overhead; + unsigned long overhead, total_free = 0; + struct ext2_group_desc *desc; int i; if (test_opt (sb, MINIX_DF)) @@ -950,9 +981,14 @@ static int ext2_statfs (struct super_blo * block group descriptors. If the sparse superblocks * feature is turned on, then not all groups have this. */ - for (i = 0; i < sbi->s_groups_count; i++) + for (i = 0; i < sbi->s_groups_count; i++) { overhead += ext2_bg_has_super(sb, i) + ext2_bg_num_gdb(sb, i); + + /* sum total free blocks -bzzz */ + desc = ext2_get_group_desc (sb, i, NULL); + total_free += le16_to_cpu(desc->bg_free_blocks_count); + } /* * Every block group has an inode bitmap, a block @@ -965,7 +1001,7 @@ static int ext2_statfs (struct super_blo buf->f_type = EXT2_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = le32_to_cpu(sbi->s_es->s_blocks_count) - overhead; - buf->f_bfree = ext2_count_free_blocks (sb); + buf->f_bfree = total_free; buf->f_bavail = buf->f_bfree - le32_to_cpu(sbi->s_es->s_r_blocks_count); if (buf->f_bfree < le32_to_cpu(sbi->s_es->s_r_blocks_count)) buf->f_bavail = 0; diff -puN include/asm-alpha/bitops.h~ext2-no-lock_super include/asm-alpha/bitops.h --- 25/include/asm-alpha/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-alpha/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -487,7 +487,9 @@ sched_find_first_bit(unsigned long b[3]) #define ext2_set_bit __test_and_set_bit +#define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) #define ext2_clear_bit __test_and_clear_bit +#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit diff -puN include/asm-arm/bitops.h~ext2-no-lock_super include/asm-arm/bitops.h --- 25/include/asm-arm/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-arm/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -357,8 +357,12 @@ static inline int sched_find_first_bit(u */ #define ext2_set_bit(nr,p) \ __test_and_set_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) +#define ext2_set_bit_atomic(lock,nr,p) \ + test_and_set_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_clear_bit(nr,p) \ __test_and_clear_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) +#define ext2_clear_bit_atomic(lock,nr,p) \ + test_and_clear_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_test_bit(nr,p) \ __test_bit(WORD_BITOFF_TO_LE(nr), (unsigned long *)(p)) #define ext2_find_first_zero_bit(p,sz) \ diff -puN include/asm-cris/bitops.h~ext2-no-lock_super include/asm-cris/bitops.h --- 25/include/asm-cris/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-cris/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -360,7 +360,9 @@ static inline int find_next_zero_bit (vo #define hweight8(x) generic_hweight8(x) #define ext2_set_bit test_and_set_bit +#define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) #define ext2_clear_bit test_and_clear_bit +#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit diff -puN include/asm-i386/bitops.h~ext2-no-lock_super include/asm-i386/bitops.h --- 25/include/asm-i386/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-i386/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -479,8 +479,12 @@ static __inline__ int ffs(int x) #define ext2_set_bit(nr,addr) \ __test_and_set_bit((nr),(unsigned long*)addr) +#define ext2_set_bit_atomic(lock,nr,addr) \ + test_and_set_bit((nr),(unsigned long*)addr) #define ext2_clear_bit(nr, addr) \ __test_and_clear_bit((nr),(unsigned long*)addr) +#define ext2_clear_bit_atomic(lock,nr, addr) \ + test_and_clear_bit((nr),(unsigned long*)addr) #define ext2_test_bit(nr, addr) test_bit((nr),(unsigned long*)addr) #define ext2_find_first_zero_bit(addr, size) \ find_first_zero_bit((unsigned long*)addr, size) diff -puN include/asm-ia64/bitops.h~ext2-no-lock_super include/asm-ia64/bitops.h --- 25/include/asm-ia64/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-ia64/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -453,7 +453,9 @@ find_next_bit (void *addr, unsigned long #define __clear_bit(nr, addr) clear_bit(nr, addr) #define ext2_set_bit test_and_set_bit +#define ext2_set_atomic(l,n,a) test_and_set_bit(n,a) #define ext2_clear_bit test_and_clear_bit +#define ext2_clear_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit diff -puN include/asm-m68k/bitops.h~ext2-no-lock_super include/asm-m68k/bitops.h --- 25/include/asm-m68k/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-m68k/bitops.h 2003-03-16 14:50:59.000000000 -0800 @@ -365,6 +365,24 @@ ext2_clear_bit (int nr, volatile void *v return retval; } +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + extern __inline__ int ext2_test_bit (int nr, const volatile void *vaddr) { diff -puN include/asm-m68knommu/bitops.h~ext2-no-lock_super include/asm-m68knommu/bitops.h --- 25/include/asm-m68knommu/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-m68knommu/bitops.h 2003-03-16 14:50:59.000000000 -0800 @@ -402,6 +402,24 @@ extern __inline__ int ext2_clear_bit(int return retval; } +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + extern __inline__ int ext2_test_bit(int nr, const volatile void * addr) { int mask; diff -puN include/asm-mips64/bitops.h~ext2-no-lock_super include/asm-mips64/bitops.h --- 25/include/asm-mips64/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-mips64/bitops.h 2003-03-16 14:50:58.000000000 -0800 @@ -531,6 +531,24 @@ ext2_clear_bit(int nr, void * addr) return retval; } +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + extern inline int ext2_test_bit(int nr, const void * addr) { @@ -599,7 +617,9 @@ found_middle: /* Native ext2 byte ordering, just collapse using defines. */ #define ext2_set_bit(nr, addr) test_and_set_bit((nr), (addr)) +#define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr), (addr)) #define ext2_clear_bit(nr, addr) test_and_clear_bit((nr), (addr)) +#define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr), (addr)) #define ext2_test_bit(nr, addr) test_bit((nr), (addr)) #define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) #define ext2_find_next_zero_bit(addr, size, offset) \ diff -puN include/asm-mips/bitops.h~ext2-no-lock_super include/asm-mips/bitops.h --- 25/include/asm-mips/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-mips/bitops.h 2003-03-16 14:50:58.000000000 -0800 @@ -824,6 +824,24 @@ extern __inline__ int ext2_clear_bit(int return retval; } +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + extern __inline__ int ext2_test_bit(int nr, const void * addr) { int mask; @@ -890,7 +908,9 @@ found_middle: /* Native ext2 byte ordering, just collapse using defines. */ #define ext2_set_bit(nr, addr) test_and_set_bit((nr), (addr)) +#define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr), (addr)) #define ext2_clear_bit(nr, addr) test_and_clear_bit((nr), (addr)) +#define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr), (addr)) #define ext2_test_bit(nr, addr) test_bit((nr), (addr)) #define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size)) #define ext2_find_next_zero_bit(addr, size, offset) \ diff -puN include/asm-parisc/bitops.h~ext2-no-lock_super include/asm-parisc/bitops.h --- 25/include/asm-parisc/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-parisc/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -389,10 +389,14 @@ found_middle: */ #ifdef __LP64__ #define ext2_set_bit(nr, addr) test_and_set_bit((nr) ^ 0x38, addr) +#define ext2_set_bit_atomic(l,nr,addr) test_and_set_bit((nr) ^ 0x38, addr) #define ext2_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 0x38, addr) +#define ext2_clear_bit_atomic(l,nr,addr) test_and_clear_bit((nr) ^ 0x38, addr) #else #define ext2_set_bit(nr, addr) test_and_set_bit((nr) ^ 0x18, addr) +#define ext2_set_bit_atomic(l,nr,addr) test_and_set_bit((nr) ^ 0x18, addr) #define ext2_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 0x18, addr) +#define ext2_clear_bit_atomic(l,nr,addr) test_and_clear_bit((nr) ^ 0x18, addr) #endif #endif /* __KERNEL__ */ diff -puN include/asm-ppc64/bitops.h~ext2-no-lock_super include/asm-ppc64/bitops.h --- 25/include/asm-ppc64/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-ppc64/bitops.h 2003-03-16 14:50:58.000000000 -0800 @@ -338,6 +338,25 @@ static __inline__ int __test_and_clear_l __test_and_set_le_bit((nr),(unsigned long*)addr) #define ext2_clear_bit(nr, addr) \ __test_and_clear_le_bit((nr),(unsigned long*)addr) + +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + #define ext2_test_bit(nr, addr) test_le_bit((nr),(unsigned long*)addr) #define ext2_find_first_zero_bit(addr, size) \ find_first_zero_le_bit((unsigned long*)addr, size) diff -puN include/asm-ppc/bitops.h~ext2-no-lock_super include/asm-ppc/bitops.h --- 25/include/asm-ppc/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-ppc/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -392,7 +392,9 @@ found_middle: #define ext2_set_bit(nr, addr) __test_and_set_bit((nr) ^ 0x18, (unsigned long *)(addr)) +#define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr) ^ 0x18, (unsigned long *)(addr)) #define ext2_clear_bit(nr, addr) __test_and_clear_bit((nr) ^ 0x18, (unsigned long *)(addr)) +#define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr) ^ 0x18, (unsigned long *)(addr)) static __inline__ int ext2_test_bit(int nr, __const__ void * addr) { diff -puN include/asm-s390/bitops.h~ext2-no-lock_super include/asm-s390/bitops.h --- 25/include/asm-s390/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-s390/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -805,8 +805,12 @@ extern __inline__ int fls(int x) #define ext2_set_bit(nr, addr) \ test_and_set_bit((nr)^24, (unsigned long *)addr) +#define ext2_set_bit_atomic(lock, nr, addr) \ + test_and_set_bit((nr)^24, (unsigned long *)addr) #define ext2_clear_bit(nr, addr) \ test_and_clear_bit((nr)^24, (unsigned long *)addr) +#define ext2_clear_bit_atomic(lock, nr, addr) \ + test_and_clear_bit((nr)^24, (unsigned long *)addr) #define ext2_test_bit(nr, addr) \ test_bit((nr)^24, (unsigned long *)addr) diff -puN include/asm-s390x/bitops.h~ext2-no-lock_super include/asm-s390x/bitops.h --- 25/include/asm-s390x/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-s390x/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -838,8 +838,12 @@ extern __inline__ int fls(int x) #define ext2_set_bit(nr, addr) \ test_and_set_bit((nr)^56, (unsigned long *)addr) +#define ext2_set_bit_atomic(lock, nr, addr) \ + test_and_set_bit((nr)^56, (unsigned long *)addr) #define ext2_clear_bit(nr, addr) \ test_and_clear_bit((nr)^56, (unsigned long *)addr) +#define ext2_clear_bit_atomic(lock, nr, addr) \ + test_and_clear_bit((nr)^56, (unsigned long *)addr) #define ext2_test_bit(nr, addr) \ test_bit((nr)^56, (unsigned long *)addr) diff -puN include/asm-sh/bitops.h~ext2-no-lock_super include/asm-sh/bitops.h --- 25/include/asm-sh/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-sh/bitops.h 2003-03-16 14:50:59.000000000 -0800 @@ -344,6 +344,24 @@ found_middle: } #endif +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + /* Bitmap functions for the minix filesystem. */ #define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr) #define minix_set_bit(nr,addr) set_bit(nr,addr) diff -puN include/asm-sparc64/bitops.h~ext2-no-lock_super include/asm-sparc64/bitops.h --- 25/include/asm-sparc64/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-sparc64/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -351,7 +351,9 @@ found_middle: #ifdef __KERNEL__ #define ext2_set_bit(nr,addr) test_and_set_le_bit((nr),(unsigned long *)(addr)) +#define ext2_set_bit_atomic(lock,nr,addr) test_and_set_le_bit((nr),(unsigned long *)(addr)) #define ext2_clear_bit(nr,addr) test_and_clear_le_bit((nr),(unsigned long *)(addr)) +#define ext2_clear_bit_atomic(lock,nr,addr) test_and_clear_le_bit((nr),(unsigned long *)(addr)) #define ext2_test_bit(nr,addr) test_le_bit((nr),(unsigned long *)(addr)) #define ext2_find_first_zero_bit(addr, size) \ find_first_zero_le_bit((unsigned long *)(addr), (size)) diff -puN include/asm-sparc/bitops.h~ext2-no-lock_super include/asm-sparc/bitops.h --- 25/include/asm-sparc/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-sparc/bitops.h 2003-03-16 14:50:58.000000000 -0800 @@ -455,6 +455,25 @@ found_middle: #define ext2_set_bit __test_and_set_le_bit #define ext2_clear_bit __test_and_clear_le_bit + +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + #define ext2_test_bit test_le_bit #define ext2_find_first_zero_bit find_first_zero_le_bit #define ext2_find_next_zero_bit find_next_zero_le_bit diff -puN include/asm-v850/bitops.h~ext2-no-lock_super include/asm-v850/bitops.h --- 25/include/asm-v850/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-v850/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -252,7 +252,9 @@ static inline int sched_find_first_bit(u #define hweight8(x) generic_hweight8 (x) #define ext2_set_bit test_and_set_bit +#define ext2_set_bit_atomic(l,n,a) test_and_set_bit(n,a) #define ext2_clear_bit test_and_clear_bit +#define ext2_clear_bit_atomic(l,n,a) test_and_clear_bit(n,a) #define ext2_test_bit test_bit #define ext2_find_first_zero_bit find_first_zero_bit #define ext2_find_next_zero_bit find_next_zero_bit diff -puN include/asm-x86_64/bitops.h~ext2-no-lock_super include/asm-x86_64/bitops.h --- 25/include/asm-x86_64/bitops.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/asm-x86_64/bitops.h 2003-03-16 14:49:52.000000000 -0800 @@ -487,8 +487,12 @@ static __inline__ int ffs(int x) #define ext2_set_bit(nr,addr) \ __test_and_set_bit((nr),(unsigned long*)addr) +#define ext2_set_bit_atomic(lock,nr,addr) \ + test_and_set_bit((nr),(unsigned long*)addr) #define ext2_clear_bit(nr, addr) \ __test_and_clear_bit((nr),(unsigned long*)addr) +#define ext2_clear_bit_atomic(lock,nr,addr) \ + test_and_clear_bit((nr),(unsigned long*)addr) #define ext2_test_bit(nr, addr) test_bit((nr),(unsigned long*)addr) #define ext2_find_first_zero_bit(addr, size) \ find_first_zero_bit((unsigned long*)addr, size) diff -puN include/linux/ext2_fs_sb.h~ext2-no-lock_super include/linux/ext2_fs_sb.h --- 25/include/linux/ext2_fs_sb.h~ext2-no-lock_super 2003-03-16 14:49:52.000000000 -0800 +++ 25-akpm/include/linux/ext2_fs_sb.h 2003-03-16 14:50:11.000000000 -0800 @@ -16,6 +16,12 @@ #ifndef _LINUX_EXT2_FS_SB #define _LINUX_EXT2_FS_SB +struct ext2_bg_info { + u8 debts; + spinlock_t balloc_lock; + unsigned int reserved; +} ____cacheline_aligned_in_smp; + /* * second extended-fs super-block data in memory */ @@ -44,7 +50,7 @@ struct ext2_sb_info { int s_first_ino; u32 s_next_generation; unsigned long s_dir_count; - u8 *s_debts; + struct ext2_bg_info *s_bgi; }; #endif /* _LINUX_EXT2_FS_SB */ _