gfs2: per-filesystem bufdata cache Turn the per-module bufdata cache into a per-filesystem cache. The cache is destroyed on unmount, so any possible bufdata leaks are detected then instead of when the gfs2 module is removed. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 403b5e1..03eac36 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c
@@ -694,7 +694,7 @@ bool gfs2_release_folio(struct folio *folio, gfp_t gfp_mask) if (!bd->bd_blkno && !list_empty(&bd->bd_list)) list_del_init(&bd->bd_list); if (list_empty(&bd->bd_list)) - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); } bh = bh->b_this_page;
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 6146577..dec6bcc 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h
@@ -847,6 +847,7 @@ struct gfs2_sbd { unsigned long sd_last_warning; struct dentry *debugfs_dir; /* debugfs directory */ + struct kmem_cache *sd_bufdata; }; #define glock_sbd(gl) ((gl)->gl_name.ln_sbd)
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 8397d345..b47d79e 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c
@@ -983,7 +983,7 @@ static void empty_ail1_list(struct gfs2_sbd *sdp) } } -static void gfs2_trans_drain_list(struct list_head *list) +static void gfs2_trans_drain_list(struct gfs2_sbd *sdp, struct list_head *list) { struct gfs2_bufdata *bd; @@ -992,24 +992,25 @@ static void gfs2_trans_drain_list(struct list_head *list) list_del_init(&bd->bd_list); if (!list_empty(&bd->bd_ail_st_list)) gfs2_remove_from_ail(bd); - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); } } /** * gfs2_trans_drain - drain the buf and databuf queue for a failed transaction + * @sdp: the superblock * @tr: the transaction to drain * * When this is called, we're taking an error exit for a log write that failed * but since we bypassed the after_commit functions, we need to remove the * items from the buf and databuf queue. */ -static void gfs2_trans_drain(struct gfs2_trans *tr) +static void gfs2_trans_drain(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { if (!tr) return; - gfs2_trans_drain_list(&tr->tr_buf); - gfs2_trans_drain_list(&tr->tr_databuf); + gfs2_trans_drain_list(sdp, &tr->tr_buf); + gfs2_trans_drain_list(sdp, &tr->tr_databuf); } void gfs2_remove_from_journal(struct buffer_head *bh, int meta) @@ -1037,7 +1038,7 @@ void gfs2_remove_from_journal(struct buffer_head *bh, int meta) gfs2_trans_add_revoke(sdp, bd); } else if (was_pinned) { bh->b_private = NULL; - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); } else if (!list_empty(&bd->bd_ail_st_list) && !list_empty(&bd->bd_ail_gl_list)) { gfs2_remove_from_ail(bd); @@ -1181,7 +1182,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) return; out_withdraw: - gfs2_trans_drain(tr); + gfs2_trans_drain(sdp, tr); /** * If the tr_list is empty, we're withdrawing during a log * flush that targets a transaction, but the transaction was
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 6dabe73..797931e 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c
@@ -894,7 +894,7 @@ void gfs2_drain_revokes(struct gfs2_sbd *sdp) list_del_init(&bd->bd_list); gl = bd->bd_gl; gfs2_glock_remove_revoke(gl); - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); } }
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 9d65719..1b38d73 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c
@@ -115,12 +115,6 @@ static int __init init_gfs2_fs(void) if (!gfs2_inode_cachep) goto fail_cachep3; - gfs2_bufdata_cachep = kmem_cache_create("gfs2_bufdata", - sizeof(struct gfs2_bufdata), - 0, 0, NULL); - if (!gfs2_bufdata_cachep) - goto fail_cachep4; - gfs2_rgrpd_cachep = kmem_cache_create("gfs2_rgrpd", sizeof(struct gfs2_rgrpd), 0, 0, NULL); @@ -205,8 +199,6 @@ static int __init init_gfs2_fs(void) fail_cachep6: kmem_cache_destroy(gfs2_rgrpd_cachep); fail_cachep5: - kmem_cache_destroy(gfs2_bufdata_cachep); -fail_cachep4: kmem_cache_destroy(gfs2_inode_cachep); fail_cachep3: kmem_cache_destroy(gfs2_glock_aspace_cachep); @@ -245,7 +237,6 @@ static void __exit exit_gfs2_fs(void) kmem_cache_destroy(gfs2_qadata_cachep); kmem_cache_destroy(gfs2_quotad_cachep); kmem_cache_destroy(gfs2_rgrpd_cachep); - kmem_cache_destroy(gfs2_bufdata_cachep); kmem_cache_destroy(gfs2_inode_cachep); kmem_cache_destroy(gfs2_glock_aspace_cachep); kmem_cache_destroy(gfs2_glock_cachep);
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index c7d57de..36e9bc8 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c
@@ -1198,9 +1198,17 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) if (!sdp->sd_delete_wq) goto fail_glock_wq; + char *bufdata_name = kasprintf(GFP_KERNEL, "gfs2-bufdata/%s", sdp->sd_fsname); + sdp->sd_bufdata = kmem_cache_create(bufdata_name, + sizeof(struct gfs2_bufdata), + 0, 0, NULL); + kfree(bufdata_name); + if (!sdp->sd_bufdata) + goto fail_delete_wq; + error = gfs2_sys_fs_add(sdp); if (error) - goto fail_delete_wq; + goto fail_bufdata; gfs2_create_debugfs_file(sdp); @@ -1305,6 +1313,8 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) fail_debug: gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp); +fail_bufdata: + kmem_cache_destroy(sdp->sd_bufdata); fail_delete_wq: destroy_workqueue(sdp->sd_delete_wq); fail_glock_wq:
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index e4219a0..3fabd57 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c
@@ -639,6 +639,7 @@ static void gfs2_put_super(struct super_block *sb) /* Take apart glock structures and buffer lists */ gfs2_gl_hash_clear(sdp); iput(sdp->sd_inode); + kmem_cache_destroy(sdp->sd_bufdata); gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp);
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index 65cbe06..0c22bc7 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c
@@ -165,12 +165,15 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) sb_end_intwrite(sdp->sd_vfs); } +struct gfs2_sbd; + static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl, struct buffer_head *bh) { + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; struct gfs2_bufdata *bd; - bd = kmem_cache_zalloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL); + bd = kmem_cache_zalloc(sdp->sd_bufdata, GFP_NOFS | __GFP_NOFAIL); bd->bd_bh = bh; bd->bd_gl = gl; INIT_LIST_HEAD(&bd->bd_list); @@ -213,7 +216,7 @@ void gfs2_trans_add_data(struct gfs2_glock *gl, struct buffer_head *bh) lock_buffer(bh); spin_lock(&sdp->sd_log_lock); if (bh->b_private) { - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); bd = bh->b_private; } else { bh->b_private = bd; @@ -277,7 +280,7 @@ void gfs2_trans_add_meta(struct gfs2_glock *gl, struct buffer_head *bh) lock_buffer(bh); spin_lock(&sdp->sd_log_lock); if (bh->b_private) { - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); bd = bh->b_private; } else { bh->b_private = bd; @@ -340,7 +343,7 @@ void gfs2_trans_remove_revoke(struct gfs2_sbd *sdp, u64 blkno, unsigned int len) sdp->sd_log_num_revoke--; if (bd->bd_gl) gfs2_glock_remove_revoke(bd->bd_gl); - kmem_cache_free(gfs2_bufdata_cachep, bd); + kmem_cache_free(sdp->sd_bufdata, bd); gfs2_log_release_revokes(sdp, 1); if (--n == 0) break;
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 0260320..c35c882 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c
@@ -29,7 +29,6 @@ struct kmem_cache *gfs2_glock_cachep __read_mostly; struct kmem_cache *gfs2_glock_aspace_cachep __read_mostly; struct kmem_cache *gfs2_inode_cachep __read_mostly; -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;
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h index ffcc47d..940dc45 100644 --- a/fs/gfs2/util.h +++ b/fs/gfs2/util.h
@@ -160,7 +160,6 @@ gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__) extern struct kmem_cache *gfs2_glock_cachep; extern struct kmem_cache *gfs2_glock_aspace_cachep; extern struct kmem_cache *gfs2_inode_cachep; -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;