summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/lib/tdb/common/lock.c106
-rw-r--r--source4/lib/tdb/common/open.c12
-rw-r--r--source4/lib/tdb/common/tdb_private.h4
-rw-r--r--source4/lib/tdb/common/transaction.c10
4 files changed, 96 insertions, 36 deletions
diff --git a/source4/lib/tdb/common/lock.c b/source4/lib/tdb/common/lock.c
index a5bff2d0b3..8a964371d3 100644
--- a/source4/lib/tdb/common/lock.c
+++ b/source4/lib/tdb/common/lock.c
@@ -107,6 +107,9 @@ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len)
/* lock a list in the database. list -1 is the alloc list */
int tdb_lock(struct tdb_context *tdb, int list, int ltype)
{
+ struct tdb_lock_type *new_lck;
+ int i;
+
/* a global lock allows us to avoid per chain locks */
if (tdb->global_lock.count &&
(ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) {
@@ -125,18 +128,50 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
if (tdb->flags & TDB_NOLOCK)
return 0;
+ for (i=0; i<tdb->num_lockrecs; i++) {
+ if (tdb->lockrecs[i].list == list) {
+ if (tdb->lockrecs[i].count == 0) {
+ /*
+ * Can't happen, see tdb_unlock(). It should
+ * be an assert.
+ */
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: "
+ "lck->count == 0 for list %d", list));
+ }
+ /*
+ * Just increment the in-memory struct, posix locks
+ * don't stack.
+ */
+ tdb->lockrecs[i].count++;
+ return 0;
+ }
+ }
+
+ new_lck = (struct tdb_lock_type *)realloc(
+ tdb->lockrecs,
+ sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1));
+ if (new_lck == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ tdb->lockrecs = new_lck;
+
/* Since fcntl locks don't nest, we do a lock for the first one,
and simply bump the count for future ones */
- if (tdb->locked[list+1].count == 0) {
- if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0, 1)) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d ltype=%d (%s)\n",
- list, ltype, strerror(errno)));
- return -1;
- }
- tdb->locked[list+1].ltype = ltype;
- tdb->num_locks++;
+ if (tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW,
+ 0, 1)) {
+ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d "
+ "ltype=%d (%s)\n", list, ltype, strerror(errno)));
+ return -1;
}
- tdb->locked[list+1].count++;
+
+ tdb->num_locks++;
+
+ tdb->lockrecs[tdb->num_lockrecs].list = list;
+ tdb->lockrecs[tdb->num_lockrecs].count = 1;
+ tdb->lockrecs[tdb->num_lockrecs].ltype = ltype;
+ tdb->num_lockrecs += 1;
+
return 0;
}
@@ -146,6 +181,8 @@ int tdb_lock(struct tdb_context *tdb, int list, int ltype)
int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
{
int ret = -1;
+ int i;
+ struct tdb_lock_type *lck = NULL;
/* a global lock allows us to avoid per chain locks */
if (tdb->global_lock.count &&
@@ -166,19 +203,52 @@ int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
return ret;
}
- if (tdb->locked[list+1].count==0) {
+ for (i=0; i<tdb->num_lockrecs; i++) {
+ if (tdb->lockrecs[i].list == list) {
+ lck = &tdb->lockrecs[i];
+ break;
+ }
+ }
+
+ if ((lck == NULL) || (lck->count == 0)) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n"));
- return ret;
+ return -1;
+ }
+
+ if (lck->count > 1) {
+ lck->count--;
+ return 0;
}
- if (tdb->locked[list+1].count == 1) {
- /* Down to last nested lock: unlock underneath */
- ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0, 1);
- tdb->num_locks--;
- } else {
- ret = 0;
+ /*
+ * This lock has count==1 left, so we need to unlock it in the
+ * kernel. We don't bother with decrementing the in-memory array
+ * element, we're about to overwrite it with the last array element
+ * anyway.
+ */
+
+ ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK,
+ F_SETLKW, 0, 1);
+ tdb->num_locks--;
+
+ /*
+ * Shrink the array by overwriting the element just unlocked with the
+ * last array element.
+ */
+
+ if (tdb->num_lockrecs > 1) {
+ *lck = tdb->lockrecs[tdb->num_lockrecs-1];
+ }
+ tdb->num_lockrecs -= 1;
+
+ /*
+ * We don't bother with realloc when the array shrinks, but if we have
+ * a completely idle tdb we should get rid of the locked array.
+ */
+
+ if (tdb->num_lockrecs == 0) {
+ SAFE_FREE(tdb->lockrecs);
}
- tdb->locked[list+1].count--;
if (ret)
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n"));
diff --git a/source4/lib/tdb/common/open.c b/source4/lib/tdb/common/open.c
index e1f21aa856..3d6e222b96 100644
--- a/source4/lib/tdb/common/open.c
+++ b/source4/lib/tdb/common/open.c
@@ -263,15 +263,6 @@ 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->locked = (struct tdb_lock_type *)calloc(tdb->header.hash_size+1,
- sizeof(tdb->locked[0]));
- if (!tdb->locked) {
- TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
- "failed to allocate lock structure for %s\n",
- name));
- errno = ENOMEM;
- goto fail;
- }
tdb_mmap(tdb);
if (locked) {
if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) {
@@ -324,7 +315,6 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
if (tdb->fd != -1)
if (close(tdb->fd) != 0)
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n"));
- SAFE_FREE(tdb->locked);
SAFE_FREE(tdb);
errno = save_errno;
return NULL;
@@ -354,7 +344,7 @@ int tdb_close(struct tdb_context *tdb)
SAFE_FREE(tdb->name);
if (tdb->fd != -1)
ret = close(tdb->fd);
- SAFE_FREE(tdb->locked);
+ SAFE_FREE(tdb->lockrecs);
/* Remove from contexts list */
for (i = &tdbs; *i; i = &(*i)->next) {
diff --git a/source4/lib/tdb/common/tdb_private.h b/source4/lib/tdb/common/tdb_private.h
index a3495e42aa..5277857942 100644
--- a/source4/lib/tdb/common/tdb_private.h
+++ b/source4/lib/tdb/common/tdb_private.h
@@ -123,6 +123,7 @@ struct tdb_header {
};
struct tdb_lock_type {
+ int list;
u32 count;
u32 ltype;
};
@@ -152,7 +153,8 @@ struct tdb_context {
int read_only; /* opened read-only */
int traverse_read; /* read-only traversal */
struct tdb_lock_type global_lock;
- struct tdb_lock_type *locked; /* array of chain locks */
+ int num_lockrecs;
+ struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */
enum TDB_ERROR ecode; /* error code for last tdb error */
struct tdb_header header; /* a cached copy of the header */
u32 flags; /* the flags passed to tdb_open */
diff --git a/source4/lib/tdb/common/transaction.c b/source4/lib/tdb/common/transaction.c
index 7dff9a95e3..5c94eb0afe 100644
--- a/source4/lib/tdb/common/transaction.c
+++ b/source4/lib/tdb/common/transaction.c
@@ -516,12 +516,10 @@ int tdb_transaction_cancel(struct tdb_context *tdb)
/* remove any locks created during the transaction */
if (tdb->num_locks != 0) {
- int h;
- for (h=0;h<tdb->header.hash_size+1;h++) {
- if (tdb->locked[h].count != 0) {
- tdb_brlock(tdb,FREELIST_TOP+4*h,F_UNLCK,F_SETLKW, 0, 1);
- tdb->locked[h].count = 0;
- }
+ int i;
+ for (i=0;i<tdb->num_lockrecs;i++) {
+ tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list,
+ F_UNLCK,F_SETLKW, 0, 1);
}
tdb->num_locks = 0;
}