File: [Development] / xfs-linux / xfs_trans_ail.c (download)
Revision 1.29, Tue Oct 24 07:44:36 1995 UTC (21 years, 11 months ago) by ack
Branch: MAIN
Changes since 1.28: +0 -0
lines
auto-merge of changes from /hosts/clyde/proj/banyan/isms/irix/kern/fs/xfs/RCS/xfs_trans_ail.c,v
> ----------------------------
> revision 1.28
> date: 1995/10/13 00:28:42; author: doucette; state: Exp; lines: +2 -1
> Include sys/uuid.h since the uuid_t definition has been moved out
> of sys/types.h for DCE.
> =============================================================================
|
#ident "$Revision$"
#include <sys/debug.h>
#ifdef SIM
#define _KERNEL 1
#endif
#include <sys/param.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/uuid.h>
#ifdef SIM
#undef _KERNEL
#endif
#ifndef SIM
#include <sys/systm.h>
#endif
#include "xfs_types.h"
#include "xfs_inum.h"
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_mount.h"
#include "xfs_log.h"
#include "xfs_trans_priv.h"
#ifdef SIM
#include "sim.h"
#endif
STATIC void
xfs_ail_insert(
xfs_ail_entry_t *base,
xfs_log_item_t *lip);
STATIC xfs_log_item_t *
xfs_ail_delete(
xfs_ail_entry_t *base,
xfs_log_item_t *lip);
STATIC xfs_log_item_t *
xfs_ail_min(
xfs_ail_entry_t *base);
STATIC xfs_log_item_t *
xfs_ail_next(
xfs_ail_entry_t *base,
xfs_log_item_t *lip);
#ifdef XFSDEBUG
STATIC void
xfs_ail_check(
xfs_ail_entry_t *base);
#else
#define xfs_ail_check(a)
#endif /* XFSDEBUG */
/*
* This is called by the log manager code to determine the LSN
* of the tail of the log. This is exactly the LSN of the first
* item in the AIL. If the AIL is empty, then this function
* returns 0.
*
* We need the AIL lock in order to get a coherent read of the
* lsn of the last item in the AIL.
*/
xfs_lsn_t
xfs_trans_tail_ail(
xfs_mount_t *mp)
{
int s;
xfs_lsn_t lsn;
xfs_log_item_t *lip;
s = AIL_LOCK(mp);
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
*
* 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 s;
int gen;
int restarts;
int lock_result;
int flush_log;
#define XFS_TRANS_PUSH_AIL_RESTARTS 10
s = AIL_LOCK(mp);
lip = xfs_trans_first_ail(mp, &gen);
if (lip == NULL) {
/*
* Just return if the AIL is empty.
*/
AIL_UNLOCK(mp, s);
return (xfs_lsn_t)0;
}
/*
* 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 can't stop until we've at least
* tried to push on everything in the AIL with an LSN less than
* the given threshold.
*/
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);
IOP_PUSH(lip);
s = AIL_LOCK(mp);
break;
case XFS_ITEM_PINNED:
flush_log = 1;
break;
case XFS_ITEM_LOCKED:
case XFS_ITEM_FLUSHING:
#ifdef NOTYET
/*
* Flush the log in this case just in case the
* item is being flushed but got stuck on a
* pinned object (like flushing an inode on a
* pinned buffer).
*/
flush_log = 1;
#endif
break;
default:
ASSERT(0);
break;
}
lip = xfs_trans_next_ail(mp, lip, &gen, &restarts);
if (lip == NULL) {
break;
}
}
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_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
s = AIL_LOCK(mp);
}
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 */
/*
* This is to be called when an item is unlocked that may have
* been in the AIL. It will wake up the first member of the AIL
* wait list if this item's unlocking might allow it to progress.
* If the item is in the AIL, then we need to get the AIL lock
* while doing our checking so we don't race with someone going
* to sleep waiting for this event in xfs_trans_push_ail().
*/
void
xfs_trans_unlocked_item(
xfs_mount_t *mp,
xfs_log_item_t *lip)
{
int s;
xfs_log_item_t *min_lip;
if (!(lip->li_flags & XFS_LI_IN_AIL)) {
return;
}
s = AIL_LOCK(mp);
min_lip = xfs_ail_min(&mp->m_ail);
AIL_UNLOCK(mp, s);
if (min_lip == lip)
xfs_log_move_tail(mp, 1);
} /* xfs_trans_unlocked_item */
/*
* Update the position of the item in the AIL with the new
* lsn. If it is not yet in the AIL, add it. Otherwise, move
* it to its new position by removing it and re-adding it.
*
* Wakeup anyone with an lsn less than the item's lsn. If the item
* we move in the AIL is the minimum one, update the tail lsn in the
* log manager.
*
* Increment the AIL's generation count to indicate that the tree
* has changed.
*
* This function must be called with the AIL lock held. The lock
* is dropped before returning, so the caller must pass in the
* cookie returned by AIL_LOCK.
*/
void
xfs_trans_update_ail(
xfs_mount_t *mp,
xfs_log_item_t *lip,
xfs_lsn_t lsn,
int s)
{
xfs_ail_entry_t *ailp;
xfs_log_item_t *dlip;
xfs_log_item_t *mlip; /* ptr to minimum lip */
ailp = &(mp->m_ail);
mlip = xfs_ail_min(ailp);
if (lip->li_flags & XFS_LI_IN_AIL) {
dlip = xfs_ail_delete(ailp, lip);
ASSERT(dlip == lip);
} else {
lip->li_flags |= XFS_LI_IN_AIL;
}
lip->li_lsn = lsn;
xfs_ail_insert(ailp, lip);
mp->m_ail_gen++;
if (mlip == dlip) {
mlip = xfs_ail_min(&(mp->m_ail));
AIL_UNLOCK(mp, s);
xfs_log_move_tail(mp, mlip->li_lsn);
} else {
AIL_UNLOCK(mp, s);
}
} /* xfs_trans_update_ail */
/*
* Delete the given item from the AIL. It must already be in
* the AIL.
*
* Wakeup anyone with an lsn less than item's lsn. If the item
* we delete in the AIL is the minimum one, update the tail lsn in the
* log manager.
*
* Clear the IN_AIL flag from the item, reset its lsn to 0, and
* bump the AIL's generation count to indicate that the tree
* has changed.
*
* This function must be called with the AIL lock held. The lock
* is dropped before returning, so the caller must pass in the
* cookie returned by AIL_LOCK.
*/
void
xfs_trans_delete_ail(
xfs_mount_t *mp,
xfs_log_item_t *lip,
int s)
{
xfs_ail_entry_t *ailp;
xfs_log_item_t *dlip;
xfs_log_item_t *mlip;
ASSERT(lip->li_flags & XFS_LI_IN_AIL);
ailp = &(mp->m_ail);
mlip = xfs_ail_min(ailp);
dlip = xfs_ail_delete(ailp, lip);
ASSERT(dlip == lip);
lip->li_flags &= ~XFS_LI_IN_AIL;
lip->li_lsn = 0;
mp->m_ail_gen++;
if (mlip == dlip) {
mlip = xfs_ail_min(&(mp->m_ail));
AIL_UNLOCK(mp, s);
xfs_log_move_tail(mp, (mlip ? mlip->li_lsn : 0));
} else {
AIL_UNLOCK(mp, s);
}
}
/*
* Return the item in the AIL with the smallest lsn.
* Return the current tree generation number for use
* in calls to xfs_trans_next_ail().
*/
xfs_log_item_t *
xfs_trans_first_ail(
xfs_mount_t *mp,
int *gen)
{
xfs_log_item_t *lip;
lip = xfs_ail_min(&(mp->m_ail));
*gen = (int)mp->m_ail_gen;
return (lip);
}
/*
* If the generation count of the tree has not changed since the
* caller last took something from the AIL, then return the elmt
* in the tree which follows the one given. If the count has changed,
* then return the minimum elmt of the AIL and bump the restarts counter
* if one is given.
*/
xfs_log_item_t *
xfs_trans_next_ail(
xfs_mount_t *mp,
xfs_log_item_t *lip,
int *gen,
int *restarts)
{
xfs_log_item_t *nlip;
if (mp->m_ail_gen == *gen) {
nlip = xfs_ail_next(&(mp->m_ail), lip);
} else {
nlip = xfs_ail_min(&(mp->m_ail));
if (gen != NULL) {
*gen = (int)mp->m_ail_gen;
}
if (restarts != NULL) {
(*restarts)++;
}
}
return (nlip);
}
/*
* The active item list (AIL) is a doubly linked list of log
* items sorted by ascending lsn. The base of the list is
* a forw/back pointer pair embedded in the xfs mount structure.
* The base is initialized with both pointers pointing to the
* base. This case always needs to be distinguished, because
* the base has no lsn to look at. We almost always insert
* at the end of the list, so on inserts we search from the
* end of the list to find where the new item belongs.
*/
/*
* Initialize the doubly linked list to point only to itself.
*/
void
xfs_trans_ail_init(
xfs_mount_t *mp)
{
mp->m_ail.ail_forw = (xfs_log_item_t*)&(mp->m_ail);
mp->m_ail.ail_back = (xfs_log_item_t*)&(mp->m_ail);
}
/*
* Insert the given log item into the AIL.
* We almost always insert at the end of the list, so on inserts
* we search from the end of the list to find where the
* new item belongs.
*/
STATIC void
xfs_ail_insert(
xfs_ail_entry_t *base,
xfs_log_item_t *lip)
/* ARGSUSED */
{
xfs_log_item_t *next_lip;
/*
* If the list is empty, just insert the item.
*/
if (base->ail_back == (xfs_log_item_t*)base) {
base->ail_forw = lip;
base->ail_back = lip;
lip->li_ail.ail_forw = (xfs_log_item_t*)base;
lip->li_ail.ail_back = (xfs_log_item_t*)base;
return;
}
next_lip = base->ail_back;
while ((next_lip != (xfs_log_item_t*)base) &&
(next_lip->li_lsn > lip->li_lsn)) {
next_lip = next_lip->li_ail.ail_back;
}
ASSERT((next_lip == (xfs_log_item_t*)base) ||
(next_lip->li_lsn <= lip->li_lsn));
lip->li_ail.ail_forw = next_lip->li_ail.ail_forw;
lip->li_ail.ail_back = next_lip;
next_lip->li_ail.ail_forw = lip;
lip->li_ail.ail_forw->li_ail.ail_back = lip;
xfs_ail_check(base);
return;
}
/*
* Delete the given item from the AIL. Return a pointer to the item.
*/
/*ARGSUSED*/
STATIC xfs_log_item_t *
xfs_ail_delete(
xfs_ail_entry_t *base,
xfs_log_item_t *lip)
/* ARGSUSED */
{
lip->li_ail.ail_forw->li_ail.ail_back = lip->li_ail.ail_back;
lip->li_ail.ail_back->li_ail.ail_forw = lip->li_ail.ail_forw;
lip->li_ail.ail_forw = NULL;
lip->li_ail.ail_back = NULL;
xfs_ail_check(base);
return lip;
}
/*
* Return a pointer to the first item in the AIL.
* If the AIL is empty, then return NULL.
*/
STATIC xfs_log_item_t *
xfs_ail_min(
xfs_ail_entry_t *base)
/* ARGSUSED */
{
if (base->ail_forw == (xfs_log_item_t*)base) {
return NULL;
}
return base->ail_forw;
}
/*
* Return a pointer to the item which follows
* the given item in the AIL. If the given item
* is the last item in the list, then return NULL.
*/
STATIC xfs_log_item_t *
xfs_ail_next(
xfs_ail_entry_t *base,
xfs_log_item_t *lip)
/* ARGSUSED */
{
if (lip->li_ail.ail_forw == (xfs_log_item_t*)base) {
return NULL;
}
return lip->li_ail.ail_forw;
}
#ifdef XFSDEBUG
/*
* Check that the list is sorted as it should be.
*/
STATIC void
xfs_ail_check(
xfs_ail_entry_t *base)
{
xfs_log_item_t *lip;
xfs_log_item_t *prev_lip;
lip = base->ail_forw;
if (lip == (xfs_log_item_t*)base) {
/*
* Make sure the pointers are correct when the list
* is empty.
*/
ASSERT(base->ail_back == (xfs_log_item_t*)base);
return;
}
/*
* Walk the list checking forward and backward pointers,
* lsn ordering, and that every entry has the XFS_LI_IN_AIL
* flag set.
*/
prev_lip = (xfs_log_item_t*)base;
while (lip != (xfs_log_item_t*)base) {
if (prev_lip != (xfs_log_item_t*)base) {
ASSERT(prev_lip->li_ail.ail_forw == lip);
ASSERT(prev_lip->li_lsn <= lip->li_lsn);
}
ASSERT(lip->li_ail.ail_back == prev_lip);
ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0);
prev_lip = lip;
lip = lip->li_ail.ail_forw;
}
ASSERT(lip == (xfs_log_item_t*)base);
ASSERT(base->ail_back == prev_lip);
}
#endif /* XFSDEBUG */