X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=ccan%2Ftdb2%2Ffree.c;h=0b724f23cce7c6fc239e630c5f44b027e461d177;hb=2c56e4d9ec516aceb9c5b26ceebd6d99361855c5;hp=f9e81ca4a134e5f9df51a8b096f2dd0ea70f2aac;hpb=25ed8785693e98492c0c516a8845866ec784f2b9;p=ccan diff --git a/ccan/tdb2/free.c b/ccan/tdb2/free.c index f9e81ca4..0b724f23 100644 --- a/ccan/tdb2/free.c +++ b/ccan/tdb2/free.c @@ -279,7 +279,7 @@ static int coalesce(struct tdb_context *tdb, tdb_off_t off, break; } - if (remove_from_list(tdb, list, r) == -1) { + if (remove_from_list(tdb, nlist, r) == -1) { tdb_unlock_free_list(tdb, nlist); goto err; } @@ -297,6 +297,14 @@ static int coalesce(struct tdb_context *tdb, tdb_off_t off, if (!r) goto err; + if (r->data_len != data_len) { + tdb->ecode = TDB_ERR_CORRUPT; + tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv, + "coalesce: expected data len %llu not %llu\n", + (long long)data_len, (long long)r->data_len); + goto err; + } + if (remove_from_list(tdb, list, r) == -1) goto err; @@ -559,92 +567,39 @@ static bool zones_happy(struct tdb_context *tdb) return true; } -/* Expand the database. */ -int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, - bool growing) +/* Returns how much extra room we get, or TDB_OFF_ERR. */ +static tdb_len_t expand_to_fill_zones(struct tdb_context *tdb) { - uint64_t new_num_buckets, new_num_zones, new_zone_bits; - uint64_t i, old_num_total, old_num_zones, old_size, old_zone_bits; - tdb_len_t add, freebucket_size, needed; - tdb_off_t off, old_free_off; - const tdb_off_t *oldf; - struct tdb_used_record fhdr; - - /* We need room for the record header too. */ - needed = sizeof(struct tdb_used_record) - + adjust_size(klen, dlen, growing); - - /* tdb_allrecord_lock will update header; did zones change? */ - old_zone_bits = tdb->header.v.zone_bits; - old_num_zones = tdb->header.v.num_zones; - - /* FIXME: this is overkill. An expand lock? */ - if (tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false) == -1) - return -1; - - /* Someone may have expanded for us. */ - if (old_zone_bits != tdb->header.v.zone_bits - || old_num_zones != tdb->header.v.num_zones) - goto success; - - /* They may have also expanded the underlying size (otherwise we'd - * have expanded our mmap to look at those offsets already). */ - old_size = tdb->map_size; - tdb->methods->oob(tdb, tdb->map_size + 1, true); - if (tdb->map_size != old_size) - goto success; - - /* Did we enlarge zones without enlarging file? */ - if (tdb->map_size < tdb->header.v.num_zones<header.v.zone_bits) { - add = (tdb->header.v.num_zones<header.v.zone_bits) - - tdb->map_size; - /* Updates tdb->map_size. */ - if (tdb->methods->expand_file(tdb, add) == -1) - goto fail; - if (add_free_record(tdb, tdb->map_size - add, add) == -1) - goto fail; - if (add >= needed) { - /* Allocate from this zone. */ - tdb->last_zone = zone_of(tdb, tdb->map_size - add); - goto success; - } - } + tdb_len_t add; - /* Slow path. Should we increase the number of buckets? */ - new_num_buckets = tdb->header.v.free_buckets; - if (larger_buckets_might_help(tdb)) - new_num_buckets++; - - /* Now we'll need room for the new free buckets, too. Assume - * worst case (zones expand). */ - needed += sizeof(fhdr) - + ((tdb->header.v.num_zones+1) - * (new_num_buckets+1) * sizeof(tdb_off_t)); + /* We can enlarge zones without enlarging file to match. */ + add = (tdb->header.v.num_zones<header.v.zone_bits) + - tdb->map_size; + if (add <= sizeof(struct tdb_free_record)) + return 0; - /* If we need less that one zone, and they're working well, just add - * another one. */ - if (needed < (1UL<header.v.zone_bits) && zones_happy(tdb)) { - new_num_zones = tdb->header.v.num_zones+1; - new_zone_bits = tdb->header.v.zone_bits; - add = 1ULL << tdb->header.v.zone_bits; - } else { - /* Increase the zone size. */ - new_num_zones = tdb->header.v.num_zones; - new_zone_bits = tdb->header.v.zone_bits+1; - while ((new_num_zones << new_zone_bits) - < tdb->map_size + needed) { - new_zone_bits++; - } + /* Updates tdb->map_size. */ + if (tdb->methods->expand_file(tdb, add) == -1) + return TDB_OFF_ERR; + if (add_free_record(tdb, tdb->map_size - add, add) == -1) + return TDB_OFF_ERR; + return add; +} - /* We expand by enough full zones to meet the need. */ - add = ((tdb->map_size + needed + (1ULL << new_zone_bits)-1) - & ~((1ULL << new_zone_bits)-1)) - - tdb->map_size; - } +static int update_zones(struct tdb_context *tdb, + uint64_t new_num_zones, + uint64_t new_zone_bits, + uint64_t new_num_buckets, + tdb_len_t add) +{ + tdb_len_t freebucket_size; + const tdb_off_t *oldf; + tdb_off_t i, off, old_num_total, old_free_off; + struct tdb_used_record fhdr; /* Updates tdb->map_size. */ if (tdb->methods->expand_file(tdb, add) == -1) - goto fail; + return -1; /* Use first part as new free bucket array. */ off = tdb->map_size - add; @@ -653,9 +608,9 @@ int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, /* Write header. */ if (set_header(tdb, &fhdr, 0, freebucket_size, freebucket_size, 0)) - goto fail; + return -1; if (tdb_write_convert(tdb, off, &fhdr, sizeof(fhdr)) == -1) - goto fail; + return -1; /* Adjust off to point to start of buckets, add to be remainder. */ add -= freebucket_size + sizeof(fhdr); @@ -667,7 +622,7 @@ int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, oldf = tdb_access_read(tdb, old_free_off, old_num_total * sizeof(tdb_off_t), true); if (!oldf) - goto fail; + return -1; /* Switch to using our new zone. */ if (zero_out(tdb, off, freebucket_size) == -1) @@ -716,14 +671,95 @@ int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, /* Start allocating from where the new space is. */ tdb->last_zone = zone_of(tdb, tdb->map_size - add); tdb_access_release(tdb, oldf); - if (write_header(tdb) == -1) + return write_header(tdb); + +fail_release: + tdb_access_release(tdb, oldf); + return -1; +} + +/* Expand the database. */ +int tdb_expand(struct tdb_context *tdb, tdb_len_t klen, tdb_len_t dlen, + bool growing) +{ + uint64_t new_num_buckets, new_num_zones, new_zone_bits; + uint64_t old_num_zones, old_size, old_zone_bits; + tdb_len_t add, needed; + + /* We need room for the record header too. */ + needed = sizeof(struct tdb_used_record) + + adjust_size(klen, dlen, growing); + + /* tdb_allrecord_lock will update header; did zones change? */ + old_zone_bits = tdb->header.v.zone_bits; + old_num_zones = tdb->header.v.num_zones; + + /* FIXME: this is overkill. An expand lock? */ + if (tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false) == -1) + return -1; + + /* Someone may have expanded for us. */ + if (old_zone_bits != tdb->header.v.zone_bits + || old_num_zones != tdb->header.v.num_zones) + goto success; + + /* They may have also expanded the underlying size (otherwise we'd + * have expanded our mmap to look at those offsets already). */ + old_size = tdb->map_size; + tdb->methods->oob(tdb, tdb->map_size + 1, true); + if (tdb->map_size != old_size) + goto success; + + add = expand_to_fill_zones(tdb); + if (add == TDB_OFF_ERR) goto fail; + + if (add >= needed) { + /* Allocate from this zone. */ + tdb->last_zone = zone_of(tdb, tdb->map_size - add); + goto success; + } + + /* Slow path. Should we increase the number of buckets? */ + new_num_buckets = tdb->header.v.free_buckets; + if (larger_buckets_might_help(tdb)) + new_num_buckets++; + + /* Now we'll need room for the new free buckets, too. Assume + * worst case (zones expand). */ + needed += sizeof(struct tdb_used_record) + + ((tdb->header.v.num_zones+1) + * (new_num_buckets+1) * sizeof(tdb_off_t)); + + /* If we need less that one zone, and they're working well, just add + * another one. */ + if (needed < (1UL<header.v.zone_bits) && zones_happy(tdb)) { + new_num_zones = tdb->header.v.num_zones+1; + new_zone_bits = tdb->header.v.zone_bits; + add = 1ULL << tdb->header.v.zone_bits; + } else { + /* Increase the zone size. */ + new_num_zones = tdb->header.v.num_zones; + new_zone_bits = tdb->header.v.zone_bits+1; + while ((new_num_zones << new_zone_bits) + < tdb->map_size + needed) { + new_zone_bits++; + } + + /* We expand by enough full zones to meet the need. */ + add = ((tdb->map_size + needed + (1ULL << new_zone_bits)-1) + & ~((1ULL << new_zone_bits)-1)) + - tdb->map_size; + } + + if (update_zones(tdb, new_num_zones, new_zone_bits, new_num_buckets, + add) == -1) + goto fail; + success: tdb_allrecord_unlock(tdb, F_WRLCK); return 0; -fail_release: - tdb_access_release(tdb, oldf); fail: tdb_allrecord_unlock(tdb, F_WRLCK); return -1;