tdb: cleanup: remove ltype argument from _tdb_transaction_cancel.
[sahlberg/ctdb.git] / lib / tdb / common / transaction.c
index 20f2bfc2cd4595a27114fc9662c483c83b72b29d..0f9f80c86e0f59bb6860da3e36cac7e01f3db61a 100644 (file)
@@ -76,7 +76,7 @@
     to reduce this to 3 or even 2 with some more work.
 
   - check for a valid recovery record on open of the tdb, while the
-    global lock is held. Automatically recover from the transaction
+    open lock is held. Automatically recover from the transaction
     recovery area if needed, then continue with the open as
     usual. This allows for smooth crash recovery with no administrator
     intervention.
@@ -411,8 +411,18 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
 /*
   brlock during a transaction - ignore them
 */
-static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, 
-                             int rw_type, int lck_type, int probe, size_t len)
+static int transaction_brlock(struct tdb_context *tdb,
+                             int rw_type, tdb_off_t offset, size_t len,
+                             enum tdb_lock_flags flags)
+{
+       /* FIXME: We actually grab the open lock during a transaction. */
+       if (offset == OPEN_LOCK)
+               return tdb_brlock(tdb, rw_type, offset, len, flags);
+       return 0;
+}
+
+static int transaction_brunlock(struct tdb_context *tdb,
+                               int rw_type, tdb_off_t offset, size_t len)
 {
        return 0;
 }
@@ -423,7 +433,8 @@ static const struct tdb_methods transaction_methods = {
        transaction_next_hash_chain,
        transaction_oob,
        transaction_expand_file,
-       transaction_brlock
+       transaction_brlock,
+       transaction_brunlock
 };
 
 
@@ -452,7 +463,7 @@ int tdb_transaction_start(struct tdb_context *tdb)
                return 0;
        }
 
-       if (tdb->num_locks != 0 || tdb->global_lock.count) {
+       if (tdb_have_extra_locks(tdb)) {
                /* the caller must not have any locks when starting a
                   transaction as otherwise we'll be screwed by lack
                   of nested locks in posix */
@@ -491,10 +502,9 @@ int tdb_transaction_start(struct tdb_context *tdb)
        
        /* get a read lock from the freelist to the end of file. This
           is upgraded to a write lock during the commit */
-       if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) {
+       if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, true) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n"));
-               tdb->ecode = TDB_ERR_LOCK;
-               goto fail;
+               goto fail_allrecord_lock;
        }
 
        /* setup a copy of the hash table heads so the hash scan in
@@ -527,8 +537,9 @@ int tdb_transaction_start(struct tdb_context *tdb)
        return 0;
        
 fail:
-       tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
-       tdb_transaction_unlock(tdb);
+       tdb_allrecord_unlock(tdb, F_RDLCK, false);
+fail_allrecord_lock:
+       tdb_transaction_unlock(tdb, F_WRLCK);
        SAFE_FREE(tdb->transaction->blocks);
        SAFE_FREE(tdb->transaction->hash_heads);
        SAFE_FREE(tdb->transaction);
@@ -545,7 +556,7 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t
                return 0;
        }
 
-       if (fsync(tdb->fd) != 0) {
+       if (fdatasync(tdb->fd) != 0) {
                tdb->ecode = TDB_ERR_IO;
                TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
                return -1;
@@ -566,7 +577,7 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t
 }
 
 
-int _tdb_transaction_cancel(struct tdb_context *tdb)
+static int _tdb_transaction_cancel(struct tdb_context *tdb)
 {      
        int i, ret = 0;
 
@@ -593,38 +604,23 @@ int _tdb_transaction_cancel(struct tdb_context *tdb)
 
        if (tdb->transaction->magic_offset) {
                const struct tdb_methods *methods = tdb->transaction->io_methods;
-               uint32_t zero = 0;
+               const uint32_t invalid = TDB_RECOVERY_INVALID_MAGIC;
 
                /* remove the recovery marker */
-               if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &zero, 4) == -1 ||
+               if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &invalid, 4) == -1 ||
                transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n"));
                        ret = -1;
                }
        }
 
