Commit be97a74b authored by Stephen Lord's avatar Stephen Lord Committed by Linus Torvalds

[XFS] Do not release the last iclog of a transaction before we get our

callbacks attached to it. Otherwise we can end up executing the
callback out of order.

SGI Modid: 2.5.x-xfs:slinx:137750a
parent 42500f5b
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
/* Local miscellaneous function prototypes */ /* Local miscellaneous function prototypes */
STATIC int xlog_bdstrat_cb(struct xfs_buf *); STATIC int xlog_bdstrat_cb(struct xfs_buf *);
STATIC int xlog_commit_record(xfs_mount_t *mp, xlog_ticket_t *ticket, STATIC int xlog_commit_record(xfs_mount_t *mp, xlog_ticket_t *ticket,
xfs_lsn_t *); xlog_in_core_t **, xfs_lsn_t *);
STATIC xlog_t * xlog_alloc_log(xfs_mount_t *mp, STATIC xlog_t * xlog_alloc_log(xfs_mount_t *mp,
dev_t log_dev, dev_t log_dev,
xfs_daddr_t blk_offset, xfs_daddr_t blk_offset,
...@@ -55,7 +55,9 @@ STATIC int xlog_sync(xlog_t *log, xlog_in_core_t *iclog); ...@@ -55,7 +55,9 @@ STATIC int xlog_sync(xlog_t *log, xlog_in_core_t *iclog);
STATIC void xlog_unalloc_log(xlog_t *log); STATIC void xlog_unalloc_log(xlog_t *log);
STATIC int xlog_write(xfs_mount_t *mp, xfs_log_iovec_t region[], STATIC int xlog_write(xfs_mount_t *mp, xfs_log_iovec_t region[],
int nentries, xfs_log_ticket_t tic, int nentries, xfs_log_ticket_t tic,
xfs_lsn_t *start_lsn, uint flags); xfs_lsn_t *start_lsn,
xlog_in_core_t **commit_iclog,
uint flags);
/* local state machine functions */ /* local state machine functions */
STATIC void xlog_state_done_syncing(xlog_in_core_t *iclog, int); STATIC void xlog_state_done_syncing(xlog_in_core_t *iclog, int);
...@@ -70,10 +72,6 @@ STATIC int xlog_state_get_iclog_space(xlog_t *log, ...@@ -70,10 +72,6 @@ STATIC int xlog_state_get_iclog_space(xlog_t *log,
xlog_ticket_t *ticket, xlog_ticket_t *ticket,
int *continued_write, int *continued_write,
int *logoffsetp); int *logoffsetp);
STATIC int xlog_state_lsn_is_synced(xlog_t *log,
xfs_lsn_t lsn,
xfs_log_callback_t *cb,
int *abortflg);
STATIC void xlog_state_put_ticket(xlog_t *log, STATIC void xlog_state_put_ticket(xlog_t *log,
xlog_ticket_t *tic); xlog_ticket_t *tic);
STATIC int xlog_state_release_iclog(xlog_t *log, STATIC int xlog_state_release_iclog(xlog_t *log,
...@@ -254,6 +252,7 @@ xlog_trace_iclog(xlog_in_core_t *iclog, uint state) ...@@ -254,6 +252,7 @@ xlog_trace_iclog(xlog_in_core_t *iclog, uint state)
xfs_lsn_t xfs_lsn_t
xfs_log_done(xfs_mount_t *mp, xfs_log_done(xfs_mount_t *mp,
xfs_log_ticket_t xtic, xfs_log_ticket_t xtic,
void **iclog,
uint flags) uint flags)
{ {
xlog_t *log = mp->m_log; xlog_t *log = mp->m_log;
...@@ -271,7 +270,8 @@ xfs_log_done(xfs_mount_t *mp, ...@@ -271,7 +270,8 @@ xfs_log_done(xfs_mount_t *mp,
* If we get an error, just continue and give back the log ticket. * If we get an error, just continue and give back the log ticket.
*/ */
(((ticket->t_flags & XLOG_TIC_INITED) == 0) && (((ticket->t_flags & XLOG_TIC_INITED) == 0) &&
(xlog_commit_record(mp, ticket, &lsn)))) { (xlog_commit_record(mp, ticket,
(xlog_in_core_t **)iclog, &lsn)))) {
lsn = (xfs_lsn_t) -1; lsn = (xfs_lsn_t) -1;
if (ticket->t_flags & XLOG_TIC_PERM_RESERV) { if (ticket->t_flags & XLOG_TIC_PERM_RESERV) {
flags |= XFS_LOG_REL_PERM_RESERV; flags |= XFS_LOG_REL_PERM_RESERV;
...@@ -354,21 +354,39 @@ xfs_log_force(xfs_mount_t *mp, ...@@ -354,21 +354,39 @@ xfs_log_force(xfs_mount_t *mp,
* been synced to disk, we add the callback to the callback list of the * been synced to disk, we add the callback to the callback list of the
* in-core log. * in-core log.
*/ */
void int
xfs_log_notify(xfs_mount_t *mp, /* mount of partition */ xfs_log_notify(xfs_mount_t *mp, /* mount of partition */
xfs_lsn_t lsn, /* lsn looking for */ void *iclog_hndl, /* iclog to hang callback off */
xfs_log_callback_t *cb) xfs_log_callback_t *cb)
{ {
xlog_t *log = mp->m_log; xlog_t *log = mp->m_log;
int abortflg; xlog_in_core_t *iclog = (xlog_in_core_t *)iclog_hndl;
int abortflg, spl;
#if defined(DEBUG) || defined(XLOG_NOLOG) #if defined(DEBUG) || defined(XLOG_NOLOG)
if (! xlog_debug && xlog_devt == log->l_dev) if (! xlog_debug && xlog_devt == log->l_dev)
return; return 0;
#endif #endif
cb->cb_next = 0; cb->cb_next = 0;
if (xlog_state_lsn_is_synced(log, lsn, cb, &abortflg)) spl = LOG_LOCK(log);
abortflg = (iclog->ic_state & XLOG_STATE_IOERROR);
if (!abortflg) {
ASSERT_ALWAYS((iclog->ic_state == XLOG_STATE_ACTIVE) ||
(iclog->ic_state == XLOG_STATE_WANT_SYNC));
cb->cb_next = 0;
*(iclog->ic_callback_tail) = cb;
iclog->ic_callback_tail = &(cb->cb_next);
}
LOG_UNLOCK(log, spl);
if (!abortflg) {
if (xlog_state_release_iclog(log, iclog)) {
xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
return EIO;
}
} else {
cb->cb_func(cb->cb_arg, abortflg); cb->cb_func(cb->cb_arg, abortflg);
}
return 0;
} /* xfs_log_notify */ } /* xfs_log_notify */
...@@ -611,7 +629,7 @@ xfs_log_unmount_write(xfs_mount_t *mp) ...@@ -611,7 +629,7 @@ xfs_log_unmount_write(xfs_mount_t *mp)
/* remove inited flag */ /* remove inited flag */
((xlog_ticket_t *)tic)->t_flags = 0; ((xlog_ticket_t *)tic)->t_flags = 0;
error = xlog_write(mp, reg, 1, tic, &lsn, error = xlog_write(mp, reg, 1, tic, &lsn,
XLOG_UNMOUNT_TRANS); NULL, XLOG_UNMOUNT_TRANS);
/* /*
* At this point, we're umounting anyway, * At this point, we're umounting anyway,
* so there's no point in transitioning log state * so there's no point in transitioning log state
...@@ -717,7 +735,7 @@ xfs_log_write(xfs_mount_t * mp, ...@@ -717,7 +735,7 @@ xfs_log_write(xfs_mount_t * mp,
if (XLOG_FORCED_SHUTDOWN(log)) if (XLOG_FORCED_SHUTDOWN(log))
return XFS_ERROR(EIO); return XFS_ERROR(EIO);
if ((error = xlog_write(mp, reg, nentries, tic, start_lsn, 0))) { if ((error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0))) {
xfs_force_shutdown(mp, XFS_LOG_IO_ERROR); xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
} }
return (error); return (error);
...@@ -1259,6 +1277,7 @@ xlog_alloc_log(xfs_mount_t *mp, ...@@ -1259,6 +1277,7 @@ xlog_alloc_log(xfs_mount_t *mp,
STATIC int STATIC int
xlog_commit_record(xfs_mount_t *mp, xlog_commit_record(xfs_mount_t *mp,
xlog_ticket_t *ticket, xlog_ticket_t *ticket,
xlog_in_core_t **iclog,
xfs_lsn_t *commitlsnp) xfs_lsn_t *commitlsnp)
{ {
int error; int error;
...@@ -1267,8 +1286,9 @@ xlog_commit_record(xfs_mount_t *mp, ...@@ -1267,8 +1286,9 @@ xlog_commit_record(xfs_mount_t *mp,
reg[0].i_addr = 0; reg[0].i_addr = 0;
reg[0].i_len = 0; reg[0].i_len = 0;
ASSERT_ALWAYS(iclog);
if ((error = xlog_write(mp, reg, 1, ticket, commitlsnp, if ((error = xlog_write(mp, reg, 1, ticket, commitlsnp,
XLOG_COMMIT_TRANS))) { iclog, XLOG_COMMIT_TRANS))) {
xfs_force_shutdown(mp, XFS_LOG_IO_ERROR); xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
} }
return (error); return (error);
...@@ -1614,6 +1634,7 @@ xlog_write(xfs_mount_t * mp, ...@@ -1614,6 +1634,7 @@ xlog_write(xfs_mount_t * mp,
int nentries, int nentries,
xfs_log_ticket_t tic, xfs_log_ticket_t tic,
xfs_lsn_t *start_lsn, xfs_lsn_t *start_lsn,
xlog_in_core_t **commit_iclog,
uint flags) uint flags)
{ {
xlog_t *log = mp->m_log; xlog_t *log = mp->m_log;
...@@ -1776,7 +1797,10 @@ xlog_write(xfs_mount_t * mp, ...@@ -1776,7 +1797,10 @@ xlog_write(xfs_mount_t * mp,
if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) { if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) {
xlog_state_want_sync(log, iclog); xlog_state_want_sync(log, iclog);
if ((error = xlog_state_release_iclog(log, iclog))) if (commit_iclog) {
ASSERT(flags & XLOG_COMMIT_TRANS);
*commit_iclog = iclog;
} else if ((error = xlog_state_release_iclog(log, iclog)))
return (error); return (error);
if (index == nentries) if (index == nentries)
return 0; /* we are done */ return 0; /* we are done */
...@@ -1788,6 +1812,11 @@ xlog_write(xfs_mount_t * mp, ...@@ -1788,6 +1812,11 @@ xlog_write(xfs_mount_t * mp,
} /* for (index = 0; index < nentries; ) */ } /* for (index = 0; index < nentries; ) */
ASSERT(len == 0); ASSERT(len == 0);
if (commit_iclog) {
ASSERT(flags & XLOG_COMMIT_TRANS);
*commit_iclog = iclog;
return 0;
}
return (xlog_state_release_iclog(log, iclog)); return (xlog_state_release_iclog(log, iclog));
} /* xlog_write */ } /* xlog_write */
...@@ -2060,6 +2089,12 @@ xlog_state_do_callback( ...@@ -2060,6 +2089,12 @@ xlog_state_do_callback(
if (!(iclog->ic_state & XLOG_STATE_IOERROR)) if (!(iclog->ic_state & XLOG_STATE_IOERROR))
iclog->ic_state = XLOG_STATE_DIRTY; iclog->ic_state = XLOG_STATE_DIRTY;
/*
* Transition from DIRTY to ACTIVE if applicable.
* NOP if STATE_IOERROR.
*/
xlog_state_clean_log(log);
/* wake up threads waiting in xfs_log_force() */ /* wake up threads waiting in xfs_log_force() */
sv_broadcast(&iclog->ic_forcesema); sv_broadcast(&iclog->ic_forcesema);
...@@ -2098,12 +2133,6 @@ xlog_state_do_callback( ...@@ -2098,12 +2133,6 @@ xlog_state_do_callback(
} }
#endif #endif
/*
* Transition from DIRTY to ACTIVE if applicable. NOP if
* STATE_IOERROR.
*/
xlog_state_clean_log(log);
if (log->l_iclog->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_IOERROR)) { if (log->l_iclog->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_IOERROR)) {
flushcnt = log->l_flushcnt; flushcnt = log->l_flushcnt;
log->l_flushcnt = 0; log->l_flushcnt = 0;
...@@ -2654,52 +2683,6 @@ xlog_ungrant_log_space(xlog_t *log, ...@@ -2654,52 +2683,6 @@ xlog_ungrant_log_space(xlog_t *log,
} /* xlog_ungrant_log_space */ } /* xlog_ungrant_log_space */
/*
* If the lsn is not found or the iclog with the lsn is in the callback
* state, we need to call the function directly. This is done outside
* this function's scope. Otherwise, we insert the callback at the end
* of the iclog's callback list.
*/
int
xlog_state_lsn_is_synced(xlog_t *log,
xfs_lsn_t lsn,
xfs_log_callback_t *cb,
int *abortflg)
{
xlog_in_core_t *iclog;
SPLDECL(s);
int lsn_is_synced = 1;
*abortflg = 0;
s = LOG_LOCK(log);
iclog = log->l_iclog;
do {
if (INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT) != lsn) {
iclog = iclog->ic_next;
continue;
} else {
if (iclog->ic_state & XLOG_STATE_DIRTY) /* call it*/
break;
if (iclog->ic_state & XLOG_STATE_IOERROR) {
*abortflg = XFS_LI_ABORTED;
break;
}
/* insert callback onto end of list */
cb->cb_next = 0;
*(iclog->ic_callback_tail) = cb;
iclog->ic_callback_tail = &(cb->cb_next);
lsn_is_synced = 0;
break;
}
} while (iclog != log->l_iclog);
LOG_UNLOCK(log, s);
return lsn_is_synced;
} /* xlog_state_lsn_is_synced */
/* /*
* Atomically put back used ticket. * Atomically put back used ticket.
*/ */
......
...@@ -148,6 +148,7 @@ typedef struct xfs_log_callback { ...@@ -148,6 +148,7 @@ typedef struct xfs_log_callback {
struct xfs_mount; struct xfs_mount;
xfs_lsn_t xfs_log_done(struct xfs_mount *mp, xfs_lsn_t xfs_log_done(struct xfs_mount *mp,
xfs_log_ticket_t ticket, xfs_log_ticket_t ticket,
void **iclog,
uint flags); uint flags);
int xfs_log_force(struct xfs_mount *mp, int xfs_log_force(struct xfs_mount *mp,
xfs_lsn_t lsn, xfs_lsn_t lsn,
...@@ -160,8 +161,8 @@ int xfs_log_mount(struct xfs_mount *mp, ...@@ -160,8 +161,8 @@ int xfs_log_mount(struct xfs_mount *mp,
int xfs_log_mount_finish(struct xfs_mount *mp, int); int xfs_log_mount_finish(struct xfs_mount *mp, int);
void xfs_log_move_tail(struct xfs_mount *mp, void xfs_log_move_tail(struct xfs_mount *mp,
xfs_lsn_t tail_lsn); xfs_lsn_t tail_lsn);
void xfs_log_notify(struct xfs_mount *mp, int xfs_log_notify(struct xfs_mount *mp,
xfs_lsn_t lsn, void *iclog,
xfs_log_callback_t *callback_entry); xfs_log_callback_t *callback_entry);
int xfs_log_reserve(struct xfs_mount *mp, int xfs_log_reserve(struct xfs_mount *mp,
int length, int length,
......
...@@ -285,7 +285,7 @@ xfs_trans_reserve( ...@@ -285,7 +285,7 @@ xfs_trans_reserve(
} else { } else {
log_flags = 0; log_flags = 0;
} }
xfs_log_done(tp->t_mountp, tp->t_ticket, log_flags); xfs_log_done(tp->t_mountp, tp->t_ticket, NULL, log_flags);
tp->t_ticket = NULL; tp->t_ticket = NULL;
tp->t_log_res = 0; tp->t_log_res = 0;
tp->t_flags &= ~XFS_TRANS_PERM_LOG_RES; tp->t_flags &= ~XFS_TRANS_PERM_LOG_RES;
...@@ -669,6 +669,7 @@ xfs_trans_commit( ...@@ -669,6 +669,7 @@ xfs_trans_commit(
#if defined(XLOG_NOLOG) || defined(DEBUG) #if defined(XLOG_NOLOG) || defined(DEBUG)
static xfs_lsn_t trans_lsn = 1; static xfs_lsn_t trans_lsn = 1;
#endif #endif
void *commit_iclog;
int shutdown; int shutdown;
commit_lsn = -1; commit_lsn = -1;
...@@ -706,7 +707,8 @@ xfs_trans_commit( ...@@ -706,7 +707,8 @@ xfs_trans_commit(
xfs_trans_unreserve_and_mod_dquots(tp); xfs_trans_unreserve_and_mod_dquots(tp);
} }
if (tp->t_ticket) { if (tp->t_ticket) {
commit_lsn = xfs_log_done(mp, tp->t_ticket, log_flags); commit_lsn = xfs_log_done(mp, tp->t_ticket,
NULL, log_flags);
if (commit_lsn == -1 && !shutdown) if (commit_lsn == -1 && !shutdown)
shutdown = XFS_ERROR(EIO); shutdown = XFS_ERROR(EIO);
} }
...@@ -773,7 +775,7 @@ xfs_trans_commit( ...@@ -773,7 +775,7 @@ xfs_trans_commit(
#if defined(XLOG_NOLOG) || defined(DEBUG) #if defined(XLOG_NOLOG) || defined(DEBUG)
if (xlog_debug) { if (xlog_debug) {
commit_lsn = xfs_log_done(mp, tp->t_ticket, commit_lsn = xfs_log_done(mp, tp->t_ticket,
log_flags); &commit_iclog, log_flags);
} else { } else {
commit_lsn = 0; commit_lsn = 0;
tp->t_lsn = trans_lsn++; tp->t_lsn = trans_lsn++;
...@@ -785,7 +787,7 @@ xfs_trans_commit( ...@@ -785,7 +787,7 @@ xfs_trans_commit(
* any time. However, all the items associated with the transaction * any time. However, all the items associated with the transaction
* are still locked and pinned in memory. * are still locked and pinned in memory.
*/ */
commit_lsn = xfs_log_done(mp, tp->t_ticket, log_flags); commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);
#endif #endif
tp->t_commit_lsn = commit_lsn; tp->t_commit_lsn = commit_lsn;
...@@ -845,20 +847,27 @@ xfs_trans_commit( ...@@ -845,20 +847,27 @@ xfs_trans_commit(
if (xlog_debug) { if (xlog_debug) {
tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed; tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed;
tp->t_logcb.cb_arg = tp; tp->t_logcb.cb_arg = tp;
xfs_log_notify(mp, commit_lsn, &(tp->t_logcb)); error = xfs_log_notify(mp, commit_iclog, &(tp->t_logcb));
} else { } else {
xfs_trans_committed(tp, 0); xfs_trans_committed(tp, 0);
} }
#else #else
tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed; tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed;
tp->t_logcb.cb_arg = tp; tp->t_logcb.cb_arg = tp;
xfs_log_notify(mp, commit_lsn, &(tp->t_logcb));
/* We need to pass the iclog buffer which was used for the
* transaction commit record into this function, attach
* the callback to it, and then release it. This will guarantee
* that we do callbacks on the transaction in the correct order.
*/
error = xfs_log_notify(mp, commit_iclog, &(tp->t_logcb));
#endif #endif
/* /*
* If the transaction needs to be synchronous, then force the * If the transaction needs to be synchronous, then force the
* log out now and wait for it. * log out now and wait for it.
*/ */
if (sync) { if (sync) {
if (!error)
error = xfs_log_force(mp, commit_lsn, error = xfs_log_force(mp, commit_lsn,
XFS_LOG_FORCE | XFS_LOG_SYNC); XFS_LOG_FORCE | XFS_LOG_SYNC);
XFS_STATS_INC(xfsstats.xs_trans_sync); XFS_STATS_INC(xfsstats.xs_trans_sync);
...@@ -1070,7 +1079,7 @@ xfs_trans_cancel( ...@@ -1070,7 +1079,7 @@ xfs_trans_cancel(
} else { } else {
log_flags = 0; log_flags = 0;
} }
xfs_log_done(tp->t_mountp, tp->t_ticket, log_flags); xfs_log_done(tp->t_mountp, tp->t_ticket, NULL, log_flags);
} }
xfs_trans_free_items(tp, flags); xfs_trans_free_items(tp, flags);
xfs_trans_free_busy(tp); xfs_trans_free_busy(tp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment