[SCM] Samba Shared Repository - branch v3-2-test updated - initial-v3-2-unstable-1280-gc6609c0

Jeremy Allison jra at samba.org
Fri Jan 11 23:11:39 GMT 2008


The branch, v3-2-test has been updated
       via  c6609c042b128e7d63eb64cfdfb0f6b65cb59d76 (commit)
      from  75acdb54a454ffda9d422fcafb573c8f5581d2e8 (commit)

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v3-2-test


- Log -----------------------------------------------------------------
commit c6609c042b128e7d63eb64cfdfb0f6b65cb59d76
Author: Jeremy Allison <jra at samba.org>
Date:   Fri Jan 11 15:08:37 2008 -0800

    Sync tdb with the tdb changes in ctdb. Spoke to tridge about
    this. Fixes insidious problem with order n^2 freelist merging.
    Jeremy.

-----------------------------------------------------------------------

Summary of changes:
 source/lib/tdb/common/freelist.c    |  101 ++++++++---
 source/lib/tdb/common/io.c          |   20 +-
 source/lib/tdb/common/lock.c        |    7 +
 source/lib/tdb/common/open.c        |   16 ++-
 source/lib/tdb/common/tdb.c         |   72 +++++++-
 source/lib/tdb/common/tdb_private.h |    8 +-
 source/lib/tdb/common/transaction.c |  343 +++++++++++++++++------------------
 source/lib/tdb/common/traverse.c    |    3 +
 source/lib/tdb/configure.ac         |    2 +-
 source/lib/tdb/include/tdb.h        |    4 +
 10 files changed, 358 insertions(+), 218 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source/lib/tdb/common/freelist.c b/source/lib/tdb/common/freelist.c
index b109643..c086c15 100644
--- a/source/lib/tdb/common/freelist.c
+++ b/source/lib/tdb/common/freelist.c
@@ -27,6 +27,12 @@
 
 #include "tdb_private.h"
 
+/* 'right' merges can involve O(n^2) cost when combined with a
+   traverse, so they are disabled until we find a way to do them in 
+   O(1) time
+*/
+#define USE_RIGHT_MERGES 0
+
 /* read a freelist record and check for simple errors */
 int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec)
 {
@@ -56,7 +62,7 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct
 }
 
 
-
+#if USE_RIGHT_MERGES
 /* Remove an element from the freelist.  Must have alloc lock. */
 static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next)
 {
@@ -75,6 +81,7 @@ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_
 	TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off));
 	return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
 }
+#endif
 
 
 /* update a record tailer (must hold allocation lock) */
@@ -93,8 +100,6 @@ static int update_tailer(struct tdb_context *tdb, tdb_off_t offset,
    neccessary. */
 int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
 {
-	tdb_off_t right, left;
-
 	/* Allocation and tailer lock */
 	if (tdb_lock(tdb, -1, F_WRLCK) != 0)
 		return -1;
@@ -105,9 +110,10 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
 		goto fail;
 	}
 
+#if USE_RIGHT_MERGES
 	/* Look right first (I'm an Australian, dammit) */
-	right = offset + sizeof(*rec) + rec->rec_len;
-	if (right + sizeof(*rec) <= tdb->map_size) {
+	if (offset + sizeof(*rec) + rec->rec_len + sizeof(*rec) <= tdb->map_size) {
+		tdb_off_t right = offset + sizeof(*rec) + rec->rec_len;
 		struct list_struct r;
 
 		if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
@@ -122,13 +128,18 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
 				goto left;
 			}
 			rec->rec_len += sizeof(r) + r.rec_len;
+			if (update_tailer(tdb, offset, rec) == -1) {
+				TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset));
+				goto fail;
+			}
 		}
 	}
-
 left:
+#endif
+
 	/* Look left */
-	left = offset - sizeof(tdb_off_t);
-	if (left > TDB_DATA_START(tdb->header.hash_size)) {
+	if (offset - sizeof(tdb_off_t) > TDB_DATA_START(tdb->header.hash_size)) {
+		tdb_off_t left = offset - sizeof(tdb_off_t);
 		struct list_struct l;
 		tdb_off_t leftsize;
 		
@@ -145,7 +156,12 @@ left:
 
 		left = offset - leftsize;
 
-		/* Now read in record */
+		if (leftsize > offset ||
+		    left < TDB_DATA_START(tdb->header.hash_size)) {
+			goto update;
+		}
+
+		/* Now read in the left record */
 		if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
 			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
 			goto update;
@@ -153,21 +169,24 @@ left:
 
 		/* If it's free, expand to include it. */
 		if (l.magic == TDB_FREE_MAGIC) {
-			if (remove_from_freelist(tdb, left, l.next) == -1) {
-				TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left));
-				goto update;
-			} else {
-				offset = left;
-				rec->rec_len += leftsize;
+			/* we now merge the new record into the left record, rather than the other 
+			   way around. This makes the operation O(1) instead of O(n). This change
+			   prevents traverse from being O(n^2) after a lot of deletes */
+			l.rec_len += sizeof(*rec) + rec->rec_len;
+			if (tdb_rec_write(tdb, left, &l) == -1) {
+				TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_left failed at %u\n", left));
+				goto fail;
 			}
+			if (update_tailer(tdb, left, &l) == -1) {
+				TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset));
+				goto fail;
+			}
+			tdb_unlock(tdb, -1, F_WRLCK);
+			return 0;
 		}
 	}
 
 update:
