|
|
@@ -367,8 +367,6 @@ static void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
|
|
|
ext4_group_t group);
|
|
|
static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,
|
|
|
ext4_group_t group);
|
|
|
-static void ext4_free_data_callback(struct super_block *sb,
|
|
|
- struct ext4_journal_cb_entry *jce, int rc);
|
|
|
|
|
|
static inline void *mb_correct_addr_and_bit(int *bit, void *addr)
|
|
|
{
|
|
|
@@ -2639,6 +2637,7 @@ int ext4_mb_init(struct super_block *sb)
|
|
|
spin_lock_init(&sbi->s_md_lock);
|
|
|
spin_lock_init(&sbi->s_bal_lock);
|
|
|
sbi->s_mb_free_pending = 0;
|
|
|
+ INIT_LIST_HEAD(&sbi->s_freed_data_list);
|
|
|
|
|
|
sbi->s_mb_max_to_scan = MB_DEFAULT_MAX_TO_SCAN;
|
|
|
sbi->s_mb_min_to_scan = MB_DEFAULT_MIN_TO_SCAN;
|
|
|
@@ -2782,7 +2781,8 @@ 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 cluster, int count)
|
|
|
+ ext4_group_t block_group, ext4_grpblk_t cluster, int count,
|
|
|
+ struct bio **biop)
|
|
|
{
|
|
|
ext4_fsblk_t discard_block;
|
|
|
|
|
|
@@ -2791,18 +2791,18 @@ static inline int ext4_issue_discard(struct super_block *sb,
|
|
|
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);
|
|
|
+ if (biop) {
|
|
|
+ return __blkdev_issue_discard(sb->s_bdev,
|
|
|
+ (sector_t)discard_block << (sb->s_blocksize_bits - 9),
|
|
|
+ (sector_t)count << (sb->s_blocksize_bits - 9),
|
|
|
+ GFP_NOFS, 0, biop);
|
|
|
+ } else
|
|
|
+ return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * This function is called by the jbd2 layer once the commit has finished,
|
|
|
- * so we know we can free the blocks that were released with that commit.
|
|
|
- */
|
|
|
-static void ext4_free_data_callback(struct super_block *sb,
|
|
|
- struct ext4_journal_cb_entry *jce,
|
|
|
- int rc)
|
|
|
+static void ext4_free_data_in_buddy(struct super_block *sb,
|
|
|
+ struct ext4_free_data *entry)
|
|
|
{
|
|
|
- struct ext4_free_data *entry = (struct ext4_free_data *)jce;
|
|
|
struct ext4_buddy e4b;
|
|
|
struct ext4_group_info *db;
|
|
|
int err, count = 0, count2 = 0;
|
|
|
@@ -2810,18 +2810,6 @@ static void ext4_free_data_callback(struct super_block *sb,
|
|
|
mb_debug(1, "gonna free %u blocks in group %u (0x%p):",
|
|
|
entry->efd_count, entry->efd_group, entry);
|
|
|
|
|
|
- if (test_opt(sb, DISCARD)) {
|
|
|
- err = ext4_issue_discard(sb, entry->efd_group,
|
|
|
- entry->efd_start_cluster,
|
|
|
- entry->efd_count);
|
|
|
- if (err && err != -EOPNOTSUPP)
|
|
|
- ext4_msg(sb, KERN_WARNING, "discard request in"
|
|
|
- " group:%d block:%d count:%d failed"
|
|
|
- " with %d", entry->efd_group,
|
|
|
- entry->efd_start_cluster,
|
|
|
- entry->efd_count, err);
|
|
|
- }
|
|
|
-
|
|
|
err = ext4_mb_load_buddy(sb, entry->efd_group, &e4b);
|
|
|
/* we expect to find existing buddy because it's pinned */
|
|
|
BUG_ON(err != 0);
|
|
|
@@ -2862,6 +2850,56 @@ static void ext4_free_data_callback(struct super_block *sb,
|
|
|
mb_debug(1, "freed %u blocks in %u structures\n", count, count2);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This function is called by the jbd2 layer once the commit has finished,
|
|
|
+ * so we know we can free the blocks that were released with that commit.
|
|
|
+ */
|
|
|
+void ext4_process_freed_data(struct super_block *sb, tid_t commit_tid)
|
|
|
+{
|
|
|
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
|
|
|
+ struct ext4_free_data *entry, *tmp;
|
|
|
+ struct bio *discard_bio = NULL;
|
|
|
+ struct list_head freed_data_list;
|
|
|
+ struct list_head *cut_pos = NULL;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&freed_data_list);
|
|
|
+
|
|
|
+ spin_lock(&sbi->s_md_lock);
|
|
|
+ list_for_each_entry(entry, &sbi->s_freed_data_list, efd_list) {
|
|
|
+ if (entry->efd_tid != commit_tid)
|
|
|
+ break;
|
|
|
+ cut_pos = &entry->efd_list;
|
|
|
+ }
|
|
|
+ if (cut_pos)
|
|
|
+ list_cut_position(&freed_data_list, &sbi->s_freed_data_list,
|
|
|
+ cut_pos);
|
|
|
+ spin_unlock(&sbi->s_md_lock);
|
|
|
+
|
|
|
+ if (test_opt(sb, DISCARD)) {
|
|
|
+ list_for_each_entry(entry, &freed_data_list, efd_list) {
|
|
|
+ err = ext4_issue_discard(sb, entry->efd_group,
|
|
|
+ entry->efd_start_cluster,
|
|
|
+ entry->efd_count,
|
|
|
+ &discard_bio);
|
|
|
+ if (err && err != -EOPNOTSUPP) {
|
|
|
+ ext4_msg(sb, KERN_WARNING, "discard request in"
|
|
|
+ " group:%d block:%d count:%d failed"
|
|
|
+ " with %d", entry->efd_group,
|
|
|
+ entry->efd_start_cluster,
|
|
|
+ entry->efd_count, err);
|
|
|
+ } else if (err == -EOPNOTSUPP)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (discard_bio)
|
|
|
+ submit_bio_wait(discard_bio);
|
|
|
+ }
|
|
|
+
|
|
|
+ list_for_each_entry_safe(entry, tmp, &freed_data_list, efd_list)
|
|
|
+ ext4_free_data_in_buddy(sb, entry);
|
|
|
+}
|
|
|
+
|
|
|
int __init ext4_init_mballoc(void)
|
|
|
{
|
|
|
ext4_pspace_cachep = KMEM_CACHE(ext4_prealloc_space,
|
|
|
@@ -4583,14 +4621,28 @@ out:
|
|
|
* are contiguous, AND the extents were freed by the same transaction,
|
|
|
* AND the blocks are associated with the same group.
|
|
|
*/
|
|
|
-static int can_merge(struct ext4_free_data *entry1,
|
|
|
- struct ext4_free_data *entry2)
|
|
|
+static void ext4_try_merge_freed_extent(struct ext4_sb_info *sbi,
|
|
|
+ struct ext4_free_data *entry,
|
|
|
+ struct ext4_free_data *new_entry,
|
|
|
+ struct rb_root *entry_rb_root)
|
|
|
{
|
|
|
- if ((entry1->efd_tid == entry2->efd_tid) &&
|
|
|
- (entry1->efd_group == entry2->efd_group) &&
|
|
|
- ((entry1->efd_start_cluster + entry1->efd_count) == entry2->efd_start_cluster))
|
|
|
- return 1;
|
|
|
- return 0;
|
|
|
+ if ((entry->efd_tid != new_entry->efd_tid) ||
|
|
|
+ (entry->efd_group != new_entry->efd_group))
|
|
|
+ return;
|
|
|
+ if (entry->efd_start_cluster + entry->efd_count ==
|
|
|
+ new_entry->efd_start_cluster) {
|
|
|
+ new_entry->efd_start_cluster = entry->efd_start_cluster;
|
|
|
+ new_entry->efd_count += entry->efd_count;
|
|
|
+ } else if (new_entry->efd_start_cluster + new_entry->efd_count ==
|
|
|
+ entry->efd_start_cluster) {
|
|
|
+ new_entry->efd_count += entry->efd_count;
|
|
|
+ } else
|
|
|
+ return;
|
|
|
+ spin_lock(&sbi->s_md_lock);
|
|
|
+ list_del(&entry->efd_list);
|
|
|
+ spin_unlock(&sbi->s_md_lock);
|
|
|
+ rb_erase(&entry->efd_node, entry_rb_root);
|
|
|
+ kmem_cache_free(ext4_free_data_cachep, entry);
|
|
|
}
|
|
|
|
|
|
static noinline_for_stack int
|
|
|
@@ -4646,29 +4698,19 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
|
|
|
node = rb_prev(new_node);
|
|
|
if (node) {
|
|
|
entry = rb_entry(node, struct ext4_free_data, efd_node);
|
|
|
- if (can_merge(entry, new_entry) &&
|
|
|
- ext4_journal_callback_try_del(handle, &entry->efd_jce)) {
|
|
|
- new_entry->efd_start_cluster = entry->efd_start_cluster;
|
|
|
- new_entry->efd_count += entry->efd_count;
|
|
|
- rb_erase(node, &(db->bb_free_root));
|
|
|
- kmem_cache_free(ext4_free_data_cachep, entry);
|
|
|
- }
|
|
|
+ ext4_try_merge_freed_extent(sbi, entry, new_entry,
|
|
|
+ &(db->bb_free_root));
|
|
|
}
|
|
|
|
|
|
node = rb_next(new_node);
|
|
|
if (node) {
|
|
|
entry = rb_entry(node, struct ext4_free_data, efd_node);
|
|
|
- if (can_merge(new_entry, entry) &&
|
|
|
- ext4_journal_callback_try_del(handle, &entry->efd_jce)) {
|
|
|
- new_entry->efd_count += entry->efd_count;
|
|
|
- rb_erase(node, &(db->bb_free_root));
|
|
|
- kmem_cache_free(ext4_free_data_cachep, entry);
|
|
|
- }
|
|
|
+ ext4_try_merge_freed_extent(sbi, entry, new_entry,
|
|
|
+ &(db->bb_free_root));
|
|
|
}
|
|
|
- /* Add the extent to transaction's private list */
|
|
|
- new_entry->efd_jce.jce_func = ext4_free_data_callback;
|
|
|
+
|
|
|
spin_lock(&sbi->s_md_lock);
|
|
|
- _ext4_journal_callback_add(handle, &new_entry->efd_jce);
|
|
|
+ list_add_tail(&new_entry->efd_list, &sbi->s_freed_data_list);
|
|
|
sbi->s_mb_free_pending += clusters;
|
|
|
spin_unlock(&sbi->s_md_lock);
|
|
|
return 0;
|
|
|
@@ -4871,7 +4913,8 @@ do_more:
|
|
|
* them with group lock_held
|
|
|
*/
|
|
|
if (test_opt(sb, DISCARD)) {
|
|
|
- err = ext4_issue_discard(sb, block_group, bit, count);
|
|
|
+ err = ext4_issue_discard(sb, block_group, bit, count,
|
|
|
+ NULL);
|
|
|
if (err && err != -EOPNOTSUPP)
|
|
|
ext4_msg(sb, KERN_WARNING, "discard request in"
|
|
|
" group:%d block:%d count:%lu failed"
|
|
|
@@ -5094,7 +5137,7 @@ __acquires(bitlock)
|
|
|
*/
|
|
|
mb_mark_used(e4b, &ex);
|
|
|
ext4_unlock_group(sb, group);
|
|
|
- ret = ext4_issue_discard(sb, group, start, count);
|
|
|
+ ret = ext4_issue_discard(sb, group, start, count, NULL);
|
|
|
ext4_lock_group(sb, group);
|
|
|
mb_free_blocks(NULL, e4b, start, ex.fe_len);
|
|
|
return ret;
|