xfs: factor xlog_write
xlog_write is a mess that takes a lot of effort to understand. It is a mass of nested loops with 4 space indents to get it to fit in 80 columns and lots of funky variables that aren't obvious what they mean or do. Break it down into understandable chunks. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com>
This commit is contained in:
parent
9b9fc2b760
commit
b5203cd0a4
1 changed files with 225 additions and 112 deletions
337
fs/xfs/xfs_log.c
337
fs/xfs/xfs_log.c
|
@ -1629,6 +1629,193 @@ xlog_print_tic_res(xfs_mount_t *mp, xlog_ticket_t *ticket)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the potential space needed by the log vector. Each region gets
|
||||
* its own xlog_op_header_t and may need to be double word aligned.
|
||||
*/
|
||||
static int
|
||||
xlog_write_calc_vec_length(
|
||||
struct xlog_ticket *ticket,
|
||||
struct xfs_log_iovec reg[],
|
||||
int nentries)
|
||||
{
|
||||
int headers = 0;
|
||||
int len = 0;
|
||||
int i;
|
||||
|
||||
/* acct for start rec of xact */
|
||||
if (ticket->t_flags & XLOG_TIC_INITED)
|
||||
headers++;
|
||||
|
||||
for (i = 0; i < nentries; i++) {
|
||||
/* each region gets >= 1 */
|
||||
headers++;
|
||||
|
||||
len += reg[i].i_len;
|
||||
xlog_tic_add_region(ticket, reg[i].i_len, reg[i].i_type);
|
||||
}
|
||||
|
||||
ticket->t_res_num_ophdrs += headers;
|
||||
len += headers * sizeof(struct xlog_op_header);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* If first write for transaction, insert start record We can't be trying to
|
||||
* commit if we are inited. We can't have any "partial_copy" if we are inited.
|
||||
*/
|
||||
static int
|
||||
xlog_write_start_rec(
|
||||
__psint_t ptr,
|
||||
struct xlog_ticket *ticket)
|
||||
{
|
||||
struct xlog_op_header *ophdr = (struct xlog_op_header *)ptr;
|
||||
|
||||
if (!(ticket->t_flags & XLOG_TIC_INITED))
|
||||
return 0;
|
||||
|
||||
ophdr->oh_tid = cpu_to_be32(ticket->t_tid);
|
||||
ophdr->oh_clientid = ticket->t_clientid;
|
||||
ophdr->oh_len = 0;
|
||||
ophdr->oh_flags = XLOG_START_TRANS;
|
||||
ophdr->oh_res2 = 0;
|
||||
|
||||
ticket->t_flags &= ~XLOG_TIC_INITED;
|
||||
|
||||
return sizeof(struct xlog_op_header);
|
||||
}
|
||||
|
||||
static xlog_op_header_t *
|
||||
xlog_write_setup_ophdr(
|
||||
struct log *log,
|
||||
__psint_t ptr,
|
||||
struct xlog_ticket *ticket,
|
||||
uint flags)
|
||||
{
|
||||
struct xlog_op_header *ophdr = (struct xlog_op_header *)ptr;
|
||||
|
||||
ophdr->oh_tid = cpu_to_be32(ticket->t_tid);
|
||||
ophdr->oh_clientid = ticket->t_clientid;
|
||||
ophdr->oh_res2 = 0;
|
||||
|
||||
/* are we copying a commit or unmount record? */
|
||||
ophdr->oh_flags = flags;
|
||||
|
||||
/*
|
||||
* We've seen logs corrupted with bad transaction client ids. This
|
||||
* makes sure that XFS doesn't generate them on. Turn this into an EIO
|
||||
* and shut down the filesystem.
|
||||
*/
|
||||
switch (ophdr->oh_clientid) {
|
||||
case XFS_TRANSACTION:
|
||||
case XFS_VOLUME:
|
||||
case XFS_LOG:
|
||||
break;
|
||||
default:
|
||||
xfs_fs_cmn_err(CE_WARN, log->l_mp,
|
||||
"Bad XFS transaction clientid 0x%x in ticket 0x%p",
|
||||
ophdr->oh_clientid, ticket);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ophdr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the parameters of the region copy into the log. This has
|
||||
* to handle region write split across multiple log buffers - this
|
||||
* state is kept external to this function so that this code can
|
||||
* can be written in an obvious, self documenting manner.
|
||||
*/
|
||||
static int
|
||||
xlog_write_setup_copy(
|
||||
struct xlog_ticket *ticket,
|
||||
struct xlog_op_header *ophdr,
|
||||
int space_available,
|
||||
int space_required,
|
||||
int *copy_off,
|
||||
int *copy_len,
|
||||
int *last_was_partial_copy,
|
||||
int *bytes_consumed)
|
||||
{
|
||||
int still_to_copy;
|
||||
|
||||
still_to_copy = space_required - *bytes_consumed;
|
||||
*copy_off = *bytes_consumed;
|
||||
|
||||
if (still_to_copy <= space_available) {
|
||||
/* write of region completes here */
|
||||
*copy_len = still_to_copy;
|
||||
ophdr->oh_len = cpu_to_be32(*copy_len);
|
||||
if (*last_was_partial_copy)
|
||||
ophdr->oh_flags |= (XLOG_END_TRANS|XLOG_WAS_CONT_TRANS);
|
||||
*last_was_partial_copy = 0;
|
||||
*bytes_consumed = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* partial write of region, needs extra log op header reservation */
|
||||
*copy_len = space_available;
|
||||
ophdr->oh_len = cpu_to_be32(*copy_len);
|
||||
ophdr->oh_flags |= XLOG_CONTINUE_TRANS;
|
||||
if (*last_was_partial_copy)
|
||||
ophdr->oh_flags |= XLOG_WAS_CONT_TRANS;
|
||||
*bytes_consumed += *copy_len;
|
||||
(*last_was_partial_copy)++;
|
||||
|
||||
/* account for new log op header */
|
||||
ticket->t_curr_res -= sizeof(struct xlog_op_header);
|
||||
ticket->t_res_num_ophdrs++;
|
||||
|
||||
return sizeof(struct xlog_op_header);
|
||||
}
|
||||
|
||||
static int
|
||||
xlog_write_copy_finish(
|
||||
struct log *log,
|
||||
struct xlog_in_core *iclog,
|
||||
uint flags,
|
||||
int *record_cnt,
|
||||
int *data_cnt,
|
||||
int *partial_copy,
|
||||
int *partial_copy_len,
|
||||
int log_offset,
|
||||
struct xlog_in_core **commit_iclog)
|
||||
{
|
||||
if (*partial_copy) {
|
||||
/*
|
||||
* This iclog has already been marked WANT_SYNC by
|
||||
* xlog_state_get_iclog_space.
|
||||
*/
|
||||
xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt);
|
||||
*record_cnt = 0;
|
||||
*data_cnt = 0;
|
||||
return xlog_state_release_iclog(log, iclog);
|
||||
}
|
||||
|
||||
*partial_copy = 0;
|
||||
*partial_copy_len = 0;
|
||||
|
||||
if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) {
|
||||
/* no more space in this iclog - push it. */
|
||||
xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt);
|
||||
*record_cnt = 0;
|
||||
*data_cnt = 0;
|
||||
|
||||
spin_lock(&log->l_icloglock);
|
||||
xlog_state_want_sync(log, iclog);
|
||||
spin_unlock(&log->l_icloglock);
|
||||
|
||||
if (!commit_iclog)
|
||||
return xlog_state_release_iclog(log, iclog);
|
||||
ASSERT(flags & XLOG_COMMIT_TRANS);
|
||||
*commit_iclog = iclog;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write some region out to in-core log
|
||||
*
|
||||
|
@ -1689,7 +1876,6 @@ xlog_write(
|
|||
int start_rec_copy; /* # bytes to copy for start record */
|
||||
int partial_copy; /* did we split a region? */
|
||||
int partial_copy_len;/* # bytes copied if split region */
|
||||
int need_copy; /* # bytes need to memcpy this region */
|
||||
int copy_len; /* # bytes actually memcpy'ing */
|
||||
int copy_off; /* # bytes from entry start */
|
||||
int contwr; /* continued write of in-core log? */
|
||||
|
@ -1697,24 +1883,9 @@ xlog_write(
|
|||
int record_cnt = 0, data_cnt = 0;
|
||||
|
||||
partial_copy_len = partial_copy = 0;
|
||||
|
||||
/* Calculate potential maximum space. Each region gets its own
|
||||
* xlog_op_header_t and may need to be double word aligned.
|
||||
*/
|
||||
len = 0;
|
||||
if (ticket->t_flags & XLOG_TIC_INITED) { /* acct for start rec of xact */
|
||||
len += sizeof(xlog_op_header_t);
|
||||
ticket->t_res_num_ophdrs++;
|
||||
}
|
||||
|
||||
for (index = 0; index < nentries; index++) {
|
||||
len += sizeof(xlog_op_header_t); /* each region gets >= 1 */
|
||||
ticket->t_res_num_ophdrs++;
|
||||
len += reg[index].i_len;
|
||||
xlog_tic_add_region(ticket, reg[index].i_len, reg[index].i_type);
|
||||
}
|
||||
contwr = *start_lsn = 0;
|
||||
|
||||
len = xlog_write_calc_vec_length(ticket, reg, nentries);
|
||||
if (ticket->t_curr_res < len) {
|
||||
xlog_print_tic_res(mp, ticket);
|
||||
#ifdef DEBUG
|
||||
|
@ -1748,81 +1919,23 @@ xlog_write(
|
|||
while (index < nentries) {
|
||||
ASSERT(reg[index].i_len % sizeof(__int32_t) == 0);
|
||||
ASSERT((__psint_t)ptr % sizeof(__int32_t) == 0);
|
||||
start_rec_copy = 0;
|
||||
|
||||
/* If first write for transaction, insert start record.
|
||||
* We can't be trying to commit if we are inited. We can't
|
||||
* have any "partial_copy" if we are inited.
|
||||
*/
|
||||
if (ticket->t_flags & XLOG_TIC_INITED) {
|
||||
logop_head = (xlog_op_header_t *)ptr;
|
||||
logop_head->oh_tid = cpu_to_be32(ticket->t_tid);
|
||||
logop_head->oh_clientid = ticket->t_clientid;
|
||||
logop_head->oh_len = 0;
|
||||
logop_head->oh_flags = XLOG_START_TRANS;
|
||||
logop_head->oh_res2 = 0;
|
||||
ticket->t_flags &= ~XLOG_TIC_INITED; /* clear bit */
|
||||
start_rec_copy = xlog_write_start_rec(ptr, ticket);
|
||||
if (start_rec_copy) {
|
||||
record_cnt++;
|
||||
|
||||
start_rec_copy = sizeof(xlog_op_header_t);
|
||||
xlog_write_adv_cnt(ptr, len, log_offset, start_rec_copy);
|
||||
}
|
||||
|
||||
/* Copy log operation header directly into data section */
|
||||
logop_head = (xlog_op_header_t *)ptr;
|
||||
logop_head->oh_tid = cpu_to_be32(ticket->t_tid);
|
||||
logop_head->oh_clientid = ticket->t_clientid;
|
||||
logop_head->oh_res2 = 0;
|
||||
|
||||
/* header copied directly */
|
||||
logop_head = xlog_write_setup_ophdr(log, ptr, ticket, flags);
|
||||
if (!logop_head)
|
||||
return XFS_ERROR(EIO);
|
||||
xlog_write_adv_cnt(ptr, len, log_offset, sizeof(xlog_op_header_t));
|
||||
|
||||
/* are we copying a commit or unmount record? */
|
||||
logop_head->oh_flags = flags;
|
||||
|
||||
/*
|
||||
* We've seen logs corrupted with bad transaction client
|
||||
* ids. This makes sure that XFS doesn't generate them on.
|
||||
* Turn this into an EIO and shut down the filesystem.
|
||||
*/
|
||||
switch (logop_head->oh_clientid) {
|
||||
case XFS_TRANSACTION:
|
||||
case XFS_VOLUME:
|
||||
case XFS_LOG:
|
||||
break;
|
||||
default:
|
||||
xfs_fs_cmn_err(CE_WARN, mp,
|
||||
"Bad XFS transaction clientid 0x%x in ticket 0x%p",
|
||||
logop_head->oh_clientid, ticket);
|
||||
return XFS_ERROR(EIO);
|
||||
}
|
||||
|
||||
/* Partial write last time? => (partial_copy != 0)
|
||||
* need_copy is the amount we'd like to copy if everything could
|
||||
* fit in the current memcpy.
|
||||
*/
|
||||
need_copy = reg[index].i_len - partial_copy_len;
|
||||
|
||||
copy_off = partial_copy_len;
|
||||
if (need_copy <= iclog->ic_size - log_offset) { /*complete write */
|
||||
copy_len = need_copy;
|
||||
logop_head->oh_len = cpu_to_be32(copy_len);
|
||||
if (partial_copy)
|
||||
logop_head->oh_flags|= (XLOG_END_TRANS|XLOG_WAS_CONT_TRANS);
|
||||
partial_copy_len = partial_copy = 0;
|
||||
} else { /* partial write */
|
||||
copy_len = iclog->ic_size - log_offset;
|
||||
logop_head->oh_len = cpu_to_be32(copy_len);
|
||||
logop_head->oh_flags |= XLOG_CONTINUE_TRANS;
|
||||
if (partial_copy)
|
||||
logop_head->oh_flags |= XLOG_WAS_CONT_TRANS;
|
||||
partial_copy_len += copy_len;
|
||||
partial_copy++;
|
||||
len += sizeof(xlog_op_header_t); /* from splitting of region */
|
||||
/* account for new log op header */
|
||||
ticket->t_curr_res -= sizeof(xlog_op_header_t);
|
||||
ticket->t_res_num_ophdrs++;
|
||||
}
|
||||
len += xlog_write_setup_copy(ticket, logop_head,
|
||||
iclog->ic_size - log_offset,
|
||||
reg[index].i_len, ©_off,
|
||||
©_len, &partial_copy,
|
||||
&partial_copy_len);
|
||||
xlog_verify_dest_ptr(log, ptr);
|
||||
|
||||
/* copy region */
|
||||
|
@ -1834,34 +1947,34 @@ xlog_write(
|
|||
copy_len += start_rec_copy + sizeof(xlog_op_header_t);
|
||||
record_cnt++;
|
||||
data_cnt += contwr ? copy_len : 0;
|
||||
if (partial_copy) { /* copied partial region */
|
||||
/* already marked WANT_SYNC by xlog_state_get_iclog_space */
|
||||
xlog_state_finish_copy(log, iclog, record_cnt, data_cnt);
|
||||
record_cnt = data_cnt = 0;
|
||||
if ((error = xlog_state_release_iclog(log, iclog)))
|
||||
return error;
|
||||
break; /* don't increment index */
|
||||
} else { /* copied entire region */
|
||||
index++;
|
||||
partial_copy_len = partial_copy = 0;
|
||||
|
||||
if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) {
|
||||
xlog_state_finish_copy(log, iclog, record_cnt, data_cnt);
|
||||
record_cnt = data_cnt = 0;
|
||||
spin_lock(&log->l_icloglock);
|
||||
xlog_state_want_sync(log, iclog);
|
||||
spin_unlock(&log->l_icloglock);
|
||||
if (commit_iclog) {
|
||||
ASSERT(flags & XLOG_COMMIT_TRANS);
|
||||
*commit_iclog = iclog;
|
||||
} else if ((error = xlog_state_release_iclog(log, iclog)))
|
||||
return error;
|
||||
if (index == nentries)
|
||||
return 0; /* we are done */
|
||||
else
|
||||
break;
|
||||
}
|
||||
} /* if (partial_copy) */
|
||||
error = xlog_write_copy_finish(log, iclog, flags,
|
||||
&record_cnt, &data_cnt,
|
||||
&partial_copy, &partial_copy_len,
|
||||
log_offset, commit_iclog);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* if we had a partial copy, we need to get more iclog
|
||||
* space but we don't want to increment the region
|
||||
* index because there is still more is this region to write.
|
||||
*
|
||||
* If we completed writing this region, and we flushed
|
||||
* the iclog (indicated by resetting of the record
|
||||
* count), then we also need to get more log space. If
|
||||
* this was the last record, though, we are done and
|
||||
* can just return.
|
||||
*/
|
||||
if (partial_copy)
|
||||
break;
|
||||
|
||||
index++;
|
||||
if (record_cnt == 0) {
|
||||
if (index == nentries)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
} /* while (index < nentries) */
|
||||
} /* for (index = 0; index < nentries; ) */
|
||||
ASSERT(len == 0);
|
||||
|
|
Loading…
Reference in a new issue