-	if (update_tailer(tdb, offset, rec) == -1) {
-		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset));
-		goto fail;
-	}
 
 	/* Now, prepend to free list */
 	rec->magic = TDB_FREE_MAGIC;
@@ -261,6 +280,7 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st
 		tdb_off_t rec_ptr, last_ptr;
 		tdb_len_t rec_len;
 	} bestfit;
+	float multiplier = 1.0;
 
 	if (tdb_lock(tdb, -1, F_WRLCK) == -1)
 		return 0;
@@ -295,18 +315,27 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st
 				bestfit.rec_len = rec->rec_len;
 				bestfit.rec_ptr = rec_ptr;
 				bestfit.last_ptr = last_ptr;
-				/* consider a fit to be good enough if
-				   we aren't wasting more than half
-				   the space */
-				if (bestfit.rec_len < 2*length) {
-					break;
-				}
 			}
 		}
 
 		/* move to the next record */
 		last_ptr = rec_ptr;
 		rec_ptr = rec->next;
+
+		/* if we've found a record that is big enough, then
+		   stop searching if its also not too big. The
+		   definition of 'too big' changes as we scan
+		   through */
+		if (bestfit.rec_len > 0 &&
+		    bestfit.rec_len < length * multiplier) {
+			break;
+		}
+		
+		/* this multiplier means we only extremely rarely
+		   search more than 50 or so records. At 50 records we
+		   accept records up to 11 times larger than what we
+		   want */
+		multiplier *= 1.05;
 	}
 
 	if (bestfit.rec_ptr != 0) {
@@ -328,3 +357,25 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st
 	return 0;
 }
 
+
+
+/* 
+   return the size of the freelist - used to decide if we should repack 
+*/
+int tdb_freelist_size(struct tdb_context *tdb)
+{
+	tdb_off_t ptr;
+	int count=0;
+
+	if (tdb_lock(tdb, -1, F_RDLCK) == -1) {
+		return -1;
+	}
+
+	ptr = FREELIST_TOP;
+	while (tdb_ofs_read(tdb, ptr, &ptr) == 0 && ptr != 0) {
+		count++;
+	}
+
+	tdb_unlock(tdb, -1, F_RDLCK);
+	return count;
+}
diff --git a/source/lib/tdb/common/io.c b/source/lib/tdb/common/io.c
index 8ab0768..172ab69 100644
--- a/source/lib/tdb/common/io.c
+++ b/source/lib/tdb/common/io.c
@@ -101,8 +101,8 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
 					 off+written);
 		}
 		if (written == -1) {
-			/* Ensure ecode is set for log fn. */
-			tdb->ecode = TDB_ERR_IO;
+		/* Ensure ecode is set for log fn. */
+		tdb->ecode = TDB_ERR_IO;
 			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d "
 				 "len=%d (%s)\n", off, len, strerror(errno)));
 			return TDB_ERRCODE(TDB_ERR_IO, -1);
@@ -111,8 +111,8 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
 				 "write %d bytes at %d in two attempts\n",
 				 len, off));
 			errno = ENOSPC;
-			return TDB_ERRCODE(TDB_ERR_IO, -1);
-		}
+		return TDB_ERRCODE(TDB_ERR_IO, -1);
+	}
 	}
 	return 0;
 }
