bcachefs-tools/libbcache/keybuf.c
2017-03-10 12:40:01 -09:00

196 lines
4.1 KiB
C

#include "bcache.h"
#include "btree_gc.h"
#include "btree_iter.h"
#include "keybuf.h"
#include <trace/events/bcache.h>
/*
* For buffered iteration over the btree, with predicates and ratelimiting and
* whatnot
*/
static inline int keybuf_cmp(struct keybuf_key *l, struct keybuf_key *r)
{
/* Overlapping keys compare equal */
if (bkey_cmp(l->key.k.p, bkey_start_pos(&r->key.k)) <= 0)
return -1;
if (bkey_cmp(bkey_start_pos(&l->key.k), r->key.k.p) >= 0)
return 1;
return 0;
}
static inline int keybuf_nonoverlapping_cmp(struct keybuf_key *l,
struct keybuf_key *r)
{
return clamp_t(s64, bkey_cmp(l->key.k.p, r->key.k.p), -1, 1);
}
void bch_refill_keybuf(struct bch_fs *c, struct keybuf *buf,
struct bpos end, keybuf_pred_fn *pred)
{
struct bpos start = buf->last_scanned;
struct btree_iter iter;
struct bkey_s_c k;
unsigned nr_found = 0;
for_each_btree_key(&iter, c, BTREE_ID_EXTENTS, buf->last_scanned, k) {
if (bkey_cmp(k.k->p, end) >= 0) {
buf->last_scanned = k.k->p;
goto done;
}
if (pred(buf, k)) {
struct keybuf_key *w;
spin_lock(&buf->lock);
w = array_alloc(&buf->freelist);
if (!w) {
spin_unlock(&buf->lock);
goto done;
}
bkey_reassemble(&w->key, k);
atomic_set(&w->ref, -1); /* -1 means hasn't started */
if (RB_INSERT(&buf->keys, w, node, keybuf_cmp))
array_free(&buf->freelist, w);
else
nr_found++;
spin_unlock(&buf->lock);
}
buf->last_scanned = k.k->p;
bch_btree_iter_cond_resched(&iter);
}
/* If we end up here, it means:
* - the map_fn didn't fill up the keybuf
* - the map_fn didn't see the end key
* - there were no more keys to map over
* Therefore, we are at the end of the key space */
buf->last_scanned = POS_MAX;
done:
bch_btree_iter_unlock(&iter);
trace_bcache_keyscan(nr_found,
start.inode, start.offset,
buf->last_scanned.inode,
buf->last_scanned.offset);
spin_lock(&buf->lock);
if (!RB_EMPTY_ROOT(&buf->keys)) {
struct keybuf_key *w;
w = RB_FIRST(&buf->keys, struct keybuf_key, node);
buf->start = bkey_start_pos(&w->key.k);
w = RB_LAST(&buf->keys, struct keybuf_key, node);
buf->end = w->key.k.p;
} else {
buf->start = POS_MAX;
buf->end = POS_MAX;
}
spin_unlock(&buf->lock);
}
static void bch_keybuf_del(struct keybuf *buf, struct keybuf_key *w)
{
rb_erase(&w->node, &buf->keys);
array_free(&buf->freelist, w);
}
void bch_keybuf_put(struct keybuf *buf, struct keybuf_key *w)
{
BUG_ON(atomic_read(&w->ref) <= 0);
if (atomic_dec_and_test(&w->ref)) {
up(&buf->in_flight);
spin_lock(&buf->lock);
bch_keybuf_del(buf, w);
spin_unlock(&buf->lock);
}
}
void bch_keybuf_recalc_oldest_gens(struct bch_fs *c, struct keybuf *buf)
{
struct keybuf_key *w, *n;
spin_lock(&buf->lock);
rbtree_postorder_for_each_entry_safe(w, n,
&buf->keys, node)
bch_btree_key_recalc_oldest_gen(c, bkey_i_to_s_c(&w->key));
spin_unlock(&buf->lock);
}
bool bch_keybuf_check_overlapping(struct keybuf *buf, struct bpos start,
struct bpos end)
{
bool ret = false;
struct keybuf_key *w, *next, s = { .key.k.p = start };
if (bkey_cmp(end, buf->start) <= 0 ||
bkey_cmp(start, buf->end) >= 0)
return false;
spin_lock(&buf->lock);
for (w = RB_GREATER(&buf->keys, s, node, keybuf_nonoverlapping_cmp);
w && bkey_cmp(bkey_start_pos(&w->key.k), end) < 0;
w = next) {
next = RB_NEXT(w, node);
if (atomic_read(&w->ref) == -1)
bch_keybuf_del(buf, w);
else
ret = true;
}
spin_unlock(&buf->lock);
return ret;
}
struct keybuf_key *bch_keybuf_next(struct keybuf *buf)
{
struct keybuf_key *w;
spin_lock(&buf->lock);
w = RB_FIRST(&buf->keys, struct keybuf_key, node);
while (w && atomic_read(&w->ref) != -1)
w = RB_NEXT(w, node);
if (!w) {
spin_unlock(&buf->lock);
return NULL;
}
atomic_set(&w->ref, 1);
spin_unlock(&buf->lock);
down(&buf->in_flight);
return w;
}
void bch_keybuf_init(struct keybuf *buf)
{
sema_init(&buf->in_flight, KEYBUF_REFILL_BATCH / 2);
buf->last_scanned = POS_MAX;
buf->start = POS_MIN;
buf->end = POS_MIN;
buf->keys = RB_ROOT;
spin_lock_init(&buf->lock);
array_allocator_init(&buf->freelist);
}