/* * xfs_trans_push_ail * * This routine is called to move the tail of the AIL * forward. It does this by trying to flush items in the AIL * whose lsns are below the given threshold_lsn. * * The routine returns the lsn of the tail of the log. */ xfs_lsn_t xfs_trans_push_ail( xfs_mount_t *mp, xfs_lsn_t threshold_lsn) { xfs_lsn_t lsn; xfs_log_item_t *lip; int gen; int restarts; int lock_result; int flush_log; SPLDECL(s); #define XFS_TRANS_PUSH_AIL_RESTARTS 10 AIL_LOCK(mp,s); lip = xfs_trans_first_ail(mp, &gen); if (lip == NULL || XFS_FORCED_SHUTDOWN(mp)) { /* * Just return if the AIL is empty. */ AIL_UNLOCK(mp, s); return (xfs_lsn_t)0; } XFS_STATS_INC(xs_push_ail); /* * While the item we are looking at is below the given threshold * try to flush it out. Make sure to limit the number of times * we allow xfs_trans_next_ail() to restart scanning from the * beginning of the list. We'd like not to stop until we've at least * tried to push on everything in the AIL with an LSN less than * the given threshold. However, we may give up before that if * we realize that we've been holding the AIL_LOCK for 'too long', * blocking interrupts. Currently, too long is < 500us roughly. */ flush_log = 0; restarts = 0; while (((restarts < XFS_TRANS_PUSH_AIL_RESTARTS) && (XFS_LSN_CMP(lip->li_lsn, threshold_lsn) < 0))) { /* * If we can lock the item without sleeping, unlock * the AIL lock and flush the item. Then re-grab the * AIL lock so we can look for the next item on the * AIL. Since we unlock the AIL while we flush the * item, the next routine may start over again at the * the beginning of the list if anything has changed. * That is what the generation count is for. * * If we can't lock the item, either its holder will flush * it or it is already being flushed or it is being relogged. * In any of these case it is being taken care of and we * can just skip to the next item in the list. */ lock_result = IOP_TRYLOCK(lip); switch (lock_result) { case XFS_ITEM_SUCCESS: AIL_UNLOCK(mp, s); XFS_STATS_INC(xs_push_ail_success); IOP_PUSH(lip); AIL_LOCK(mp,s); break; case XFS_ITEM_PUSHBUF: AIL_UNLOCK(mp, s); XFS_STATS_INC(xs_push_ail_pushbuf); #ifdef XFSRACEDEBUG delay_for_intr(); delay(300); #endif ASSERT(lip->li_ops->iop_pushbuf); ASSERT(lip); IOP_PUSHBUF(lip); AIL_LOCK(mp,s); break; case XFS_ITEM_PINNED: XFS_STATS_INC(xs_push_ail_pinned); flush_log = 1; break; case XFS_ITEM_LOCKED: XFS_STATS_INC(xs_push_ail_locked); break; case XFS_ITEM_FLUSHING: XFS_STATS_INC(xs_push_ail_flushing); break; default: ASSERT(0); break; } lip = xfs_trans_next_ail(mp, lip, &gen, &restarts); if (lip == NULL) { break; } if (XFS_FORCED_SHUTDOWN(mp)) { /* * Just return if we shut down during the last try. */ AIL_UNLOCK(mp, s); return (xfs_lsn_t)0; } } if (flush_log) { /* * If something we need to push out was pinned, then * push out the log so it will become unpinned and * move forward in the AIL. */ AIL_UNLOCK(mp, s); XFS_STATS_INC(xs_push_ail_flush); xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE); AIL_LOCK(mp, s); } lip = xfs_ail_min(&(mp->m_ail)); if (lip == NULL) { lsn = (xfs_lsn_t)0; } else { lsn = lip->li_lsn; } AIL_UNLOCK(mp, s); return lsn; } /* xfs_trans_push_ail */
/* * Function that does the work of pushing on the AIL */ long xfsaild_push( xfs_mount_t *mp, xfs_lsn_t *last_lsn) { long tout = 1000; /* milliseconds */ xfs_lsn_t last_pushed_lsn = *last_lsn; xfs_lsn_t target = mp->m_ail.xa_target; xfs_lsn_t lsn; xfs_log_item_t *lip; int gen; int restarts; int flush_log, count, stuck; #define XFS_TRANS_PUSH_AIL_RESTARTS 10 spin_lock(&mp->m_ail_lock); lip = xfs_trans_first_push_ail(mp, &gen, *last_lsn); if (!lip || XFS_FORCED_SHUTDOWN(mp)) { /* * AIL is empty or our push has reached the end. */ spin_unlock(&mp->m_ail_lock); last_pushed_lsn = 0; goto out; } XFS_STATS_INC(xs_push_ail); /* * While the item we are looking at is below the given threshold * try to flush it out. We'd like not to stop until we've at least * tried to push on everything in the AIL with an LSN less than * the given threshold. * * However, we will stop after a certain number of pushes and wait * for a reduced timeout to fire before pushing further. This * prevents use from spinning when we can't do anything or there is * lots of contention on the AIL lists. */ tout = 10; lsn = lip->li_lsn; flush_log = stuck = count = restarts = 0; while ((XFS_LSN_CMP(lip->li_lsn, target) < 0)) { int lock_result; /* * If we can lock the item without sleeping, unlock the AIL * lock and flush the item. Then re-grab the AIL lock so we * can look for the next item on the AIL. List changes are * handled by the AIL lookup functions internally * * If we can't lock the item, either its holder will flush it * or it is already being flushed or it is being relogged. In * any of these case it is being taken care of and we can just * skip to the next item in the list. */ lock_result = IOP_TRYLOCK(lip); spin_unlock(&mp->m_ail_lock); switch (lock_result) { case XFS_ITEM_SUCCESS: XFS_STATS_INC(xs_push_ail_success); IOP_PUSH(lip); last_pushed_lsn = lsn; break; case XFS_ITEM_PUSHBUF: XFS_STATS_INC(xs_push_ail_pushbuf); IOP_PUSHBUF(lip); last_pushed_lsn = lsn; break; case XFS_ITEM_PINNED: XFS_STATS_INC(xs_push_ail_pinned); stuck++; flush_log = 1; break; case XFS_ITEM_LOCKED: XFS_STATS_INC(xs_push_ail_locked); last_pushed_lsn = lsn; stuck++; break; case XFS_ITEM_FLUSHING: XFS_STATS_INC(xs_push_ail_flushing); last_pushed_lsn = lsn; stuck++; break; default: ASSERT(0); break; } spin_lock(&mp->m_ail_lock); /* should we bother continuing? */ if (XFS_FORCED_SHUTDOWN(mp)) break; ASSERT(mp->m_log); count++; /* * Are there too many items we can't do anything with? * If we we are skipping too many items because we can't flush * them or they are already being flushed, we back off and * given them time to complete whatever operation is being * done. i.e. remove pressure from the AIL while we can't make * progress so traversals don't slow down further inserts and * removals to/from the AIL. * * The value of 100 is an arbitrary magic number based on * observation. */ if (stuck > 100) break; lip = xfs_trans_next_ail(mp, lip, &gen, &restarts); if (lip == NULL) break; if (restarts > XFS_TRANS_PUSH_AIL_RESTARTS) break; lsn = lip->li_lsn; } spin_unlock(&mp->m_ail_lock); if (flush_log) { /* * If something we need to push out was pinned, then * push out the log so it will become unpinned and * move forward in the AIL. */ XFS_STATS_INC(xs_push_ail_flush); xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE); } if (!count) { /* We're past our target or empty, so idle */ tout = 1000; } else if (XFS_LSN_CMP(lsn, target) >= 0) { /* * We reached the target so wait a bit longer for I/O to * complete and remove pushed items from the AIL before we * start the next scan from the start of the AIL. */ tout += 20; last_pushed_lsn = 0; } else if ((restarts > XFS_TRANS_PUSH_AIL_RESTARTS) || ((stuck * 100) / count > 90)) { /* * Either there is a lot of contention on the AIL or we * are stuck due to operations in progress. "Stuck" in this * case is defined as >90% of the items we tried to push * were stuck. * * Backoff a bit more to allow some I/O to complete before * continuing from where we were. */ tout += 10; } out: *last_lsn = last_pushed_lsn; return tout; } /* xfsaild_push */