@@ -230,7 +230,7 @@ void tdb_mmap(struct tdb_context *tdb)
   says to use for mmap expansion */
 static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
 {
-	char buf[1024];
+	char buf[8192];
 
 	if (tdb->read_only || tdb->traverse_read) {
 		tdb->ecode = TDB_ERR_RDONLY;
@@ -294,7 +294,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad
 int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
 {
 	struct list_struct rec;
-	tdb_off_t offset;
+	tdb_off_t offset, new_size;	
 
 	if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
 		TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
@@ -304,9 +304,11 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
 	/* must know about any previous expansions by another process */
 	tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
 
-	/* always make room for at least 10 more records, and round
-           the database up to a multiple of the page size */
-	size = TDB_ALIGN(tdb->map_size + size*10, tdb->page_size) - tdb->map_size;
+	/* always make room for at least 100 more records, and at
+           least 25% more space. Round the database up to a multiple
+           of the page size */
+	new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25);
+	size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size;
 
 	if (!(tdb->flags & TDB_INTERNAL))
 		tdb_munmap(tdb);
diff --git a/source/lib/tdb/common/lock.c b/source/lib/tdb/common/lock.c
index e3fe888..f156c0f 100644
--- a/source/lib/tdb/common/lock.c
+++ b/source/lib/tdb/common/lock.c
@@ -505,6 +505,9 @@ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
 /* record lock stops delete underneath */
 int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
 {
+	if (tdb->global_lock.count) {
+		return 0;
+	}
 	return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0;
 }
 
@@ -537,6 +540,10 @@ int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off)
 	struct tdb_traverse_lock *i;
 	uint32_t count = 0;
 
+	if (tdb->global_lock.count) {
+		return 0;
+	}
+
 	if (off == 0)
 		return 0;
 	for (i = &tdb->travlocks; i; i = i->next)
diff --git a/source/lib/tdb/common/open.c b/source/lib/tdb/common/open.c
index 6efa482..6bd8fda 100644
--- a/source/lib/tdb/common/open.c
+++ b/source/lib/tdb/common/open.c
@@ -35,7 +35,7 @@ static struct tdb_context *tdbs = NULL;
 static unsigned int default_tdb_hash(TDB_DATA *key)
 {
 	uint32_t value;	/* Used to compute the hash value.  */
-	uint32_t i;	/* Used to cycle through random values. */
+	uint32_t   i;	/* Used to cycle through random values. */
 
 	/* Set the initial value from the key size. */
 	for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++)
@@ -90,7 +90,7 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size)
 		size -= written;
 		written = write(tdb->fd, newdb+written, size);
 		if (written == size) {
-			ret = 0;
+		ret = 0;
 		} else if (written >= 0) {
 			/* a second incomplete write - we give up.
 			 * guessing the errno... */
@@ -152,6 +152,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 	int rev = 0, locked = 0;
 	unsigned char *vp;
 	uint32_t vertest;
+	unsigned v;
 
 	if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) {
 		/* Can't log this */
@@ -178,7 +179,9 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 		tdb->page_size = 0x2000;
 	}
 
-	tdb->max_dead_records = (open_flags & TDB_VOLATILE) ? 5 : 0;
+	if (open_flags & TDB_VOLATILE) {
+		tdb->max_dead_records = 5;
+	}
 
 	if ((open_flags & O_ACCMODE) == O_WRONLY) {
 		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n",
@@ -213,6 +216,10 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 		goto fail;	/* errno set by open(2) */
 	}
 
+	/* on exec, don't inherit the fd */
+	v = fcntl(tdb->fd, F_GETFD, 0);
+        fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC);
+
 	/* ensure there is only one process initialising at once */
 	if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
 		TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n",
@@ -240,7 +247,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 		/* its not a valid database - possibly initialise it */
 		if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) {
 			if (errno == 0) {
-				errno = EIO; /* ie bad format or something */
+			errno = EIO; /* ie bad format or something */
 			}
 			goto fail;
 		}
@@ -281,6 +288,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
 	tdb->map_size = st.st_size;
 	tdb->device = st.st_dev;
 	tdb->inode = st.st_ino;
+	tdb->max_dead_records = 0;
 	tdb_mmap(tdb);
 	if (locked) {
 		if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {
diff --git a/source/lib/tdb/common/tdb.c b/source/lib/tdb/common/tdb.c
index 0e9d1db..bf3abb7 100644
--- a/source/lib/tdb/common/tdb.c
+++ b/source/lib/tdb/common/tdb.c
@@ -102,8 +102,7 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash,
 }
 
 /* As tdb_find, but if you succeed, keep the lock */
-tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, 
-							 uint32_t hash, int locktype,
+tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype,
 			   struct list_struct *rec)
 {
 	uint32_t rec_ptr;
@@ -237,14 +236,15 @@ int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
 }
 
 /* actually delete an entry in the database given the offset */
