Commit 8df9675f authored by Theodore Ts'o's avatar Theodore Ts'o

ext4: Avoid races caused by on-line resizing and SMP memory reordering

Ext4's on-line resizing adds a new block group and then, only at the
last step adjusts s_groups_count.  However, it's possible on SMP
systems that another CPU could see the updated the s_group_count and
not see the newly initialized data structures for the just-added block
group.  For this reason, it's important to insert a SMP read barrier
after reading s_groups_count and before reading any (for example) the
new block group descriptors allowed by the increased value of
s_groups_count.

Unfortunately, we rather blatently violate this locking protocol
documented in fs/ext4/resize.c.  Fortunately, (1) on-line resizes
happen relatively rarely, and (2) it seems rare that the filesystem
code will immediately try to use just-added block group before any
memory ordering issues resolve themselves.  So apparently problems
here are relatively hard to hit, since ext3 has been vulnerable to the
same issue for years with no one apparently complaining.
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
parent 9ca92389
...@@ -88,6 +88,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ...@@ -88,6 +88,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
ext4_group_t block_group, struct ext4_group_desc *gdp) ext4_group_t block_group, struct ext4_group_desc *gdp)
{ {
int bit, bit_max; int bit, bit_max;
ext4_group_t ngroups = ext4_get_groups_count(sb);
unsigned free_blocks, group_blocks; unsigned free_blocks, group_blocks;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
...@@ -123,7 +124,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ...@@ -123,7 +124,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
bit_max += ext4_bg_num_gdb(sb, block_group); bit_max += ext4_bg_num_gdb(sb, block_group);
} }
if (block_group == sbi->s_groups_count - 1) { if (block_group == ngroups - 1) {
/* /*
* Even though mke2fs always initialize first and last group * Even though mke2fs always initialize first and last group
* if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need
...@@ -131,7 +132,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ...@@ -131,7 +132,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
*/ */
group_blocks = ext4_blocks_count(sbi->s_es) - group_blocks = ext4_blocks_count(sbi->s_es) -
le32_to_cpu(sbi->s_es->s_first_data_block) - le32_to_cpu(sbi->s_es->s_first_data_block) -
(EXT4_BLOCKS_PER_GROUP(sb) * (sbi->s_groups_count - 1)); (EXT4_BLOCKS_PER_GROUP(sb) * (ngroups - 1));
} else { } else {
group_blocks = EXT4_BLOCKS_PER_GROUP(sb); group_blocks = EXT4_BLOCKS_PER_GROUP(sb);
} }
...@@ -205,18 +206,18 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, ...@@ -205,18 +206,18 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
{ {
unsigned int group_desc; unsigned int group_desc;
unsigned int offset; unsigned int offset;
ext4_group_t ngroups = ext4_get_groups_count(sb);
struct ext4_group_desc *desc; struct ext4_group_desc *desc;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
if (block_group >= sbi->s_groups_count) { if (block_group >= ngroups) {
ext4_error(sb, "ext4_get_group_desc", ext4_error(sb, "ext4_get_group_desc",
"block_group >= groups_count - " "block_group >= groups_count - "
"block_group = %u, groups_count = %u", "block_group = %u, groups_count = %u",
block_group, sbi->s_groups_count); block_group, ngroups);
return NULL; return NULL;
} }
smp_rmb();
group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb); group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1); offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
...@@ -665,7 +666,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) ...@@ -665,7 +666,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
ext4_fsblk_t desc_count; ext4_fsblk_t desc_count;
struct ext4_group_desc *gdp; struct ext4_group_desc *gdp;
ext4_group_t i; ext4_group_t i;
ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; ext4_group_t ngroups = ext4_get_groups_count(sb);
#ifdef EXT4FS_DEBUG #ifdef EXT4FS_DEBUG
struct ext4_super_block *es; struct ext4_super_block *es;
ext4_fsblk_t bitmap_count; ext4_fsblk_t bitmap_count;
...@@ -677,7 +678,6 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) ...@@ -677,7 +678,6 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
bitmap_count = 0; bitmap_count = 0;
gdp = NULL; gdp = NULL;
smp_rmb();
for (i = 0; i < ngroups; i++) { for (i = 0; i < ngroups; i++) {
gdp = ext4_get_group_desc(sb, i, NULL); gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp) if (!gdp)
...@@ -700,7 +700,6 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) ...@@ -700,7 +700,6 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
return bitmap_count; return bitmap_count;
#else #else
desc_count = 0; desc_count = 0;
smp_rmb();
for (i = 0; i < ngroups; i++) { for (i = 0; i < ngroups; i++) {
gdp = ext4_get_group_desc(sb, i, NULL); gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp) if (!gdp)
......
...@@ -1228,6 +1228,18 @@ struct ext4_group_info *ext4_get_group_info(struct super_block *sb, ...@@ -1228,6 +1228,18 @@ struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
return grp_info[indexv][indexh]; return grp_info[indexv][indexh];
} }
/*
* Reading s_groups_count requires using smp_rmb() afterwards. See
* the locking protocol documented in the comments of ext4_group_add()
* in resize.c
*/
static inline ext4_group_t ext4_get_groups_count(struct super_block *sb)
{
ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count;
smp_rmb();
return ngroups;
}
static inline ext4_group_t ext4_flex_group(struct ext4_sb_info *sbi, static inline ext4_group_t ext4_flex_group(struct ext4_sb_info *sbi,
ext4_group_t block_group) ext4_group_t block_group)
......
...@@ -316,7 +316,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) ...@@ -316,7 +316,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
static int find_group_dir(struct super_block *sb, struct inode *parent, static int find_group_dir(struct super_block *sb, struct inode *parent,
ext4_group_t *best_group) ext4_group_t *best_group)
{ {
ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; ext4_group_t ngroups = ext4_get_groups_count(sb);
unsigned int freei, avefreei; unsigned int freei, avefreei;
struct ext4_group_desc *desc, *best_desc = NULL; struct ext4_group_desc *desc, *best_desc = NULL;
ext4_group_t group; ext4_group_t group;
...@@ -353,7 +353,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, ...@@ -353,7 +353,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent,
struct flex_groups *flex_group = sbi->s_flex_groups; struct flex_groups *flex_group = sbi->s_flex_groups;
ext4_group_t parent_group = EXT4_I(parent)->i_block_group; ext4_group_t parent_group = EXT4_I(parent)->i_block_group;
ext4_group_t parent_fbg_group = ext4_flex_group(sbi, parent_group); ext4_group_t parent_fbg_group = ext4_flex_group(sbi, parent_group);
ext4_group_t ngroups = sbi->s_groups_count; ext4_group_t ngroups = ext4_get_groups_count(sb);
int flex_size = ext4_flex_bg_size(sbi); int flex_size = ext4_flex_bg_size(sbi);
ext4_group_t best_flex = parent_fbg_group; ext4_group_t best_flex = parent_fbg_group;
int blocks_per_flex = sbi->s_blocks_per_group * flex_size; int blocks_per_flex = sbi->s_blocks_per_group * flex_size;
...@@ -362,7 +362,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, ...@@ -362,7 +362,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent,
ext4_group_t n_fbg_groups; ext4_group_t n_fbg_groups;
ext4_group_t i; ext4_group_t i;
n_fbg_groups = (sbi->s_groups_count + flex_size - 1) >> n_fbg_groups = (ngroups + flex_size - 1) >>
sbi->s_log_groups_per_flex; sbi->s_log_groups_per_flex;
find_close_to_parent: find_close_to_parent:
...@@ -478,20 +478,21 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, ...@@ -478,20 +478,21 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent,
{ {
ext4_group_t parent_group = EXT4_I(parent)->i_block_group; ext4_group_t parent_group = EXT4_I(parent)->i_block_group;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_group_t ngroups = sbi->s_groups_count; ext4_group_t real_ngroups = ext4_get_groups_count(sb);
int inodes_per_group = EXT4_INODES_PER_GROUP(sb); int inodes_per_group = EXT4_INODES_PER_GROUP(sb);
unsigned int freei, avefreei; unsigned int freei, avefreei;
ext4_fsblk_t freeb, avefreeb; ext4_fsblk_t freeb, avefreeb;
unsigned int ndirs; unsigned int ndirs;
int max_dirs, min_inodes; int max_dirs, min_inodes;
ext4_grpblk_t min_blocks; ext4_grpblk_t min_blocks;
ext4_group_t i, grp, g; ext4_group_t i, grp, g, ngroups;
struct ext4_group_desc *desc; struct ext4_group_desc *desc;
struct orlov_stats stats; struct orlov_stats stats;
int flex_size = ext4_flex_bg_size(sbi); int flex_size = ext4_flex_bg_size(sbi);
ngroups = real_ngroups;
if (flex_size > 1) { if (flex_size > 1) {
ngroups = (ngroups + flex_size - 1) >> ngroups = (real_ngroups + flex_size - 1) >>
sbi->s_log_groups_per_flex; sbi->s_log_groups_per_flex;
parent_group >>= sbi->s_log_groups_per_flex; parent_group >>= sbi->s_log_groups_per_flex;
} }
...@@ -543,7 +544,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, ...@@ -543,7 +544,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent,
*/ */
grp *= flex_size; grp *= flex_size;
for (i = 0; i < flex_size; i++) { for (i = 0; i < flex_size; i++) {
if (grp+i >= sbi->s_groups_count) if (grp+i >= real_ngroups)
break; break;
desc = ext4_get_group_desc(sb, grp+i, NULL); desc = ext4_get_group_desc(sb, grp+i, NULL);
if (desc && ext4_free_inodes_count(sb, desc)) { if (desc && ext4_free_inodes_count(sb, desc)) {
...@@ -583,7 +584,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, ...@@ -583,7 +584,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent,
} }
fallback: fallback:
ngroups = sbi->s_groups_count; ngroups = real_ngroups;
avefreei = freei / ngroups; avefreei = freei / ngroups;
fallback_retry: fallback_retry:
parent_group = EXT4_I(parent)->i_block_group; parent_group = EXT4_I(parent)->i_block_group;
...@@ -613,9 +614,8 @@ static int find_group_other(struct super_block *sb, struct inode *parent, ...@@ -613,9 +614,8 @@ static int find_group_other(struct super_block *sb, struct inode *parent,
ext4_group_t *group, int mode) ext4_group_t *group, int mode)
{ {
ext4_group_t parent_group = EXT4_I(parent)->i_block_group; ext4_group_t parent_group = EXT4_I(parent)->i_block_group;
ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; ext4_group_t i, last, ngroups = ext4_get_groups_count(sb);
struct ext4_group_desc *desc; struct ext4_group_desc *desc;
ext4_group_t i, last;
int flex_size = ext4_flex_bg_size(EXT4_SB(sb)); int flex_size = ext4_flex_bg_size(EXT4_SB(sb));
/* /*
...@@ -799,11 +799,10 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode) ...@@ -799,11 +799,10 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode)
struct super_block *sb; struct super_block *sb;
struct buffer_head *inode_bitmap_bh = NULL; struct buffer_head *inode_bitmap_bh = NULL;
struct buffer_head *group_desc_bh; struct buffer_head *group_desc_bh;
ext4_group_t group = 0; ext4_group_t ngroups, group = 0;
unsigned long ino = 0; unsigned long ino = 0;
struct inode *inode; struct inode *inode;
struct ext4_group_desc *gdp = NULL; struct ext4_group_desc *gdp = NULL;
struct ext4_super_block *es;
struct ext4_inode_info *ei; struct ext4_inode_info *ei;
struct ext4_sb_info *sbi; struct ext4_sb_info *sbi;
int ret2, err = 0; int ret2, err = 0;
...@@ -818,15 +817,14 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode) ...@@ -818,15 +817,14 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode)
return ERR_PTR(-EPERM); return ERR_PTR(-EPERM);
sb = dir->i_sb; sb = dir->i_sb;
ngroups = ext4_get_groups_count(sb);
trace_mark(ext4_request_inode, "dev %s dir %lu mode %d", sb->s_id, trace_mark(ext4_request_inode, "dev %s dir %lu mode %d", sb->s_id,
dir->i_ino, mode); dir->i_ino, mode);
inode = new_inode(sb); inode = new_inode(sb);
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
ei = EXT4_I(inode); ei = EXT4_I(inode);
sbi = EXT4_SB(sb); sbi = EXT4_SB(sb);
es = sbi->s_es;
if (sbi->s_log_groups_per_flex && test_opt(sb, OLDALLOC)) { if (sbi->s_log_groups_per_flex && test_opt(sb, OLDALLOC)) {
ret2 = find_group_flex(sb, dir, &group); ret2 = find_group_flex(sb, dir, &group);
...@@ -856,7 +854,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode) ...@@ -856,7 +854,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode)
if (ret2 == -1) if (ret2 == -1)
goto out; goto out;
for (i = 0; i < sbi->s_groups_count; i++) { for (i = 0; i < ngroups; i++) {
err = -EIO; err = -EIO;
gdp = ext4_get_group_desc(sb, group, &group_desc_bh); gdp = ext4_get_group_desc(sb, group, &group_desc_bh);
...@@ -917,7 +915,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode) ...@@ -917,7 +915,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode)
* group descriptor metadata has not yet been updated. * group descriptor metadata has not yet been updated.
* So we just go onto the next blockgroup. * So we just go onto the next blockgroup.
*/ */
if (++group == sbi->s_groups_count) if (++group == ngroups)
group = 0; group = 0;
} }
err = -ENOSPC; err = -ENOSPC;
...@@ -1158,7 +1156,7 @@ unsigned long ext4_count_free_inodes(struct super_block *sb) ...@@ -1158,7 +1156,7 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)
{ {
unsigned long desc_count; unsigned long desc_count;
struct ext4_group_desc *gdp; struct ext4_group_desc *gdp;
ext4_group_t i; ext4_group_t i, ngroups = ext4_get_groups_count(sb);
#ifdef EXT4FS_DEBUG #ifdef EXT4FS_DEBUG
struct ext4_super_block *es; struct ext4_super_block *es;
unsigned long bitmap_count, x; unsigned long bitmap_count, x;
...@@ -1168,7 +1166,7 @@ unsigned long ext4_count_free_inodes(struct super_block *sb) ...@@ -1168,7 +1166,7 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)
desc_count = 0; desc_count = 0;
bitmap_count = 0; bitmap_count = 0;
gdp = NULL; gdp = NULL;
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) { for (i = 0; i < ngroups; i++) {
gdp = ext4_get_group_desc(sb, i, NULL); gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp) if (!gdp)
continue; continue;
...@@ -1190,7 +1188,7 @@ unsigned long ext4_count_free_inodes(struct super_block *sb) ...@@ -1190,7 +1188,7 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)
return desc_count; return desc_count;
#else #else
desc_count = 0; desc_count = 0;
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) { for (i = 0; i < ngroups; i++) {
gdp = ext4_get_group_desc(sb, i, NULL); gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp) if (!gdp)
continue; continue;
...@@ -1205,9 +1203,9 @@ unsigned long ext4_count_free_inodes(struct super_block *sb) ...@@ -1205,9 +1203,9 @@ unsigned long ext4_count_free_inodes(struct super_block *sb)
unsigned long ext4_count_dirs(struct super_block * sb) unsigned long ext4_count_dirs(struct super_block * sb)
{ {
unsigned long count = 0; unsigned long count = 0;
ext4_group_t i; ext4_group_t i, ngroups = ext4_get_groups_count(sb);
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) { for (i = 0; i < ngroups; i++) {
struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, NULL); struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp) if (!gdp)
continue; continue;
......
...@@ -4965,7 +4965,8 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk) ...@@ -4965,7 +4965,8 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
*/ */
int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk) int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk)
{ {
int groups, gdpblocks; ext4_group_t groups, ngroups = ext4_get_groups_count(inode->i_sb);
int gdpblocks;
int idxblocks; int idxblocks;
int ret = 0; int ret = 0;
...@@ -4992,8 +4993,8 @@ int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk) ...@@ -4992,8 +4993,8 @@ int ext4_meta_trans_blocks(struct inode *inode, int nrblocks, int chunk)
groups += nrblocks; groups += nrblocks;
gdpblocks = groups; gdpblocks = groups;
if (groups > EXT4_SB(inode->i_sb)->s_groups_count) if (groups > ngroups)
groups = EXT4_SB(inode->i_sb)->s_groups_count; groups = ngroups;
if (groups > EXT4_SB(inode->i_sb)->s_gdb_count) if (groups > EXT4_SB(inode->i_sb)->s_gdb_count)
gdpblocks = EXT4_SB(inode->i_sb)->s_gdb_count; gdpblocks = EXT4_SB(inode->i_sb)->s_gdb_count;
......
...@@ -739,6 +739,7 @@ static void ext4_mb_generate_buddy(struct super_block *sb, ...@@ -739,6 +739,7 @@ static void ext4_mb_generate_buddy(struct super_block *sb,
static int ext4_mb_init_cache(struct page *page, char *incore) static int ext4_mb_init_cache(struct page *page, char *incore)
{ {
ext4_group_t ngroups;
int blocksize; int blocksize;
int blocks_per_page; int blocks_per_page;
int groups_per_page; int groups_per_page;
...@@ -757,6 +758,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore) ...@@ -757,6 +758,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
inode = page->mapping->host; inode = page->mapping->host;
sb = inode->i_sb; sb = inode->i_sb;
ngroups = ext4_get_groups_count(sb);
blocksize = 1 << inode->i_blkbits; blocksize = 1 << inode->i_blkbits;
blocks_per_page = PAGE_CACHE_SIZE / blocksize; blocks_per_page = PAGE_CACHE_SIZE / blocksize;
...@@ -780,7 +782,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore) ...@@ -780,7 +782,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
for (i = 0; i < groups_per_page; i++) { for (i = 0; i < groups_per_page; i++) {
struct ext4_group_desc *desc; struct ext4_group_desc *desc;
if (first_group + i >= EXT4_SB(sb)->s_groups_count) if (first_group + i >= ngroups)
break; break;
err = -EIO; err = -EIO;
...@@ -852,7 +854,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore) ...@@ -852,7 +854,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
struct ext4_group_info *grinfo; struct ext4_group_info *grinfo;
group = (first_block + i) >> 1; group = (first_block + i) >> 1;
if (group >= EXT4_SB(sb)->s_groups_count) if (group >= ngroups)
break; break;
/* /*
...@@ -1788,6 +1790,7 @@ int ext4_mb_get_buddy_cache_lock(struct super_block *sb, ext4_group_t group) ...@@ -1788,6 +1790,7 @@ int ext4_mb_get_buddy_cache_lock(struct super_block *sb, ext4_group_t group)
int block, pnum; int block, pnum;
int blocks_per_page; int blocks_per_page;
int groups_per_page; int groups_per_page;
ext4_group_t ngroups = ext4_get_groups_count(sb);
ext4_group_t first_group; ext4_group_t first_group;
struct ext4_group_info *grp; struct ext4_group_info *grp;
...@@ -1807,7 +1810,7 @@ int ext4_mb_get_buddy_cache_lock(struct super_block *sb, ext4_group_t group) ...@@ -1807,7 +1810,7 @@ int ext4_mb_get_buddy_cache_lock(struct super_block *sb, ext4_group_t group)
/* read all groups the page covers into the cache */ /* read all groups the page covers into the cache */
for (i = 0; i < groups_per_page; i++) { for (i = 0; i < groups_per_page; i++) {
if ((first_group + i) >= EXT4_SB(sb)->s_groups_count) if ((first_group + i) >= ngroups)
break; break;
grp = ext4_get_group_info(sb, first_group + i); grp = ext4_get_group_info(sb, first_group + i);
/* take all groups write allocation /* take all groups write allocation
...@@ -1945,8 +1948,7 @@ static int ext4_mb_init_group(struct super_block *sb, ext4_group_t group) ...@@ -1945,8 +1948,7 @@ static int ext4_mb_init_group(struct super_block *sb, ext4_group_t group)
static noinline_for_stack int static noinline_for_stack int
ext4_mb_regular_allocator(struct ext4_allocation_context *ac) ext4_mb_regular_allocator(struct ext4_allocation_context *ac)
{ {
ext4_group_t group; ext4_group_t ngroups, group, i;
ext4_group_t i;
int cr; int cr;
int err = 0; int err = 0;
int bsbits; int bsbits;
...@@ -1957,6 +1959,7 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) ...@@ -1957,6 +1959,7 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac)
sb = ac->ac_sb; sb = ac->ac_sb;
sbi = EXT4_SB(sb); sbi = EXT4_SB(sb);
ngroups = ext4_get_groups_count(sb);
BUG_ON(ac->ac_status == AC_STATUS_FOUND); BUG_ON(ac->ac_status == AC_STATUS_FOUND);
/* first, try the goal */ /* first, try the goal */
...@@ -2017,11 +2020,11 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) ...@@ -2017,11 +2020,11 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac)
*/ */
group = ac->ac_g_ex.fe_group; group = ac->ac_g_ex.fe_group;
for (i = 0; i < EXT4_SB(sb)->s_groups_count; group++, i++) { for (i = 0; i < ngroups; group++, i++) {
struct ext4_group_info *grp; struct ext4_group_info *grp;
struct ext4_group_desc *desc; struct ext4_group_desc *desc;
if (group == EXT4_SB(sb)->s_groups_count) if (group == ngroups)
group = 0; group = 0;
/* quick check to skip empty groups */ /* quick check to skip empty groups */
...@@ -2315,12 +2318,10 @@ static struct file_operations ext4_mb_seq_history_fops = { ...@@ -2315,12 +2318,10 @@ static struct file_operations ext4_mb_seq_history_fops = {
static void *ext4_mb_seq_groups_start(struct seq_file *seq, loff_t *pos) static void *ext4_mb_seq_groups_start(struct seq_file *seq, loff_t *pos)
{ {
struct super_block *sb = seq->private; struct super_block *sb = seq->private;
struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_group_t group; ext4_group_t group;
if (*pos < 0 || *pos >= sbi->s_groups_count) if (*pos < 0 || *pos >= ext4_get_groups_count(sb))
return NULL; return NULL;
group = *pos + 1; group = *pos + 1;
return (void *) ((unsigned long) group); return (void *) ((unsigned long) group);
} }
...@@ -2328,11 +2329,10 @@ static void *ext4_mb_seq_groups_start(struct seq_file *seq, loff_t *pos) ...@@ -2328,11 +2329,10 @@ static void *ext4_mb_seq_groups_start(struct seq_file *seq, loff_t *pos)
static void *ext4_mb_seq_groups_next(struct seq_file *seq, void *v, loff_t *pos) static void *ext4_mb_seq_groups_next(struct seq_file *seq, void *v, loff_t *pos)
{ {
struct super_block *sb = seq->private; struct super_block *sb = seq->private;
struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_group_t group; ext4_group_t group;
++*pos; ++*pos;
if (*pos < 0 || *pos >= sbi->s_groups_count) if (*pos < 0 || *pos >= ext4_get_groups_count(sb))
return NULL; return NULL;
group = *pos + 1; group = *pos + 1;
return (void *) ((unsigned long) group); return (void *) ((unsigned long) group);
...@@ -2587,6 +2587,7 @@ void ext4_mb_update_group_info(struct ext4_group_info *grp, ext4_grpblk_t add) ...@@ -2587,6 +2587,7 @@ void ext4_mb_update_group_info(struct ext4_group_info *grp, ext4_grpblk_t add)
static int ext4_mb_init_backend(struct super_block *sb) static int ext4_mb_init_backend(struct super_block *sb)
{ {
ext4_group_t ngroups = ext4_get_groups_count(sb);
ext4_group_t i; ext4_group_t i;
int metalen; int metalen;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
...@@ -2598,7 +2599,7 @@ static int ext4_mb_init_backend(struct super_block *sb) ...@@ -2598,7 +2599,7 @@ static int ext4_mb_init_backend(struct super_block *sb)
struct ext4_group_desc *desc; struct ext4_group_desc *desc;
/* This is the number of blocks used by GDT */ /* This is the number of blocks used by GDT */
num_meta_group_infos = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - num_meta_group_infos = (ngroups + EXT4_DESC_PER_BLOCK(sb) -
1) >> EXT4_DESC_PER_BLOCK_BITS(sb); 1) >> EXT4_DESC_PER_BLOCK_BITS(sb);
/* /*
...@@ -2644,7 +2645,7 @@ static int ext4_mb_init_backend(struct super_block *sb) ...@@ -2644,7 +2645,7 @@ static int ext4_mb_init_backend(struct super_block *sb)
for (i = 0; i < num_meta_group_infos; i++) { for (i = 0; i < num_meta_group_infos; i++) {
if ((i + 1) == num_meta_group_infos) if ((i + 1) == num_meta_group_infos)
metalen = sizeof(*meta_group_info) * metalen = sizeof(*meta_group_info) *
(sbi->s_groups_count - (ngroups -
(i << EXT4_DESC_PER_BLOCK_BITS(sb))); (i << EXT4_DESC_PER_BLOCK_BITS(sb)));
meta_group_info = kmalloc(metalen, GFP_KERNEL); meta_group_info = kmalloc(metalen, GFP_KERNEL);
if (meta_group_info == NULL) { if (meta_group_info == NULL) {
...@@ -2655,7 +2656,7 @@ static int ext4_mb_init_backend(struct super_block *sb) ...@@ -2655,7 +2656,7 @@ static int ext4_mb_init_backend(struct super_block *sb)
sbi->s_group_info[i] = meta_group_info; sbi->s_group_info[i] = meta_group_info;
} }
for (i = 0; i < sbi->s_groups_count; i++) { for (i = 0; i < ngroups; i++) {
desc = ext4_get_group_desc(sb, i, NULL); desc = ext4_get_group_desc(sb, i, NULL);
if (desc == NULL) { if (desc == NULL) {
printk(KERN_ERR printk(KERN_ERR
...@@ -2781,13 +2782,14 @@ static void ext4_mb_cleanup_pa(struct ext4_group_info *grp) ...@@ -2781,13 +2782,14 @@ static void ext4_mb_cleanup_pa(struct ext4_group_info *grp)
int ext4_mb_release(struct super_block *sb) int ext4_mb_release(struct super_block *sb)
{ {
ext4_group_t ngroups = ext4_get_groups_count(sb);
ext4_group_t i; ext4_group_t i;
int num_meta_group_infos; int num_meta_group_infos;
struct ext4_group_info *grinfo; struct ext4_group_info *grinfo;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
if (sbi->s_group_info) { if (sbi->s_group_info) {
for (i = 0; i < sbi->s_groups_count; i++) { for (i = 0; i < ngroups; i++) {
grinfo = ext4_get_group_info(sb, i); grinfo = ext4_get_group_info(sb, i);
#ifdef DOUBLE_CHECK #ifdef DOUBLE_CHECK
kfree(grinfo->bb_bitmap); kfree(grinfo->bb_bitmap);
...@@ -2797,7 +2799,7 @@ int ext4_mb_release(struct super_block *sb) ...@@ -2797,7 +2799,7 @@ int ext4_mb_release(struct super_block *sb)
ext4_unlock_group(sb, i); ext4_unlock_group(sb, i);
kfree(grinfo); kfree(grinfo);
} }
num_meta_group_infos = (sbi->s_groups_count + num_meta_group_infos = (ngroups +
EXT4_DESC_PER_BLOCK(sb) - 1) >> EXT4_DESC_PER_BLOCK(sb) - 1) >>
EXT4_DESC_PER_BLOCK_BITS(sb); EXT4_DESC_PER_BLOCK_BITS(sb);
for (i = 0; i < num_meta_group_infos; i++) for (i = 0; i < num_meta_group_infos; i++)
...@@ -4121,7 +4123,7 @@ static void ext4_mb_return_to_preallocation(struct inode *inode, ...@@ -4121,7 +4123,7 @@ static void ext4_mb_return_to_preallocation(struct inode *inode,
static void ext4_mb_show_ac(struct ext4_allocation_context *ac) static void ext4_mb_show_ac(struct ext4_allocation_context *ac)
{ {
struct super_block *sb = ac->ac_sb; struct super_block *sb = ac->ac_sb;
ext4_group_t i; ext4_group_t ngroups, i;
printk(KERN_ERR "EXT4-fs: Can't allocate:" printk(KERN_ERR "EXT4-fs: Can't allocate:"
" Allocation context details:\n"); " Allocation context details:\n");
...@@ -4145,7 +4147,8 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac) ...@@ -4145,7 +4147,8 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac)
printk(KERN_ERR "EXT4-fs: %lu scanned, %d found\n", ac->ac_ex_scanned, printk(KERN_ERR "EXT4-fs: %lu scanned, %d found\n", ac->ac_ex_scanned,
ac->ac_found); ac->ac_found);
printk(KERN_ERR "EXT4-fs: groups: \n"); printk(KERN_ERR "EXT4-fs: groups: \n");
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) { ngroups = ext4_get_groups_count(sb);
for (i = 0; i < ngroups; i++) {
struct ext4_group_info *grp = ext4_get_group_info(sb, i); struct ext4_group_info *grp = ext4_get_group_info(sb, i);
struct ext4_prealloc_space *pa; struct ext4_prealloc_space *pa;
ext4_grpblk_t start; ext4_grpblk_t start;
...@@ -4469,13 +4472,13 @@ static int ext4_mb_release_context(struct ext4_allocation_context *ac) ...@@ -4469,13 +4472,13 @@ static int ext4_mb_release_context(struct ext4_allocation_context *ac)
static int ext4_mb_discard_preallocations(struct super_block *sb, int needed) static int ext4_mb_discard_preallocations(struct super_block *sb, int needed)
{ {
ext4_group_t i; ext4_group_t i, ngroups = ext4_get_groups_count(sb);
int ret; int ret;
int freed = 0; int freed = 0;
trace_mark(ext4_mb_discard_preallocations, "dev %s needed %d", trace_mark(ext4_mb_discard_preallocations, "dev %s needed %d",
sb->s_id, needed); sb->s_id, needed);
for (i = 0; i < EXT4_SB(sb)->s_groups_count && needed > 0; i++) { for (i = 0; i < ngroups && needed > 0; i++) {
ret = ext4_mb_discard_group_preallocations(sb, i, needed); ret = ext4_mb_discard_group_preallocations(sb, i, needed);
freed += ret; freed += ret;
needed -= ret; needed -= ret;
......
...@@ -3557,9 +3557,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -3557,9 +3557,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
if (test_opt(sb, MINIX_DF)) { if (test_opt(sb, MINIX_DF)) {
sbi->s_overhead_last = 0; sbi->s_overhead_last = 0;
} else if (sbi->s_blocks_last != ext4_blocks_count(es)) { } else if (sbi->s_blocks_last != ext4_blocks_count(es)) {
ext4_group_t ngroups = sbi->s_groups_count, i; ext4_group_t i, ngroups = ext4_get_groups_count(sb);
ext4_fsblk_t overhead = 0; ext4_fsblk_t overhead = 0;
smp_rmb();
/* /*
* Compute the overhead (FS structures). This is constant * Compute the overhead (FS structures). This is constant
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment