Browse Source

tdb2: fix coalesce race #3

When we're coalescing, we need to drop the lock on the current free list, as
we've enlarged the block and it may now belong in a different list.

Unfortunately (as shown by repeated tdbtorture -n 8) another coalescing run
can do the coalescing while we've dropped the lock.  So for this case, we
use the TDB_COALESCING_MAGIC flag so it doesn't look free.
Rusty Russell 15 years ago
parent
commit
06a5b1a852
1 changed files with 11 additions and 1 deletions
  1. 11 1
      ccan/tdb2/free.c

+ 11 - 1
ccan/tdb2/free.c

@@ -347,7 +347,17 @@ static int coalesce(struct tdb_context *tdb,
 	if (remove_from_list(tdb, b_off, off, r) == -1)
 	if (remove_from_list(tdb, b_off, off, r) == -1)
 		goto err;
 		goto err;
 
 
-	/* We have to drop this to avoid deadlocks. */
+	r = tdb_access_write(tdb, off, sizeof(*r), true);
+	if (!r)
+		goto err;
+
+	/* We have to drop this to avoid deadlocks, so make sure record
+	 * doesn't get coalesced by someone else! */
+	r->magic_and_meta = TDB_COALESCING_MAGIC | zone_bits;
+	r->data_len = end - off - sizeof(struct tdb_used_record);
+	if (tdb_access_commit(tdb, r) != 0)
+		goto err;
+
 	tdb_unlock_free_bucket(tdb, b_off);
 	tdb_unlock_free_bucket(tdb, b_off);
 
 
 	if (add_free_record(tdb, zone_bits, off, end - off) == -1)
 	if (add_free_record(tdb, zone_bits, off, end - off) == -1)