summaryrefslogtreecommitdiff
path: root/src/log
diff options
context:
space:
mode:
Diffstat (limited to 'src/log')
-rw-r--r--src/log/log.c124
-rw-r--r--src/log/log_slot.c378
2 files changed, 331 insertions, 171 deletions
diff --git a/src/log/log.c b/src/log/log.c
index 413df312a15..803d3e8dfab 100644
--- a/src/log/log.c
+++ b/src/log/log.c
@@ -24,7 +24,7 @@ static int __log_write_internal(
* __log_wait_for_earlier_slot --
* Wait for write_lsn to catch up to this slot.
*/
-static void
+static int
__log_wait_for_earlier_slot(WT_SESSION_IMPL *session, WT_LOGSLOT *slot)
{
WT_CONNECTION_IMPL *conn;
@@ -41,16 +41,18 @@ __log_wait_for_earlier_slot(WT_SESSION_IMPL *session, WT_LOGSLOT *slot)
* unlock in case an earlier thread is trying to switch its
* slot and complete its operation.
*/
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
__wt_spin_unlock(session, &log->log_slot_lock);
- __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
+ __wt_cond_signal(session, conn->log_wrlsn_cond);
if (++yield_count < WT_THOUSAND)
__wt_yield();
else
- __wt_cond_wait(session, log->log_write_cond, 200);
+ __wt_cond_wait(session, log->log_write_cond, 200, NULL);
if (F_ISSET(session, WT_SESSION_LOCKED_SLOT))
__wt_spin_lock(session, &log->log_slot_lock);
}
+ return (0);
}
/*
@@ -62,16 +64,21 @@ static int
__log_fs_write(WT_SESSION_IMPL *session,
WT_LOGSLOT *slot, wt_off_t offset, size_t len, const void *buf)
{
+ WT_DECL_RET;
+
/*
* If we're writing into a new log file, we have to wait for all
* writes to the previous log file to complete otherwise there could
* be a hole at the end of the previous log file that we cannot detect.
*/
if (slot->slot_release_lsn.l.file < slot->slot_start_lsn.l.file) {
- __log_wait_for_earlier_slot(session, slot);
+ WT_RET(__log_wait_for_earlier_slot(session, slot));
WT_RET(__wt_log_force_sync(session, &slot->slot_release_lsn));
}
- return (__wt_write(session, slot->slot_fh, offset, len, buf));
+ if ((ret = __wt_write(session, slot->slot_fh, offset, len, buf)) != 0)
+ WT_PANIC_MSG(session, ret,
+ "%s: fatal log failure", slot->slot_fh->name);
+ return (ret);
}
/*
@@ -89,7 +96,7 @@ __wt_log_ckpt(WT_SESSION_IMPL *session, WT_LSN *ckp_lsn)
log = conn->log;
log->ckpt_lsn = *ckp_lsn;
if (conn->log_cond != NULL)
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
}
/*
@@ -105,6 +112,7 @@ __wt_log_flush_lsn(WT_SESSION_IMPL *session, WT_LSN *lsn, bool start)
conn = S2C(session);
log = conn->log;
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
WT_RET(__wt_log_force_write(session, 1, NULL));
__wt_log_wrlsn(session, NULL);
if (start)
@@ -169,8 +177,9 @@ __wt_log_force_sync(WT_SESSION_IMPL *session, WT_LSN *min_lsn)
* log file ready to close.
*/
while (log->sync_lsn.l.file < min_lsn->l.file) {
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
__wt_cond_signal(session, S2C(session)->log_file_cond);
- __wt_cond_wait(session, log->log_sync_cond, 10000);
+ __wt_cond_wait(session, log->log_sync_cond, 10000, NULL);
}
__wt_spin_lock(session, &log->log_sync_lock);
WT_ASSERT(session, log->log_dir_fh != NULL);
@@ -300,14 +309,11 @@ void
__wt_log_written_reset(WT_SESSION_IMPL *session)
{
WT_CONNECTION_IMPL *conn;
- WT_LOG *log;
conn = S2C(session);
- if (!FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))
- return;
- log = conn->log;
- log->log_written = 0;
- return;
+
+ if (FLD_ISSET(conn->log_flags, WT_CONN_LOG_ENABLED))
+ conn->log->log_written = 0;
}
/*
@@ -777,8 +783,8 @@ __log_openfile(WT_SESSION_IMPL *session,
__wt_log_desc_byteswap(desc);
if (desc->log_magic != WT_LOG_MAGIC)
WT_PANIC_RET(session, WT_ERROR,
- "log file %s corrupted: Bad magic number %" PRIu32,
- (*fhp)->name, desc->log_magic);
+ "log file %s corrupted: Bad magic number %" PRIu32,
+ (*fhp)->name, desc->log_magic);
if (desc->majorv > WT_LOG_MAJOR_VERSION ||
(desc->majorv == WT_LOG_MAJOR_VERSION &&
desc->minorv > WT_LOG_MINOR_VERSION))
@@ -895,12 +901,12 @@ __log_newfile(WT_SESSION_IMPL *session, bool conn_open, bool *created)
*/
create_log = true;
if (conn->log_prealloc > 0 && !conn->hot_backup) {
- __wt_readlock(session, conn->hot_backup_lock);
+ __wt_readlock(session, &conn->hot_backup_lock);
if (conn->hot_backup)
- __wt_readunlock(session, conn->hot_backup_lock);
+ __wt_readunlock(session, &conn->hot_backup_lock);
else {
ret = __log_alloc_prealloc(session, log->fileid);
- __wt_readunlock(session, conn->hot_backup_lock);
+ __wt_readunlock(session, &conn->hot_backup_lock);
/*
* If ret is 0 it means we found a pre-allocated file.
@@ -915,7 +921,7 @@ __log_newfile(WT_SESSION_IMPL *session, bool conn_open, bool *created)
else {
WT_STAT_CONN_INCR(session, log_prealloc_missed);
if (conn->log_cond != NULL)
- __wt_cond_auto_signal(
+ __wt_cond_signal(
session, conn->log_cond);
}
}
@@ -1029,12 +1035,12 @@ __log_truncate_file(WT_SESSION_IMPL *session, WT_FH *log_fh, wt_off_t offset)
log = conn->log;
if (!F_ISSET(log, WT_LOG_TRUNCATE_NOTSUP) && !conn->hot_backup) {
- __wt_readlock(session, conn->hot_backup_lock);
+ __wt_readlock(session, &conn->hot_backup_lock);
if (conn->hot_backup)
- __wt_readunlock(session, conn->hot_backup_lock);
+ __wt_readunlock(session, &conn->hot_backup_lock);
else {
ret = __wt_ftruncate(session, log_fh, offset);
- __wt_readunlock(session, conn->hot_backup_lock);
+ __wt_readunlock(session, &conn->hot_backup_lock);
if (ret != ENOTSUP)
return (ret);
F_SET(log, WT_LOG_TRUNCATE_NOTSUP);
@@ -1462,7 +1468,7 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
* be holes in the log file.
*/
WT_STAT_CONN_INCR(session, log_release_write_lsn);
- __log_wait_for_earlier_slot(session, slot);
+ WT_ERR(__log_wait_for_earlier_slot(session, slot));
log->write_start_lsn = slot->slot_start_lsn;
log->write_lsn = slot->slot_end_lsn;
@@ -1483,6 +1489,7 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
* current fsync completes and advance log->sync_lsn.
*/
while (F_ISSET(slot, WT_SLOT_SYNC | WT_SLOT_SYNC_DIR)) {
+ WT_ERR(WT_SESSION_CHECK_PANIC(session));
/*
* We have to wait until earlier log files have finished their
* sync operations. The most recent one will set the LSN to the
@@ -1490,7 +1497,8 @@ __wt_log_release(WT_SESSION_IMPL *session, WT_LOGSLOT *slot, bool *freep)
*/
if (log->sync_lsn.l.file < slot->slot_end_lsn.l.file ||
__wt_spin_trylock(session, &log->log_sync_lock) != 0) {
- __wt_cond_wait(session, log->log_sync_cond, 10000);
+ __wt_cond_wait(
+ session, log->log_sync_cond, 10000, NULL);
continue;
}
locked = true;
@@ -1655,10 +1663,7 @@ __wt_log_scan(WT_SESSION_IMPL *session, WT_LSN *lsnp, uint32_t flags,
WT_RET(__log_get_files(session,
WT_LOG_FILENAME, &logfiles, &logcount));
if (logcount == 0)
- /*
- * Return it is not supported if none don't exist.
- */
- return (ENOTSUP);
+ WT_RET_MSG(session, ENOTSUP, "no log files found");
for (i = 0; i < logcount; i++) {
WT_ERR(__wt_log_extract_lognum(session, logfiles[i],
&lognum));
@@ -1674,6 +1679,10 @@ __wt_log_scan(WT_SESSION_IMPL *session, WT_LSN *lsnp, uint32_t flags,
&log_fh, WT_LOG_FILENAME, start_lsn.l.file, WT_LOG_OPEN_VERIFY));
WT_ERR(__wt_filesize(session, log_fh, &log_size));
rd_lsn = start_lsn;
+ if (LF_ISSET(WT_LOGSCAN_RECOVER))
+ __wt_verbose(session, WT_VERB_RECOVERY_PROGRESS,
+ "Recovering log %" PRIu32 " through %" PRIu32,
+ rd_lsn.l.file, end_lsn.l.file);
WT_ERR(__wt_scr_alloc(session, WT_LOG_ALIGN, &buf));
WT_ERR(__wt_scr_alloc(session, 0, &decryptitem));
@@ -1722,6 +1731,11 @@ advance:
WT_ERR(__log_openfile(session,
&log_fh, WT_LOG_FILENAME,
rd_lsn.l.file, WT_LOG_OPEN_VERIFY));
+ if (LF_ISSET(WT_LOGSCAN_RECOVER))
+ __wt_verbose(session, WT_VERB_RECOVERY_PROGRESS,
+ "Recovering log %" PRIu32
+ " through %" PRIu32,
+ rd_lsn.l.file, end_lsn.l.file);
WT_ERR(__wt_filesize(session, log_fh, &log_size));
eol = false;
continue;
@@ -1758,9 +1772,8 @@ advance:
if (eol)
/* Found a hole. This LSN is the end. */
break;
- else
- /* Last record in log. Look for more. */
- goto advance;
+ /* Last record in log. Look for more. */
+ goto advance;
}
rdup_len = __wt_rduppo2(reclen, allocsize);
if (reclen > allocsize) {
@@ -1906,7 +1919,6 @@ __wt_log_force_write(WT_SESSION_IMPL *session, bool retry, bool *did_work)
{
WT_LOG *log;
WT_MYSLOT myslot;
- uint32_t joined;
log = S2C(session)->log;
memset(&myslot, 0, sizeof(myslot));
@@ -1914,14 +1926,7 @@ __wt_log_force_write(WT_SESSION_IMPL *session, bool retry, bool *did_work)
if (did_work != NULL)
*did_work = true;
myslot.slot = log->active_slot;
- joined = WT_LOG_SLOT_JOINED(log->active_slot->slot_state);
- if (joined == 0) {
- WT_STAT_CONN_INCR(session, log_force_write_skip);
- if (did_work != NULL)
- *did_work = false;
- return (0);
- }
- return (__wt_log_slot_switch(session, &myslot, retry, true));
+ return (__wt_log_slot_switch(session, &myslot, retry, true, did_work));
}
/*
@@ -2120,7 +2125,11 @@ __log_write_internal(WT_SESSION_IMPL *session, WT_ITEM *record, WT_LSN *lsnp,
WT_STAT_CONN_INCR(session, log_writes);
- __wt_log_slot_join(session, rdup_len, flags, &myslot);
+ /*
+ * The only time joining a slot should ever return an error is if it
+ * detects a panic.
+ */
+ WT_ERR(__wt_log_slot_join(session, rdup_len, flags, &myslot));
/*
* If the addition of this record crosses the buffer boundary,
* switch in a new slot.
@@ -2129,7 +2138,7 @@ __log_write_internal(WT_SESSION_IMPL *session, WT_ITEM *record, WT_LSN *lsnp,
ret = 0;
if (myslot.end_offset >= WT_LOG_SLOT_BUF_MAX ||
F_ISSET(&myslot, WT_MYSLOT_UNBUFFERED) || force)
- ret = __wt_log_slot_switch(session, &myslot, true, false);
+ ret = __wt_log_slot_switch(session, &myslot, true, false, NULL);
if (ret == 0)
ret = __log_fill(session, &myslot, false, record, &lsn);
release_size = __wt_log_slot_release(
@@ -2154,7 +2163,7 @@ __log_write_internal(WT_SESSION_IMPL *session, WT_ITEM *record, WT_LSN *lsnp,
* XXX I've seen times when conditions are NULL.
*/
if (conn->log_cond != NULL) {
- __wt_cond_auto_signal(session, conn->log_cond);
+ __wt_cond_signal(session, conn->log_cond);
__wt_yield();
} else
WT_ERR(__wt_log_force_write(session, 1, NULL));
@@ -2162,13 +2171,19 @@ __log_write_internal(WT_SESSION_IMPL *session, WT_ITEM *record, WT_LSN *lsnp,
if (LF_ISSET(WT_LOG_FLUSH)) {
/* Wait for our writes to reach the OS */
while (__wt_log_cmp(&log->write_lsn, &lsn) <= 0 &&
- myslot.slot->slot_error == 0)
- __wt_cond_wait(session, log->log_write_cond, 10000);
+ myslot.slot->slot_error == 0) {
+ WT_ERR(WT_SESSION_CHECK_PANIC(session));
+ __wt_cond_wait(
+ session, log->log_write_cond, 10000, NULL);
+ }
} else if (LF_ISSET(WT_LOG_FSYNC)) {
/* Wait for our writes to reach disk */
while (__wt_log_cmp(&log->sync_lsn, &lsn) <= 0 &&
- myslot.slot->slot_error == 0)
- __wt_cond_wait(session, log->log_sync_cond, 10000);
+ myslot.slot->slot_error == 0) {
+ WT_ERR(WT_SESSION_CHECK_PANIC(session));
+ __wt_cond_wait(
+ session, log->log_sync_cond, 10000, NULL);
+ }
}
/*
@@ -2193,12 +2208,12 @@ err:
/*
* If one of the sync flags is set, assert the proper LSN has moved to
- * match.
+ * match on success.
*/
- WT_ASSERT(session, !LF_ISSET(WT_LOG_FLUSH) ||
+ WT_ASSERT(session, ret != 0 || !LF_ISSET(WT_LOG_FLUSH) ||
__wt_log_cmp(&log->write_lsn, &lsn) >= 0);
- WT_ASSERT(session,
- !LF_ISSET(WT_LOG_FSYNC) || __wt_log_cmp(&log->sync_lsn, &lsn) >= 0);
+ WT_ASSERT(session, ret != 0 || !LF_ISSET(WT_LOG_FSYNC) ||
+ __wt_log_cmp(&log->sync_lsn, &lsn) >= 0);
return (ret);
}
@@ -2223,8 +2238,10 @@ __wt_log_vprintf(WT_SESSION_IMPL *session, const char *fmt, va_list ap)
return (0);
va_copy(ap_copy, ap);
- len = (size_t)vsnprintf(NULL, 0, fmt, ap_copy) + 1;
+ len = 1;
+ ret = __wt_vsnprintf_len_incr(NULL, 0, &len, fmt, ap_copy);
va_end(ap_copy);
+ WT_RET(ret);
WT_RET(
__wt_logrec_alloc(session, sizeof(WT_LOG_RECORD) + len, &logrec));
@@ -2241,7 +2258,8 @@ __wt_log_vprintf(WT_SESSION_IMPL *session, const char *fmt, va_list ap)
rec_fmt, rectype));
logrec->size += (uint32_t)header_size;
- (void)vsnprintf((char *)logrec->data + logrec->size, len, fmt, ap);
+ WT_ERR(__wt_vsnprintf(
+ (char *)logrec->data + logrec->size, len, fmt, ap));
__wt_verbose(session, WT_VERB_LOG,
"log_printf: %s", (char *)logrec->data + logrec->size);
diff --git a/src/log/log_slot.c b/src/log/log_slot.c
index a29a34e5652..97e317ce68c 100644
--- a/src/log/log_slot.c
+++ b/src/log/log_slot.c
@@ -8,6 +8,49 @@
#include "wt_internal.h"
+#ifdef HAVE_DIAGNOSTIC
+/*
+ * __log_slot_dump --
+ * Dump the entire slot state.
+ */
+static void
+__log_slot_dump(WT_SESSION_IMPL *session)
+{
+ WT_CONNECTION_IMPL *conn;
+ WT_LOG *log;
+ WT_LOGSLOT *slot;
+ int earliest, i;
+
+ conn = S2C(session);
+ log = conn->log;
+ earliest = 0;
+ for (i = 0; i < WT_SLOT_POOL; i++) {
+ slot = &log->slot_pool[i];
+ if (__wt_log_cmp(&slot->slot_release_lsn,
+ &log->slot_pool[earliest].slot_release_lsn) < 0)
+ earliest = i;
+ __wt_errx(session, "Slot %d:", i);
+ __wt_errx(session, " State: %" PRIx64 " Flags: %" PRIx32,
+ slot->slot_state, slot->flags);
+ __wt_errx(session, " Start LSN: %" PRIu32 "/%" PRIu32,
+ slot->slot_start_lsn.l.file, slot->slot_start_lsn.l.offset);
+ __wt_errx(session, " End LSN: %" PRIu32 "/%" PRIu32,
+ slot->slot_end_lsn.l.file, slot->slot_end_lsn.l.offset);
+ __wt_errx(session, " Release LSN: %" PRIu32 "/%" PRIu32,
+ slot->slot_release_lsn.l.file,
+ slot->slot_release_lsn.l.offset);
+ __wt_errx(session, " Offset: start: %" PRIuMAX
+ " last:%" PRIuMAX, (uintmax_t)slot->slot_start_offset,
+ (uintmax_t)slot->slot_last_offset);
+ __wt_errx(session, " Unbuffered: %" PRId64
+ " error: %" PRId32, slot->slot_unbuffered,
+ slot->slot_error);
+ }
+ __wt_errx(session, "Earliest slot: %d", earliest);
+
+}
+#endif
+
/*
* __wt_log_slot_activate --
* Initialize a slot to become active.
@@ -21,7 +64,6 @@ __wt_log_slot_activate(WT_SESSION_IMPL *session, WT_LOGSLOT *slot)
conn = S2C(session);
log = conn->log;
- slot->slot_state = 0;
/*
* !!! slot_release_lsn must be set outside this function because
* this function may be called after a log file switch and the
@@ -30,12 +72,19 @@ __wt_log_slot_activate(WT_SESSION_IMPL *session, WT_LOGSLOT *slot)
* set for closing the file handle on a log file switch. The flags
* are reset when the slot is freed. See log_slot_free.
*/
+ slot->slot_unbuffered = 0;
slot->slot_start_lsn = slot->slot_end_lsn = log->alloc_lsn;
slot->slot_start_offset = log->alloc_lsn.l.offset;
slot->slot_last_offset = log->alloc_lsn.l.offset;
slot->slot_fh = log->log_fh;
slot->slot_error = 0;
- slot->slot_unbuffered = 0;
+ WT_DIAGNOSTIC_YIELD;
+ /*
+ * Set the slot state last. Other threads may have a stale pointer
+ * to this slot and could try to alter the state and other fields once
+ * they see the state cleared.
+ */
+ WT_PUBLISH(slot->slot_state, 0);
}
/*
@@ -50,6 +99,10 @@ __log_slot_close(
WT_CONNECTION_IMPL *conn;
WT_LOG *log;
int64_t end_offset, new_state, old_state;
+#ifdef HAVE_DIAGNOSTIC
+ struct timespec begin, now;
+ int count;
+#endif
WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_SLOT));
WT_ASSERT(session, releasep != NULL);
@@ -101,9 +154,33 @@ retry:
* that value. If the state is unbuffered, wait for the unbuffered
* size to be set.
*/
- while (WT_LOG_SLOT_UNBUFFERED_ISSET(old_state) &&
- slot->slot_unbuffered == 0)
- __wt_yield();
+#ifdef HAVE_DIAGNOSTIC
+ count = 0;
+ __wt_epoch(session, &begin);
+#endif
+ if (WT_LOG_SLOT_UNBUFFERED_ISSET(old_state)) {
+ while (slot->slot_unbuffered == 0) {
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
+ __wt_yield();
+#ifdef HAVE_DIAGNOSTIC
+ ++count;
+ if (count > WT_MILLION) {
+ __wt_epoch(session, &now);
+ if (WT_TIMEDIFF_SEC(now, begin) > 10) {
+ __wt_errx(session, "SLOT_CLOSE: Slot %"
+ PRIu32 " Timeout unbuffered, state 0x%"
+ PRIx64 " unbuffered %" PRIu64,
+ (uint32_t)(slot - &log->slot_pool[0]),
+ slot->slot_state,
+ slot->slot_unbuffered);
+ __log_slot_dump(session);
+ __wt_abort(session);
+ }
+ count = 0;
+ }
+#endif
+ }
+ }
end_offset =
WT_LOG_SLOT_JOINED_BUFFERED(old_state) + slot->slot_unbuffered;
@@ -118,17 +195,104 @@ retry:
}
/*
+ * __log_slot_new --
+ * Find a free slot and switch it as the new active slot.
+ * Must be called holding the slot lock.
+ */
+static int
+__log_slot_new(WT_SESSION_IMPL *session)
+{
+ WT_CONNECTION_IMPL *conn;
+ WT_LOG *log;
+ WT_LOGSLOT *slot;
+ int32_t i, pool_i;
+#ifdef HAVE_DIAGNOSTIC
+ struct timespec begin, now;
+ int count;
+#endif
+
+ WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_SLOT));
+ conn = S2C(session);
+ log = conn->log;
+ /*
+ * Although this function is single threaded, multiple threads could
+ * be trying to set a new active slot sequentially. If we find an
+ * active slot that is valid, return.
+ */
+ if ((slot = log->active_slot) != NULL &&
+ WT_LOG_SLOT_OPEN(slot->slot_state))
+ return (0);
+
+#ifdef HAVE_DIAGNOSTIC
+ count = 0;
+ __wt_epoch(session, &begin);
+#endif
+ /*
+ * Keep trying until we can find a free slot.
+ */
+ for (;;) {
+ /*
+ * Rotate among the slots to lessen collisions.
+ */
+ for (i = 0, pool_i = log->pool_index; i < WT_SLOT_POOL;
+ i++, pool_i++) {
+ if (pool_i >= WT_SLOT_POOL)
+ pool_i = 0;
+ slot = &log->slot_pool[pool_i];
+ if (slot->slot_state == WT_LOG_SLOT_FREE) {
+ /*
+ * Acquire our starting position in the
+ * log file. Assume the full buffer size.
+ */
+ WT_RET(__wt_log_acquire(session,
+ log->slot_buf_size, slot));
+ /*
+ * We have a new, initialized slot to use.
+ * Set it as the active slot.
+ */
+ WT_STAT_CONN_INCR(session,
+ log_slot_transitions);
+ log->active_slot = slot;
+ log->pool_index = pool_i;
+ return (0);
+ }
+ }
+ /*
+ * If we didn't find any free slots signal the worker thread.
+ */
+ WT_STAT_CONN_INCR(session, log_slot_no_free_slots);
+ __wt_cond_signal(session, conn->log_wrlsn_cond);
+ __wt_yield();
+#ifdef HAVE_DIAGNOSTIC
+ ++count;
+ if (count > WT_MILLION) {
+ __wt_epoch(session, &now);
+ if (WT_TIMEDIFF_SEC(now, begin) > 10) {
+ __wt_errx(session,
+ "SLOT_NEW: Timeout free slot");
+ __log_slot_dump(session);
+ __wt_abort(session);
+ }
+ count = 0;
+ }
+#endif
+ }
+ /* NOTREACHED */
+}
+
+/*
* __log_slot_switch_internal --
* Switch out the current slot and set up a new one.
*/
static int
__log_slot_switch_internal(
- WT_SESSION_IMPL *session, WT_MYSLOT *myslot, bool forced)
+ WT_SESSION_IMPL *session, WT_MYSLOT *myslot, bool forced, bool *did_work)
{
WT_DECL_RET;
WT_LOG *log;
WT_LOGSLOT *slot;
bool free_slot, release;
+ uint32_t joined;
log = S2C(session)->log;
release = false;
@@ -142,10 +306,23 @@ __log_slot_switch_internal(
*/
if (slot != log->active_slot)
return (0);
+ /*
+ * If the current active slot is unused and this is a forced switch,
+ * we're done. If this is a non-forced switch we always switch
+ * because the slot could be part of an unbuffered operation.
+ */
+ joined = WT_LOG_SLOT_JOINED(slot->slot_state);
+ if (joined == 0 && forced) {
+ WT_STAT_CONN_INCR(session, log_force_write_skip);
+ if (did_work != NULL)
+ *did_work = false;
+ return (0);
+ }
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
/*
- * We may come through here multiple times if we were able to close
- * a slot but could not set up a new one. If we closed it already,
+ * We may come through here multiple times if we were not able to
+ * set up a new one. If we closed it already,
* don't try to do it again but still set up the new slot.
*/
if (!F_ISSET(myslot, WT_MYSLOT_CLOSE)) {
@@ -157,20 +334,30 @@ __log_slot_switch_internal(
if (ret == WT_NOTFOUND)
return (0);
WT_RET(ret);
- if (release) {
- WT_RET(__wt_log_release(session, slot, &free_slot));
- if (free_slot)
- __wt_log_slot_free(session, slot);
- }
+ /*
+ * Set that we have closed this slot because we may call in here
+ * multiple times if we retry creating a new slot. Similarly
+ * set retain whether this slot needs releasing so that we don't
+ * lose that information if we retry.
+ */
+ F_SET(myslot, WT_MYSLOT_CLOSE);
+ if (release)
+ F_SET(myslot, WT_MYSLOT_NEEDS_RELEASE);
}
/*
- * Set that we have closed this slot because we may call in here
- * multiple times if we retry creating a new slot.
+ * Now that the slot is closed, set up a new one so that joining
+ * threads don't have to wait on writing the previous slot if we
+ * release it. Release after setting a new one.
*/
- F_SET(myslot, WT_MYSLOT_CLOSE);
- WT_RET(__wt_log_slot_new(session));
+ WT_RET(__log_slot_new(session));
F_CLR(myslot, WT_MYSLOT_CLOSE);
- return (0);
+ if (F_ISSET(myslot, WT_MYSLOT_NEEDS_RELEASE)) {
+ WT_RET(__wt_log_release(session, slot, &free_slot));
+ F_CLR(myslot, WT_MYSLOT_NEEDS_RELEASE);
+ if (free_slot)
+ __wt_log_slot_free(session, slot);
+ }
+ return (ret);
}
/*
@@ -178,13 +365,14 @@ __log_slot_switch_internal(
* Switch out the current slot and set up a new one.
*/
int
-__wt_log_slot_switch(
- WT_SESSION_IMPL *session, WT_MYSLOT *myslot, bool retry, bool forced)
+__wt_log_slot_switch(WT_SESSION_IMPL *session,
+ WT_MYSLOT *myslot, bool retry, bool forced, bool *did_work)
{
WT_DECL_RET;
WT_LOG *log;
log = S2C(session)->log;
+
/*
* !!! Since the WT_WITH_SLOT_LOCK macro is a do-while loop, the
* compiler does not like it combined directly with the while loop
@@ -198,7 +386,8 @@ __wt_log_slot_switch(
*/
do {
WT_WITH_SLOT_LOCK(session, log,
- ret = __log_slot_switch_internal(session, myslot, forced));
+ ret = __log_slot_switch_internal(
+ session, myslot, forced, did_work));
if (ret == EBUSY) {
WT_STAT_CONN_INCR(session, log_slot_switch_busy);
__wt_yield();
@@ -208,67 +397,6 @@ __wt_log_slot_switch(
}
/*
- * __wt_log_slot_new --
- * Find a free slot and switch it as the new active slot.
- * Must be called holding the slot lock.
- */
-int
-__wt_log_slot_new(WT_SESSION_IMPL *session)
-{
- WT_CONNECTION_IMPL *conn;
- WT_LOG *log;
- WT_LOGSLOT *slot;
- int32_t i;
-
- WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_SLOT));
- conn = S2C(session);
- log = conn->log;
- /*
- * Although this function is single threaded, multiple threads could
- * be trying to set a new active slot sequentially. If we find an
- * active slot that is valid, return.
- */
- if ((slot = log->active_slot) != NULL &&
- WT_LOG_SLOT_OPEN(slot->slot_state))
- return (0);
-
- /*
- * Keep trying until we can find a free slot.
- */
- for (;;) {
- /*
- * For now just restart at 0. We could use log->pool_index
- * if that is inefficient.
- */
- for (i = 0; i < WT_SLOT_POOL; i++) {
- slot = &log->slot_pool[i];
- if (slot->slot_state == WT_LOG_SLOT_FREE) {
- /*
- * Acquire our starting position in the
- * log file. Assume the full buffer size.
- */
- WT_RET(__wt_log_acquire(session,
- log->slot_buf_size, slot));
- /*
- * We have a new, initialized slot to use.
- * Set it as the active slot.
- */
- WT_STAT_CONN_INCR(session,
- log_slot_transitions);
- log->active_slot = slot;
- return (0);
- }
- }
- /*
- * If we didn't find any free slots signal the worker thread.
- */
- __wt_cond_auto_signal(session, conn->log_wrlsn_cond);
- __wt_yield();
- }
- /* NOTREACHED */
-}
-
-/*
* __wt_log_slot_init --
* Initialize the slot array.
*/
@@ -311,10 +439,13 @@ __wt_log_slot_init(WT_SESSION_IMPL *session)
/*
* We cannot initialize the release LSN in the activate function
* because that function can be called after a log file switch.
+ * The release LSN is usually the same as the slot_start_lsn except
+ * around a log file switch.
*/
slot->slot_release_lsn = log->alloc_lsn;
__wt_log_slot_activate(session, slot);
log->active_slot = slot;
+ log->pool_index = 0;
if (0) {
err: while (--i >= 0)
@@ -361,7 +492,7 @@ __wt_log_slot_destroy(WT_SESSION_IMPL *session)
* __wt_log_slot_join --
* Join a consolidated logging slot.
*/
-void
+int
__wt_log_slot_join(WT_SESSION_IMPL *session, uint64_t mysize,
uint32_t flags, WT_MYSLOT *myslot)
{
@@ -370,66 +501,76 @@ __wt_log_slot_join(WT_SESSION_IMPL *session, uint64_t mysize,
WT_LOGSLOT *slot;
int64_t flag_state, new_state, old_state, released;
int32_t join_offset, new_join;
-#ifdef HAVE_DIAGNOSTIC
- bool unbuf_force;
-#endif
+ bool unbuffered, yld;
conn = S2C(session);
log = conn->log;
WT_ASSERT(session, !F_ISSET(session, WT_SESSION_LOCKED_SLOT));
+ WT_ASSERT(session, mysize != 0);
/*
* There should almost always be a slot open.
*/
+ unbuffered = false;
#ifdef HAVE_DIAGNOSTIC
- unbuf_force = (++log->write_calls % WT_THOUSAND) == 0;
+ yld = (++log->write_calls % 7) == 0;
+ if ((log->write_calls % WT_THOUSAND) == 0 ||
+ mysize > WT_LOG_SLOT_BUF_MAX) {
+#else
+ yld = false;
+ if (mysize > WT_LOG_SLOT_BUF_MAX) {
#endif
+ unbuffered = true;
+ F_SET(myslot, WT_MYSLOT_UNBUFFERED);
+ }
for (;;) {
WT_BARRIER();
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
slot = log->active_slot;
old_state = slot->slot_state;
- /*
- * Try to join our size into the existing size and
- * atomically write it back into the state.
- */
- flag_state = WT_LOG_SLOT_FLAGS(old_state);
- released = WT_LOG_SLOT_RELEASED(old_state);
- join_offset = WT_LOG_SLOT_JOINED(old_state);
-#ifdef HAVE_DIAGNOSTIC
- if (unbuf_force || mysize > WT_LOG_SLOT_BUF_MAX) {
-#else
- if (mysize > WT_LOG_SLOT_BUF_MAX) {
-#endif
- new_join = join_offset + WT_LOG_SLOT_UNBUFFERED;
- F_SET(myslot, WT_MYSLOT_UNBUFFERED);
- myslot->slot = slot;
+ if (WT_LOG_SLOT_OPEN(old_state)) {
+ /*
+ * Try to join our size into the existing size and
+ * atomically write it back into the state.
+ */
+ flag_state = WT_LOG_SLOT_FLAGS(old_state);
+ released = WT_LOG_SLOT_RELEASED(old_state);
+ join_offset = WT_LOG_SLOT_JOINED(old_state);
+ if (unbuffered)
+ new_join = join_offset + WT_LOG_SLOT_UNBUFFERED;
+ else
+ new_join = join_offset + (int32_t)mysize;
+ new_state = (int64_t)WT_LOG_SLOT_JOIN_REL(
+ (int64_t)new_join, (int64_t)released,
+ (int64_t)flag_state);
+
+ /*
+ * Braces used due to potential empty body warning.
+ */
+ if (yld) {
+ WT_DIAGNOSTIC_YIELD;
+ }
+ /*
+ * Attempt to swap our size into the state.
+ */
+ if (__wt_atomic_casiv64(
+ &slot->slot_state, old_state, new_state))
+ break;
+ WT_STAT_CONN_INCR(session, log_slot_races);
} else
- new_join = join_offset + (int32_t)mysize;
- new_state = (int64_t)WT_LOG_SLOT_JOIN_REL(
- (int64_t)new_join, (int64_t)released, (int64_t)flag_state);
-
- /*
- * Check if the slot is open for joining and we are able to
- * swap in our size into the state.
- */
- if (WT_LOG_SLOT_OPEN(old_state) &&
- __wt_atomic_casiv64(
- &slot->slot_state, old_state, new_state))
- break;
+ WT_STAT_CONN_INCR(session, log_slot_active_closed);
/*
* The slot is no longer open or we lost the race to
* update it. Yield and try again.
*/
- WT_STAT_CONN_INCR(session, log_slot_races);
__wt_yield();
}
/*
* We joined this slot. Fill in our information to return to
* the caller.
*/
- if (mysize != 0)
- WT_STAT_CONN_INCR(session, log_slot_joins);
+ WT_STAT_CONN_INCR(session, log_slot_joins);
if (LF_ISSET(WT_LOG_DSYNC | WT_LOG_FSYNC))
F_SET(slot, WT_SLOT_SYNC_DIR);
if (LF_ISSET(WT_LOG_FLUSH))
@@ -444,6 +585,7 @@ __wt_log_slot_join(WT_SESSION_IMPL *session, uint64_t mysize,
myslot->slot = slot;
myslot->offset = join_offset;
myslot->end_offset = (wt_off_t)((uint64_t)join_offset + mysize);
+ return (0);
}
/*
@@ -459,7 +601,6 @@ __wt_log_slot_release(WT_SESSION_IMPL *session, WT_MYSLOT *myslot, int64_t size)
wt_off_t cur_offset, my_start;
int64_t my_size, rel_size;
- WT_UNUSED(session);
slot = myslot->slot;
my_start = slot->slot_start_offset + myslot->offset;
/*
@@ -468,6 +609,7 @@ __wt_log_slot_release(WT_SESSION_IMPL *session, WT_MYSLOT *myslot, int64_t size)
* was written rather than the beginning record of the slot.
*/
while ((cur_offset = slot->slot_last_offset) < my_start) {
+ WT_RET(WT_SESSION_CHECK_PANIC(session));
/*
* Set our offset if we are larger.
*/