-       /* remove any global lock created during the transaction */
-       if (tdb->global_lock.count != 0) {
-               tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size);
-               tdb->global_lock.count = 0;
-       }
-
-       /* remove any locks created during the transaction */
-       if (tdb->num_locks != 0) {
-               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;
-               tdb->num_lockrecs = 0;
-               SAFE_FREE(tdb->lockrecs);
-       }
+       /* This also removes the OPEN_LOCK, if we have it. */
+       tdb_release_extra_locks(tdb);
 
        /* restore the normal io methods */
        tdb->methods = tdb->transaction->io_methods;
 
-       tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
-       tdb_transaction_unlock(tdb);
+       tdb_transaction_unlock(tdb, F_WRLCK);
        SAFE_FREE(tdb->transaction->hash_heads);
        SAFE_FREE(tdb->transaction);
        
@@ -687,10 +683,16 @@ static int tdb_recovery_allocate(struct tdb_context *tdb,
 
        rec.rec_len = 0;
 
-       if (recovery_head != 0 && 
-           methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
-               TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
-               return -1;
+       if (recovery_head != 0) {
+               if (methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
+                       return -1;
+               }
+               /* ignore invalid recovery regions: can happen in crash */
+               if (rec.magic != TDB_RECOVERY_MAGIC &&
+                   rec.magic != TDB_RECOVERY_INVALID_MAGIC) {
+                       recovery_head = 0;
+               }
        }
 
        *recovery_size = tdb_recovery_size(tdb);
@@ -785,7 +787,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb,
        rec = (struct tdb_record *)data;
        memset(rec, 0, sizeof(*rec));
 
-       rec->magic    = 0;
+       rec->magic    = TDB_RECOVERY_INVALID_MAGIC;
        rec->data_len = recovery_size;
        rec->rec_len  = recovery_max_size;
        rec->key_len  = old_map_size;
@@ -923,7 +925,7 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
        
        /* if there are any locks pending then the caller has not
           nested their locks properly, so fail the transaction */
-       if (tdb->num_locks || tdb->global_lock.count) {
+       if (tdb_have_extra_locks(tdb)) {
                tdb->ecode = TDB_ERR_LOCK;
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: locks pending on commit\n"));
                _tdb_transaction_cancel(tdb);
@@ -931,18 +933,16 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
        }
 
        /* upgrade the main transaction lock region to a write lock */
-       if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) {
+       if (tdb_allrecord_upgrade(tdb) == -1) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to upgrade hash locks\n"));
-               tdb->ecode = TDB_ERR_LOCK;
                _tdb_transaction_cancel(tdb);
                return -1;
        }
 
-       /* get the global lock - this prevents new users attaching to the database
+       /* get the open lock - this prevents new users attaching to the database
           during the commit */
-       if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get global lock\n"));
-               tdb->ecode = TDB_ERR_LOCK;
+       if (tdb_nest_lock(tdb, OPEN_LOCK, F_WRLCK, TDB_LOCK_WAIT) == -1) {
+               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get open lock\n"));
                _tdb_transaction_cancel(tdb);
                return -1;
        }
@@ -951,7 +951,6 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
                /* write the recovery data to the end of the file */
                if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n"));
-                       tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
                        _tdb_transaction_cancel(tdb);
                        return -1;
                }
@@ -966,7 +965,6 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
                                             tdb->transaction->old_map_size) == -1) {
                        tdb->ecode = TDB_ERR_IO;
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: expansion failed\n"));
-                       tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
                        _tdb_transaction_cancel(tdb);
                        return -1;
                }
@@ -974,7 +972,7 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
                methods->tdb_oob(tdb, tdb->map_size + 1, 1);
        }
 
-       /* Keep the global lock until the actual commit */
+       /* Keep the open lock until the actual commit */
 
        return 0;
 }
@@ -1056,7 +1054,6 @@ int tdb_transaction_commit(struct tdb_context *tdb)
                        tdb_transaction_recover(tdb); 
 
                        _tdb_transaction_cancel(tdb);
-                       tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
 
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n"));
                        return -1;
@@ -1072,8 +1069,6 @@ int tdb_transaction_commit(struct tdb_context *tdb)
                return -1;
        }
 
-       tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
-
        /*
          TODO: maybe write to some dummy hdr field, or write to magic
          offset without mmap, before the last sync, instead of the
@@ -1105,7 +1100,7 @@ int tdb_transaction_commit(struct tdb_context *tdb)
 
 /*
   recover from an aborted transaction. Must be called with exclusive
-  database write access already established (including the global
+  database write access already established (including the open
   lock to prevent new processes attaching)
 */
 int tdb_transaction_recover(struct tdb_context *tdb)