Commit c814b4f2 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Darrick J. Wong
Browse files

xfs: refactor xlog_state_clean_iclog



Factor out a few self-contained helpers from xlog_state_clean_iclog, and
update the documentation so it primarily documents why things happens
instead of how.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarBrian Foster <bfoster@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
parent 12e6a0f4
Loading
Loading
Loading
Loading
+88 −92
Original line number Diff line number Diff line
@@ -2540,111 +2540,107 @@ next_lv:
 *****************************************************************************
 */

/*
 * An iclog has just finished IO completion processing, so we need to update
 * the iclog state and propagate that up into the overall log state. Hence we
 * prepare the iclog for cleaning, and then clean all the pending dirty iclogs
 * starting from the head, and then wake up any threads that are waiting for the
 * iclog to be marked clean.
 *
 * The ordering of marking iclogs ACTIVE must be maintained, so an iclog
 * doesn't become ACTIVE beyond one that is SYNCING.  This is also required to
 * maintain the notion that we use a ordered wait queue to hold off would be
 * writers to the log when every iclog is trying to sync to disk.
 *
 * Caller must hold the icloglock before calling us.
 *
 * State Change: !IOERROR -> DIRTY -> ACTIVE
 */
STATIC void
xlog_state_clean_iclog(
	struct xlog		*log,
	struct xlog_in_core	*dirty_iclog)
static void
xlog_state_activate_iclog(
	struct xlog_in_core	*iclog,
	int			*iclogs_changed)
{
	struct xlog_in_core	*iclog;
	int			changed = 0;

	/* Prepare the completed iclog. */
	if (dirty_iclog->ic_state != XLOG_STATE_IOERROR)
		dirty_iclog->ic_state = XLOG_STATE_DIRTY;

	/* Walk all the iclogs to update the ordered active state. */
	iclog = log->l_iclog;
	do {
		if (iclog->ic_state == XLOG_STATE_DIRTY) {
			iclog->ic_state	= XLOG_STATE_ACTIVE;
			iclog->ic_offset       = 0;
	ASSERT(list_empty_careful(&iclog->ic_callbacks));

	/*
			 * If the number of ops in this iclog indicate it just
			 * contains the dummy transaction, we can
			 * change state into IDLE (the second time around).
			 * Otherwise we should change the state into
			 * NEED a dummy.
	 * If the number of ops in this iclog indicate it just contains the
	 * dummy transaction, we can change state into IDLE (the second time
	 * around). Otherwise we should change the state into NEED a dummy.
	 * We don't need to cover the dummy.
	 */
			if (!changed &&
			   (be32_to_cpu(iclog->ic_header.h_num_logops) ==
			   		XLOG_COVER_OPS)) {
				changed = 1;
	if (*iclogs_changed == 0 &&
	    iclog->ic_header.h_num_logops == cpu_to_be32(XLOG_COVER_OPS)) {
		*iclogs_changed = 1;
	} else {
		/*
				 * We have two dirty iclogs so start over
				 * This could also be num of ops indicates
				 * this is not the dummy going out.
		 * We have two dirty iclogs so start over.  This could also be
		 * num of ops indicating this is not the dummy going out.
		 */
				changed = 2;
		*iclogs_changed = 2;
	}

	iclog->ic_state	= XLOG_STATE_ACTIVE;
	iclog->ic_offset = 0;
	iclog->ic_header.h_num_logops = 0;
	memset(iclog->ic_header.h_cycle_data, 0,
		sizeof(iclog->ic_header.h_cycle_data));
	iclog->ic_header.h_lsn = 0;
		} else if (iclog->ic_state == XLOG_STATE_ACTIVE)
			/* do nothing */;
		else
			break;	/* stop cleaning */
		iclog = iclog->ic_next;
	} while (iclog != log->l_iclog);
}

/*
 * Loop through all iclogs and mark all iclogs currently marked DIRTY as
 * ACTIVE after iclog I/O has completed.
 */
static void
xlog_state_activate_iclogs(
	struct xlog		*log,
	int			*iclogs_changed)
{
	struct xlog_in_core	*iclog = log->l_iclog;

	do {
		if (iclog->ic_state == XLOG_STATE_DIRTY)
			xlog_state_activate_iclog(iclog, iclogs_changed);
		/*
	 * Wake up threads waiting in xfs_log_force() for the dirty iclog
	 * to be cleaned.
		 * The ordering of marking iclogs ACTIVE must be maintained, so
		 * an iclog doesn't become ACTIVE beyond one that is SYNCING.
		 */
	wake_up_all(&dirty_iclog->ic_force_wait);
		else if (iclog->ic_state != XLOG_STATE_ACTIVE)
			break;
	} while ((iclog = iclog->ic_next) != log->l_iclog);
}

static int
xlog_covered_state(
	int			prev_state,
	int			iclogs_changed)
{
	/*
	 * Change state for the dummy log recording.
	 * We usually go to NEED. But we go to NEED2 if the changed indicates
	 * we are done writing the dummy record.
	 * If we are done with the second dummy recored (DONE2), then
	 * we go to IDLE.
	 * We usually go to NEED. But we go to NEED2 if the changed indicates we
	 * are done writing the dummy record.  If we are done with the second
	 * dummy recored (DONE2), then we go to IDLE.
	 */
	if (changed) {
		switch (log->l_covered_state) {
	switch (prev_state) {
	case XLOG_STATE_COVER_IDLE:
	case XLOG_STATE_COVER_NEED:
	case XLOG_STATE_COVER_NEED2:
			log->l_covered_state = XLOG_STATE_COVER_NEED;
		break;

	case XLOG_STATE_COVER_DONE:
			if (changed == 1)
				log->l_covered_state = XLOG_STATE_COVER_NEED2;
			else
				log->l_covered_state = XLOG_STATE_COVER_NEED;
		if (iclogs_changed == 1)
			return XLOG_STATE_COVER_NEED2;
		break;

	case XLOG_STATE_COVER_DONE2:
			if (changed == 1)
				log->l_covered_state = XLOG_STATE_COVER_IDLE;
			else
				log->l_covered_state = XLOG_STATE_COVER_NEED;
		if (iclogs_changed == 1)
			return XLOG_STATE_COVER_IDLE;
		break;

	default:
		ASSERT(0);
	}

	return XLOG_STATE_COVER_NEED;
}

STATIC void
xlog_state_clean_iclog(
	struct xlog		*log,
	struct xlog_in_core	*dirty_iclog)
{
	int			iclogs_changed = 0;

	if (dirty_iclog->ic_state != XLOG_STATE_IOERROR)
		dirty_iclog->ic_state = XLOG_STATE_DIRTY;

	xlog_state_activate_iclogs(log, &iclogs_changed);
	wake_up_all(&dirty_iclog->ic_force_wait);

	if (iclogs_changed) {
		log->l_covered_state = xlog_covered_state(log->l_covered_state,
				iclogs_changed);
	}
}