mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-22 00:00:03 +03:00
Update bcachefs sources to 438696e03da7 bcachefs: rename version -> bversion for big endian builds
This commit is contained in:
parent
7a98f526b5
commit
9f20109509
@ -1 +1 @@
|
||||
82792a451950397b8594b399bed03cfda1f31299
|
||||
438696e03da7c2d1678bdcce98939ba642cbb467
|
||||
|
@ -71,7 +71,7 @@ struct bch_inode_unpacked create_file(struct bch_fs *c,
|
||||
|
||||
static const struct xattr_handler *xattr_resolve_name(char **name)
|
||||
{
|
||||
const struct xattr_handler **handlers = bch2_xattr_handlers;
|
||||
const struct xattr_handler * const *handlers = bch2_xattr_handlers;
|
||||
const struct xattr_handler *handler;
|
||||
|
||||
for_each_xattr_handler(handlers, handler) {
|
||||
|
@ -62,6 +62,29 @@ static inline void bitmap_complement(unsigned long *dst, const unsigned long *sr
|
||||
dst[k] = ~src[k];
|
||||
}
|
||||
|
||||
static inline bool __bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, unsigned int bits)
|
||||
{
|
||||
unsigned int k;
|
||||
unsigned int lim = bits/BITS_PER_LONG;
|
||||
unsigned long result = 0;
|
||||
|
||||
for (k = 0; k < lim; k++)
|
||||
result |= (dst[k] = bitmap1[k] & ~bitmap2[k]);
|
||||
if (bits % BITS_PER_LONG)
|
||||
result |= (dst[k] = bitmap1[k] & ~bitmap2[k] &
|
||||
BITMAP_LAST_WORD_MASK(bits));
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
static inline bool bitmap_andnot(unsigned long *dst, const unsigned long *src1,
|
||||
const unsigned long *src2, unsigned int nbits)
|
||||
{
|
||||
if (small_const_nbits(nbits))
|
||||
return (*dst = *src1 & ~(*src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
|
||||
return __bitmap_andnot(dst, src1, src2, nbits);
|
||||
}
|
||||
|
||||
static inline void bitmap_zero(unsigned long *dst, int nbits)
|
||||
{
|
||||
memset(dst, 0, BITS_TO_LONGS(nbits) * sizeof(unsigned long));
|
||||
|
@ -19,7 +19,7 @@ static inline int srcu_read_lock(struct srcu_struct *ssp)
|
||||
|
||||
static inline bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp)
|
||||
@ -58,7 +58,7 @@ static inline void cleanup_srcu_struct(struct srcu_struct *ssp) {}
|
||||
static inline void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
|
||||
rcu_callback_t func)
|
||||
{
|
||||
func(rhp);
|
||||
call_rcu(rhp, func);
|
||||
}
|
||||
|
||||
static inline int init_srcu_struct(struct srcu_struct *ssp)
|
||||
|
@ -137,7 +137,7 @@ static struct posix_acl *bch2_acl_from_disk(struct btree_trans *trans,
|
||||
return NULL;
|
||||
|
||||
acl = allocate_dropping_locks(trans, ret,
|
||||
posix_acl_alloc(count, GFP_KERNEL));
|
||||
posix_acl_alloc(count, _gfp));
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (ret) {
|
||||
@ -427,8 +427,7 @@ int bch2_acl_chmod(struct btree_trans *trans, subvol_inum inum,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = allocate_dropping_locks_errcode(trans,
|
||||
__posix_acl_chmod(&acl, GFP_KERNEL, mode));
|
||||
ret = allocate_dropping_locks_errcode(trans, __posix_acl_chmod(&acl, _gfp, mode));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -2310,7 +2310,7 @@ int bch2_dev_remove_alloc(struct bch_fs *c, struct bch_dev *ca)
|
||||
* We clear the LRU and need_discard btrees first so that we don't race
|
||||
* with bch2_do_invalidates() and bch2_do_discards()
|
||||
*/
|
||||
ret = bch2_dev_remove_stripes(c, ca) ?:
|
||||
ret = bch2_dev_remove_stripes(c, ca->dev_idx) ?:
|
||||
bch2_btree_delete_range(c, BTREE_ID_lru, start, end,
|
||||
BTREE_TRIGGER_norun, NULL) ?:
|
||||
bch2_btree_delete_range(c, BTREE_ID_need_discard, start, end,
|
||||
@ -2324,7 +2324,7 @@ int bch2_dev_remove_alloc(struct bch_fs *c, struct bch_dev *ca)
|
||||
bch2_btree_delete_range(c, BTREE_ID_alloc, start, end,
|
||||
BTREE_TRIGGER_norun, NULL) ?:
|
||||
bch2_dev_usage_remove(c, ca->dev_idx);
|
||||
bch_err_msg(c, ret, "removing dev alloc info");
|
||||
bch_err_msg(ca, ret, "removing dev alloc info");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ enum bch_validate_flags;
|
||||
static inline bool bch2_dev_bucket_exists(struct bch_fs *c, struct bpos pos)
|
||||
{
|
||||
rcu_read_lock();
|
||||
struct bch_dev *ca = bch2_dev_rcu(c, pos.inode);
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, pos.inode);
|
||||
bool ret = ca && bucket_valid(ca, pos.offset);
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "bbpos.h"
|
||||
#include "alloc_background.h"
|
||||
#include "backpointers.h"
|
||||
#include "bbpos.h"
|
||||
#include "bkey_buf.h"
|
||||
#include "btree_cache.h"
|
||||
#include "btree_update.h"
|
||||
@ -502,7 +501,7 @@ found:
|
||||
prt_printf(&buf, "\n %s ", bch2_btree_id_str(o_btree));
|
||||
bch2_bkey_val_to_text(&buf, c, extent2);
|
||||
|
||||
struct nonce nonce = extent_nonce(extent.k->version, p.crc);
|
||||
struct nonce nonce = extent_nonce(extent.k->bversion, p.crc);
|
||||
struct bch_csum csum = bch2_checksum(c, p.crc.csum_type, nonce, data_buf, bytes);
|
||||
if (fsck_err_on(bch2_crc_cmp(csum, p.crc.csum),
|
||||
trans, dup_backpointer_to_bad_csum_extent,
|
||||
|
@ -134,26 +134,35 @@ static inline enum bch_data_type bch2_bkey_ptr_data_type(struct bkey_s_c k,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __bch2_extent_ptr_to_bp(struct bch_fs *c, struct bch_dev *ca,
|
||||
enum btree_id btree_id, unsigned level,
|
||||
struct bkey_s_c k, struct extent_ptr_decoded p,
|
||||
const union bch_extent_entry *entry,
|
||||
struct bpos *bucket_pos, struct bch_backpointer *bp,
|
||||
u64 sectors)
|
||||
{
|
||||
u32 bucket_offset;
|
||||
*bucket_pos = PTR_BUCKET_POS_OFFSET(ca, &p.ptr, &bucket_offset);
|
||||
*bp = (struct bch_backpointer) {
|
||||
.btree_id = btree_id,
|
||||
.level = level,
|
||||
.data_type = bch2_bkey_ptr_data_type(k, p, entry),
|
||||
.bucket_offset = ((u64) bucket_offset << MAX_EXTENT_COMPRESS_RATIO_SHIFT) +
|
||||
p.crc.offset,
|
||||
.bucket_len = sectors,
|
||||
.pos = k.k->p,
|
||||
};
|
||||
}
|
||||
|
||||
static inline void bch2_extent_ptr_to_bp(struct bch_fs *c, struct bch_dev *ca,
|
||||
enum btree_id btree_id, unsigned level,
|
||||
struct bkey_s_c k, struct extent_ptr_decoded p,
|
||||
const union bch_extent_entry *entry,
|
||||
struct bpos *bucket_pos, struct bch_backpointer *bp)
|
||||
{
|
||||
enum bch_data_type data_type = bch2_bkey_ptr_data_type(k, p, entry);
|
||||
s64 sectors = level ? btree_sectors(c) : k.k->size;
|
||||
u32 bucket_offset;
|
||||
u64 sectors = ptr_disk_sectors(level ? btree_sectors(c) : k.k->size, p);
|
||||
|
||||
*bucket_pos = PTR_BUCKET_POS_OFFSET(ca, &p.ptr, &bucket_offset);
|
||||
*bp = (struct bch_backpointer) {
|
||||
.btree_id = btree_id,
|
||||
.level = level,
|
||||
.data_type = data_type,
|
||||
.bucket_offset = ((u64) bucket_offset << MAX_EXTENT_COMPRESS_RATIO_SHIFT) +
|
||||
p.crc.offset,
|
||||
.bucket_len = ptr_disk_sectors(sectors, p),
|
||||
.pos = k.k->p,
|
||||
};
|
||||
__bch2_extent_ptr_to_bp(c, ca, btree_id, level, k, p, entry, bucket_pos, bp, sectors);
|
||||
}
|
||||
|
||||
int bch2_get_next_backpointer(struct btree_trans *, struct bch_dev *ca, struct bpos, int,
|
||||
|
@ -594,6 +594,7 @@ struct bch_dev {
|
||||
#define BCH_FS_FLAGS() \
|
||||
x(new_fs) \
|
||||
x(started) \
|
||||
x(clean_recovery) \
|
||||
x(btree_running) \
|
||||
x(accounting_replay_done) \
|
||||
x(may_go_rw) \
|
||||
@ -776,7 +777,7 @@ struct bch_fs {
|
||||
unsigned nsec_per_time_unit;
|
||||
u64 features;
|
||||
u64 compat;
|
||||
unsigned long errors_silent[BITS_TO_LONGS(BCH_SB_ERR_MAX)];
|
||||
unsigned long errors_silent[BITS_TO_LONGS(BCH_FSCK_ERR_MAX)];
|
||||
u64 btrees_lost_data;
|
||||
} sb;
|
||||
|
||||
|
@ -217,13 +217,13 @@ struct bkey {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
__u8 pad[1];
|
||||
|
||||
struct bversion version;
|
||||
struct bversion bversion;
|
||||
__u32 size; /* extent size, in sectors */
|
||||
struct bpos p;
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
struct bpos p;
|
||||
__u32 size; /* extent size, in sectors */
|
||||
struct bversion version;
|
||||
struct bversion bversion;
|
||||
|
||||
__u8 pad[1];
|
||||
#endif
|
||||
@ -328,8 +328,8 @@ enum bch_bkey_fields {
|
||||
bkey_format_field(OFFSET, p.offset), \
|
||||
bkey_format_field(SNAPSHOT, p.snapshot), \
|
||||
bkey_format_field(SIZE, size), \
|
||||
bkey_format_field(VERSION_HI, version.hi), \
|
||||
bkey_format_field(VERSION_LO, version.lo), \
|
||||
bkey_format_field(VERSION_HI, bversion.hi), \
|
||||
bkey_format_field(VERSION_LO, bversion.lo), \
|
||||
}, \
|
||||
})
|
||||
|
||||
|
@ -214,9 +214,9 @@ static __always_inline int bversion_cmp(struct bversion l, struct bversion r)
|
||||
#define ZERO_VERSION ((struct bversion) { .hi = 0, .lo = 0 })
|
||||
#define MAX_VERSION ((struct bversion) { .hi = ~0, .lo = ~0ULL })
|
||||
|
||||
static __always_inline int bversion_zero(struct bversion v)
|
||||
static __always_inline bool bversion_zero(struct bversion v)
|
||||
{
|
||||
return !bversion_cmp(v, ZERO_VERSION);
|
||||
return bversion_cmp(v, ZERO_VERSION) == 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BCACHEFS_DEBUG
|
||||
@ -554,8 +554,8 @@ static inline void bch2_bkey_pack_test(void) {}
|
||||
x(BKEY_FIELD_OFFSET, p.offset) \
|
||||
x(BKEY_FIELD_SNAPSHOT, p.snapshot) \
|
||||
x(BKEY_FIELD_SIZE, size) \
|
||||
x(BKEY_FIELD_VERSION_HI, version.hi) \
|
||||
x(BKEY_FIELD_VERSION_LO, version.lo)
|
||||
x(BKEY_FIELD_VERSION_HI, bversion.hi) \
|
||||
x(BKEY_FIELD_VERSION_LO, bversion.lo)
|
||||
|
||||
struct bkey_format_state {
|
||||
u64 field_min[BKEY_NR_FIELDS];
|
||||
|
@ -289,7 +289,7 @@ void bch2_bkey_to_text(struct printbuf *out, const struct bkey *k)
|
||||
|
||||
bch2_bpos_to_text(out, k->p);
|
||||
|
||||
prt_printf(out, " len %u ver %llu", k->size, k->version.lo);
|
||||
prt_printf(out, " len %u ver %llu", k->size, k->bversion.lo);
|
||||
} else {
|
||||
prt_printf(out, "(null)");
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ bool bch2_bkey_normalize(struct bch_fs *, struct bkey_s);
|
||||
static inline bool bch2_bkey_maybe_mergable(const struct bkey *l, const struct bkey *r)
|
||||
{
|
||||
return l->type == r->type &&
|
||||
!bversion_cmp(l->version, r->version) &&
|
||||
!bversion_cmp(l->bversion, r->bversion) &&
|
||||
bpos_eq(l->p, bkey_start_pos(r));
|
||||
}
|
||||
|
||||
|
@ -804,8 +804,7 @@ got_node:
|
||||
|
||||
mutex_unlock(&bc->lock);
|
||||
|
||||
if (memalloc_flags_do(PF_MEMALLOC_NORECLAIM,
|
||||
btree_node_data_alloc(c, b, GFP_KERNEL|__GFP_NOWARN))) {
|
||||
if (btree_node_data_alloc(c, b, GFP_NOWAIT|__GFP_NOWARN)) {
|
||||
bch2_trans_unlock(trans);
|
||||
if (btree_node_data_alloc(c, b, GFP_KERNEL|__GFP_NOWARN))
|
||||
goto err;
|
||||
|
@ -513,6 +513,8 @@ int bch2_check_topology(struct bch_fs *c)
|
||||
struct bpos pulled_from_scan = POS_MIN;
|
||||
int ret = 0;
|
||||
|
||||
bch2_trans_srcu_unlock(trans);
|
||||
|
||||
for (unsigned i = 0; i < btree_id_nr_alive(c) && !ret; i++) {
|
||||
struct btree_root *r = bch2_btree_id_root(c, i);
|
||||
bool reconstructed_root = false;
|
||||
@ -599,15 +601,15 @@ static int bch2_gc_mark_key(struct btree_trans *trans, enum btree_id btree_id,
|
||||
|
||||
if (initial) {
|
||||
BUG_ON(bch2_journal_seq_verify &&
|
||||
k.k->version.lo > atomic64_read(&c->journal.seq));
|
||||
k.k->bversion.lo > atomic64_read(&c->journal.seq));
|
||||
|
||||
if (fsck_err_on(btree_id != BTREE_ID_accounting &&
|
||||
k.k->version.lo > atomic64_read(&c->key_version),
|
||||
k.k->bversion.lo > atomic64_read(&c->key_version),
|
||||
trans, bkey_version_in_future,
|
||||
"key version number higher than recorded %llu\n %s",
|
||||
atomic64_read(&c->key_version),
|
||||
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
||||
atomic64_set(&c->key_version, k.k->version.lo);
|
||||
atomic64_set(&c->key_version, k.k->bversion.lo);
|
||||
}
|
||||
|
||||
if (mustfix_fsck_err_on(level && !bch2_dev_btree_bitmap_marked(c, k),
|
||||
|
@ -1195,6 +1195,10 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
set_btree_bset(b, b->set, &b->data->keys);
|
||||
|
||||
b->nr = bch2_key_sort_fix_overlapping(c, &sorted->keys, iter);
|
||||
memset((uint8_t *)(sorted + 1) + b->nr.live_u64s * sizeof(u64), 0,
|
||||
btree_buf_bytes(b) -
|
||||
sizeof(struct btree_node) -
|
||||
b->nr.live_u64s * sizeof(u64));
|
||||
|
||||
u64s = le16_to_cpu(sorted->keys.u64s);
|
||||
*sorted = *b->data;
|
||||
@ -1219,7 +1223,7 @@ int bch2_btree_node_read_done(struct bch_fs *c, struct bch_dev *ca,
|
||||
ret = bch2_bkey_val_validate(c, u.s_c, READ);
|
||||
if (ret == -BCH_ERR_fsck_delete_bkey ||
|
||||
(bch2_inject_invalid_keys &&
|
||||
!bversion_cmp(u.k->version, MAX_VERSION))) {
|
||||
!bversion_cmp(u.k->bversion, MAX_VERSION))) {
|
||||
btree_keys_account_key_drop(&b->nr, 0, k);
|
||||
|
||||
i->u64s = cpu_to_le16(le16_to_cpu(i->u64s) - k->u64s);
|
||||
|
@ -6,8 +6,6 @@
|
||||
#include "btree_types.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <linux/sched/mm.h>
|
||||
|
||||
void bch2_trans_updates_to_text(struct printbuf *, struct btree_trans *);
|
||||
void bch2_btree_path_to_text(struct printbuf *, struct btree_trans *, btree_path_idx_t);
|
||||
void bch2_trans_paths_to_text(struct printbuf *, struct btree_trans *);
|
||||
@ -873,19 +871,13 @@ struct bkey_s_c bch2_btree_iter_peek_and_restart_outlined(struct btree_iter *);
|
||||
(_do) ?: bch2_trans_relock(_trans); \
|
||||
})
|
||||
|
||||
#define memalloc_flags_do(_flags, _do) \
|
||||
({ \
|
||||
unsigned _saved_flags = memalloc_flags_save(_flags); \
|
||||
typeof(_do) _ret = _do; \
|
||||
memalloc_noreclaim_restore(_saved_flags); \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
#define allocate_dropping_locks_errcode(_trans, _do) \
|
||||
({ \
|
||||
int _ret = memalloc_flags_do(PF_MEMALLOC_NORECLAIM|PF_MEMALLOC_NOWARN, _do);\
|
||||
gfp_t _gfp = GFP_NOWAIT|__GFP_NOWARN; \
|
||||
int _ret = _do; \
|
||||
\
|
||||
if (bch2_err_matches(_ret, ENOMEM)) { \
|
||||
_gfp = GFP_KERNEL; \
|
||||
_ret = drop_locks_do(_trans, _do); \
|
||||
} \
|
||||
_ret; \
|
||||
@ -893,10 +885,12 @@ struct bkey_s_c bch2_btree_iter_peek_and_restart_outlined(struct btree_iter *);
|
||||
|
||||
#define allocate_dropping_locks(_trans, _ret, _do) \
|
||||
({ \
|
||||
typeof(_do) _p = memalloc_flags_do(PF_MEMALLOC_NORECLAIM|PF_MEMALLOC_NOWARN, _do);\
|
||||
gfp_t _gfp = GFP_NOWAIT|__GFP_NOWARN; \
|
||||
typeof(_do) _p = _do; \
|
||||
\
|
||||
_ret = 0; \
|
||||
if (unlikely(!_p)) { \
|
||||
_gfp = GFP_KERNEL; \
|
||||
_ret = drop_locks_do(_trans, ((_p = _do), 0)); \
|
||||
} \
|
||||
_p; \
|
||||
|
@ -116,9 +116,9 @@ static void bkey_cached_free(struct btree_key_cache *bc,
|
||||
this_cpu_inc(*bc->nr_pending);
|
||||
}
|
||||
|
||||
static struct bkey_cached *__bkey_cached_alloc(unsigned key_u64s)
|
||||
static struct bkey_cached *__bkey_cached_alloc(unsigned key_u64s, gfp_t gfp)
|
||||
{
|
||||
gfp_t gfp = GFP_KERNEL|__GFP_ACCOUNT|__GFP_RECLAIMABLE;
|
||||
gfp |= __GFP_ACCOUNT|__GFP_RECLAIMABLE;
|
||||
|
||||
struct bkey_cached *ck = kmem_cache_zalloc(bch2_key_cache, gfp);
|
||||
if (unlikely(!ck))
|
||||
@ -147,7 +147,7 @@ bkey_cached_alloc(struct btree_trans *trans, struct btree_path *path, unsigned k
|
||||
goto lock;
|
||||
|
||||
ck = allocate_dropping_locks(trans, ret,
|
||||
__bkey_cached_alloc(key_u64s));
|
||||
__bkey_cached_alloc(key_u64s, _gfp));
|
||||
if (ret) {
|
||||
if (ck)
|
||||
kfree(ck->k);
|
||||
@ -241,7 +241,7 @@ static int btree_key_cache_create(struct btree_trans *trans, struct btree_path *
|
||||
mark_btree_node_locked_noreset(path, 0, BTREE_NODE_UNLOCKED);
|
||||
|
||||
struct bkey_i *new_k = allocate_dropping_locks(trans, ret,
|
||||
kmalloc(key_u64s * sizeof(u64), GFP_KERNEL));
|
||||
kmalloc(key_u64s * sizeof(u64), _gfp));
|
||||
if (unlikely(!new_k)) {
|
||||
bch_err(trans->c, "error allocating memory for key cache key, btree %s u64s %u",
|
||||
bch2_btree_id_str(ck->key.btree_id), key_u64s);
|
||||
|
@ -275,7 +275,7 @@ static int read_btree_nodes(struct find_btree_nodes *f)
|
||||
w->ca = ca;
|
||||
|
||||
t = kthread_run(read_btree_nodes_worker, w, "read_btree_nodes/%s", ca->name);
|
||||
ret = IS_ERR_OR_NULL(t);
|
||||
ret = PTR_ERR_OR_ZERO(t);
|
||||
if (ret) {
|
||||
percpu_ref_put(&ca->io_ref);
|
||||
closure_put(&cl);
|
||||
|
@ -684,10 +684,10 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
|
||||
!(flags & BCH_TRANS_COMMIT_no_journal_res)) {
|
||||
if (bch2_journal_seq_verify)
|
||||
trans_for_each_update(trans, i)
|
||||
i->k->k.version.lo = trans->journal_res.seq;
|
||||
i->k->k.bversion.lo = trans->journal_res.seq;
|
||||
else if (bch2_inject_invalid_keys)
|
||||
trans_for_each_update(trans, i)
|
||||
i->k->k.version = MAX_VERSION;
|
||||
i->k->k.bversion = MAX_VERSION;
|
||||
}
|
||||
|
||||
h = trans->hooks;
|
||||
@ -700,27 +700,31 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
|
||||
|
||||
struct jset_entry *entry = trans->journal_entries;
|
||||
|
||||
if (likely(!(flags & BCH_TRANS_COMMIT_skip_accounting_apply))) {
|
||||
percpu_down_read(&c->mark_lock);
|
||||
|
||||
for (entry = trans->journal_entries;
|
||||
entry != (void *) ((u64 *) trans->journal_entries + trans->journal_entries_u64s);
|
||||
entry = vstruct_next(entry))
|
||||
if (jset_entry_is_key(entry) && entry->start->k.type == KEY_TYPE_accounting) {
|
||||
if (entry->type == BCH_JSET_ENTRY_write_buffer_keys &&
|
||||
entry->start->k.type == KEY_TYPE_accounting) {
|
||||
BUG_ON(!trans->journal_res.ref);
|
||||
|
||||
struct bkey_i_accounting *a = bkey_i_to_accounting(entry->start);
|
||||
|
||||
a->k.version = journal_pos_to_bversion(&trans->journal_res,
|
||||
a->k.bversion = journal_pos_to_bversion(&trans->journal_res,
|
||||
(u64 *) entry - (u64 *) trans->journal_entries);
|
||||
BUG_ON(bversion_zero(a->k.version));
|
||||
ret = bch2_accounting_mem_mod_locked(trans, accounting_i_to_s_c(a), false, false);
|
||||
BUG_ON(bversion_zero(a->k.bversion));
|
||||
|
||||
if (likely(!(flags & BCH_TRANS_COMMIT_skip_accounting_apply))) {
|
||||
ret = bch2_accounting_mem_mod_locked(trans, accounting_i_to_s_c(a), BCH_ACCOUNTING_normal);
|
||||
if (ret)
|
||||
goto revert_fs_usage;
|
||||
}
|
||||
}
|
||||
percpu_up_read(&c->mark_lock);
|
||||
|
||||
/* XXX: we only want to run this if deltas are nonzero */
|
||||
bch2_trans_account_disk_usage_change(trans);
|
||||
}
|
||||
|
||||
trans_for_each_update(trans, i)
|
||||
if (btree_node_type_has_atomic_triggers(i->bkey_type)) {
|
||||
@ -735,6 +739,40 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
|
||||
goto fatal_err;
|
||||
}
|
||||
|
||||
trans_for_each_update(trans, i) {
|
||||
enum bch_validate_flags invalid_flags = 0;
|
||||
|
||||
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
|
||||
invalid_flags |= BCH_VALIDATE_write|BCH_VALIDATE_commit;
|
||||
|
||||
ret = bch2_bkey_validate(c, bkey_i_to_s_c(i->k),
|
||||
i->bkey_type, invalid_flags);
|
||||
if (unlikely(ret)){
|
||||
bch2_trans_inconsistent(trans, "invalid bkey on insert from %s -> %ps\n",
|
||||
trans->fn, (void *) i->ip_allocated);
|
||||
goto fatal_err;
|
||||
}
|
||||
btree_insert_entry_checks(trans, i);
|
||||
}
|
||||
|
||||
for (struct jset_entry *i = trans->journal_entries;
|
||||
i != (void *) ((u64 *) trans->journal_entries + trans->journal_entries_u64s);
|
||||
i = vstruct_next(i)) {
|
||||
enum bch_validate_flags invalid_flags = 0;
|
||||
|
||||
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
|
||||
invalid_flags |= BCH_VALIDATE_write|BCH_VALIDATE_commit;
|
||||
|
||||
ret = bch2_journal_entry_validate(c, NULL, i,
|
||||
bcachefs_metadata_version_current,
|
||||
CPU_BIG_ENDIAN, invalid_flags);
|
||||
if (unlikely(ret)) {
|
||||
bch2_trans_inconsistent(trans, "invalid journal entry on insert from %s\n",
|
||||
trans->fn);
|
||||
goto fatal_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(!(flags & BCH_TRANS_COMMIT_no_journal_res))) {
|
||||
struct journal *j = &c->journal;
|
||||
struct jset_entry *entry;
|
||||
@ -798,7 +836,7 @@ revert_fs_usage:
|
||||
struct bkey_s_accounting a = bkey_i_to_s_accounting(entry2->start);
|
||||
|
||||
bch2_accounting_neg(a);
|
||||
bch2_accounting_mem_mod_locked(trans, a.c, false, false);
|
||||
bch2_accounting_mem_mod_locked(trans, a.c, BCH_ACCOUNTING_normal);
|
||||
bch2_accounting_neg(a);
|
||||
}
|
||||
percpu_up_read(&c->mark_lock);
|
||||
@ -1019,40 +1057,6 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
|
||||
if (ret)
|
||||
goto out_reset;
|
||||
|
||||
trans_for_each_update(trans, i) {
|
||||
enum bch_validate_flags invalid_flags = 0;
|
||||
|
||||
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
|
||||
invalid_flags |= BCH_VALIDATE_write|BCH_VALIDATE_commit;
|
||||
|
||||
ret = bch2_bkey_validate(c, bkey_i_to_s_c(i->k),
|
||||
i->bkey_type, invalid_flags);
|
||||
if (unlikely(ret)){
|
||||
bch2_trans_inconsistent(trans, "invalid bkey on insert from %s -> %ps\n",
|
||||
trans->fn, (void *) i->ip_allocated);
|
||||
return ret;
|
||||
}
|
||||
btree_insert_entry_checks(trans, i);
|
||||
}
|
||||
|
||||
for (struct jset_entry *i = trans->journal_entries;
|
||||
i != (void *) ((u64 *) trans->journal_entries + trans->journal_entries_u64s);
|
||||
i = vstruct_next(i)) {
|
||||
enum bch_validate_flags invalid_flags = 0;
|
||||
|
||||
if (!(flags & BCH_TRANS_COMMIT_no_journal_res))
|
||||
invalid_flags |= BCH_VALIDATE_write|BCH_VALIDATE_commit;
|
||||
|
||||
ret = bch2_journal_entry_validate(c, NULL, i,
|
||||
bcachefs_metadata_version_current,
|
||||
CPU_BIG_ENDIAN, invalid_flags);
|
||||
if (unlikely(ret)) {
|
||||
bch2_trans_inconsistent(trans, "invalid journal entry on insert from %s\n",
|
||||
trans->fn);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(!test_bit(BCH_FS_may_go_rw, &c->flags))) {
|
||||
ret = do_bch2_trans_commit_to_journal_replay(trans);
|
||||
goto out_reset;
|
||||
|
@ -220,7 +220,8 @@ static inline struct bkey_i *__bch2_bkey_make_mut_noupdate(struct btree_trans *t
|
||||
if (type && k.k->type != type)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
mut = bch2_trans_kmalloc_nomemzero(trans, bytes);
|
||||
/* extra padding for varint_decode_fast... */
|
||||
mut = bch2_trans_kmalloc_nomemzero(trans, bytes + 8);
|
||||
if (!IS_ERR(mut)) {
|
||||
bkey_reassemble(mut, k);
|
||||
|
||||
|
@ -281,7 +281,7 @@ int bch2_check_fix_ptrs(struct btree_trans *trans,
|
||||
goto err;
|
||||
|
||||
rcu_read_lock();
|
||||
bch2_bkey_drop_ptrs(bkey_i_to_s(new), ptr, !bch2_dev_rcu(c, ptr->dev));
|
||||
bch2_bkey_drop_ptrs(bkey_i_to_s(new), ptr, !bch2_dev_exists(c, ptr->dev));
|
||||
rcu_read_unlock();
|
||||
|
||||
if (level) {
|
||||
@ -565,11 +565,14 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
|
||||
s64 *sectors,
|
||||
enum btree_iter_update_trigger_flags flags)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
bool insert = !(flags & BTREE_TRIGGER_overwrite);
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
struct bch_fs *c = trans->c;
|
||||
u64 abs_sectors = ptr_disk_sectors(level ? btree_sectors(c) : k.k->size, p);
|
||||
*sectors = insert ? abs_sectors : -abs_sectors;
|
||||
|
||||
struct bch_dev *ca = bch2_dev_tryget(c, p.ptr.dev);
|
||||
if (unlikely(!ca)) {
|
||||
if (insert && p.ptr.dev != BCH_SB_MEMBER_INVALID)
|
||||
@ -579,8 +582,7 @@ static int bch2_trigger_pointer(struct btree_trans *trans,
|
||||
|
||||
struct bpos bucket;
|
||||
struct bch_backpointer bp;
|
||||
bch2_extent_ptr_to_bp(trans->c, ca, btree_id, level, k, p, entry, &bucket, &bp);
|
||||
*sectors = insert ? bp.bucket_len : -((s64) bp.bucket_len);
|
||||
__bch2_extent_ptr_to_bp(trans->c, ca, btree_id, level, k, p, entry, &bucket, &bp, abs_sectors);
|
||||
|
||||
if (flags & BTREE_TRIGGER_transactional) {
|
||||
struct bkey_i_alloc_v4 *a = bch2_trans_start_alloc_update(trans, bucket, 0);
|
||||
|
@ -639,7 +639,7 @@ int bch2_data_update_init(struct btree_trans *trans,
|
||||
|
||||
bch2_write_op_init(&m->op, c, io_opts);
|
||||
m->op.pos = bkey_start_pos(k.k);
|
||||
m->op.version = k.k->version;
|
||||
m->op.version = k.k->bversion;
|
||||
m->op.target = data_opts.target;
|
||||
m->op.write_point = wp;
|
||||
m->op.nr_replicas = 0;
|
||||
|
@ -134,6 +134,10 @@ int bch2_accounting_validate(struct bch_fs *c, struct bkey_s_c k,
|
||||
void *end = &acc_k + 1;
|
||||
int ret = 0;
|
||||
|
||||
bkey_fsck_err_on(bversion_zero(k.k->bversion),
|
||||
c, accounting_key_version_0,
|
||||
"accounting key with version=0");
|
||||
|
||||
switch (acc_k.type) {
|
||||
case BCH_DISK_ACCOUNTING_nr_inodes:
|
||||
end = field_end(acc_k, nr_inodes);
|
||||
@ -291,7 +295,7 @@ static int __bch2_accounting_mem_insert(struct bch_fs *c, struct bkey_s_c_accoun
|
||||
|
||||
struct accounting_mem_entry n = {
|
||||
.pos = a.k->p,
|
||||
.version = a.k->version,
|
||||
.bversion = a.k->bversion,
|
||||
.nr_counters = bch2_accounting_counters(a.k),
|
||||
.v[0] = __alloc_percpu_gfp(n.nr_counters * sizeof(u64),
|
||||
sizeof(u64), GFP_KERNEL),
|
||||
@ -319,11 +323,13 @@ err:
|
||||
return -BCH_ERR_ENOMEM_disk_accounting;
|
||||
}
|
||||
|
||||
int bch2_accounting_mem_insert(struct bch_fs *c, struct bkey_s_c_accounting a, bool gc)
|
||||
int bch2_accounting_mem_insert(struct bch_fs *c, struct bkey_s_c_accounting a,
|
||||
enum bch_accounting_mode mode)
|
||||
{
|
||||
struct bch_replicas_padded r;
|
||||
|
||||
if (accounting_to_replicas(&r.e, a.k->p) &&
|
||||
if (mode != BCH_ACCOUNTING_read &&
|
||||
accounting_to_replicas(&r.e, a.k->p) &&
|
||||
!bch2_replicas_marked_locked(c, &r.e))
|
||||
return -BCH_ERR_btree_insert_need_mark_replicas;
|
||||
|
||||
@ -566,7 +572,9 @@ int bch2_gc_accounting_done(struct bch_fs *c)
|
||||
struct { __BKEY_PADDED(k, BCH_ACCOUNTING_MAX_COUNTERS); } k_i;
|
||||
|
||||
accounting_key_init(&k_i.k, &acc_k, src_v, nr);
|
||||
bch2_accounting_mem_mod_locked(trans, bkey_i_to_s_c_accounting(&k_i.k), false, false);
|
||||
bch2_accounting_mem_mod_locked(trans,
|
||||
bkey_i_to_s_c_accounting(&k_i.k),
|
||||
BCH_ACCOUNTING_normal);
|
||||
|
||||
preempt_disable();
|
||||
struct bch_fs_usage_base *dst = this_cpu_ptr(c->usage);
|
||||
@ -589,30 +597,14 @@ fsck_err:
|
||||
static int accounting_read_key(struct btree_trans *trans, struct bkey_s_c k)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
if (k.k->type != KEY_TYPE_accounting)
|
||||
return 0;
|
||||
|
||||
percpu_down_read(&c->mark_lock);
|
||||
int ret = bch2_accounting_mem_mod_locked(trans, bkey_s_c_to_accounting(k), false, true);
|
||||
int ret = bch2_accounting_mem_mod_locked(trans, bkey_s_c_to_accounting(k),
|
||||
BCH_ACCOUNTING_read);
|
||||
percpu_up_read(&c->mark_lock);
|
||||
|
||||
if (bch2_accounting_key_is_zero(bkey_s_c_to_accounting(k)) &&
|
||||
ret == -BCH_ERR_btree_insert_need_mark_replicas)
|
||||
ret = 0;
|
||||
|
||||
struct disk_accounting_pos acc;
|
||||
bpos_to_disk_accounting_pos(&acc, k.k->p);
|
||||
|
||||
if (fsck_err_on(ret == -BCH_ERR_btree_insert_need_mark_replicas,
|
||||
trans, accounting_replicas_not_marked,
|
||||
"accounting not marked in superblock replicas\n %s",
|
||||
(bch2_accounting_key_to_text(&buf, &acc),
|
||||
buf.buf)))
|
||||
ret = bch2_accounting_update_sb_one(c, k.k->p);
|
||||
fsck_err:
|
||||
printbuf_exit(&buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -624,6 +616,7 @@ int bch2_accounting_read(struct bch_fs *c)
|
||||
{
|
||||
struct bch_accounting_mem *acc = &c->accounting;
|
||||
struct btree_trans *trans = bch2_trans_get(c);
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
int ret = for_each_btree_key(trans, iter,
|
||||
BTREE_ID_accounting, POS_MIN,
|
||||
@ -647,7 +640,7 @@ int bch2_accounting_read(struct bch_fs *c)
|
||||
accounting_pos_cmp, &k.k->p);
|
||||
|
||||
bool applied = idx < acc->k.nr &&
|
||||
bversion_cmp(acc->k.data[idx].version, k.k->version) >= 0;
|
||||
bversion_cmp(acc->k.data[idx].bversion, k.k->bversion) >= 0;
|
||||
|
||||
if (applied)
|
||||
continue;
|
||||
@ -655,7 +648,7 @@ int bch2_accounting_read(struct bch_fs *c)
|
||||
if (i + 1 < &darray_top(*keys) &&
|
||||
i[1].k->k.type == KEY_TYPE_accounting &&
|
||||
!journal_key_cmp(i, i + 1)) {
|
||||
BUG_ON(bversion_cmp(i[0].k->k.version, i[1].k->k.version) >= 0);
|
||||
WARN_ON(bversion_cmp(i[0].k->k.bversion, i[1].k->k.bversion) >= 0);
|
||||
|
||||
i[1].journal_seq = i[0].journal_seq;
|
||||
|
||||
@ -674,6 +667,45 @@ int bch2_accounting_read(struct bch_fs *c)
|
||||
keys->gap = keys->nr = dst - keys->data;
|
||||
|
||||
percpu_down_read(&c->mark_lock);
|
||||
for (unsigned i = 0; i < acc->k.nr; i++) {
|
||||
u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
|
||||
bch2_accounting_mem_read_counters(acc, i, v, ARRAY_SIZE(v), false);
|
||||
|
||||
if (bch2_is_zero(v, sizeof(v[0]) * acc->k.data[i].nr_counters))
|
||||
continue;
|
||||
|
||||
struct bch_replicas_padded r;
|
||||
if (!accounting_to_replicas(&r.e, acc->k.data[i].pos))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If the replicas entry is invalid it'll get cleaned up by
|
||||
* check_allocations:
|
||||
*/
|
||||
if (bch2_replicas_entry_validate(&r.e, c, &buf))
|
||||
continue;
|
||||
|
||||
struct disk_accounting_pos k;
|
||||
bpos_to_disk_accounting_pos(&k, acc->k.data[i].pos);
|
||||
|
||||
if (fsck_err_on(!bch2_replicas_marked_locked(c, &r.e),
|
||||
trans, accounting_replicas_not_marked,
|
||||
"accounting not marked in superblock replicas\n %s",
|
||||
(printbuf_reset(&buf),
|
||||
bch2_accounting_key_to_text(&buf, &k),
|
||||
buf.buf))) {
|
||||
/*
|
||||
* We're not RW yet and still single threaded, dropping
|
||||
* and retaking lock is ok:
|
||||
*/
|
||||
percpu_up_read(&c->mark_lock);
|
||||
ret = bch2_mark_replicas(c, &r.e);
|
||||
if (ret)
|
||||
goto fsck_err;
|
||||
percpu_down_read(&c->mark_lock);
|
||||
}
|
||||
}
|
||||
|
||||
preempt_disable();
|
||||
struct bch_fs_usage_base *usage = this_cpu_ptr(c->usage);
|
||||
|
||||
@ -709,8 +741,10 @@ int bch2_accounting_read(struct bch_fs *c)
|
||||
}
|
||||
}
|
||||
preempt_enable();
|
||||
fsck_err:
|
||||
percpu_up_read(&c->mark_lock);
|
||||
err:
|
||||
printbuf_exit(&buf);
|
||||
bch2_trans_put(trans);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
|
@ -36,8 +36,8 @@ static inline void bch2_accounting_accumulate(struct bkey_i_accounting *dst,
|
||||
|
||||
for (unsigned i = 0; i < bch2_accounting_counters(&dst->k); i++)
|
||||
dst->v.d[i] += src.v->d[i];
|
||||
if (bversion_cmp(dst->k.version, src.k->version) < 0)
|
||||
dst->k.version = src.k->version;
|
||||
if (bversion_cmp(dst->k.bversion, src.k->bversion) < 0)
|
||||
dst->k.bversion = src.k->bversion;
|
||||
}
|
||||
|
||||
static inline void fs_usage_data_type_to_base(struct bch_fs_usage_base *fs_usage,
|
||||
@ -103,23 +103,35 @@ static inline int accounting_pos_cmp(const void *_l, const void *_r)
|
||||
return bpos_cmp(*l, *r);
|
||||
}
|
||||
|
||||
int bch2_accounting_mem_insert(struct bch_fs *, struct bkey_s_c_accounting, bool);
|
||||
enum bch_accounting_mode {
|
||||
BCH_ACCOUNTING_normal,
|
||||
BCH_ACCOUNTING_gc,
|
||||
BCH_ACCOUNTING_read,
|
||||
};
|
||||
|
||||
int bch2_accounting_mem_insert(struct bch_fs *, struct bkey_s_c_accounting, enum bch_accounting_mode);
|
||||
void bch2_accounting_mem_gc(struct bch_fs *);
|
||||
|
||||
/*
|
||||
* Update in memory counters so they match the btree update we're doing; called
|
||||
* from transaction commit path
|
||||
*/
|
||||
static inline int bch2_accounting_mem_mod_locked(struct btree_trans *trans, struct bkey_s_c_accounting a, bool gc, bool read)
|
||||
static inline int bch2_accounting_mem_mod_locked(struct btree_trans *trans,
|
||||
struct bkey_s_c_accounting a,
|
||||
enum bch_accounting_mode mode)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct bch_accounting_mem *acc = &c->accounting;
|
||||
struct disk_accounting_pos acc_k;
|
||||
bpos_to_disk_accounting_pos(&acc_k, a.k->p);
|
||||
bool gc = mode == BCH_ACCOUNTING_gc;
|
||||
|
||||
EBUG_ON(gc && !acc->gc_running);
|
||||
|
||||
if (acc_k.type == BCH_DISK_ACCOUNTING_inum)
|
||||
return 0;
|
||||
|
||||
if (!gc && !read) {
|
||||
if (mode == BCH_ACCOUNTING_normal) {
|
||||
switch (acc_k.type) {
|
||||
case BCH_DISK_ACCOUNTING_persistent_reserved:
|
||||
trans->fs_usage_delta.reserved += acc_k.persistent_reserved.nr_replicas * a.v->d[0];
|
||||
@ -140,14 +152,11 @@ static inline int bch2_accounting_mem_mod_locked(struct btree_trans *trans, stru
|
||||
}
|
||||
}
|
||||
|
||||
struct bch_accounting_mem *acc = &c->accounting;
|
||||
unsigned idx;
|
||||
|
||||
EBUG_ON(gc && !acc->gc_running);
|
||||
|
||||
while ((idx = eytzinger0_find(acc->k.data, acc->k.nr, sizeof(acc->k.data[0]),
|
||||
accounting_pos_cmp, &a.k->p)) >= acc->k.nr) {
|
||||
int ret = bch2_accounting_mem_insert(c, a, gc);
|
||||
int ret = bch2_accounting_mem_insert(c, a, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -164,7 +173,7 @@ static inline int bch2_accounting_mem_mod_locked(struct btree_trans *trans, stru
|
||||
static inline int bch2_accounting_mem_add(struct btree_trans *trans, struct bkey_s_c_accounting a, bool gc)
|
||||
{
|
||||
percpu_down_read(&trans->c->mark_lock);
|
||||
int ret = bch2_accounting_mem_mod_locked(trans, a, gc, false);
|
||||
int ret = bch2_accounting_mem_mod_locked(trans, a, gc ? BCH_ACCOUNTING_gc : BCH_ACCOUNTING_normal);
|
||||
percpu_up_read(&trans->c->mark_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
struct accounting_mem_entry {
|
||||
struct bpos pos;
|
||||
struct bversion version;
|
||||
struct bversion bversion;
|
||||
unsigned nr_counters;
|
||||
u64 __percpu *v[2];
|
||||
};
|
||||
|
165
libbcachefs/ec.c
165
libbcachefs/ec.c
@ -838,6 +838,7 @@ int bch2_ec_read_extent(struct btree_trans *trans, struct bch_read_bio *rbio,
|
||||
struct bch_stripe *v;
|
||||
unsigned i, offset;
|
||||
const char *msg = NULL;
|
||||
struct printbuf msgbuf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
closure_init_stack(&cl);
|
||||
@ -896,7 +897,6 @@ out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
err:
|
||||
struct printbuf msgbuf = PRINTBUF;
|
||||
bch2_bkey_val_to_text(&msgbuf, c, orig_k);
|
||||
bch_err_ratelimited(c,
|
||||
"error doing reconstruct read: %s\n %s", msg, msgbuf.buf);
|
||||
@ -907,12 +907,12 @@ err:
|
||||
|
||||
/* stripe bucket accounting: */
|
||||
|
||||
static int __ec_stripe_mem_alloc(struct bch_fs *c, size_t idx)
|
||||
static int __ec_stripe_mem_alloc(struct bch_fs *c, size_t idx, gfp_t gfp)
|
||||
{
|
||||
ec_stripes_heap n, *h = &c->ec_stripes_heap;
|
||||
|
||||
if (idx >= h->size) {
|
||||
if (!init_heap(&n, max(1024UL, roundup_pow_of_two(idx + 1)), GFP_KERNEL))
|
||||
if (!init_heap(&n, max(1024UL, roundup_pow_of_two(idx + 1)), gfp))
|
||||
return -BCH_ERR_ENOMEM_ec_stripe_mem_alloc;
|
||||
|
||||
mutex_lock(&c->ec_stripes_heap_lock);
|
||||
@ -926,11 +926,11 @@ static int __ec_stripe_mem_alloc(struct bch_fs *c, size_t idx)
|
||||
free_heap(&n);
|
||||
}
|
||||
|
||||
if (!genradix_ptr_alloc(&c->stripes, idx, GFP_KERNEL))
|
||||
if (!genradix_ptr_alloc(&c->stripes, idx, gfp))
|
||||
return -BCH_ERR_ENOMEM_ec_stripe_mem_alloc;
|
||||
|
||||
if (c->gc_pos.phase != GC_PHASE_not_running &&
|
||||
!genradix_ptr_alloc(&c->gc_stripes, idx, GFP_KERNEL))
|
||||
!genradix_ptr_alloc(&c->gc_stripes, idx, gfp))
|
||||
return -BCH_ERR_ENOMEM_ec_stripe_mem_alloc;
|
||||
|
||||
return 0;
|
||||
@ -940,7 +940,7 @@ static int ec_stripe_mem_alloc(struct btree_trans *trans,
|
||||
struct btree_iter *iter)
|
||||
{
|
||||
return allocate_dropping_locks_errcode(trans,
|
||||
__ec_stripe_mem_alloc(trans->c, iter->pos.offset));
|
||||
__ec_stripe_mem_alloc(trans->c, iter->pos.offset, _gfp));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1721,6 +1721,8 @@ static int ec_new_stripe_alloc(struct bch_fs *c, struct ec_stripe_head *h)
|
||||
|
||||
static void ec_stripe_head_devs_update(struct bch_fs *c, struct ec_stripe_head *h)
|
||||
{
|
||||
struct bch_devs_mask devs = h->devs;
|
||||
|
||||
rcu_read_lock();
|
||||
h->devs = target_rw_devs(c, BCH_DATA_user, h->disk_label
|
||||
? group_to_target(h->disk_label - 1)
|
||||
@ -1762,7 +1764,10 @@ static void ec_stripe_head_devs_update(struct bch_fs *c, struct ec_stripe_head *
|
||||
h->nr_active_devs, h->redundancy + 2, err);
|
||||
}
|
||||
|
||||
if (h->s && !h->s->allocated)
|
||||
struct bch_devs_mask devs_leaving;
|
||||
bitmap_andnot(devs_leaving.d, devs.d, h->devs.d, BCH_SB_MEMBERS_MAX);
|
||||
|
||||
if (h->s && !h->s->allocated && dev_mask_nr(&devs_leaving))
|
||||
ec_stripe_new_cancel(c, h, -EINTR);
|
||||
|
||||
h->rw_devs_change_count = c->rw_devs_change_count;
|
||||
@ -1855,39 +1860,38 @@ err:
|
||||
static int new_stripe_alloc_buckets(struct btree_trans *trans, struct ec_stripe_head *h,
|
||||
enum bch_watermark watermark, struct closure *cl)
|
||||
{
|
||||
struct ec_stripe_new *s = h->s;
|
||||
struct bch_fs *c = trans->c;
|
||||
struct bch_devs_mask devs = h->devs;
|
||||
struct open_bucket *ob;
|
||||
struct open_buckets buckets;
|
||||
struct bch_stripe *v = &bkey_i_to_stripe(&s->new_stripe.key)->v;
|
||||
struct bch_stripe *v = &bkey_i_to_stripe(&h->s->new_stripe.key)->v;
|
||||
unsigned i, j, nr_have_parity = 0, nr_have_data = 0;
|
||||
bool have_cache = true;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(v->nr_blocks != s->nr_data + s->nr_parity);
|
||||
BUG_ON(v->nr_redundant != s->nr_parity);
|
||||
BUG_ON(v->nr_blocks != h->s->nr_data + h->s->nr_parity);
|
||||
BUG_ON(v->nr_redundant != h->s->nr_parity);
|
||||
|
||||
/* * We bypass the sector allocator which normally does this: */
|
||||
bitmap_and(devs.d, devs.d, c->rw_devs[BCH_DATA_user].d, BCH_SB_MEMBERS_MAX);
|
||||
|
||||
for_each_set_bit(i, s->blocks_gotten, v->nr_blocks) {
|
||||
for_each_set_bit(i, h->s->blocks_gotten, v->nr_blocks) {
|
||||
__clear_bit(v->ptrs[i].dev, devs.d);
|
||||
if (i < s->nr_data)
|
||||
if (i < h->s->nr_data)
|
||||
nr_have_data++;
|
||||
else
|
||||
nr_have_parity++;
|
||||
}
|
||||
|
||||
BUG_ON(nr_have_data > s->nr_data);
|
||||
BUG_ON(nr_have_parity > s->nr_parity);
|
||||
BUG_ON(nr_have_data > h->s->nr_data);
|
||||
BUG_ON(nr_have_parity > h->s->nr_parity);
|
||||
|
||||
buckets.nr = 0;
|
||||
if (nr_have_parity < s->nr_parity) {
|
||||
if (nr_have_parity < h->s->nr_parity) {
|
||||
ret = bch2_bucket_alloc_set_trans(trans, &buckets,
|
||||
&h->parity_stripe,
|
||||
&devs,
|
||||
s->nr_parity,
|
||||
h->s->nr_parity,
|
||||
&nr_have_parity,
|
||||
&have_cache, 0,
|
||||
BCH_DATA_parity,
|
||||
@ -1895,14 +1899,14 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans, struct ec_stripe_
|
||||
cl);
|
||||
|
||||
open_bucket_for_each(c, &buckets, ob, i) {
|
||||
j = find_next_zero_bit(s->blocks_gotten,
|
||||
s->nr_data + s->nr_parity,
|
||||
s->nr_data);
|
||||
BUG_ON(j >= s->nr_data + s->nr_parity);
|
||||
j = find_next_zero_bit(h->s->blocks_gotten,
|
||||
h->s->nr_data + h->s->nr_parity,
|
||||
h->s->nr_data);
|
||||
BUG_ON(j >= h->s->nr_data + h->s->nr_parity);
|
||||
|
||||
s->blocks[j] = buckets.v[i];
|
||||
h->s->blocks[j] = buckets.v[i];
|
||||
v->ptrs[j] = bch2_ob_ptr(c, ob);
|
||||
__set_bit(j, s->blocks_gotten);
|
||||
__set_bit(j, h->s->blocks_gotten);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
@ -1910,11 +1914,11 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans, struct ec_stripe_
|
||||
}
|
||||
|
||||
buckets.nr = 0;
|
||||
if (nr_have_data < s->nr_data) {
|
||||
if (nr_have_data < h->s->nr_data) {
|
||||
ret = bch2_bucket_alloc_set_trans(trans, &buckets,
|
||||
&h->block_stripe,
|
||||
&devs,
|
||||
s->nr_data,
|
||||
h->s->nr_data,
|
||||
&nr_have_data,
|
||||
&have_cache, 0,
|
||||
BCH_DATA_user,
|
||||
@ -1922,13 +1926,13 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans, struct ec_stripe_
|
||||
cl);
|
||||
|
||||
open_bucket_for_each(c, &buckets, ob, i) {
|
||||
j = find_next_zero_bit(s->blocks_gotten,
|
||||
s->nr_data, 0);
|
||||
BUG_ON(j >= s->nr_data);
|
||||
j = find_next_zero_bit(h->s->blocks_gotten,
|
||||
h->s->nr_data, 0);
|
||||
BUG_ON(j >= h->s->nr_data);
|
||||
|
||||
s->blocks[j] = buckets.v[i];
|
||||
h->s->blocks[j] = buckets.v[i];
|
||||
v->ptrs[j] = bch2_ob_ptr(c, ob);
|
||||
__set_bit(j, s->blocks_gotten);
|
||||
__set_bit(j, h->s->blocks_gotten);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
@ -1974,53 +1978,12 @@ static s64 get_existing_stripe(struct bch_fs *c,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_new_stripe_from_existing(struct bch_fs *c, struct ec_stripe_new *s)
|
||||
{
|
||||
struct bch_stripe *new_v = &bkey_i_to_stripe(&s->new_stripe.key)->v;
|
||||
struct bch_stripe *existing_v = &bkey_i_to_stripe(&s->existing_stripe.key)->v;
|
||||
unsigned i;
|
||||
|
||||
BUG_ON(existing_v->nr_redundant != s->nr_parity);
|
||||
s->nr_data = existing_v->nr_blocks -
|
||||
existing_v->nr_redundant;
|
||||
|
||||
int ret = ec_stripe_buf_init(&s->existing_stripe, 0, le16_to_cpu(existing_v->sectors));
|
||||
if (ret) {
|
||||
bch2_stripe_close(c, s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BUG_ON(s->existing_stripe.size != le16_to_cpu(existing_v->sectors));
|
||||
|
||||
/*
|
||||
* Free buckets we initially allocated - they might conflict with
|
||||
* blocks from the stripe we're reusing:
|
||||
*/
|
||||
for_each_set_bit(i, s->blocks_gotten, new_v->nr_blocks) {
|
||||
bch2_open_bucket_put(c, c->open_buckets + s->blocks[i]);
|
||||
s->blocks[i] = 0;
|
||||
}
|
||||
memset(s->blocks_gotten, 0, sizeof(s->blocks_gotten));
|
||||
memset(s->blocks_allocated, 0, sizeof(s->blocks_allocated));
|
||||
|
||||
for (i = 0; i < existing_v->nr_blocks; i++) {
|
||||
if (stripe_blockcount_get(existing_v, i)) {
|
||||
__set_bit(i, s->blocks_gotten);
|
||||
__set_bit(i, s->blocks_allocated);
|
||||
}
|
||||
|
||||
ec_block_io(c, &s->existing_stripe, READ, i, &s->iodone);
|
||||
}
|
||||
|
||||
bkey_copy(&s->new_stripe.key, &s->existing_stripe.key);
|
||||
s->have_existing_stripe = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __bch2_ec_stripe_head_reuse(struct btree_trans *trans, struct ec_stripe_head *h)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct bch_stripe *new_v = &bkey_i_to_stripe(&h->s->new_stripe.key)->v;
|
||||
struct bch_stripe *existing_v;
|
||||
unsigned i;
|
||||
s64 idx;
|
||||
int ret;
|
||||
|
||||
@ -2040,7 +2003,45 @@ static int __bch2_ec_stripe_head_reuse(struct btree_trans *trans, struct ec_stri
|
||||
return ret;
|
||||
}
|
||||
|
||||
return init_new_stripe_from_existing(c, h->s);
|
||||
existing_v = &bkey_i_to_stripe(&h->s->existing_stripe.key)->v;
|
||||
|
||||
BUG_ON(existing_v->nr_redundant != h->s->nr_parity);
|
||||
h->s->nr_data = existing_v->nr_blocks -
|
||||
existing_v->nr_redundant;
|
||||
|
||||
ret = ec_stripe_buf_init(&h->s->existing_stripe, 0, h->blocksize);
|
||||
if (ret) {
|
||||
bch2_stripe_close(c, h->s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BUG_ON(h->s->existing_stripe.size != h->blocksize);
|
||||
BUG_ON(h->s->existing_stripe.size != le16_to_cpu(existing_v->sectors));
|
||||
|
||||
/*
|
||||
* Free buckets we initially allocated - they might conflict with
|
||||
* blocks from the stripe we're reusing:
|
||||
*/
|
||||
for_each_set_bit(i, h->s->blocks_gotten, new_v->nr_blocks) {
|
||||
bch2_open_bucket_put(c, c->open_buckets + h->s->blocks[i]);
|
||||
h->s->blocks[i] = 0;
|
||||
}
|
||||
memset(h->s->blocks_gotten, 0, sizeof(h->s->blocks_gotten));
|
||||
memset(h->s->blocks_allocated, 0, sizeof(h->s->blocks_allocated));
|
||||
|
||||
for (i = 0; i < existing_v->nr_blocks; i++) {
|
||||
if (stripe_blockcount_get(existing_v, i)) {
|
||||
__set_bit(i, h->s->blocks_gotten);
|
||||
__set_bit(i, h->s->blocks_allocated);
|
||||
}
|
||||
|
||||
ec_block_io(c, &h->s->existing_stripe, READ, i, &h->s->iodone);
|
||||
}
|
||||
|
||||
bkey_copy(&h->s->new_stripe.key, &h->s->existing_stripe.key);
|
||||
h->s->have_existing_stripe = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __bch2_ec_stripe_head_reserve(struct btree_trans *trans, struct ec_stripe_head *h)
|
||||
@ -2238,14 +2239,8 @@ static int bch2_invalidate_stripe_to_dev(struct btree_trans *trans, struct bkey_
|
||||
|
||||
struct bkey_ptrs ptrs = bch2_bkey_ptrs(bkey_i_to_s(&s->k_i));
|
||||
bkey_for_each_ptr(ptrs, ptr)
|
||||
if (ptr->dev == k_a.k->p.inode) {
|
||||
if (stripe_blockcount_get(&s->v, ptr - &ptrs.start->ptr)) {
|
||||
bch_err(trans->c, "trying to invalidate device in stripe when stripe block not empty");
|
||||
ret = -BCH_ERR_invalidate_stripe_to_dev;
|
||||
goto err;
|
||||
}
|
||||
if (ptr->dev == k_a.k->p.inode)
|
||||
ptr->dev = BCH_SB_MEMBER_INVALID;
|
||||
}
|
||||
|
||||
sectors = -sectors;
|
||||
|
||||
@ -2259,11 +2254,11 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bch2_dev_remove_stripes(struct bch_fs *c, struct bch_dev *ca)
|
||||
int bch2_dev_remove_stripes(struct bch_fs *c, unsigned dev_idx)
|
||||
{
|
||||
return bch2_trans_run(c,
|
||||
for_each_btree_key_upto_commit(trans, iter,
|
||||
BTREE_ID_alloc, POS(ca->dev_idx, 0), POS(ca->dev_idx, U64_MAX),
|
||||
BTREE_ID_alloc, POS(dev_idx, 0), POS(dev_idx, U64_MAX),
|
||||
BTREE_ITER_intent, k,
|
||||
NULL, NULL, 0, ({
|
||||
bch2_invalidate_stripe_to_dev(trans, k);
|
||||
@ -2338,7 +2333,7 @@ int bch2_stripes_read(struct bch_fs *c)
|
||||
if (k.k->type != KEY_TYPE_stripe)
|
||||
continue;
|
||||
|
||||
ret = __ec_stripe_mem_alloc(c, k.k->p.offset);
|
||||
ret = __ec_stripe_mem_alloc(c, k.k->p.offset, GFP_KERNEL);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -254,7 +254,7 @@ static inline void ec_stripe_new_put(struct bch_fs *c, struct ec_stripe_new *s,
|
||||
}
|
||||
}
|
||||
|
||||
int bch2_dev_remove_stripes(struct bch_fs *, struct bch_dev *);
|
||||
int bch2_dev_remove_stripes(struct bch_fs *, unsigned);
|
||||
|
||||
void bch2_ec_stop_dev(struct bch_fs *, struct bch_dev *);
|
||||
void bch2_fs_ec_stop(struct bch_fs *);
|
||||
|
@ -239,7 +239,19 @@ int __bch2_fsck_err(struct bch_fs *c,
|
||||
if (!c)
|
||||
c = trans->c;
|
||||
|
||||
WARN_ON(!trans && bch2_current_has_btree_trans(c));
|
||||
/*
|
||||
* Ugly: if there's a transaction in the current task it has to be
|
||||
* passed in to unlock if we prompt for user input.
|
||||
*
|
||||
* But, plumbing a transaction and transaction restarts into
|
||||
* bkey_validate() is problematic.
|
||||
*
|
||||
* So:
|
||||
* - make all bkey errors AUTOFIX, they're simple anyways (we just
|
||||
* delete the key)
|
||||
* - and we don't need to warn if we're not prompting
|
||||
*/
|
||||
WARN_ON(!(flags & FSCK_AUTOFIX) && !trans && bch2_current_has_btree_trans(c));
|
||||
|
||||
if ((flags & FSCK_CAN_FIX) &&
|
||||
test_bit(err, c->sb.errors_silent))
|
||||
|
@ -184,7 +184,7 @@ do { \
|
||||
ret = -BCH_ERR_fsck_delete_bkey; \
|
||||
goto fsck_err; \
|
||||
} \
|
||||
int _ret = __bch2_bkey_fsck_err(c, k, FSCK_CAN_FIX, \
|
||||
int _ret = __bch2_bkey_fsck_err(c, k, FSCK_CAN_FIX|FSCK_AUTOFIX,\
|
||||
BCH_FSCK_ERR_##_err_type, \
|
||||
_err_msg, ##__VA_ARGS__); \
|
||||
if (_ret != -BCH_ERR_fsck_fix && \
|
||||
|
@ -185,12 +185,17 @@ static void __wait_on_freeing_inode(struct inode *inode)
|
||||
finish_wait(wq, &wait.wq_entry);
|
||||
}
|
||||
|
||||
struct bch_inode_info *__bch2_inode_hash_find(struct bch_fs *c, subvol_inum inum)
|
||||
{
|
||||
return rhashtable_lookup_fast(&c->vfs_inodes_table, &inum, bch2_vfs_inodes_params);
|
||||
}
|
||||
|
||||
static struct bch_inode_info *bch2_inode_hash_find(struct bch_fs *c, struct btree_trans *trans,
|
||||
subvol_inum inum)
|
||||
{
|
||||
struct bch_inode_info *inode;
|
||||
repeat:
|
||||
inode = rhashtable_lookup_fast(&c->vfs_inodes_table, &inum, bch2_vfs_inodes_params);
|
||||
inode = __bch2_inode_hash_find(c, inum);
|
||||
if (inode) {
|
||||
spin_lock(&inode->v.i_lock);
|
||||
if (!test_bit(EI_INODE_HASHED, &inode->ei_flags)) {
|
||||
@ -273,6 +278,14 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
#define memalloc_flags_do(_flags, _do) \
|
||||
({ \
|
||||
unsigned _saved_flags = memalloc_flags_save(_flags); \
|
||||
typeof(_do) _ret = _do; \
|
||||
memalloc_noreclaim_restore(_saved_flags); \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
static struct inode *bch2_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
BUG();
|
||||
@ -1698,11 +1711,16 @@ static void bch2_evict_inode(struct inode *vinode)
|
||||
{
|
||||
struct bch_fs *c = vinode->i_sb->s_fs_info;
|
||||
struct bch_inode_info *inode = to_bch_ei(vinode);
|
||||
bool delete = !inode->v.i_nlink && !is_bad_inode(&inode->v);
|
||||
|
||||
/*
|
||||
* evict() has waited for outstanding writeback, we'll do no more IO
|
||||
* through this inode: it's safe to remove from VFS inode hashtable here
|
||||
*
|
||||
* Do that now so that other threads aren't blocked from pulling it back
|
||||
* in, there's no reason for them to be:
|
||||
*/
|
||||
if (!delete)
|
||||
bch2_inode_hash_remove(c, inode);
|
||||
|
||||
truncate_inode_pages_final(&inode->v.i_data);
|
||||
@ -1711,12 +1729,18 @@ static void bch2_evict_inode(struct inode *vinode)
|
||||
|
||||
BUG_ON(!is_bad_inode(&inode->v) && inode->ei_quota_reserved);
|
||||
|
||||
if (!inode->v.i_nlink && !is_bad_inode(&inode->v)) {
|
||||
if (delete) {
|
||||
bch2_quota_acct(c, inode->ei_qid, Q_SPC, -((s64) inode->v.i_blocks),
|
||||
KEY_TYPE_QUOTA_WARN);
|
||||
bch2_quota_acct(c, inode->ei_qid, Q_INO, -1,
|
||||
KEY_TYPE_QUOTA_WARN);
|
||||
bch2_inode_rm(c, inode_inum(inode));
|
||||
|
||||
/*
|
||||
* If we are deleting, we need it present in the vfs hash table
|
||||
* so that fsck can check if unlinked inodes are still open:
|
||||
*/
|
||||
bch2_inode_hash_remove(c, inode);
|
||||
}
|
||||
|
||||
mutex_lock(&c->vfs_inodes_lock);
|
||||
@ -1910,6 +1934,7 @@ static int bch2_show_options(struct seq_file *seq, struct dentry *root)
|
||||
|
||||
bch2_opts_to_text(&buf, c->opts, c, c->disk_sb.sb,
|
||||
OPT_MOUNT, OPT_HIDDEN, OPT_SHOW_MOUNT_STYLE);
|
||||
printbuf_nul_terminate(&buf);
|
||||
seq_puts(seq, buf.buf);
|
||||
|
||||
int ret = buf.allocation_failure ? -ENOMEM : 0;
|
||||
|
@ -146,6 +146,8 @@ struct bch_inode_info *
|
||||
__bch2_create(struct mnt_idmap *, struct bch_inode_info *,
|
||||
struct dentry *, umode_t, dev_t, subvol_inum, unsigned);
|
||||
|
||||
struct bch_inode_info *__bch2_inode_hash_find(struct bch_fs *, subvol_inum);
|
||||
|
||||
int bch2_fs_quota_transfer(struct bch_fs *,
|
||||
struct bch_inode_info *,
|
||||
struct bch_qid,
|
||||
@ -196,6 +198,11 @@ int bch2_vfs_init(void);
|
||||
|
||||
#define bch2_inode_update_after_write(_trans, _inode, _inode_u, _fields) ({ do {} while (0); })
|
||||
|
||||
static inline struct bch_inode_info *__bch2_inode_hash_find(struct bch_fs *c, subvol_inum inum)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void bch2_evict_subvolume_inodes(struct bch_fs *c,
|
||||
snapshot_id_list *s) {}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "darray.h"
|
||||
#include "dirent.h"
|
||||
#include "error.h"
|
||||
#include "fs.h"
|
||||
#include "fs-common.h"
|
||||
#include "fsck.h"
|
||||
#include "inode.h"
|
||||
@ -20,6 +21,49 @@
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/dcache.h> /* struct qstr */
|
||||
|
||||
static bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
|
||||
struct bkey_s_c_dirent d)
|
||||
{
|
||||
return inode->bi_dir == d.k->p.inode &&
|
||||
inode->bi_dir_offset == d.k->p.offset;
|
||||
}
|
||||
|
||||
static bool dirent_points_to_inode_nowarn(struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
if (d.v->d_type == DT_SUBVOL
|
||||
? le32_to_cpu(d.v->d_child_subvol) == inode->bi_subvol
|
||||
: le64_to_cpu(d.v->d_inum) == inode->bi_inum)
|
||||
return 0;
|
||||
return -BCH_ERR_ENOENT_dirent_doesnt_match_inode;
|
||||
}
|
||||
|
||||
static void dirent_inode_mismatch_msg(struct printbuf *out,
|
||||
struct bch_fs *c,
|
||||
struct bkey_s_c_dirent dirent,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
prt_str(out, "inode points to dirent that does not point back:");
|
||||
prt_newline(out);
|
||||
bch2_bkey_val_to_text(out, c, dirent.s_c);
|
||||
prt_newline(out);
|
||||
bch2_inode_unpacked_to_text(out, inode);
|
||||
}
|
||||
|
||||
static int dirent_points_to_inode(struct bch_fs *c,
|
||||
struct bkey_s_c_dirent dirent,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
int ret = dirent_points_to_inode_nowarn(dirent, inode);
|
||||
if (ret) {
|
||||
struct printbuf buf = PRINTBUF;
|
||||
dirent_inode_mismatch_msg(&buf, c, dirent, inode);
|
||||
bch_warn(c, "%s", buf.buf);
|
||||
printbuf_exit(&buf);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: this is handling transaction restarts without returning
|
||||
* -BCH_ERR_transaction_restart_nested, this is not how we do things anymore:
|
||||
@ -345,14 +389,17 @@ static int reattach_inode(struct btree_trans *trans,
|
||||
static int remove_backpointer(struct btree_trans *trans,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
struct btree_iter iter;
|
||||
struct bkey_s_c_dirent d;
|
||||
int ret;
|
||||
if (!inode->bi_dir)
|
||||
return 0;
|
||||
|
||||
d = bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_dirents,
|
||||
POS(inode->bi_dir, inode->bi_dir_offset), 0,
|
||||
struct bch_fs *c = trans->c;
|
||||
struct btree_iter iter;
|
||||
struct bkey_s_c_dirent d =
|
||||
bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_dirents,
|
||||
SPOS(inode->bi_dir, inode->bi_dir_offset, inode->bi_snapshot), 0,
|
||||
dirent);
|
||||
ret = bkey_err(d) ?:
|
||||
int ret = bkey_err(d) ?:
|
||||
dirent_points_to_inode(c, d, inode) ?:
|
||||
__remove_dirent(trans, d.k->p);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
return ret;
|
||||
@ -370,6 +417,7 @@ static int reattach_subvol(struct btree_trans *trans, struct bkey_s_c_subvolume
|
||||
return ret;
|
||||
|
||||
ret = remove_backpointer(trans, &inode);
|
||||
if (!bch2_err_matches(ret, ENOENT))
|
||||
bch_err_msg(c, ret, "removing dirent");
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -625,12 +673,12 @@ static int ref_visible2(struct bch_fs *c,
|
||||
struct inode_walker_entry {
|
||||
struct bch_inode_unpacked inode;
|
||||
u32 snapshot;
|
||||
bool seen_this_pos;
|
||||
u64 count;
|
||||
};
|
||||
|
||||
struct inode_walker {
|
||||
bool first_this_inode;
|
||||
bool have_inodes;
|
||||
bool recalculate_sums;
|
||||
struct bpos last_pos;
|
||||
|
||||
@ -668,6 +716,12 @@ static int get_inodes_all_snapshots(struct btree_trans *trans,
|
||||
struct bkey_s_c k;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We no longer have inodes for w->last_pos; clear this to avoid
|
||||
* screwing up check_i_sectors/check_subdir_count if we take a
|
||||
* transaction restart here:
|
||||
*/
|
||||
w->have_inodes = false;
|
||||
w->recalculate_sums = false;
|
||||
w->inodes.nr = 0;
|
||||
|
||||
@ -685,6 +739,7 @@ static int get_inodes_all_snapshots(struct btree_trans *trans,
|
||||
return ret;
|
||||
|
||||
w->first_this_inode = true;
|
||||
w->have_inodes = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -739,9 +794,6 @@ static struct inode_walker_entry *walk_inode(struct btree_trans *trans,
|
||||
int ret = get_inodes_all_snapshots(trans, w, k.k->p.inode);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
} else if (bkey_cmp(w->last_pos, k.k->p)) {
|
||||
darray_for_each(w->inodes, i)
|
||||
i->seen_this_pos = false;
|
||||
}
|
||||
|
||||
w->last_pos = k.k->p;
|
||||
@ -895,21 +947,6 @@ static struct bkey_s_c_dirent inode_get_dirent(struct btree_trans *trans,
|
||||
return dirent_get_by_pos(trans, iter, SPOS(inode->bi_dir, inode->bi_dir_offset, *snapshot));
|
||||
}
|
||||
|
||||
static bool inode_points_to_dirent(struct bch_inode_unpacked *inode,
|
||||
struct bkey_s_c_dirent d)
|
||||
{
|
||||
return inode->bi_dir == d.k->p.inode &&
|
||||
inode->bi_dir_offset == d.k->p.offset;
|
||||
}
|
||||
|
||||
static bool dirent_points_to_inode(struct bkey_s_c_dirent d,
|
||||
struct bch_inode_unpacked *inode)
|
||||
{
|
||||
return d.v->d_type == DT_SUBVOL
|
||||
? le32_to_cpu(d.v->d_child_subvol) == inode->bi_subvol
|
||||
: le64_to_cpu(d.v->d_inum) == inode->bi_inum;
|
||||
}
|
||||
|
||||
static int check_inode_deleted_list(struct btree_trans *trans, struct bpos p)
|
||||
{
|
||||
struct btree_iter iter;
|
||||
@ -919,13 +956,14 @@ static int check_inode_deleted_list(struct btree_trans *trans, struct bpos p)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_inode_dirent_inode(struct btree_trans *trans, struct bkey_s_c inode_k,
|
||||
static int check_inode_dirent_inode(struct btree_trans *trans,
|
||||
struct bch_inode_unpacked *inode,
|
||||
u32 inode_snapshot, bool *write_inode)
|
||||
bool *write_inode)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
|
||||
u32 inode_snapshot = inode->bi_snapshot;
|
||||
struct btree_iter dirent_iter = {};
|
||||
struct bkey_s_c_dirent d = inode_get_dirent(trans, &dirent_iter, inode, &inode_snapshot);
|
||||
int ret = bkey_err(d);
|
||||
@ -935,13 +973,13 @@ static int check_inode_dirent_inode(struct btree_trans *trans, struct bkey_s_c i
|
||||
if (fsck_err_on(ret,
|
||||
trans, inode_points_to_missing_dirent,
|
||||
"inode points to missing dirent\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, inode_k), buf.buf)) ||
|
||||
fsck_err_on(!ret && !dirent_points_to_inode(d, inode),
|
||||
(bch2_inode_unpacked_to_text(&buf, inode), buf.buf)) ||
|
||||
fsck_err_on(!ret && dirent_points_to_inode_nowarn(d, inode),
|
||||
trans, inode_points_to_wrong_dirent,
|
||||
"inode points to dirent that does not point back:\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, inode_k),
|
||||
prt_newline(&buf),
|
||||
bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
|
||||
"%s",
|
||||
(printbuf_reset(&buf),
|
||||
dirent_inode_mismatch_msg(&buf, c, d, inode),
|
||||
buf.buf))) {
|
||||
/*
|
||||
* We just clear the backpointer fields for now. If we find a
|
||||
* dirent that points to this inode in check_dirents(), we'll
|
||||
@ -962,6 +1000,22 @@ fsck_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool bch2_inode_is_open(struct bch_fs *c, struct bpos p)
|
||||
{
|
||||
subvol_inum inum = {
|
||||
.subvol = snapshot_t(c, p.snapshot)->subvol,
|
||||
.inum = p.offset,
|
||||
};
|
||||
|
||||
/* snapshot tree corruption, can't safely delete */
|
||||
if (!inum.subvol) {
|
||||
bch_warn_ratelimited(c, "%s(): snapshot %u has no subvol, unlinked but can't safely delete", __func__, p.snapshot);
|
||||
return true;
|
||||
}
|
||||
|
||||
return __bch2_inode_hash_find(c, inum) != NULL;
|
||||
}
|
||||
|
||||
static int check_inode(struct btree_trans *trans,
|
||||
struct btree_iter *iter,
|
||||
struct bkey_s_c k,
|
||||
@ -1028,6 +1082,17 @@ static int check_inode(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (u.bi_flags & BCH_INODE_unlinked) {
|
||||
if (!test_bit(BCH_FS_started, &c->flags)) {
|
||||
/*
|
||||
* If we're not in online fsck, don't delete unlinked
|
||||
* inodes, just make sure they're on the deleted list.
|
||||
*
|
||||
* They might be referred to by a logged operation -
|
||||
* i.e. we might have crashed in the middle of a
|
||||
* truncate on an unlinked but open file - so we want to
|
||||
* let the delete_dead_inodes kill it after resuming
|
||||
* logged ops.
|
||||
*/
|
||||
ret = check_inode_deleted_list(trans, k.k->p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -1036,21 +1101,25 @@ static int check_inode(struct btree_trans *trans,
|
||||
trans, unlinked_inode_not_on_deleted_list,
|
||||
"inode %llu:%u unlinked, but not on deleted list",
|
||||
u.bi_inum, k.k->p.snapshot);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (u.bi_flags & BCH_INODE_unlinked &&
|
||||
(!c->sb.clean ||
|
||||
fsck_err(trans, inode_unlinked_but_clean,
|
||||
"filesystem marked clean, but inode %llu unlinked",
|
||||
u.bi_inum))) {
|
||||
ret = bch2_btree_bit_mod_buffered(trans, BTREE_ID_deleted_inodes, k.k->p, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
if (fsck_err_on(bch2_inode_is_open(c, k.k->p),
|
||||
trans, inode_unlinked_and_not_open,
|
||||
"inode %llu%u unlinked and not open",
|
||||
u.bi_inum, u.bi_snapshot)) {
|
||||
ret = bch2_inode_rm_snapshot(trans, u.bi_inum, iter->pos.snapshot);
|
||||
bch_err_msg(c, ret, "in fsck deleting inode");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* i_size_dirty is vestigal, since we now have logged ops for truncate * */
|
||||
if (u.bi_flags & BCH_INODE_i_size_dirty &&
|
||||
(!c->sb.clean ||
|
||||
(!test_bit(BCH_FS_clean_recovery, &c->flags) ||
|
||||
fsck_err(trans, inode_i_size_dirty_but_clean,
|
||||
"filesystem marked clean, but inode %llu has i_size dirty",
|
||||
u.bi_inum))) {
|
||||
@ -1079,8 +1148,9 @@ static int check_inode(struct btree_trans *trans,
|
||||
do_update = true;
|
||||
}
|
||||
|
||||
/* i_sectors_dirty is vestigal, i_sectors is always updated transactionally */
|
||||
if (u.bi_flags & BCH_INODE_i_sectors_dirty &&
|
||||
(!c->sb.clean ||
|
||||
(!test_bit(BCH_FS_clean_recovery, &c->flags) ||
|
||||
fsck_err(trans, inode_i_sectors_dirty_but_clean,
|
||||
"filesystem marked clean, but inode %llu has i_sectors dirty",
|
||||
u.bi_inum))) {
|
||||
@ -1108,7 +1178,7 @@ static int check_inode(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
if (u.bi_dir || u.bi_dir_offset) {
|
||||
ret = check_inode_dirent_inode(trans, k, &u, k.k->p.snapshot, &do_update);
|
||||
ret = check_inode_dirent_inode(trans, &u, &do_update);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
@ -1537,10 +1607,10 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
struct bkey_s_c k,
|
||||
struct inode_walker *inode,
|
||||
struct snapshots_seen *s,
|
||||
struct extent_ends *extent_ends)
|
||||
struct extent_ends *extent_ends,
|
||||
struct disk_reservation *res)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct inode_walker_entry *i;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
@ -1550,7 +1620,7 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (inode->last_pos.inode != k.k->p.inode) {
|
||||
if (inode->last_pos.inode != k.k->p.inode && inode->have_inodes) {
|
||||
ret = check_i_sectors(trans, inode);
|
||||
if (ret)
|
||||
goto err;
|
||||
@ -1560,12 +1630,12 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
i = walk_inode(trans, inode, k);
|
||||
ret = PTR_ERR_OR_ZERO(i);
|
||||
struct inode_walker_entry *extent_i = walk_inode(trans, inode, k);
|
||||
ret = PTR_ERR_OR_ZERO(extent_i);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = check_key_has_inode(trans, iter, inode, i, k);
|
||||
ret = check_key_has_inode(trans, iter, inode, extent_i, k);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -1574,24 +1644,19 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
&inode->recalculate_sums);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check inodes in reverse order, from oldest snapshots to newest,
|
||||
* starting from the inode that matches this extent's snapshot. If we
|
||||
* didn't have one, iterate over all inodes:
|
||||
* Check inodes in reverse order, from oldest snapshots to
|
||||
* newest, starting from the inode that matches this extent's
|
||||
* snapshot. If we didn't have one, iterate over all inodes:
|
||||
*/
|
||||
if (!i)
|
||||
i = &darray_last(inode->inodes);
|
||||
|
||||
for (;
|
||||
for (struct inode_walker_entry *i = extent_i ?: &darray_last(inode->inodes);
|
||||
inode->inodes.data && i >= inode->inodes.data;
|
||||
--i) {
|
||||
if (i->snapshot > k.k->p.snapshot ||
|
||||
!key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot))
|
||||
continue;
|
||||
|
||||
if (k.k->type != KEY_TYPE_whiteout) {
|
||||
if (fsck_err_on(!(i->inode.bi_flags & BCH_INODE_i_size_dirty) &&
|
||||
k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 &&
|
||||
!bkey_extent_is_reservation(k),
|
||||
@ -1611,13 +1676,25 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
goto err;
|
||||
|
||||
iter->k.type = KEY_TYPE_whiteout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bkey_extent_is_allocation(k.k))
|
||||
ret = bch2_trans_commit(trans, res, NULL, BCH_TRANS_COMMIT_no_enospc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (bkey_extent_is_allocation(k.k)) {
|
||||
for (struct inode_walker_entry *i = extent_i ?: &darray_last(inode->inodes);
|
||||
inode->inodes.data && i >= inode->inodes.data;
|
||||
--i) {
|
||||
if (i->snapshot > k.k->p.snapshot ||
|
||||
!key_visible_in_snapshot(c, s, i->snapshot, k.k->p.snapshot))
|
||||
continue;
|
||||
|
||||
i->count += k.k->size;
|
||||
}
|
||||
|
||||
i->seen_this_pos = true;
|
||||
}
|
||||
|
||||
if (k.k->type != KEY_TYPE_whiteout) {
|
||||
@ -1648,13 +1725,11 @@ int bch2_check_extents(struct bch_fs *c)
|
||||
extent_ends_init(&extent_ends);
|
||||
|
||||
int ret = bch2_trans_run(c,
|
||||
for_each_btree_key_commit(trans, iter, BTREE_ID_extents,
|
||||
for_each_btree_key(trans, iter, BTREE_ID_extents,
|
||||
POS(BCACHEFS_ROOT_INO, 0),
|
||||
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
|
||||
&res, NULL,
|
||||
BCH_TRANS_COMMIT_no_enospc, ({
|
||||
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, ({
|
||||
bch2_disk_reservation_put(c, &res);
|
||||
check_extent(trans, &iter, k, &w, &s, &extent_ends) ?:
|
||||
check_extent(trans, &iter, k, &w, &s, &extent_ends, &res) ?:
|
||||
check_extent_overbig(trans, &iter, k);
|
||||
})) ?:
|
||||
check_i_sectors_notnested(trans, &w));
|
||||
@ -1740,6 +1815,7 @@ static int check_dirent_inode_dirent(struct btree_trans *trans,
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
struct btree_iter bp_iter = { NULL };
|
||||
int ret = 0;
|
||||
|
||||
if (inode_points_to_dirent(target, d))
|
||||
@ -1752,7 +1828,7 @@ static int check_dirent_inode_dirent(struct btree_trans *trans,
|
||||
prt_printf(&buf, "\n "),
|
||||
bch2_inode_unpacked_to_text(&buf, target),
|
||||
buf.buf)))
|
||||
goto out_noiter;
|
||||
goto err;
|
||||
|
||||
if (!target->bi_dir &&
|
||||
!target->bi_dir_offset) {
|
||||
@ -1761,7 +1837,6 @@ static int check_dirent_inode_dirent(struct btree_trans *trans,
|
||||
return __bch2_fsck_write_inode(trans, target, target_snapshot);
|
||||
}
|
||||
|
||||
struct btree_iter bp_iter = { NULL };
|
||||
struct bkey_s_c_dirent bp_dirent = dirent_get_by_pos(trans, &bp_iter,
|
||||
SPOS(target->bi_dir, target->bi_dir_offset, target_snapshot));
|
||||
ret = bkey_err(bp_dirent);
|
||||
@ -1822,7 +1897,6 @@ out:
|
||||
err:
|
||||
fsck_err:
|
||||
bch2_trans_iter_exit(trans, &bp_iter);
|
||||
out_noiter:
|
||||
printbuf_exit(&buf);
|
||||
bch_err_fn(c, ret);
|
||||
return ret;
|
||||
@ -2057,7 +2131,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (k.k->type == KEY_TYPE_whiteout)
|
||||
goto out;
|
||||
|
||||
if (dir->last_pos.inode != k.k->p.inode) {
|
||||
if (dir->last_pos.inode != k.k->p.inode && dir->have_inodes) {
|
||||
ret = check_subdir_count(trans, dir);
|
||||
if (ret)
|
||||
goto err;
|
||||
@ -2119,11 +2193,15 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (d.v->d_type == DT_DIR)
|
||||
for_each_visible_inode(c, s, dir, d.k->p.snapshot, i)
|
||||
i->count++;
|
||||
}
|
||||
out:
|
||||
err:
|
||||
fsck_err:
|
||||
@ -2146,12 +2224,9 @@ int bch2_check_dirents(struct bch_fs *c)
|
||||
snapshots_seen_init(&s);
|
||||
|
||||
int ret = bch2_trans_run(c,
|
||||
for_each_btree_key_commit(trans, iter, BTREE_ID_dirents,
|
||||
for_each_btree_key(trans, iter, BTREE_ID_dirents,
|
||||
POS(BCACHEFS_ROOT_INO, 0),
|
||||
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots,
|
||||
k,
|
||||
NULL, NULL,
|
||||
BCH_TRANS_COMMIT_no_enospc,
|
||||
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
|
||||
check_dirent(trans, &iter, k, &hash_info, &dir, &target, &s)) ?:
|
||||
check_subdir_count_notnested(trans, &dir));
|
||||
|
||||
@ -2296,22 +2371,6 @@ static bool darray_u32_has(darray_u32 *d, u32 v)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've checked that inode backpointers point to valid dirents; here, it's
|
||||
* sufficient to check that the subvolume root has a dirent:
|
||||
*/
|
||||
static int subvol_has_dirent(struct btree_trans *trans, struct bkey_s_c_subvolume s)
|
||||
{
|
||||
struct bch_inode_unpacked inode;
|
||||
int ret = bch2_inode_find_by_inum_trans(trans,
|
||||
(subvol_inum) { s.k->p.offset, le64_to_cpu(s.v->inode) },
|
||||
&inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return inode.bi_dir != 0;
|
||||
}
|
||||
|
||||
static int check_subvol_path(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c k)
|
||||
{
|
||||
struct bch_fs *c = trans->c;
|
||||
@ -2330,14 +2389,24 @@ static int check_subvol_path(struct btree_trans *trans, struct btree_iter *iter,
|
||||
|
||||
struct bkey_s_c_subvolume s = bkey_s_c_to_subvolume(k);
|
||||
|
||||
ret = subvol_has_dirent(trans, s);
|
||||
if (ret < 0)
|
||||
struct bch_inode_unpacked subvol_root;
|
||||
ret = bch2_inode_find_by_inum_trans(trans,
|
||||
(subvol_inum) { s.k->p.offset, le64_to_cpu(s.v->inode) },
|
||||
&subvol_root);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (fsck_err_on(!ret,
|
||||
/*
|
||||
* We've checked that inode backpointers point to valid dirents;
|
||||
* here, it's sufficient to check that the subvolume root has a
|
||||
* dirent:
|
||||
*/
|
||||
if (fsck_err_on(!subvol_root.bi_dir,
|
||||
trans, subvol_unreachable,
|
||||
"unreachable subvolume %s",
|
||||
(bch2_bkey_val_to_text(&buf, c, s.s_c),
|
||||
prt_newline(&buf),
|
||||
bch2_inode_unpacked_to_text(&buf, &subvol_root),
|
||||
buf.buf))) {
|
||||
ret = reattach_subvol(trans, s);
|
||||
break;
|
||||
@ -2432,10 +2501,8 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
break;
|
||||
|
||||
if (!ret && !dirent_points_to_inode(d, &inode)) {
|
||||
if (!ret && (ret = dirent_points_to_inode(c, d, &inode)))
|
||||
bch2_trans_iter_exit(trans, &dirent_iter);
|
||||
ret = -BCH_ERR_ENOENT_dirent_doesnt_match_inode;
|
||||
}
|
||||
|
||||
if (bch2_err_matches(ret, ENOENT)) {
|
||||
ret = 0;
|
||||
|
@ -320,9 +320,11 @@ static noinline int bch2_inode_unpack_slowpath(struct bkey_s_c k,
|
||||
int bch2_inode_unpack(struct bkey_s_c k,
|
||||
struct bch_inode_unpacked *unpacked)
|
||||
{
|
||||
if (likely(k.k->type == KEY_TYPE_inode_v3))
|
||||
return bch2_inode_unpack_v3(k, unpacked);
|
||||
return bch2_inode_unpack_slowpath(k, unpacked);
|
||||
unpacked->bi_snapshot = k.k->p.snapshot;
|
||||
|
||||
return likely(k.k->type == KEY_TYPE_inode_v3)
|
||||
? bch2_inode_unpack_v3(k, unpacked)
|
||||
: bch2_inode_unpack_slowpath(k, unpacked);
|
||||
}
|
||||
|
||||
int bch2_inode_peek_nowarn(struct btree_trans *trans,
|
||||
@ -557,7 +559,7 @@ static void __bch2_inode_unpacked_to_text(struct printbuf *out,
|
||||
|
||||
void bch2_inode_unpacked_to_text(struct printbuf *out, struct bch_inode_unpacked *inode)
|
||||
{
|
||||
prt_printf(out, "inum: %llu ", inode->bi_inum);
|
||||
prt_printf(out, "inum: %llu:%u ", inode->bi_inum, inode->bi_snapshot);
|
||||
__bch2_inode_unpacked_to_text(out, inode);
|
||||
}
|
||||
|
||||
@ -1111,7 +1113,7 @@ static int may_delete_deleted_inode(struct btree_trans *trans,
|
||||
pos.offset, pos.snapshot))
|
||||
goto delete;
|
||||
|
||||
if (c->sb.clean &&
|
||||
if (test_bit(BCH_FS_clean_recovery, &c->flags) &&
|
||||
!fsck_err(trans, deleted_inode_but_clean,
|
||||
"filesystem marked as clean but have deleted inode %llu:%u",
|
||||
pos.offset, pos.snapshot)) {
|
||||
|
@ -69,6 +69,7 @@ typedef u64 u96;
|
||||
|
||||
struct bch_inode_unpacked {
|
||||
u64 bi_inum;
|
||||
u32 bi_snapshot;
|
||||
u64 bi_journal_seq;
|
||||
__le64 bi_hash_seed;
|
||||
u64 bi_size;
|
||||
|
@ -517,7 +517,7 @@ static int __bch2_rbio_narrow_crcs(struct btree_trans *trans,
|
||||
if ((ret = bkey_err(k)))
|
||||
goto out;
|
||||
|
||||
if (bversion_cmp(k.k->version, rbio->version) ||
|
||||
if (bversion_cmp(k.k->bversion, rbio->version) ||
|
||||
!bch2_bkey_matches_ptr(c, k, rbio->pick.ptr, data_offset))
|
||||
goto out;
|
||||
|
||||
@ -1031,7 +1031,7 @@ get_bio:
|
||||
rbio->read_pos = read_pos;
|
||||
rbio->data_btree = data_btree;
|
||||
rbio->data_pos = data_pos;
|
||||
rbio->version = k.k->version;
|
||||
rbio->version = k.k->bversion;
|
||||
rbio->promote = promote;
|
||||
INIT_WORK(&rbio->work, NULL);
|
||||
|
||||
|
@ -697,7 +697,7 @@ static void init_append_extent(struct bch_write_op *op,
|
||||
e = bkey_extent_init(op->insert_keys.top);
|
||||
e->k.p = op->pos;
|
||||
e->k.size = crc.uncompressed_size;
|
||||
e->k.version = version;
|
||||
e->k.bversion = version;
|
||||
|
||||
if (crc.csum_type ||
|
||||
crc.compression_type ||
|
||||
@ -1544,7 +1544,7 @@ static void bch2_write_data_inline(struct bch_write_op *op, unsigned data_len)
|
||||
|
||||
id = bkey_inline_data_init(op->insert_keys.top);
|
||||
id->k.p = op->pos;
|
||||
id->k.version = op->version;
|
||||
id->k.bversion = op->version;
|
||||
id->k.size = sectors;
|
||||
|
||||
iter = bio->bi_iter;
|
||||
|
@ -605,7 +605,7 @@ static int journal_entry_data_usage_validate(struct bch_fs *c,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (journal_entry_err_on(bch2_replicas_entry_validate(&u->r, c->disk_sb.sb, &err),
|
||||
if (journal_entry_err_on(bch2_replicas_entry_validate(&u->r, c, &err),
|
||||
c, version, jset, entry,
|
||||
journal_entry_data_usage_bad_size,
|
||||
"invalid journal entry usage: %s", err.buf)) {
|
||||
|
@ -37,6 +37,14 @@ static int resume_logged_op(struct btree_trans *trans, struct btree_iter *iter,
|
||||
const struct bch_logged_op_fn *fn = logged_op_fn(k.k->type);
|
||||
struct bkey_buf sk;
|
||||
u32 restart_count = trans->restart_count;
|
||||
struct printbuf buf = PRINTBUF;
|
||||
int ret = 0;
|
||||
|
||||
fsck_err_on(test_bit(BCH_FS_clean_recovery, &c->flags),
|
||||
trans, logged_op_but_clean,
|
||||
"filesystem marked as clean but have logged op\n%s",
|
||||
(bch2_bkey_val_to_text(&buf, c, k),
|
||||
buf.buf));
|
||||
|
||||
if (!fn)
|
||||
return 0;
|
||||
@ -47,8 +55,9 @@ static int resume_logged_op(struct btree_trans *trans, struct btree_iter *iter,
|
||||
fn->resume(trans, sk.k);
|
||||
|
||||
bch2_bkey_buf_exit(&sk, c);
|
||||
|
||||
return trans_was_restarted(trans, restart_count);
|
||||
fsck_err:
|
||||
printbuf_exit(&buf);
|
||||
return ret ?: trans_was_restarted(trans, restart_count);
|
||||
}
|
||||
|
||||
int bch2_resume_logged_ops(struct bch_fs *c)
|
||||
|
@ -478,7 +478,9 @@ start_gp:
|
||||
*/
|
||||
if (!p->cb_armed) {
|
||||
p->cb_armed = true;
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
__call_rcu(pending->srcu, &p->cb, rcu_pending_rcu_cb);
|
||||
goto free_node;
|
||||
} else {
|
||||
__start_poll_synchronize_rcu(pending->srcu);
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ static int bch2_journal_replay_accounting_key(struct btree_trans *trans,
|
||||
struct bkey_s_c old = bch2_btree_path_peek_slot(btree_iter_path(trans, &iter), &u);
|
||||
|
||||
/* Has this delta already been applied to the btree? */
|
||||
if (bversion_cmp(old.k->version, k->k->k.version) >= 0) {
|
||||
if (bversion_cmp(old.k->bversion, k->k->k.bversion) >= 0) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
@ -717,6 +717,8 @@ int bch2_fs_recovery(struct bch_fs *c)
|
||||
|
||||
if (c->opts.fsck)
|
||||
set_bit(BCH_FS_fsck_running, &c->flags);
|
||||
if (c->sb.clean)
|
||||
set_bit(BCH_FS_clean_recovery, &c->flags);
|
||||
|
||||
ret = bch2_blacklist_table_initialize(c);
|
||||
if (ret) {
|
||||
@ -862,6 +864,9 @@ use_clean:
|
||||
|
||||
clear_bit(BCH_FS_fsck_running, &c->flags);
|
||||
|
||||
/* in case we don't run journal replay, i.e. norecovery mode */
|
||||
set_bit(BCH_FS_accounting_replay_done, &c->flags);
|
||||
|
||||
/* fsync if we fixed errors */
|
||||
if (test_bit(BCH_FS_errors_fixed, &c->flags) &&
|
||||
bch2_write_ref_tryget(c, BCH_WRITE_REF_fsync)) {
|
||||
|
@ -50,7 +50,7 @@
|
||||
x(check_directory_structure, 30, PASS_ONLINE|PASS_FSCK) \
|
||||
x(check_nlinks, 31, PASS_FSCK) \
|
||||
x(resume_logged_ops, 23, PASS_ALWAYS) \
|
||||
x(delete_dead_inodes, 32, PASS_FSCK|PASS_UNCLEAN) \
|
||||
x(delete_dead_inodes, 32, PASS_ALWAYS) \
|
||||
x(fix_reflink_p, 33, 0) \
|
||||
x(set_fs_needs_rebalance, 34, 0) \
|
||||
|
||||
|
@ -367,7 +367,7 @@ static int bch2_make_extent_indirect(struct btree_trans *trans,
|
||||
r_v->k.type = bkey_type_to_indirect(&orig->k);
|
||||
r_v->k.p = reflink_iter.pos;
|
||||
bch2_key_resize(&r_v->k, orig->k.size);
|
||||
r_v->k.version = orig->k.version;
|
||||
r_v->k.bversion = orig->k.bversion;
|
||||
|
||||
set_bkey_val_bytes(&r_v->k, sizeof(__le64) + bkey_val_bytes(&orig->k));
|
||||
|
||||
|
@ -66,7 +66,7 @@ void bch2_replicas_entry_to_text(struct printbuf *out,
|
||||
prt_printf(out, "]");
|
||||
}
|
||||
|
||||
int bch2_replicas_entry_validate(struct bch_replicas_entry_v1 *r,
|
||||
static int bch2_replicas_entry_validate_locked(struct bch_replicas_entry_v1 *r,
|
||||
struct bch_sb *sb,
|
||||
struct printbuf *err)
|
||||
{
|
||||
@ -94,6 +94,16 @@ bad:
|
||||
return -BCH_ERR_invalid_replicas_entry;
|
||||
}
|
||||
|
||||
int bch2_replicas_entry_validate(struct bch_replicas_entry_v1 *r,
|
||||
struct bch_fs *c,
|
||||
struct printbuf *err)
|
||||
{
|
||||
mutex_lock(&c->sb_lock);
|
||||
int ret = bch2_replicas_entry_validate_locked(r, c->disk_sb.sb, err);
|
||||
mutex_unlock(&c->sb_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bch2_cpu_replicas_to_text(struct printbuf *out,
|
||||
struct bch_replicas_cpu *r)
|
||||
{
|
||||
@ -676,7 +686,7 @@ static int bch2_cpu_replicas_validate(struct bch_replicas_cpu *cpu_r,
|
||||
struct bch_replicas_entry_v1 *e =
|
||||
cpu_replicas_entry(cpu_r, i);
|
||||
|
||||
int ret = bch2_replicas_entry_validate(e, sb, err);
|
||||
int ret = bch2_replicas_entry_validate_locked(e, sb, err);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -795,7 +805,7 @@ bool bch2_have_enough_devs(struct bch_fs *c, struct bch_devs_mask devs,
|
||||
for (unsigned i = 0; i < e->nr_devs; i++) {
|
||||
nr_online += test_bit(e->devs[i], devs.d);
|
||||
|
||||
struct bch_dev *ca = bch2_dev_rcu(c, e->devs[i]);
|
||||
struct bch_dev *ca = bch2_dev_rcu_noerror(c, e->devs[i]);
|
||||
nr_failed += !ca || ca->mi.state == BCH_MEMBER_STATE_failed;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
@ -10,7 +10,7 @@ void bch2_replicas_entry_sort(struct bch_replicas_entry_v1 *);
|
||||
void bch2_replicas_entry_to_text(struct printbuf *,
|
||||
struct bch_replicas_entry_v1 *);
|
||||
int bch2_replicas_entry_validate(struct bch_replicas_entry_v1 *,
|
||||
struct bch_sb *, struct printbuf *);
|
||||
struct bch_fs *, struct printbuf *);
|
||||
void bch2_cpu_replicas_to_text(struct printbuf *, struct bch_replicas_cpu *);
|
||||
|
||||
static inline struct bch_replicas_entry_v1 *
|
||||
|
@ -155,7 +155,7 @@ struct bch_sb_field_clean *bch2_read_superblock_clean(struct bch_fs *c)
|
||||
SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
|
||||
c->sb.clean = false;
|
||||
mutex_unlock(&c->sb_lock);
|
||||
return NULL;
|
||||
return ERR_PTR(-BCH_ERR_invalid_sb_clean);
|
||||
}
|
||||
|
||||
clean = kmemdup(sb_clean, vstruct_bytes(&sb_clean->field),
|
||||
@ -167,6 +167,7 @@ struct bch_sb_field_clean *bch2_read_superblock_clean(struct bch_fs *c)
|
||||
|
||||
ret = bch2_sb_clean_validate_late(c, clean, READ);
|
||||
if (ret) {
|
||||
kfree(clean);
|
||||
mutex_unlock(&c->sb_lock);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
@ -312,8 +312,7 @@ static void bch2_sb_downgrade_to_text(struct printbuf *out, struct bch_sb *sb,
|
||||
if (!first)
|
||||
prt_char(out, ',');
|
||||
first = false;
|
||||
unsigned e = le16_to_cpu(i->errors[j]);
|
||||
prt_str(out, e < BCH_SB_ERR_MAX ? bch2_sb_error_strs[e] : "(unknown)");
|
||||
bch2_sb_error_id_to_text(out, le16_to_cpu(i->errors[j]));
|
||||
}
|
||||
prt_newline(out);
|
||||
}
|
||||
@ -353,7 +352,9 @@ int bch2_sb_downgrade_update(struct bch_fs *c)
|
||||
for (unsigned i = 0; i < src->nr_errors; i++)
|
||||
dst->errors[i] = cpu_to_le16(src->errors[i]);
|
||||
|
||||
downgrade_table_extra(c, &table);
|
||||
ret = downgrade_table_extra(c, &table);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!dst->recovery_passes[0] &&
|
||||
!dst->recovery_passes[1] &&
|
||||
@ -399,7 +400,7 @@ void bch2_sb_set_downgrade(struct bch_fs *c, unsigned new_minor, unsigned old_mi
|
||||
|
||||
for (unsigned j = 0; j < le16_to_cpu(i->nr_errors); j++) {
|
||||
unsigned e = le16_to_cpu(i->errors[j]);
|
||||
if (e < BCH_SB_ERR_MAX)
|
||||
if (e < BCH_FSCK_ERR_MAX)
|
||||
__set_bit(e, c->sb.errors_silent);
|
||||
if (e < sizeof(ext->errors_silent) * 8)
|
||||
__set_bit_le64(e, ext->errors_silent);
|
||||
|
@ -7,12 +7,12 @@
|
||||
const char * const bch2_sb_error_strs[] = {
|
||||
#define x(t, n, ...) [n] = #t,
|
||||
BCH_SB_ERRS()
|
||||
NULL
|
||||
#undef x
|
||||
};
|
||||
|
||||
static void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id id)
|
||||
void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id id)
|
||||
{
|
||||
if (id < BCH_SB_ERR_MAX)
|
||||
if (id < BCH_FSCK_ERR_MAX)
|
||||
prt_str(out, bch2_sb_error_strs[id]);
|
||||
else
|
||||
prt_printf(out, "(unknown error %u)", id);
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
extern const char * const bch2_sb_error_strs[];
|
||||
|
||||
void bch2_sb_error_id_to_text(struct printbuf *, enum bch_sb_error_id);
|
||||
|
||||
extern const struct bch_sb_field_ops bch_sb_field_ops_errors;
|
||||
|
||||
void bch2_sb_error_count(struct bch_fs *, enum bch_sb_error_id);
|
||||
|
@ -210,22 +210,23 @@ enum bch_fsck_flags {
|
||||
x(inode_snapshot_mismatch, 196, 0) \
|
||||
x(inode_unlinked_but_clean, 197, 0) \
|
||||
x(inode_unlinked_but_nlink_nonzero, 198, 0) \
|
||||
x(inode_unlinked_and_not_open, 281, 0) \
|
||||
x(inode_checksum_type_invalid, 199, 0) \
|
||||
x(inode_compression_type_invalid, 200, 0) \
|
||||
x(inode_subvol_root_but_not_dir, 201, 0) \
|
||||
x(inode_i_size_dirty_but_clean, 202, 0) \
|
||||
x(inode_i_sectors_dirty_but_clean, 203, 0) \
|
||||
x(inode_i_sectors_wrong, 204, 0) \
|
||||
x(inode_dir_wrong_nlink, 205, 0) \
|
||||
x(inode_dir_multiple_links, 206, 0) \
|
||||
x(inode_multiple_links_but_nlink_0, 207, 0) \
|
||||
x(inode_wrong_backpointer, 208, 0) \
|
||||
x(inode_wrong_nlink, 209, 0) \
|
||||
x(inode_unreachable, 210, 0) \
|
||||
x(deleted_inode_but_clean, 211, 0) \
|
||||
x(deleted_inode_missing, 212, 0) \
|
||||
x(deleted_inode_is_dir, 213, 0) \
|
||||
x(deleted_inode_not_unlinked, 214, 0) \
|
||||
x(inode_i_size_dirty_but_clean, 202, FSCK_AUTOFIX) \
|
||||
x(inode_i_sectors_dirty_but_clean, 203, FSCK_AUTOFIX) \
|
||||
x(inode_i_sectors_wrong, 204, FSCK_AUTOFIX) \
|
||||
x(inode_dir_wrong_nlink, 205, FSCK_AUTOFIX) \
|
||||
x(inode_dir_multiple_links, 206, FSCK_AUTOFIX) \
|
||||
x(inode_multiple_links_but_nlink_0, 207, FSCK_AUTOFIX) \
|
||||
x(inode_wrong_backpointer, 208, FSCK_AUTOFIX) \
|
||||
x(inode_wrong_nlink, 209, FSCK_AUTOFIX) \
|
||||
x(inode_unreachable, 210, FSCK_AUTOFIX) \
|
||||
x(deleted_inode_but_clean, 211, FSCK_AUTOFIX) \
|
||||
x(deleted_inode_missing, 212, FSCK_AUTOFIX) \
|
||||
x(deleted_inode_is_dir, 213, FSCK_AUTOFIX) \
|
||||
x(deleted_inode_not_unlinked, 214, FSCK_AUTOFIX) \
|
||||
x(extent_overlapping, 215, 0) \
|
||||
x(key_in_missing_inode, 216, 0) \
|
||||
x(key_in_wrong_inode_type, 217, 0) \
|
||||
@ -255,7 +256,7 @@ enum bch_fsck_flags {
|
||||
x(dir_loop, 241, 0) \
|
||||
x(hash_table_key_duplicate, 242, 0) \
|
||||
x(hash_table_key_wrong_offset, 243, 0) \
|
||||
x(unlinked_inode_not_on_deleted_list, 244, 0) \
|
||||
x(unlinked_inode_not_on_deleted_list, 244, FSCK_AUTOFIX) \
|
||||
x(reflink_p_front_pad_bad, 245, 0) \
|
||||
x(journal_entry_dup_same_device, 246, 0) \
|
||||
x(inode_bi_subvol_missing, 247, 0) \
|
||||
@ -270,7 +271,7 @@ enum bch_fsck_flags {
|
||||
x(subvol_children_not_set, 256, 0) \
|
||||
x(subvol_children_bad, 257, 0) \
|
||||
x(subvol_loop, 258, 0) \
|
||||
x(subvol_unreachable, 259, 0) \
|
||||
x(subvol_unreachable, 259, FSCK_AUTOFIX) \
|
||||
x(btree_node_bkey_bad_u64s, 260, 0) \
|
||||
x(btree_node_topology_empty_interior_node, 261, 0) \
|
||||
x(btree_ptr_v2_min_key_bad, 262, 0) \
|
||||
@ -282,8 +283,8 @@ enum bch_fsck_flags {
|
||||
x(btree_ptr_v2_written_0, 268, 0) \
|
||||
x(subvol_snapshot_bad, 269, 0) \
|
||||
x(subvol_inode_bad, 270, 0) \
|
||||
x(alloc_key_stripe_sectors_wrong, 271, 0) \
|
||||
x(accounting_mismatch, 272, 0) \
|
||||
x(alloc_key_stripe_sectors_wrong, 271, FSCK_AUTOFIX) \
|
||||
x(accounting_mismatch, 272, FSCK_AUTOFIX) \
|
||||
x(accounting_replicas_not_marked, 273, 0) \
|
||||
x(invalid_btree_id, 274, 0) \
|
||||
x(alloc_key_io_time_bad, 275, 0) \
|
||||
@ -292,12 +293,14 @@ enum bch_fsck_flags {
|
||||
x(accounting_key_replicas_nr_devs_0, 278, FSCK_AUTOFIX) \
|
||||
x(accounting_key_replicas_nr_required_bad, 279, FSCK_AUTOFIX) \
|
||||
x(accounting_key_replicas_devs_unsorted, 280, FSCK_AUTOFIX) \
|
||||
x(accounting_key_version_0, 282, FSCK_AUTOFIX) \
|
||||
x(logged_op_but_clean, 283, FSCK_AUTOFIX) \
|
||||
x(MAX, 284, 0)
|
||||
|
||||
enum bch_sb_error_id {
|
||||
#define x(t, n, ...) BCH_FSCK_ERR_##t = n,
|
||||
BCH_SB_ERRS()
|
||||
#undef x
|
||||
BCH_SB_ERR_MAX
|
||||
};
|
||||
|
||||
struct bch_sb_field_errors {
|
||||
|
@ -169,11 +169,17 @@ static int __do_six_trylock(struct six_lock *lock, enum six_lock_type type,
|
||||
ret = -1 - SIX_LOCK_write;
|
||||
}
|
||||
} else if (type == SIX_LOCK_write && lock->readers) {
|
||||
if (try) {
|
||||
if (try)
|
||||
atomic_add(SIX_LOCK_HELD_write, &lock->state);
|
||||
smp_mb__after_atomic();
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure atomic_add happens before pcpu_read_count and
|
||||
* six_set_bitmask in slow path happens before pcpu_read_count.
|
||||
*
|
||||
* Paired with the smp_mb() in read lock fast path (per-cpu mode)
|
||||
* and the one before atomic_read in read unlock path.
|
||||
*/
|
||||
smp_mb();
|
||||
ret = !pcpu_read_count(lock);
|
||||
|
||||
if (try && !ret) {
|
||||
|
@ -469,6 +469,7 @@ static u32 bch2_snapshot_tree_oldest_subvol(struct bch_fs *c, u32 snapshot_root)
|
||||
u32 id = snapshot_root;
|
||||
u32 subvol = 0, s;
|
||||
|
||||
rcu_read_lock();
|
||||
while (id) {
|
||||
s = snapshot_t(c, id)->subvol;
|
||||
|
||||
@ -477,6 +478,7 @@ static u32 bch2_snapshot_tree_oldest_subvol(struct bch_fs *c, u32 snapshot_root)
|
||||
|
||||
id = bch2_snapshot_tree_next(c, id);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return subvol;
|
||||
}
|
||||
@ -1782,6 +1784,7 @@ static int bch2_propagate_key_to_snapshot_leaf(struct btree_trans *trans,
|
||||
new->k.p.snapshot = leaf_id;
|
||||
ret = bch2_trans_update(trans, &iter, new, 0);
|
||||
out:
|
||||
bch2_set_btree_iter_dontneed(&iter);
|
||||
bch2_trans_iter_exit(trans, &iter);
|
||||
return ret;
|
||||
}
|
||||
|
@ -92,33 +92,31 @@ static int check_subvol(struct btree_trans *trans,
|
||||
}
|
||||
|
||||
struct bch_inode_unpacked inode;
|
||||
struct btree_iter inode_iter = {};
|
||||
ret = bch2_inode_peek_nowarn(trans, &inode_iter, &inode,
|
||||
ret = bch2_inode_find_by_inum_nowarn_trans(trans,
|
||||
(subvol_inum) { k.k->p.offset, le64_to_cpu(subvol.v->inode) },
|
||||
0);
|
||||
bch2_trans_iter_exit(trans, &inode_iter);
|
||||
|
||||
if (ret && !bch2_err_matches(ret, ENOENT))
|
||||
return ret;
|
||||
|
||||
if (fsck_err_on(ret,
|
||||
trans, subvol_to_missing_root,
|
||||
&inode);
|
||||
if (!ret) {
|
||||
if (fsck_err_on(inode.bi_subvol != subvol.k->p.offset,
|
||||
trans, subvol_root_wrong_bi_subvol,
|
||||
"subvol root %llu:%u has wrong bi_subvol field: got %u, should be %llu",
|
||||
inode.bi_inum, inode.bi_snapshot,
|
||||
inode.bi_subvol, subvol.k->p.offset)) {
|
||||
inode.bi_subvol = subvol.k->p.offset;
|
||||
ret = __bch2_fsck_write_inode(trans, &inode, le32_to_cpu(subvol.v->snapshot));
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
} else if (bch2_err_matches(ret, ENOENT)) {
|
||||
if (fsck_err(trans, subvol_to_missing_root,
|
||||
"subvolume %llu points to missing subvolume root %llu:%u",
|
||||
k.k->p.offset, le64_to_cpu(subvol.v->inode),
|
||||
le32_to_cpu(subvol.v->snapshot))) {
|
||||
ret = bch2_subvolume_delete(trans, iter->pos.offset);
|
||||
bch_err_msg(c, ret, "deleting subvolume %llu", iter->pos.offset);
|
||||
return ret ?: -BCH_ERR_transaction_restart_nested;
|
||||
ret = ret ?: -BCH_ERR_transaction_restart_nested;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (fsck_err_on(inode.bi_subvol != subvol.k->p.offset,
|
||||
trans, subvol_root_wrong_bi_subvol,
|
||||
"subvol root %llu:%u has wrong bi_subvol field: got %u, should be %llu",
|
||||
inode.bi_inum, inode_iter.k.p.snapshot,
|
||||
inode.bi_subvol, subvol.k->p.offset)) {
|
||||
inode.bi_subvol = subvol.k->p.offset;
|
||||
ret = __bch2_fsck_write_inode(trans, &inode, le32_to_cpu(subvol.v->snapshot));
|
||||
if (ret)
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -137,7 +135,7 @@ static int check_subvol(struct btree_trans *trans,
|
||||
"%s: snapshot tree %u not found", __func__, snapshot_tree);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err;
|
||||
|
||||
if (fsck_err_on(le32_to_cpu(st.master_subvol) != subvol.k->p.offset,
|
||||
trans, subvol_not_master_and_not_snapshot,
|
||||
@ -147,7 +145,7 @@ static int check_subvol(struct btree_trans *trans,
|
||||
bch2_bkey_make_mut_typed(trans, iter, &subvol.s_c, 0, subvolume);
|
||||
ret = PTR_ERR_OR_ZERO(s);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err;
|
||||
|
||||
SET_BCH_SUBVOLUME_SNAP(&s->v, true);
|
||||
}
|
||||
|
@ -799,8 +799,10 @@ retry:
|
||||
i < layout.sb_offset + layout.nr_superblocks; i++) {
|
||||
offset = le64_to_cpu(*i);
|
||||
|
||||
if (offset == opt_get(*opts, sb))
|
||||
if (offset == opt_get(*opts, sb)) {
|
||||
ret = -BCH_ERR_invalid;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = read_one_super(sb, offset, &err);
|
||||
if (!ret)
|
||||
@ -1188,7 +1190,8 @@ static void bch2_sb_ext_to_text(struct printbuf *out, struct bch_sb *sb,
|
||||
le_bitvector_to_cpu(errors_silent, (void *) e->errors_silent, sizeof(e->errors_silent) * 8);
|
||||
|
||||
prt_printf(out, "Errors to silently fix:\t");
|
||||
prt_bitflags_vector(out, bch2_sb_error_strs, errors_silent, sizeof(e->errors_silent) * 8);
|
||||
prt_bitflags_vector(out, bch2_sb_error_strs, errors_silent,
|
||||
min(BCH_FSCK_ERR_MAX, sizeof(e->errors_silent) * 8));
|
||||
prt_newline(out);
|
||||
|
||||
kfree(errors_silent);
|
||||
|
@ -394,7 +394,7 @@ static int insert_test_extent(struct bch_fs *c,
|
||||
k.k_i.k.p.offset = end;
|
||||
k.k_i.k.p.snapshot = U32_MAX;
|
||||
k.k_i.k.size = end - start;
|
||||
k.k_i.k.version.lo = test_version++;
|
||||
k.k_i.k.bversion.lo = test_version++;
|
||||
|
||||
ret = bch2_btree_insert(c, BTREE_ID_extents, &k.k_i, NULL, 0, 0);
|
||||
bch_err_fn(c, ret);
|
||||
|
Loading…
Reference in New Issue
Block a user