mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-03-31 00:00:03 +03:00
Update bcachefs sources to ad29cf999a91 bcachefs: set_btree_iter_dontneed also clears should_be_locked
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
5639fb38ca
commit
5aaa6422b6
.bcachefs_revision
libbcachefs
acl.cbackpointers.cbackpointers.hbcachefs.hbcachefs_format.hbkey.hbkey_methods.cbtree_cache.cbtree_gc.cbtree_io.cbtree_iter.hbtree_journal_iter.cbtree_key_cache.cbtree_locking.cbtree_node_scan.cbtree_trans_commit.cbtree_types.hbtree_update_interior.cbtree_update_interior.hbtree_write_buffer.cbuckets.hchardev.cchecksum.cchecksum.hcompress.hdata_update.cdebug.cec.cec.hextents.ceytzinger.ceytzinger.hfs-io-direct.cfs-io.cjournal_io.cjournal_reclaim.cjournal_types.hopts.copts.hrecovery.crecovery_passes.csb-downgrade.csb-errors_types.hsb-members.csb-members.hsnapshot.csuper-io.csuper.csuper_types.hsysfs.ctests.cutil.h
@ -1 +1 @@
|
||||
09d4c2acbf4c864fef0f520bbcba256c9a19102e
|
||||
ad29cf999a9161e7849aa229d2028854f90728c2
|
||||
|
@ -281,7 +281,6 @@ struct posix_acl *bch2_get_acl(struct mnt_idmap *idmap,
|
||||
struct xattr_search_key search = X_SEARCH(acl_to_xattr_type(type), "", 0);
|
||||
struct btree_trans *trans = bch2_trans_get(c);
|
||||
struct btree_iter iter = { NULL };
|
||||
struct bkey_s_c_xattr xattr;
|
||||
struct posix_acl *acl = NULL;
|
||||
struct bkey_s_c k;
|
||||
int ret;
|
||||
@ -290,29 +289,28 @@ retry:
|
||||
|
||||
ret = bch2_hash_lookup(trans, &iter, bch2_xattr_hash_desc,
|
||||
&hash, inode_inum(inode), &search, 0);
|
||||
if (ret) {
|
||||
if (!bch2_err_matches(ret, ENOENT))
|
||||
acl = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
k = bch2_btree_iter_peek_slot(&iter);
|
||||
ret = bkey_err(k);
|
||||
if (ret) {
|
||||
acl = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
xattr = bkey_s_c_to_xattr(k);
|
||||
struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k);
|
||||
acl = bch2_acl_from_disk(trans, xattr_val(xattr.v),
|
||||
le16_to_cpu(xattr.v->x_val_len));
|
||||
|
||||
if (!IS_ERR(acl))
|
||||
set_cached_acl(&inode->v, type, acl);
|
||||
out:
|
||||
if (bch2_err_matches(PTR_ERR_OR_ZERO(acl), BCH_ERR_transaction_restart))
|
||||
le16_to_cpu(xattr.v->x_val_len));
|
||||
ret = PTR_ERR_OR_ZERO(acl);
|
||||
err:
|
||||
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
||||
goto retry;
|
||||
|
||||
if (ret)
|
||||
acl = !bch2_err_matches(ret, ENOENT) ? ERR_PTR(ret) : NULL;
|
||||
|
||||
if (!IS_ERR_OR_NULL(acl))
|
||||
set_cached_acl(&inode->v, type, acl);
|
||||
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
bch2_trans_put(trans);
|
||||
return acl;
|
||||
|
@ -49,13 +49,15 @@ int bch2_backpointer_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
if (!bch2_dev_exists2(c, bp.k->p.inode))
|
||||
return 0;
|
||||
|
||||
struct bch_dev *ca = bch_dev_bkey_exists(c, bp.k->p.inode);
|
||||
struct bpos bucket = bp_pos_to_bucket(c, bp.k->p);
|
||||
int ret = 0;
|
||||
|
||||
bkey_fsck_err_on(!bpos_eq(bp.k->p, bucket_pos_to_bp(c, bucket, bp.v->bucket_offset)),
|
||||
bkey_fsck_err_on((bp.v->bucket_offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT) >= ca->mi.bucket_size ||
|
||||
!bpos_eq(bp.k->p, bucket_pos_to_bp(c, bucket, bp.v->bucket_offset)),
|
||||
c, err,
|
||||
backpointer_pos_wrong,
|
||||
"backpointer at wrong pos");
|
||||
backpointer_bucket_offset_wrong,
|
||||
"backpointer bucket_offset wrong");
|
||||
fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
@ -53,14 +53,11 @@ static inline struct bpos bucket_pos_to_bp(const struct bch_fs *c,
|
||||
u64 bucket_offset)
|
||||
{
|
||||
struct bch_dev *ca = bch_dev_bkey_exists(c, bucket.inode);
|
||||
struct bpos ret;
|
||||
|
||||
ret = POS(bucket.inode,
|
||||
(bucket_to_sector(ca, bucket.offset) <<
|
||||
MAX_EXTENT_COMPRESS_RATIO_SHIFT) + bucket_offset);
|
||||
struct bpos ret = POS(bucket.inode,
|
||||
(bucket_to_sector(ca, bucket.offset) <<
|
||||
MAX_EXTENT_COMPRESS_RATIO_SHIFT) + bucket_offset);
|
||||
|
||||
EBUG_ON(!bkey_eq(bucket, bp_pos_to_bucket(c, ret)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -709,6 +709,8 @@ struct btree_trans_buf {
|
||||
x(stripe_delete) \
|
||||
x(reflink) \
|
||||
x(fallocate) \
|
||||
x(fsync) \
|
||||
x(dio_write) \
|
||||
x(discard) \
|
||||
x(discard_fast) \
|
||||
x(invalidate) \
|
||||
|
@ -578,7 +578,8 @@ struct bch_member {
|
||||
__le64 nbuckets; /* device size */
|
||||
__le16 first_bucket; /* index of first bucket used */
|
||||
__le16 bucket_size; /* sectors */
|
||||
__le32 pad;
|
||||
__u8 btree_bitmap_shift;
|
||||
__u8 pad[3];
|
||||
__le64 last_mount; /* time_t */
|
||||
|
||||
__le64 flags;
|
||||
@ -587,6 +588,7 @@ struct bch_member {
|
||||
__le64 errors_at_reset[BCH_MEMBER_ERROR_NR];
|
||||
__le64 errors_reset_time;
|
||||
__le64 seq;
|
||||
__le64 btree_allocated_bitmap;
|
||||
};
|
||||
|
||||
#define BCH_MEMBER_V1_BYTES 56
|
||||
@ -876,7 +878,8 @@ struct bch_sb_field_downgrade {
|
||||
x(rebalance_work, BCH_VERSION(1, 3)) \
|
||||
x(member_seq, BCH_VERSION(1, 4)) \
|
||||
x(subvolume_fs_parent, BCH_VERSION(1, 5)) \
|
||||
x(btree_subvolume_children, BCH_VERSION(1, 6))
|
||||
x(btree_subvolume_children, BCH_VERSION(1, 6)) \
|
||||
x(mi_btree_bitmap, BCH_VERSION(1, 7))
|
||||
|
||||
enum bcachefs_metadata_version {
|
||||
bcachefs_metadata_version_min = 9,
|
||||
@ -1314,7 +1317,7 @@ static inline __u64 __bset_magic(struct bch_sb *sb)
|
||||
x(write_buffer_keys, 11) \
|
||||
x(datetime, 12)
|
||||
|
||||
enum {
|
||||
enum bch_jset_entry_type {
|
||||
#define x(f, nr) BCH_JSET_ENTRY_##f = nr,
|
||||
BCH_JSET_ENTRY_TYPES()
|
||||
#undef x
|
||||
@ -1360,7 +1363,7 @@ struct jset_entry_blacklist_v2 {
|
||||
x(inodes, 1) \
|
||||
x(key_version, 2)
|
||||
|
||||
enum {
|
||||
enum bch_fs_usage_type {
|
||||
#define x(f, nr) BCH_FS_USAGE_##f = nr,
|
||||
BCH_FS_USAGE_TYPES()
|
||||
#undef x
|
||||
@ -1535,6 +1538,20 @@ enum btree_id {
|
||||
BTREE_ID_NR
|
||||
};
|
||||
|
||||
static inline bool btree_id_is_alloc(enum btree_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case BTREE_ID_alloc:
|
||||
case BTREE_ID_backpointers:
|
||||
case BTREE_ID_need_discard:
|
||||
case BTREE_ID_freespace:
|
||||
case BTREE_ID_bucket_gens:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define BTREE_MAX_DEPTH 4U
|
||||
|
||||
/* Btree nodes */
|
||||
|
@ -314,6 +314,12 @@ static inline unsigned bkeyp_key_u64s(const struct bkey_format *format,
|
||||
return bkey_packed(k) ? format->key_u64s : BKEY_U64s;
|
||||
}
|
||||
|
||||
static inline bool bkeyp_u64s_valid(const struct bkey_format *f,
|
||||
const struct bkey_packed *k)
|
||||
{
|
||||
return ((unsigned) k->u64s - bkeyp_key_u64s(f, k) <= U8_MAX - BKEY_U64s);
|
||||
}
|
||||
|
||||
static inline unsigned bkeyp_key_bytes(const struct bkey_format *format,
|
||||
const struct bkey_packed *k)
|
||||
{
|
||||
|
@ -171,11 +171,15 @@ int __bch2_bkey_invalid(struct bch_fs *c, struct bkey_s_c k,
|
||||
if (type >= BKEY_TYPE_NR)
|
||||
return 0;
|
||||
|
||||
bkey_fsck_err_on((flags & BKEY_INVALID_COMMIT) &&
|
||||
bkey_fsck_err_on((type == BKEY_TYPE_btree ||
|
||||
(flags & BKEY_INVALID_COMMIT)) &&
|
||||
!(bch2_key_types_allowed[type] & BIT_ULL(k.k->type)), c, err,
|
||||
bkey_invalid_type_for_btree,
|
||||
"invalid key type for btree %s (%s)",
|
||||
bch2_btree_node_type_str(type), bch2_bkey_types[k.k->type]);
|
||||
bch2_btree_node_type_str(type),
|
||||
k.k->type < KEY_TYPE_MAX
|
||||
? bch2_bkey_types[k.k->type]
|
||||
: "(unknown)");
|
||||
|
||||
if (btree_node_type_is_extents(type) && !bkey_whiteout(k.k)) {
|
||||
bkey_fsck_err_on(k.k->size == 0, c, err,
|
||||
|
@ -709,9 +709,31 @@ static noinline struct btree *bch2_btree_node_fill(struct btree_trans *trans,
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_cache *bc = &c->btree_cache;
|
||||
struct btree *b;
|
||||
u32 seq;
|
||||
|
||||
BUG_ON(level + 1 >= BTREE_MAX_DEPTH);
|
||||
if (unlikely(level >= BTREE_MAX_DEPTH)) {
|
||||
int ret = bch2_fs_topology_error(c, "attempting to get btree node at level %u, >= max depth %u",
|
||||
level, BTREE_MAX_DEPTH);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (unlikely(!bkey_is_btree_ptr(&k->k))) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
|
||||
|
||||
int ret = bch2_fs_topology_error(c, "attempting to get btree node with non-btree key %s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (unlikely(k->k.u64s > BKEY_BTREE_PTR_U64s_MAX)) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
|
||||
|
||||
int ret = bch2_fs_topology_error(c, "attempting to get btree node with too big key %s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parent node must be locked, else we could read in a btree node that's
|
||||
* been freed:
|
||||
@ -752,34 +774,26 @@ static noinline struct btree *bch2_btree_node_fill(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
set_btree_node_read_in_flight(b);
|
||||
|
||||
six_unlock_write(&b->c.lock);
|
||||
seq = six_lock_seq(&b->c.lock);
|
||||
six_unlock_intent(&b->c.lock);
|
||||
|
||||
/* Unlock before doing IO: */
|
||||
if (path && sync)
|
||||
bch2_trans_unlock_noassert(trans);
|
||||
|
||||
bch2_btree_node_read(trans, b, sync);
|
||||
|
||||
if (!sync)
|
||||
return NULL;
|
||||
|
||||
if (path) {
|
||||
int ret = bch2_trans_relock(trans) ?:
|
||||
bch2_btree_path_relock_intent(trans, path);
|
||||
if (ret) {
|
||||
BUG_ON(!trans->restarted);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
u32 seq = six_lock_seq(&b->c.lock);
|
||||
|
||||
if (!six_relock_type(&b->c.lock, lock_type, seq)) {
|
||||
BUG_ON(!path);
|
||||
/* Unlock before doing IO: */
|
||||
six_unlock_intent(&b->c.lock);
|
||||
bch2_trans_unlock_noassert(trans);
|
||||
|
||||
trace_and_count(c, trans_restart_relock_after_fill, trans, _THIS_IP_, path);
|
||||
return ERR_PTR(btree_trans_restart(trans, BCH_ERR_transaction_restart_relock_after_fill));
|
||||
bch2_btree_node_read(trans, b, sync);
|
||||
|
||||
if (!sync)
|
||||
return NULL;
|
||||
|
||||
if (!six_relock_type(&b->c.lock, lock_type, seq))
|
||||
b = NULL;
|
||||
} else {
|
||||
bch2_btree_node_read(trans, b, sync);
|
||||
if (lock_type == SIX_LOCK_read)
|
||||
six_lock_downgrade(&b->c.lock);
|
||||
}
|
||||
|
||||
return b;
|
||||
@ -1112,18 +1126,19 @@ int bch2_btree_node_prefetch(struct btree_trans *trans,
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_cache *bc = &c->btree_cache;
|
||||
struct btree *b;
|
||||
|
||||
BUG_ON(path && !btree_node_locked(path, level + 1));
|
||||
BUG_ON(level >= BTREE_MAX_DEPTH);
|
||||
|
||||
b = btree_cache_find(bc, k);
|
||||
struct btree *b = btree_cache_find(bc, k);
|
||||
if (b)
|
||||
return 0;
|
||||
|
||||
b = bch2_btree_node_fill(trans, path, k, btree_id,
|
||||
level, SIX_LOCK_read, false);
|
||||
return PTR_ERR_OR_ZERO(b);
|
||||
if (!IS_ERR_OR_NULL(b))
|
||||
six_unlock_read(&b->c.lock);
|
||||
return bch2_trans_relock(trans) ?: PTR_ERR_OR_ZERO(b);
|
||||
}
|
||||
|
||||
void bch2_btree_node_evict(struct btree_trans *trans, const struct bkey_i *k)
|
||||
@ -1148,6 +1163,8 @@ wait_on_io:
|
||||
|
||||
btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_intent);
|
||||
btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_write);
|
||||
if (unlikely(b->hash_val != btree_ptr_hash_val(k)))
|
||||
goto out;
|
||||
|
||||
if (btree_node_dirty(b)) {
|
||||
__bch2_btree_node_write(c, b, BTREE_WRITE_cache_reclaim);
|
||||
@ -1162,7 +1179,7 @@ wait_on_io:
|
||||
btree_node_data_free(c, b);
|
||||
bch2_btree_node_hash_remove(bc, b);
|
||||
mutex_unlock(&bc->lock);
|
||||
|
||||
out:
|
||||
six_unlock_write(&b->c.lock);
|
||||
six_unlock_intent(&b->c.lock);
|
||||
}
|
||||
|
@ -368,11 +368,16 @@ again:
|
||||
buf.buf)) {
|
||||
bch2_btree_node_evict(trans, cur_k.k);
|
||||
cur = NULL;
|
||||
ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_scan_for_btree_nodes) ?:
|
||||
bch2_journal_key_delete(c, b->c.btree_id,
|
||||
b->c.level, cur_k.k->k.p);
|
||||
ret = bch2_journal_key_delete(c, b->c.btree_id,
|
||||
b->c.level, cur_k.k->k.p);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (!btree_id_is_alloc(b->c.btree_id)) {
|
||||
ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_scan_for_btree_nodes);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -544,12 +549,12 @@ reconstruct_root:
|
||||
bch2_btree_root_alloc_fake(c, i, 0);
|
||||
} else {
|
||||
bch2_btree_root_alloc_fake(c, i, 1);
|
||||
bch2_shoot_down_journal_keys(c, i, 1, BTREE_MAX_DEPTH, POS_MIN, SPOS_MAX);
|
||||
ret = bch2_get_scanned_nodes(c, i, 0, POS_MIN, SPOS_MAX);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
bch2_shoot_down_journal_keys(c, i, 1, BTREE_MAX_DEPTH, POS_MIN, SPOS_MAX);
|
||||
reconstructed_root = true;
|
||||
}
|
||||
|
||||
@ -823,6 +828,7 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
|
||||
struct bch_fs *c = trans->c;
|
||||
struct bkey deleted = KEY(0, 0, 0);
|
||||
struct bkey_s_c old = (struct bkey_s_c) { &deleted, NULL };
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
deleted.p = k->k->p;
|
||||
@ -843,11 +849,23 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (mustfix_fsck_err_on(level && !bch2_dev_btree_bitmap_marked(c, *k),
|
||||
c, btree_bitmap_not_marked,
|
||||
"btree ptr not marked in member info btree allocated bitmap\n %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, *k),
|
||||
buf.buf))) {
|
||||
mutex_lock(&c->sb_lock);
|
||||
bch2_dev_btree_bitmap_mark(c, *k);
|
||||
bch2_write_super(c);
|
||||
mutex_unlock(&c->sb_lock);
|
||||
}
|
||||
|
||||
ret = commit_do(trans, NULL, NULL, 0,
|
||||
bch2_key_trigger(trans, btree_id, level, old,
|
||||
unsafe_bkey_s_c_to_s(*k), BTREE_TRIGGER_GC));
|
||||
fsck_err:
|
||||
err:
|
||||
printbuf_exit(&buf);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ static int bset_key_invalid(struct bch_fs *c, struct btree *b,
|
||||
(rw == WRITE ? bch2_bkey_val_invalid(c, k, READ, err) : 0);
|
||||
}
|
||||
|
||||
static bool __bkey_valid(struct bch_fs *c, struct btree *b,
|
||||
static bool bkey_packed_valid(struct bch_fs *c, struct btree *b,
|
||||
struct bset *i, struct bkey_packed *k)
|
||||
{
|
||||
if (bkey_p_next(k) > vstruct_last(i))
|
||||
@ -840,7 +840,7 @@ static bool __bkey_valid(struct bch_fs *c, struct btree *b,
|
||||
if (k->format > KEY_FORMAT_CURRENT)
|
||||
return false;
|
||||
|
||||
if (k->u64s < bkeyp_key_u64s(&b->format, k))
|
||||
if (!bkeyp_u64s_valid(&b->format, k))
|
||||
return false;
|
||||
|
||||
struct printbuf buf = PRINTBUF;
|
||||
@ -884,11 +884,13 @@ static int validate_bset_keys(struct bch_fs *c, struct btree *b,
|
||||
"invalid bkey format %u", k->format))
|
||||
goto drop_this_key;
|
||||
|
||||
if (btree_err_on(k->u64s < bkeyp_key_u64s(&b->format, k),
|
||||
if (btree_err_on(!bkeyp_u64s_valid(&b->format, k),
|
||||
-BCH_ERR_btree_node_read_err_fixable,
|
||||
c, NULL, b, i,
|
||||
btree_node_bkey_bad_u64s,
|
||||
"k->u64s too small (%u < %u)", k->u64s, bkeyp_key_u64s(&b->format, k)))
|
||||
"bad k->u64s %u (min %u max %lu)", k->u64s,
|
||||
bkeyp_key_u64s(&b->format, k),
|
||||
U8_MAX - BKEY_U64s + bkeyp_key_u64s(&b->format, k)))
|
||||
goto drop_this_key;
|
||||
|
||||
if (!write)
|
||||
@ -947,13 +949,12 @@ drop_this_key:
|
||||
* do
|
||||
*/
|
||||
|
||||
if (!__bkey_valid(c, b, i, (void *) ((u64 *) k + next_good_key))) {
|
||||
if (!bkey_packed_valid(c, b, i, (void *) ((u64 *) k + next_good_key))) {
|
||||
for (next_good_key = 1;
|
||||
next_good_key < (u64 *) vstruct_last(i) - (u64 *) k;
|
||||
next_good_key++)
|
||||
if (__bkey_valid(c, b, i, (void *) ((u64 *) k + next_good_key)))
|
||||
if (bkey_packed_valid(c, b, i, (void *) ((u64 *) k + next_good_key)))
|
||||
goto got_good_key;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1339,7 +1340,9 @@ start:
|
||||
rb->start_time);
|
||||
bio_put(&rb->bio);
|
||||
|
||||
if (saw_error && !btree_node_read_error(b)) {
|
||||
if (saw_error &&
|
||||
!btree_node_read_error(b) &&
|
||||
c->curr_recovery_pass != BCH_RECOVERY_PASS_scan_for_btree_nodes) {
|
||||
printbuf_reset(&buf);
|
||||
bch2_bpos_to_text(&buf, b->key.k.p);
|
||||
bch_err_ratelimited(c, "%s: rewriting btree node at btree=%s level=%u %s due to error",
|
||||
|
@ -498,8 +498,13 @@ static inline void set_btree_iter_dontneed(struct btree_iter *iter)
|
||||
{
|
||||
struct btree_trans *trans = iter->trans;
|
||||
|
||||
if (!trans->restarted)
|
||||
btree_iter_path(trans, iter)->preserve = false;
|
||||
if (!iter->path || trans->restarted)
|
||||
return;
|
||||
|
||||
struct btree_path *path = btree_iter_path(trans, iter);
|
||||
path->preserve = false;
|
||||
if (path->ref == 1)
|
||||
path->should_be_locked = false;
|
||||
}
|
||||
|
||||
void *__bch2_trans_kmalloc(struct btree_trans *, size_t);
|
||||
@ -642,7 +647,7 @@ int __bch2_btree_trans_too_many_iters(struct btree_trans *);
|
||||
|
||||
static inline int btree_trans_too_many_iters(struct btree_trans *trans)
|
||||
{
|
||||
if (bitmap_weight(trans->paths_allocated, trans->nr_paths) > BTREE_ITER_INITIAL - 8)
|
||||
if (bitmap_weight(trans->paths_allocated, trans->nr_paths) > BTREE_ITER_NORMAL_LIMIT - 8)
|
||||
return __bch2_btree_trans_too_many_iters(trans);
|
||||
|
||||
return 0;
|
||||
|
@ -130,12 +130,30 @@ struct bkey_i *bch2_journal_keys_peek_slot(struct bch_fs *c, enum btree_id btree
|
||||
return bch2_journal_keys_peek_upto(c, btree_id, level, pos, pos, &idx);
|
||||
}
|
||||
|
||||
static void journal_iter_verify(struct journal_iter *iter)
|
||||
{
|
||||
struct journal_keys *keys = iter->keys;
|
||||
size_t gap_size = keys->size - keys->nr;
|
||||
|
||||
BUG_ON(iter->idx >= keys->gap &&
|
||||
iter->idx < keys->gap + gap_size);
|
||||
|
||||
if (iter->idx < keys->size) {
|
||||
struct journal_key *k = keys->data + iter->idx;
|
||||
|
||||
int cmp = cmp_int(k->btree_id, iter->btree_id) ?:
|
||||
cmp_int(k->level, iter->level);
|
||||
BUG_ON(cmp < 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void journal_iters_fix(struct bch_fs *c)
|
||||
{
|
||||
struct journal_keys *keys = &c->journal_keys;
|
||||
/* The key we just inserted is immediately before the gap: */
|
||||
size_t gap_end = keys->gap + (keys->size - keys->nr);
|
||||
struct btree_and_journal_iter *iter;
|
||||
struct journal_key *new_key = &keys->data[keys->gap - 1];
|
||||
struct journal_iter *iter;
|
||||
|
||||
/*
|
||||
* If an iterator points one after the key we just inserted, decrement
|
||||
@ -143,9 +161,14 @@ static void journal_iters_fix(struct bch_fs *c)
|
||||
* decrement was unnecessary, bch2_btree_and_journal_iter_peek() will
|
||||
* handle that:
|
||||
*/
|
||||
list_for_each_entry(iter, &c->journal_iters, journal.list)
|
||||
if (iter->journal.idx == gap_end)
|
||||
iter->journal.idx = keys->gap - 1;
|
||||
list_for_each_entry(iter, &c->journal_iters, list) {
|
||||
journal_iter_verify(iter);
|
||||
if (iter->idx == gap_end &&
|
||||
new_key->btree_id == iter->btree_id &&
|
||||
new_key->level == iter->level)
|
||||
iter->idx = keys->gap - 1;
|
||||
journal_iter_verify(iter);
|
||||
}
|
||||
}
|
||||
|
||||
static void journal_iters_move_gap(struct bch_fs *c, size_t old_gap, size_t new_gap)
|
||||
@ -192,7 +215,12 @@ int bch2_journal_key_insert_take(struct bch_fs *c, enum btree_id id,
|
||||
if (idx > keys->gap)
|
||||
idx -= keys->size - keys->nr;
|
||||
|
||||
size_t old_gap = keys->gap;
|
||||
|
||||
if (keys->nr == keys->size) {
|
||||
journal_iters_move_gap(c, old_gap, keys->size);
|
||||
old_gap = keys->size;
|
||||
|
||||
struct journal_keys new_keys = {
|
||||
.nr = keys->nr,
|
||||
.size = max_t(size_t, keys->size, 8) * 2,
|
||||
@ -216,7 +244,7 @@ int bch2_journal_key_insert_take(struct bch_fs *c, enum btree_id id,
|
||||
keys->gap = keys->nr;
|
||||
}
|
||||
|
||||
journal_iters_move_gap(c, keys->gap, idx);
|
||||
journal_iters_move_gap(c, old_gap, idx);
|
||||
|
||||
move_gap(keys, idx);
|
||||
|
||||
@ -301,16 +329,21 @@ static void bch2_journal_iter_advance(struct journal_iter *iter)
|
||||
|
||||
static struct bkey_s_c bch2_journal_iter_peek(struct journal_iter *iter)
|
||||
{
|
||||
struct journal_key *k = iter->keys->data + iter->idx;
|
||||
journal_iter_verify(iter);
|
||||
|
||||
while (iter->idx < iter->keys->size) {
|
||||
struct journal_key *k = iter->keys->data + iter->idx;
|
||||
|
||||
int cmp = cmp_int(k->btree_id, iter->btree_id) ?:
|
||||
cmp_int(k->level, iter->level);
|
||||
if (cmp > 0)
|
||||
break;
|
||||
BUG_ON(cmp);
|
||||
|
||||
while (k < iter->keys->data + iter->keys->size &&
|
||||
k->btree_id == iter->btree_id &&
|
||||
k->level == iter->level) {
|
||||
if (!k->overwritten)
|
||||
return bkey_i_to_s_c(k->k);
|
||||
|
||||
bch2_journal_iter_advance(iter);
|
||||
k = iter->keys->data + iter->idx;
|
||||
}
|
||||
|
||||
return bkey_s_c_null;
|
||||
@ -330,6 +363,8 @@ static void bch2_journal_iter_init(struct bch_fs *c,
|
||||
iter->level = level;
|
||||
iter->keys = &c->journal_keys;
|
||||
iter->idx = bch2_journal_key_search(&c->journal_keys, id, level, pos);
|
||||
|
||||
journal_iter_verify(iter);
|
||||
}
|
||||
|
||||
static struct bkey_s_c bch2_journal_iter_peek_btree(struct btree_and_journal_iter *iter)
|
||||
@ -434,10 +469,15 @@ void __bch2_btree_and_journal_iter_init_node_iter(struct btree_trans *trans,
|
||||
iter->trans = trans;
|
||||
iter->b = b;
|
||||
iter->node_iter = node_iter;
|
||||
bch2_journal_iter_init(trans->c, &iter->journal, b->c.btree_id, b->c.level, pos);
|
||||
INIT_LIST_HEAD(&iter->journal.list);
|
||||
iter->pos = b->data->min_key;
|
||||
iter->at_end = false;
|
||||
INIT_LIST_HEAD(&iter->journal.list);
|
||||
|
||||
if (trans->journal_replay_not_finished) {
|
||||
bch2_journal_iter_init(trans->c, &iter->journal, b->c.btree_id, b->c.level, pos);
|
||||
if (!test_bit(BCH_FS_may_go_rw, &trans->c->flags))
|
||||
list_add(&iter->journal.list, &trans->c->journal_iters);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -452,9 +492,6 @@ void bch2_btree_and_journal_iter_init_node_iter(struct btree_trans *trans,
|
||||
|
||||
bch2_btree_node_iter_init_from_start(&node_iter, b);
|
||||
__bch2_btree_and_journal_iter_init_node_iter(trans, iter, b, node_iter, b->data->min_key);
|
||||
if (trans->journal_replay_not_finished &&
|
||||
!test_bit(BCH_FS_may_go_rw, &trans->c->flags))
|
||||
list_add(&iter->journal.list, &trans->c->journal_iters);
|
||||
}
|
||||
|
||||
/* sort and dedup all keys in the journal: */
|
||||
|
@ -169,6 +169,7 @@ static void bkey_cached_move_to_freelist(struct btree_key_cache *bc,
|
||||
} else {
|
||||
mutex_lock(&bc->lock);
|
||||
list_move_tail(&ck->list, &bc->freed_pcpu);
|
||||
bc->nr_freed_pcpu++;
|
||||
mutex_unlock(&bc->lock);
|
||||
}
|
||||
}
|
||||
@ -245,6 +246,7 @@ bkey_cached_alloc(struct btree_trans *trans, struct btree_path *path,
|
||||
if (!list_empty(&bc->freed_pcpu)) {
|
||||
ck = list_last_entry(&bc->freed_pcpu, struct bkey_cached, list);
|
||||
list_del_init(&ck->list);
|
||||
bc->nr_freed_pcpu--;
|
||||
}
|
||||
mutex_unlock(&bc->lock);
|
||||
}
|
||||
@ -659,7 +661,7 @@ static int btree_key_cache_flush_pos(struct btree_trans *trans,
|
||||
commit_flags |= BCH_WATERMARK_reclaim;
|
||||
|
||||
if (ck->journal.seq != journal_last_seq(j) ||
|
||||
j->watermark == BCH_WATERMARK_stripe)
|
||||
!test_bit(JOURNAL_SPACE_LOW, &c->journal.flags))
|
||||
commit_flags |= BCH_TRANS_COMMIT_no_journal_res;
|
||||
|
||||
ret = bch2_btree_iter_traverse(&b_iter) ?:
|
||||
|
@ -440,33 +440,7 @@ void bch2_btree_node_lock_write_nofail(struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
struct btree_bkey_cached_common *b)
|
||||
{
|
||||
struct btree_path *linked;
|
||||
unsigned i, iter;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* XXX BIG FAT NOTICE
|
||||
*
|
||||
* Drop all read locks before taking a write lock:
|
||||
*
|
||||
* This is a hack, because bch2_btree_node_lock_write_nofail() is a
|
||||
* hack - but by dropping read locks first, this should never fail, and
|
||||
* we only use this in code paths where whatever read locks we've
|
||||
* already taken are no longer needed:
|
||||
*/
|
||||
|
||||
trans_for_each_path(trans, linked, iter) {
|
||||
if (!linked->nodes_locked)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < BTREE_MAX_DEPTH; i++)
|
||||
if (btree_node_read_locked(linked, i)) {
|
||||
btree_node_unlock(trans, linked, i);
|
||||
btree_path_set_dirty(linked, BTREE_ITER_NEED_RELOCK);
|
||||
}
|
||||
}
|
||||
|
||||
ret = __btree_node_lock_write(trans, path, b, true);
|
||||
int ret = __btree_node_lock_write(trans, path, b, true);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,19 @@ static void try_read_btree_node(struct find_btree_nodes *f, struct bch_dev *ca,
|
||||
if (le64_to_cpu(bn->magic) != bset_magic(c))
|
||||
return;
|
||||
|
||||
if (bch2_csum_type_is_encryption(BSET_CSUM_TYPE(&bn->keys))) {
|
||||
struct nonce nonce = btree_nonce(&bn->keys, 0);
|
||||
unsigned bytes = (void *) &bn->keys - (void *) &bn->flags;
|
||||
|
||||
bch2_encrypt(c, BSET_CSUM_TYPE(&bn->keys), nonce, &bn->flags, bytes);
|
||||
}
|
||||
|
||||
if (btree_id_is_alloc(BTREE_NODE_ID(bn)))
|
||||
return;
|
||||
|
||||
if (BTREE_NODE_LEVEL(bn) >= BTREE_MAX_DEPTH)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
struct found_btree_node n = {
|
||||
.btree_id = BTREE_NODE_ID(bn),
|
||||
@ -192,8 +205,13 @@ static int read_btree_nodes_worker(void *p)
|
||||
last_print = jiffies;
|
||||
}
|
||||
|
||||
try_read_btree_node(w->f, ca, bio, buf,
|
||||
bucket * ca->mi.bucket_size + bucket_offset);
|
||||
u64 sector = bucket * ca->mi.bucket_size + bucket_offset;
|
||||
|
||||
if (c->sb.version_upgrade_complete >= bcachefs_metadata_version_mi_btree_bitmap &&
|
||||
!bch2_dev_btree_bitmap_marked_sectors(ca, sector, btree_sectors(c)))
|
||||
continue;
|
||||
|
||||
try_read_btree_node(w->f, ca, bio, buf, sector);
|
||||
}
|
||||
err:
|
||||
bio_put(bio);
|
||||
@ -213,6 +231,9 @@ static int read_btree_nodes(struct find_btree_nodes *f)
|
||||
closure_init_stack(&cl);
|
||||
|
||||
for_each_online_member(c, ca) {
|
||||
if (!(ca->mi.data_allowed & BIT(BCH_DATA_btree)))
|
||||
continue;
|
||||
|
||||
struct find_btree_nodes_worker *w = kmalloc(sizeof(*w), GFP_KERNEL);
|
||||
struct task_struct *t;
|
||||
|
||||
@ -290,7 +311,7 @@ again:
|
||||
found_btree_node_to_text(&buf, c, n);
|
||||
bch_err(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return -1;
|
||||
return -BCH_ERR_fsck_repair_unimplemented;
|
||||
}
|
||||
}
|
||||
|
||||
@ -436,6 +457,9 @@ bool bch2_btree_has_scanned_nodes(struct bch_fs *c, enum btree_id btree)
|
||||
int bch2_get_scanned_nodes(struct bch_fs *c, enum btree_id btree,
|
||||
unsigned level, struct bpos node_min, struct bpos node_max)
|
||||
{
|
||||
if (btree_id_is_alloc(btree))
|
||||
return 0;
|
||||
|
||||
struct find_btree_nodes *f = &c->found_btree_nodes;
|
||||
|
||||
int ret = bch2_run_explicit_recovery_pass(c, BCH_RECOVERY_PASS_scan_for_btree_nodes);
|
||||
|
@ -397,12 +397,13 @@ static int btree_key_can_insert_cached(struct btree_trans *trans, unsigned flags
|
||||
struct bkey_cached *ck = (void *) path->l[0].b;
|
||||
unsigned new_u64s;
|
||||
struct bkey_i *new_k;
|
||||
unsigned watermark = flags & BCH_WATERMARK_MASK;
|
||||
|
||||
EBUG_ON(path->level);
|
||||
|
||||
if (!test_bit(BKEY_CACHED_DIRTY, &ck->flags) &&
|
||||
bch2_btree_key_cache_must_wait(c) &&
|
||||
!(flags & BCH_TRANS_COMMIT_journal_reclaim))
|
||||
if (watermark < BCH_WATERMARK_reclaim &&
|
||||
!test_bit(BKEY_CACHED_DIRTY, &ck->flags) &&
|
||||
bch2_btree_key_cache_must_wait(c))
|
||||
return -BCH_ERR_btree_insert_need_journal_reclaim;
|
||||
|
||||
/*
|
||||
@ -499,9 +500,8 @@ static int run_one_trans_trigger(struct btree_trans *trans, struct btree_insert_
|
||||
}
|
||||
|
||||
static int run_btree_triggers(struct btree_trans *trans, enum btree_id btree_id,
|
||||
struct btree_insert_entry *btree_id_start)
|
||||
unsigned btree_id_start)
|
||||
{
|
||||
struct btree_insert_entry *i;
|
||||
bool trans_trigger_run;
|
||||
int ret, overwrite;
|
||||
|
||||
@ -514,13 +514,13 @@ static int run_btree_triggers(struct btree_trans *trans, enum btree_id btree_id,
|
||||
do {
|
||||
trans_trigger_run = false;
|
||||
|
||||
for (i = btree_id_start;
|
||||
i < trans->updates + trans->nr_updates && i->btree_id <= btree_id;
|
||||
for (unsigned i = btree_id_start;
|
||||
i < trans->nr_updates && trans->updates[i].btree_id <= btree_id;
|
||||
i++) {
|
||||
if (i->btree_id != btree_id)
|
||||
if (trans->updates[i].btree_id != btree_id)
|
||||
continue;
|
||||
|
||||
ret = run_one_trans_trigger(trans, i, overwrite);
|
||||
ret = run_one_trans_trigger(trans, trans->updates + i, overwrite);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret)
|
||||
@ -534,8 +534,7 @@ static int run_btree_triggers(struct btree_trans *trans, enum btree_id btree_id,
|
||||
|
||||
static int bch2_trans_commit_run_triggers(struct btree_trans *trans)
|
||||
{
|
||||
struct btree_insert_entry *btree_id_start = trans->updates;
|
||||
unsigned btree_id = 0;
|
||||
unsigned btree_id = 0, btree_id_start = 0;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
@ -549,8 +548,8 @@ static int bch2_trans_commit_run_triggers(struct btree_trans *trans)
|
||||
if (btree_id == BTREE_ID_alloc)
|
||||
continue;
|
||||
|
||||
while (btree_id_start < trans->updates + trans->nr_updates &&
|
||||
btree_id_start->btree_id < btree_id)
|
||||
while (btree_id_start < trans->nr_updates &&
|
||||
trans->updates[btree_id_start].btree_id < btree_id)
|
||||
btree_id_start++;
|
||||
|
||||
ret = run_btree_triggers(trans, btree_id, btree_id_start);
|
||||
@ -558,11 +557,13 @@ static int bch2_trans_commit_run_triggers(struct btree_trans *trans)
|
||||
return ret;
|
||||
}
|
||||
|
||||
trans_for_each_update(trans, i) {
|
||||
for (unsigned idx = 0; idx < trans->nr_updates; idx++) {
|
||||
struct btree_insert_entry *i = trans->updates + idx;
|
||||
|
||||
if (i->btree_id > BTREE_ID_alloc)
|
||||
break;
|
||||
if (i->btree_id == BTREE_ID_alloc) {
|
||||
ret = run_btree_triggers(trans, BTREE_ID_alloc, i);
|
||||
ret = run_btree_triggers(trans, BTREE_ID_alloc, idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
@ -826,7 +827,8 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, unsigned flags
|
||||
struct bch_fs *c = trans->c;
|
||||
int ret = 0, u64s_delta = 0;
|
||||
|
||||
trans_for_each_update(trans, i) {
|
||||
for (unsigned idx = 0; idx < trans->nr_updates; idx++) {
|
||||
struct btree_insert_entry *i = trans->updates + idx;
|
||||
if (i->cached)
|
||||
continue;
|
||||
|
||||
|
@ -364,7 +364,21 @@ struct btree_insert_entry {
|
||||
unsigned long ip_allocated;
|
||||
};
|
||||
|
||||
/* Number of btree paths we preallocate, usually enough */
|
||||
#define BTREE_ITER_INITIAL 64
|
||||
/*
|
||||
* Lmiit for btree_trans_too_many_iters(); this is enough that almost all code
|
||||
* paths should run inside this limit, and if they don't it usually indicates a
|
||||
* bug (leaking/duplicated btree paths).
|
||||
*
|
||||
* exception: some fsck paths
|
||||
*
|
||||
* bugs with excessive path usage seem to have possibly been eliminated now, so
|
||||
* we might consider eliminating this (and btree_trans_too_many_iter()) at some
|
||||
* point.
|
||||
*/
|
||||
#define BTREE_ITER_NORMAL_LIMIT 256
|
||||
/* never exceed limit */
|
||||
#define BTREE_ITER_MAX (1U << 10)
|
||||
|
||||
struct btree_trans_commit_hook;
|
||||
|
@ -21,14 +21,15 @@
|
||||
#include "keylist.h"
|
||||
#include "recovery_passes.h"
|
||||
#include "replicas.h"
|
||||
#include "sb-members.h"
|
||||
#include "super-io.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <linux/random.h>
|
||||
|
||||
const char * const bch2_btree_update_modes[] = {
|
||||
static const char * const bch2_btree_update_modes[] = {
|
||||
#define x(t) #t,
|
||||
BCH_WATERMARKS()
|
||||
BTREE_UPDATE_MODES()
|
||||
#undef x
|
||||
NULL
|
||||
};
|
||||
@ -605,6 +606,26 @@ static void btree_update_add_key(struct btree_update *as,
|
||||
bch2_keylist_push(keys);
|
||||
}
|
||||
|
||||
static bool btree_update_new_nodes_marked_sb(struct btree_update *as)
|
||||
{
|
||||
for_each_keylist_key(&as->new_keys, k)
|
||||
if (!bch2_dev_btree_bitmap_marked(as->c, bkey_i_to_s_c(k)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void btree_update_new_nodes_mark_sb(struct btree_update *as)
|
||||
{
|
||||
struct bch_fs *c = as->c;
|
||||
|
||||
mutex_lock(&c->sb_lock);
|
||||
for_each_keylist_key(&as->new_keys, k)
|
||||
bch2_dev_btree_bitmap_mark(c, bkey_i_to_s_c(k));
|
||||
|
||||
bch2_write_super(c);
|
||||
mutex_unlock(&c->sb_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The transactional part of an interior btree node update, where we journal the
|
||||
* update we did to the interior node and update alloc info:
|
||||
@ -662,6 +683,9 @@ static void btree_update_nodes_written(struct btree_update *as)
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!btree_update_new_nodes_marked_sb(as))
|
||||
btree_update_new_nodes_mark_sb(as);
|
||||
|
||||
/*
|
||||
* Wait for any in flight writes to finish before we free the old nodes
|
||||
* on disk:
|
||||
@ -704,9 +728,13 @@ static void btree_update_nodes_written(struct btree_update *as)
|
||||
bch2_fs_fatal_err_on(ret && !bch2_journal_error(&c->journal), c,
|
||||
"%s", bch2_err_str(ret));
|
||||
err:
|
||||
if (as->b) {
|
||||
|
||||
b = as->b;
|
||||
/*
|
||||
* We have to be careful because another thread might be getting ready
|
||||
* to free as->b and calling btree_update_reparent() on us - we'll
|
||||
* recheck under btree_update_lock below:
|
||||
*/
|
||||
b = READ_ONCE(as->b);
|
||||
if (b) {
|
||||
btree_path_idx_t path_idx = get_unlocked_mut_path(trans,
|
||||
as->btree_id, b->c.level, b->key.k.p);
|
||||
struct btree_path *path = trans->paths + path_idx;
|
||||
@ -850,15 +878,17 @@ static void btree_update_updated_node(struct btree_update *as, struct btree *b)
|
||||
{
|
||||
struct bch_fs *c = as->c;
|
||||
|
||||
mutex_lock(&c->btree_interior_update_lock);
|
||||
list_add_tail(&as->unwritten_list, &c->btree_interior_updates_unwritten);
|
||||
|
||||
BUG_ON(as->mode != BTREE_UPDATE_none);
|
||||
BUG_ON(as->update_level_end < b->c.level);
|
||||
BUG_ON(!btree_node_dirty(b));
|
||||
BUG_ON(!b->c.level);
|
||||
|
||||
mutex_lock(&c->btree_interior_update_lock);
|
||||
list_add_tail(&as->unwritten_list, &c->btree_interior_updates_unwritten);
|
||||
|
||||
as->mode = BTREE_UPDATE_node;
|
||||
as->b = b;
|
||||
as->update_level_end = b->c.level;
|
||||
|
||||
set_btree_node_write_blocked(b);
|
||||
list_add(&as->write_blocked_list, &b->write_blocked);
|
||||
@ -1100,7 +1130,7 @@ static void bch2_btree_update_done(struct btree_update *as, struct btree_trans *
|
||||
|
||||
static struct btree_update *
|
||||
bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
|
||||
unsigned level, bool split, unsigned flags)
|
||||
unsigned level_start, bool split, unsigned flags)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_update *as;
|
||||
@ -1108,7 +1138,7 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
|
||||
int disk_res_flags = (flags & BCH_TRANS_COMMIT_no_enospc)
|
||||
? BCH_DISK_RESERVATION_NOFAIL : 0;
|
||||
unsigned nr_nodes[2] = { 0, 0 };
|
||||
unsigned update_level = level;
|
||||
unsigned level_end = level_start;
|
||||
enum bch_watermark watermark = flags & BCH_WATERMARK_MASK;
|
||||
int ret = 0;
|
||||
u32 restart_count = trans->restart_count;
|
||||
@ -1123,34 +1153,30 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
|
||||
flags &= ~BCH_WATERMARK_MASK;
|
||||
flags |= watermark;
|
||||
|
||||
if (watermark < c->journal.watermark) {
|
||||
struct journal_res res = { 0 };
|
||||
unsigned journal_flags = watermark|JOURNAL_RES_GET_CHECK;
|
||||
if (watermark < BCH_WATERMARK_reclaim &&
|
||||
test_bit(JOURNAL_SPACE_LOW, &c->journal.flags)) {
|
||||
if (flags & BCH_TRANS_COMMIT_journal_reclaim)
|
||||
return ERR_PTR(-BCH_ERR_journal_reclaim_would_deadlock);
|
||||
|
||||
if ((flags & BCH_TRANS_COMMIT_journal_reclaim) &&
|
||||
watermark < BCH_WATERMARK_reclaim)
|
||||
journal_flags |= JOURNAL_RES_GET_NONBLOCK;
|
||||
|
||||
ret = drop_locks_do(trans,
|
||||
bch2_journal_res_get(&c->journal, &res, 1, journal_flags));
|
||||
if (bch2_err_matches(ret, BCH_ERR_operation_blocked))
|
||||
ret = -BCH_ERR_journal_reclaim_would_deadlock;
|
||||
bch2_trans_unlock(trans);
|
||||
wait_event(c->journal.wait, !test_bit(JOURNAL_SPACE_LOW, &c->journal.flags));
|
||||
ret = bch2_trans_relock(trans);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
nr_nodes[!!update_level] += 1 + split;
|
||||
update_level++;
|
||||
nr_nodes[!!level_end] += 1 + split;
|
||||
level_end++;
|
||||
|
||||
ret = bch2_btree_path_upgrade(trans, path, update_level + 1);
|
||||
ret = bch2_btree_path_upgrade(trans, path, level_end + 1);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!btree_path_node(path, update_level)) {
|
||||
if (!btree_path_node(path, level_end)) {
|
||||
/* Allocating new root? */
|
||||
nr_nodes[1] += split;
|
||||
update_level = BTREE_MAX_DEPTH;
|
||||
level_end = BTREE_MAX_DEPTH;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1158,11 +1184,11 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
|
||||
* Always check for space for two keys, even if we won't have to
|
||||
* split at prior level - it might have been a merge instead:
|
||||
*/
|
||||
if (bch2_btree_node_insert_fits(path->l[update_level].b,
|
||||
if (bch2_btree_node_insert_fits(path->l[level_end].b,
|
||||
BKEY_BTREE_PTR_U64s_MAX * 2))
|
||||
break;
|
||||
|
||||
split = path->l[update_level].b->nr.live_u64s > BTREE_SPLIT_THRESHOLD(c);
|
||||
split = path->l[level_end].b->nr.live_u64s > BTREE_SPLIT_THRESHOLD(c);
|
||||
}
|
||||
|
||||
if (!down_read_trylock(&c->gc_lock)) {
|
||||
@ -1176,14 +1202,15 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
|
||||
as = mempool_alloc(&c->btree_interior_update_pool, GFP_NOFS);
|
||||
memset(as, 0, sizeof(*as));
|
||||
closure_init(&as->cl, NULL);
|
||||
as->c = c;
|
||||
as->start_time = start_time;
|
||||
as->ip_started = _RET_IP_;
|
||||
as->mode = BTREE_UPDATE_none;
|
||||
as->watermark = watermark;
|
||||
as->took_gc_lock = true;
|
||||
as->btree_id = path->btree_id;
|
||||
as->update_level = update_level;
|
||||
as->c = c;
|
||||
as->start_time = start_time;
|
||||
as->ip_started = _RET_IP_;
|
||||
as->mode = BTREE_UPDATE_none;
|
||||
as->watermark = watermark;
|
||||
as->took_gc_lock = true;
|
||||
as->btree_id = path->btree_id;
|
||||
as->update_level_start = level_start;
|
||||
as->update_level_end = level_end;
|
||||
INIT_LIST_HEAD(&as->list);
|
||||
INIT_LIST_HEAD(&as->unwritten_list);
|
||||
INIT_LIST_HEAD(&as->write_blocked_list);
|
||||
@ -1277,23 +1304,29 @@ static void bch2_btree_set_root_inmem(struct bch_fs *c, struct btree *b)
|
||||
bch2_recalc_btree_reserve(c);
|
||||
}
|
||||
|
||||
static void bch2_btree_set_root(struct btree_update *as,
|
||||
struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
struct btree *b)
|
||||
static int bch2_btree_set_root(struct btree_update *as,
|
||||
struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
struct btree *b,
|
||||
bool nofail)
|
||||
{
|
||||
struct bch_fs *c = as->c;
|
||||
struct btree *old;
|
||||
|
||||
trace_and_count(c, btree_node_set_root, trans, b);
|
||||
|
||||
old = btree_node_root(c, b);
|
||||
struct btree *old = btree_node_root(c, b);
|
||||
|
||||
/*
|
||||
* Ensure no one is using the old root while we switch to the
|
||||
* new root:
|
||||
*/
|
||||
bch2_btree_node_lock_write_nofail(trans, path, &old->c);
|
||||
if (nofail) {
|
||||
bch2_btree_node_lock_write_nofail(trans, path, &old->c);
|
||||
} else {
|
||||
int ret = bch2_btree_node_lock_write(trans, path, &old->c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bch2_btree_set_root_inmem(c, b);
|
||||
|
||||
@ -1307,6 +1340,7 @@ static void bch2_btree_set_root(struct btree_update *as,
|
||||
* depend on the new root would have to update the new root.
|
||||
*/
|
||||
bch2_btree_node_unlock_write(trans, path, old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Interior node updates: */
|
||||
@ -1373,12 +1407,12 @@ static void bch2_insert_fixup_btree_ptr(struct btree_update *as,
|
||||
}
|
||||
|
||||
static void
|
||||
__bch2_btree_insert_keys_interior(struct btree_update *as,
|
||||
struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
struct btree *b,
|
||||
struct btree_node_iter node_iter,
|
||||
struct keylist *keys)
|
||||
bch2_btree_insert_keys_interior(struct btree_update *as,
|
||||
struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
struct btree *b,
|
||||
struct btree_node_iter node_iter,
|
||||
struct keylist *keys)
|
||||
{
|
||||
struct bkey_i *insert = bch2_keylist_front(keys);
|
||||
struct bkey_packed *k;
|
||||
@ -1534,7 +1568,7 @@ static void btree_split_insert_keys(struct btree_update *as,
|
||||
|
||||
bch2_btree_node_iter_init(&node_iter, b, &bch2_keylist_front(keys)->k.p);
|
||||
|
||||
__bch2_btree_insert_keys_interior(as, trans, path, b, node_iter, keys);
|
||||
bch2_btree_insert_keys_interior(as, trans, path, b, node_iter, keys);
|
||||
|
||||
BUG_ON(bch2_btree_node_check_topology(trans, b));
|
||||
}
|
||||
@ -1649,15 +1683,16 @@ static int btree_split(struct btree_update *as, struct btree_trans *trans,
|
||||
if (parent) {
|
||||
/* Split a non root node */
|
||||
ret = bch2_btree_insert_node(as, trans, path, parent, &as->parent_keys);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else if (n3) {
|
||||
bch2_btree_set_root(as, trans, trans->paths + path, n3);
|
||||
ret = bch2_btree_set_root(as, trans, trans->paths + path, n3, false);
|
||||
} else {
|
||||
/* Root filled up but didn't need to be split */
|
||||
bch2_btree_set_root(as, trans, trans->paths + path, n1);
|
||||
ret = bch2_btree_set_root(as, trans, trans->paths + path, n1, false);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (n3) {
|
||||
bch2_btree_update_get_open_buckets(as, n3);
|
||||
bch2_btree_node_write(c, n3, SIX_LOCK_intent, 0);
|
||||
@ -1714,27 +1749,6 @@ err:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void
|
||||
bch2_btree_insert_keys_interior(struct btree_update *as,
|
||||
struct btree_trans *trans,
|
||||
struct btree_path *path,
|
||||
struct btree *b,
|
||||
struct keylist *keys)
|
||||
{
|
||||
struct btree_path *linked;
|
||||
unsigned i;
|
||||
|
||||
__bch2_btree_insert_keys_interior(as, trans, path, b,
|
||||
path->l[b->c.level].iter, keys);
|
||||
|
||||
btree_update_updated_node(as, b);
|
||||
|
||||
trans_for_each_path_with_node(trans, b, linked, i)
|
||||
bch2_btree_node_iter_peek(&linked->l[b->c.level].iter, b);
|
||||
|
||||
bch2_trans_verify_paths(trans);
|
||||
}
|
||||
|
||||
/**
|
||||
* bch2_btree_insert_node - insert bkeys into a given btree node
|
||||
*
|
||||
@ -1755,7 +1769,8 @@ static int bch2_btree_insert_node(struct btree_update *as, struct btree_trans *t
|
||||
struct keylist *keys)
|
||||
{
|
||||
struct bch_fs *c = as->c;
|
||||
struct btree_path *path = trans->paths + path_idx;
|
||||
struct btree_path *path = trans->paths + path_idx, *linked;
|
||||
unsigned i;
|
||||
int old_u64s = le16_to_cpu(btree_bset_last(b)->u64s);
|
||||
int old_live_u64s = b->nr.live_u64s;
|
||||
int live_u64s_added, u64s_added;
|
||||
@ -1784,7 +1799,13 @@ static int bch2_btree_insert_node(struct btree_update *as, struct btree_trans *t
|
||||
return ret;
|
||||
}
|
||||
|
||||
bch2_btree_insert_keys_interior(as, trans, path, b, keys);
|
||||
bch2_btree_insert_keys_interior(as, trans, path, b,
|
||||
path->l[b->c.level].iter, keys);
|
||||
|
||||
trans_for_each_path_with_node(trans, b, linked, i)
|
||||
bch2_btree_node_iter_peek(&linked->l[b->c.level].iter, b);
|
||||
|
||||
bch2_trans_verify_paths(trans);
|
||||
|
||||
live_u64s_added = (int) b->nr.live_u64s - old_live_u64s;
|
||||
u64s_added = (int) le16_to_cpu(btree_bset_last(b)->u64s) - old_u64s;
|
||||
@ -1798,6 +1819,7 @@ static int bch2_btree_insert_node(struct btree_update *as, struct btree_trans *t
|
||||
bch2_maybe_compact_whiteouts(c, b))
|
||||
bch2_trans_node_reinit_iter(trans, b);
|
||||
|
||||
btree_update_updated_node(as, b);
|
||||
bch2_btree_node_unlock_write(trans, path, b);
|
||||
|
||||
BUG_ON(bch2_btree_node_check_topology(trans, b));
|
||||
@ -1807,7 +1829,7 @@ split:
|
||||
* We could attempt to avoid the transaction restart, by calling
|
||||
* bch2_btree_path_upgrade() and allocating more nodes:
|
||||
*/
|
||||
if (b->c.level >= as->update_level) {
|
||||
if (b->c.level >= as->update_level_end) {
|
||||
trace_and_count(c, trans_restart_split_race, trans, _THIS_IP_, b);
|
||||
return btree_trans_restart(trans, BCH_ERR_transaction_restart_split_race);
|
||||
}
|
||||
@ -1873,7 +1895,9 @@ static void __btree_increase_depth(struct btree_update *as, struct btree_trans *
|
||||
bch2_keylist_add(&as->parent_keys, &b->key);
|
||||
btree_split_insert_keys(as, trans, path_idx, n, &as->parent_keys);
|
||||
|
||||
bch2_btree_set_root(as, trans, path, n);
|
||||
int ret = bch2_btree_set_root(as, trans, path, n, true);
|
||||
BUG_ON(ret);
|
||||
|
||||
bch2_btree_update_get_open_buckets(as, n);
|
||||
bch2_btree_node_write(c, n, SIX_LOCK_intent, 0);
|
||||
bch2_trans_node_add(trans, path, n);
|
||||
@ -1926,6 +1950,18 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
|
||||
BUG_ON(!trans->paths[path].should_be_locked);
|
||||
BUG_ON(!btree_node_locked(&trans->paths[path], level));
|
||||
|
||||
/*
|
||||
* Work around a deadlock caused by the btree write buffer not doing
|
||||
* merges and leaving tons of merges for us to do - we really don't need
|
||||
* to be doing merges at all from the interior update path, and if the
|
||||
* interior update path is generating too many new interior updates we
|
||||
* deadlock:
|
||||
*/
|
||||
if ((flags & BCH_WATERMARK_MASK) == BCH_WATERMARK_interior_updates)
|
||||
return 0;
|
||||
|
||||
flags &= ~BCH_WATERMARK_MASK;
|
||||
|
||||
b = trans->paths[path].l[level].b;
|
||||
|
||||
if ((sib == btree_prev_sib && bpos_eq(b->data->min_key, POS_MIN)) ||
|
||||
@ -2071,6 +2107,10 @@ err:
|
||||
bch2_path_put(trans, new_path, true);
|
||||
bch2_path_put(trans, sib_path, true);
|
||||
bch2_trans_verify_locks(trans);
|
||||
if (ret == -BCH_ERR_journal_reclaim_would_deadlock)
|
||||
ret = 0;
|
||||
if (!ret)
|
||||
ret = bch2_trans_relock(trans);
|
||||
return ret;
|
||||
err_free_update:
|
||||
bch2_btree_node_free_never_used(as, trans, n);
|
||||
@ -2116,12 +2156,13 @@ int bch2_btree_node_rewrite(struct btree_trans *trans,
|
||||
if (parent) {
|
||||
bch2_keylist_add(&as->parent_keys, &n->key);
|
||||
ret = bch2_btree_insert_node(as, trans, iter->path, parent, &as->parent_keys);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
bch2_btree_set_root(as, trans, btree_iter_path(trans, iter), n);
|
||||
ret = bch2_btree_set_root(as, trans, btree_iter_path(trans, iter), n, false);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
bch2_btree_update_get_open_buckets(as, n);
|
||||
bch2_btree_node_write(c, n, SIX_LOCK_intent, 0);
|
||||
|
||||
@ -2519,9 +2560,11 @@ void bch2_btree_root_alloc_fake(struct bch_fs *c, enum btree_id id, unsigned lev
|
||||
|
||||
static void bch2_btree_update_to_text(struct printbuf *out, struct btree_update *as)
|
||||
{
|
||||
prt_printf(out, "%ps: btree=%s watermark=%s mode=%s nodes_written=%u cl.remaining=%u journal_seq=%llu\n",
|
||||
prt_printf(out, "%ps: btree=%s l=%u-%u watermark=%s mode=%s nodes_written=%u cl.remaining=%u journal_seq=%llu\n",
|
||||
(void *) as->ip_started,
|
||||
bch2_btree_id_str(as->btree_id),
|
||||
as->update_level_start,
|
||||
as->update_level_end,
|
||||
bch2_watermarks[as->watermark],
|
||||
bch2_btree_update_modes[as->mode],
|
||||
as->nodes_written,
|
||||
|
@ -57,7 +57,8 @@ struct btree_update {
|
||||
unsigned took_gc_lock:1;
|
||||
|
||||
enum btree_id btree_id;
|
||||
unsigned update_level;
|
||||
unsigned update_level_start;
|
||||
unsigned update_level_end;
|
||||
|
||||
struct disk_reservation disk_res;
|
||||
|
||||
|
@ -316,6 +316,16 @@ static int bch2_btree_write_buffer_flush_locked(struct btree_trans *trans)
|
||||
bpos_gt(k->k.k.p, path->l[0].b->key.k.p)) {
|
||||
bch2_btree_node_unlock_write(trans, path, path->l[0].b);
|
||||
write_locked = false;
|
||||
|
||||
ret = lockrestart_do(trans,
|
||||
bch2_btree_iter_traverse(&iter) ?:
|
||||
bch2_foreground_maybe_merge(trans, iter.path, 0,
|
||||
BCH_WATERMARK_reclaim|
|
||||
BCH_TRANS_COMMIT_journal_reclaim|
|
||||
BCH_TRANS_COMMIT_no_check_rw|
|
||||
BCH_TRANS_COMMIT_no_enospc));
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,10 +392,10 @@ static int bch2_btree_write_buffer_flush_locked(struct btree_trans *trans)
|
||||
|
||||
ret = commit_do(trans, NULL, NULL,
|
||||
BCH_WATERMARK_reclaim|
|
||||
BCH_TRANS_COMMIT_journal_reclaim|
|
||||
BCH_TRANS_COMMIT_no_check_rw|
|
||||
BCH_TRANS_COMMIT_no_enospc|
|
||||
BCH_TRANS_COMMIT_no_journal_res|
|
||||
BCH_TRANS_COMMIT_journal_reclaim,
|
||||
BCH_TRANS_COMMIT_no_journal_res ,
|
||||
btree_write_buffered_insert(trans, i));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -395,14 +395,6 @@ static inline const char *bch2_data_type_str(enum bch_data_type type)
|
||||
: "(invalid data type)";
|
||||
}
|
||||
|
||||
static inline void bch2_prt_data_type(struct printbuf *out, enum bch_data_type type)
|
||||
{
|
||||
if (type < BCH_DATA_NR)
|
||||
prt_str(out, __bch2_data_types[type]);
|
||||
else
|
||||
prt_printf(out, "(invalid data type %u)", type);
|
||||
}
|
||||
|
||||
/* disk reservations: */
|
||||
|
||||
static inline void bch2_disk_reservation_put(struct bch_fs *c,
|
||||
|
@ -134,42 +134,38 @@ static long bch2_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg
|
||||
struct fsck_thread {
|
||||
struct thread_with_stdio thr;
|
||||
struct bch_fs *c;
|
||||
char **devs;
|
||||
size_t nr_devs;
|
||||
struct bch_opts opts;
|
||||
};
|
||||
|
||||
static void bch2_fsck_thread_exit(struct thread_with_stdio *_thr)
|
||||
{
|
||||
struct fsck_thread *thr = container_of(_thr, struct fsck_thread, thr);
|
||||
if (thr->devs)
|
||||
for (size_t i = 0; i < thr->nr_devs; i++)
|
||||
kfree(thr->devs[i]);
|
||||
kfree(thr->devs);
|
||||
kfree(thr);
|
||||
}
|
||||
|
||||
static int bch2_fsck_offline_thread_fn(struct thread_with_stdio *stdio)
|
||||
{
|
||||
struct fsck_thread *thr = container_of(stdio, struct fsck_thread, thr);
|
||||
struct bch_fs *c = bch2_fs_open(thr->devs, thr->nr_devs, thr->opts);
|
||||
struct bch_fs *c = thr->c;
|
||||
|
||||
if (IS_ERR(c))
|
||||
return PTR_ERR(c);
|
||||
int ret = PTR_ERR_OR_ZERO(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
int ret = 0;
|
||||
if (test_bit(BCH_FS_errors_fixed, &c->flags))
|
||||
ret |= 1;
|
||||
if (test_bit(BCH_FS_error, &c->flags))
|
||||
ret |= 4;
|
||||
ret = bch2_fs_start(thr->c);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
bch2_fs_stop(c);
|
||||
|
||||
if (ret & 1)
|
||||
if (test_bit(BCH_FS_errors_fixed, &c->flags)) {
|
||||
bch2_stdio_redirect_printf(&stdio->stdio, false, "%s: errors fixed\n", c->name);
|
||||
if (ret & 4)
|
||||
ret |= 1;
|
||||
}
|
||||
if (test_bit(BCH_FS_error, &c->flags)) {
|
||||
bch2_stdio_redirect_printf(&stdio->stdio, false, "%s: still has errors\n", c->name);
|
||||
|
||||
ret |= 4;
|
||||
}
|
||||
err:
|
||||
bch2_fs_stop(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -182,7 +178,7 @@ static long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_a
|
||||
{
|
||||
struct bch_ioctl_fsck_offline arg;
|
||||
struct fsck_thread *thr = NULL;
|
||||
u64 *devs = NULL;
|
||||
darray_str(devs) = {};
|
||||
long ret = 0;
|
||||
|
||||
if (copy_from_user(&arg, user_arg, sizeof(arg)))
|
||||
@ -194,28 +190,31 @@ static long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_a
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!(devs = kcalloc(arg.nr_devs, sizeof(*devs), GFP_KERNEL)) ||
|
||||
!(thr = kzalloc(sizeof(*thr), GFP_KERNEL)) ||
|
||||
!(thr->devs = kcalloc(arg.nr_devs, sizeof(*thr->devs), GFP_KERNEL))) {
|
||||
for (size_t i = 0; i < arg.nr_devs; i++) {
|
||||
u64 dev_u64;
|
||||
ret = copy_from_user_errcode(&dev_u64, &user_arg->devs[i], sizeof(u64));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
char *dev_str = strndup_user((char __user *)(unsigned long) dev_u64, PATH_MAX);
|
||||
ret = PTR_ERR_OR_ZERO(dev_str);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = darray_push(&devs, dev_str);
|
||||
if (ret) {
|
||||
kfree(dev_str);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
thr = kzalloc(sizeof(*thr), GFP_KERNEL);
|
||||
if (!thr) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
thr->opts = bch2_opts_empty();
|
||||
thr->nr_devs = arg.nr_devs;
|
||||
|
||||
if (copy_from_user(devs, &user_arg->devs[0],
|
||||
array_size(sizeof(user_arg->devs[0]), arg.nr_devs))) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < arg.nr_devs; i++) {
|
||||
thr->devs[i] = strndup_user((char __user *)(unsigned long) devs[i], PATH_MAX);
|
||||
ret = PTR_ERR_OR_ZERO(thr->devs[i]);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (arg.opts) {
|
||||
char *optstr = strndup_user((char __user *)(unsigned long) arg.opts, 1 << 16);
|
||||
@ -230,15 +229,26 @@ static long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_a
|
||||
|
||||
opt_set(thr->opts, stdio, (u64)(unsigned long)&thr->thr.stdio);
|
||||
|
||||
/* We need request_key() to be called before we punt to kthread: */
|
||||
opt_set(thr->opts, nostart, true);
|
||||
|
||||
thr->c = bch2_fs_open(devs.data, arg.nr_devs, thr->opts);
|
||||
|
||||
if (!IS_ERR(thr->c) &&
|
||||
thr->c->opts.errors == BCH_ON_ERROR_panic)
|
||||
thr->c->opts.errors = BCH_ON_ERROR_ro;
|
||||
|
||||
ret = bch2_run_thread_with_stdio(&thr->thr, &bch2_offline_fsck_ops);
|
||||
err:
|
||||
if (ret < 0) {
|
||||
if (thr)
|
||||
bch2_fsck_thread_exit(&thr->thr);
|
||||
pr_err("ret %s", bch2_err_str(ret));
|
||||
}
|
||||
kfree(devs);
|
||||
out:
|
||||
darray_for_each(devs, i)
|
||||
kfree(*i);
|
||||
darray_exit(&devs);
|
||||
return ret;
|
||||
err:
|
||||
if (thr)
|
||||
bch2_fsck_thread_exit(&thr->thr);
|
||||
pr_err("ret %s", bch2_err_str(ret));
|
||||
goto out;
|
||||
}
|
||||
|
||||
static long bch2_global_ioctl(unsigned cmd, void __user *arg)
|
||||
|
@ -429,15 +429,20 @@ int bch2_rechecksum_bio(struct bch_fs *c, struct bio *bio,
|
||||
extent_nonce(version, crc_old), bio);
|
||||
|
||||
if (bch2_crc_cmp(merged, crc_old.csum) && !c->opts.no_data_io) {
|
||||
bch_err(c, "checksum error in %s() (memory corruption or bug?)\n"
|
||||
"expected %0llx:%0llx got %0llx:%0llx (old type %s new type %s)",
|
||||
__func__,
|
||||
crc_old.csum.hi,
|
||||
crc_old.csum.lo,
|
||||
merged.hi,
|
||||
merged.lo,
|
||||
bch2_csum_types[crc_old.csum_type],
|
||||
bch2_csum_types[new_csum_type]);
|
||||
struct printbuf buf = PRINTBUF;
|
||||
prt_printf(&buf, "checksum error in %s() (memory corruption or bug?)\n"
|
||||
"expected %0llx:%0llx got %0llx:%0llx (old type ",
|
||||
__func__,
|
||||
crc_old.csum.hi,
|
||||
crc_old.csum.lo,
|
||||
merged.hi,
|
||||
merged.lo);
|
||||
bch2_prt_csum_type(&buf, crc_old.csum_type);
|
||||
prt_str(&buf, " new type ");
|
||||
bch2_prt_csum_type(&buf, new_csum_type);
|
||||
prt_str(&buf, ")");
|
||||
bch_err(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -61,11 +61,12 @@ static inline void bch2_csum_err_msg(struct printbuf *out,
|
||||
struct bch_csum expected,
|
||||
struct bch_csum got)
|
||||
{
|
||||
prt_printf(out, "checksum error: got ");
|
||||
prt_str(out, "checksum error, type ");
|
||||
bch2_prt_csum_type(out, type);
|
||||
prt_str(out, ": got ");
|
||||
bch2_csum_to_text(out, type, got);
|
||||
prt_str(out, " should be ");
|
||||
bch2_csum_to_text(out, type, expected);
|
||||
prt_printf(out, " type %s", bch2_csum_types[type]);
|
||||
}
|
||||
|
||||
int bch2_chacha_encrypt_key(struct bch_key *, struct nonce, void *, size_t);
|
||||
|
@ -47,14 +47,6 @@ static inline enum bch_compression_type bch2_compression_opt_to_type(unsigned v)
|
||||
return __bch2_compression_opt_to_type[bch2_compression_decode(v).type];
|
||||
}
|
||||
|
||||
static inline void bch2_prt_compression_type(struct printbuf *out, enum bch_compression_type type)
|
||||
{
|
||||
if (type < BCH_COMPRESSION_TYPE_NR)
|
||||
prt_str(out, __bch2_compression_types[type]);
|
||||
else
|
||||
prt_printf(out, "(invalid compression type %u)", type);
|
||||
}
|
||||
|
||||
int bch2_bio_uncompress_inplace(struct bch_fs *, struct bio *,
|
||||
struct bch_extent_crc_unpacked *);
|
||||
int bch2_bio_uncompress(struct bch_fs *, struct bio *, struct bio *,
|
||||
|
@ -598,6 +598,8 @@ int bch2_data_update_init(struct btree_trans *trans,
|
||||
i++;
|
||||
}
|
||||
|
||||
unsigned durability_required = max(0, (int) (io_opts.data_replicas - durability_have));
|
||||
|
||||
/*
|
||||
* If current extent durability is less than io_opts.data_replicas,
|
||||
* we're not trying to rereplicate the extent up to data_replicas here -
|
||||
@ -607,7 +609,7 @@ int bch2_data_update_init(struct btree_trans *trans,
|
||||
* rereplicate, currently, so that users don't get an unexpected -ENOSPC
|
||||
*/
|
||||
if (!(m->data_opts.write_flags & BCH_WRITE_CACHED) &&
|
||||
durability_have >= io_opts.data_replicas) {
|
||||
!durability_required) {
|
||||
m->data_opts.kill_ptrs |= m->data_opts.rewrite_ptrs;
|
||||
m->data_opts.rewrite_ptrs = 0;
|
||||
/* if iter == NULL, it's just a promote */
|
||||
@ -616,11 +618,18 @@ int bch2_data_update_init(struct btree_trans *trans,
|
||||
goto done;
|
||||
}
|
||||
|
||||
m->op.nr_replicas = min(durability_removing, io_opts.data_replicas - durability_have) +
|
||||
m->op.nr_replicas = min(durability_removing, durability_required) +
|
||||
m->data_opts.extra_replicas;
|
||||
m->op.nr_replicas_required = m->op.nr_replicas;
|
||||
|
||||
BUG_ON(!m->op.nr_replicas);
|
||||
/*
|
||||
* If device(s) were set to durability=0 after data was written to them
|
||||
* we can end up with a duribilty=0 extent, and the normal algorithm
|
||||
* that tries not to increase durability doesn't work:
|
||||
*/
|
||||
if (!(durability_have + durability_removing))
|
||||
m->op.nr_replicas = max((unsigned) m->op.nr_replicas, 1);
|
||||
|
||||
m->op.nr_replicas_required = m->op.nr_replicas;
|
||||
|
||||
if (reserve_sectors) {
|
||||
ret = bch2_disk_reservation_add(c, &m->op.res, reserve_sectors,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "btree_iter.h"
|
||||
#include "btree_locking.h"
|
||||
#include "btree_update.h"
|
||||
#include "btree_update_interior.h"
|
||||
#include "buckets.h"
|
||||
#include "debug.h"
|
||||
#include "error.h"
|
||||
@ -668,7 +669,7 @@ static ssize_t bch2_journal_pins_read(struct file *file, char __user *buf,
|
||||
i->size = size;
|
||||
i->ret = 0;
|
||||
|
||||
do {
|
||||
while (1) {
|
||||
err = flush_buf(i);
|
||||
if (err)
|
||||
return err;
|
||||
@ -676,9 +677,12 @@ static ssize_t bch2_journal_pins_read(struct file *file, char __user *buf,
|
||||
if (!i->size)
|
||||
break;
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
done = bch2_journal_seq_pins_to_text(&i->buf, &c->journal, &i->iter);
|
||||
i->iter++;
|
||||
} while (!done);
|
||||
}
|
||||
|
||||
if (i->buf.allocation_failure)
|
||||
return -ENOMEM;
|
||||
@ -693,13 +697,45 @@ static const struct file_operations journal_pins_ops = {
|
||||
.read = bch2_journal_pins_read,
|
||||
};
|
||||
|
||||
static ssize_t bch2_btree_updates_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *ppos)
|
||||
{
|
||||
struct dump_iter *i = file->private_data;
|
||||
struct bch_fs *c = i->c;
|
||||
int err;
|
||||
|
||||
i->ubuf = buf;
|
||||
i->size = size;
|
||||
i->ret = 0;
|
||||
|
||||
if (!i->iter) {
|
||||
bch2_btree_updates_to_text(&i->buf, c);
|
||||
i->iter++;
|
||||
}
|
||||
|
||||
err = flush_buf(i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (i->buf.allocation_failure)
|
||||
return -ENOMEM;
|
||||
|
||||
return i->ret;
|
||||
}
|
||||
|
||||
static const struct file_operations btree_updates_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = bch2_dump_open,
|
||||
.release = bch2_dump_release,
|
||||
.read = bch2_btree_updates_read,
|
||||
};
|
||||
|
||||
static int btree_transaction_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct bch_fs *c = inode->i_private;
|
||||
struct dump_iter *i;
|
||||
|
||||
i = kzalloc(sizeof(struct dump_iter), GFP_KERNEL);
|
||||
|
||||
if (!i)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -866,6 +902,20 @@ void bch2_fs_debug_exit(struct bch_fs *c)
|
||||
debugfs_remove_recursive(c->fs_debug_dir);
|
||||
}
|
||||
|
||||
static void bch2_fs_debug_btree_init(struct bch_fs *c, struct btree_debug *bd)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
||||
d = debugfs_create_dir(bch2_btree_id_str(bd->id), c->btree_debug_dir);
|
||||
|
||||
debugfs_create_file("keys", 0400, d, bd, &btree_debug_ops);
|
||||
|
||||
debugfs_create_file("formats", 0400, d, bd, &btree_format_debug_ops);
|
||||
|
||||
debugfs_create_file("bfloat-failed", 0400, d, bd,
|
||||
&bfloat_failed_debug_ops);
|
||||
}
|
||||
|
||||
void bch2_fs_debug_init(struct bch_fs *c)
|
||||
{
|
||||
struct btree_debug *bd;
|
||||
@ -888,6 +938,9 @@ void bch2_fs_debug_init(struct bch_fs *c)
|
||||
debugfs_create_file("journal_pins", 0400, c->fs_debug_dir,
|
||||
c->btree_debug, &journal_pins_ops);
|
||||
|
||||
debugfs_create_file("btree_updates", 0400, c->fs_debug_dir,
|
||||
c->btree_debug, &btree_updates_ops);
|
||||
|
||||
debugfs_create_file("btree_transaction_stats", 0400, c->fs_debug_dir,
|
||||
c, &btree_transaction_stats_op);
|
||||
|
||||
@ -902,21 +955,7 @@ void bch2_fs_debug_init(struct bch_fs *c)
|
||||
bd < c->btree_debug + ARRAY_SIZE(c->btree_debug);
|
||||
bd++) {
|
||||
bd->id = bd - c->btree_debug;
|
||||
debugfs_create_file(bch2_btree_id_str(bd->id),
|
||||
0400, c->btree_debug_dir, bd,
|
||||
&btree_debug_ops);
|
||||
|
||||
snprintf(name, sizeof(name), "%s-formats",
|
||||
bch2_btree_id_str(bd->id));
|
||||
|
||||
debugfs_create_file(name, 0400, c->btree_debug_dir, bd,
|
||||
&btree_format_debug_ops);
|
||||
|
||||
snprintf(name, sizeof(name), "%s-bfloat-failed",
|
||||
bch2_btree_id_str(bd->id));
|
||||
|
||||
debugfs_create_file(name, 0400, c->btree_debug_dir, bd,
|
||||
&bfloat_failed_debug_ops);
|
||||
bch2_fs_debug_btree_init(c, bd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,29 +131,33 @@ fsck_err:
|
||||
void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
struct bkey_s_c k)
|
||||
{
|
||||
const struct bch_stripe *s = bkey_s_c_to_stripe(k).v;
|
||||
unsigned i, nr_data = s->nr_blocks - s->nr_redundant;
|
||||
const struct bch_stripe *sp = bkey_s_c_to_stripe(k).v;
|
||||
struct bch_stripe s = {};
|
||||
|
||||
prt_printf(out, "algo %u sectors %u blocks %u:%u csum %u gran %u",
|
||||
s->algorithm,
|
||||
le16_to_cpu(s->sectors),
|
||||
nr_data,
|
||||
s->nr_redundant,
|
||||
s->csum_type,
|
||||
1U << s->csum_granularity_bits);
|
||||
memcpy(&s, sp, min(sizeof(s), bkey_val_bytes(k.k)));
|
||||
|
||||
for (i = 0; i < s->nr_blocks; i++) {
|
||||
const struct bch_extent_ptr *ptr = s->ptrs + i;
|
||||
struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
|
||||
u32 offset;
|
||||
u64 b = sector_to_bucket_and_offset(ca, ptr->offset, &offset);
|
||||
unsigned nr_data = s.nr_blocks - s.nr_redundant;
|
||||
|
||||
prt_printf(out, " %u:%llu:%u", ptr->dev, b, offset);
|
||||
if (i < nr_data)
|
||||
prt_printf(out, "#%u", stripe_blockcount_get(s, i));
|
||||
prt_printf(out, " gen %u", ptr->gen);
|
||||
if (ptr_stale(ca, ptr))
|
||||
prt_printf(out, " stale");
|
||||
prt_printf(out, "algo %u sectors %u blocks %u:%u csum ",
|
||||
s.algorithm,
|
||||
le16_to_cpu(s.sectors),
|
||||
nr_data,
|
||||
s.nr_redundant);
|
||||
bch2_prt_csum_type(out, s.csum_type);
|
||||
prt_printf(out, " gran %u", 1U << s.csum_granularity_bits);
|
||||
|
||||
for (unsigned i = 0; i < s.nr_blocks; i++) {
|
||||
const struct bch_extent_ptr *ptr = sp->ptrs + i;
|
||||
|
||||
if ((void *) ptr >= bkey_val_end(k))
|
||||
break;
|
||||
|
||||
bch2_extent_ptr_to_text(out, c, ptr);
|
||||
|
||||
if (s.csum_type < BCH_CSUM_NR &&
|
||||
i < nr_data &&
|
||||
stripe_blockcount_offset(&s, i) < bkey_val_bytes(k.k))
|
||||
prt_printf(out, "#%u", stripe_blockcount_get(sp, i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -607,10 +611,8 @@ static void ec_validate_checksums(struct bch_fs *c, struct ec_stripe_buf *buf)
|
||||
struct printbuf err = PRINTBUF;
|
||||
struct bch_dev *ca = bch_dev_bkey_exists(c, v->ptrs[i].dev);
|
||||
|
||||
prt_printf(&err, "stripe checksum error: expected %0llx:%0llx got %0llx:%0llx (type %s)\n",
|
||||
want.hi, want.lo,
|
||||
got.hi, got.lo,
|
||||
bch2_csum_types[v->csum_type]);
|
||||
prt_str(&err, "stripe ");
|
||||
bch2_csum_err_msg(&err, v->csum_type, want, got);
|
||||
prt_printf(&err, " for %ps at %u of\n ", (void *) _RET_IP_, i);
|
||||
bch2_bkey_val_to_text(&err, c, bkey_i_to_s_c(&buf->key));
|
||||
bch_err_ratelimited(ca, "%s", err.buf);
|
||||
|
@ -32,6 +32,8 @@ static inline unsigned stripe_csums_per_device(const struct bch_stripe *s)
|
||||
static inline unsigned stripe_csum_offset(const struct bch_stripe *s,
|
||||
unsigned dev, unsigned csum_idx)
|
||||
{
|
||||
EBUG_ON(s->csum_type >= BCH_CSUM_NR);
|
||||
|
||||
unsigned csum_bytes = bch_crc_bytes[s->csum_type];
|
||||
|
||||
return sizeof(struct bch_stripe) +
|
||||
|
@ -998,7 +998,9 @@ void bch2_extent_ptr_to_text(struct printbuf *out, struct bch_fs *c, const struc
|
||||
prt_str(out, " cached");
|
||||
if (ptr->unwritten)
|
||||
prt_str(out, " unwritten");
|
||||
if (ca && ptr_stale(ca, ptr))
|
||||
if (b >= ca->mi.first_bucket &&
|
||||
b < ca->mi.nbuckets &&
|
||||
ptr_stale(ca, ptr))
|
||||
prt_printf(out, " stale");
|
||||
}
|
||||
}
|
||||
@ -1028,11 +1030,12 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
struct bch_extent_crc_unpacked crc =
|
||||
bch2_extent_crc_unpack(k.k, entry_to_crc(entry));
|
||||
|
||||
prt_printf(out, "crc: c_size %u size %u offset %u nonce %u csum %s compress ",
|
||||
prt_printf(out, "crc: c_size %u size %u offset %u nonce %u csum ",
|
||||
crc.compressed_size,
|
||||
crc.uncompressed_size,
|
||||
crc.offset, crc.nonce,
|
||||
bch2_csum_types[crc.csum_type]);
|
||||
crc.offset, crc.nonce);
|
||||
bch2_prt_csum_type(out, crc.csum_type);
|
||||
prt_str(out, " compress ");
|
||||
bch2_prt_compression_type(out, crc.compression_type);
|
||||
break;
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ static void swap_bytes(void *a, void *b, size_t n)
|
||||
|
||||
struct wrapper {
|
||||
cmp_func_t cmp;
|
||||
swap_func_t swap;
|
||||
swap_func_t swap_func;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -125,7 +125,7 @@ struct wrapper {
|
||||
static void do_swap(void *a, void *b, size_t size, swap_r_func_t swap_func, const void *priv)
|
||||
{
|
||||
if (swap_func == SWAP_WRAPPER) {
|
||||
((const struct wrapper *)priv)->swap(a, b, (int)size);
|
||||
((const struct wrapper *)priv)->swap_func(a, b, (int)size);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ void eytzinger0_sort_r(void *base, size_t n, size_t size,
|
||||
int i, c, r;
|
||||
|
||||
/* called from 'sort' without swap function, let's pick the default */
|
||||
if (swap_func == SWAP_WRAPPER && !((struct wrapper *)priv)->swap)
|
||||
if (swap_func == SWAP_WRAPPER && !((struct wrapper *)priv)->swap_func)
|
||||
swap_func = NULL;
|
||||
|
||||
if (!swap_func) {
|
||||
@ -227,7 +227,7 @@ void eytzinger0_sort(void *base, size_t n, size_t size,
|
||||
{
|
||||
struct wrapper w = {
|
||||
.cmp = cmp_func,
|
||||
.swap = swap_func,
|
||||
.swap_func = swap_func,
|
||||
};
|
||||
|
||||
return eytzinger0_sort_r(base, n, size, _CMP_WRAPPER, SWAP_WRAPPER, &w);
|
||||
|
@ -242,8 +242,8 @@ static inline unsigned inorder_to_eytzinger0(unsigned i, unsigned size)
|
||||
(_i) = eytzinger0_next((_i), (_size)))
|
||||
|
||||
/* return greatest node <= @search, or -1 if not found */
|
||||
static inline ssize_t eytzinger0_find_le(void *base, size_t nr, size_t size,
|
||||
cmp_func_t cmp, const void *search)
|
||||
static inline int eytzinger0_find_le(void *base, size_t nr, size_t size,
|
||||
cmp_func_t cmp, const void *search)
|
||||
{
|
||||
unsigned i, n = 0;
|
||||
|
||||
@ -256,18 +256,32 @@ static inline ssize_t eytzinger0_find_le(void *base, size_t nr, size_t size,
|
||||
} while (n < nr);
|
||||
|
||||
if (n & 1) {
|
||||
/* @i was greater than @search, return previous node: */
|
||||
/*
|
||||
* @i was greater than @search, return previous node:
|
||||
*
|
||||
* if @i was leftmost/smallest element,
|
||||
* eytzinger0_prev(eytzinger0_first())) returns -1, as expected
|
||||
*/
|
||||
return eytzinger0_prev(i, nr);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
static inline ssize_t eytzinger0_find_gt(void *base, size_t nr, size_t size,
|
||||
cmp_func_t cmp, const void *search)
|
||||
static inline int eytzinger0_find_gt(void *base, size_t nr, size_t size,
|
||||
cmp_func_t cmp, const void *search)
|
||||
{
|
||||
ssize_t idx = eytzinger0_find_le(base, nr, size, cmp, search);
|
||||
return eytzinger0_next(idx, size);
|
||||
|
||||
/*
|
||||
* if eytitzinger0_find_le() returned -1 - no element was <= search - we
|
||||
* want to return the first element; next/prev identities mean this work
|
||||
* as expected
|
||||
*
|
||||
* similarly if find_le() returns last element, we should return -1;
|
||||
* identities mean this all works out:
|
||||
*/
|
||||
return eytzinger0_next(idx, nr);
|
||||
}
|
||||
|
||||
#define eytzinger0_find(base, nr, size, _cmp, search) \
|
||||
|
@ -387,6 +387,8 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio)
|
||||
ret = dio->op.error ?: ((long) dio->written << 9);
|
||||
bio_put(&dio->op.wbio.bio);
|
||||
|
||||
bch2_write_ref_put(dio->op.c, BCH_WRITE_REF_dio_write);
|
||||
|
||||
/* inode->i_dio_count is our ref on inode and thus bch_fs */
|
||||
inode_dio_end(&inode->v);
|
||||
|
||||
@ -590,22 +592,25 @@ ssize_t bch2_direct_write(struct kiocb *req, struct iov_iter *iter)
|
||||
prefetch(&inode->ei_inode);
|
||||
prefetch((void *) &inode->ei_inode + 64);
|
||||
|
||||
if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_dio_write))
|
||||
return -EROFS;
|
||||
|
||||
inode_lock(&inode->v);
|
||||
|
||||
ret = generic_write_checks(req, iter);
|
||||
if (unlikely(ret <= 0))
|
||||
goto err;
|
||||
goto err_put_write_ref;
|
||||
|
||||
ret = file_remove_privs(file);
|
||||
if (unlikely(ret))
|
||||
goto err;
|
||||
goto err_put_write_ref;
|
||||
|
||||
ret = file_update_time(file);
|
||||
if (unlikely(ret))
|
||||
goto err;
|
||||
goto err_put_write_ref;
|
||||
|
||||
if (unlikely((req->ki_pos|iter->count) & (block_bytes(c) - 1)))
|
||||
goto err;
|
||||
goto err_put_write_ref;
|
||||
|
||||
inode_dio_begin(&inode->v);
|
||||
bch2_pagecache_block_get(inode);
|
||||
@ -645,7 +650,7 @@ ssize_t bch2_direct_write(struct kiocb *req, struct iov_iter *iter)
|
||||
}
|
||||
|
||||
ret = bch2_dio_write_loop(dio);
|
||||
err:
|
||||
out:
|
||||
if (locked)
|
||||
inode_unlock(&inode->v);
|
||||
return ret;
|
||||
@ -653,7 +658,9 @@ err_put_bio:
|
||||
bch2_pagecache_block_put(inode);
|
||||
bio_put(bio);
|
||||
inode_dio_end(&inode->v);
|
||||
goto err;
|
||||
err_put_write_ref:
|
||||
bch2_write_ref_put(c, BCH_WRITE_REF_dio_write);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void bch2_fs_fs_io_direct_exit(struct bch_fs *c)
|
||||
|
@ -174,18 +174,18 @@ void __bch2_i_sectors_acct(struct bch_fs *c, struct bch_inode_info *inode,
|
||||
static int bch2_flush_inode(struct bch_fs *c,
|
||||
struct bch_inode_info *inode)
|
||||
{
|
||||
struct bch_inode_unpacked u;
|
||||
int ret;
|
||||
|
||||
if (c->opts.journal_flush_disabled)
|
||||
return 0;
|
||||
|
||||
ret = bch2_inode_find_by_inum(c, inode_inum(inode), &u);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_fsync))
|
||||
return -EROFS;
|
||||
|
||||
return bch2_journal_flush_seq(&c->journal, u.bi_journal_seq) ?:
|
||||
bch2_inode_flush_nocow_writes(c, inode);
|
||||
struct bch_inode_unpacked u;
|
||||
int ret = bch2_inode_find_by_inum(c, inode_inum(inode), &u) ?:
|
||||
bch2_journal_flush_seq(&c->journal, u.bi_journal_seq) ?:
|
||||
bch2_inode_flush_nocow_writes(c, inode);
|
||||
bch2_write_ref_put(c, BCH_WRITE_REF_fsync);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
|
@ -247,7 +247,7 @@ static void journal_entry_err_msg(struct printbuf *out,
|
||||
|
||||
if (entry) {
|
||||
prt_str(out, " type=");
|
||||
prt_str(out, bch2_jset_entry_types[entry->type]);
|
||||
bch2_prt_jset_entry_type(out, entry->type);
|
||||
}
|
||||
|
||||
if (!jset) {
|
||||
@ -403,7 +403,8 @@ static void journal_entry_btree_keys_to_text(struct printbuf *out, struct bch_fs
|
||||
jset_entry_for_each_key(entry, k) {
|
||||
if (!first) {
|
||||
prt_newline(out);
|
||||
prt_printf(out, "%s: ", bch2_jset_entry_types[entry->type]);
|
||||
bch2_prt_jset_entry_type(out, entry->type);
|
||||
prt_str(out, ": ");
|
||||
}
|
||||
prt_printf(out, "btree=%s l=%u ", bch2_btree_id_str(entry->btree_id), entry->level);
|
||||
bch2_bkey_val_to_text(out, c, bkey_i_to_s_c(k));
|
||||
@ -563,9 +564,9 @@ static void journal_entry_usage_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
struct jset_entry_usage *u =
|
||||
container_of(entry, struct jset_entry_usage, entry);
|
||||
|
||||
prt_printf(out, "type=%s v=%llu",
|
||||
bch2_fs_usage_types[u->entry.btree_id],
|
||||
le64_to_cpu(u->v));
|
||||
prt_str(out, "type=");
|
||||
bch2_prt_fs_usage_type(out, u->entry.btree_id);
|
||||
prt_printf(out, " v=%llu", le64_to_cpu(u->v));
|
||||
}
|
||||
|
||||
static int journal_entry_data_usage_validate(struct bch_fs *c,
|
||||
@ -827,11 +828,11 @@ int bch2_journal_entry_validate(struct bch_fs *c,
|
||||
void bch2_journal_entry_to_text(struct printbuf *out, struct bch_fs *c,
|
||||
struct jset_entry *entry)
|
||||
{
|
||||
bch2_prt_jset_entry_type(out, entry->type);
|
||||
|
||||
if (entry->type < BCH_JSET_ENTRY_NR) {
|
||||
prt_printf(out, "%s: ", bch2_jset_entry_types[entry->type]);
|
||||
prt_str(out, ": ");
|
||||
bch2_jset_entry_ops[entry->type].to_text(out, c, entry);
|
||||
} else {
|
||||
prt_printf(out, "(unknown type %u)", entry->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,8 @@ void bch2_journal_set_watermark(struct journal *j)
|
||||
track_event_change(&c->times[BCH_TIME_blocked_write_buffer_full], low_on_wb))
|
||||
trace_and_count(c, journal_full, c);
|
||||
|
||||
mod_bit(JOURNAL_SPACE_LOW, &j->flags, low_on_space || low_on_pin);
|
||||
|
||||
swap(watermark, j->watermark);
|
||||
if (watermark > j->watermark)
|
||||
journal_wake(j);
|
||||
|
@ -134,6 +134,7 @@ enum journal_flags {
|
||||
JOURNAL_STARTED,
|
||||
JOURNAL_MAY_SKIP_FLUSH,
|
||||
JOURNAL_NEED_FLUSH_WRITE,
|
||||
JOURNAL_SPACE_LOW,
|
||||
};
|
||||
|
||||
/* Reasons we may fail to get a journal reservation: */
|
||||
|
@ -43,7 +43,7 @@ const char * const __bch2_btree_ids[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const bch2_csum_types[] = {
|
||||
static const char * const __bch2_csum_types[] = {
|
||||
BCH_CSUM_TYPES()
|
||||
NULL
|
||||
};
|
||||
@ -53,7 +53,7 @@ const char * const bch2_csum_opts[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const __bch2_compression_types[] = {
|
||||
static const char * const __bch2_compression_types[] = {
|
||||
BCH_COMPRESSION_TYPES()
|
||||
NULL
|
||||
};
|
||||
@ -83,18 +83,39 @@ const char * const bch2_member_states[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const bch2_jset_entry_types[] = {
|
||||
static const char * const __bch2_jset_entry_types[] = {
|
||||
BCH_JSET_ENTRY_TYPES()
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const bch2_fs_usage_types[] = {
|
||||
static const char * const __bch2_fs_usage_types[] = {
|
||||
BCH_FS_USAGE_TYPES()
|
||||
NULL
|
||||
};
|
||||
|
||||
#undef x
|
||||
|
||||
static void prt_str_opt_boundscheck(struct printbuf *out, const char * const opts[],
|
||||
unsigned nr, const char *type, unsigned idx)
|
||||
{
|
||||
if (idx < nr)
|
||||
prt_str(out, opts[idx]);
|
||||
else
|
||||
prt_printf(out, "(unknown %s %u)", type, idx);
|
||||
}
|
||||
|
||||
#define PRT_STR_OPT_BOUNDSCHECKED(name, type) \
|
||||
void bch2_prt_##name(struct printbuf *out, type t) \
|
||||
{ \
|
||||
prt_str_opt_boundscheck(out, __bch2_##name##s, ARRAY_SIZE(__bch2_##name##s) - 1, #name, t);\
|
||||
}
|
||||
|
||||
PRT_STR_OPT_BOUNDSCHECKED(jset_entry_type, enum bch_jset_entry_type);
|
||||
PRT_STR_OPT_BOUNDSCHECKED(fs_usage_type, enum bch_fs_usage_type);
|
||||
PRT_STR_OPT_BOUNDSCHECKED(data_type, enum bch_data_type);
|
||||
PRT_STR_OPT_BOUNDSCHECKED(csum_type, enum bch_csum_type);
|
||||
PRT_STR_OPT_BOUNDSCHECKED(compression_type, enum bch_compression_type);
|
||||
|
||||
static int bch2_opt_fix_errors_parse(struct bch_fs *c, const char *val, u64 *res,
|
||||
struct printbuf *err)
|
||||
{
|
||||
|
@ -16,18 +16,20 @@ extern const char * const bch2_version_upgrade_opts[];
|
||||
extern const char * const bch2_sb_features[];
|
||||
extern const char * const bch2_sb_compat[];
|
||||
extern const char * const __bch2_btree_ids[];
|
||||
extern const char * const bch2_csum_types[];
|
||||
extern const char * const bch2_csum_opts[];
|
||||
extern const char * const __bch2_compression_types[];
|
||||
extern const char * const bch2_compression_opts[];
|
||||
extern const char * const bch2_str_hash_types[];
|
||||
extern const char * const bch2_str_hash_opts[];
|
||||
extern const char * const __bch2_data_types[];
|
||||
extern const char * const bch2_member_states[];
|
||||
extern const char * const bch2_jset_entry_types[];
|
||||
extern const char * const bch2_fs_usage_types[];
|
||||
extern const char * const bch2_d_types[];
|
||||
|
||||
void bch2_prt_jset_entry_type(struct printbuf *, enum bch_jset_entry_type);
|
||||
void bch2_prt_fs_usage_type(struct printbuf *, enum bch_fs_usage_type);
|
||||
void bch2_prt_data_type(struct printbuf *, enum bch_data_type);
|
||||
void bch2_prt_csum_type(struct printbuf *, enum bch_csum_type);
|
||||
void bch2_prt_compression_type(struct printbuf *, enum bch_compression_type);
|
||||
|
||||
static inline const char *bch2_d_type_str(unsigned d_type)
|
||||
{
|
||||
return (d_type < BCH_DT_MAX ? bch2_d_types[d_type] : NULL) ?: "(bad d_type)";
|
||||
|
@ -47,20 +47,6 @@ void bch2_btree_lost_data(struct bch_fs *c, enum btree_id btree)
|
||||
}
|
||||
}
|
||||
|
||||
static bool btree_id_is_alloc(enum btree_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case BTREE_ID_alloc:
|
||||
case BTREE_ID_backpointers:
|
||||
case BTREE_ID_need_discard:
|
||||
case BTREE_ID_freespace:
|
||||
case BTREE_ID_bucket_gens:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* for -o reconstruct_alloc: */
|
||||
static void bch2_reconstruct_alloc(struct bch_fs *c)
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ static int bch2_set_may_go_rw(struct bch_fs *c)
|
||||
|
||||
set_bit(BCH_FS_may_go_rw, &c->flags);
|
||||
|
||||
if (keys->nr || c->opts.fsck || !c->sb.clean)
|
||||
if (keys->nr || c->opts.fsck || !c->sb.clean || c->recovery_passes_explicit)
|
||||
return bch2_fs_read_write_early(c);
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,7 +51,10 @@
|
||||
BCH_FSCK_ERR_subvol_fs_path_parent_wrong) \
|
||||
x(btree_subvolume_children, \
|
||||
BIT_ULL(BCH_RECOVERY_PASS_check_subvols), \
|
||||
BCH_FSCK_ERR_subvol_children_not_set)
|
||||
BCH_FSCK_ERR_subvol_children_not_set) \
|
||||
x(mi_btree_bitmap, \
|
||||
BIT_ULL(BCH_RECOVERY_PASS_check_allocations), \
|
||||
BCH_FSCK_ERR_btree_bitmap_not_marked)
|
||||
|
||||
#define DOWNGRADE_TABLE()
|
||||
|
||||
|
@ -130,7 +130,7 @@
|
||||
x(bucket_gens_nonzero_for_invalid_buckets, 122) \
|
||||
x(need_discard_freespace_key_to_invalid_dev_bucket, 123) \
|
||||
x(need_discard_freespace_key_bad, 124) \
|
||||
x(backpointer_pos_wrong, 125) \
|
||||
x(backpointer_bucket_offset_wrong, 125) \
|
||||
x(backpointer_to_missing_device, 126) \
|
||||
x(backpointer_to_missing_alloc, 127) \
|
||||
x(backpointer_to_missing_ptr, 128) \
|
||||
@ -270,7 +270,8 @@
|
||||
x(btree_ptr_v2_min_key_bad, 262) \
|
||||
x(btree_root_unreadable_and_scan_found_nothing, 263) \
|
||||
x(snapshot_node_missing, 264) \
|
||||
x(dup_backpointer_to_bad_csum_extent, 265)
|
||||
x(dup_backpointer_to_bad_csum_extent, 265) \
|
||||
x(btree_bitmap_not_marked, 266)
|
||||
|
||||
enum bch_sb_error_id {
|
||||
#define x(t, n) BCH_FSCK_ERR_##t = n,
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "bcachefs.h"
|
||||
#include "btree_cache.h"
|
||||
#include "disk_groups.h"
|
||||
#include "opts.h"
|
||||
#include "replicas.h"
|
||||
@ -426,3 +427,55 @@ void bch2_dev_errors_reset(struct bch_dev *ca)
|
||||
bch2_write_super(c);
|
||||
mutex_unlock(&c->sb_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Per member "range has btree nodes" bitmap:
|
||||
*
|
||||
* This is so that if we ever have to run the btree node scan to repair we don't
|
||||
* have to scan full devices:
|
||||
*/
|
||||
|
||||
bool bch2_dev_btree_bitmap_marked(struct bch_fs *c, struct bkey_s_c k)
|
||||
{
|
||||
bkey_for_each_ptr(bch2_bkey_ptrs_c(k), ptr)
|
||||
if (!bch2_dev_btree_bitmap_marked_sectors(bch_dev_bkey_exists(c, ptr->dev),
|
||||
ptr->offset, btree_sectors(c)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __bch2_dev_btree_bitmap_mark(struct bch_sb_field_members_v2 *mi, unsigned dev,
|
||||
u64 start, unsigned sectors)
|
||||
{
|
||||
struct bch_member *m = __bch2_members_v2_get_mut(mi, dev);
|
||||
u64 bitmap = le64_to_cpu(m->btree_allocated_bitmap);
|
||||
|
||||
u64 end = start + sectors;
|
||||
|
||||
int resize = ilog2(roundup_pow_of_two(end)) - (m->btree_bitmap_shift + 6);
|
||||
if (resize > 0) {
|
||||
u64 new_bitmap = 0;
|
||||
|
||||
for (unsigned i = 0; i < 64; i++)
|
||||
if (bitmap & BIT_ULL(i))
|
||||
new_bitmap |= BIT_ULL(i >> resize);
|
||||
bitmap = new_bitmap;
|
||||
m->btree_bitmap_shift += resize;
|
||||
}
|
||||
|
||||
for (unsigned bit = sectors >> m->btree_bitmap_shift;
|
||||
bit << m->btree_bitmap_shift < end;
|
||||
bit++)
|
||||
bitmap |= BIT_ULL(bit);
|
||||
|
||||
m->btree_allocated_bitmap = cpu_to_le64(bitmap);
|
||||
}
|
||||
|
||||
void bch2_dev_btree_bitmap_mark(struct bch_fs *c, struct bkey_s_c k)
|
||||
{
|
||||
lockdep_assert_held(&c->sb_lock);
|
||||
|
||||
struct bch_sb_field_members_v2 *mi = bch2_sb_field_get(c->disk_sb.sb, members_v2);
|
||||
bkey_for_each_ptr(bch2_bkey_ptrs_c(k), ptr)
|
||||
__bch2_dev_btree_bitmap_mark(mi, ptr->dev, ptr->offset, btree_sectors(c));
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define _BCACHEFS_SB_MEMBERS_H
|
||||
|
||||
#include "darray.h"
|
||||
#include "bkey_types.h"
|
||||
|
||||
extern char * const bch2_member_error_strs[];
|
||||
|
||||
@ -220,6 +221,8 @@ static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
|
||||
: 1,
|
||||
.freespace_initialized = BCH_MEMBER_FREESPACE_INITIALIZED(mi),
|
||||
.valid = bch2_member_exists(mi),
|
||||
.btree_bitmap_shift = mi->btree_bitmap_shift,
|
||||
.btree_allocated_bitmap = le64_to_cpu(mi->btree_allocated_bitmap),
|
||||
};
|
||||
}
|
||||
|
||||
@ -228,4 +231,22 @@ void bch2_sb_members_from_cpu(struct bch_fs *);
|
||||
void bch2_dev_io_errors_to_text(struct printbuf *, struct bch_dev *);
|
||||
void bch2_dev_errors_reset(struct bch_dev *);
|
||||
|
||||
static inline bool bch2_dev_btree_bitmap_marked_sectors(struct bch_dev *ca, u64 start, unsigned sectors)
|
||||
{
|
||||
u64 end = start + sectors;
|
||||
|
||||
if (end > 64 << ca->mi.btree_bitmap_shift)
|
||||
return false;
|
||||
|
||||
for (unsigned bit = sectors >> ca->mi.btree_bitmap_shift;
|
||||
bit << ca->mi.btree_bitmap_shift < end;
|
||||
bit++)
|
||||
if (!(ca->mi.btree_allocated_bitmap & BIT_ULL(bit)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bch2_dev_btree_bitmap_marked(struct bch_fs *, struct bkey_s_c);
|
||||
void bch2_dev_btree_bitmap_mark(struct bch_fs *, struct bkey_s_c);
|
||||
|
||||
#endif /* _BCACHEFS_SB_MEMBERS_H */
|
||||
|
@ -125,6 +125,15 @@ static inline u32 get_ancestor_below(struct snapshot_table *t, u32 id, u32 ances
|
||||
return s->parent;
|
||||
}
|
||||
|
||||
static bool test_ancestor_bitmap(struct snapshot_table *t, u32 id, u32 ancestor)
|
||||
{
|
||||
const struct snapshot_t *s = __snapshot_t(t, id);
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
return test_bit(ancestor - id - 1, s->is_ancestor);
|
||||
}
|
||||
|
||||
bool __bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
|
||||
{
|
||||
bool ret;
|
||||
@ -140,13 +149,11 @@ bool __bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
|
||||
while (id && id < ancestor - IS_ANCESTOR_BITMAP)
|
||||
id = get_ancestor_below(t, id, ancestor);
|
||||
|
||||
if (id && id < ancestor) {
|
||||
ret = test_bit(ancestor - id - 1, __snapshot_t(t, id)->is_ancestor);
|
||||
ret = id && id < ancestor
|
||||
? test_ancestor_bitmap(t, id, ancestor)
|
||||
: id == ancestor;
|
||||
|
||||
EBUG_ON(ret != __bch2_snapshot_is_ancestor_early(t, id, ancestor));
|
||||
} else {
|
||||
ret = id == ancestor;
|
||||
}
|
||||
EBUG_ON(ret != __bch2_snapshot_is_ancestor_early(t, id, ancestor));
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
|
||||
|
@ -700,8 +700,11 @@ retry:
|
||||
return -ENOMEM;
|
||||
|
||||
sb->sb_name = kstrdup(path, GFP_KERNEL);
|
||||
if (!sb->sb_name)
|
||||
return -ENOMEM;
|
||||
if (!sb->sb_name) {
|
||||
ret = -ENOMEM;
|
||||
prt_printf(&err, "error allocating memory for sb_name");
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifndef __KERNEL__
|
||||
if (opt_get(*opts, direct_io) == false)
|
||||
|
@ -288,8 +288,13 @@ static void __bch2_fs_read_only(struct bch_fs *c)
|
||||
if (test_bit(JOURNAL_REPLAY_DONE, &c->journal.flags) &&
|
||||
!test_bit(BCH_FS_emergency_ro, &c->flags))
|
||||
set_bit(BCH_FS_clean_shutdown, &c->flags);
|
||||
|
||||
bch2_fs_journal_stop(&c->journal);
|
||||
|
||||
bch_info(c, "%sshutdown complete, journal seq %llu",
|
||||
test_bit(BCH_FS_clean_shutdown, &c->flags) ? "" : "un",
|
||||
c->journal.seq_ondisk);
|
||||
|
||||
/*
|
||||
* After stopping journal:
|
||||
*/
|
||||
|
@ -37,6 +37,8 @@ struct bch_member_cpu {
|
||||
u8 durability;
|
||||
u8 freespace_initialized;
|
||||
u8 valid;
|
||||
u8 btree_bitmap_shift;
|
||||
u64 btree_allocated_bitmap;
|
||||
};
|
||||
|
||||
#endif /* _BCACHEFS_SUPER_TYPES_H */
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "btree_iter.h"
|
||||
#include "btree_key_cache.h"
|
||||
#include "btree_update.h"
|
||||
#include "btree_update_interior.h"
|
||||
#include "btree_gc.h"
|
||||
#include "buckets.h"
|
||||
#include "clock.h"
|
||||
@ -26,6 +25,7 @@
|
||||
#include "ec.h"
|
||||
#include "inode.h"
|
||||
#include "journal.h"
|
||||
#include "journal_reclaim.h"
|
||||
#include "keylist.h"
|
||||
#include "move.h"
|
||||
#include "movinggc.h"
|
||||
@ -139,6 +139,7 @@ do { \
|
||||
write_attribute(trigger_gc);
|
||||
write_attribute(trigger_discards);
|
||||
write_attribute(trigger_invalidates);
|
||||
write_attribute(trigger_journal_flush);
|
||||
write_attribute(prune_cache);
|
||||
write_attribute(btree_wakeup);
|
||||
rw_attribute(btree_gc_periodic);
|
||||
@ -166,7 +167,6 @@ read_attribute(btree_write_stats);
|
||||
read_attribute(btree_cache_size);
|
||||
read_attribute(compression_stats);
|
||||
read_attribute(journal_debug);
|
||||
read_attribute(btree_updates);
|
||||
read_attribute(btree_cache);
|
||||
read_attribute(btree_key_cache);
|
||||
read_attribute(stripes_heap);
|
||||
@ -415,9 +415,6 @@ SHOW(bch2_fs)
|
||||
if (attr == &sysfs_journal_debug)
|
||||
bch2_journal_debug_to_text(out, &c->journal);
|
||||
|
||||
if (attr == &sysfs_btree_updates)
|
||||
bch2_btree_updates_to_text(out, c);
|
||||
|
||||
if (attr == &sysfs_btree_cache)
|
||||
bch2_btree_cache_to_text(out, c);
|
||||
|
||||
@ -505,7 +502,7 @@ STORE(bch2_fs)
|
||||
|
||||
/* Debugging: */
|
||||
|
||||
if (!test_bit(BCH_FS_rw, &c->flags))
|
||||
if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_sysfs))
|
||||
return -EROFS;
|
||||
|
||||
if (attr == &sysfs_prune_cache) {
|
||||
@ -538,6 +535,11 @@ STORE(bch2_fs)
|
||||
if (attr == &sysfs_trigger_invalidates)
|
||||
bch2_do_invalidates(c);
|
||||
|
||||
if (attr == &sysfs_trigger_journal_flush) {
|
||||
bch2_journal_flush_all_pins(&c->journal);
|
||||
bch2_journal_meta(&c->journal);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BCACHEFS_TESTS
|
||||
if (attr == &sysfs_perf_test) {
|
||||
char *tmp = kstrdup(buf, GFP_KERNEL), *p = tmp;
|
||||
@ -558,6 +560,7 @@ STORE(bch2_fs)
|
||||
size = ret;
|
||||
}
|
||||
#endif
|
||||
bch2_write_ref_put(c, BCH_WRITE_REF_sysfs);
|
||||
return size;
|
||||
}
|
||||
SYSFS_OPS(bch2_fs);
|
||||
@ -639,7 +642,6 @@ SYSFS_OPS(bch2_fs_internal);
|
||||
struct attribute *bch2_fs_internal_files[] = {
|
||||
&sysfs_flags,
|
||||
&sysfs_journal_debug,
|
||||
&sysfs_btree_updates,
|
||||
&sysfs_btree_cache,
|
||||
&sysfs_btree_key_cache,
|
||||
&sysfs_new_stripes,
|
||||
@ -657,6 +659,7 @@ struct attribute *bch2_fs_internal_files[] = {
|
||||
&sysfs_trigger_gc,
|
||||
&sysfs_trigger_discards,
|
||||
&sysfs_trigger_invalidates,
|
||||
&sysfs_trigger_journal_flush,
|
||||
&sysfs_prune_cache,
|
||||
&sysfs_btree_wakeup,
|
||||
|
||||
|
@ -672,7 +672,7 @@ static int __do_delete(struct btree_trans *trans, struct bpos pos)
|
||||
|
||||
bch2_trans_iter_init(trans, &iter, BTREE_ID_xattrs, pos,
|
||||
BTREE_ITER_INTENT);
|
||||
k = bch2_btree_iter_peek(&iter);
|
||||
k = bch2_btree_iter_peek_upto(&iter, POS(0, U64_MAX));
|
||||
ret = bkey_err(k);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
@ -788,6 +788,14 @@ static inline int copy_from_user_errcode(void *to, const void __user *from, unsi
|
||||
|
||||
#endif
|
||||
|
||||
static inline void mod_bit(long nr, volatile unsigned long *addr, bool v)
|
||||
{
|
||||
if (v)
|
||||
set_bit(nr, addr);
|
||||
else
|
||||
clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
static inline void __set_bit_le64(size_t bit, __le64 *addr)
|
||||
{
|
||||
addr[bit / 64] |= cpu_to_le64(BIT_ULL(bit % 64));
|
||||
@ -795,7 +803,7 @@ static inline void __set_bit_le64(size_t bit, __le64 *addr)
|
||||
|
||||
static inline void __clear_bit_le64(size_t bit, __le64 *addr)
|
||||
{
|
||||
addr[bit / 64] &= !cpu_to_le64(BIT_ULL(bit % 64));
|
||||
addr[bit / 64] &= ~cpu_to_le64(BIT_ULL(bit % 64));
|
||||
}
|
||||
|
||||
static inline bool test_bit_le64(size_t bit, __le64 *addr)
|
||||
|
Loading…
Reference in New Issue
Block a user