]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
gfs2: new slab for transactions
authorBob Peterson <rpeterso@redhat.com>
Wed, 17 Apr 2019 18:04:27 +0000 (12:04 -0600)
committerAndreas Gruenbacher <agruenba@redhat.com>
Fri, 5 Jun 2020 19:24:25 +0000 (21:24 +0200)
This patch adds a new slab for gfs2 transactions. That allows us to
reduce kernel memory fragmentation, have better organization of data
for analysis of vmcore dumps. A new centralized function is added to
free the slab objects, and it exposes use-after-free by giving
warnings if a transaction is freed while it still has bd elements
attached to its buffers or ail lists. We make sure to initialize
those transaction ail lists so we can check their integrity when freeing.

At a later time, we should add a slab initialization function to
make it more efficient, but for this initial patch I wanted to
minimize the impact.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/log.c
fs/gfs2/main.c
fs/gfs2/trans.c
fs/gfs2/trans.h
fs/gfs2/util.c
fs/gfs2/util.h

index a81af1bde1bbe219e3e9b33300ac803e97242a6d..a7415ab91c5f3502262f1e03aebea98c8adaf80a 100644 (file)
@@ -30,6 +30,7 @@
 #include "util.h"
 #include "dir.h"
 #include "trace_gfs2.h"
+#include "trans.h"
 
 static void gfs2_log_shutdown(struct gfs2_sbd *sdp);
 
@@ -378,7 +379,7 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
                list_del(&tr->tr_list);
                gfs2_assert_warn(sdp, list_empty(&tr->tr_ail1_list));
                gfs2_assert_warn(sdp, list_empty(&tr->tr_ail2_list));
-               kfree(tr);
+               gfs2_trans_free(sdp, tr);
        }
 
        spin_unlock(&sdp->sd_ail_lock);
@@ -863,14 +864,14 @@ static void ail_drain(struct gfs2_sbd *sdp)
                gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail1_list);
                gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail2_list);
                list_del(&tr->tr_list);
-               kfree(tr);
+               gfs2_trans_free(sdp, tr);
        }
        while (!list_empty(&sdp->sd_ail2_list)) {
                tr = list_first_entry(&sdp->sd_ail2_list, struct gfs2_trans,
                                      tr_list);
                gfs2_ail_empty_tr(sdp, tr, &tr->tr_ail2_list);
                list_del(&tr->tr_list);
-               kfree(tr);
+               gfs2_trans_free(sdp, tr);
        }
        spin_unlock(&sdp->sd_ail_lock);
 }
@@ -1008,7 +1009,7 @@ out:
        trace_gfs2_log_flush(sdp, 0, flags);
        up_write(&sdp->sd_log_flush_lock);
 
