Commit 4da0dd95 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull gfs2 updates from Andreas Gruenbacher:

 - Fix possible data loss during inode evict

 - Fix a race during bufdata allocation

 - More careful cleaning up during a withdraw

 - Prevent excessive log flushing under memory pressure

 - Various other minor fixes and cleanups

* tag 'gfs2-for-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: prevent NULL pointer dereference during unmount
  gfs2: hide error messages after withdraw
  gfs2: wait for withdraw earlier during unmount
  gfs2: inode directory consistency checks
  gfs2: gfs2_log_flush withdraw fixes
  gfs2: add some missing log locking
  gfs2: fix address space truncation during withdraw
  gfs2: drain ail under sd_log_flush_lock
  gfs2: bufdata allocation race
  gfs2: Remove trans_drain code duplication
  gfs2: Move gfs2_remove_from_journal to log.c
  gfs2: Get rid of gfs2_log_[un]lock helpers
  gfs2: less aggressive low-memory log flushing
  gfs2: Fix data loss during inode evict
  gfs2: minor evict_[un]linked_inode cleanup
  gfs2: Avoid unnecessary transactions in evict_linked_inode
  gfs2: Remove unnecessary check in gfs2_evict_inode
  gfs2: Call unlock_new_inode before d_instantiate
parents acf6c670 74b4dbb9
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -158,6 +158,7 @@ static int gfs2_writepages(struct address_space *mapping,
			   struct writeback_control *wbc)
{
	struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping);
	long initial_nr_to_write = wbc->nr_to_write;
	struct iomap_writepage_ctx wpc = {
		.inode		= mapping->host,
		.wbc		= wbc,
@@ -166,13 +167,13 @@ static int gfs2_writepages(struct address_space *mapping,
	int ret;

	/*
	 * Even if we didn't write enough pages here, we might still be holding
	 * Even if we didn't write any pages here, we might still be holding
	 * dirty pages in the ail. We forcibly flush the ail because we don't
	 * want balance_dirty_pages() to loop indefinitely trying to write out
	 * pages held in the ail that it can't find.
	 */
	ret = iomap_writepages(&wpc);
	if (ret == 0 && wbc->nr_to_write > 0)
	if (ret == 0 && wbc->nr_to_write == initial_nr_to_write)
		set_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags);
	return ret;
}
@@ -582,7 +583,7 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh)
	struct gfs2_bufdata *bd;

	lock_buffer(bh);
	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_log_lock);
	clear_buffer_dirty(bh);
	bd = bh->b_private;
	if (bd) {
@@ -598,7 +599,7 @@ static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh)
	clear_buffer_mapped(bh);
	clear_buffer_req(bh);
	clear_buffer_new(bh);
	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);
	unlock_buffer(bh);
}

@@ -666,7 +667,7 @@ bool gfs2_release_folio(struct folio *folio, gfp_t gfp_mask)
	 * again.
	 */

	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_log_lock);
	bh = head;
	do {
		if (atomic_read(&bh->b_count))
@@ -698,12 +699,12 @@ bool gfs2_release_folio(struct folio *folio, gfp_t gfp_mask)

		bh = bh->b_this_page;
	} while (bh != head);
	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);

	return try_to_free_buffers(folio);

cannot_release:
	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);
	return false;
}

+1 −1
Original line number Diff line number Diff line
@@ -1539,7 +1539,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
			revokes = jblocks_rqsted;
			if (meta)
				revokes += end - start;
			else if (ip->i_depth)
			else if (ip->i_diskflags & GFS2_DIF_EXHASH)
				revokes += sdp->sd_inptrs;
			ret = gfs2_trans_begin(sdp, jblocks_rqsted, revokes);
			if (ret)
+9 −4
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync,
	struct buffer_head *bh;
	const unsigned long b_state = (1UL << BH_Dirty)|(1UL << BH_Pinned)|(1UL << BH_Lock);

	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_log_lock);
	spin_lock(&sdp->sd_ail_lock);
	list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) {
		if (nr_revokes == 0)
@@ -80,7 +80,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync,
	}
	GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count));
	spin_unlock(&sdp->sd_ail_lock);
	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);
}


@@ -109,10 +109,10 @@ static int gfs2_ail_empty_gl(struct gfs2_glock *gl)
		 * If none of these conditions are true, our revokes are all
		 * flushed and we can return.
		 */
		gfs2_log_lock(sdp);
		spin_lock(&sdp->sd_log_lock);
		have_revokes = !list_empty(&sdp->sd_log_revokes);
		log_in_flight = atomic_read(&sdp->sd_log_in_flight);
		gfs2_log_unlock(sdp);
		spin_unlock(&sdp->sd_log_lock);
		if (have_revokes)
			goto flush;
		if (log_in_flight)
