Commit 40129b8c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull gfs2 fix from Andreas Gruenbacher:
 "Fix a memory leak on filesystem withdraw.

  We didn't detect this bug because we have slab merging on by default
  (CONFIG_SLAB_MERGE_DEFAULT). Adding 'slub_nomerge' to the kernel
  command line exposed the problem"

* tag 'gfs2-v5.9-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: add some much needed cleanup for log flushes that fail
parents b0bfd5ec 462582b9
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -901,6 +901,36 @@ static void empty_ail1_list(struct gfs2_sbd *sdp)
	}
}

/**
 * drain_bd - drain the buf and databuf queue for a failed transaction
 * @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 trans_drain(struct gfs2_trans *tr)
{
	struct gfs2_bufdata *bd;
	struct list_head *head;

	if (!tr)
		return;

	head = &tr->tr_buf;
	while (!list_empty(head)) {
		bd = list_first_entry(head, struct gfs2_bufdata, bd_list);
		list_del_init(&bd->bd_list);
		kmem_cache_free(gfs2_bufdata_cachep, bd);
	}
	head = &tr->tr_databuf;
	while (!list_empty(head)) {
		bd = list_first_entry(head, struct gfs2_bufdata, bd_list);
		list_del_init(&bd->bd_list);
		kmem_cache_free(gfs2_bufdata_cachep, bd);
	}
}

/**
 * gfs2_log_flush - flush incore transaction(s)
 * @sdp: the filesystem
@@ -1005,6 +1035,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)

out:
	if (gfs2_withdrawn(sdp)) {
		trans_drain(tr);
		/**
		 * If the tr_list is empty, we're withdrawing during a log
		 * flush that targets a transaction, but the transaction was
+1 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
		tr->tr_reserved += gfs2_struct2blk(sdp, revokes);
	INIT_LIST_HEAD(&tr->tr_databuf);
	INIT_LIST_HEAD(&tr->tr_buf);
	INIT_LIST_HEAD(&tr->tr_list);
	INIT_LIST_HEAD(&tr->tr_ail1_list);
	INIT_LIST_HEAD(&tr->tr_ail2_list);