#include "alloc_background.h"
#include "btree_iter.h"
#include "btree_update.h"
+#include "btree_write_buffer.h"
#include "error.h"
#include "lru.h"
#include "recovery.h"
+/* KEY_TYPE_lru is obsolete: */
int bch2_lru_invalid(const struct bch_fs *c, struct bkey_s_c k,
- int rw, struct printbuf *err)
+ enum bkey_invalid_flags flags,
+ struct printbuf *err)
{
- const struct bch_lru *lru = bkey_s_c_to_lru(k).v;
+ if (!lru_pos_time(k.k->p)) {
+ prt_printf(err, "lru entry at time=0");
+ return -BCH_ERR_invalid_bkey;
- if (bkey_val_bytes(k.k) < sizeof(*lru)) {
- pr_buf(err, "incorrect value size (%zu < %zu)",
- bkey_val_bytes(k.k), sizeof(*lru));
- return -EINVAL;
}
return 0;
{
const struct bch_lru *lru = bkey_s_c_to_lru(k).v;
- pr_buf(out, "idx %llu", le64_to_cpu(lru->idx));
+ prt_printf(out, "idx %llu", le64_to_cpu(lru->idx));
}
-int bch2_lru_delete(struct btree_trans *trans, u64 id, u64 idx, u64 time)
+void bch2_lru_pos_to_text(struct printbuf *out, struct bpos lru)
{
- struct bch_fs *c = trans->c;
- struct btree_iter iter;
- struct bkey_s_c k;
- u64 existing_idx;
- int ret = 0;
-
- if (!time)
- return 0;
-
- bch2_trans_iter_init(trans, &iter, BTREE_ID_lru,
- POS(id, time),
- BTREE_ITER_INTENT|
- BTREE_ITER_WITH_UPDATES);
- k = bch2_btree_iter_peek_slot(&iter);
- ret = bkey_err(k);
- if (ret)
- goto err;
-
- if (k.k->type != KEY_TYPE_lru) {
- bch2_fs_inconsistent(c,
- "pointer to nonexistent lru %llu:%llu",
- id, time);
- ret = -EIO;
- goto err;
- }
-
- existing_idx = le64_to_cpu(bkey_s_c_to_lru(k).v->idx);
- if (existing_idx != idx) {
- bch2_fs_inconsistent(c,
- "lru %llu:%llu with wrong backpointer: got %llu, should be %llu",
- id, time, existing_idx, idx);
- ret = -EIO;
- goto err;
- }
-
- ret = bch2_btree_delete_at(trans, &iter, 0);
-err:
- bch2_trans_iter_exit(trans, &iter);
- return ret;
+ prt_printf(out, "%llu:%llu -> %llu:%llu",
+ lru_pos_id(lru),
+ lru_pos_time(lru),
+ u64_to_bucket(lru.offset).inode,
+ u64_to_bucket(lru.offset).offset);
}
-int bch2_lru_set(struct btree_trans *trans, u64 lru_id, u64 idx, u64 *time)
+static int __bch2_lru_set(struct btree_trans *trans, u16 lru_id,
+ u64 dev_bucket, u64 time, bool set)
{
- struct btree_iter iter;
- struct bkey_s_c k;
- struct bkey_i_lru *lru;
- int ret = 0;
-
- if (!*time)
- return 0;
-
- for_each_btree_key_norestart(trans, iter, BTREE_ID_lru,
- POS(lru_id, *time),
- BTREE_ITER_SLOTS|
- BTREE_ITER_INTENT|
- BTREE_ITER_WITH_UPDATES, k, ret)
- if (bkey_deleted(k.k))
- break;
-
- if (ret)
- goto err;
-
- BUG_ON(iter.pos.inode != lru_id);
- *time = iter.pos.offset;
-
- lru = bch2_trans_kmalloc(trans, sizeof(*lru));
- ret = PTR_ERR_OR_ZERO(lru);
- if (ret)
- goto err;
+ return time
+ ? bch2_btree_bit_mod(trans, BTREE_ID_lru,
+ lru_pos(lru_id, dev_bucket, time), set)
+ : 0;
+}
- bkey_lru_init(&lru->k_i);
- lru->k.p = iter.pos;
- lru->v.idx = cpu_to_le64(idx);
+int bch2_lru_del(struct btree_trans *trans, u16 lru_id, u64 dev_bucket, u64 time)
+{
+ return __bch2_lru_set(trans, lru_id, dev_bucket, time, KEY_TYPE_deleted);
+}
- ret = bch2_trans_update(trans, &iter, &lru->k_i, 0);
- if (ret)
- goto err;
-err:
- bch2_trans_iter_exit(trans, &iter);
- return ret;
+int bch2_lru_set(struct btree_trans *trans, u16 lru_id, u64 dev_bucket, u64 time)
+{
+ return __bch2_lru_set(trans, lru_id, dev_bucket, time, KEY_TYPE_set);
}
-int bch2_lru_change(struct btree_trans *trans, u64 id, u64 idx,
- u64 old_time, u64 *new_time)
+int bch2_lru_change(struct btree_trans *trans,
+ u16 lru_id, u64 dev_bucket,
+ u64 old_time, u64 new_time)
{
- if (old_time == *new_time)
+ if (old_time == new_time)
return 0;
- return bch2_lru_delete(trans, id, idx, old_time) ?:
- bch2_lru_set(trans, id, idx, new_time);
+ return bch2_lru_del(trans, lru_id, dev_bucket, old_time) ?:
+ bch2_lru_set(trans, lru_id, dev_bucket, new_time);
}
+static const char * const bch2_lru_types[] = {
+#define x(n) #n,
+ BCH_LRU_TYPES()
+#undef x
+ NULL
+};
+
static int bch2_check_lru_key(struct btree_trans *trans,
- struct btree_iter *lru_iter, bool initial)
+ struct btree_iter *lru_iter,
+ struct bkey_s_c lru_k,
+ struct bpos *last_flushed_pos)
{
struct bch_fs *c = trans->c;
struct btree_iter iter;
- struct bkey_s_c lru_k, k;
- struct bch_alloc_v4 a;
+ struct bkey_s_c k;
+ struct bch_alloc_v4 a_convert;
+ const struct bch_alloc_v4 *a;
struct printbuf buf1 = PRINTBUF;
struct printbuf buf2 = PRINTBUF;
- struct bpos alloc_pos;
+ enum bch_lru_type type = lru_type(lru_k);
+ struct bpos alloc_pos = u64_to_bucket(lru_k.k->p.offset);
+ u64 idx;
int ret;
- lru_k = bch2_btree_iter_peek(lru_iter);
- if (!lru_k.k)
- return 0;
-
- ret = bkey_err(lru_k);
- if (ret)
- return ret;
-
- alloc_pos = POS(lru_k.k->p.inode,
- le64_to_cpu(bkey_s_c_to_lru(lru_k).v->idx));
-
if (fsck_err_on(!bch2_dev_bucket_exists(c, alloc_pos), c,
"lru key points to nonexistent device:bucket %llu:%llu",
alloc_pos.inode, alloc_pos.offset))
return bch2_btree_delete_at(trans, lru_iter, 0);
- bch2_trans_iter_init(trans, &iter, BTREE_ID_alloc, alloc_pos, 0);
- k = bch2_btree_iter_peek_slot(&iter);
+ k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_alloc, alloc_pos, 0);
ret = bkey_err(k);
if (ret)
goto err;
- bch2_alloc_to_v4(k, &a);
+ a = bch2_alloc_to_v4(k, &a_convert);
- if (fsck_err_on(a.data_type != BCH_DATA_cached ||
- a.io_time[READ] != lru_k.k->p.offset, c,
- "incorrect lru entry %s\n"
- " for %s",
- (bch2_bkey_val_to_text(&buf1, c, lru_k), buf1.buf),
- (bch2_bkey_val_to_text(&buf2, c, k), buf2.buf))) {
- struct bkey_i *update =
- bch2_trans_kmalloc(trans, sizeof(*update));
-
- ret = PTR_ERR_OR_ZERO(update);
- if (ret)
- goto err;
-
- bkey_init(&update->k);
- update->k.p = lru_iter->pos;
+ switch (type) {
+ case BCH_LRU_read:
+ idx = alloc_lru_idx_read(*a);
+ break;
+ case BCH_LRU_fragmentation:
+ idx = a->fragmentation_lru;
+ break;
+ }
- ret = bch2_trans_update(trans, lru_iter, update, 0);
- if (ret)
- goto err;
+ if (lru_k.k->type != KEY_TYPE_set ||
+ lru_pos_time(lru_k.k->p) != idx) {
+ if (!bpos_eq(*last_flushed_pos, lru_k.k->p)) {
+ *last_flushed_pos = lru_k.k->p;
+ ret = bch2_btree_write_buffer_flush_sync(trans) ?:
+ -BCH_ERR_transaction_restart_write_buffer_flush;
+ goto out;
+ }
+
+ if (c->opts.reconstruct_alloc ||
+ fsck_err(c, "incorrect lru entry: lru %s time %llu\n"
+ " %s\n"
+ " for %s",
+ bch2_lru_types[type],
+ lru_pos_time(lru_k.k->p),
+ (bch2_bkey_val_to_text(&buf1, c, lru_k), buf1.buf),
+ (bch2_bkey_val_to_text(&buf2, c, k), buf2.buf)))
+ ret = bch2_btree_delete_at(trans, lru_iter, 0);
}
+out:
err:
fsck_err:
bch2_trans_iter_exit(trans, &iter);
return ret;
}
-int bch2_check_lrus(struct bch_fs *c, bool initial)
+int bch2_check_lrus(struct bch_fs *c)
{
- struct btree_trans trans;
struct btree_iter iter;
struct bkey_s_c k;
+ struct bpos last_flushed_pos = POS_MIN;
int ret = 0;
- bch2_trans_init(&trans, c, 0, 0);
-
- for_each_btree_key(&trans, iter, BTREE_ID_lru, POS_MIN,
- BTREE_ITER_PREFETCH, k, ret) {
- ret = __bch2_trans_do(&trans, NULL, NULL, 0,
- bch2_check_lru_key(&trans, &iter, initial));
- if (ret)
- break;
- }
- bch2_trans_iter_exit(&trans, &iter);
-
- bch2_trans_exit(&trans);
+ ret = bch2_trans_run(c,
+ for_each_btree_key_commit(trans, iter,
+ BTREE_ID_lru, POS_MIN, BTREE_ITER_PREFETCH, k,
+ NULL, NULL, BTREE_INSERT_NOFAIL|BTREE_INSERT_LAZY_RW,
+ bch2_check_lru_key(trans, &iter, k, &last_flushed_pos)));
+ if (ret)
+ bch_err_fn(c, ret);
return ret;
}