From: Rusty Russell Date: Fri, 3 Sep 2010 12:52:08 +0000 (+0930) Subject: tdb2: expand lock now nests inside other locks. X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=84a19b9bada57df46adc76d46672546a675b00a7 tdb2: expand lock now nests inside other locks. This eliminates all the "drop lock and retry" logic: we expand inside alloc(). --- diff --git a/ccan/tdb2/free.c b/ccan/tdb2/free.c index 9af6f5d0..efc29e88 100644 --- a/ccan/tdb2/free.c +++ b/ccan/tdb2/free.c @@ -533,53 +533,6 @@ int set_header(struct tdb_context *tdb, return 0; } -static tdb_len_t adjust_size(size_t keylen, size_t datalen, bool growing) -{ - tdb_len_t size = keylen + datalen; - - if (size < MIN_DATA_LEN) - size = MIN_DATA_LEN; - - /* Overallocate if this is coming from an enlarging store. */ - if (growing) - size += datalen / 2; - - /* Round to next uint64_t boundary. */ - return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL); -} - -/* If this fails, try tdb_expand. */ -tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen, - uint64_t hash, bool growing) -{ - tdb_off_t off; - tdb_len_t size, actual; - struct tdb_used_record rec; - - /* We don't want header to change during this! */ - assert(tdb->header_uptodate); - - size = adjust_size(keylen, datalen, growing); - - off = get_free(tdb, size, &actual); - if (unlikely(off == TDB_OFF_ERR || off == 0)) - return off; - - /* Some supergiant values can't be encoded. */ - /* FIXME: Check before, and limit actual in get_free. */ - if (set_header(tdb, &rec, keylen, datalen, actual, hash, - tdb->zhdr.zone_bits) != 0) { - add_free_record(tdb, tdb->zhdr.zone_bits, off, - sizeof(rec) + actual); - return TDB_OFF_ERR; - } - - if (tdb_write_convert(tdb, off, &rec, sizeof(rec)) != 0) - return TDB_OFF_ERR; - - return off; -} - static bool zones_happy(struct tdb_context *tdb) { /* FIXME: look at distribution of zones. */ @@ -594,8 +547,7 @@ static tdb_len_t overhead(unsigned int zone_bits) } /* Expand the database (by adding a zone). */ -int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, - bool growing) +static int tdb_expand(struct tdb_context *tdb, tdb_len_t size) { uint64_t old_size; tdb_off_t off; @@ -606,8 +558,7 @@ int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, bool enlarge_zone; /* We need room for the record header too. */ - wanted = sizeof(struct tdb_used_record) - + (adjust_size(klen, dlen, growing)<map_size-1-off) == -1) goto fail; + /* Try allocating from this zone now. */ + tdb->zone_off = old_size - 1; + tdb->zhdr = zhdr; + success: tdb_unlock_expand(tdb, F_WRLCK); return 0; @@ -675,3 +630,57 @@ fail: tdb_unlock_expand(tdb, F_WRLCK); return -1; } + +static tdb_len_t adjust_size(size_t keylen, size_t datalen, bool growing) +{ + tdb_len_t size = keylen + datalen; + + if (size < MIN_DATA_LEN) + size = MIN_DATA_LEN; + + /* Overallocate if this is coming from an enlarging store. */ + if (growing) + size += datalen / 2; + + /* Round to next uint64_t boundary. */ + return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL); +} + +/* This won't fail: it will expand the database if it has to. */ +tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen, + uint64_t hash, bool growing) +{ + tdb_off_t off; + tdb_len_t size, actual; + struct tdb_used_record rec; + + /* We don't want header to change during this! */ + assert(tdb->header_uptodate); + + size = adjust_size(keylen, datalen, growing); + +again: + off = get_free(tdb, size, &actual); + if (unlikely(off == TDB_OFF_ERR)) + return off; + + if (unlikely(off == 0)) { + if (tdb_expand(tdb, size) == -1) + return TDB_OFF_ERR; + goto again; + } + + /* Some supergiant values can't be encoded. */ + /* FIXME: Check before, and limit actual in get_free. */ + if (set_header(tdb, &rec, keylen, datalen, actual, hash, + tdb->zhdr.zone_bits) != 0) { + add_free_record(tdb, tdb->zhdr.zone_bits, off, + sizeof(rec) + actual); + return TDB_OFF_ERR; + } + + if (tdb_write_convert(tdb, off, &rec, sizeof(rec)) != 0) + return TDB_OFF_ERR; + + return off; +} diff --git a/ccan/tdb2/private.h b/ccan/tdb2/private.h index 60e14d2a..2074e315 100644 --- a/ccan/tdb2/private.h +++ b/ccan/tdb2/private.h @@ -405,9 +405,6 @@ void tdb_unlock_open(struct tdb_context *tdb); int tdb_lock_expand(struct tdb_context *tdb, int ltype); void tdb_unlock_expand(struct tdb_context *tdb, int ltype); -/* Expand the file. */ -int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, - bool growing); #if 0 /* Low-level locking primitives. */ diff --git a/ccan/tdb2/tdb.c b/ccan/tdb2/tdb.c index 9ee64746..54ab06b8 100644 --- a/ccan/tdb2/tdb.c +++ b/ccan/tdb2/tdb.c @@ -624,17 +624,11 @@ static void enlarge_hash(struct tdb_context *tdb) if ((1ULL << tdb->header.v.hash_bits) != num) goto unlock; -again: /* Allocate our new array. */ hlen = num * sizeof(tdb_off_t) * 2; newoff = alloc(tdb, 0, hlen, 0, false); if (unlikely(newoff == TDB_OFF_ERR)) goto unlock; - if (unlikely(newoff == 0)) { - if (tdb_expand(tdb, 0, hlen, false) == -1) - goto unlock; - goto again; - } /* Step over record header! */ newoff += sizeof(struct tdb_used_record); @@ -755,7 +749,7 @@ static tdb_off_t find_and_lock(struct tdb_context *tdb, bucket, rec); } -/* Returns -1 on error, 0 on OK, 1 on "expand and retry." */ +/* Returns -1 on error, 0 on OK" */ static int replace_data(struct tdb_context *tdb, uint64_t h, struct tdb_data key, struct tdb_data dbuf, tdb_off_t bucket, @@ -770,9 +764,6 @@ static int replace_data(struct tdb_context *tdb, if (unlikely(new_off == TDB_OFF_ERR)) return -1; - if (unlikely(new_off == 0)) - return 1; - /* We didn't like the existing one: remove it. */ if (old_off) add_free_record(tdb, old_zone, old_off, @@ -849,13 +840,6 @@ int tdb_store(struct tdb_context *tdb, rec_zone_bits(&rec), off != 0); unlock_lists(tdb, start, num, F_WRLCK); - if (unlikely(ret == 1)) { - /* Expand, then try again... */ - if (tdb_expand(tdb, key.dsize, dbuf.dsize, off != 0) == -1) - return -1; - return tdb_store(tdb, key, dbuf, flag); - } - /* FIXME: by simple simulation, this approximated 60% full. * Check in real case! */ if (unlikely(num > 4 * tdb->header.v.hash_bits - 30)) @@ -933,13 +917,6 @@ int tdb_append(struct tdb_context *tdb, unlock_lists(tdb, start, num, F_WRLCK); free(newdata); - if (unlikely(ret == 1)) { - /* Expand, then try again. */ - if (tdb_expand(tdb, key.dsize, dbuf.dsize, true) == -1) - return -1; - return tdb_append(tdb, key, dbuf); - } - /* FIXME: by simple simulation, this approximated 60% full. * Check in real case! */ if (unlikely(num > 4 * tdb->header.v.hash_bits - 30)) diff --git a/ccan/tdb2/test/run-02-expand.c b/ccan/tdb2/test/run-02-expand.c index b303f5b2..3ce33b9f 100644 --- a/ccan/tdb2/test/run-02-expand.c +++ b/ccan/tdb2/test/run-02-expand.c @@ -26,33 +26,33 @@ int main(int argc, char *argv[]) /* First expand. Should add a zone, doubling file size.. */ val = tdb->map_size - 1 - sizeof(struct tdb_header); - ok1(tdb_expand(tdb, 1, 1, false) == 0); + ok1(tdb_expand(tdb, 1) == 0); ok1(tdb->map_size == 2 * val + 1 + sizeof(struct tdb_header)); ok1(tdb_check(tdb, NULL, NULL) == 0); /* Second expand, add another zone of same size. */ - ok1(tdb_expand(tdb, 1, 1, false) == 0); + ok1(tdb_expand(tdb, 1) == 0); ok1(tdb->map_size == 3 * val + 1 + sizeof(struct tdb_header)); ok1(tdb_check(tdb, NULL, NULL) == 0); /* Large expand, but can only add 4th zone of same size. */ - ok1(tdb_expand(tdb, 0, 4*val, false) == 0); + ok1(tdb_expand(tdb, 4*val) == 0); ok1(tdb->map_size == 4 * val + 1 + sizeof(struct tdb_header)); ok1(tdb_check(tdb, NULL, NULL) == 0); /* Large expand now will double file. */ - ok1(tdb_expand(tdb, 0, 4*val, false) == 0); + ok1(tdb_expand(tdb, 4*val) == 0); ok1(tdb->map_size == 8 * val + 1 + sizeof(struct tdb_header)); ok1(tdb_check(tdb, NULL, NULL) == 0); /* And again? */ - ok1(tdb_expand(tdb, 0, 4*val, false) == 0); + ok1(tdb_expand(tdb, 4*val) == 0); ok1(tdb->map_size == 16 * val + 1 + sizeof(struct tdb_header)); ok1(tdb_check(tdb, NULL, NULL) == 0); /* Below comfort level, will add a single 8*val zone. */ - ok1(tdb_expand(tdb, 0, ((8*val) >> TDB_COMFORT_FACTOR_BITS) - - sizeof(struct tdb_used_record), false) == 0); + ok1(tdb_expand(tdb, ((8*val) >> TDB_COMFORT_FACTOR_BITS) + - sizeof(struct tdb_used_record)) == 0); ok1(tdb->map_size == 24 * val + 1 + sizeof(struct tdb_header)); tdb_close(tdb); }