[PATCH] lib/tdb: new locking hook.
Rusty Russell
rusty at rustcorp.com.au
Thu Aug 27 20:29:12 MDT 2009
This obsoletes the *mark and *nonblock calls, by allowing the user
to do fancy work replacing, recording or faking fcntl locks. This
should make ctdb's life easier. Clumsily, this also fixes a shadow
warning (wait) introduced by previous patch.
(Note tdb_increment_seqnum_nonblock is not obsoleted, since despite
the name it simply operates lockless).
We could avoid adding YA open function if we didn't want to allow
non-blocking open, or complete tracking of all lock operations.
Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>
---
lib/tdb/common/lock.c | 43 +++++++++++++++++++++++++++++++++++++----
lib/tdb/common/open.c | 28 ++++++++++++++++++++++++++-
lib/tdb/common/tdb_private.h | 2 +
lib/tdb/docs/README | 32 +++++++++++++++++++++++++++++++
lib/tdb/include/tdb.h | 24 +++++++++++++++++++++++
5 files changed, 123 insertions(+), 6 deletions(-)
diff --git a/lib/tdb/common/lock.c b/lib/tdb/common/lock.c
index 493012f..86b7f3b 100644
--- a/lib/tdb/common/lock.c
+++ b/lib/tdb/common/lock.c
@@ -33,7 +33,7 @@ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *ptr)
}
static int fcntl_lock(struct tdb_context *tdb,
- int rw, off_t off, off_t len, bool wait)
+ int rw, off_t off, off_t len, bool lwait)
{
struct flock fl;
@@ -43,7 +43,7 @@ static int fcntl_lock(struct tdb_context *tdb,
fl.l_len = len;
fl.l_pid = 0;
- if (wait)
+ if (lwait)
return fcntl(tdb->fd, F_SETLKW, &fl);
else
return fcntl(tdb->fd, F_SETLK, &fl);
@@ -119,6 +119,39 @@ static int fcntl_unlock(struct tdb_context *tdb, int rw, off_t off, off_t len)
return fcntl(tdb->fd, F_SETLKW, &fl);
}
+static void *noop_init_locks(struct tdb_context *tdb)
+{
+ /* any non-NULL pointer will work; we don't use it anyway. */
+ return tdb;
+}
+
+static void noop_destroy_locks(struct tdb_context *tdb)
+{
+}
+
+const struct tdb_lock_methods tdb_default_lock_methods = {
+ .init_locks = noop_init_locks,
+ .destroy_locks = noop_destroy_locks,
+ .lock = fcntl_lock,
+ .unlock = fcntl_unlock,
+};
+
+const struct tdb_lock_methods *tdb_get_lock_methods(struct tdb_context *tdb,
+ void **lock_data)
+{
+ if (lock_data)
+ *lock_data = tdb->lock_data;
+ return tdb->lock_methods;
+}
+
+void tdb_set_lock_methods(struct tdb_context *tdb,
+ const struct tdb_lock_methods *lock_methods,
+ void *lock_data)
+{
+ tdb->lock_methods = lock_methods;
+ tdb->lock_data = lock_data;
+}
+
/* a byte range locking function - return 0 on success
this functions locks/unlocks 1 byte at the specified offset.
@@ -147,8 +180,8 @@ int tdb_brlock(struct tdb_context *tdb,
}
do {
- ret = fcntl_lock(tdb, rw_type, offset, len,
- flags & TDB_LOCK_WAIT);
+ ret = tdb->lock_methods->lock(tdb, rw_type, offset, len,
+ flags & TDB_LOCK_WAIT);
/* Check for a sigalarm break. */
if (ret == -1 && errno == EINTR &&
tdb->interrupt_sig_ptr &&
@@ -181,7 +214,7 @@ int tdb_brunlock(struct tdb_context *tdb,
}
do {
- ret = fcntl_unlock(tdb, rw_type, offset, len);
+ ret = tdb->lock_methods->unlock(tdb, rw_type, offset, len);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
diff --git a/lib/tdb/common/open.c b/lib/tdb/common/open.c
index 76c67aa..b5e7de0 100644
--- a/lib/tdb/common/open.c
+++ b/lib/tdb/common/open.c
@@ -134,7 +134,7 @@ static int tdb_already_open(dev_t device,
struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode)
{
- return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL);
+ return tdb_open_exl(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL, &tdb_default_lock_methods);
}
/* a default logging function */
@@ -149,6 +149,16 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
const struct tdb_logging_context *log_ctx,
tdb_hash_func hash_fn)
{
+ return tdb_open_exl(name, hash_size, tdb_flags, open_flags, mode,
+ log_ctx, hash_fn, &tdb_default_lock_methods);
+}
+
+struct tdb_context *tdb_open_exl(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ const struct tdb_logging_context *log_ctx,
+ tdb_hash_func hash_fn,
+ const struct tdb_lock_methods *lock_methods)
+{
struct tdb_context *tdb;
struct stat st;
int rev = 0, locked = 0;
@@ -224,6 +234,12 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
v = fcntl(tdb->fd, F_GETFD, 0);
fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC);
+ /* initialize locking. */
+ tdb->lock_methods = lock_methods;
+ tdb->lock_data = lock_methods->init_locks(tdb);
+ if (!tdb->lock_data)
+ goto fail;
+
/* ensure there is only one process initialising at once */
if (tdb->methods->brlock(tdb, F_WRLCK, GLOBAL_LOCK, 1, TDB_LOCK_WAIT) == -1) {
TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n",
@@ -355,6 +371,9 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
#ifdef TDB_TRACE
close(tdb->tracefd);
#endif
+ if (tdb->lock_data)
+ tdb->lock_methods->destroy_locks(tdb);
+
if (tdb->map_ptr) {
if (tdb->flags & TDB_INTERNAL)
SAFE_FREE(tdb->map_ptr);
@@ -481,6 +500,13 @@ static int tdb_reopen_internal(struct tdb_context *tdb, bool active_lock)
tdb_mmap(tdb);
#endif /* fake pread or pwrite */
+ tdb->lock_methods->destroy_locks(tdb);
+ tdb->lock_data = tdb->lock_methods->init_locks(tdb);
+ if (!tdb->lock_data) {
+ TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to re-initialize locks\n"));
+ goto fail;
+ }
+
if (active_lock &&
(tdb->methods->brlock(tdb, F_RDLCK, ACTIVE_LOCK, 1, TDB_LOCK_WAIT) == -1)) {
TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n"));
diff --git a/lib/tdb/common/tdb_private.h b/lib/tdb/common/tdb_private.h
index 28b88af..fdedf0b 100644
--- a/lib/tdb/common/tdb_private.h
+++ b/lib/tdb/common/tdb_private.h
@@ -198,6 +198,8 @@ struct tdb_context {
uint32_t flags; /* the flags passed to tdb_open */
struct tdb_traverse_lock travlocks; /* current traversal locks */
struct tdb_context *next; /* all tdbs to avoid multiple opens */
+ const struct tdb_lock_methods *lock_methods;
+ void *lock_data;
dev_t device; /* uniquely identifies this tdb */
ino_t inode; /* uniquely identifies this tdb */
struct tdb_logging_context log;
diff --git a/lib/tdb/docs/README b/lib/tdb/docs/README
index 454e4ba..6fcdf8f 100644
--- a/lib/tdb/docs/README
+++ b/lib/tdb/docs/README
@@ -81,6 +81,38 @@ hash function. Be careful when passing a hash function - all users of
the database must use the same hash function or you will get data
corruption.
+----------------------------------------------------------------------
+TDB_CONTEXT *tdb_open_exl(char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ tdb_log_func log_fn,
+ tdb_hash_func hash_fn,
+ const struct tdb_lock_methods *lock_methods)
+
+This is like tdb_open_ex(), but includes a set of locking functions
+which can be used to enhance or override the defaults. This has several
+methods:
+
+ void *(*init_locks)(struct tdb_context *tdb);
+
+This is called from tdb_open_exl, and also from tdb_reopen/tdb_reopen_all.
+It returns a non-NULL pointer on success, which can be accessed through
+tdb_get_lock_methods().
+
+ void (*destroy_locks)(struct tdb_context *tdb);
+
+This is called from tdb_close, and also tdb_reopen/tdb_reopen_all.
+
+ int (*lock)(struct tdb_context *tdb, int rw, off_t off, off_t len,
+ bool wait);
+ int (*unlock)(struct tdb_context *tdb, int rw, off_t off, off_t len);
+
+These two functions are expected to place a fcntl-style lock on the tdb_fd().
+rw is either F_RDLCK or F_WRLCK, off is the offset from the start of the file,
+and len is the length: a len of 0 means "to infinity". They return 0 on
+success, -1 on error. The (successful) calls to lock and unlock should be
+symmetrical, but a robust implementation should detect locking errors.
+
+The default tdb_lock_methods can be found in "tdb_default_lock_methods".
----------------------------------------------------------------------
char *tdb_error(TDB_CONTEXT *tdb);
diff --git a/lib/tdb/include/tdb.h b/lib/tdb/include/tdb.h
index c00b6cb..bcc0b0d 100644
--- a/lib/tdb/include/tdb.h
+++ b/lib/tdb/include/tdb.h
@@ -87,12 +87,31 @@ struct tdb_logging_context {
void *log_private;
};
+struct tdb_lock_methods {
+ /* Called from tdb_open* and tdb_close */
+ void *(*init_locks)(struct tdb_context *tdb);
+ void (*destroy_locks)(struct tdb_context *tdb);
+
+ /* Grab a lock on the file, fcntl-style. */
+ int (*lock)(struct tdb_context *tdb, int rw, off_t off, off_t len,
+ bool wait);
+ int (*unlock)(struct tdb_context *tdb, int rw, off_t off, off_t len);
+};
+
+/* The default locking methods. */
+extern const struct tdb_lock_methods tdb_default_lock_methods;
+
struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode);
struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags,
int open_flags, mode_t mode,
const struct tdb_logging_context *log_ctx,
tdb_hash_func hash_fn);
+struct tdb_context *tdb_open_exl(const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ const struct tdb_logging_context *log_ctx,
+ tdb_hash_func hash_fn,
+ const struct tdb_lock_methods *lock_methods);
void tdb_set_max_dead(struct tdb_context *tdb, int max_dead);
int tdb_reopen(struct tdb_context *tdb);
@@ -148,6 +167,11 @@ int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key);
int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key);
+const struct tdb_lock_methods *tdb_get_lock_methods(struct tdb_context *tdb,
+ void **lock_data);
+void tdb_set_lock_methods(struct tdb_context *tdb,
+ const struct tdb_lock_methods *lock_methods,
+ void *lock_data);
void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr);
--
1.6.0.4
More information about the samba-technical
mailing list