static struct btree_path *btree_path_alloc(struct btree_trans *, struct btree_path *);
+/*
+ * Unlocks before scheduling
+ * Note: does not revalidate iterator
+ */
+static inline int bch2_trans_cond_resched(struct btree_trans *trans)
+{
+ if (need_resched() || race_fault()) {
+ bch2_trans_unlock(trans);
+ schedule();
+ return bch2_trans_relock(trans) ? 0 : -EINTR;
+ } else {
+ return 0;
+ }
+}
+
static inline int __btree_path_cmp(const struct btree_path *l,
enum btree_id r_btree_id,
bool r_cached,
{
struct btree *b = path->l[level].b;
- EBUG_ON(btree_lock_want(path, level) != BTREE_NODE_INTENT_LOCKED);
-
if (!is_btree_node(path, level))
return false;
+ switch (btree_lock_want(path, level)) {
+ case BTREE_NODE_UNLOCKED:
+ BUG_ON(btree_node_locked(path, level));
+ return true;
+ case BTREE_NODE_READ_LOCKED:
+ BUG_ON(btree_node_intent_locked(path, level));
+ return bch2_btree_node_relock(trans, path, level);
+ case BTREE_NODE_INTENT_LOCKED:
+ break;
+ }
+
if (btree_node_intent_locked(path, level))
return true;
unsigned l;
if (!path->nodes_locked) {
- BUG_ON(path->uptodate == BTREE_ITER_UPTODATE);
+ BUG_ON(path->uptodate == BTREE_ITER_UPTODATE &&
+ btree_path_node(path, path->level));
return;
}
for (i = 0; i < (!path->cached ? BTREE_MAX_DEPTH : 1); i++) {
if (!path->l[i].b) {
- BUG_ON(c->btree_roots[path->btree_id].b->c.level > i);
+ BUG_ON(!path->cached &&
+ c->btree_roots[path->btree_id].b->c.level > i);
break;
}
} while (ret);
}
- if (unlikely(ret == -EIO)) {
- trans->error = true;
+ if (unlikely(ret == -EIO))
goto out;
- }
BUG_ON(ret && ret != -EINTR);
EBUG_ON(!(trans->paths_allocated & (1ULL << path->idx)));
- if (path->nodes_locked)
+ if (path->nodes_locked ||
+ !btree_path_node(path, path->level))
i++;
}
unsigned depth_want = path->level;
int ret = 0;
+ if (unlikely(trans->restarted)) {
+ ret = -EINTR;
+ goto out;
+ }
+
/*
* Ensure we obey path->should_be_locked: if it's set, we can't unlock
* and re-traverse the path without a transaction restart:
int __must_check bch2_btree_path_traverse(struct btree_trans *trans,
struct btree_path *path, unsigned flags)
{
- int ret;
-
if (path->uptodate < BTREE_ITER_NEED_RELOCK)
return 0;
- ret = bch2_trans_cond_resched(trans) ?:
+ return bch2_trans_cond_resched(trans) ?:
btree_path_traverse_one(trans, path, flags, _RET_IP_);
- if (unlikely(ret) && hweight64(trans->paths_allocated) == 1) {
- ret = __btree_path_traverse_all(trans, ret, _RET_IP_);
- BUG_ON(ret == -EINTR);
- }
-
- return ret;
}
static void btree_path_copy(struct btree_trans *trans, struct btree_path *dst,
struct btree_path *path;
struct btree_insert_entry *i;
unsigned idx;
- char buf[300];
+ char buf1[300], buf2[300];
btree_trans_verify_sorted(trans);
path->idx, path->ref, path->intent_ref,
path->preserve ? " preserve" : "",
bch2_btree_ids[path->btree_id],
- (bch2_bpos_to_text(&PBUF(buf), path->pos), buf),
+ (bch2_bpos_to_text(&PBUF(buf1), path->pos), buf1),
#ifdef CONFIG_BCACHEFS_DEBUG
(void *) path->ip_allocated
#else
#endif
);
- trans_for_each_update(trans, i)
- printk(KERN_ERR "update: btree %s %s %pS\n",
+ trans_for_each_update(trans, i) {
+ struct bkey u;
+ struct bkey_s_c old = bch2_btree_path_peek_slot(i->path, &u);
+
+ printk(KERN_ERR "update: btree %s %pS\n old %s\n new %s",
bch2_btree_ids[i->btree_id],
- (bch2_bkey_val_to_text(&PBUF(buf), trans->c, bkey_i_to_s_c(i->k)), buf),
- (void *) i->ip_allocated);
+ (void *) i->ip_allocated,
+ (bch2_bkey_val_to_text(&PBUF(buf1), trans->c, old), buf1),
+ (bch2_bkey_val_to_text(&PBUF(buf2), trans->c, bkey_i_to_s_c(i->k)), buf2));
+ }
}
static struct btree_path *btree_path_alloc(struct btree_trans *trans,
struct btree *bch2_btree_iter_peek_node(struct btree_iter *iter)
{
+ struct btree_trans *trans = iter->trans;
struct btree *b = NULL;
int ret;
EBUG_ON(iter->path->cached);
bch2_btree_iter_verify(iter);
- ret = bch2_btree_path_traverse(iter->trans, iter->path, iter->flags);
+ ret = bch2_btree_path_traverse(trans, iter->path, iter->flags);
if (ret)
- goto out;
+ goto err;
b = btree_path_node(iter->path, iter->path->level);
if (!b)
bkey_init(&iter->k);
iter->k.p = iter->pos = b->key.k.p;
+
+ iter->path = btree_path_set_pos(trans, iter->path, b->key.k.p,
+ iter->flags & BTREE_ITER_INTENT);
iter->path->should_be_locked = true;
+ BUG_ON(iter->path->uptodate);
out:
bch2_btree_iter_verify_entry_exit(iter);
bch2_btree_iter_verify(iter);
return b;
+err:
+ b = ERR_PTR(ret);
+ goto out;
}
struct btree *bch2_btree_iter_next_node(struct btree_iter *iter)
struct btree_trans *trans = iter->trans;
struct btree_path *path = iter->path;
struct btree *b = NULL;
+ unsigned l;
int ret;
+ BUG_ON(trans->restarted);
EBUG_ON(iter->path->cached);
bch2_btree_iter_verify(iter);
- /* already got to end? */
+ /* already at end? */
if (!btree_path_node(path, path->level))
- goto out;
-
- bch2_trans_cond_resched(trans);
+ return NULL;
- btree_node_unlock(path, path->level);
- path->l[path->level].b = BTREE_ITER_NO_NODE_UP;
- path->level++;
+ /* got to end? */
+ if (!btree_path_node(path, path->level + 1)) {
+ btree_node_unlock(path, path->level);
+ path->l[path->level].b = BTREE_ITER_NO_NODE_UP;
+ path->level++;
+ return NULL;
+ }
- btree_path_set_dirty(path, BTREE_ITER_NEED_TRAVERSE);
- ret = bch2_btree_path_traverse(trans, path, iter->flags);
- if (ret)
- goto out;
+ if (!bch2_btree_node_relock(trans, path, path->level + 1)) {
+ __bch2_btree_path_unlock(path);
+ path->l[path->level].b = BTREE_ITER_NO_NODE_GET_LOCKS;
+ path->l[path->level + 1].b = BTREE_ITER_NO_NODE_GET_LOCKS;
+ btree_trans_restart(trans);
+ ret = -EINTR;
+ goto err;
+ }
- /* got to end? */
- b = btree_path_node(path, path->level);
- if (!b)
- goto out;
+ b = btree_path_node(path, path->level + 1);
- if (bpos_cmp(iter->pos, b->key.k.p) < 0) {
+ if (!bpos_cmp(iter->pos, b->key.k.p)) {
+ btree_node_unlock(path, path->level);
+ path->l[path->level].b = BTREE_ITER_NO_NODE_UP;
+ path->level++;
+ } else {
/*
* Haven't gotten to the end of the parent node: go back down to
* the next child node
btree_path_set_pos(trans, path, bpos_successor(iter->pos),
iter->flags & BTREE_ITER_INTENT);
- /* Unlock to avoid screwing up our lock invariants: */
- btree_node_unlock(path, path->level);
-
path->level = iter->min_depth;
+
+ for (l = path->level + 1; l < BTREE_MAX_DEPTH; l++)
+ if (btree_lock_want(path, l) == BTREE_NODE_UNLOCKED)
+ btree_node_unlock(path, l);
+
btree_path_set_dirty(path, BTREE_ITER_NEED_TRAVERSE);
bch2_btree_iter_verify(iter);
ret = bch2_btree_path_traverse(trans, path, iter->flags);
- if (ret) {
- b = NULL;
- goto out;
- }
+ if (ret)
+ goto err;
b = path->l[path->level].b;
}
bkey_init(&iter->k);
iter->k.p = iter->pos = b->key.k.p;
+
+ iter->path = btree_path_set_pos(trans, iter->path, b->key.k.p,
+ iter->flags & BTREE_ITER_INTENT);
iter->path->should_be_locked = true;
+ BUG_ON(iter->path->uptodate);
out:
bch2_btree_iter_verify_entry_exit(iter);
bch2_btree_iter_verify(iter);
return b;
+err:
+ b = ERR_PTR(ret);
+ goto out;
}
/* Iterate across keys (in leaf nodes only) */
inline bool bch2_btree_iter_advance(struct btree_iter *iter)
{
struct bpos pos = iter->k.p;
- bool ret = bpos_cmp(pos, SPOS_MAX) != 0;
+ bool ret = (iter->flags & BTREE_ITER_ALL_SNAPSHOTS
+ ? bpos_cmp(pos, SPOS_MAX)
+ : bkey_cmp(pos, SPOS_MAX)) != 0;
if (ret && !(iter->flags & BTREE_ITER_IS_EXTENTS))
pos = bkey_successor(iter, pos);
trans_for_each_update(trans, i)
__btree_path_put(i->path, true);
+ memset(&trans->journal_res, 0, sizeof(trans->journal_res));
trans->extra_journal_res = 0;
trans->nr_updates = 0;
trans->mem_top = 0;
#endif
}
-int bch2_trans_exit(struct btree_trans *trans)
+void bch2_trans_exit(struct btree_trans *trans)
__releases(&c->btree_trans_barrier)
{
struct btree_insert_entry *i;
trans->mem = (void *) 0x1;
trans->paths = (void *) 0x1;
-
- return trans->error ? -EIO : 0;
}
static void __maybe_unused