-int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct*rec)
+int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec)
 {
 	tdb_off_t last_ptr, i;
 	struct list_struct lastrec;
 
 	if (tdb->read_only || tdb->traverse_read) return -1;
 
-	if (tdb_write_lock_record(tdb, rec_ptr) == -1) {
+	if (tdb->traverse_write != 0 || 
+	    tdb_write_lock_record(tdb, rec_ptr) == -1) {
 		/* Someone traversing here: mark it as dead */
 		rec->magic = TDB_DEAD_MAGIC;
 		return tdb_rec_write(tdb, rec_ptr, rec);
@@ -666,6 +666,16 @@ int tdb_get_flags(struct tdb_context *tdb)
 	return tdb->flags;
 }
 
+void tdb_add_flags(struct tdb_context *tdb, unsigned flags)
+{
+	tdb->flags |= flags;
+}
+
+void tdb_remove_flags(struct tdb_context *tdb, unsigned flags)
+{
+	tdb->flags &= ~flags;
+}
+
 
 /*
   enable sequence number handling on an open tdb
@@ -674,3 +684,57 @@ void tdb_enable_seqnum(struct tdb_context *tdb)
 {
 	tdb->flags |= TDB_SEQNUM;
 }
+
+
+/*
+  wipe the entire database, deleting all records. This can be done
+  very fast by using a global lock. The entire data portion of the
+  file becomes a single entry in the freelist.
+ */
+int tdb_wipe_all(struct tdb_context *tdb)
+{
+	int i;
+	tdb_off_t offset = 0;
+	ssize_t data_len;
+
+	if (tdb_lockall(tdb) != 0) {
+		return -1;
+	}
+
+	/* wipe the hashes */
+	for (i=0;i<tdb->header.hash_size;i++) {
+		if (tdb_ofs_write(tdb, TDB_HASH_TOP(i), &offset) == -1) {
+			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write hash %d\n", i));
+			goto failed;
+		}
+	}
+
+	/* wipe the freelist */
+	if (tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
+		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write freelist\n"));
+		goto failed;
+	}
+
+	/* add all the rest of the file to the freelist */
+	data_len = (tdb->map_size - TDB_DATA_START(tdb->header.hash_size)) - sizeof(struct list_struct);
+	if (data_len > 0) {
+		struct list_struct rec;
+		memset(&rec,'\0',sizeof(rec));
+		rec.rec_len = data_len;
+		if (tdb_free(tdb, TDB_DATA_START(tdb->header.hash_size), &rec) == -1) {
+			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to add free record\n"));
+			goto failed;
+		}
+	}
+
+	if (tdb_unlockall(tdb) != 0) {
+		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n"));
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	tdb_unlockall(tdb);
+	return -1;
+}
diff --git a/source/lib/tdb/common/tdb_private.h b/source/lib/tdb/common/tdb_private.h
index 58c30c1..63a6d04 100644
--- a/source/lib/tdb/common/tdb_private.h
+++ b/source/lib/tdb/common/tdb_private.h
@@ -38,6 +38,10 @@
 typedef uint32_t tdb_len_t;
 typedef uint32_t tdb_off_t;
 
+#ifndef offsetof
+#define offsetof(t,f) ((unsigned int)&((t *)0)->f)
+#endif
+
 #define TDB_MAGIC_FOOD "TDB file\n"
 #define TDB_VERSION (0x26011967 + 6)
 #define TDB_MAGIC (0x26011999U)
@@ -54,7 +58,7 @@ typedef uint32_t tdb_off_t;
 #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
 #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t))
 #define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t))
-#define TDB_DATA_START(hash_size) TDB_HASH_TOP(hash_size-1)
+#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + sizeof(tdb_off_t))
 #define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start)
 #define TDB_SEQNUM_OFS    offsetof(struct tdb_header, sequence_number)
 #define TDB_PAD_BYTE 0x42
@@ -144,6 +148,7 @@ struct tdb_context {
 	tdb_len_t map_size; /* how much space has been mapped */
 	int read_only; /* opened read-only */
 	int traverse_read; /* read-only traversal */
+	int traverse_write; /* read-write traversal */
 	struct tdb_lock_type global_lock;
 	int num_lockrecs;
 	struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */
@@ -173,7 +178,6 @@ struct tdb_context {
 int tdb_munmap(struct tdb_context *tdb);
 void tdb_mmap(struct tdb_context *tdb);
 int tdb_lock(struct tdb_context *tdb, int list, int ltype);
-int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype);
 int tdb_unlock(struct tdb_context *tdb, int list, int ltype);
 int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len);
 int tdb_transaction_lock(struct tdb_context *tdb, int ltype);
diff --git a/source/lib/tdb/common/transaction.c b/source/lib/tdb/common/transaction.c
index 7eaacf7..2da09fa 100644
--- a/source/lib/tdb/common/transaction.c
+++ b/source/lib/tdb/common/transaction.c
@@ -87,13 +87,6 @@


-- 
Samba Shared Repository


More information about the samba-cvs mailing list