]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/ext4/mballoc.c
Merge tag 'char-misc-4.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
[karo-tx-linux.git] / fs / ext4 / mballoc.c
index b7928cddd539ee223584db33ab8cb3341faa2e86..5a1052627a81413685241f0c4669bbf755e2c5f9 100644 (file)
@@ -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)
 {
@@ -2297,9 +2295,12 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
        int err, buddy_loaded = 0;
        struct ext4_buddy e4b;
        struct ext4_group_info *grinfo;
+       unsigned char blocksize_bits = min_t(unsigned char,
+                                            sb->s_blocksize_bits,
+                                            EXT4_MAX_BLOCK_LOG_SIZE);
        struct sg {
                struct ext4_group_info info;
-               ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2];
+               ext4_grpblk_t counters[blocksize_bits + 2];
        } sg;
 
        group--;
@@ -2308,8 +2309,6 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
                              " 2^0   2^1   2^2   2^3   2^4   2^5   2^6  "
                              " 2^7   2^8   2^9   2^10  2^11  2^12  2^13  ]\n");
 
-       i = (sb->s_blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) +
-               sizeof(struct ext4_group_info);
        grinfo = ext4_get_group_info(sb, group);
        /* Load the group info in memory only if not already loaded. */
        if (unlikely(EXT4_MB_GRP_NEED_INIT(grinfo))) {
@@ -2321,7 +2320,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
                buddy_loaded = 1;
        }
 
-       memcpy(&sg, ext4_get_group_info(sb, group), i);
+       memcpy(&sg, ext4_get_group_info(sb, group), sizeof(sg));
 
        if (buddy_loaded)
                ext4_mb_unload_buddy(&e4b);
@@ -2329,7 +2328,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
        seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free,
                        sg.info.bb_fragments, sg.info.bb_first_free);
        for (i = 0; i <= 13; i++)
-               seq_printf(seq, " %-5u", i <= sb->s_blocksize_bits + 1 ?
+               seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ?
                                sg.info.bb_counters[i] : 0);
        seq_printf(seq, " ]\n");
 
@@ -2639,6 +2638,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 +2782,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 +2792,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 +2811,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 +2851,58 @@ 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);
+                       bio_put(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,
@@ -3529,7 +3570,7 @@ void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
                ext4_set_bits(bitmap, start, len);
                preallocated += len;
        }
-       mb_debug(1, "prellocated %u for group %u\n", preallocated, group);
+       mb_debug(1, "preallocated %u for group %u\n", preallocated, group);
 }
 
 static void ext4_mb_pa_callback(struct rcu_head *head)
@@ -4464,7 +4505,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
        trace_ext4_request_blocks(ar);
 
        /* Allow to use superuser reservation for quota file */
-       if (IS_NOQUOTA(ar->inode))
+       if (ext4_is_quota_file(ar->inode))
                ar->flags |= EXT4_MB_USE_ROOT_BLOCKS;
 
        if ((ar->flags & EXT4_MB_DELALLOC_RESERVED) == 0) {
@@ -4583,14 +4624,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 +4701,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 +4916,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 +5140,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;