mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-22 00:00:03 +03:00
Update bcachefs sources to c887148ebf99 thread_with_file: add f_ops.flush
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
6ff5313cbe
commit
e5b2870d05
@ -1 +1 @@
|
|||||||
9a555a741e807275c320807babd3f42efb8fee90
|
c887148ebf9989ce8bdf6f814d4342ba5bf465fa
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include <linux/sched/task.h>
|
#include <linux/sched/task.h>
|
||||||
#include <linux/sort.h>
|
#include <linux/sort.h>
|
||||||
|
|
||||||
|
static void bch2_discard_one_bucket_fast(struct bch_fs *c, struct bpos bucket);
|
||||||
|
|
||||||
/* Persistent alloc info: */
|
/* Persistent alloc info: */
|
||||||
|
|
||||||
static const unsigned BCH_ALLOC_V1_FIELD_BYTES[] = {
|
static const unsigned BCH_ALLOC_V1_FIELD_BYTES[] = {
|
||||||
@ -866,14 +868,14 @@ int bch2_trigger_alloc(struct btree_trans *trans,
|
|||||||
#define statechange(expr) !eval_state(old_a, expr) && eval_state(new_a, expr)
|
#define statechange(expr) !eval_state(old_a, expr) && eval_state(new_a, expr)
|
||||||
#define bucket_flushed(a) (!a->journal_seq || a->journal_seq <= c->journal.flushed_seq_ondisk)
|
#define bucket_flushed(a) (!a->journal_seq || a->journal_seq <= c->journal.flushed_seq_ondisk)
|
||||||
|
|
||||||
if (statechange(a->data_type == BCH_DATA_free &&
|
if (statechange(a->data_type == BCH_DATA_free) &&
|
||||||
bucket_flushed(a)))
|
bucket_flushed(new_a))
|
||||||
closure_wake_up(&c->freelist_wait);
|
closure_wake_up(&c->freelist_wait);
|
||||||
|
|
||||||
if (statechange(a->data_type == BCH_DATA_need_discard &&
|
if (statechange(a->data_type == BCH_DATA_need_discard) &&
|
||||||
bucket_flushed(a)) &&
|
!bch2_bucket_is_open(c, new.k->p.inode, new.k->p.offset) &&
|
||||||
!bch2_bucket_is_open(c, new.k->p.inode, new.k->p.offset))
|
bucket_flushed(new_a))
|
||||||
bch2_do_discards(c);
|
bch2_discard_one_bucket_fast(c, new.k->p);
|
||||||
|
|
||||||
if (statechange(a->data_type == BCH_DATA_cached) &&
|
if (statechange(a->data_type == BCH_DATA_cached) &&
|
||||||
!bch2_bucket_is_open(c, new.k->p.inode, new.k->p.offset) &&
|
!bch2_bucket_is_open(c, new.k->p.inode, new.k->p.offset) &&
|
||||||
@ -1609,6 +1611,36 @@ int bch2_check_alloc_to_lru_refs(struct bch_fs *c)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int discard_in_flight_add(struct bch_fs *c, struct bpos bucket)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&c->discard_buckets_in_flight_lock);
|
||||||
|
darray_for_each(c->discard_buckets_in_flight, i)
|
||||||
|
if (bkey_eq(*i, bucket)) {
|
||||||
|
ret = -EEXIST;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = darray_push(&c->discard_buckets_in_flight, bucket);
|
||||||
|
out:
|
||||||
|
mutex_unlock(&c->discard_buckets_in_flight_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void discard_in_flight_remove(struct bch_fs *c, struct bpos bucket)
|
||||||
|
{
|
||||||
|
mutex_lock(&c->discard_buckets_in_flight_lock);
|
||||||
|
darray_for_each(c->discard_buckets_in_flight, i)
|
||||||
|
if (bkey_eq(*i, bucket)) {
|
||||||
|
darray_remove_item(&c->discard_buckets_in_flight, i);
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
BUG();
|
||||||
|
found:
|
||||||
|
mutex_unlock(&c->discard_buckets_in_flight_lock);
|
||||||
|
}
|
||||||
|
|
||||||
struct discard_buckets_state {
|
struct discard_buckets_state {
|
||||||
u64 seen;
|
u64 seen;
|
||||||
u64 open;
|
u64 open;
|
||||||
@ -1647,6 +1679,7 @@ static int bch2_discard_one_bucket(struct btree_trans *trans,
|
|||||||
struct bch_dev *ca;
|
struct bch_dev *ca;
|
||||||
struct bkey_i_alloc_v4 *a;
|
struct bkey_i_alloc_v4 *a;
|
||||||
struct printbuf buf = PRINTBUF;
|
struct printbuf buf = PRINTBUF;
|
||||||
|
bool discard_locked = false;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
ca = bch_dev_bkey_exists(c, pos.inode);
|
ca = bch_dev_bkey_exists(c, pos.inode);
|
||||||
@ -1714,6 +1747,11 @@ static int bch2_discard_one_bucket(struct btree_trans *trans,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (discard_in_flight_add(c, SPOS(iter.pos.inode, iter.pos.offset, true)))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
discard_locked = true;
|
||||||
|
|
||||||
if (!bkey_eq(*discard_pos_done, iter.pos) &&
|
if (!bkey_eq(*discard_pos_done, iter.pos) &&
|
||||||
ca->mi.discard && !c->opts.nochanges) {
|
ca->mi.discard && !c->opts.nochanges) {
|
||||||
/*
|
/*
|
||||||
@ -1745,6 +1783,8 @@ write:
|
|||||||
count_event(c, bucket_discard);
|
count_event(c, bucket_discard);
|
||||||
s->discarded++;
|
s->discarded++;
|
||||||
out:
|
out:
|
||||||
|
if (discard_locked)
|
||||||
|
discard_in_flight_remove(c, iter.pos);
|
||||||
s->seen++;
|
s->seen++;
|
||||||
bch2_trans_iter_exit(trans, &iter);
|
bch2_trans_iter_exit(trans, &iter);
|
||||||
percpu_ref_put(&ca->io_ref);
|
percpu_ref_put(&ca->io_ref);
|
||||||
@ -1784,6 +1824,92 @@ void bch2_do_discards(struct bch_fs *c)
|
|||||||
bch2_write_ref_put(c, BCH_WRITE_REF_discard);
|
bch2_write_ref_put(c, BCH_WRITE_REF_discard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bch2_clear_bucket_needs_discard(struct btree_trans *trans, struct bpos bucket)
|
||||||
|
{
|
||||||
|
struct btree_iter iter;
|
||||||
|
bch2_trans_iter_init(trans, &iter, BTREE_ID_alloc, bucket, BTREE_ITER_INTENT);
|
||||||
|
struct bkey_s_c k = bch2_btree_iter_peek_slot(&iter);
|
||||||
|
int ret = bkey_err(k);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
struct bkey_i_alloc_v4 *a = bch2_alloc_to_v4_mut(trans, k);
|
||||||
|
ret = PTR_ERR_OR_ZERO(a);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
SET_BCH_ALLOC_V4_NEED_DISCARD(&a->v, false);
|
||||||
|
a->v.data_type = alloc_data_type(a->v, a->v.data_type);
|
||||||
|
|
||||||
|
ret = bch2_trans_update(trans, &iter, &a->k_i, 0);
|
||||||
|
err:
|
||||||
|
bch2_trans_iter_exit(trans, &iter);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bch2_do_discards_fast_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct bch_fs *c = container_of(work, struct bch_fs, discard_fast_work);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
bool got_bucket = false;
|
||||||
|
struct bpos bucket;
|
||||||
|
struct bch_dev *ca;
|
||||||
|
|
||||||
|
mutex_lock(&c->discard_buckets_in_flight_lock);
|
||||||
|
darray_for_each(c->discard_buckets_in_flight, i) {
|
||||||
|
if (i->snapshot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ca = bch_dev_bkey_exists(c, i->inode);
|
||||||
|
|
||||||
|
if (!percpu_ref_tryget(&ca->io_ref)) {
|
||||||
|
darray_remove_item(&c->discard_buckets_in_flight, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
got_bucket = true;
|
||||||
|
bucket = *i;
|
||||||
|
i->snapshot = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mutex_unlock(&c->discard_buckets_in_flight_lock);
|
||||||
|
|
||||||
|
if (!got_bucket)
|
||||||
|
break;
|
||||||
|
|
||||||
|
blkdev_issue_discard(ca->disk_sb.bdev,
|
||||||
|
bucket.offset * ca->mi.bucket_size,
|
||||||
|
ca->mi.bucket_size,
|
||||||
|
GFP_KERNEL);
|
||||||
|
|
||||||
|
int ret = bch2_trans_do(c, NULL, NULL,
|
||||||
|
BCH_WATERMARK_btree|
|
||||||
|
BCH_TRANS_COMMIT_no_enospc,
|
||||||
|
bch2_clear_bucket_needs_discard(trans, bucket));
|
||||||
|
bch_err_fn(c, ret);
|
||||||
|
|
||||||
|
percpu_ref_put(&ca->io_ref);
|
||||||
|
discard_in_flight_remove(c, bucket);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bch2_write_ref_put(c, BCH_WRITE_REF_discard_fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bch2_discard_one_bucket_fast(struct bch_fs *c, struct bpos bucket)
|
||||||
|
{
|
||||||
|
struct bch_dev *ca = bch_dev_bkey_exists(c, bucket.inode);
|
||||||
|
|
||||||
|
if (!percpu_ref_is_dying(&ca->io_ref) &&
|
||||||
|
!discard_in_flight_add(c, bucket) &&
|
||||||
|
bch2_write_ref_tryget(c, BCH_WRITE_REF_discard_fast) &&
|
||||||
|
!queue_work(c->write_ref_wq, &c->discard_fast_work))
|
||||||
|
bch2_write_ref_put(c, BCH_WRITE_REF_discard_fast);
|
||||||
|
}
|
||||||
|
|
||||||
static int invalidate_one_bucket(struct btree_trans *trans,
|
static int invalidate_one_bucket(struct btree_trans *trans,
|
||||||
struct btree_iter *lru_iter,
|
struct btree_iter *lru_iter,
|
||||||
struct bkey_s_c lru_k,
|
struct bkey_s_c lru_k,
|
||||||
@ -2215,9 +2341,16 @@ void bch2_dev_allocator_add(struct bch_fs *c, struct bch_dev *ca)
|
|||||||
set_bit(ca->dev_idx, c->rw_devs[i].d);
|
set_bit(ca->dev_idx, c->rw_devs[i].d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bch2_fs_allocator_background_exit(struct bch_fs *c)
|
||||||
|
{
|
||||||
|
darray_exit(&c->discard_buckets_in_flight);
|
||||||
|
}
|
||||||
|
|
||||||
void bch2_fs_allocator_background_init(struct bch_fs *c)
|
void bch2_fs_allocator_background_init(struct bch_fs *c)
|
||||||
{
|
{
|
||||||
spin_lock_init(&c->freelist_lock);
|
spin_lock_init(&c->freelist_lock);
|
||||||
|
mutex_init(&c->discard_buckets_in_flight_lock);
|
||||||
INIT_WORK(&c->discard_work, bch2_do_discards_work);
|
INIT_WORK(&c->discard_work, bch2_do_discards_work);
|
||||||
|
INIT_WORK(&c->discard_fast_work, bch2_do_discards_fast_work);
|
||||||
INIT_WORK(&c->invalidate_work, bch2_do_invalidates_work);
|
INIT_WORK(&c->invalidate_work, bch2_do_invalidates_work);
|
||||||
}
|
}
|
||||||
|
@ -269,6 +269,7 @@ u64 bch2_min_rw_member_capacity(struct bch_fs *);
|
|||||||
void bch2_dev_allocator_remove(struct bch_fs *, struct bch_dev *);
|
void bch2_dev_allocator_remove(struct bch_fs *, struct bch_dev *);
|
||||||
void bch2_dev_allocator_add(struct bch_fs *, struct bch_dev *);
|
void bch2_dev_allocator_add(struct bch_fs *, struct bch_dev *);
|
||||||
|
|
||||||
|
void bch2_fs_allocator_background_exit(struct bch_fs *);
|
||||||
void bch2_fs_allocator_background_init(struct bch_fs *);
|
void bch2_fs_allocator_background_init(struct bch_fs *);
|
||||||
|
|
||||||
#endif /* _BCACHEFS_ALLOC_BACKGROUND_H */
|
#endif /* _BCACHEFS_ALLOC_BACKGROUND_H */
|
||||||
|
@ -708,6 +708,7 @@ struct btree_trans_buf {
|
|||||||
x(reflink) \
|
x(reflink) \
|
||||||
x(fallocate) \
|
x(fallocate) \
|
||||||
x(discard) \
|
x(discard) \
|
||||||
|
x(discard_fast) \
|
||||||
x(invalidate) \
|
x(invalidate) \
|
||||||
x(delete_dead_snapshots) \
|
x(delete_dead_snapshots) \
|
||||||
x(snapshot_delete_pagecache) \
|
x(snapshot_delete_pagecache) \
|
||||||
@ -943,8 +944,11 @@ struct bch_fs {
|
|||||||
unsigned write_points_nr;
|
unsigned write_points_nr;
|
||||||
|
|
||||||
struct buckets_waiting_for_journal buckets_waiting_for_journal;
|
struct buckets_waiting_for_journal buckets_waiting_for_journal;
|
||||||
struct work_struct discard_work;
|
|
||||||
struct work_struct invalidate_work;
|
struct work_struct invalidate_work;
|
||||||
|
struct work_struct discard_work;
|
||||||
|
struct mutex discard_buckets_in_flight_lock;
|
||||||
|
DARRAY(struct bpos) discard_buckets_in_flight;
|
||||||
|
struct work_struct discard_fast_work;
|
||||||
|
|
||||||
/* GARBAGE COLLECTION */
|
/* GARBAGE COLLECTION */
|
||||||
struct task_struct *gc_thread;
|
struct task_struct *gc_thread;
|
||||||
|
@ -189,7 +189,11 @@ struct bversion {
|
|||||||
__u32 hi;
|
__u32 hi;
|
||||||
__u64 lo;
|
__u64 lo;
|
||||||
#endif
|
#endif
|
||||||
} __packed __aligned(4);
|
} __packed
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
__aligned(4)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
struct bkey {
|
struct bkey {
|
||||||
/* Size of combined key and value, in u64s */
|
/* Size of combined key and value, in u64s */
|
||||||
|
@ -747,7 +747,8 @@ void bch2_trans_downgrade(struct btree_trans *trans)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
trans_for_each_path(trans, path, i)
|
trans_for_each_path(trans, path, i)
|
||||||
bch2_btree_path_downgrade(trans, path);
|
if (path->ref)
|
||||||
|
bch2_btree_path_downgrade(trans, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bch2_trans_relock(struct btree_trans *trans)
|
int bch2_trans_relock(struct btree_trans *trans)
|
||||||
|
@ -590,7 +590,9 @@ static int bch2_journal_keys_to_write_buffer(struct bch_fs *c, struct journal_bu
|
|||||||
entry->type = BCH_JSET_ENTRY_btree_keys;
|
entry->type = BCH_JSET_ENTRY_btree_keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock(&c->journal.lock);
|
||||||
buf->need_flush_to_write_buffer = false;
|
buf->need_flush_to_write_buffer = false;
|
||||||
|
spin_unlock(&c->journal.lock);
|
||||||
out:
|
out:
|
||||||
bch2_journal_keys_to_write_buffer_end(c, &dst);
|
bch2_journal_keys_to_write_buffer_end(c, &dst);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -155,14 +155,28 @@ static void bch2_fsck_thread_exit(struct thread_with_stdio *_thr)
|
|||||||
kfree(thr);
|
kfree(thr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bch2_fsck_offline_thread_fn(struct thread_with_stdio *stdio)
|
static int bch2_fsck_offline_thread_fn(struct thread_with_stdio *stdio)
|
||||||
{
|
{
|
||||||
struct fsck_thread *thr = container_of(stdio, struct fsck_thread, thr);
|
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 = bch2_fs_open(thr->devs, thr->nr_devs, thr->opts);
|
||||||
|
|
||||||
thr->thr.thr.ret = PTR_ERR_OR_ZERO(c);
|
if (IS_ERR(c))
|
||||||
if (!thr->thr.thr.ret)
|
return PTR_ERR(c);
|
||||||
bch2_fs_stop(c);
|
|
||||||
|
int ret = 0;
|
||||||
|
if (test_bit(BCH_FS_errors_fixed, &c->flags))
|
||||||
|
ret |= 1;
|
||||||
|
if (test_bit(BCH_FS_error, &c->flags))
|
||||||
|
ret |= 4;
|
||||||
|
|
||||||
|
bch2_fs_stop(c);
|
||||||
|
|
||||||
|
if (ret & 1)
|
||||||
|
stdio_redirect_printf(&stdio->stdio, false, "%s: errors fixed\n", c->name);
|
||||||
|
if (ret & 4)
|
||||||
|
stdio_redirect_printf(&stdio->stdio, false, "%s: still has errors\n", c->name);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct thread_with_stdio_ops bch2_offline_fsck_ops = {
|
static const struct thread_with_stdio_ops bch2_offline_fsck_ops = {
|
||||||
@ -763,7 +777,7 @@ static long bch2_ioctl_disk_resize_journal(struct bch_fs *c,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bch2_fsck_online_thread_fn(struct thread_with_stdio *stdio)
|
static int bch2_fsck_online_thread_fn(struct thread_with_stdio *stdio)
|
||||||
{
|
{
|
||||||
struct fsck_thread *thr = container_of(stdio, struct fsck_thread, thr);
|
struct fsck_thread *thr = container_of(stdio, struct fsck_thread, thr);
|
||||||
struct bch_fs *c = thr->c;
|
struct bch_fs *c = thr->c;
|
||||||
@ -795,6 +809,7 @@ static void bch2_fsck_online_thread_fn(struct thread_with_stdio *stdio)
|
|||||||
|
|
||||||
up(&c->online_fsck_mutex);
|
up(&c->online_fsck_mutex);
|
||||||
bch2_ro_ref_put(c);
|
bch2_ro_ref_put(c);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct thread_with_stdio_ops bch2_online_fsck_ops = {
|
static const struct thread_with_stdio_ops bch2_online_fsck_ops = {
|
||||||
|
@ -342,6 +342,27 @@ static int remove_backpointer(struct btree_trans *trans,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reattach_subvol(struct btree_trans *trans, struct bkey_s_c_subvolume s)
|
||||||
|
{
|
||||||
|
struct bch_fs *c = trans->c;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
ret = remove_backpointer(trans, &inode);
|
||||||
|
bch_err_msg(c, ret, "removing dirent");
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = reattach_inode(trans, &inode, le32_to_cpu(s.v->snapshot));
|
||||||
|
bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct snapshots_seen_entry {
|
struct snapshots_seen_entry {
|
||||||
u32 id;
|
u32 id;
|
||||||
u32 equiv;
|
u32 equiv;
|
||||||
@ -2111,6 +2132,107 @@ int bch2_check_root(struct bch_fs *c)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef DARRAY(u32) darray_u32;
|
||||||
|
|
||||||
|
static bool darray_u32_has(darray_u32 *d, u32 v)
|
||||||
|
{
|
||||||
|
darray_for_each(*d, i)
|
||||||
|
if (*i == v)
|
||||||
|
return true;
|
||||||
|
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;
|
||||||
|
struct btree_iter parent_iter = {};
|
||||||
|
darray_u32 subvol_path = {};
|
||||||
|
struct printbuf buf = PRINTBUF;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (k.k->type != KEY_TYPE_subvolume)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (k.k->p.offset != BCACHEFS_ROOT_SUBVOL) {
|
||||||
|
ret = darray_push(&subvol_path, k.k->p.offset);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
struct bkey_s_c_subvolume s = bkey_s_c_to_subvolume(k);
|
||||||
|
|
||||||
|
ret = subvol_has_dirent(trans, s);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (fsck_err_on(!ret,
|
||||||
|
c, subvol_unreachable,
|
||||||
|
"unreachable subvolume %s",
|
||||||
|
(bch2_bkey_val_to_text(&buf, c, s.s_c),
|
||||||
|
buf.buf))) {
|
||||||
|
ret = reattach_subvol(trans, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 parent = le32_to_cpu(s.v->fs_path_parent);
|
||||||
|
|
||||||
|
if (darray_u32_has(&subvol_path, parent)) {
|
||||||
|
if (fsck_err(c, subvol_loop, "subvolume loop"))
|
||||||
|
ret = reattach_subvol(trans, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bch2_trans_iter_exit(trans, &parent_iter);
|
||||||
|
bch2_trans_iter_init(trans, &parent_iter,
|
||||||
|
BTREE_ID_subvolumes, POS(0, parent), 0);
|
||||||
|
k = bch2_btree_iter_peek_slot(&parent_iter);
|
||||||
|
ret = bkey_err(k);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (fsck_err_on(k.k->type != KEY_TYPE_subvolume,
|
||||||
|
c, subvol_unreachable,
|
||||||
|
"unreachable subvolume %s",
|
||||||
|
(bch2_bkey_val_to_text(&buf, c, s.s_c),
|
||||||
|
buf.buf))) {
|
||||||
|
ret = reattach_subvol(trans, s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fsck_err:
|
||||||
|
err:
|
||||||
|
printbuf_exit(&buf);
|
||||||
|
darray_exit(&subvol_path);
|
||||||
|
bch2_trans_iter_exit(trans, &parent_iter);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bch2_check_subvolume_structure(struct bch_fs *c)
|
||||||
|
{
|
||||||
|
int ret = bch2_trans_run(c,
|
||||||
|
for_each_btree_key_commit(trans, iter,
|
||||||
|
BTREE_ID_subvolumes, POS_MIN, BTREE_ITER_PREFETCH, k,
|
||||||
|
NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
|
||||||
|
check_subvol_path(trans, &iter, k)));
|
||||||
|
bch_err_fn(c, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct pathbuf_entry {
|
struct pathbuf_entry {
|
||||||
u64 inum;
|
u64 inum;
|
||||||
u32 snapshot;
|
u32 snapshot;
|
||||||
@ -2127,22 +2249,9 @@ static bool path_is_dup(pathbuf *p, u64 inum, u32 snapshot)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int path_down(struct bch_fs *c, pathbuf *p,
|
|
||||||
u64 inum, u32 snapshot)
|
|
||||||
{
|
|
||||||
int ret = darray_push(p, ((struct pathbuf_entry) {
|
|
||||||
.inum = inum,
|
|
||||||
.snapshot = snapshot,
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (ret)
|
|
||||||
bch_err(c, "fsck: error allocating memory for pathbuf, size %zu",
|
|
||||||
p->size);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that a given inode is reachable from the root:
|
* Check that a given inode is reachable from its subvolume root - we already
|
||||||
|
* verified subvolume connectivity:
|
||||||
*
|
*
|
||||||
* XXX: we should also be verifying that inodes are in the right subvolumes
|
* XXX: we should also be verifying that inodes are in the right subvolumes
|
||||||
*/
|
*/
|
||||||
@ -2159,8 +2268,7 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
|
|||||||
|
|
||||||
BUG_ON(bch2_inode_unpack(inode_k, &inode));
|
BUG_ON(bch2_inode_unpack(inode_k, &inode));
|
||||||
|
|
||||||
while (!(inode.bi_inum == BCACHEFS_ROOT_INO &&
|
while (!inode.bi_subvol) {
|
||||||
inode.bi_subvol == BCACHEFS_ROOT_SUBVOL)) {
|
|
||||||
struct btree_iter dirent_iter;
|
struct btree_iter dirent_iter;
|
||||||
struct bkey_s_c_dirent d;
|
struct bkey_s_c_dirent d;
|
||||||
u32 parent_snapshot = snapshot;
|
u32 parent_snapshot = snapshot;
|
||||||
@ -2191,11 +2299,12 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
|
|||||||
if (!S_ISDIR(inode.bi_mode))
|
if (!S_ISDIR(inode.bi_mode))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = path_down(c, p, inode.bi_inum, snapshot);
|
ret = darray_push(p, ((struct pathbuf_entry) {
|
||||||
if (ret) {
|
.inum = inode.bi_inum,
|
||||||
bch_err(c, "memory allocation failure");
|
.snapshot = snapshot,
|
||||||
|
}));
|
||||||
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
snapshot = parent_snapshot;
|
snapshot = parent_snapshot;
|
||||||
|
|
||||||
@ -2222,18 +2331,15 @@ static int check_path(struct btree_trans *trans, pathbuf *p, struct bkey_s_c ino
|
|||||||
pr_err("%llu:%u", i->inum, i->snapshot);
|
pr_err("%llu:%u", i->inum, i->snapshot);
|
||||||
pr_err("%llu:%u", inode.bi_inum, snapshot);
|
pr_err("%llu:%u", inode.bi_inum, snapshot);
|
||||||
|
|
||||||
if (!fsck_err(c, dir_loop, "directory structure loop"))
|
if (fsck_err(c, dir_loop, "directory structure loop")) {
|
||||||
return 0;
|
ret = remove_backpointer(trans, &inode);
|
||||||
|
|
||||||
ret = remove_backpointer(trans, &inode);
|
|
||||||
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
||||||
bch_err_msg(c, ret, "removing dirent");
|
bch_err_msg(c, ret, "removing dirent");
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = reattach_inode(trans, &inode, snapshot);
|
ret = reattach_inode(trans, &inode, snapshot);
|
||||||
if (ret && !bch2_err_matches(ret, BCH_ERR_transaction_restart))
|
|
||||||
bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
|
bch_err_msg(c, ret, "reattaching inode %llu", inode.bi_inum);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ int bch2_check_indirect_extents(struct bch_fs *);
|
|||||||
int bch2_check_dirents(struct bch_fs *);
|
int bch2_check_dirents(struct bch_fs *);
|
||||||
int bch2_check_xattrs(struct bch_fs *);
|
int bch2_check_xattrs(struct bch_fs *);
|
||||||
int bch2_check_root(struct bch_fs *);
|
int bch2_check_root(struct bch_fs *);
|
||||||
|
int bch2_check_subvolume_structure(struct bch_fs *);
|
||||||
int bch2_check_directory_structure(struct bch_fs *);
|
int bch2_check_directory_structure(struct bch_fs *);
|
||||||
int bch2_check_nlinks(struct bch_fs *);
|
int bch2_check_nlinks(struct bch_fs *);
|
||||||
int bch2_fix_reflink_p(struct bch_fs *);
|
int bch2_fix_reflink_p(struct bch_fs *);
|
||||||
|
@ -530,7 +530,8 @@ static void __bch2_write_index(struct bch_write_op *op)
|
|||||||
|
|
||||||
bch_err_inum_offset_ratelimited(c,
|
bch_err_inum_offset_ratelimited(c,
|
||||||
insert->k.p.inode, insert->k.p.offset << 9,
|
insert->k.p.inode, insert->k.p.offset << 9,
|
||||||
"write error while doing btree update: %s",
|
"%s write error while doing btree update: %s",
|
||||||
|
op->flags & BCH_WRITE_MOVE ? "move" : "user",
|
||||||
bch2_err_str(ret));
|
bch2_err_str(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1067,7 +1068,8 @@ do_write:
|
|||||||
*_dst = dst;
|
*_dst = dst;
|
||||||
return more;
|
return more;
|
||||||
csum_err:
|
csum_err:
|
||||||
bch_err(c, "error verifying existing checksum while rewriting existing data (memory corruption?)");
|
bch_err(c, "%s writ error: error verifying existing checksum while rewriting existing data (memory corruption?)",
|
||||||
|
op->flags & BCH_WRITE_MOVE ? "move" : "user");
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
err:
|
err:
|
||||||
if (to_wbio(dst)->bounce)
|
if (to_wbio(dst)->bounce)
|
||||||
@ -1169,7 +1171,8 @@ static void bch2_nocow_write_convert_unwritten(struct bch_write_op *op)
|
|||||||
|
|
||||||
bch_err_inum_offset_ratelimited(c,
|
bch_err_inum_offset_ratelimited(c,
|
||||||
insert->k.p.inode, insert->k.p.offset << 9,
|
insert->k.p.inode, insert->k.p.offset << 9,
|
||||||
"write error while doing btree update: %s",
|
"%s write error while doing btree update: %s",
|
||||||
|
op->flags & BCH_WRITE_MOVE ? "move" : "user",
|
||||||
bch2_err_str(ret));
|
bch2_err_str(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1449,7 +1452,9 @@ err:
|
|||||||
bch_err_inum_offset_ratelimited(c,
|
bch_err_inum_offset_ratelimited(c,
|
||||||
op->pos.inode,
|
op->pos.inode,
|
||||||
op->pos.offset << 9,
|
op->pos.offset << 9,
|
||||||
"%s(): error: %s", __func__, bch2_err_str(ret));
|
"%s(): %s error: %s", __func__,
|
||||||
|
op->flags & BCH_WRITE_MOVE ? "move" : "user",
|
||||||
|
bch2_err_str(ret));
|
||||||
op->error = ret;
|
op->error = ret;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1573,7 +1578,8 @@ CLOSURE_CALLBACK(bch2_write)
|
|||||||
bch_err_inum_offset_ratelimited(c,
|
bch_err_inum_offset_ratelimited(c,
|
||||||
op->pos.inode,
|
op->pos.inode,
|
||||||
op->pos.offset << 9,
|
op->pos.offset << 9,
|
||||||
"misaligned write");
|
"%s write error: misaligned write",
|
||||||
|
op->flags & BCH_WRITE_MOVE ? "move" : "user");
|
||||||
op->error = -EIO;
|
op->error = -EIO;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -53,33 +53,48 @@ static void bch2_journal_buf_to_text(struct printbuf *out, struct journal *j, u6
|
|||||||
unsigned i = seq & JOURNAL_BUF_MASK;
|
unsigned i = seq & JOURNAL_BUF_MASK;
|
||||||
struct journal_buf *buf = j->buf + i;
|
struct journal_buf *buf = j->buf + i;
|
||||||
|
|
||||||
prt_printf(out, "seq:");
|
prt_str(out, "seq:");
|
||||||
prt_tab(out);
|
prt_tab(out);
|
||||||
prt_printf(out, "%llu", seq);
|
prt_printf(out, "%llu", seq);
|
||||||
prt_newline(out);
|
prt_newline(out);
|
||||||
printbuf_indent_add(out, 2);
|
printbuf_indent_add(out, 2);
|
||||||
|
|
||||||
prt_printf(out, "refcount:");
|
prt_str(out, "refcount:");
|
||||||
prt_tab(out);
|
prt_tab(out);
|
||||||
prt_printf(out, "%u", journal_state_count(s, i));
|
prt_printf(out, "%u", journal_state_count(s, i));
|
||||||
prt_newline(out);
|
prt_newline(out);
|
||||||
|
|
||||||
prt_printf(out, "size:");
|
prt_str(out, "size:");
|
||||||
prt_tab(out);
|
prt_tab(out);
|
||||||
prt_human_readable_u64(out, vstruct_bytes(buf->data));
|
prt_human_readable_u64(out, vstruct_bytes(buf->data));
|
||||||
prt_newline(out);
|
prt_newline(out);
|
||||||
|
|
||||||
prt_printf(out, "expires");
|
prt_str(out, "expires:");
|
||||||
prt_tab(out);
|
prt_tab(out);
|
||||||
prt_printf(out, "%li jiffies", buf->expires - jiffies);
|
prt_printf(out, "%li jiffies", buf->expires - jiffies);
|
||||||
prt_newline(out);
|
prt_newline(out);
|
||||||
|
|
||||||
|
prt_str(out, "flags:");
|
||||||
|
prt_tab(out);
|
||||||
|
if (buf->noflush)
|
||||||
|
prt_str(out, "noflush ");
|
||||||
|
if (buf->must_flush)
|
||||||
|
prt_str(out, "must_flush ");
|
||||||
|
if (buf->separate_flush)
|
||||||
|
prt_str(out, "separate_flush ");
|
||||||
|
if (buf->need_flush_to_write_buffer)
|
||||||
|
prt_str(out, "need_flush_to_write_buffer ");
|
||||||
|
if (buf->need_flush_to_write_buffer)
|
||||||
|
prt_str(out, "need_flush_to_write_buffer ");
|
||||||
if (buf->write_done)
|
if (buf->write_done)
|
||||||
prt_printf(out, "write done\n");
|
prt_str(out, "write done ");
|
||||||
else if (buf->write_allocated)
|
if (buf->write_started)
|
||||||
prt_printf(out, "write allocated\n");
|
prt_str(out, "write started ");
|
||||||
else if (buf->write_started)
|
if (buf->write_allocated)
|
||||||
prt_printf(out, "write started\n");
|
prt_str(out, "write allocated ");
|
||||||
|
if (buf->write_done)
|
||||||
|
prt_str(out, "write done");
|
||||||
|
prt_newline(out);
|
||||||
|
|
||||||
printbuf_indent_sub(out, 2);
|
printbuf_indent_sub(out, 2);
|
||||||
}
|
}
|
||||||
@ -1420,6 +1435,7 @@ void __bch2_journal_debug_to_text(struct printbuf *out, struct journal *j)
|
|||||||
prt_printf(out, "reclaim kicked:\t\t%u\n", j->reclaim_kicked);
|
prt_printf(out, "reclaim kicked:\t\t%u\n", j->reclaim_kicked);
|
||||||
prt_printf(out, "reclaim runs in:\t%u ms\n", time_after(j->next_reclaim, now)
|
prt_printf(out, "reclaim runs in:\t%u ms\n", time_after(j->next_reclaim, now)
|
||||||
? jiffies_to_msecs(j->next_reclaim - jiffies) : 0);
|
? jiffies_to_msecs(j->next_reclaim - jiffies) : 0);
|
||||||
|
prt_printf(out, "blocked:\t\t%u\n", j->blocked);
|
||||||
prt_printf(out, "current entry sectors:\t%u\n", j->cur_entry_sectors);
|
prt_printf(out, "current entry sectors:\t%u\n", j->cur_entry_sectors);
|
||||||
prt_printf(out, "current entry error:\t%s\n", bch2_journal_errors[j->cur_entry_error]);
|
prt_printf(out, "current entry error:\t%s\n", bch2_journal_errors[j->cur_entry_error]);
|
||||||
prt_printf(out, "current entry:\t\t");
|
prt_printf(out, "current entry:\t\t");
|
||||||
|
@ -1830,7 +1830,10 @@ static int bch2_journal_write_prep(struct journal *j, struct journal_buf *w)
|
|||||||
|
|
||||||
if (wb.wb)
|
if (wb.wb)
|
||||||
bch2_journal_keys_to_write_buffer_end(c, &wb);
|
bch2_journal_keys_to_write_buffer_end(c, &wb);
|
||||||
|
|
||||||
|
spin_lock(&c->journal.lock);
|
||||||
w->need_flush_to_write_buffer = false;
|
w->need_flush_to_write_buffer = false;
|
||||||
|
spin_unlock(&c->journal.lock);
|
||||||
|
|
||||||
start = end = vstruct_last(jset);
|
start = end = vstruct_last(jset);
|
||||||
|
|
||||||
@ -1948,12 +1951,20 @@ CLOSURE_CALLBACK(bch2_journal_write)
|
|||||||
unsigned nr_rw_members = 0;
|
unsigned nr_rw_members = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
for_each_rw_member(c, ca)
|
||||||
|
nr_rw_members++;
|
||||||
|
|
||||||
BUG_ON(BCH_SB_CLEAN(c->disk_sb.sb));
|
BUG_ON(BCH_SB_CLEAN(c->disk_sb.sb));
|
||||||
|
BUG_ON(!w->write_started);
|
||||||
BUG_ON(w->write_allocated);
|
BUG_ON(w->write_allocated);
|
||||||
|
BUG_ON(w->write_done);
|
||||||
|
|
||||||
j->write_start_time = local_clock();
|
j->write_start_time = local_clock();
|
||||||
|
|
||||||
spin_lock(&j->lock);
|
spin_lock(&j->lock);
|
||||||
|
if (nr_rw_members > 1)
|
||||||
|
w->separate_flush = true;
|
||||||
|
|
||||||
ret = bch2_journal_write_pick_flush(j, w);
|
ret = bch2_journal_write_pick_flush(j, w);
|
||||||
spin_unlock(&j->lock);
|
spin_unlock(&j->lock);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -2008,12 +2019,6 @@ CLOSURE_CALLBACK(bch2_journal_write)
|
|||||||
if (c->opts.nochanges)
|
if (c->opts.nochanges)
|
||||||
goto no_io;
|
goto no_io;
|
||||||
|
|
||||||
for_each_rw_member(c, ca)
|
|
||||||
nr_rw_members++;
|
|
||||||
|
|
||||||
if (nr_rw_members > 1)
|
|
||||||
w->separate_flush = true;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark journal replicas before we submit the write to guarantee
|
* Mark journal replicas before we submit the write to guarantee
|
||||||
* recovery will find the journal entries after a crash.
|
* recovery will find the journal entries after a crash.
|
||||||
|
@ -887,9 +887,11 @@ int bch2_journal_flush_device_pins(struct journal *j, int dev_idx)
|
|||||||
journal_seq_pin(j, seq)->devs);
|
journal_seq_pin(j, seq)->devs);
|
||||||
seq++;
|
seq++;
|
||||||
|
|
||||||
spin_unlock(&j->lock);
|
if (replicas.e.nr_devs) {
|
||||||
ret = bch2_mark_replicas(c, &replicas.e);
|
spin_unlock(&j->lock);
|
||||||
spin_lock(&j->lock);
|
ret = bch2_mark_replicas(c, &replicas.e);
|
||||||
|
spin_lock(&j->lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
spin_unlock(&j->lock);
|
spin_unlock(&j->lock);
|
||||||
err:
|
err:
|
||||||
|
@ -412,11 +412,11 @@ void bch2_rebalance_status_to_text(struct printbuf *out, struct bch_fs *c)
|
|||||||
u64 now = atomic64_read(&c->io_clock[WRITE].now);
|
u64 now = atomic64_read(&c->io_clock[WRITE].now);
|
||||||
|
|
||||||
prt_str(out, "io wait duration: ");
|
prt_str(out, "io wait duration: ");
|
||||||
bch2_prt_human_readable_s64(out, r->wait_iotime_end - r->wait_iotime_start);
|
bch2_prt_human_readable_s64(out, (r->wait_iotime_end - r->wait_iotime_start) << 9);
|
||||||
prt_newline(out);
|
prt_newline(out);
|
||||||
|
|
||||||
prt_str(out, "io wait remaining: ");
|
prt_str(out, "io wait remaining: ");
|
||||||
bch2_prt_human_readable_s64(out, r->wait_iotime_end - now);
|
bch2_prt_human_readable_s64(out, (r->wait_iotime_end - now) << 9);
|
||||||
prt_newline(out);
|
prt_newline(out);
|
||||||
|
|
||||||
prt_str(out, "duration waited: ");
|
prt_str(out, "duration waited: ");
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
x(check_dirents, 27, PASS_FSCK) \
|
x(check_dirents, 27, PASS_FSCK) \
|
||||||
x(check_xattrs, 28, PASS_FSCK) \
|
x(check_xattrs, 28, PASS_FSCK) \
|
||||||
x(check_root, 29, PASS_ONLINE|PASS_FSCK) \
|
x(check_root, 29, PASS_ONLINE|PASS_FSCK) \
|
||||||
|
x(check_subvolume_structure, 36, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(check_directory_structure, 30, PASS_ONLINE|PASS_FSCK) \
|
x(check_directory_structure, 30, PASS_ONLINE|PASS_FSCK) \
|
||||||
x(check_nlinks, 31, PASS_FSCK) \
|
x(check_nlinks, 31, PASS_FSCK) \
|
||||||
x(delete_dead_inodes, 32, PASS_FSCK|PASS_UNCLEAN) \
|
x(delete_dead_inodes, 32, PASS_FSCK|PASS_UNCLEAN) \
|
||||||
|
@ -262,7 +262,9 @@
|
|||||||
x(subvol_fs_path_parent_wrong, 254) \
|
x(subvol_fs_path_parent_wrong, 254) \
|
||||||
x(subvol_root_fs_path_parent_nonzero, 255) \
|
x(subvol_root_fs_path_parent_nonzero, 255) \
|
||||||
x(subvol_children_not_set, 256) \
|
x(subvol_children_not_set, 256) \
|
||||||
x(subvol_children_bad, 257)
|
x(subvol_children_bad, 257) \
|
||||||
|
x(subvol_loop, 258) \
|
||||||
|
x(subvol_unreachable, 259)
|
||||||
|
|
||||||
enum bch_sb_error_id {
|
enum bch_sb_error_id {
|
||||||
#define x(t, n) BCH_FSCK_ERR_##t = n,
|
#define x(t, n) BCH_FSCK_ERR_##t = n,
|
||||||
|
Loading…
Reference in New Issue
Block a user