@@ -457,6 +457,11 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
	ip->i_depth = (u8)depth;
	ip->i_entries = be32_to_cpu(str->di_entries);

	if (!S_ISDIR(inode->i_mode) && (ip->i_diskflags & GFS2_DIF_EXHASH)) {
		gfs2_consist_inode(ip);
		return -EIO;
	}

	if (gfs2_is_stuffed(ip) && inode->i_size > gfs2_max_stuffed_size(ip)) {
		gfs2_consist_inode(ip);
		return -EIO;
+1 −2
Original line number Diff line number Diff line
@@ -892,7 +892,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
		goto fail_gunlock4;

	mark_inode_dirty(inode);
	d_instantiate(dentry, inode);
	d_instantiate_new(dentry, inode);
	/* After instantiate, errors should result in evict which will destroy
	 * both inode and iopen glocks properly. */
	if (file) {
@@ -904,7 +904,6 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
	gfs2_glock_dq_uninit(&gh);
	gfs2_glock_put(io_gl);
	gfs2_qa_put(dip);
	unlock_new_inode(inode);
	return error;

fail_gunlock4:
+92 −35
Original line number Diff line number Diff line
@@ -72,7 +72,7 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct)
 *
 */

void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
static void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
{
	bd->bd_tr = NULL;
	list_del_init(&bd->bd_ail_st_list);
@@ -467,7 +467,8 @@ void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
{
	atomic_add(blks, &sdp->sd_log_blks_free);
	trace_gfs2_log_blocks(sdp, blks);
	gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <=
	gfs2_assert_withdraw(sdp, !sdp->sd_jdesc ||
			atomic_read(&sdp->sd_log_blks_free) <=
			sdp->sd_jdesc->jd_blocks);
	if (atomic_read(&sdp->sd_log_blks_needed))
		wake_up(&sdp->sd_log_waitq);
@@ -800,9 +801,9 @@ void gfs2_flush_revokes(struct gfs2_sbd *sdp)
	/* number of revokes we still have room for */
	unsigned int max_revokes = atomic_read(&sdp->sd_log_revokes_available);

	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_log_lock);
	gfs2_ail1_empty(sdp, max_revokes);
	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);
}

/**
@@ -983,49 +984,90 @@ static void empty_ail1_list(struct gfs2_sbd *sdp)
	}
}

static void gfs2_trans_drain_list(struct gfs2_sbd *sdp, struct list_head *list)
{
	struct gfs2_bufdata *bd;

	while (!list_empty(list)) {
		bd = list_first_entry(list, struct gfs2_bufdata, bd_list);
		struct buffer_head *bh = bd->bd_bh;

		WARN_ON_ONCE(!buffer_pinned(bh));
		clear_buffer_pinned(bh);
		trace_gfs2_pin(bd, 0);
		atomic_dec(&sdp->sd_log_pinned);
		list_del_init(&bd->bd_list);
		brelse(bh);
	}
}

/**
 * trans_drain - drain the buf and databuf queue for a failed transaction
 * gfs2_trans_drain - drain the buf and databuf queue for a failed transaction
 * @sdp: the filesystem
 * @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)
static void gfs2_trans_drain(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
	struct gfs2_bufdata *bd;
	struct list_head *head;

	if (!tr)
		return;
	gfs2_trans_drain_list(sdp, &tr->tr_buf);
	gfs2_trans_drain_list(sdp, &tr->tr_databuf);
}

	head = &tr->tr_buf;
	while (!list_empty(head)) {
		bd = list_first_entry(head, struct gfs2_bufdata, bd_list);
void gfs2_remove_from_journal(struct buffer_head *bh, int meta)
{
	struct address_space *mapping = bh->b_folio->mapping;
	struct gfs2_sbd *sdp = gfs2_mapping2sbd(mapping);
	struct gfs2_bufdata *bd = bh->b_private;
	struct gfs2_trans *tr = current->journal_info;
	int was_pinned = 0;

	if (test_clear_buffer_pinned(bh)) {
		trace_gfs2_pin(bd, 0);
		atomic_dec(&sdp->sd_log_pinned);
		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);
		if (tr) {
			if (meta == REMOVE_META)
				tr->tr_num_buf_rm++;
			else
				tr->tr_num_databuf_rm++;
			set_bit(TR_TOUCHED, &tr->tr_flags);
		}
	head = &tr->tr_databuf;
	while (!list_empty(head)) {
		bd = list_first_entry(head, struct gfs2_bufdata, bd_list);
		list_del_init(&bd->bd_list);
		if (!list_empty(&bd->bd_ail_st_list))
		was_pinned = 1;
		brelse(bh);
	}
	if (bd) {
		if (bd->bd_tr) {
			if (tr)
				gfs2_trans_add_revoke(sdp, bd);
			else
				 gfs2_remove_from_ail(bd);
		} else if (was_pinned) {
			bh->b_private = NULL;
			kmem_cache_free(gfs2_bufdata_cachep, bd);
		} else if (!list_empty(&bd->bd_ail_st_list) &&
			   !list_empty(&bd->bd_ail_gl_list)) {
			gfs2_remove_from_ail(bd);
		}
	}
	clear_buffer_dirty(bh);
	clear_buffer_uptodate(bh);
}

/**
 * gfs2_log_flush - flush incore transaction(s)
 * __gfs2_log_flush - flush incore transaction(s)
 * @sdp: The filesystem
 * @gl: The glock structure to flush.  If NULL, flush the whole incore log
 * @flags: The log header flags: GFS2_LOG_HEAD_FLUSH_* and debug flags
 *
 */

