|
@@ -381,22 +381,28 @@ static inline void mb_clear_bit_atomic(spinlock_t *lock, int bit, void *addr)
|
|
|
|
|
|
static inline int mb_find_next_zero_bit(void *addr, int max, int start)
|
|
static inline int mb_find_next_zero_bit(void *addr, int max, int start)
|
|
{
|
|
{
|
|
- int fix = 0;
|
|
|
|
|
|
+ int fix = 0, ret, tmpmax;
|
|
addr = mb_correct_addr_and_bit(&fix, addr);
|
|
addr = mb_correct_addr_and_bit(&fix, addr);
|
|
- max += fix;
|
|
|
|
|
|
+ tmpmax = max + fix;
|
|
start += fix;
|
|
start += fix;
|
|
|
|
|
|
- return ext4_find_next_zero_bit(addr, max, start) - fix;
|
|
|
|
|
|
+ ret = ext4_find_next_zero_bit(addr, tmpmax, start) - fix;
|
|
|
|
+ if (ret > max)
|
|
|
|
+ return max;
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static inline int mb_find_next_bit(void *addr, int max, int start)
|
|
static inline int mb_find_next_bit(void *addr, int max, int start)
|
|
{
|
|
{
|
|
- int fix = 0;
|
|
|
|
|
|
+ int fix = 0, ret, tmpmax;
|
|
addr = mb_correct_addr_and_bit(&fix, addr);
|
|
addr = mb_correct_addr_and_bit(&fix, addr);
|
|
- max += fix;
|
|
|
|
|
|
+ tmpmax = max + fix;
|
|
start += fix;
|
|
start += fix;
|
|
|
|
|
|
- return ext4_find_next_bit(addr, max, start) - fix;
|
|
|
|
|
|
+ ret = ext4_find_next_bit(addr, tmpmax, start) - fix;
|
|
|
|
+ if (ret > max)
|
|
|
|
+ return max;
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static void *mb_find_buddy(struct ext4_buddy *e4b, int order, int *max)
|
|
static void *mb_find_buddy(struct ext4_buddy *e4b, int order, int *max)
|
|
@@ -803,6 +809,7 @@ static int ext4_mb_init_cache(struct page *page, char *incore)
|
|
if (!buffer_uptodate(bh[i]))
|
|
if (!buffer_uptodate(bh[i]))
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
|
|
+ err = 0;
|
|
first_block = page->index * blocks_per_page;
|
|
first_block = page->index * blocks_per_page;
|
|
for (i = 0; i < blocks_per_page; i++) {
|
|
for (i = 0; i < blocks_per_page; i++) {
|
|
int group;
|
|
int group;
|
|
@@ -883,6 +890,7 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
|
|
int pnum;
|
|
int pnum;
|
|
int poff;
|
|
int poff;
|
|
struct page *page;
|
|
struct page *page;
|
|
|
|
+ int ret;
|
|
|
|
|
|
mb_debug("load group %lu\n", group);
|
|
mb_debug("load group %lu\n", group);
|
|
|
|
|
|
@@ -914,15 +922,21 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
|
|
if (page) {
|
|
if (page) {
|
|
BUG_ON(page->mapping != inode->i_mapping);
|
|
BUG_ON(page->mapping != inode->i_mapping);
|
|
if (!PageUptodate(page)) {
|
|
if (!PageUptodate(page)) {
|
|
- ext4_mb_init_cache(page, NULL);
|
|
|
|
|
|
+ ret = ext4_mb_init_cache(page, NULL);
|
|
|
|
+ if (ret) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
mb_cmp_bitmaps(e4b, page_address(page) +
|
|
mb_cmp_bitmaps(e4b, page_address(page) +
|
|
(poff * sb->s_blocksize));
|
|
(poff * sb->s_blocksize));
|
|
}
|
|
}
|
|
unlock_page(page);
|
|
unlock_page(page);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if (page == NULL || !PageUptodate(page))
|
|
|
|
|
|
+ if (page == NULL || !PageUptodate(page)) {
|
|
|
|
+ ret = -EIO;
|
|
goto err;
|
|
goto err;
|
|
|
|
+ }
|
|
e4b->bd_bitmap_page = page;
|
|
e4b->bd_bitmap_page = page;
|
|
e4b->bd_bitmap = page_address(page) + (poff * sb->s_blocksize);
|
|
e4b->bd_bitmap = page_address(page) + (poff * sb->s_blocksize);
|
|
mark_page_accessed(page);
|
|
mark_page_accessed(page);
|
|
@@ -938,14 +952,20 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
|
|
page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
|
|
page = find_or_create_page(inode->i_mapping, pnum, GFP_NOFS);
|
|
if (page) {
|
|
if (page) {
|
|
BUG_ON(page->mapping != inode->i_mapping);
|
|
BUG_ON(page->mapping != inode->i_mapping);
|
|
- if (!PageUptodate(page))
|
|
|
|
- ext4_mb_init_cache(page, e4b->bd_bitmap);
|
|
|
|
-
|
|
|
|
|
|
+ if (!PageUptodate(page)) {
|
|
|
|
+ ret = ext4_mb_init_cache(page, e4b->bd_bitmap);
|
|
|
|
+ if (ret) {
|
|
|
|
+ unlock_page(page);
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
unlock_page(page);
|
|
unlock_page(page);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if (page == NULL || !PageUptodate(page))
|
|
|
|
|
|
+ if (page == NULL || !PageUptodate(page)) {
|
|
|
|
+ ret = -EIO;
|
|
goto err;
|
|
goto err;
|
|
|
|
+ }
|
|
e4b->bd_buddy_page = page;
|
|
e4b->bd_buddy_page = page;
|
|
e4b->bd_buddy = page_address(page) + (poff * sb->s_blocksize);
|
|
e4b->bd_buddy = page_address(page) + (poff * sb->s_blocksize);
|
|
mark_page_accessed(page);
|
|
mark_page_accessed(page);
|
|
@@ -962,7 +982,7 @@ err:
|
|
page_cache_release(e4b->bd_buddy_page);
|
|
page_cache_release(e4b->bd_buddy_page);
|
|
e4b->bd_buddy = NULL;
|
|
e4b->bd_buddy = NULL;
|
|
e4b->bd_bitmap = NULL;
|
|
e4b->bd_bitmap = NULL;
|
|
- return -EIO;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static void ext4_mb_release_desc(struct ext4_buddy *e4b)
|
|
static void ext4_mb_release_desc(struct ext4_buddy *e4b)
|
|
@@ -1031,7 +1051,7 @@ static void mb_set_bits(spinlock_t *lock, void *bm, int cur, int len)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
|
|
|
|
|
|
+static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
|
|
int first, int count)
|
|
int first, int count)
|
|
{
|
|
{
|
|
int block = 0;
|
|
int block = 0;
|
|
@@ -1071,11 +1091,12 @@ static int mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
|
|
blocknr += block;
|
|
blocknr += block;
|
|
blocknr +=
|
|
blocknr +=
|
|
le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
|
|
le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
|
|
-
|
|
|
|
|
|
+ ext4_unlock_group(sb, e4b->bd_group);
|
|
ext4_error(sb, __func__, "double-free of inode"
|
|
ext4_error(sb, __func__, "double-free of inode"
|
|
" %lu's block %llu(bit %u in group %lu)\n",
|
|
" %lu's block %llu(bit %u in group %lu)\n",
|
|
inode ? inode->i_ino : 0, blocknr, block,
|
|
inode ? inode->i_ino : 0, blocknr, block,
|
|
e4b->bd_group);
|
|
e4b->bd_group);
|
|
|
|
+ ext4_lock_group(sb, e4b->bd_group);
|
|
}
|
|
}
|
|
mb_clear_bit(block, EXT4_MB_BITMAP(e4b));
|
|
mb_clear_bit(block, EXT4_MB_BITMAP(e4b));
|
|
e4b->bd_info->bb_counters[order]++;
|
|
e4b->bd_info->bb_counters[order]++;
|
|
@@ -1113,8 +1134,6 @@ static int mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
|
|
} while (1);
|
|
} while (1);
|
|
}
|
|
}
|
|
mb_check_buddy(e4b);
|
|
mb_check_buddy(e4b);
|
|
-
|
|
|
|
- return 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static int mb_find_extent(struct ext4_buddy *e4b, int order, int block,
|
|
static int mb_find_extent(struct ext4_buddy *e4b, int order, int block,
|
|
@@ -1730,10 +1749,6 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac)
|
|
ac->ac_g_ex.fe_start = sbi->s_mb_last_start;
|
|
ac->ac_g_ex.fe_start = sbi->s_mb_last_start;
|
|
spin_unlock(&sbi->s_md_lock);
|
|
spin_unlock(&sbi->s_md_lock);
|
|
}
|
|
}
|
|
-
|
|
|
|
- /* searching for the right group start from the goal value specified */
|
|
|
|
- group = ac->ac_g_ex.fe_group;
|
|
|
|
-
|
|
|
|
/* Let's just scan groups to find more-less suitable blocks */
|
|
/* Let's just scan groups to find more-less suitable blocks */
|
|
cr = ac->ac_2order ? 0 : 1;
|
|
cr = ac->ac_2order ? 0 : 1;
|
|
/*
|
|
/*
|
|
@@ -1743,6 +1758,12 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac)
|
|
repeat:
|
|
repeat:
|
|
for (; cr < 4 && ac->ac_status == AC_STATUS_CONTINUE; cr++) {
|
|
for (; cr < 4 && ac->ac_status == AC_STATUS_CONTINUE; cr++) {
|
|
ac->ac_criteria = cr;
|
|
ac->ac_criteria = cr;
|
|
|
|
+ /*
|
|
|
|
+ * searching for the right group start
|
|
|
|
+ * from the goal value specified
|
|
|
|
+ */
|
|
|
|
+ group = ac->ac_g_ex.fe_group;
|
|
|
|
+
|
|
for (i = 0; i < EXT4_SB(sb)->s_groups_count; group++, i++) {
|
|
for (i = 0; i < EXT4_SB(sb)->s_groups_count; group++, i++) {
|
|
struct ext4_group_info *grp;
|
|
struct ext4_group_info *grp;
|
|
struct ext4_group_desc *desc;
|
|
struct ext4_group_desc *desc;
|
|
@@ -1963,6 +1984,8 @@ static int ext4_mb_seq_history_open(struct inode *inode, struct file *file)
|
|
int rc;
|
|
int rc;
|
|
int size;
|
|
int size;
|
|
|
|
|
|
|
|
+ if (unlikely(sbi->s_mb_history == NULL))
|
|
|
|
+ return -ENOMEM;
|
|
s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
if (s == NULL)
|
|
if (s == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
@@ -2165,9 +2188,7 @@ static void ext4_mb_history_init(struct super_block *sb)
|
|
sbi->s_mb_history_cur = 0;
|
|
sbi->s_mb_history_cur = 0;
|
|
spin_lock_init(&sbi->s_mb_history_lock);
|
|
spin_lock_init(&sbi->s_mb_history_lock);
|
|
i = sbi->s_mb_history_max * sizeof(struct ext4_mb_history);
|
|
i = sbi->s_mb_history_max * sizeof(struct ext4_mb_history);
|
|
- sbi->s_mb_history = kmalloc(i, GFP_KERNEL);
|
|
|
|
- if (likely(sbi->s_mb_history != NULL))
|
|
|
|
- memset(sbi->s_mb_history, 0, i);
|
|
|
|
|
|
+ sbi->s_mb_history = kzalloc(i, GFP_KERNEL);
|
|
/* if we can't allocate history, then we simple won't use it */
|
|
/* if we can't allocate history, then we simple won't use it */
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2215,21 +2236,192 @@ ext4_mb_store_history(struct ext4_allocation_context *ac)
|
|
#define ext4_mb_history_init(sb)
|
|
#define ext4_mb_history_init(sb)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+
|
|
|
|
+/* Create and initialize ext4_group_info data for the given group. */
|
|
|
|
+int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
|
|
|
|
+ struct ext4_group_desc *desc)
|
|
|
|
+{
|
|
|
|
+ int i, len;
|
|
|
|
+ int metalen = 0;
|
|
|
|
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
|
+ struct ext4_group_info **meta_group_info;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * First check if this group is the first of a reserved block.
|
|
|
|
+ * If it's true, we have to allocate a new table of pointers
|
|
|
|
+ * to ext4_group_info structures
|
|
|
|
+ */
|
|
|
|
+ if (group % EXT4_DESC_PER_BLOCK(sb) == 0) {
|
|
|
|
+ metalen = sizeof(*meta_group_info) <<
|
|
|
|
+ EXT4_DESC_PER_BLOCK_BITS(sb);
|
|
|
|
+ meta_group_info = kmalloc(metalen, GFP_KERNEL);
|
|
|
|
+ if (meta_group_info == NULL) {
|
|
|
|
+ printk(KERN_ERR "EXT4-fs: can't allocate mem for a "
|
|
|
|
+ "buddy group\n");
|
|
|
|
+ goto exit_meta_group_info;
|
|
|
|
+ }
|
|
|
|
+ sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] =
|
|
|
|
+ meta_group_info;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * calculate needed size. if change bb_counters size,
|
|
|
|
+ * don't forget about ext4_mb_generate_buddy()
|
|
|
|
+ */
|
|
|
|
+ len = offsetof(typeof(**meta_group_info),
|
|
|
|
+ bb_counters[sb->s_blocksize_bits + 2]);
|
|
|
|
+
|
|
|
|
+ meta_group_info =
|
|
|
|
+ sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)];
|
|
|
|
+ i = group & (EXT4_DESC_PER_BLOCK(sb) - 1);
|
|
|
|
+
|
|
|
|
+ meta_group_info[i] = kzalloc(len, GFP_KERNEL);
|
|
|
|
+ if (meta_group_info[i] == NULL) {
|
|
|
|
+ printk(KERN_ERR "EXT4-fs: can't allocate buddy mem\n");
|
|
|
|
+ goto exit_group_info;
|
|
|
|
+ }
|
|
|
|
+ set_bit(EXT4_GROUP_INFO_NEED_INIT_BIT,
|
|
|
|
+ &(meta_group_info[i]->bb_state));
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * initialize bb_free to be able to skip
|
|
|
|
+ * empty groups without initialization
|
|
|
|
+ */
|
|
|
|
+ if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
|
|
|
|
+ meta_group_info[i]->bb_free =
|
|
|
|
+ ext4_free_blocks_after_init(sb, group, desc);
|
|
|
|
+ } else {
|
|
|
|
+ meta_group_info[i]->bb_free =
|
|
|
|
+ le16_to_cpu(desc->bg_free_blocks_count);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ INIT_LIST_HEAD(&meta_group_info[i]->bb_prealloc_list);
|
|
|
|
+
|
|
|
|
+#ifdef DOUBLE_CHECK
|
|
|
|
+ {
|
|
|
|
+ struct buffer_head *bh;
|
|
|
|
+ meta_group_info[i]->bb_bitmap =
|
|
|
|
+ kmalloc(sb->s_blocksize, GFP_KERNEL);
|
|
|
|
+ BUG_ON(meta_group_info[i]->bb_bitmap == NULL);
|
|
|
|
+ bh = ext4_read_block_bitmap(sb, group);
|
|
|
|
+ BUG_ON(bh == NULL);
|
|
|
|
+ memcpy(meta_group_info[i]->bb_bitmap, bh->b_data,
|
|
|
|
+ sb->s_blocksize);
|
|
|
|
+ put_bh(bh);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+exit_group_info:
|
|
|
|
+ /* If a meta_group_info table has been allocated, release it now */
|
|
|
|
+ if (group % EXT4_DESC_PER_BLOCK(sb) == 0)
|
|
|
|
+ kfree(sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)]);
|
|
|
|
+exit_meta_group_info:
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+} /* ext4_mb_add_groupinfo */
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Add a group to the existing groups.
|
|
|
|
+ * This function is used for online resize
|
|
|
|
+ */
|
|
|
|
+int ext4_mb_add_more_groupinfo(struct super_block *sb, ext4_group_t group,
|
|
|
|
+ struct ext4_group_desc *desc)
|
|
|
|
+{
|
|
|
|
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
|
+ struct inode *inode = sbi->s_buddy_cache;
|
|
|
|
+ int blocks_per_page;
|
|
|
|
+ int block;
|
|
|
|
+ int pnum;
|
|
|
|
+ struct page *page;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ /* Add group based on group descriptor*/
|
|
|
|
+ err = ext4_mb_add_groupinfo(sb, group, desc);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Cache pages containing dynamic mb_alloc datas (buddy and bitmap
|
|
|
|
+ * datas) are set not up to date so that they will be re-initilaized
|
|
|
|
+ * during the next call to ext4_mb_load_buddy
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ /* Set buddy page as not up to date */
|
|
|
|
+ blocks_per_page = PAGE_CACHE_SIZE / sb->s_blocksize;
|
|
|
|
+ block = group * 2;
|
|
|
|
+ pnum = block / blocks_per_page;
|
|
|
|
+ page = find_get_page(inode->i_mapping, pnum);
|
|
|
|
+ if (page != NULL) {
|
|
|
|
+ ClearPageUptodate(page);
|
|
|
|
+ page_cache_release(page);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Set bitmap page as not up to date */
|
|
|
|
+ block++;
|
|
|
|
+ pnum = block / blocks_per_page;
|
|
|
|
+ page = find_get_page(inode->i_mapping, pnum);
|
|
|
|
+ if (page != NULL) {
|
|
|
|
+ ClearPageUptodate(page);
|
|
|
|
+ page_cache_release(page);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Update an existing group.
|
|
|
|
+ * This function is used for online resize
|
|
|
|
+ */
|
|
|
|
+void ext4_mb_update_group_info(struct ext4_group_info *grp, ext4_grpblk_t add)
|
|
|
|
+{
|
|
|
|
+ grp->bb_free += add;
|
|
|
|
+}
|
|
|
|
+
|
|
static int ext4_mb_init_backend(struct super_block *sb)
|
|
static int ext4_mb_init_backend(struct super_block *sb)
|
|
{
|
|
{
|
|
ext4_group_t i;
|
|
ext4_group_t i;
|
|
- int j, len, metalen;
|
|
|
|
|
|
+ int metalen;
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
- int num_meta_group_infos =
|
|
|
|
- (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) >>
|
|
|
|
- EXT4_DESC_PER_BLOCK_BITS(sb);
|
|
|
|
|
|
+ struct ext4_super_block *es = sbi->s_es;
|
|
|
|
+ int num_meta_group_infos;
|
|
|
|
+ int num_meta_group_infos_max;
|
|
|
|
+ int array_size;
|
|
struct ext4_group_info **meta_group_info;
|
|
struct ext4_group_info **meta_group_info;
|
|
|
|
+ struct ext4_group_desc *desc;
|
|
|
|
+
|
|
|
|
+ /* This is the number of blocks used by GDT */
|
|
|
|
+ num_meta_group_infos = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) -
|
|
|
|
+ 1) >> EXT4_DESC_PER_BLOCK_BITS(sb);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This is the total number of blocks used by GDT including
|
|
|
|
+ * the number of reserved blocks for GDT.
|
|
|
|
+ * The s_group_info array is allocated with this value
|
|
|
|
+ * to allow a clean online resize without a complex
|
|
|
|
+ * manipulation of pointer.
|
|
|
|
+ * The drawback is the unused memory when no resize
|
|
|
|
+ * occurs but it's very low in terms of pages
|
|
|
|
+ * (see comments below)
|
|
|
|
+ * Need to handle this properly when META_BG resizing is allowed
|
|
|
|
+ */
|
|
|
|
+ num_meta_group_infos_max = num_meta_group_infos +
|
|
|
|
+ le16_to_cpu(es->s_reserved_gdt_blocks);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * array_size is the size of s_group_info array. We round it
|
|
|
|
+ * to the next power of two because this approximation is done
|
|
|
|
+ * internally by kmalloc so we can have some more memory
|
|
|
|
+ * for free here (e.g. may be used for META_BG resize).
|
|
|
|
+ */
|
|
|
|
+ array_size = 1;
|
|
|
|
+ while (array_size < sizeof(*sbi->s_group_info) *
|
|
|
|
+ num_meta_group_infos_max)
|
|
|
|
+ array_size = array_size << 1;
|
|
/* An 8TB filesystem with 64-bit pointers requires a 4096 byte
|
|
/* An 8TB filesystem with 64-bit pointers requires a 4096 byte
|
|
* kmalloc. A 128kb malloc should suffice for a 256TB filesystem.
|
|
* kmalloc. A 128kb malloc should suffice for a 256TB filesystem.
|
|
* So a two level scheme suffices for now. */
|
|
* So a two level scheme suffices for now. */
|
|
- sbi->s_group_info = kmalloc(sizeof(*sbi->s_group_info) *
|
|
|
|
- num_meta_group_infos, GFP_KERNEL);
|
|
|
|
|
|
+ sbi->s_group_info = kmalloc(array_size, GFP_KERNEL);
|
|
if (sbi->s_group_info == NULL) {
|
|
if (sbi->s_group_info == NULL) {
|
|
printk(KERN_ERR "EXT4-fs: can't allocate buddy meta group\n");
|
|
printk(KERN_ERR "EXT4-fs: can't allocate buddy meta group\n");
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
@@ -2256,63 +2448,15 @@ 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;
|
|
}
|
|
}
|
|
|
|
|
|
- /*
|
|
|
|
- * calculate needed size. if change bb_counters size,
|
|
|
|
- * don't forget about ext4_mb_generate_buddy()
|
|
|
|
- */
|
|
|
|
- len = sizeof(struct ext4_group_info);
|
|
|
|
- len += sizeof(unsigned short) * (sb->s_blocksize_bits + 2);
|
|
|
|
for (i = 0; i < sbi->s_groups_count; i++) {
|
|
for (i = 0; i < sbi->s_groups_count; i++) {
|
|
- struct ext4_group_desc *desc;
|
|
|
|
-
|
|
|
|
- meta_group_info =
|
|
|
|
- sbi->s_group_info[i >> EXT4_DESC_PER_BLOCK_BITS(sb)];
|
|
|
|
- j = i & (EXT4_DESC_PER_BLOCK(sb) - 1);
|
|
|
|
-
|
|
|
|
- meta_group_info[j] = kzalloc(len, GFP_KERNEL);
|
|
|
|
- if (meta_group_info[j] == NULL) {
|
|
|
|
- printk(KERN_ERR "EXT4-fs: can't allocate buddy mem\n");
|
|
|
|
- goto err_freebuddy;
|
|
|
|
- }
|
|
|
|
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
|
|
"EXT4-fs: can't read descriptor %lu\n", i);
|
|
"EXT4-fs: can't read descriptor %lu\n", i);
|
|
- i++;
|
|
|
|
goto err_freebuddy;
|
|
goto err_freebuddy;
|
|
}
|
|
}
|
|
- memset(meta_group_info[j], 0, len);
|
|
|
|
- set_bit(EXT4_GROUP_INFO_NEED_INIT_BIT,
|
|
|
|
- &(meta_group_info[j]->bb_state));
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * initialize bb_free to be able to skip
|
|
|
|
- * empty groups without initialization
|
|
|
|
- */
|
|
|
|
- if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
|
|
|
|
- meta_group_info[j]->bb_free =
|
|
|
|
- ext4_free_blocks_after_init(sb, i, desc);
|
|
|
|
- } else {
|
|
|
|
- meta_group_info[j]->bb_free =
|
|
|
|
- le16_to_cpu(desc->bg_free_blocks_count);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- INIT_LIST_HEAD(&meta_group_info[j]->bb_prealloc_list);
|
|
|
|
-
|
|
|
|
-#ifdef DOUBLE_CHECK
|
|
|
|
- {
|
|
|
|
- struct buffer_head *bh;
|
|
|
|
- meta_group_info[j]->bb_bitmap =
|
|
|
|
- kmalloc(sb->s_blocksize, GFP_KERNEL);
|
|
|
|
- BUG_ON(meta_group_info[j]->bb_bitmap == NULL);
|
|
|
|
- bh = read_block_bitmap(sb, i);
|
|
|
|
- BUG_ON(bh == NULL);
|
|
|
|
- memcpy(meta_group_info[j]->bb_bitmap, bh->b_data,
|
|
|
|
- sb->s_blocksize);
|
|
|
|
- put_bh(bh);
|
|
|
|
- }
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
|
|
+ if (ext4_mb_add_groupinfo(sb, i, desc) != 0)
|
|
|
|
+ goto err_freebuddy;
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -2336,6 +2480,7 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery)
|
|
unsigned i;
|
|
unsigned i;
|
|
unsigned offset;
|
|
unsigned offset;
|
|
unsigned max;
|
|
unsigned max;
|
|
|
|
+ int ret;
|
|
|
|
|
|
if (!test_opt(sb, MBALLOC))
|
|
if (!test_opt(sb, MBALLOC))
|
|
return 0;
|
|
return 0;
|
|
@@ -2370,12 +2515,12 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery)
|
|
} while (i <= sb->s_blocksize_bits + 1);
|
|
} while (i <= sb->s_blocksize_bits + 1);
|
|
|
|
|
|
/* init file for buddy data */
|
|
/* init file for buddy data */
|
|
- i = ext4_mb_init_backend(sb);
|
|
|
|
- if (i) {
|
|
|
|
|
|
+ ret = ext4_mb_init_backend(sb);
|
|
|
|
+ if (ret != 0) {
|
|
clear_opt(sbi->s_mount_opt, MBALLOC);
|
|
clear_opt(sbi->s_mount_opt, MBALLOC);
|
|
kfree(sbi->s_mb_offsets);
|
|
kfree(sbi->s_mb_offsets);
|
|
kfree(sbi->s_mb_maxs);
|
|
kfree(sbi->s_mb_maxs);
|
|
- return i;
|
|
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
spin_lock_init(&sbi->s_md_lock);
|
|
spin_lock_init(&sbi->s_md_lock);
|
|
@@ -2548,8 +2693,7 @@ ext4_mb_free_committed_blocks(struct super_block *sb)
|
|
ext4_lock_group(sb, md->group);
|
|
ext4_lock_group(sb, md->group);
|
|
for (i = 0; i < md->num; i++) {
|
|
for (i = 0; i < md->num; i++) {
|
|
mb_debug(" %u", md->blocks[i]);
|
|
mb_debug(" %u", md->blocks[i]);
|
|
- err = mb_free_blocks(NULL, &e4b, md->blocks[i], 1);
|
|
|
|
- BUG_ON(err != 0);
|
|
|
|
|
|
+ mb_free_blocks(NULL, &e4b, md->blocks[i], 1);
|
|
}
|
|
}
|
|
mb_debug("\n");
|
|
mb_debug("\n");
|
|
ext4_unlock_group(sb, md->group);
|
|
ext4_unlock_group(sb, md->group);
|
|
@@ -2575,25 +2719,24 @@ ext4_mb_free_committed_blocks(struct super_block *sb)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-#define MB_PROC_VALUE_READ(name) \
|
|
|
|
-static int ext4_mb_read_##name(char *page, char **start, \
|
|
|
|
- off_t off, int count, int *eof, void *data) \
|
|
|
|
|
|
+#define MB_PROC_FOPS(name) \
|
|
|
|
+static int ext4_mb_##name##_proc_show(struct seq_file *m, void *v) \
|
|
{ \
|
|
{ \
|
|
- struct ext4_sb_info *sbi = data; \
|
|
|
|
- int len; \
|
|
|
|
- *eof = 1; \
|
|
|
|
- if (off != 0) \
|
|
|
|
- return 0; \
|
|
|
|
- len = sprintf(page, "%ld\n", sbi->s_mb_##name); \
|
|
|
|
- *start = page; \
|
|
|
|
- return len; \
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#define MB_PROC_VALUE_WRITE(name) \
|
|
|
|
-static int ext4_mb_write_##name(struct file *file, \
|
|
|
|
- const char __user *buf, unsigned long cnt, void *data) \
|
|
|
|
|
|
+ struct ext4_sb_info *sbi = m->private; \
|
|
|
|
+ \
|
|
|
|
+ seq_printf(m, "%ld\n", sbi->s_mb_##name); \
|
|
|
|
+ return 0; \
|
|
|
|
+} \
|
|
|
|
+ \
|
|
|
|
+static int ext4_mb_##name##_proc_open(struct inode *inode, struct file *file)\
|
|
|
|
+{ \
|
|
|
|
+ return single_open(file, ext4_mb_##name##_proc_show, PDE(inode)->data);\
|
|
|
|
+} \
|
|
|
|
+ \
|
|
|
|
+static ssize_t ext4_mb_##name##_proc_write(struct file *file, \
|
|
|
|
+ const char __user *buf, size_t cnt, loff_t *ppos) \
|
|
{ \
|
|
{ \
|
|
- struct ext4_sb_info *sbi = data; \
|
|
|
|
|
|
+ struct ext4_sb_info *sbi = PDE(file->f_path.dentry->d_inode)->data;\
|
|
char str[32]; \
|
|
char str[32]; \
|
|
long value; \
|
|
long value; \
|
|
if (cnt >= sizeof(str)) \
|
|
if (cnt >= sizeof(str)) \
|
|
@@ -2605,31 +2748,32 @@ static int ext4_mb_write_##name(struct file *file, \
|
|
return -ERANGE; \
|
|
return -ERANGE; \
|
|
sbi->s_mb_##name = value; \
|
|
sbi->s_mb_##name = value; \
|
|
return cnt; \
|
|
return cnt; \
|
|
-}
|
|
|
|
|
|
+} \
|
|
|
|
+ \
|
|
|
|
+static const struct file_operations ext4_mb_##name##_proc_fops = { \
|
|
|
|
+ .owner = THIS_MODULE, \
|
|
|
|
+ .open = ext4_mb_##name##_proc_open, \
|
|
|
|
+ .read = seq_read, \
|
|
|
|
+ .llseek = seq_lseek, \
|
|
|
|
+ .release = single_release, \
|
|
|
|
+ .write = ext4_mb_##name##_proc_write, \
|
|
|
|
+};
|
|
|
|
|
|
-MB_PROC_VALUE_READ(stats);
|
|
|
|
-MB_PROC_VALUE_WRITE(stats);
|
|
|
|
-MB_PROC_VALUE_READ(max_to_scan);
|
|
|
|
-MB_PROC_VALUE_WRITE(max_to_scan);
|
|
|
|
-MB_PROC_VALUE_READ(min_to_scan);
|
|
|
|
-MB_PROC_VALUE_WRITE(min_to_scan);
|
|
|
|
-MB_PROC_VALUE_READ(order2_reqs);
|
|
|
|
-MB_PROC_VALUE_WRITE(order2_reqs);
|
|
|
|
-MB_PROC_VALUE_READ(stream_request);
|
|
|
|
-MB_PROC_VALUE_WRITE(stream_request);
|
|
|
|
-MB_PROC_VALUE_READ(group_prealloc);
|
|
|
|
-MB_PROC_VALUE_WRITE(group_prealloc);
|
|
|
|
|
|
+MB_PROC_FOPS(stats);
|
|
|
|
+MB_PROC_FOPS(max_to_scan);
|
|
|
|
+MB_PROC_FOPS(min_to_scan);
|
|
|
|
+MB_PROC_FOPS(order2_reqs);
|
|
|
|
+MB_PROC_FOPS(stream_request);
|
|
|
|
+MB_PROC_FOPS(group_prealloc);
|
|
|
|
|
|
#define MB_PROC_HANDLER(name, var) \
|
|
#define MB_PROC_HANDLER(name, var) \
|
|
do { \
|
|
do { \
|
|
- proc = create_proc_entry(name, mode, sbi->s_mb_proc); \
|
|
|
|
|
|
+ proc = proc_create_data(name, mode, sbi->s_mb_proc, \
|
|
|
|
+ &ext4_mb_##var##_proc_fops, sbi); \
|
|
if (proc == NULL) { \
|
|
if (proc == NULL) { \
|
|
printk(KERN_ERR "EXT4-fs: can't to create %s\n", name); \
|
|
printk(KERN_ERR "EXT4-fs: can't to create %s\n", name); \
|
|
goto err_out; \
|
|
goto err_out; \
|
|
} \
|
|
} \
|
|
- proc->data = sbi; \
|
|
|
|
- proc->read_proc = ext4_mb_read_##var ; \
|
|
|
|
- proc->write_proc = ext4_mb_write_##var; \
|
|
|
|
} while (0)
|
|
} while (0)
|
|
|
|
|
|
static int ext4_mb_init_per_dev_proc(struct super_block *sb)
|
|
static int ext4_mb_init_per_dev_proc(struct super_block *sb)
|
|
@@ -2639,6 +2783,10 @@ static int ext4_mb_init_per_dev_proc(struct super_block *sb)
|
|
struct proc_dir_entry *proc;
|
|
struct proc_dir_entry *proc;
|
|
char devname[64];
|
|
char devname[64];
|
|
|
|
|
|
|
|
+ if (proc_root_ext4 == NULL) {
|
|
|
|
+ sbi->s_mb_proc = NULL;
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
bdevname(sb->s_bdev, devname);
|
|
bdevname(sb->s_bdev, devname);
|
|
sbi->s_mb_proc = proc_mkdir(devname, proc_root_ext4);
|
|
sbi->s_mb_proc = proc_mkdir(devname, proc_root_ext4);
|
|
|
|
|
|
@@ -2747,7 +2895,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
|
|
|
|
|
|
|
|
|
|
err = -EIO;
|
|
err = -EIO;
|
|
- bitmap_bh = read_block_bitmap(sb, ac->ac_b_ex.fe_group);
|
|
|
|
|
|
+ bitmap_bh = ext4_read_block_bitmap(sb, ac->ac_b_ex.fe_group);
|
|
if (!bitmap_bh)
|
|
if (!bitmap_bh)
|
|
goto out_err;
|
|
goto out_err;
|
|
|
|
|
|
@@ -2816,7 +2964,23 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
|
|
le16_add_cpu(&gdp->bg_free_blocks_count, -ac->ac_b_ex.fe_len);
|
|
le16_add_cpu(&gdp->bg_free_blocks_count, -ac->ac_b_ex.fe_len);
|
|
gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp);
|
|
gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp);
|
|
spin_unlock(sb_bgl_lock(sbi, ac->ac_b_ex.fe_group));
|
|
spin_unlock(sb_bgl_lock(sbi, ac->ac_b_ex.fe_group));
|
|
- percpu_counter_sub(&sbi->s_freeblocks_counter, ac->ac_b_ex.fe_len);
|
|
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * free blocks account has already be reduced/reserved
|
|
|
|
+ * at write_begin() time for delayed allocation
|
|
|
|
+ * do not double accounting
|
|
|
|
+ */
|
|
|
|
+ if (!(ac->ac_flags & EXT4_MB_DELALLOC_RESERVED))
|
|
|
|
+ percpu_counter_sub(&sbi->s_freeblocks_counter,
|
|
|
|
+ ac->ac_b_ex.fe_len);
|
|
|
|
+
|
|
|
|
+ if (sbi->s_log_groups_per_flex) {
|
|
|
|
+ ext4_group_t flex_group = ext4_flex_group(sbi,
|
|
|
|
+ ac->ac_b_ex.fe_group);
|
|
|
|
+ spin_lock(sb_bgl_lock(sbi, flex_group));
|
|
|
|
+ sbi->s_flex_groups[flex_group].free_blocks -= ac->ac_b_ex.fe_len;
|
|
|
|
+ spin_unlock(sb_bgl_lock(sbi, flex_group));
|
|
|
|
+ }
|
|
|
|
|
|
err = ext4_journal_dirty_metadata(handle, bitmap_bh);
|
|
err = ext4_journal_dirty_metadata(handle, bitmap_bh);
|
|
if (err)
|
|
if (err)
|
|
@@ -3473,8 +3637,6 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh,
|
|
if (bit >= end)
|
|
if (bit >= end)
|
|
break;
|
|
break;
|
|
next = mb_find_next_bit(bitmap_bh->b_data, end, bit);
|
|
next = mb_find_next_bit(bitmap_bh->b_data, end, bit);
|
|
- if (next > end)
|
|
|
|
- next = end;
|
|
|
|
start = group * EXT4_BLOCKS_PER_GROUP(sb) + bit +
|
|
start = group * EXT4_BLOCKS_PER_GROUP(sb) + bit +
|
|
le32_to_cpu(sbi->s_es->s_first_data_block);
|
|
le32_to_cpu(sbi->s_es->s_first_data_block);
|
|
mb_debug(" free preallocated %u/%u in group %u\n",
|
|
mb_debug(" free preallocated %u/%u in group %u\n",
|
|
@@ -3569,7 +3731,7 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
|
|
if (list_empty(&grp->bb_prealloc_list))
|
|
if (list_empty(&grp->bb_prealloc_list))
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- bitmap_bh = read_block_bitmap(sb, group);
|
|
|
|
|
|
+ bitmap_bh = ext4_read_block_bitmap(sb, group);
|
|
if (bitmap_bh == NULL) {
|
|
if (bitmap_bh == NULL) {
|
|
/* error handling here */
|
|
/* error handling here */
|
|
ext4_mb_release_desc(&e4b);
|
|
ext4_mb_release_desc(&e4b);
|
|
@@ -3743,7 +3905,7 @@ repeat:
|
|
err = ext4_mb_load_buddy(sb, group, &e4b);
|
|
err = ext4_mb_load_buddy(sb, group, &e4b);
|
|
BUG_ON(err != 0); /* error handling here */
|
|
BUG_ON(err != 0); /* error handling here */
|
|
|
|
|
|
- bitmap_bh = read_block_bitmap(sb, group);
|
|
|
|
|
|
+ bitmap_bh = ext4_read_block_bitmap(sb, group);
|
|
if (bitmap_bh == NULL) {
|
|
if (bitmap_bh == NULL) {
|
|
/* error handling here */
|
|
/* error handling here */
|
|
ext4_mb_release_desc(&e4b);
|
|
ext4_mb_release_desc(&e4b);
|
|
@@ -4011,10 +4173,21 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
|
|
sbi = EXT4_SB(sb);
|
|
sbi = EXT4_SB(sb);
|
|
|
|
|
|
if (!test_opt(sb, MBALLOC)) {
|
|
if (!test_opt(sb, MBALLOC)) {
|
|
- block = ext4_new_blocks_old(handle, ar->inode, ar->goal,
|
|
|
|
|
|
+ block = ext4_old_new_blocks(handle, ar->inode, ar->goal,
|
|
&(ar->len), errp);
|
|
&(ar->len), errp);
|
|
return block;
|
|
return block;
|
|
}
|
|
}
|
|
|
|
+ if (!EXT4_I(ar->inode)->i_delalloc_reserved_flag) {
|
|
|
|
+ /*
|
|
|
|
+ * With delalloc we already reserved the blocks
|
|
|
|
+ */
|
|
|
|
+ ar->len = ext4_has_free_blocks(sbi, ar->len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ar->len == 0) {
|
|
|
|
+ *errp = -ENOSPC;
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
|
|
while (ar->len && DQUOT_ALLOC_BLOCK(ar->inode, ar->len)) {
|
|
while (ar->len && DQUOT_ALLOC_BLOCK(ar->inode, ar->len)) {
|
|
ar->flags |= EXT4_MB_HINT_NOPREALLOC;
|
|
ar->flags |= EXT4_MB_HINT_NOPREALLOC;
|
|
@@ -4026,10 +4199,14 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
|
|
}
|
|
}
|
|
inquota = ar->len;
|
|
inquota = ar->len;
|
|
|
|
|
|
|
|
+ if (EXT4_I(ar->inode)->i_delalloc_reserved_flag)
|
|
|
|
+ ar->flags |= EXT4_MB_DELALLOC_RESERVED;
|
|
|
|
+
|
|
ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
|
|
ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
|
|
if (!ac) {
|
|
if (!ac) {
|
|
|
|
+ ar->len = 0;
|
|
*errp = -ENOMEM;
|
|
*errp = -ENOMEM;
|
|
- return 0;
|
|
|
|
|
|
+ goto out1;
|
|
}
|
|
}
|
|
|
|
|
|
ext4_mb_poll_new_transaction(sb, handle);
|
|
ext4_mb_poll_new_transaction(sb, handle);
|
|
@@ -4037,12 +4214,11 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
|
|
*errp = ext4_mb_initialize_context(ac, ar);
|
|
*errp = ext4_mb_initialize_context(ac, ar);
|
|
if (*errp) {
|
|
if (*errp) {
|
|
ar->len = 0;
|
|
ar->len = 0;
|
|
- goto out;
|
|
|
|
|
|
+ goto out2;
|
|
}
|
|
}
|
|
|
|
|
|
ac->ac_op = EXT4_MB_HISTORY_PREALLOC;
|
|
ac->ac_op = EXT4_MB_HISTORY_PREALLOC;
|
|
if (!ext4_mb_use_preallocated(ac)) {
|
|
if (!ext4_mb_use_preallocated(ac)) {
|
|
-
|
|
|
|
ac->ac_op = EXT4_MB_HISTORY_ALLOC;
|
|
ac->ac_op = EXT4_MB_HISTORY_ALLOC;
|
|
ext4_mb_normalize_request(ac, ar);
|
|
ext4_mb_normalize_request(ac, ar);
|
|
repeat:
|
|
repeat:
|
|
@@ -4085,11 +4261,12 @@ repeat:
|
|
|
|
|
|
ext4_mb_release_context(ac);
|
|
ext4_mb_release_context(ac);
|
|
|
|
|
|
-out:
|
|
|
|
|
|
+out2:
|
|
|
|
+ kmem_cache_free(ext4_ac_cachep, ac);
|
|
|
|
+out1:
|
|
if (ar->len < inquota)
|
|
if (ar->len < inquota)
|
|
DQUOT_FREE_BLOCK(ar->inode, inquota - ar->len);
|
|
DQUOT_FREE_BLOCK(ar->inode, inquota - ar->len);
|
|
|
|
|
|
- kmem_cache_free(ext4_ac_cachep, ac);
|
|
|
|
return block;
|
|
return block;
|
|
}
|
|
}
|
|
static void ext4_mb_poll_new_transaction(struct super_block *sb,
|
|
static void ext4_mb_poll_new_transaction(struct super_block *sb,
|
|
@@ -4242,7 +4419,7 @@ do_more:
|
|
overflow = bit + count - EXT4_BLOCKS_PER_GROUP(sb);
|
|
overflow = bit + count - EXT4_BLOCKS_PER_GROUP(sb);
|
|
count -= overflow;
|
|
count -= overflow;
|
|
}
|
|
}
|
|
- bitmap_bh = read_block_bitmap(sb, block_group);
|
|
|
|
|
|
+ bitmap_bh = ext4_read_block_bitmap(sb, block_group);
|
|
if (!bitmap_bh)
|
|
if (!bitmap_bh)
|
|
goto error_return;
|
|
goto error_return;
|
|
gdp = ext4_get_group_desc(sb, block_group, &gd_bh);
|
|
gdp = ext4_get_group_desc(sb, block_group, &gd_bh);
|
|
@@ -4309,10 +4486,9 @@ do_more:
|
|
ext4_mb_free_metadata(handle, &e4b, block_group, bit, count);
|
|
ext4_mb_free_metadata(handle, &e4b, block_group, bit, count);
|
|
} else {
|
|
} else {
|
|
ext4_lock_group(sb, block_group);
|
|
ext4_lock_group(sb, block_group);
|
|
- err = mb_free_blocks(inode, &e4b, bit, count);
|
|
|
|
|
|
+ mb_free_blocks(inode, &e4b, bit, count);
|
|
ext4_mb_return_to_preallocation(inode, &e4b, block, count);
|
|
ext4_mb_return_to_preallocation(inode, &e4b, block, count);
|
|
ext4_unlock_group(sb, block_group);
|
|
ext4_unlock_group(sb, block_group);
|
|
- BUG_ON(err != 0);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
spin_lock(sb_bgl_lock(sbi, block_group));
|
|
spin_lock(sb_bgl_lock(sbi, block_group));
|
|
@@ -4321,6 +4497,13 @@ do_more:
|
|
spin_unlock(sb_bgl_lock(sbi, block_group));
|
|
spin_unlock(sb_bgl_lock(sbi, block_group));
|
|
percpu_counter_add(&sbi->s_freeblocks_counter, count);
|
|
percpu_counter_add(&sbi->s_freeblocks_counter, count);
|
|
|
|
|
|
|
|
+ if (sbi->s_log_groups_per_flex) {
|
|
|
|
+ ext4_group_t flex_group = ext4_flex_group(sbi, block_group);
|
|
|
|
+ spin_lock(sb_bgl_lock(sbi, flex_group));
|
|
|
|
+ sbi->s_flex_groups[flex_group].free_blocks += count;
|
|
|
|
+ spin_unlock(sb_bgl_lock(sbi, flex_group));
|
|
|
|
+ }
|
|
|
|
+
|
|
ext4_mb_release_desc(&e4b);
|
|
ext4_mb_release_desc(&e4b);
|
|
|
|
|
|
*freed += count;
|
|
*freed += count;
|