2022-01-04 20:32:09 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
#include "bcachefs.h"
|
|
|
|
#include "buckets_waiting_for_journal.h"
|
|
|
|
#include <linux/hash.h>
|
|
|
|
#include <linux/random.h>
|
|
|
|
|
|
|
|
static inline struct bucket_hashed *
|
|
|
|
bucket_hash(struct buckets_waiting_for_journal_table *t,
|
|
|
|
unsigned hash_seed_idx, u64 dev_bucket)
|
|
|
|
{
|
|
|
|
return t->d + hash_64(dev_bucket ^ t->hash_seeds[hash_seed_idx], t->bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bucket_table_init(struct buckets_waiting_for_journal_table *t, size_t bits)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
t->bits = bits;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(t->hash_seeds); i++)
|
|
|
|
get_random_bytes(&t->hash_seeds[i], sizeof(t->hash_seeds[i]));
|
|
|
|
memset(t->d, 0, sizeof(t->d[0]) << t->bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bch2_bucket_needs_journal_commit(struct buckets_waiting_for_journal *b,
|
|
|
|
u64 flushed_seq,
|
|
|
|
unsigned dev, u64 bucket)
|
|
|
|
{
|
|
|
|
struct buckets_waiting_for_journal_table *t;
|
|
|
|
u64 dev_bucket = (u64) dev << 56 | bucket;
|
|
|
|
bool ret = false;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
mutex_lock(&b->lock);
|
|
|
|
t = b->t;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(t->hash_seeds); i++) {
|
|
|
|
struct bucket_hashed *h = bucket_hash(t, i, dev_bucket);
|
|
|
|
|
|
|
|
if (h->dev_bucket == dev_bucket) {
|
|
|
|
ret = h->journal_seq > flushed_seq;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&b->lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bucket_table_insert(struct buckets_waiting_for_journal_table *t,
|
|
|
|
struct bucket_hashed *new,
|
|
|
|
u64 flushed_seq)
|
|
|
|
{
|
|
|
|
struct bucket_hashed *last_evicted = NULL;
|
|
|
|
unsigned tries, i;
|
|
|
|
|
|
|
|
for (tries = 0; tries < 10; tries++) {
|
|
|
|
struct bucket_hashed *old, *victim = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(t->hash_seeds); i++) {
|
|
|
|
old = bucket_hash(t, i, new->dev_bucket);
|
|
|
|
|
|
|
|
if (old->dev_bucket == new->dev_bucket ||
|
|
|
|
old->journal_seq <= flushed_seq) {
|
|
|
|
*old = *new;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_evicted != old)
|
|
|
|
victim = old;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* hashed to same slot 3 times: */
|
|
|
|
if (!victim)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Failed to find an empty slot: */
|
|
|
|
swap(*new, *victim);
|
|
|
|
last_evicted = victim;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bch2_set_bucket_needs_journal_commit(struct buckets_waiting_for_journal *b,
|
|
|
|
u64 flushed_seq,
|
|
|
|
unsigned dev, u64 bucket,
|
|
|
|
u64 journal_seq)
|
|
|
|
{
|
|
|
|
struct buckets_waiting_for_journal_table *t, *n;
|
|
|
|
struct bucket_hashed tmp, new = {
|
|
|
|
.dev_bucket = (u64) dev << 56 | bucket,
|
|
|
|
.journal_seq = journal_seq,
|
|
|
|
};
|
2024-08-15 10:02:55 -07:00
|
|
|
size_t i, size, new_bits, nr_elements = 1, nr_rehashes = 0, nr_rehashes_this_size = 0;
|
2022-01-04 20:32:09 -07:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
mutex_lock(&b->lock);
|
|
|
|
|
|
|
|
if (likely(bucket_table_insert(b->t, &new, flushed_seq)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
t = b->t;
|
|
|
|
size = 1UL << t->bits;
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
nr_elements += t->d[i].journal_seq > flushed_seq;
|
|
|
|
|
2024-08-15 10:02:55 -07:00
|
|
|
new_bits = ilog2(roundup_pow_of_two(nr_elements * 3));
|
2024-08-17 16:51:13 -07:00
|
|
|
realloc:
|
2022-01-04 20:32:09 -07:00
|
|
|
n = kvmalloc(sizeof(*n) + (sizeof(n->d[0]) << new_bits), GFP_KERNEL);
|
|
|
|
if (!n) {
|
2023-03-14 12:35:57 -07:00
|
|
|
ret = -BCH_ERR_ENOMEM_buckets_waiting_for_journal_set;
|
2022-01-04 20:32:09 -07:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
retry_rehash:
|
2024-08-15 10:02:55 -07:00
|
|
|
if (nr_rehashes_this_size == 3) {
|
|
|
|
new_bits++;
|
|
|
|
nr_rehashes_this_size = 0;
|
2024-08-17 16:51:13 -07:00
|
|
|
kvfree(n);
|
|
|
|
goto realloc;
|
2024-08-15 10:02:55 -07:00
|
|
|
}
|
|
|
|
|
2022-01-04 20:32:09 -07:00
|
|
|
nr_rehashes++;
|
2024-08-15 10:02:55 -07:00
|
|
|
nr_rehashes_this_size++;
|
|
|
|
|
2022-01-04 20:32:09 -07:00
|
|
|
bucket_table_init(n, new_bits);
|
|
|
|
|
|
|
|
tmp = new;
|
|
|
|
BUG_ON(!bucket_table_insert(n, &tmp, flushed_seq));
|
|
|
|
|
|
|
|
for (i = 0; i < 1UL << t->bits; i++) {
|
|
|
|
if (t->d[i].journal_seq <= flushed_seq)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tmp = t->d[i];
|
|
|
|
if (!bucket_table_insert(n, &tmp, flushed_seq))
|
|
|
|
goto retry_rehash;
|
|
|
|
}
|
|
|
|
|
|
|
|
b->t = n;
|
|
|
|
kvfree(t);
|
|
|
|
|
bcachefs: Fix -Wformat in bch2_set_bucket_needs_journal_commit()
When building bcachefs for 32-bit ARM, there is a compiler warning in
bch2_set_bucket_needs_journal_commit() due to a debug print using the
wrong specifier:
fs/bcachefs/buckets_waiting_for_journal.c:137:30: error: format specifies type 'size_t' (aka 'unsigned int') but the argument has type 'unsigned long' [-Werror,-Wformat]
136 | pr_debug("took %zu rehashes, table at %zu/%zu elements",
| ~~~
| %lu
137 | nr_rehashes, nr_elements, 1UL << b->t->bits);
| ^~~~~~~~~~~~~~~~~
include/linux/printk.h:579:26: note: expanded from macro 'pr_debug'
579 | dynamic_pr_debug(fmt, ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dynamic_debug.h:270:22: note: expanded from macro 'dynamic_pr_debug'
270 | pr_fmt(fmt), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/linux/dynamic_debug.h:250:59: note: expanded from macro '_dynamic_func_call'
250 | _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:248:65: note: expanded from macro '_dynamic_func_call_cls'
248 | __dynamic_func_call_cls(__UNIQUE_ID(ddebug), cls, fmt, func, ##__VA_ARGS__)
| ^~~~~~~~~~~
include/linux/dynamic_debug.h:224:15: note: expanded from macro '__dynamic_func_call_cls'
224 | func(&id, ##__VA_ARGS__); \
| ^~~~~~~~~~~
1 error generated.
On 64-bit architectures, size_t is 'unsigned long', so there is no
warning when using %zu but on 32-bit architectures, size_t is
'unsigned int'. Use the correct specifier to resolve the warning.
Fixes: 7a82e75ddaef ("bcachefs: New data structure for buckets waiting on journal commit")
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-09-12 12:15:38 -07:00
|
|
|
pr_debug("took %zu rehashes, table at %zu/%lu elements",
|
2022-01-04 20:32:09 -07:00
|
|
|
nr_rehashes, nr_elements, 1UL << b->t->bits);
|
|
|
|
out:
|
|
|
|
mutex_unlock(&b->lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bch2_fs_buckets_waiting_for_journal_exit(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
struct buckets_waiting_for_journal *b = &c->buckets_waiting_for_journal;
|
|
|
|
|
|
|
|
kvfree(b->t);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INITIAL_TABLE_BITS 3
|
|
|
|
|
|
|
|
int bch2_fs_buckets_waiting_for_journal_init(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
struct buckets_waiting_for_journal *b = &c->buckets_waiting_for_journal;
|
|
|
|
|
|
|
|
mutex_init(&b->lock);
|
|
|
|
|
|
|
|
b->t = kvmalloc(sizeof(*b->t) +
|
|
|
|
(sizeof(b->t->d[0]) << INITIAL_TABLE_BITS), GFP_KERNEL);
|
|
|
|
if (!b->t)
|
2023-03-14 12:35:57 -07:00
|
|
|
return -BCH_ERR_ENOMEM_buckets_waiting_for_journal_init;
|
2022-01-04 20:32:09 -07:00
|
|
|
|
|
|
|
bucket_table_init(b->t, INITIAL_TABLE_BITS);
|
|
|
|
return 0;
|
|
|
|
}
|