-       kfree(tr);
+       gfs2_trans_free(sdp, tr);
 }
 
 /**
index a1a295b739fb893586dd5130edb15c2063852ea5..733470ca6be9d5812dd1b8ee5f7da6051f22aeda 100644 (file)
@@ -143,6 +143,12 @@ static int __init init_gfs2_fs(void)
        if (!gfs2_qadata_cachep)
                goto fail_cachep7;
 
+       gfs2_trans_cachep = kmem_cache_create("gfs2_trans",
+                                              sizeof(struct gfs2_trans),
+                                              0, 0, NULL);
+       if (!gfs2_trans_cachep)
+               goto fail_cachep8;
+
        error = register_shrinker(&gfs2_qd_shrinker);
        if (error)
                goto fail_shrinker;
@@ -194,6 +200,8 @@ fail_fs2:
 fail_fs1:
        unregister_shrinker(&gfs2_qd_shrinker);
 fail_shrinker:
+       kmem_cache_destroy(gfs2_trans_cachep);
+fail_cachep8:
        kmem_cache_destroy(gfs2_qadata_cachep);
 fail_cachep7:
        kmem_cache_destroy(gfs2_quotad_cachep);
@@ -236,6 +244,7 @@ static void __exit exit_gfs2_fs(void)
        rcu_barrier();
 
        mempool_destroy(gfs2_page_pool);
+       kmem_cache_destroy(gfs2_trans_cachep);
        kmem_cache_destroy(gfs2_qadata_cachep);
        kmem_cache_destroy(gfs2_quotad_cachep);
        kmem_cache_destroy(gfs2_rgrpd_cachep);
index 62a65ed9a9f5d7e16401ec00c5d87866e9dcb270..a3dfa3aa87ad920147363de4d2f92e2e2c54f9ae 100644 (file)
@@ -37,7 +37,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
        if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))
                return -EROFS;
 
-       tr = kzalloc(sizeof(struct gfs2_trans), GFP_NOFS);
+       tr = kmem_cache_zalloc(gfs2_trans_cachep, GFP_NOFS);
        if (!tr)
                return -ENOMEM;
 
@@ -67,7 +67,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
 
 fail:
        sb_end_intwrite(sdp->sd_vfs);
-       kfree(tr);
+       kmem_cache_free(gfs2_trans_cachep, tr);
 
        return error;
 }
@@ -95,7 +95,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
        if (!test_bit(TR_TOUCHED, &tr->tr_flags)) {
                gfs2_log_release(sdp, tr->tr_reserved);
                if (alloced) {
-                       kfree(tr);
+                       gfs2_trans_free(sdp, tr);
                        sb_end_intwrite(sdp->sd_vfs);
                }
                return;
@@ -111,7 +111,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
 
        gfs2_log_commit(sdp, tr);
        if (alloced && !test_bit(TR_ATTACHED, &tr->tr_flags))
-               kfree(tr);
+               gfs2_trans_free(sdp, tr);
        up_read(&sdp->sd_log_flush_lock);
 
        if (sdp->sd_vfs->s_flags & SB_SYNCHRONOUS)
@@ -278,3 +278,14 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len)
        gfs2_log_unlock(sdp);
 }
 
+void gfs2_trans_free(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
+{
+       if (tr == NULL)
+               return;
+
+       gfs2_assert_warn(sdp, list_empty(&tr->tr_ail1_list));
+       gfs2_assert_warn(sdp, list_empty(&tr->tr_ail2_list));
+       gfs2_assert_warn(sdp, list_empty(&tr->tr_databuf));
+       gfs2_assert_warn(sdp, list_empty(&tr->tr_buf));
+       kmem_cache_free(gfs2_trans_cachep, tr);
+}
index 6071334de03585a6a21f693d0b3ee64779190c3c..83199ce5a5c51613f839f7329aa17ce651c45207 100644 (file)
@@ -42,5 +42,6 @@ extern void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh);
 extern void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh);
 extern void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
 extern void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len);
+extern void gfs2_trans_free(struct gfs2_sbd *sdp, struct gfs2_trans *tr);
 
 #endif /* __TRANS_DOT_H__ */
index aa087a5675af6ccafb6b30e9298f3d23cc8e2e76..1cd0328cae20a4d08183adca3022d4e69d9a45d3 100644 (file)
@@ -32,6 +32,7 @@ struct kmem_cache *gfs2_bufdata_cachep __read_mostly;
 struct kmem_cache *gfs2_rgrpd_cachep __read_mostly;
 struct kmem_cache *gfs2_quotad_cachep __read_mostly;
 struct kmem_cache *gfs2_qadata_cachep __read_mostly;
+struct kmem_cache *gfs2_trans_cachep __read_mostly;
 mempool_t *gfs2_page_pool __read_mostly;
 
 void gfs2_assert_i(struct gfs2_sbd *sdp)
index a3542560da6ffdc25f6ab97f79cbec7028aaf875..6d9157efe16c3b38ddcacd48d35eae674867f807 100644 (file)
@@ -172,6 +172,7 @@ extern struct kmem_cache *gfs2_bufdata_cachep;
 extern struct kmem_cache *gfs2_rgrpd_cachep;
 extern struct kmem_cache *gfs2_quotad_cachep;
 extern struct kmem_cache *gfs2_qadata_cachep;
+extern struct kmem_cache *gfs2_trans_cachep;
 extern mempool_t *gfs2_page_pool;
 extern struct workqueue_struct *gfs2_control_wq;