[SCM] Samba Shared Repository - branch master updated
Rusty Russell
rusty at samba.org
Fri Aug 13 11:02:41 MDT 2010
The branch, master has been updated
via 11ab430... tdb: workaround starvation problem in locking entire database.
from f00b61c... s3-selftest: move make test to selftest.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 11ab43084b10cf53b530cdc3a6036c898b79ca38
Author: Rusty Russell <rusty at rustcorp.com.au>
Date: Sat Aug 14 02:13:26 2010 +0930
tdb: workaround starvation problem in locking entire database.
We saw tdb_lockall() take 71 seconds under heavy load; this is because Linux
(at least) doesn't prevent new small locks being obtained while we're waiting
for a big log.
The workaround is to do divide and conquer using non-blocking chainlocks: if
we get down to a single chain we block. Using a simple test program where
children did "hold lock for 100ms, sleep for 1 second" the time to do
tdb_lockall() dropped signifiantly. There are ln(hashsize) locks taken in
the contended case, but that's slow anyway.
More analysis is given in my blog at http://rusty.ozlabs.org/?p=120
This may also help transactions, though in that case it's the initial
read lock which uses this gradual locking routine; the update-to-write-lock
code is separate and still tries to update in one go.
Even though ABI doesn't change, minor version bumped so behavior change
can be easily detected.
Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>
-----------------------------------------------------------------------
Summary of changes:
lib/tdb/ABI/{tdb-1.2.2.sigs => tdb-1.2.3.sigs} | 0
lib/tdb/common/lock.c | 86 +++++++++++++++++++-----
lib/tdb/configure.ac | 2 +-
lib/tdb/wscript | 2 +-
4 files changed, 71 insertions(+), 19 deletions(-)
copy lib/tdb/ABI/{tdb-1.2.2.sigs => tdb-1.2.3.sigs} (100%)
Changeset truncated at 500 lines:
diff --git a/lib/tdb/ABI/tdb-1.2.2.sigs b/lib/tdb/ABI/tdb-1.2.3.sigs
similarity index 100%
copy from lib/tdb/ABI/tdb-1.2.2.sigs
copy to lib/tdb/ABI/tdb-1.2.3.sigs
diff --git a/lib/tdb/common/lock.c b/lib/tdb/common/lock.c
index 285b7a3..803feee 100644
--- a/lib/tdb/common/lock.c
+++ b/lib/tdb/common/lock.c
@@ -152,14 +152,6 @@ int tdb_brlock(struct tdb_context *tdb,
return -1;
}
- /* Sanity check */
- if (tdb->transaction && offset >= lock_offset(-1) && len != 0) {
- tdb->ecode = TDB_ERR_RDONLY;
- TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_brlock attempted in transaction at offset %d rw_type=%d flags=%d len=%d\n",
- offset, rw_type, flags, (int)len));
- return -1;
- }
-
do {
ret = fcntl_lock(tdb, rw_type, offset, len,
flags & TDB_LOCK_WAIT);
@@ -486,11 +478,9 @@ int tdb_transaction_unlock(struct tdb_context *tdb, int ltype)
return tdb_nest_unlock(tdb, TRANSACTION_LOCK, ltype, false);
}
-
-/* lock/unlock entire database. It can only be upgradable if you have some
- * other way of guaranteeing exclusivity (ie. transaction write lock). */
-int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
- enum tdb_lock_flags flags, bool upgradable)
+/* Returns 0 if all done, -1 if error, 1 if ok. */
+static int tdb_allrecord_check(struct tdb_context *tdb, int ltype,
+ enum tdb_lock_flags flags, bool upgradable)
{
/* There are no locks on read-only dbs */
if (tdb->read_only || tdb->traverse_read) {
@@ -520,11 +510,73 @@ int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
tdb->ecode = TDB_ERR_LOCK;
return -1;
}
+ return 1;
+}
- if (tdb_brlock(tdb, ltype, FREELIST_TOP, 0, flags)) {
- if (flags & TDB_LOCK_WAIT) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno)));
- }
+/* We only need to lock individual bytes, but Linux merges consecutive locks
+ * so we lock in contiguous ranges. */
+static int tdb_chainlock_gradual(struct tdb_context *tdb,
+ int ltype, enum tdb_lock_flags flags,
+ size_t off, size_t len)
+{
+ int ret;
+ enum tdb_lock_flags nb_flags = (flags & ~TDB_LOCK_WAIT);
+
+ if (len <= 4) {
+ /* Single record. Just do blocking lock. */
+ return tdb_brlock(tdb, ltype, off, len, flags);
+ }
+
+ /* First we try non-blocking. */
+ ret = tdb_brlock(tdb, ltype, off, len, nb_flags);
+ if (ret == 0) {
+ return 0;
+ }
+
+ /* Try locking first half, then second. */
+ ret = tdb_chainlock_gradual(tdb, ltype, flags, off, len / 2);
+ if (ret == -1)
+ return -1;
+
+ ret = tdb_chainlock_gradual(tdb, ltype, flags,
+ off + len / 2, len - len / 2);
+ if (ret == -1) {
+ tdb_brunlock(tdb, ltype, off, len / 2);
+ return -1;
+ }
+ return 0;
+}
+
+/* lock/unlock entire database. It can only be upgradable if you have some
+ * other way of guaranteeing exclusivity (ie. transaction write lock).
+ * We do the locking gradually to avoid being starved by smaller locks. */
+int tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
+ enum tdb_lock_flags flags, bool upgradable)
+{
+ switch (tdb_allrecord_check(tdb, ltype, flags, upgradable)) {
+ case -1:
+ return -1;
+ case 0:
+ return 0;
+ }
+
+ /* We cover two kinds of locks:
+ * 1) Normal chain locks. Taken for almost all operations.
+ * 3) Individual records locks. Taken after normal or free
+ * chain locks.
+ *
+ * It is (1) which cause the starvation problem, so we're only
+ * gradual for that. */
+ if (tdb_chainlock_gradual(tdb, ltype, flags, FREELIST_TOP,
+ tdb->header.hash_size * 4) == -1) {
+ return -1;
+ }
+
+ /* Grab individual record locks. */
+ if (tdb_brlock(tdb, ltype, lock_offset(tdb->header.hash_size), 0,
+ flags) == -1) {
+ tdb_brunlock(tdb, ltype, FREELIST_TOP,
+ tdb->header.hash_size * 4);
return -1;
}
diff --git a/lib/tdb/configure.ac b/lib/tdb/configure.ac
index 4e36779..2843d98 100644
--- a/lib/tdb/configure.ac
+++ b/lib/tdb/configure.ac
@@ -2,7 +2,7 @@ AC_PREREQ(2.50)
AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""])
AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""])
AC_DEFUN([SMB_ENABLE], [echo -n ""])
-AC_INIT(tdb, 1.2.2)
+AC_INIT(tdb, 1.2.3)
AC_CONFIG_SRCDIR([common/tdb.c])
AC_CONFIG_HEADER(include/config.h)
AC_LIBREPLACE_ALL_CHECKS
diff --git a/lib/tdb/wscript b/lib/tdb/wscript
index a7afa98..2fdd67f 100644
--- a/lib/tdb/wscript
+++ b/lib/tdb/wscript
@@ -1,7 +1,7 @@
#!/usr/bin/env python
APPNAME = 'tdb'
-VERSION = '1.2.2'
+VERSION = '1.2.3'
blddir = 'bin'
--
Samba Shared Repository
More information about the samba-cvs
mailing list