Skip to content

Commit

Permalink
ext4: teach ext4_free_blocks() about bigalloc and clusters
Browse files Browse the repository at this point in the history
The ext4_free_blocks() function now has two new flags that indicate
whether a partial cluster at the beginning or the end of the block
extents should be freed or not.  That will be up the caller (i.e.,
truncate), who can figure out whether partial clusters at the
beginning or the end of a block range can be freed.

We also have to update the ext4_mb_free_metadata() and
release_blocks_on_commit() machinery to be cluster-based, since it is
used by ext4_free_blocks().

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
  • Loading branch information
Theodore Ts'o committed Sep 9, 2011
1 parent 53accfa commit 8413019
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
2 changes: 2 additions & 0 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ struct ext4_new_group_data {
#define EXT4_FREE_BLOCKS_FORGET 0x0002
#define EXT4_FREE_BLOCKS_VALIDATED 0x0004
#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008
#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010
#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020

/*
* Flags used by ext4_discard_partial_page_buffers
Expand Down
86 changes: 62 additions & 24 deletions fs/ext4/mballoc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2602,11 +2602,13 @@ int ext4_mb_release(struct super_block *sb)
}

static inline int ext4_issue_discard(struct super_block *sb,
ext4_group_t block_group, ext4_grpblk_t block, int count)
ext4_group_t block_group, ext4_grpblk_t cluster, int count)
{
ext4_fsblk_t discard_block;

discard_block = block + ext4_group_first_block_no(sb, block_group);
discard_block = (EXT4_C2B(EXT4_SB(sb), cluster) +
ext4_group_first_block_no(sb, block_group));
count = EXT4_C2B(EXT4_SB(sb), count);
trace_ext4_discard_blocks(sb,
(unsigned long long) discard_block, count);
return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0);
Expand All @@ -2633,7 +2635,7 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)

if (test_opt(sb, DISCARD))
ext4_issue_discard(sb, entry->group,
entry->start_blk, entry->count);
entry->start_cluster, entry->count);

err = ext4_mb_load_buddy(sb, entry->group, &e4b);
/* we expect to find existing buddy because it's pinned */
Expand All @@ -2646,7 +2648,7 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
ext4_lock_group(sb, entry->group);
/* Take it out of per group rb tree */
rb_erase(&entry->node, &(db->bb_free_root));
mb_free_blocks(NULL, &e4b, entry->start_blk, entry->count);
mb_free_blocks(NULL, &e4b, entry->start_cluster, entry->count);

/*
* Clear the trimmed flag for the group so that the next
Expand Down Expand Up @@ -3300,7 +3302,7 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,

while (n) {
entry = rb_entry(n, struct ext4_free_data, node);
ext4_set_bits(bitmap, entry->start_blk, entry->count);
ext4_set_bits(bitmap, entry->start_cluster, entry->count);
n = rb_next(n);
}
return;
Expand Down Expand Up @@ -4401,7 +4403,7 @@ static int can_merge(struct ext4_free_data *entry1,
{
if ((entry1->t_tid == entry2->t_tid) &&
(entry1->group == entry2->group) &&
((entry1->start_blk + entry1->count) == entry2->start_blk))
((entry1->start_cluster + entry1->count) == entry2->start_cluster))
return 1;
return 0;
}
Expand All @@ -4411,7 +4413,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
struct ext4_free_data *new_entry)
{
ext4_group_t group = e4b->bd_group;
ext4_grpblk_t block;
ext4_grpblk_t cluster;
struct ext4_free_data *entry;
struct ext4_group_info *db = e4b->bd_info;
struct super_block *sb = e4b->bd_sb;
Expand All @@ -4424,7 +4426,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
BUG_ON(e4b->bd_buddy_page == NULL);

new_node = &new_entry->node;
block = new_entry->start_blk;
cluster = new_entry->start_cluster;

if (!*n) {
/* first free block exent. We need to
Expand All @@ -4438,13 +4440,14 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
while (*n) {
parent = *n;
entry = rb_entry(parent, struct ext4_free_data, node);
if (block < entry->start_blk)
if (cluster < entry->start_cluster)
n = &(*n)->rb_left;
else if (block >= (entry->start_blk + entry->count))
else if (cluster >= (entry->start_cluster + entry->count))
n = &(*n)->rb_right;
else {
ext4_grp_locked_error(sb, group, 0,
ext4_group_first_block_no(sb, group) + block,
ext4_group_first_block_no(sb, group) +
EXT4_C2B(sbi, cluster),
"Block already on to-be-freed list");
return 0;
}
Expand All @@ -4458,7 +4461,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
if (node) {
entry = rb_entry(node, struct ext4_free_data, node);
if (can_merge(entry, new_entry)) {
new_entry->start_blk = entry->start_blk;
new_entry->start_cluster = entry->start_cluster;
new_entry->count += entry->count;
rb_erase(node, &(db->bb_free_root));
spin_lock(&sbi->s_md_lock);
Expand Down Expand Up @@ -4509,6 +4512,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
ext4_group_t block_group;
struct ext4_sb_info *sbi;
struct ext4_buddy e4b;
unsigned int count_clusters;
int err = 0;
int ret;

Expand Down Expand Up @@ -4557,6 +4561,38 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
if (!ext4_should_writeback_data(inode))
flags |= EXT4_FREE_BLOCKS_METADATA;

/*
* If the extent to be freed does not begin on a cluster
* boundary, we need to deal with partial clusters at the
* beginning and end of the extent. Normally we will free
* blocks at the beginning or the end unless we are explicitly
* requested to avoid doing so.
*/
overflow = block & (sbi->s_cluster_ratio - 1);
if (overflow) {
if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
overflow = sbi->s_cluster_ratio - overflow;
block += overflow;
if (count > overflow)
count -= overflow;
else
return;
} else {
block -= overflow;
count += overflow;
}
}
overflow = count & (sbi->s_cluster_ratio - 1);
if (overflow) {
if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) {
if (count > overflow)
count -= overflow;
else
return;
} else
count += sbi->s_cluster_ratio - overflow;
}

