Skip to content

Commit

Permalink
[XFS] Use atomics for iclog reference counting
Browse files Browse the repository at this point in the history
Now that we update the log tail LSN less frequently on transaction
completion, we pass the contention straight to the global log state lock
(l_iclog_lock) during transaction completion.

We currently have to take this lock to decrement the iclog reference
count. there is a reference count on each iclog, so we need to take þhe
global lock for all refcount changes.

When large numbers of processes are all doing small trnasctions, the iclog
reference counts will be quite high, and the state change that absolutely
requires the l_iclog_lock is the except rather than the norm.

Change the reference counting on the iclogs to use atomic_inc/dec so that
we can use atomic_dec_and_lock during transaction completion and avoid the
need for grabbing the l_iclog_lock for every reference count decrement
except the one that matters - the last.

SGI-PV: 975671
SGI-Modid: xfs-linux-melb:xfs-kern:30505a

Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Tim Shimmin <tes@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
  • Loading branch information
David Chinner authored and Lachlan McIlroy committed Apr 18, 2008
1 parent b589334 commit 155cc6b
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 17 deletions.
36 changes: 20 additions & 16 deletions fs/xfs/xfs_log.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,7 +675,7 @@ xfs_log_unmount_write(xfs_mount_t *mp)

spin_lock(&log->l_icloglock);
iclog = log->l_iclog;
iclog->ic_refcnt++;
atomic_inc(&iclog->ic_refcnt);
spin_unlock(&log->l_icloglock);
xlog_state_want_sync(log, iclog);
(void) xlog_state_release_iclog(log, iclog);
Expand Down Expand Up @@ -713,7 +713,7 @@ xfs_log_unmount_write(xfs_mount_t *mp)
*/
spin_lock(&log->l_icloglock);
iclog = log->l_iclog;
iclog->ic_refcnt++;
atomic_inc(&iclog->ic_refcnt);
spin_unlock(&log->l_icloglock);

xlog_state_want_sync(log, iclog);
Expand Down Expand Up @@ -1405,7 +1405,7 @@ xlog_sync(xlog_t *log,
int v2 = xfs_sb_version_haslogv2(&log->l_mp->m_sb);

XFS_STATS_INC(xs_log_writes);
ASSERT(iclog->ic_refcnt == 0);
ASSERT(atomic_read(&iclog->ic_refcnt) == 0);

/* Add for LR header */
count_init = log->l_iclog_hsize + iclog->ic_offset;
Expand Down Expand Up @@ -2309,7 +2309,7 @@ xlog_state_done_syncing(

ASSERT(iclog->ic_state == XLOG_STATE_SYNCING ||
iclog->ic_state == XLOG_STATE_IOERROR);
ASSERT(iclog->ic_refcnt == 0);
ASSERT(atomic_read(&iclog->ic_refcnt) == 0);
ASSERT(iclog->ic_bwritecnt == 1 || iclog->ic_bwritecnt == 2);


Expand Down Expand Up @@ -2391,7 +2391,7 @@ xlog_state_get_iclog_space(xlog_t *log,
ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
head = &iclog->ic_header;

iclog->ic_refcnt++; /* prevents sync */
atomic_inc(&iclog->ic_refcnt); /* prevents sync */
log_offset = iclog->ic_offset;

/* On the 1st write to an iclog, figure out lsn. This works
Expand Down Expand Up @@ -2423,12 +2423,12 @@ xlog_state_get_iclog_space(xlog_t *log,
xlog_state_switch_iclogs(log, iclog, iclog->ic_size);

/* If I'm the only one writing to this iclog, sync it to disk */
if (iclog->ic_refcnt == 1) {
if (atomic_read(&iclog->ic_refcnt) == 1) {
spin_unlock(&log->l_icloglock);
if ((error = xlog_state_release_iclog(log, iclog)))
return error;
} else {
iclog->ic_refcnt--;
atomic_dec(&iclog->ic_refcnt);
spin_unlock(&log->l_icloglock);
}
goto restart;
Expand Down Expand Up @@ -2819,18 +2819,21 @@ xlog_state_release_iclog(
{
int sync = 0; /* do we sync? */

spin_lock(&log->l_icloglock);
if (iclog->ic_state & XLOG_STATE_IOERROR)
return XFS_ERROR(EIO);

ASSERT(atomic_read(&iclog->ic_refcnt) > 0);
if (!atomic_dec_and_lock(&iclog->ic_refcnt, &log->l_icloglock))
return 0;

if (iclog->ic_state & XLOG_STATE_IOERROR) {
spin_unlock(&log->l_icloglock);
return XFS_ERROR(EIO);
}

ASSERT(iclog->ic_refcnt > 0);
ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE ||
iclog->ic_state == XLOG_STATE_WANT_SYNC);

if (--iclog->ic_refcnt == 0 &&
iclog->ic_state == XLOG_STATE_WANT_SYNC) {
if (iclog->ic_state == XLOG_STATE_WANT_SYNC) {
/* update tail before writing to iclog */
xlog_assign_tail_lsn(log->l_mp);
sync++;
Expand Down Expand Up @@ -2950,22 +2953,23 @@ xlog_state_sync_all(xlog_t *log, uint flags, int *log_flushed)
* previous iclog and go to sleep.
*/
if (iclog->ic_state == XLOG_STATE_DIRTY ||
(iclog->ic_refcnt == 0 && iclog->ic_offset == 0)) {
(atomic_read(&iclog->ic_refcnt) == 0
&& iclog->ic_offset == 0)) {
iclog = iclog->ic_prev;
if (iclog->ic_state == XLOG_STATE_ACTIVE ||
iclog->ic_state == XLOG_STATE_DIRTY)
goto no_sleep;
else
goto maybe_sleep;
} else {
if (iclog->ic_refcnt == 0) {
if (atomic_read(&iclog->ic_refcnt) == 0) {
/* We are the only one with access to this
* iclog. Flush it out now. There should
* be a roundoff of zero to show that someone
* has already taken care of the roundoff from
* the previous sync.
*/
iclog->ic_refcnt++;
atomic_inc(&iclog->ic_refcnt);
lsn = be64_to_cpu(iclog->ic_header.h_lsn);
xlog_state_switch_iclogs(log, iclog, 0);
spin_unlock(&log->l_icloglock);
Expand Down Expand Up @@ -3097,7 +3101,7 @@ xlog_state_sync(xlog_t *log,
already_slept = 1;
goto try_again;
} else {
iclog->ic_refcnt++;
atomic_inc(&iclog->ic_refcnt);
xlog_state_switch_iclogs(log, iclog, 0);
spin_unlock(&log->l_icloglock);
if (xlog_state_release_iclog(log, iclog))
Expand Down
2 changes: 1 addition & 1 deletion fs/xfs/xfs_log_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ typedef struct xlog_iclog_fields {
#endif
int ic_size;
int ic_offset;
int ic_refcnt;
atomic_t ic_refcnt;
int ic_bwritecnt;
ushort_t ic_state;
char *ic_datap; /* pointer to iclog data */
Expand Down

0 comments on commit 155cc6b

Please sign in to comment.