1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "buckets_waiting_for_journal.h"
#include <linux/jhash.h>
static u32 hash_seeds[] = {
2168153708,
1262039142,
1183479835,
};
static inline unsigned bucket_hash(u64 dev_bucket, unsigned hash_seed_idx)
{
return jhash_2words(dev_bucket << 32, dev_bucket, hash_seeds[hash_seed_idx]);
}
bool bch2_bucket_needs_journal_commit(struct bch_fs *c,
u64 flushed_seq,
unsigned dev, u64 bucket)
{
struct buckets_waiting_for_journal *b = &c->buckets_waiting_for_journal;
u64 dev_bucket = (u64) dev << 56 | bucket;
bool ret = false;
unsigned i;
mutex_lock(&b->lock);
BUG_ON(!is_power_of_2(b->nr));
for (i = 0; i < ARRAY_SIZE(hash_seeds); i++) {
u32 h = bucket_hash(dev_bucket, i) & (b->nr - 1);
if (b->d[h].dev_bucket == dev_bucket) {
ret = b->d[h].journal_seq > flushed_seq;
break;
}
}
mutex_unlock(&b->lock);
return ret;
}
static int bch2_buckets_waiting_for_journal_rehash(struct bch_fs *c)
{
struct buckets_waiting_for_journal *b = &c->buckets_waiting_for_journal;
u64 flushed_seq = c->journal.flushed_seq_ondisk;
unsigned i, j, h, new_nr = b->nr * 2, elements = 0;
struct bucket_hashed *new_table;
new_table = kvmalloc_array(new_nr, sizeof(*new_table), __GFP_ZERO);
if (!new_table)
return -ENOMEM;
for (i = 0; i < b->nr; i++) {
if (b->d[i].journal_seq < flushed_seq)
continue;
for (j = 0; j < ARRAY_SIZE(hash_seeds); j++) {
h = bucket_hash(b->d[i].dev_bucket, j);
if ((h & (b->nr - 1)) == i)
break;
}
BUG_ON(j == ARRAY_SIZE(hash_seeds));
BUG_ON(new_table[h & (new_nr - 1)].dev_bucket);
new_table[h & (new_nr - 1)] = b->d[i];
elements++;
}
kvfree(b->d);
b->nr = new_nr;
b->d = new_table;
return 0;
}
int bch2_set_bucket_needs_journal_commit(struct bch_fs *c, unsigned dev, u64 bucket,
u64 journal_seq)
{
struct buckets_waiting_for_journal *b = &c->buckets_waiting_for_journal;
struct bucket_hashed new = {
.dev_bucket = (u64) dev << 56 | bucket,
.journal_seq = journal_seq,
}, *old, *victim, *last_evicted = NULL;
u64 flushed_seq = c->journal.flushed_seq_ondisk;
unsigned tries, i;
int ret = 0;
mutex_lock(&b->lock);
BUG_ON(!is_power_of_2(b->nr));
retry:
for (tries = 0; tries < 5; tries++) {
for (i = 0; i < ARRAY_SIZE(hash_seeds); i++) {
old = b->d + (bucket_hash(new.dev_bucket, i) & (b->nr - 1));
if (old->dev_bucket == new.dev_bucket ||
old->journal_seq <= flushed_seq) {
*old = new;
goto out;
}
if (last_evicted != old)
victim = old;
}
/* Failed to find an empty slot: */
swap(new, *victim);
last_evicted = victim;
}
ret = bch2_buckets_waiting_for_journal_rehash(c);
if (!ret)
goto retry;
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->d);
}
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->nr = 8;
b->d = kvmalloc_array(b->nr, sizeof(*b->d), __GFP_ZERO);
if (!b->d)
return -ENOMEM;
return 0;
}
|