do_more:
overflow = 0;
ext4_get_group_no_and_offset(sb, block, &block_group, &bit);
Expand All @@ -4565,10 +4601,12 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
* Check to see if we are freeing blocks across a group
* boundary.
*/
if (bit + count > EXT4_CLUSTERS_PER_GROUP(sb)) {
overflow = bit + count - EXT4_CLUSTERS_PER_GROUP(sb);
if (EXT4_C2B(sbi, bit) + count > EXT4_BLOCKS_PER_GROUP(sb)) {
overflow = EXT4_C2B(sbi, bit) + count -
EXT4_BLOCKS_PER_GROUP(sb);
count -= overflow;
}
count_clusters = EXT4_B2C(sbi, count);
bitmap_bh = ext4_read_block_bitmap(sb, block_group);
if (!bitmap_bh) {
err = -EIO;
Expand All @@ -4583,9 +4621,9 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
if (in_range(ext4_block_bitmap(sb, gdp), block, count) ||
in_range(ext4_inode_bitmap(sb, gdp), block, count) ||
in_range(block, ext4_inode_table(sb, gdp),
EXT4_SB(sb)->s_itb_per_group) ||
EXT4_SB(sb)->s_itb_per_group) ||
in_range(block + count - 1, ext4_inode_table(sb, gdp),
EXT4_SB(sb)->s_itb_per_group)) {
EXT4_SB(sb)->s_itb_per_group)) {

ext4_error(sb, "Freeing blocks in system zone - "
"Block = %llu, count = %lu", block, count);
Expand All @@ -4610,11 +4648,11 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
#ifdef AGGRESSIVE_CHECK
{
int i;
for (i = 0; i < count; i++)
for (i = 0; i < count_clusters; i++)
BUG_ON(!mb_test_bit(bit + i, bitmap_bh->b_data));
}
#endif
trace_ext4_mballoc_free(sb, inode, block_group, bit, count);
trace_ext4_mballoc_free(sb, inode, block_group, bit, count_clusters);

err = ext4_mb_load_buddy(sb, block_group, &e4b);
if (err)
Expand All @@ -4631,25 +4669,25 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
err = -ENOMEM;
goto error_return;
}
new_entry->start_blk = bit;
new_entry->start_cluster = bit;
new_entry->group = block_group;
new_entry->count = count;
new_entry->count = count_clusters;
new_entry->t_tid = handle->h_transaction->t_tid;

ext4_lock_group(sb, block_group);
mb_clear_bits(bitmap_bh->b_data, bit, count);
mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
ext4_mb_free_metadata(handle, &e4b, new_entry);
} else {
/* need to update group_info->bb_free and bitmap
* with group lock held. generate_buddy look at
* them with group lock_held
*/
ext4_lock_group(sb, block_group);
mb_clear_bits(bitmap_bh->b_data, bit, count);
mb_free_blocks(inode, &e4b, bit, count);
mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
mb_free_blocks(inode, &e4b, bit, count_clusters);
}

ret = ext4_free_blks_count(sb, gdp) + count;
ret = ext4_free_blks_count(sb, gdp) + count_clusters;
ext4_free_blks_set(sb, gdp, ret);
gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp);
ext4_unlock_group(sb, block_group);
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/mballoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ struct ext4_free_data {
ext4_group_t group;

/* free block extent */
ext4_grpblk_t start_blk;
ext4_grpblk_t start_cluster;
ext4_grpblk_t count;

/* transaction which freed this extent */
Expand Down

0 comments on commit 8413019

Please sign in to comment.