diff --git a/source/tdb/common/lock.c b/source/tdb/common/lock.c index a5bff2d0b31..8a964371d3a 100644 --- a/source/tdb/common/lock.c +++ b/source/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; inum_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) { - TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); - return ret; + for (i=0; inum_lockrecs; i++) { + if (tdb->lockrecs[i].list == list) { + lck = &tdb->lockrecs[i]; + break; + } } - 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; + if ((lck == NULL) || (lck->count == 0)) { + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); + return -1; + } + + if (lck->count > 1) { + lck->count--; + return 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/source/tdb/common/open.c b/source/tdb/common/open.c index 224103e679f..3f0c35331b2 100644 --- a/source/tdb/common/open.c +++ b/source/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/source/tdb/common/tdb_private.h b/source/tdb/common/tdb_private.h index 1b45481f97b..56f609fb82c 100644 --- a/source/tdb/common/tdb_private.h +++ b/source/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/source/tdb/common/transaction.c b/source/tdb/common/transaction.c index 9f56fb2fd86..24aa5c32234 100644 --- a/source/tdb/common/transaction.c +++ b/source/tdb/common/transaction.c @@ -520,12 +520,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;hheader.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;inum_lockrecs;i++) { + tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list, + F_UNLCK,F_SETLKW, 0, 1); } tdb->num_locks = 0; }