void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
static void __gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
			     u32 flags)
{
	struct gfs2_trans *tr = NULL;
	unsigned int reserved_blocks = 0, used_blocks = 0;
@@ -1033,7 +1075,6 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
	unsigned int first_log_head;
	unsigned int reserved_revokes = 0;

	down_write(&sdp->sd_log_flush_lock);
	trace_gfs2_log_flush(sdp, 1, flags);

repeat:
@@ -1110,7 +1151,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
		goto out_withdraw;
	lops_after_commit(sdp, tr);

	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_log_lock);
	sdp->sd_log_blks_reserved = 0;

	spin_lock(&sdp->sd_ail_lock);
@@ -1119,7 +1160,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
		tr = NULL;
	}
	spin_unlock(&sdp->sd_ail_lock);
	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);

	if (!(flags & GFS2_LOG_HEAD_FLUSH_NORMAL)) {
		if (!sdp->sd_log_idle) {
@@ -1145,13 +1186,16 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
		gfs2_assert_withdraw(sdp, used_blocks < reserved_blocks);
		gfs2_log_release(sdp, reserved_blocks - used_blocks);
	}
	up_write(&sdp->sd_log_flush_lock);
	gfs2_trans_free(sdp, tr);
	trace_gfs2_log_flush(sdp, 0, flags);
	return;

out_withdraw:
	trans_drain(tr);
	if (sdp->sd_jdesc->jd_log_bio) {
		bio_io_error(sdp->sd_jdesc->jd_log_bio);
		sdp->sd_jdesc->jd_log_bio = NULL;
	}
	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
@@ -1166,6 +1210,13 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
	goto out_end;
}

void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
{
	down_write(&sdp->sd_log_flush_lock);
	__gfs2_log_flush(sdp, gl, flags);
	up_write(&sdp->sd_log_flush_lock);
}

/**
 * gfs2_merge_trans - Merge a new transaction into a cached transaction
 * @sdp: the filesystem
@@ -1200,7 +1251,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
	unsigned int unused;
	unsigned int maxres;

	gfs2_log_lock(sdp);
	spin_lock(&sdp->sd_log_lock);

	if (sdp->sd_log_tr) {
		gfs2_merge_trans(sdp, tr);
@@ -1218,7 +1269,7 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
		gfs2_log_release(sdp, unused);
	sdp->sd_log_blks_reserved = reserved;

	gfs2_log_unlock(sdp);
	spin_unlock(&sdp->sd_log_lock);
}

static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp)
@@ -1297,19 +1348,25 @@ int gfs2_logd(void *data)
			break;

		if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
			down_write(&sdp->sd_log_flush_lock);
			gfs2_ail1_empty(sdp, 0);
			gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
			__gfs2_log_flush(sdp, NULL,
					 GFS2_LOG_HEAD_FLUSH_NORMAL |
					 GFS2_LFC_LOGD_JFLUSH_REQD);
			up_write(&sdp->sd_log_flush_lock);
		}

		if (test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) ||
		    gfs2_ail_flush_reqd(sdp)) {
			clear_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags);
			down_write(&sdp->sd_log_flush_lock);
			gfs2_ail1_start(sdp);
			gfs2_ail1_wait(sdp);
			gfs2_ail1_empty(sdp, 0);
			gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
			__gfs2_log_flush(sdp, NULL,
					 GFS2_LOG_HEAD_FLUSH_NORMAL |
					 GFS2_LFC_LOGD_AIL_FLUSH_REQD);
			up_write(&sdp->sd_log_flush_lock);
		}

		t = gfs2_tune_get(sdp, gt_logd_secs) * HZ;
Loading