Update bcachefs sources to 1dbc147a6eb0 bcachefs: Add version check for bch_btree_ptr_v2.sectors_written validate

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2024-10-17 22:30:32 -04:00
parent 4f9293b045
commit 553d6f107a
53 changed files with 644 additions and 303 deletions

View File

@ -1 +1 @@
9ab6c94002b9def79b0d79d0efd4e5255100feb4 1dbc147a6eb08a0137a81a1fe4eb528186594bfe

View File

@ -32,7 +32,7 @@ void create_link(struct bch_fs *c,
struct bch_inode_unpacked parent_u; struct bch_inode_unpacked parent_u;
struct bch_inode_unpacked inode; struct bch_inode_unpacked inode;
int ret = bch2_trans_do(c, NULL, NULL, 0, int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_link_trans(trans, bch2_link_trans(trans,
(subvol_inum) { 1, parent->bi_inum }, &parent_u, (subvol_inum) { 1, parent->bi_inum }, &parent_u,
(subvol_inum) { 1, inum }, &inode, &qstr)); (subvol_inum) { 1, inum }, &inode, &qstr));
@ -51,7 +51,7 @@ struct bch_inode_unpacked create_file(struct bch_fs *c,
bch2_inode_init_early(c, &new_inode); bch2_inode_init_early(c, &new_inode);
int ret = bch2_trans_do(c, NULL, NULL, 0, int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_create_trans(trans, bch2_create_trans(trans,
(subvol_inum) { 1, parent->bi_inum }, parent, (subvol_inum) { 1, parent->bi_inum }, parent,
&new_inode, &qstr, &new_inode, &qstr,
@ -125,7 +125,7 @@ void copy_xattrs(struct bch_fs *c, struct bch_inode_unpacked *dst,
if (IS_ERR(h)) if (IS_ERR(h))
continue; continue;
int ret = bch2_trans_do(c, NULL, NULL, 0, int ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_xattr_set(trans, bch2_xattr_set(trans,
(subvol_inum) { 1, dst->bi_inum }, (subvol_inum) { 1, dst->bi_inum },
dst, &hash_info, attr, dst, &hash_info, attr,

View File

@ -111,6 +111,7 @@ static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
#define kzfree(p) free((void *) p) #define kzfree(p) free((void *) p)
#define kvmalloc(size, flags) kmalloc(size, flags) #define kvmalloc(size, flags) kmalloc(size, flags)
#define kvmalloc_noprof(size, flags) kmalloc(size, flags)
#define kvzalloc(size, flags) kzalloc(size, flags) #define kvzalloc(size, flags) kzalloc(size, flags)
#define kvfree(p) kfree(p) #define kvfree(p) kfree(p)
@ -274,6 +275,8 @@ static inline void *vmalloc(unsigned long size)
return __vmalloc(size, GFP_KERNEL); return __vmalloc(size, GFP_KERNEL);
} }
#define vmalloc_noprof(...) vmalloc(__VA_ARGS__)
static inline void *vzalloc(unsigned long size) static inline void *vzalloc(unsigned long size)
{ {
return __vmalloc(size, GFP_KERNEL|__GFP_ZERO); return __vmalloc(size, GFP_KERNEL|__GFP_ZERO);

View File

@ -96,6 +96,7 @@ do { \
#define wait_event_freezable(wq, condition) ({wait_event(wq, condition); 0; }) #define wait_event_freezable(wq, condition) ({wait_event(wq, condition); 0; })
#define wait_event_killable(wq, condition) ({wait_event(wq, condition); 0; }) #define wait_event_killable(wq, condition) ({wait_event(wq, condition); 0; })
#define wait_event_interruptible(wq, condition) ({wait_event(wq, condition); 0; }) #define wait_event_interruptible(wq, condition) ({wait_event(wq, condition); 0; })
#define wait_event_state(wq, condition, state) ({wait_event(wq, condition); 0; })
#define __wait_event_timeout(wq, condition, timeout) \ #define __wait_event_timeout(wq, condition, timeout) \
___wait_event(wq, ___wait_cond_timeout(condition), \ ___wait_event(wq, ___wait_cond_timeout(condition), \

View File

@ -639,6 +639,16 @@ int bch2_alloc_read(struct bch_fs *c)
continue; continue;
} }
if (k.k->p.offset < ca->mi.first_bucket) {
bch2_btree_iter_set_pos(&iter, POS(k.k->p.inode, ca->mi.first_bucket));
continue;
}
if (k.k->p.offset >= ca->mi.nbuckets) {
bch2_btree_iter_set_pos(&iter, POS(k.k->p.inode + 1, 0));
continue;
}
struct bch_alloc_v4 a; struct bch_alloc_v4 a;
*bucket_gen(ca, k.k->p.offset) = bch2_alloc_to_v4(k, &a)->gen; *bucket_gen(ca, k.k->p.offset) = bch2_alloc_to_v4(k, &a)->gen;
0; 0;
@ -1967,7 +1977,7 @@ static void bch2_do_discards_fast_work(struct work_struct *work)
ca->mi.bucket_size, ca->mi.bucket_size,
GFP_KERNEL); GFP_KERNEL);
int ret = bch2_trans_do(c, NULL, NULL, int ret = bch2_trans_commit_do(c, NULL, NULL,
BCH_WATERMARK_btree| BCH_WATERMARK_btree|
BCH_TRANS_COMMIT_no_enospc, BCH_TRANS_COMMIT_no_enospc,
bch2_clear_bucket_needs_discard(trans, POS(ca->dev_idx, bucket))); bch2_clear_bucket_needs_discard(trans, POS(ca->dev_idx, bucket)));
@ -2127,14 +2137,15 @@ static void bch2_do_invalidates_work(struct work_struct *work)
struct bkey_s_c k = next_lru_key(trans, &iter, ca, &wrapped); struct bkey_s_c k = next_lru_key(trans, &iter, ca, &wrapped);
ret = bkey_err(k); ret = bkey_err(k);
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
continue;
if (ret) if (ret)
break; goto restart_err;
if (!k.k) if (!k.k)
break; break;
ret = invalidate_one_bucket(trans, &iter, k, &nr_to_invalidate); ret = invalidate_one_bucket(trans, &iter, k, &nr_to_invalidate);
restart_err:
if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
continue;
if (ret) if (ret)
break; break;
@ -2340,24 +2351,19 @@ int bch2_dev_remove_alloc(struct bch_fs *c, struct bch_dev *ca)
/* Bucket IO clocks: */ /* Bucket IO clocks: */
int bch2_bucket_io_time_reset(struct btree_trans *trans, unsigned dev, static int __bch2_bucket_io_time_reset(struct btree_trans *trans, unsigned dev,
size_t bucket_nr, int rw) size_t bucket_nr, int rw)
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
struct btree_iter iter; struct btree_iter iter;
struct bkey_i_alloc_v4 *a; struct bkey_i_alloc_v4 *a =
u64 now; bch2_trans_start_alloc_update_noupdate(trans, &iter, POS(dev, bucket_nr));
int ret = 0; int ret = PTR_ERR_OR_ZERO(a);
if (bch2_trans_relock(trans))
bch2_trans_begin(trans);
a = bch2_trans_start_alloc_update_noupdate(trans, &iter, POS(dev, bucket_nr));
ret = PTR_ERR_OR_ZERO(a);
if (ret) if (ret)
return ret; return ret;
now = bch2_current_io_time(c, rw); u64 now = bch2_current_io_time(c, rw);
if (a->v.io_time[rw] == now) if (a->v.io_time[rw] == now)
goto out; goto out;
@ -2370,6 +2376,15 @@ out:
return ret; return ret;
} }
int bch2_bucket_io_time_reset(struct btree_trans *trans, unsigned dev,
size_t bucket_nr, int rw)
{
if (bch2_trans_relock(trans))
bch2_trans_begin(trans);
return nested_lockrestart_do(trans, __bch2_bucket_io_time_reset(trans, dev, bucket_nr, rw));
}
/* Startup/shutdown (ro/rw): */ /* Startup/shutdown (ro/rw): */
void bch2_recalc_capacity(struct bch_fs *c) void bch2_recalc_capacity(struct bch_fs *c)

View File

@ -684,7 +684,7 @@ struct open_bucket *bch2_bucket_alloc(struct bch_fs *c, struct bch_dev *ca,
struct bch_dev_usage usage; struct bch_dev_usage usage;
struct open_bucket *ob; struct open_bucket *ob;
bch2_trans_do(c, NULL, NULL, 0, bch2_trans_do(c,
PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(trans, ca, watermark, PTR_ERR_OR_ZERO(ob = bch2_bucket_alloc_trans(trans, ca, watermark,
data_type, cl, false, &usage))); data_type, cl, false, &usage)));
return ob; return ob;
@ -1610,8 +1610,7 @@ void bch2_open_buckets_to_text(struct printbuf *out, struct bch_fs *c,
ob < c->open_buckets + ARRAY_SIZE(c->open_buckets); ob < c->open_buckets + ARRAY_SIZE(c->open_buckets);
ob++) { ob++) {
spin_lock(&ob->lock); spin_lock(&ob->lock);
if (ob->valid && !ob->on_partial_list && if (ob->valid && (!ca || ob->dev == ca->dev_idx))
(!ca || ob->dev == ca->dev_idx))
bch2_open_bucket_to_text(out, c, ob); bch2_open_bucket_to_text(out, c, ob);
spin_unlock(&ob->lock); spin_unlock(&ob->lock);
} }

View File

@ -829,12 +829,22 @@ static int bch2_alloc_write_key(struct btree_trans *trans,
* fix that here: * fix that here:
*/ */
alloc_data_type_set(&gc, gc.data_type); alloc_data_type_set(&gc, gc.data_type);
if (gc.data_type != old_gc.data_type || if (gc.data_type != old_gc.data_type ||
gc.dirty_sectors != old_gc.dirty_sectors) { gc.dirty_sectors != old_gc.dirty_sectors) {
ret = bch2_alloc_key_to_dev_counters(trans, ca, &old_gc, &gc, BTREE_TRIGGER_gc); ret = bch2_alloc_key_to_dev_counters(trans, ca, &old_gc, &gc, BTREE_TRIGGER_gc);
if (ret) if (ret)
return ret; return ret;
/*
* Ugly: alloc_key_to_dev_counters(..., BTREE_TRIGGER_gc) is not
* safe w.r.t. transaction restarts, so fixup the gc_bucket so
* we don't run it twice:
*/
percpu_down_read(&c->mark_lock);
struct bucket *gc_m = gc_bucket(ca, iter->pos.offset);
gc_m->data_type = gc.data_type;
gc_m->dirty_sectors = gc.dirty_sectors;
percpu_up_read(&c->mark_lock);
} }
if (fsck_err_on(new.data_type != gc.data_type, if (fsck_err_on(new.data_type != gc.data_type,

View File

@ -1872,7 +1872,7 @@ static void btree_node_write_work(struct work_struct *work)
} }
} else { } else {
ret = bch2_trans_do(c, NULL, NULL, 0, ret = bch2_trans_do(c,
bch2_btree_node_update_key_get_iter(trans, b, &wbio->key, bch2_btree_node_update_key_get_iter(trans, b, &wbio->key,
BCH_WATERMARK_interior_updates| BCH_WATERMARK_interior_updates|
BCH_TRANS_COMMIT_journal_reclaim| BCH_TRANS_COMMIT_journal_reclaim|

View File

@ -1414,9 +1414,17 @@ void __noreturn bch2_trans_restart_error(struct btree_trans *trans, u32 restart_
void __noreturn bch2_trans_in_restart_error(struct btree_trans *trans) void __noreturn bch2_trans_in_restart_error(struct btree_trans *trans)
{ {
#ifdef CONFIG_BCACHEFS_DEBUG
struct printbuf buf = PRINTBUF;
bch2_prt_backtrace(&buf, &trans->last_restarted_trace);
panic("in transaction restart: %s, last restarted by\n%s",
bch2_err_str(trans->restarted),
buf.buf);
#else
panic("in transaction restart: %s, last restarted by %pS\n", panic("in transaction restart: %s, last restarted by %pS\n",
bch2_err_str(trans->restarted), bch2_err_str(trans->restarted),
(void *) trans->last_restarted_ip); (void *) trans->last_restarted_ip);
#endif
} }
void __noreturn bch2_trans_unlocked_error(struct btree_trans *trans) void __noreturn bch2_trans_unlocked_error(struct btree_trans *trans)
@ -2292,12 +2300,6 @@ struct bkey_s_c bch2_btree_iter_peek_upto(struct btree_iter *iter, struct bpos e
bch2_btree_iter_verify_entry_exit(iter); bch2_btree_iter_verify_entry_exit(iter);
ret = trans_maybe_inject_restart(trans, _RET_IP_);
if (unlikely(ret)) {
k = bkey_s_c_err(ret);
goto out_no_locked;
}
while (1) { while (1) {
k = __bch2_btree_iter_peek(iter, search_key); k = __bch2_btree_iter_peek(iter, search_key);
if (unlikely(!k.k)) if (unlikely(!k.k))
@ -2467,12 +2469,6 @@ struct bkey_s_c bch2_btree_iter_peek_prev(struct btree_iter *iter)
if (iter->flags & BTREE_ITER_with_journal) if (iter->flags & BTREE_ITER_with_journal)
return bkey_s_c_err(-BCH_ERR_btree_iter_with_journal_not_supported); return bkey_s_c_err(-BCH_ERR_btree_iter_with_journal_not_supported);
ret = trans_maybe_inject_restart(trans, _RET_IP_);
if (unlikely(ret)) {
k = bkey_s_c_err(ret);
goto out_no_locked;
}
bch2_btree_iter_verify(iter); bch2_btree_iter_verify(iter);
bch2_btree_iter_verify_entry_exit(iter); bch2_btree_iter_verify_entry_exit(iter);
@ -2610,12 +2606,6 @@ struct bkey_s_c bch2_btree_iter_peek_slot(struct btree_iter *iter)
bch2_btree_iter_verify_entry_exit(iter); bch2_btree_iter_verify_entry_exit(iter);
EBUG_ON(btree_iter_path(trans, iter)->level && (iter->flags & BTREE_ITER_with_key_cache)); EBUG_ON(btree_iter_path(trans, iter)->level && (iter->flags & BTREE_ITER_with_key_cache));
ret = trans_maybe_inject_restart(trans, _RET_IP_);
if (unlikely(ret)) {
k = bkey_s_c_err(ret);
goto out_no_locked;
}
/* extents can't span inode numbers: */ /* extents can't span inode numbers: */
if ((iter->flags & BTREE_ITER_is_extents) && if ((iter->flags & BTREE_ITER_is_extents) &&
unlikely(iter->pos.offset == KEY_OFFSET_MAX)) { unlikely(iter->pos.offset == KEY_OFFSET_MAX)) {
@ -2961,10 +2951,6 @@ void *__bch2_trans_kmalloc(struct btree_trans *trans, size_t size)
WARN_ON_ONCE(new_bytes > BTREE_TRANS_MEM_MAX); WARN_ON_ONCE(new_bytes > BTREE_TRANS_MEM_MAX);
ret = trans_maybe_inject_restart(trans, _RET_IP_);
if (ret)
return ERR_PTR(ret);
struct btree_transaction_stats *s = btree_trans_stats(trans); struct btree_transaction_stats *s = btree_trans_stats(trans);
s->max_mem = max(s->max_mem, new_bytes); s->max_mem = max(s->max_mem, new_bytes);
@ -3022,8 +3008,7 @@ out_new_mem:
if (old_bytes) { if (old_bytes) {
trace_and_count(c, trans_restart_mem_realloced, trans, _RET_IP_, new_bytes); trace_and_count(c, trans_restart_mem_realloced, trans, _RET_IP_, new_bytes);
return ERR_PTR(btree_trans_restart_ip(trans, return ERR_PTR(btree_trans_restart(trans, BCH_ERR_transaction_restart_mem_realloced));
BCH_ERR_transaction_restart_mem_realloced, _RET_IP_));
} }
out_change_top: out_change_top:
p = trans->mem + trans->mem_top; p = trans->mem + trans->mem_top;
@ -3131,14 +3116,6 @@ u32 bch2_trans_begin(struct btree_trans *trans)
trans->last_begin_ip = _RET_IP_; trans->last_begin_ip = _RET_IP_;
#ifdef CONFIG_BCACHEFS_INJECT_TRANSACTION_RESTARTS
if (trans->restarted) {
trans->restart_count_this_trans++;
} else {
trans->restart_count_this_trans = 0;
}
#endif
trans_set_locked(trans); trans_set_locked(trans);
if (trans->restarted) { if (trans->restarted) {
@ -3279,6 +3256,9 @@ void bch2_trans_put(struct btree_trans *trans)
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
if (trans->restarted)
bch2_trans_in_restart_error(trans);
bch2_trans_unlock(trans); bch2_trans_unlock(trans);
trans_for_each_update(trans, i) trans_for_each_update(trans, i)
@ -3302,6 +3282,10 @@ void bch2_trans_put(struct btree_trans *trans)
closure_return_sync(&trans->ref); closure_return_sync(&trans->ref);
trans->locking_wait.task = NULL; trans->locking_wait.task = NULL;
#ifdef CONFIG_BCACHEFS_DEBUG
darray_exit(&trans->last_restarted_trace);
#endif
unsigned long *paths_allocated = trans->paths_allocated; unsigned long *paths_allocated = trans->paths_allocated;
trans->paths_allocated = NULL; trans->paths_allocated = NULL;
trans->paths = NULL; trans->paths = NULL;

View File

@ -350,6 +350,10 @@ static int btree_trans_restart_ip(struct btree_trans *trans, int err, unsigned l
trans->restarted = err; trans->restarted = err;
trans->last_restarted_ip = ip; trans->last_restarted_ip = ip;
#ifdef CONFIG_BCACHEFS_DEBUG
darray_exit(&trans->last_restarted_trace);
bch2_save_backtrace(&trans->last_restarted_trace, current, 0, GFP_KERNEL);
#endif
return -err; return -err;
} }
@ -359,18 +363,6 @@ static int btree_trans_restart(struct btree_trans *trans, int err)
return btree_trans_restart_ip(trans, err, _THIS_IP_); return btree_trans_restart_ip(trans, err, _THIS_IP_);
} }
static inline int trans_maybe_inject_restart(struct btree_trans *trans, unsigned long ip)
{
#ifdef CONFIG_BCACHEFS_INJECT_TRANSACTION_RESTARTS
if (!(ktime_get_ns() & ~(~0ULL << min(63, (10 + trans->restart_count_this_trans))))) {
trace_and_count(trans->c, trans_restart_injected, trans, ip);
return btree_trans_restart_ip(trans,
BCH_ERR_transaction_restart_fault_inject, ip);
}
#endif
return 0;
}
bool bch2_btree_node_upgrade(struct btree_trans *, bool bch2_btree_node_upgrade(struct btree_trans *,
struct btree_path *, unsigned); struct btree_path *, unsigned);
@ -923,6 +915,8 @@ struct bkey_s_c bch2_btree_iter_peek_and_restart_outlined(struct btree_iter *);
_ret; \ _ret; \
}) })
#define bch2_trans_do(_c, _do) bch2_trans_run(_c, lockrestart_do(trans, _do))
struct btree_trans *__bch2_trans_get(struct bch_fs *, unsigned); struct btree_trans *__bch2_trans_get(struct bch_fs *, unsigned);
void bch2_trans_put(struct btree_trans *); void bch2_trans_put(struct btree_trans *);

View File

@ -1027,10 +1027,6 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
bch2_trans_verify_not_unlocked(trans); bch2_trans_verify_not_unlocked(trans);
bch2_trans_verify_not_in_restart(trans); bch2_trans_verify_not_in_restart(trans);
ret = trans_maybe_inject_restart(trans, _RET_IP_);
if (unlikely(ret))
goto out_reset;
if (!trans->nr_updates && if (!trans->nr_updates &&
!trans->journal_entries_u64s) !trans->journal_entries_u64s)
goto out_reset; goto out_reset;

View File

@ -509,13 +509,13 @@ struct btree_trans {
bool notrace_relock_fail:1; bool notrace_relock_fail:1;
enum bch_errcode restarted:16; enum bch_errcode restarted:16;
u32 restart_count; u32 restart_count;
#ifdef CONFIG_BCACHEFS_INJECT_TRANSACTION_RESTARTS
u32 restart_count_this_trans;
#endif
u64 last_begin_time; u64 last_begin_time;
unsigned long last_begin_ip; unsigned long last_begin_ip;
unsigned long last_restarted_ip; unsigned long last_restarted_ip;
#ifdef CONFIG_BCACHEFS_DEBUG
bch_stacktrace last_restarted_trace;
#endif
unsigned long last_unlock_ip; unsigned long last_unlock_ip;
unsigned long srcu_lock_time; unsigned long srcu_lock_time;

View File

@ -668,7 +668,7 @@ int bch2_btree_insert(struct bch_fs *c, enum btree_id id, struct bkey_i *k,
struct disk_reservation *disk_res, int flags, struct disk_reservation *disk_res, int flags,
enum btree_iter_update_trigger_flags iter_flags) enum btree_iter_update_trigger_flags iter_flags)
{ {
return bch2_trans_do(c, disk_res, NULL, flags, return bch2_trans_commit_do(c, disk_res, NULL, flags,
bch2_btree_insert_trans(trans, id, k, iter_flags)); bch2_btree_insert_trans(trans, id, k, iter_flags));
} }
@ -865,7 +865,7 @@ __bch2_fs_log_msg(struct bch_fs *c, unsigned commit_flags, const char *fmt,
memcpy(l->d, buf.buf, buf.pos); memcpy(l->d, buf.buf, buf.pos);
c->journal.early_journal_entries.nr += jset_u64s(u64s); c->journal.early_journal_entries.nr += jset_u64s(u64s);
} else { } else {
ret = bch2_trans_do(c, NULL, NULL, ret = bch2_trans_commit_do(c, NULL, NULL,
BCH_TRANS_COMMIT_lazy_rw|commit_flags, BCH_TRANS_COMMIT_lazy_rw|commit_flags,
__bch2_trans_log_msg(trans, &buf, u64s)); __bch2_trans_log_msg(trans, &buf, u64s));
} }

View File

@ -192,7 +192,7 @@ static inline int bch2_trans_commit(struct btree_trans *trans,
nested_lockrestart_do(_trans, _do ?: bch2_trans_commit(_trans, (_disk_res),\ nested_lockrestart_do(_trans, _do ?: bch2_trans_commit(_trans, (_disk_res),\
(_journal_seq), (_flags))) (_journal_seq), (_flags)))
#define bch2_trans_do(_c, _disk_res, _journal_seq, _flags, _do) \ #define bch2_trans_commit_do(_c, _disk_res, _journal_seq, _flags, _do) \
bch2_trans_run(_c, commit_do(trans, _disk_res, _journal_seq, _flags, _do)) bch2_trans_run(_c, commit_do(trans, _disk_res, _journal_seq, _flags, _do))
#define trans_for_each_update(_trans, _i) \ #define trans_for_each_update(_trans, _i) \

View File

@ -2239,10 +2239,8 @@ static void async_btree_node_rewrite_work(struct work_struct *work)
struct async_btree_rewrite *a = struct async_btree_rewrite *a =
container_of(work, struct async_btree_rewrite, work); container_of(work, struct async_btree_rewrite, work);
struct bch_fs *c = a->c; struct bch_fs *c = a->c;
int ret;
ret = bch2_trans_do(c, NULL, NULL, 0, int ret = bch2_trans_do(c, async_btree_node_rewrite_trans(trans, a));
async_btree_node_rewrite_trans(trans, a));
bch_err_fn_ratelimited(c, ret); bch_err_fn_ratelimited(c, ret);
bch2_write_ref_put(c, BCH_WRITE_REF_node_rewrite); bch2_write_ref_put(c, BCH_WRITE_REF_node_rewrite);
kfree(a); kfree(a);

View File

@ -1160,11 +1160,11 @@ int bch2_trans_mark_dev_sbs(struct bch_fs *c)
#define SECTORS_CACHE 1024 #define SECTORS_CACHE 1024
int __bch2_disk_reservation_add(struct bch_fs *c, struct disk_reservation *res, int __bch2_disk_reservation_add(struct bch_fs *c, struct disk_reservation *res,
u64 sectors, int flags) u64 sectors, enum bch_reservation_flags flags)
{ {
struct bch_fs_pcpu *pcpu; struct bch_fs_pcpu *pcpu;
u64 old, get; u64 old, get;
s64 sectors_available; u64 sectors_available;
int ret; int ret;
percpu_down_read(&c->mark_lock); percpu_down_read(&c->mark_lock);
@ -1202,6 +1202,9 @@ recalculate:
percpu_u64_set(&c->pcpu->sectors_available, 0); percpu_u64_set(&c->pcpu->sectors_available, 0);
sectors_available = avail_factor(__bch2_fs_usage_read_short(c).free); sectors_available = avail_factor(__bch2_fs_usage_read_short(c).free);
if (sectors_available && (flags & BCH_DISK_RESERVATION_PARTIAL))
sectors = min(sectors, sectors_available);
if (sectors <= sectors_available || if (sectors <= sectors_available ||
(flags & BCH_DISK_RESERVATION_NOFAIL)) { (flags & BCH_DISK_RESERVATION_NOFAIL)) {
atomic64_set(&c->sectors_available, atomic64_set(&c->sectors_available,

View File

@ -344,14 +344,16 @@ static inline void bch2_disk_reservation_put(struct bch_fs *c,
} }
} }
#define BCH_DISK_RESERVATION_NOFAIL (1 << 0) enum bch_reservation_flags {
BCH_DISK_RESERVATION_NOFAIL = 1 << 0,
BCH_DISK_RESERVATION_PARTIAL = 1 << 1,
};
int __bch2_disk_reservation_add(struct bch_fs *, int __bch2_disk_reservation_add(struct bch_fs *, struct disk_reservation *,
struct disk_reservation *, u64, enum bch_reservation_flags);
u64, int);
static inline int bch2_disk_reservation_add(struct bch_fs *c, struct disk_reservation *res, static inline int bch2_disk_reservation_add(struct bch_fs *c, struct disk_reservation *res,
u64 sectors, int flags) u64 sectors, enum bch_reservation_flags flags)
{ {
#ifdef __KERNEL__ #ifdef __KERNEL__
u64 old, new; u64 old, new;

View File

@ -225,6 +225,7 @@ static long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_a
opt_set(thr->opts, stdio, (u64)(unsigned long)&thr->thr.stdio); opt_set(thr->opts, stdio, (u64)(unsigned long)&thr->thr.stdio);
opt_set(thr->opts, read_only, 1); opt_set(thr->opts, read_only, 1);
opt_set(thr->opts, ratelimit_errors, 0);
/* We need request_key() to be called before we punt to kthread: */ /* We need request_key() to be called before we punt to kthread: */
opt_set(thr->opts, nostart, true); opt_set(thr->opts, nostart, true);

View File

@ -2,6 +2,7 @@
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h>
#include "darray.h" #include "darray.h"
int __bch2_darray_resize_noprof(darray_char *d, size_t element_size, size_t new_size, gfp_t gfp) int __bch2_darray_resize_noprof(darray_char *d, size_t element_size, size_t new_size, gfp_t gfp)
@ -9,7 +10,19 @@ int __bch2_darray_resize_noprof(darray_char *d, size_t element_size, size_t new_
if (new_size > d->size) { if (new_size > d->size) {
new_size = roundup_pow_of_two(new_size); new_size = roundup_pow_of_two(new_size);
void *data = kvmalloc_array_noprof(new_size, element_size, gfp); /*
* This is a workaround: kvmalloc() doesn't support > INT_MAX
* allocations, but vmalloc() does.
* The limit needs to be lifted from kvmalloc, and when it does
* we'll go back to just using that.
*/
size_t bytes;
if (unlikely(check_mul_overflow(new_size, element_size, &bytes)))
return -ENOMEM;
void *data = likely(bytes < INT_MAX)
? kvmalloc_noprof(bytes, gfp)
: vmalloc_noprof(bytes);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;

View File

@ -80,6 +80,7 @@ static bool bkey_nocow_lock(struct bch_fs *c, struct moving_context *ctxt, struc
if (ptr2 == ptr) if (ptr2 == ptr)
break; break;
ca = bch2_dev_have_ref(c, ptr2->dev);
bucket = PTR_BUCKET_POS(ca, ptr2); bucket = PTR_BUCKET_POS(ca, ptr2);
bch2_bucket_nocow_unlock(&c->nocow_locks, bucket, 0); bch2_bucket_nocow_unlock(&c->nocow_locks, bucket, 0);
} }

View File

@ -250,13 +250,6 @@ int bch2_dirent_create(struct btree_trans *trans, subvol_inum dir,
return ret; return ret;
} }
static void dirent_copy_target(struct bkey_i_dirent *dst,
struct bkey_s_c_dirent src)
{
dst->v.d_inum = src.v->d_inum;
dst->v.d_type = src.v->d_type;
}
int bch2_dirent_read_target(struct btree_trans *trans, subvol_inum dir, int bch2_dirent_read_target(struct btree_trans *trans, subvol_inum dir,
struct bkey_s_c_dirent d, subvol_inum *target) struct bkey_s_c_dirent d, subvol_inum *target)
{ {

View File

@ -34,6 +34,13 @@ static inline unsigned dirent_val_u64s(unsigned len)
int bch2_dirent_read_target(struct btree_trans *, subvol_inum, int bch2_dirent_read_target(struct btree_trans *, subvol_inum,
struct bkey_s_c_dirent, subvol_inum *); struct bkey_s_c_dirent, subvol_inum *);
static inline void dirent_copy_target(struct bkey_i_dirent *dst,
struct bkey_s_c_dirent src)
{
dst->v.d_inum = src.v->d_inum;
dst->v.d_type = src.v->d_type;
}
int bch2_dirent_create_snapshot(struct btree_trans *, u32, u64, u32, int bch2_dirent_create_snapshot(struct btree_trans *, u32, u64, u32,
const struct bch_hash_info *, u8, const struct bch_hash_info *, u8,
const struct qstr *, u64, u64 *, const struct qstr *, u64, u64 *,

View File

@ -244,10 +244,10 @@ void bch2_accounting_swab(struct bkey_s k)
} }
static inline void __accounting_to_replicas(struct bch_replicas_entry_v1 *r, static inline void __accounting_to_replicas(struct bch_replicas_entry_v1 *r,
struct disk_accounting_pos acc) struct disk_accounting_pos *acc)
{ {
unsafe_memcpy(r, &acc.replicas, unsafe_memcpy(r, &acc->replicas,
replicas_entry_bytes(&acc.replicas), replicas_entry_bytes(&acc->replicas),
"variable length struct"); "variable length struct");
} }
@ -258,7 +258,7 @@ static inline bool accounting_to_replicas(struct bch_replicas_entry_v1 *r, struc
switch (acc_k.type) { switch (acc_k.type) {
case BCH_DISK_ACCOUNTING_replicas: case BCH_DISK_ACCOUNTING_replicas:
__accounting_to_replicas(r, acc_k); __accounting_to_replicas(r, &acc_k);
return true; return true;
default: default:
return false; return false;
@ -626,7 +626,7 @@ static int bch2_disk_accounting_validate_late(struct btree_trans *trans,
switch (acc.type) { switch (acc.type) {
case BCH_DISK_ACCOUNTING_replicas: { case BCH_DISK_ACCOUNTING_replicas: {
struct bch_replicas_padded r; struct bch_replicas_padded r;
__accounting_to_replicas(&r.e, acc); __accounting_to_replicas(&r.e, &acc);
for (unsigned i = 0; i < r.e.nr_devs; i++) for (unsigned i = 0; i < r.e.nr_devs; i++)
if (r.e.devs[i] != BCH_SB_MEMBER_INVALID && if (r.e.devs[i] != BCH_SB_MEMBER_INVALID &&
@ -857,8 +857,10 @@ int bch2_dev_usage_init(struct bch_dev *ca, bool gc)
}; };
u64 v[3] = { ca->mi.nbuckets - ca->mi.first_bucket, 0, 0 }; u64 v[3] = { ca->mi.nbuckets - ca->mi.first_bucket, 0, 0 };
int ret = bch2_trans_do(c, NULL, NULL, 0, int ret = bch2_trans_do(c, ({
bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), gc)); bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), gc) ?:
(!gc ? bch2_trans_commit(trans, NULL, NULL, 0) : 0);
}));
bch_err_fn(c, ret); bch_err_fn(c, ret);
return ret; return ret;
} }

View File

@ -124,6 +124,11 @@ int bch2_stripe_validate(struct bch_fs *c, struct bkey_s_c k,
"incorrect value size (%zu < %u)", "incorrect value size (%zu < %u)",
bkey_val_u64s(k.k), stripe_val_u64s(s)); bkey_val_u64s(k.k), stripe_val_u64s(s));
bkey_fsck_err_on(s->csum_granularity_bits >= 64,
c, stripe_csum_granularity_bad,
"invalid csum granularity (%u >= 64)",
s->csum_granularity_bits);
ret = bch2_bkey_ptrs_validate(c, k, flags); ret = bch2_bkey_ptrs_validate(c, k, flags);
fsck_err: fsck_err:
return ret; return ret;
@ -145,7 +150,11 @@ void bch2_stripe_to_text(struct printbuf *out, struct bch_fs *c,
nr_data, nr_data,
s.nr_redundant); s.nr_redundant);
bch2_prt_csum_type(out, s.csum_type); bch2_prt_csum_type(out, s.csum_type);
prt_printf(out, " gran %u", 1U << s.csum_granularity_bits); prt_str(out, " gran ");
if (s.csum_granularity_bits < 64)
prt_printf(out, "%llu", 1ULL << s.csum_granularity_bits);
else
prt_printf(out, "(invalid shift %u)", s.csum_granularity_bits);
if (s.disk_label) { if (s.disk_label) {
prt_str(out, " label"); prt_str(out, " label");
@ -257,12 +266,12 @@ static int __mark_stripe_bucket(struct btree_trans *trans,
if (!deleting) { if (!deleting) {
a->stripe = s.k->p.offset; a->stripe = s.k->p.offset;
a->stripe_redundancy = s.v->nr_redundant; a->stripe_redundancy = s.v->nr_redundant;
alloc_data_type_set(a, data_type);
} else { } else {
a->stripe = 0; a->stripe = 0;
a->stripe_redundancy = 0; a->stripe_redundancy = 0;
alloc_data_type_set(a, BCH_DATA_user);
} }
alloc_data_type_set(a, data_type);
err: err:
printbuf_exit(&buf); printbuf_exit(&buf);
return ret; return ret;
@ -1177,7 +1186,7 @@ static void ec_stripe_delete_work(struct work_struct *work)
if (!idx) if (!idx)
break; break;
int ret = bch2_trans_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc, int ret = bch2_trans_commit_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
ec_stripe_delete(trans, idx)); ec_stripe_delete(trans, idx));
bch_err_fn(c, ret); bch_err_fn(c, ret);
if (ret) if (ret)
@ -1197,47 +1206,62 @@ void bch2_do_stripe_deletes(struct bch_fs *c)
/* stripe creation: */ /* stripe creation: */
static int ec_stripe_key_update(struct btree_trans *trans, static int ec_stripe_key_update(struct btree_trans *trans,
struct bkey_i_stripe *new, struct bkey_i_stripe *old,
bool create) struct bkey_i_stripe *new)
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
struct btree_iter iter; bool create = !old;
struct bkey_s_c k;
int ret;
k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_stripes, struct btree_iter iter;
new->k.p, BTREE_ITER_intent); struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_stripes,
ret = bkey_err(k); new->k.p, BTREE_ITER_intent);
int ret = bkey_err(k);
if (ret) if (ret)
goto err; goto err;
if (k.k->type != (create ? KEY_TYPE_deleted : KEY_TYPE_stripe)) { if (bch2_fs_inconsistent_on(k.k->type != (create ? KEY_TYPE_deleted : KEY_TYPE_stripe),
bch2_fs_inconsistent(c, "error %s stripe: got existing key type %s", c, "error %s stripe: got existing key type %s",
create ? "creating" : "updating", create ? "creating" : "updating",
bch2_bkey_types[k.k->type]); bch2_bkey_types[k.k->type])) {
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;
} }
if (k.k->type == KEY_TYPE_stripe) { if (k.k->type == KEY_TYPE_stripe) {
const struct bch_stripe *old = bkey_s_c_to_stripe(k).v; const struct bch_stripe *v = bkey_s_c_to_stripe(k).v;
unsigned i;
if (old->nr_blocks != new->v.nr_blocks) { BUG_ON(old->v.nr_blocks != new->v.nr_blocks);
bch_err(c, "error updating stripe: nr_blocks does not match"); BUG_ON(old->v.nr_blocks != v->nr_blocks);
ret = -EINVAL;
goto err;
}
for (i = 0; i < new->v.nr_blocks; i++) { for (unsigned i = 0; i < new->v.nr_blocks; i++) {
unsigned v = stripe_blockcount_get(old, i); unsigned sectors = stripe_blockcount_get(v, i);
BUG_ON(v && if (!bch2_extent_ptr_eq(old->v.ptrs[i], new->v.ptrs[i]) && sectors) {
(old->ptrs[i].dev != new->v.ptrs[i].dev || struct printbuf buf = PRINTBUF;
old->ptrs[i].gen != new->v.ptrs[i].gen ||
old->ptrs[i].offset != new->v.ptrs[i].offset));
stripe_blockcount_set(&new->v, i, v); prt_printf(&buf, "stripe changed nonempty block %u", i);
prt_str(&buf, "\nold: ");
bch2_bkey_val_to_text(&buf, c, k);
prt_str(&buf, "\nnew: ");
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&new->k_i));
bch2_fs_inconsistent(c, "%s", buf.buf);
printbuf_exit(&buf);
ret = -EINVAL;
goto err;
}
/*
* If the stripe ptr changed underneath us, it must have
* been dev_remove_stripes() -> * invalidate_stripe_to_dev()
*/
if (!bch2_extent_ptr_eq(old->v.ptrs[i], v->ptrs[i])) {
BUG_ON(v->ptrs[i].dev != BCH_SB_MEMBER_INVALID);
if (bch2_extent_ptr_eq(old->v.ptrs[i], new->v.ptrs[i]))
new->v.ptrs[i].dev = BCH_SB_MEMBER_INVALID;
}
stripe_blockcount_set(&new->v, i, sectors);
} }
} }
@ -1495,12 +1519,14 @@ static void ec_stripe_create(struct ec_stripe_new *s)
goto err; goto err;
} }
ret = bch2_trans_do(c, &s->res, NULL, ret = bch2_trans_commit_do(c, &s->res, NULL,
BCH_TRANS_COMMIT_no_check_rw| BCH_TRANS_COMMIT_no_check_rw|
BCH_TRANS_COMMIT_no_enospc, BCH_TRANS_COMMIT_no_enospc,
ec_stripe_key_update(trans, ec_stripe_key_update(trans,
bkey_i_to_stripe(&s->new_stripe.key), s->have_existing_stripe
!s->have_existing_stripe)); ? bkey_i_to_stripe(&s->existing_stripe.key)
: NULL,
bkey_i_to_stripe(&s->new_stripe.key)));
bch_err_msg(c, ret, "creating stripe key"); bch_err_msg(c, ret, "creating stripe key");
if (ret) { if (ret) {
goto err; goto err;
@ -1874,7 +1900,15 @@ static int new_stripe_alloc_buckets(struct btree_trans *trans,
bitmap_and(devs.d, devs.d, c->rw_devs[BCH_DATA_user].d, BCH_SB_MEMBERS_MAX); 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, s->blocks_gotten, v->nr_blocks) {
__clear_bit(v->ptrs[i].dev, devs.d); /*
* Note: we don't yet repair invalid blocks (failed/removed
* devices) when reusing stripes - we still need a codepath to
* walk backpointers and update all extents that point to that
* block when updating the stripe
*/
if (v->ptrs[i].dev != BCH_SB_MEMBER_INVALID)
__clear_bit(v->ptrs[i].dev, devs.d);
if (i < s->nr_data) if (i < s->nr_data)
nr_have_data++; nr_have_data++;
else else

View File

@ -251,7 +251,10 @@ int __bch2_fsck_err(struct bch_fs *c,
* delete the key) * delete the key)
* - and we don't need to warn if we're not prompting * - and we don't need to warn if we're not prompting
*/ */
WARN_ON(!(flags & FSCK_AUTOFIX) && !trans && bch2_current_has_btree_trans(c)); WARN_ON((flags & FSCK_CAN_FIX) &&
!(flags & FSCK_AUTOFIX) &&
!trans &&
bch2_current_has_btree_trans(c));
if ((flags & FSCK_CAN_FIX) && if ((flags & FSCK_CAN_FIX) &&
test_bit(err, c->sb.errors_silent)) test_bit(err, c->sb.errors_silent))

View File

@ -203,7 +203,8 @@ int bch2_btree_ptr_v2_validate(struct bch_fs *c, struct bkey_s_c k,
c, btree_ptr_v2_min_key_bad, c, btree_ptr_v2_min_key_bad,
"min_key > key"); "min_key > key");
if (flags & BCH_VALIDATE_write) if ((flags & BCH_VALIDATE_write) &&
c->sb.version_min >= bcachefs_metadata_version_btree_ptr_sectors_written)
bkey_fsck_err_on(!bp.v->sectors_written, bkey_fsck_err_on(!bp.v->sectors_written,
c, btree_ptr_v2_written_0, c, btree_ptr_v2_written_0,
"sectors_written == 0"); "sectors_written == 0");

View File

@ -695,6 +695,16 @@ void bch2_bkey_ptrs_to_text(struct printbuf *, struct bch_fs *,
int bch2_bkey_ptrs_validate(struct bch_fs *, struct bkey_s_c, int bch2_bkey_ptrs_validate(struct bch_fs *, struct bkey_s_c,
enum bch_validate_flags); enum bch_validate_flags);
static inline bool bch2_extent_ptr_eq(struct bch_extent_ptr ptr1,
struct bch_extent_ptr ptr2)
{
return (ptr1.cached == ptr2.cached &&
ptr1.unwritten == ptr2.unwritten &&
ptr1.offset == ptr2.offset &&
ptr1.dev == ptr2.dev &&
ptr1.dev == ptr2.dev);
}
void bch2_ptr_swab(struct bkey_s); void bch2_ptr_swab(struct bkey_s);
const struct bch_extent_rebalance *bch2_bkey_rebalance_opts(struct bkey_s_c); const struct bch_extent_rebalance *bch2_bkey_rebalance_opts(struct bkey_s_c);

View File

@ -248,6 +248,7 @@ void bch2_readahead(struct readahead_control *ractl)
struct bch_io_opts opts; struct bch_io_opts opts;
struct folio *folio; struct folio *folio;
struct readpages_iter readpages_iter; struct readpages_iter readpages_iter;
struct blk_plug plug;
bch2_inode_opts_get(&opts, c, &inode->ei_inode); bch2_inode_opts_get(&opts, c, &inode->ei_inode);
@ -255,6 +256,16 @@ void bch2_readahead(struct readahead_control *ractl)
if (ret) if (ret)
return; return;
/*
* Besides being a general performance optimization, plugging helps with
* avoiding btree transaction srcu warnings - submitting a bio can
* block, and we don't want todo that with the transaction locked.
*
* However, plugged bios are submitted when we schedule; we ideally
* would have our own scheduler hook to call unlock_long() before
* scheduling.
*/
blk_start_plug(&plug);
bch2_pagecache_add_get(inode); bch2_pagecache_add_get(inode);
struct btree_trans *trans = bch2_trans_get(c); struct btree_trans *trans = bch2_trans_get(c);
@ -281,7 +292,7 @@ void bch2_readahead(struct readahead_control *ractl)
bch2_trans_put(trans); bch2_trans_put(trans);
bch2_pagecache_add_put(inode); bch2_pagecache_add_put(inode);
blk_finish_plug(&plug);
darray_exit(&readpages_iter.folios); darray_exit(&readpages_iter.folios);
} }
@ -296,9 +307,13 @@ int bch2_read_single_folio(struct folio *folio, struct address_space *mapping)
struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch_fs *c = inode->v.i_sb->s_fs_info;
struct bch_read_bio *rbio; struct bch_read_bio *rbio;
struct bch_io_opts opts; struct bch_io_opts opts;
struct blk_plug plug;
int ret; int ret;
DECLARE_COMPLETION_ONSTACK(done); DECLARE_COMPLETION_ONSTACK(done);
BUG_ON(folio_test_uptodate(folio));
BUG_ON(folio_test_dirty(folio));
if (!bch2_folio_create(folio, GFP_KERNEL)) if (!bch2_folio_create(folio, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
@ -313,7 +328,9 @@ int bch2_read_single_folio(struct folio *folio, struct address_space *mapping)
rbio->bio.bi_iter.bi_sector = folio_sector(folio); rbio->bio.bi_iter.bi_sector = folio_sector(folio);
BUG_ON(!bio_add_folio(&rbio->bio, folio, folio_size(folio), 0)); BUG_ON(!bio_add_folio(&rbio->bio, folio, folio_size(folio), 0));
blk_start_plug(&plug);
bch2_trans_run(c, (bchfs_read(trans, rbio, inode_inum(inode), NULL), 0)); bch2_trans_run(c, (bchfs_read(trans, rbio, inode_inum(inode), NULL), 0));
blk_finish_plug(&plug);
wait_for_completion(&done); wait_for_completion(&done);
ret = blk_status_to_errno(rbio->bio.bi_status); ret = blk_status_to_errno(rbio->bio.bi_status);
@ -856,6 +873,12 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
folios_trunc(&fs, fi); folios_trunc(&fs, fi);
end = min(end, folio_end_pos(darray_last(fs))); end = min(end, folio_end_pos(darray_last(fs)));
} else { } else {
if (!folio_test_uptodate(f)) {
ret = bch2_read_single_folio(f, mapping);
if (ret)
goto out;
}
folios_trunc(&fs, fi + 1); folios_trunc(&fs, fi + 1);
end = f_pos + f_reserved; end = f_pos + f_reserved;
} }

View File

@ -70,6 +70,7 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
struct bch_io_opts opts; struct bch_io_opts opts;
struct dio_read *dio; struct dio_read *dio;
struct bio *bio; struct bio *bio;
struct blk_plug plug;
loff_t offset = req->ki_pos; loff_t offset = req->ki_pos;
bool sync = is_sync_kiocb(req); bool sync = is_sync_kiocb(req);
size_t shorten; size_t shorten;
@ -128,6 +129,8 @@ static int bch2_direct_IO_read(struct kiocb *req, struct iov_iter *iter)
*/ */
dio->should_dirty = iter_is_iovec(iter); dio->should_dirty = iter_is_iovec(iter);
blk_start_plug(&plug);
goto start; goto start;
while (iter->count) { while (iter->count) {
bio = bio_alloc_bioset(NULL, bio = bio_alloc_bioset(NULL,
@ -160,6 +163,8 @@ start:
bch2_read(c, rbio_init(bio, opts), inode_inum(inode)); bch2_read(c, rbio_init(bio, opts), inode_inum(inode));
} }
blk_finish_plug(&plug);
iter->count += shorten; iter->count += shorten;
if (sync) { if (sync) {
@ -369,6 +374,7 @@ static noinline void bch2_dio_write_flush(struct dio_write *dio)
static __always_inline long bch2_dio_write_done(struct dio_write *dio) static __always_inline long bch2_dio_write_done(struct dio_write *dio)
{ {
struct bch_fs *c = dio->op.c;
struct kiocb *req = dio->req; struct kiocb *req = dio->req;
struct bch_inode_info *inode = dio->inode; struct bch_inode_info *inode = dio->inode;
bool sync = dio->sync; bool sync = dio->sync;
@ -387,7 +393,7 @@ static __always_inline long bch2_dio_write_done(struct dio_write *dio)
ret = dio->op.error ?: ((long) dio->written << 9); ret = dio->op.error ?: ((long) dio->written << 9);
bio_put(&dio->op.wbio.bio); bio_put(&dio->op.wbio.bio);
bch2_write_ref_put(dio->op.c, BCH_WRITE_REF_dio_write); bch2_write_ref_put(c, BCH_WRITE_REF_dio_write);
/* inode->i_dio_count is our ref on inode and thus bch_fs */ /* inode->i_dio_count is our ref on inode and thus bch_fs */
inode_dio_end(&inode->v); inode_dio_end(&inode->v);

View File

@ -399,14 +399,17 @@ void bch2_folio_reservation_put(struct bch_fs *c,
bch2_quota_reservation_put(c, inode, &res->quota); bch2_quota_reservation_put(c, inode, &res->quota);
} }
int bch2_folio_reservation_get(struct bch_fs *c, static int __bch2_folio_reservation_get(struct bch_fs *c,
struct bch_inode_info *inode, struct bch_inode_info *inode,
struct folio *folio, struct folio *folio,
struct bch2_folio_reservation *res, struct bch2_folio_reservation *res,
size_t offset, size_t len) size_t offset, size_t len,
bool partial)
{ {
struct bch_folio *s = bch2_folio_create(folio, 0); struct bch_folio *s = bch2_folio_create(folio, 0);
unsigned i, disk_sectors = 0, quota_sectors = 0; unsigned i, disk_sectors = 0, quota_sectors = 0;
struct disk_reservation disk_res = {};
size_t reserved = len;
int ret; int ret;
if (!s) if (!s)
@ -422,23 +425,56 @@ int bch2_folio_reservation_get(struct bch_fs *c,
} }
if (disk_sectors) { if (disk_sectors) {
ret = bch2_disk_reservation_add(c, &res->disk, disk_sectors, 0); ret = bch2_disk_reservation_add(c, &disk_res, disk_sectors,
partial ? BCH_DISK_RESERVATION_PARTIAL : 0);
if (unlikely(ret)) if (unlikely(ret))
return ret; return ret;
if (unlikely(disk_res.sectors != disk_sectors)) {
disk_sectors = quota_sectors = 0;
for (i = round_down(offset, block_bytes(c)) >> 9;
i < round_up(offset + len, block_bytes(c)) >> 9;
i++) {
disk_sectors += sectors_to_reserve(&s->s[i], res->disk.nr_replicas);
if (disk_sectors > disk_res.sectors) {
/*
* Make sure to get a reservation that's
* aligned to the filesystem blocksize:
*/
unsigned reserved_offset = round_down(i << 9, block_bytes(c));
reserved = clamp(reserved_offset, offset, offset + len) - offset;
if (!reserved) {
bch2_disk_reservation_put(c, &disk_res);
return -BCH_ERR_ENOSPC_disk_reservation;
}
break;
}
quota_sectors += s->s[i].state == SECTOR_unallocated;
}
}
} }
if (quota_sectors) { if (quota_sectors) {
ret = bch2_quota_reservation_add(c, inode, &res->quota, quota_sectors, true); ret = bch2_quota_reservation_add(c, inode, &res->quota, quota_sectors, true);
if (unlikely(ret)) { if (unlikely(ret)) {
struct disk_reservation tmp = { .sectors = disk_sectors }; bch2_disk_reservation_put(c, &disk_res);
bch2_disk_reservation_put(c, &tmp);
res->disk.sectors -= disk_sectors;
return ret; return ret;
} }
} }
return 0; res->disk.sectors += disk_res.sectors;
return partial ? reserved : 0;
}
int bch2_folio_reservation_get(struct bch_fs *c,
struct bch_inode_info *inode,
struct folio *folio,
struct bch2_folio_reservation *res,
size_t offset, size_t len)
{
return __bch2_folio_reservation_get(c, inode, folio, res, offset, len, false);
} }
ssize_t bch2_folio_reservation_get_partial(struct bch_fs *c, ssize_t bch2_folio_reservation_get_partial(struct bch_fs *c,
@ -447,23 +483,7 @@ ssize_t bch2_folio_reservation_get_partial(struct bch_fs *c,
struct bch2_folio_reservation *res, struct bch2_folio_reservation *res,
size_t offset, size_t len) size_t offset, size_t len)
{ {
size_t l, reserved = 0; return __bch2_folio_reservation_get(c, inode, folio, res, offset, len, true);
int ret;
while ((l = len - reserved)) {
while ((ret = bch2_folio_reservation_get(c, inode, folio, res, offset, l))) {
if ((offset & (block_bytes(c) - 1)) + l <= block_bytes(c))
return reserved ?: ret;
len = reserved + l;
l /= 2;
}
offset += l;
reserved += l;
}
return reserved;
} }
static void bch2_clear_folio_bits(struct folio *folio) static void bch2_clear_folio_bits(struct folio *folio)

View File

@ -182,7 +182,7 @@ static int bch2_flush_inode(struct bch_fs *c,
struct bch_inode_unpacked u; struct bch_inode_unpacked u;
int ret = bch2_inode_find_by_inum(c, inode_inum(inode), &u) ?: int ret = bch2_inode_find_by_inum(c, inode_inum(inode), &u) ?:
bch2_journal_flush_seq(&c->journal, u.bi_journal_seq) ?: bch2_journal_flush_seq(&c->journal, u.bi_journal_seq, TASK_INTERRUPTIBLE) ?:
bch2_inode_flush_nocow_writes(c, inode); bch2_inode_flush_nocow_writes(c, inode);
bch2_write_ref_put(c, BCH_WRITE_REF_fsync); bch2_write_ref_put(c, BCH_WRITE_REF_fsync);
return ret; return ret;

View File

@ -658,7 +658,7 @@ static struct dentry *bch2_lookup(struct inode *vdir, struct dentry *dentry,
struct bch_hash_info hash = bch2_hash_info_init(c, &dir->ei_inode); struct bch_hash_info hash = bch2_hash_info_init(c, &dir->ei_inode);
struct bch_inode_info *inode; struct bch_inode_info *inode;
bch2_trans_do(c, NULL, NULL, 0, bch2_trans_do(c,
PTR_ERR_OR_ZERO(inode = bch2_lookup_trans(trans, inode_inum(dir), PTR_ERR_OR_ZERO(inode = bch2_lookup_trans(trans, inode_inum(dir),
&hash, &dentry->d_name))); &hash, &dentry->d_name)));
if (IS_ERR(inode)) if (IS_ERR(inode))
@ -871,7 +871,7 @@ static int bch2_rename2(struct mnt_idmap *idmap,
ret = bch2_subvol_is_ro_trans(trans, src_dir->ei_inum.subvol) ?: ret = bch2_subvol_is_ro_trans(trans, src_dir->ei_inum.subvol) ?:
bch2_subvol_is_ro_trans(trans, dst_dir->ei_inum.subvol); bch2_subvol_is_ro_trans(trans, dst_dir->ei_inum.subvol);
if (ret) if (ret)
goto err; goto err_tx_restart;
if (inode_attr_changing(dst_dir, src_inode, Inode_opt_project)) { if (inode_attr_changing(dst_dir, src_inode, Inode_opt_project)) {
ret = bch2_fs_quota_transfer(c, src_inode, ret = bch2_fs_quota_transfer(c, src_inode,
@ -1268,7 +1268,7 @@ static int bch2_fiemap(struct inode *vinode, struct fiemap_extent_info *info,
bch2_trans_iter_init(trans, &iter, BTREE_ID_extents, bch2_trans_iter_init(trans, &iter, BTREE_ID_extents,
POS(ei->v.i_ino, start), 0); POS(ei->v.i_ino, start), 0);
while (true) { while (!ret || bch2_err_matches(ret, BCH_ERR_transaction_restart)) {
enum btree_id data_btree = BTREE_ID_extents; enum btree_id data_btree = BTREE_ID_extents;
bch2_trans_begin(trans); bch2_trans_begin(trans);
@ -1276,14 +1276,14 @@ static int bch2_fiemap(struct inode *vinode, struct fiemap_extent_info *info,
u32 snapshot; u32 snapshot;
ret = bch2_subvolume_get_snapshot(trans, ei->ei_inum.subvol, &snapshot); ret = bch2_subvolume_get_snapshot(trans, ei->ei_inum.subvol, &snapshot);
if (ret) if (ret)
goto err; continue;
bch2_btree_iter_set_snapshot(&iter, snapshot); bch2_btree_iter_set_snapshot(&iter, snapshot);
k = bch2_btree_iter_peek_upto(&iter, end); k = bch2_btree_iter_peek_upto(&iter, end);
ret = bkey_err(k); ret = bkey_err(k);
if (ret) if (ret)
goto err; continue;
if (!k.k) if (!k.k)
break; break;
@ -1303,7 +1303,7 @@ static int bch2_fiemap(struct inode *vinode, struct fiemap_extent_info *info,
ret = bch2_read_indirect_extent(trans, &data_btree, ret = bch2_read_indirect_extent(trans, &data_btree,
&offset_into_extent, &cur); &offset_into_extent, &cur);
if (ret) if (ret)
break; continue;
k = bkey_i_to_s_c(cur.k); k = bkey_i_to_s_c(cur.k);
bch2_bkey_buf_realloc(&prev, c, k.k->u64s); bch2_bkey_buf_realloc(&prev, c, k.k->u64s);
@ -1331,10 +1331,6 @@ static int bch2_fiemap(struct inode *vinode, struct fiemap_extent_info *info,
bch2_btree_iter_set_pos(&iter, bch2_btree_iter_set_pos(&iter,
POS(iter.pos.inode, iter.pos.offset + sectors)); POS(iter.pos.inode, iter.pos.offset + sectors));
err:
if (ret &&
!bch2_err_matches(ret, BCH_ERR_transaction_restart))
break;
} }
bch2_trans_iter_exit(trans, &iter); bch2_trans_iter_exit(trans, &iter);

View File

@ -934,35 +934,138 @@ static int get_visible_inodes(struct btree_trans *trans,
return ret; return ret;
} }
static int hash_redo_key(struct btree_trans *trans, static int dirent_has_target(struct btree_trans *trans, struct bkey_s_c_dirent d)
const struct bch_hash_desc desc,
struct bch_hash_info *hash_info,
struct btree_iter *k_iter, struct bkey_s_c k)
{ {
struct bkey_i *delete; if (d.v->d_type == DT_SUBVOL) {
struct bkey_i *tmp; u32 snap;
u64 inum;
int ret = subvol_lookup(trans, le32_to_cpu(d.v->d_child_subvol), &snap, &inum);
if (ret && !bch2_err_matches(ret, ENOENT))
return ret;
return !ret;
} else {
struct btree_iter iter;
struct bkey_s_c k = bch2_bkey_get_iter(trans, &iter, BTREE_ID_inodes,
SPOS(0, le64_to_cpu(d.v->d_inum), d.k->p.snapshot), 0);
int ret = bkey_err(k);
if (ret)
return ret;
delete = bch2_trans_kmalloc(trans, sizeof(*delete)); ret = bkey_is_inode(k.k);
if (IS_ERR(delete)) bch2_trans_iter_exit(trans, &iter);
return PTR_ERR(delete); return ret;
}
}
tmp = bch2_bkey_make_mut_noupdate(trans, k); /*
if (IS_ERR(tmp)) * Prefer to delete the first one, since that will be the one at the wrong
return PTR_ERR(tmp); * offset:
* return value: 0 -> delete k1, 1 -> delete k2
*/
static int hash_pick_winner(struct btree_trans *trans,
const struct bch_hash_desc desc,
struct bch_hash_info *hash_info,
struct bkey_s_c k1,
struct bkey_s_c k2)
{
if (bkey_val_bytes(k1.k) == bkey_val_bytes(k2.k) &&
!memcmp(k1.v, k2.v, bkey_val_bytes(k1.k)))
return 0;
bkey_init(&delete->k); switch (desc.btree_id) {
delete->k.p = k_iter->pos; case BTREE_ID_dirents: {
return bch2_btree_iter_traverse(k_iter) ?: int ret = dirent_has_target(trans, bkey_s_c_to_dirent(k1));
bch2_trans_update(trans, k_iter, delete, 0) ?: if (ret < 0)
bch2_hash_set_in_snapshot(trans, desc, hash_info, return ret;
(subvol_inum) { 0, k.k->p.inode }, if (!ret)
k.k->p.snapshot, tmp, return 0;
STR_HASH_must_create|
BTREE_UPDATE_internal_snapshot_node) ?: ret = dirent_has_target(trans, bkey_s_c_to_dirent(k2));
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc); if (ret < 0)
return ret;
if (!ret)
return 1;
return 2;
}
default:
return 0;
}
}
static int fsck_update_backpointers(struct btree_trans *trans,
struct snapshots_seen *s,
const struct bch_hash_desc desc,
struct bch_hash_info *hash_info,
struct bkey_i *new)
{
if (new->k.type != KEY_TYPE_dirent)
return 0;
struct bkey_i_dirent *d = bkey_i_to_dirent(new);
struct inode_walker target = inode_walker_init();
int ret = 0;
if (d->v.d_type == DT_SUBVOL) {
BUG();
} else {
ret = get_visible_inodes(trans, &target, s, le64_to_cpu(d->v.d_inum));
if (ret)
goto err;
darray_for_each(target.inodes, i) {
i->inode.bi_dir_offset = d->k.p.offset;
ret = __bch2_fsck_write_inode(trans, &i->inode);
if (ret)
goto err;
}
}
err:
inode_walker_exit(&target);
return ret;
}
static int fsck_rename_dirent(struct btree_trans *trans,
struct snapshots_seen *s,
const struct bch_hash_desc desc,
struct bch_hash_info *hash_info,
struct bkey_s_c_dirent old)
{
struct qstr old_name = bch2_dirent_get_name(old);
struct bkey_i_dirent *new = bch2_trans_kmalloc(trans, bkey_bytes(old.k) + 32);
int ret = PTR_ERR_OR_ZERO(new);
if (ret)
return ret;
bkey_dirent_init(&new->k_i);
dirent_copy_target(new, old);
new->k.p = old.k->p;
for (unsigned i = 0; i < 1000; i++) {
unsigned len = sprintf(new->v.d_name, "%.*s.fsck_renamed-%u",
old_name.len, old_name.name, i);
unsigned u64s = BKEY_U64s + dirent_val_u64s(len);
if (u64s > U8_MAX)
return -EINVAL;
new->k.u64s = u64s;
ret = bch2_hash_set_in_snapshot(trans, bch2_dirent_hash_desc, hash_info,
(subvol_inum) { 0, old.k->p.inode },
old.k->p.snapshot, &new->k_i,
BTREE_UPDATE_internal_snapshot_node);
if (!bch2_err_matches(ret, EEXIST))
break;
}
if (ret)
return ret;
return fsck_update_backpointers(trans, s, desc, hash_info, &new->k_i);
} }
static int hash_check_key(struct btree_trans *trans, static int hash_check_key(struct btree_trans *trans,
struct snapshots_seen *s,
const struct bch_hash_desc desc, const struct bch_hash_desc desc,
struct bch_hash_info *hash_info, struct bch_hash_info *hash_info,
struct btree_iter *k_iter, struct bkey_s_c hash_k) struct btree_iter *k_iter, struct bkey_s_c hash_k)
@ -991,16 +1094,9 @@ static int hash_check_key(struct btree_trans *trans,
if (bkey_eq(k.k->p, hash_k.k->p)) if (bkey_eq(k.k->p, hash_k.k->p))
break; break;
if (fsck_err_on(k.k->type == desc.key_type && if (k.k->type == desc.key_type &&
!desc.cmp_bkey(k, hash_k), !desc.cmp_bkey(k, hash_k))
trans, hash_table_key_duplicate, goto duplicate_entries;
"duplicate hash table keys:\n%s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, hash_k),
buf.buf))) {
ret = bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0) ?: 1;
break;
}
if (bkey_deleted(k.k)) { if (bkey_deleted(k.k)) {
bch2_trans_iter_exit(trans, &iter); bch2_trans_iter_exit(trans, &iter);
@ -1013,18 +1109,66 @@ out:
return ret; return ret;
bad_hash: bad_hash:
if (fsck_err(trans, hash_table_key_wrong_offset, if (fsck_err(trans, hash_table_key_wrong_offset,
"hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n%s", "hash table key at wrong offset: btree %s inode %llu offset %llu, hashed to %llu\n %s",
bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash, bch2_btree_id_str(desc.btree_id), hash_k.k->p.inode, hash_k.k->p.offset, hash,
(printbuf_reset(&buf), (printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) { bch2_bkey_val_to_text(&buf, c, hash_k), buf.buf))) {
ret = hash_redo_key(trans, desc, hash_info, k_iter, hash_k); struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, hash_k);
bch_err_fn(c, ret); if (IS_ERR(new))
return PTR_ERR(new);
k = bch2_hash_set_or_get_in_snapshot(trans, &iter, desc, hash_info,
(subvol_inum) { 0, hash_k.k->p.inode },
hash_k.k->p.snapshot, new,
STR_HASH_must_create|
BTREE_ITER_with_updates|
BTREE_UPDATE_internal_snapshot_node);
ret = bkey_err(k);
if (ret) if (ret)
return ret; goto out;
ret = -BCH_ERR_transaction_restart_nested; if (k.k)
goto duplicate_entries;
ret = bch2_hash_delete_at(trans, desc, hash_info, k_iter,
BTREE_UPDATE_internal_snapshot_node) ?:
fsck_update_backpointers(trans, s, desc, hash_info, new) ?:
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
-BCH_ERR_transaction_restart_nested;
goto out;
} }
fsck_err: fsck_err:
goto out; goto out;
duplicate_entries:
ret = hash_pick_winner(trans, desc, hash_info, hash_k, k);
if (ret < 0)
goto out;
if (!fsck_err(trans, hash_table_key_duplicate,
"duplicate hash table keys%s:\n%s",
ret != 2 ? "" : ", both point to valid inodes",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, hash_k),
prt_newline(&buf),
bch2_bkey_val_to_text(&buf, c, k),
buf.buf)))
goto out;
switch (ret) {
case 0:
ret = bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0);
break;
case 1:
ret = bch2_hash_delete_at(trans, desc, hash_info, &iter, 0);
break;
case 2:
ret = fsck_rename_dirent(trans, s, desc, hash_info, bkey_s_c_to_dirent(hash_k)) ?:
bch2_hash_delete_at(trans, desc, hash_info, k_iter, 0);
goto out;
}
ret = bch2_trans_commit(trans, NULL, NULL, 0) ?:
-BCH_ERR_transaction_restart_nested;
goto out;
} }
static struct bkey_s_c_dirent inode_get_dirent(struct btree_trans *trans, static struct bkey_s_c_dirent inode_get_dirent(struct btree_trans *trans,
@ -1094,10 +1238,36 @@ fsck_err:
return ret; return ret;
} }
static int get_snapshot_root_inode(struct btree_trans *trans,
struct bch_inode_unpacked *root,
u64 inum)
{
struct btree_iter iter;
struct bkey_s_c k;
int ret = 0;
for_each_btree_key_reverse_norestart(trans, iter, BTREE_ID_inodes,
SPOS(0, inum, U32_MAX),
BTREE_ITER_all_snapshots, k, ret) {
if (k.k->p.offset != inum)
break;
if (bkey_is_inode(k.k))
goto found_root;
}
if (ret)
goto err;
BUG();
found_root:
BUG_ON(bch2_inode_unpack(k, root));
err:
bch2_trans_iter_exit(trans, &iter);
return ret;
}
static int check_inode(struct btree_trans *trans, static int check_inode(struct btree_trans *trans,
struct btree_iter *iter, struct btree_iter *iter,
struct bkey_s_c k, struct bkey_s_c k,
struct bch_inode_unpacked *prev, struct bch_inode_unpacked *snapshot_root,
struct snapshots_seen *s) struct snapshots_seen *s)
{ {
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
@ -1121,16 +1291,19 @@ static int check_inode(struct btree_trans *trans,
BUG_ON(bch2_inode_unpack(k, &u)); BUG_ON(bch2_inode_unpack(k, &u));
if (prev->bi_inum != u.bi_inum) if (snapshot_root->bi_inum != u.bi_inum) {
*prev = u; ret = get_snapshot_root_inode(trans, snapshot_root, u.bi_inum);
if (ret)
goto err;
}
if (fsck_err_on(prev->bi_hash_seed != u.bi_hash_seed || if (fsck_err_on(u.bi_hash_seed != snapshot_root->bi_hash_seed ||
inode_d_type(prev) != inode_d_type(&u), INODE_STR_HASH(&u) != INODE_STR_HASH(snapshot_root),
trans, inode_snapshot_mismatch, trans, inode_snapshot_mismatch,
"inodes in different snapshots don't match")) { "inodes in different snapshots don't match")) {
bch_err(c, "repair not implemented yet"); u.bi_hash_seed = snapshot_root->bi_hash_seed;
ret = -BCH_ERR_fsck_repair_unimplemented; SET_INODE_STR_HASH(&u, INODE_STR_HASH(snapshot_root));
goto err_noprint; do_update = true;
} }
if (u.bi_dir || u.bi_dir_offset) { if (u.bi_dir || u.bi_dir_offset) {
@ -1283,7 +1456,7 @@ err_noprint:
int bch2_check_inodes(struct bch_fs *c) int bch2_check_inodes(struct bch_fs *c)
{ {
struct bch_inode_unpacked prev = { 0 }; struct bch_inode_unpacked snapshot_root = {};
struct snapshots_seen s; struct snapshots_seen s;
snapshots_seen_init(&s); snapshots_seen_init(&s);
@ -1293,7 +1466,7 @@ int bch2_check_inodes(struct bch_fs *c)
POS_MIN, POS_MIN,
BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k, BTREE_ITER_prefetch|BTREE_ITER_all_snapshots, k,
NULL, NULL, BCH_TRANS_COMMIT_no_enospc, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
check_inode(trans, &iter, k, &prev, &s))); check_inode(trans, &iter, k, &snapshot_root, &s)));
snapshots_seen_exit(&s); snapshots_seen_exit(&s);
bch_err_fn(c, ret); bch_err_fn(c, ret);
@ -2305,7 +2478,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
*hash_info = bch2_hash_info_init(c, &i->inode); *hash_info = bch2_hash_info_init(c, &i->inode);
dir->first_this_inode = false; dir->first_this_inode = false;
ret = hash_check_key(trans, bch2_dirent_hash_desc, hash_info, iter, k); ret = hash_check_key(trans, s, bch2_dirent_hash_desc, hash_info, iter, k);
if (ret < 0) if (ret < 0)
goto err; goto err;
if (ret) { if (ret) {
@ -2419,7 +2592,7 @@ static int check_xattr(struct btree_trans *trans, struct btree_iter *iter,
*hash_info = bch2_hash_info_init(c, &i->inode); *hash_info = bch2_hash_info_init(c, &i->inode);
inode->first_this_inode = false; inode->first_this_inode = false;
ret = hash_check_key(trans, bch2_xattr_hash_desc, hash_info, iter, k); ret = hash_check_key(trans, NULL, bch2_xattr_hash_desc, hash_info, iter, k);
bch_err_fn(c, ret); bch_err_fn(c, ret);
return ret; return ret;
} }
@ -2507,7 +2680,7 @@ fsck_err:
/* Get root directory, create if it doesn't exist: */ /* Get root directory, create if it doesn't exist: */
int bch2_check_root(struct bch_fs *c) int bch2_check_root(struct bch_fs *c)
{ {
int ret = bch2_trans_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc, int ret = bch2_trans_commit_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
check_root_trans(trans)); check_root_trans(trans));
bch_err_fn(c, ret); bch_err_fn(c, ret);
return ret; return ret;

View File

@ -163,8 +163,8 @@ static noinline int bch2_inode_unpack_v1(struct bkey_s_c_inode inode,
unsigned fieldnr = 0, field_bits; unsigned fieldnr = 0, field_bits;
int ret; int ret;
#define x(_name, _bits) \ #define x(_name, _bits) \
if (fieldnr++ == INODE_NR_FIELDS(inode.v)) { \ if (fieldnr++ == INODEv1_NR_FIELDS(inode.v)) { \
unsigned offset = offsetof(struct bch_inode_unpacked, _name);\ unsigned offset = offsetof(struct bch_inode_unpacked, _name);\
memset((void *) unpacked + offset, 0, \ memset((void *) unpacked + offset, 0, \
sizeof(*unpacked) - offset); \ sizeof(*unpacked) - offset); \
@ -283,6 +283,8 @@ static noinline int bch2_inode_unpack_slowpath(struct bkey_s_c k,
{ {
memset(unpacked, 0, sizeof(*unpacked)); memset(unpacked, 0, sizeof(*unpacked));
unpacked->bi_snapshot = k.k->p.snapshot;
switch (k.k->type) { switch (k.k->type) {
case KEY_TYPE_inode: { case KEY_TYPE_inode: {
struct bkey_s_c_inode inode = bkey_s_c_to_inode(k); struct bkey_s_c_inode inode = bkey_s_c_to_inode(k);
@ -293,10 +295,10 @@ static noinline int bch2_inode_unpack_slowpath(struct bkey_s_c k,
unpacked->bi_flags = le32_to_cpu(inode.v->bi_flags); unpacked->bi_flags = le32_to_cpu(inode.v->bi_flags);
unpacked->bi_mode = le16_to_cpu(inode.v->bi_mode); unpacked->bi_mode = le16_to_cpu(inode.v->bi_mode);
if (INODE_NEW_VARINT(inode.v)) { if (INODEv1_NEW_VARINT(inode.v)) {
return bch2_inode_unpack_v2(unpacked, inode.v->fields, return bch2_inode_unpack_v2(unpacked, inode.v->fields,
bkey_val_end(inode), bkey_val_end(inode),
INODE_NR_FIELDS(inode.v)); INODEv1_NR_FIELDS(inode.v));
} else { } else {
return bch2_inode_unpack_v1(inode, unpacked); return bch2_inode_unpack_v1(inode, unpacked);
} }
@ -471,10 +473,10 @@ int bch2_inode_validate(struct bch_fs *c, struct bkey_s_c k,
struct bkey_s_c_inode inode = bkey_s_c_to_inode(k); struct bkey_s_c_inode inode = bkey_s_c_to_inode(k);
int ret = 0; int ret = 0;
bkey_fsck_err_on(INODE_STR_HASH(inode.v) >= BCH_STR_HASH_NR, bkey_fsck_err_on(INODEv1_STR_HASH(inode.v) >= BCH_STR_HASH_NR,
c, inode_str_hash_invalid, c, inode_str_hash_invalid,
"invalid str hash type (%llu >= %u)", "invalid str hash type (%llu >= %u)",
INODE_STR_HASH(inode.v), BCH_STR_HASH_NR); INODEv1_STR_HASH(inode.v), BCH_STR_HASH_NR);
ret = __bch2_inode_validate(c, k, flags); ret = __bch2_inode_validate(c, k, flags);
fsck_err: fsck_err:
@ -534,6 +536,9 @@ static void __bch2_inode_unpacked_to_text(struct printbuf *out,
prt_printf(out, "journal_seq=%llu\n", inode->bi_journal_seq); prt_printf(out, "journal_seq=%llu\n", inode->bi_journal_seq);
prt_printf(out, "hash_seed=%llx\n", inode->bi_hash_seed); prt_printf(out, "hash_seed=%llx\n", inode->bi_hash_seed);
prt_printf(out, "hash_type=");
bch2_prt_str_hash_type(out, INODE_STR_HASH(inode));
prt_newline(out);
prt_printf(out, "bi_size=%llu\n", inode->bi_size); prt_printf(out, "bi_size=%llu\n", inode->bi_size);
prt_printf(out, "bi_sectors=%llu\n", inode->bi_sectors); prt_printf(out, "bi_sectors=%llu\n", inode->bi_sectors);
prt_printf(out, "bi_version=%llu\n", inode->bi_version); prt_printf(out, "bi_version=%llu\n", inode->bi_version);
@ -801,10 +806,8 @@ void bch2_inode_init_early(struct bch_fs *c,
memset(inode_u, 0, sizeof(*inode_u)); memset(inode_u, 0, sizeof(*inode_u));
/* ick */ SET_INODE_STR_HASH(inode_u, str_hash);
inode_u->bi_flags |= str_hash << INODE_STR_HASH_OFFSET; get_random_bytes(&inode_u->bi_hash_seed, sizeof(inode_u->bi_hash_seed));
get_random_bytes(&inode_u->bi_hash_seed,
sizeof(inode_u->bi_hash_seed));
} }
void bch2_inode_init_late(struct bch_inode_unpacked *inode_u, u64 now, void bch2_inode_init_late(struct bch_inode_unpacked *inode_u, u64 now,
@ -1088,8 +1091,7 @@ int bch2_inode_find_by_inum_trans(struct btree_trans *trans,
int bch2_inode_find_by_inum(struct bch_fs *c, subvol_inum inum, int bch2_inode_find_by_inum(struct bch_fs *c, subvol_inum inum,
struct bch_inode_unpacked *inode) struct bch_inode_unpacked *inode)
{ {
return bch2_trans_do(c, NULL, NULL, 0, return bch2_trans_do(c, bch2_inode_find_by_inum_trans(trans, inum, inode));
bch2_inode_find_by_inum_trans(trans, inum, inode));
} }
int bch2_inode_nlink_inc(struct bch_inode_unpacked *bi) int bch2_inode_nlink_inc(struct bch_inode_unpacked *bi)

View File

@ -92,6 +92,7 @@ struct bch_inode_unpacked {
BCH_INODE_FIELDS_v3() BCH_INODE_FIELDS_v3()
#undef x #undef x
}; };
BITMASK(INODE_STR_HASH, struct bch_inode_unpacked, bi_flags, 20, 24);
struct bkey_inode_buf { struct bkey_inode_buf {
struct bkey_i_inode_v3 inode; struct bkey_i_inode_v3 inode;

View File

@ -150,9 +150,9 @@ enum __bch_inode_flags {
#undef x #undef x
}; };
LE32_BITMASK(INODE_STR_HASH, struct bch_inode, bi_flags, 20, 24); LE32_BITMASK(INODEv1_STR_HASH, struct bch_inode, bi_flags, 20, 24);
LE32_BITMASK(INODE_NR_FIELDS, struct bch_inode, bi_flags, 24, 31); LE32_BITMASK(INODEv1_NR_FIELDS, struct bch_inode, bi_flags, 24, 31);
LE32_BITMASK(INODE_NEW_VARINT, struct bch_inode, bi_flags, 31, 32); LE32_BITMASK(INODEv1_NEW_VARINT,struct bch_inode, bi_flags, 31, 32);
LE64_BITMASK(INODEv2_STR_HASH, struct bch_inode_v2, bi_flags, 20, 24); LE64_BITMASK(INODEv2_STR_HASH, struct bch_inode_v2, bi_flags, 20, 24);
LE64_BITMASK(INODEv2_NR_FIELDS, struct bch_inode_v2, bi_flags, 24, 31); LE64_BITMASK(INODEv2_NR_FIELDS, struct bch_inode_v2, bi_flags, 24, 31);

View File

@ -377,7 +377,7 @@ static int __bch2_resume_logged_op_finsert(struct btree_trans *trans,
* check for missing subvolume before fpunch, as in resume we don't want * check for missing subvolume before fpunch, as in resume we don't want
* it to be a fatal error * it to be a fatal error
*/ */
ret = __bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot, warn_errors); ret = lockrestart_do(trans, __bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot, warn_errors));
if (ret) if (ret)
return ret; return ret;

View File

@ -409,8 +409,8 @@ retry:
bch2_trans_begin(trans); bch2_trans_begin(trans);
rbio->bio.bi_status = 0; rbio->bio.bi_status = 0;
k = bch2_btree_iter_peek_slot(&iter); ret = lockrestart_do(trans, bkey_err(k = bch2_btree_iter_peek_slot(&iter)));
if (bkey_err(k)) if (ret)
goto err; goto err;
bch2_bkey_buf_reassemble(&sk, c, k); bch2_bkey_buf_reassemble(&sk, c, k);
@ -557,8 +557,8 @@ out:
static noinline void bch2_rbio_narrow_crcs(struct bch_read_bio *rbio) static noinline void bch2_rbio_narrow_crcs(struct bch_read_bio *rbio)
{ {
bch2_trans_do(rbio->c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc, bch2_trans_commit_do(rbio->c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
__bch2_rbio_narrow_crcs(trans, rbio)); __bch2_rbio_narrow_crcs(trans, rbio));
} }
/* Inner part that may run in process context */ /* Inner part that may run in process context */

View File

@ -1437,7 +1437,7 @@ again:
* freeing up space on specific disks, which means that * freeing up space on specific disks, which means that
* allocations for specific disks may hang arbitrarily long: * allocations for specific disks may hang arbitrarily long:
*/ */
ret = bch2_trans_do(c, NULL, NULL, 0, ret = bch2_trans_run(c, lockrestart_do(trans,
bch2_alloc_sectors_start_trans(trans, bch2_alloc_sectors_start_trans(trans,
op->target, op->target,
op->opts.erasure_code && !(op->flags & BCH_WRITE_CACHED), op->opts.erasure_code && !(op->flags & BCH_WRITE_CACHED),
@ -1447,7 +1447,7 @@ again:
op->nr_replicas_required, op->nr_replicas_required,
op->watermark, op->watermark,
op->flags, op->flags,
&op->cl, &wp)); &op->cl, &wp)));
if (unlikely(ret)) { if (unlikely(ret)) {
if (bch2_err_matches(ret, BCH_ERR_operation_blocked)) if (bch2_err_matches(ret, BCH_ERR_operation_blocked))
break; break;

View File

@ -758,7 +758,7 @@ out:
return ret; return ret;
} }
int bch2_journal_flush_seq(struct journal *j, u64 seq) int bch2_journal_flush_seq(struct journal *j, u64 seq, unsigned task_state)
{ {
u64 start_time = local_clock(); u64 start_time = local_clock();
int ret, ret2; int ret, ret2;
@ -769,7 +769,9 @@ int bch2_journal_flush_seq(struct journal *j, u64 seq)
if (seq <= j->flushed_seq_ondisk) if (seq <= j->flushed_seq_ondisk)
return 0; return 0;
ret = wait_event_interruptible(j->wait, (ret2 = bch2_journal_flush_seq_async(j, seq, NULL))); ret = wait_event_state(j->wait,
(ret2 = bch2_journal_flush_seq_async(j, seq, NULL)),
task_state);
if (!ret) if (!ret)
bch2_time_stats_update(j->flush_seq_time, start_time); bch2_time_stats_update(j->flush_seq_time, start_time);
@ -788,7 +790,7 @@ void bch2_journal_flush_async(struct journal *j, struct closure *parent)
int bch2_journal_flush(struct journal *j) int bch2_journal_flush(struct journal *j)
{ {
return bch2_journal_flush_seq(j, atomic64_read(&j->seq)); return bch2_journal_flush_seq(j, atomic64_read(&j->seq), TASK_UNINTERRUPTIBLE);
} }
/* /*
@ -846,7 +848,7 @@ static int __bch2_journal_meta(struct journal *j)
bch2_journal_res_put(j, &res); bch2_journal_res_put(j, &res);
return bch2_journal_flush_seq(j, res.seq); return bch2_journal_flush_seq(j, res.seq, TASK_UNINTERRUPTIBLE);
} }
int bch2_journal_meta(struct journal *j) int bch2_journal_meta(struct journal *j)

View File

@ -401,7 +401,7 @@ void bch2_journal_entry_res_resize(struct journal *,
int bch2_journal_flush_seq_async(struct journal *, u64, struct closure *); int bch2_journal_flush_seq_async(struct journal *, u64, struct closure *);
void bch2_journal_flush_async(struct journal *, struct closure *); void bch2_journal_flush_async(struct journal *, struct closure *);
int bch2_journal_flush_seq(struct journal *, u64); int bch2_journal_flush_seq(struct journal *, u64, unsigned);
int bch2_journal_flush(struct journal *); int bch2_journal_flush(struct journal *);
bool bch2_journal_noflush_seq(struct journal *, u64); bool bch2_journal_noflush_seq(struct journal *, u64);
int bch2_journal_meta(struct journal *); int bch2_journal_meta(struct journal *);

View File

@ -63,7 +63,7 @@ const char * const bch2_compression_opts[] = {
NULL NULL
}; };
const char * const bch2_str_hash_types[] = { const char * const __bch2_str_hash_types[] = {
BCH_STR_HASH_TYPES() BCH_STR_HASH_TYPES()
NULL NULL
}; };
@ -115,6 +115,7 @@ PRT_STR_OPT_BOUNDSCHECKED(fs_usage_type, enum bch_fs_usage_type);
PRT_STR_OPT_BOUNDSCHECKED(data_type, enum bch_data_type); PRT_STR_OPT_BOUNDSCHECKED(data_type, enum bch_data_type);
PRT_STR_OPT_BOUNDSCHECKED(csum_type, enum bch_csum_type); PRT_STR_OPT_BOUNDSCHECKED(csum_type, enum bch_csum_type);
PRT_STR_OPT_BOUNDSCHECKED(compression_type, enum bch_compression_type); PRT_STR_OPT_BOUNDSCHECKED(compression_type, enum bch_compression_type);
PRT_STR_OPT_BOUNDSCHECKED(str_hash_type, enum bch_str_hash_type);
static int bch2_opt_fix_errors_parse(struct bch_fs *c, const char *val, u64 *res, static int bch2_opt_fix_errors_parse(struct bch_fs *c, const char *val, u64 *res,
struct printbuf *err) struct printbuf *err)

View File

@ -18,7 +18,7 @@ extern const char * const bch2_sb_compat[];
extern const char * const __bch2_btree_ids[]; extern const char * const __bch2_btree_ids[];
extern const char * const bch2_csum_opts[]; extern const char * const bch2_csum_opts[];
extern const char * const bch2_compression_opts[]; extern const char * const bch2_compression_opts[];
extern const char * const bch2_str_hash_types[]; extern const char * const __bch2_str_hash_types[];
extern const char * const bch2_str_hash_opts[]; extern const char * const bch2_str_hash_opts[];
extern const char * const __bch2_data_types[]; extern const char * const __bch2_data_types[];
extern const char * const bch2_member_states[]; extern const char * const bch2_member_states[];
@ -29,6 +29,7 @@ void bch2_prt_fs_usage_type(struct printbuf *, enum bch_fs_usage_type);
void bch2_prt_data_type(struct printbuf *, enum bch_data_type); void bch2_prt_data_type(struct printbuf *, enum bch_data_type);
void bch2_prt_csum_type(struct printbuf *, enum bch_csum_type); void bch2_prt_csum_type(struct printbuf *, enum bch_csum_type);
void bch2_prt_compression_type(struct printbuf *, enum bch_compression_type); void bch2_prt_compression_type(struct printbuf *, enum bch_compression_type);
void bch2_prt_str_hash_type(struct printbuf *, enum bch_str_hash_type);
static inline const char *bch2_d_type_str(unsigned d_type) static inline const char *bch2_d_type_str(unsigned d_type)
{ {

View File

@ -869,7 +869,7 @@ static int bch2_set_quota(struct super_block *sb, struct kqid qid,
bkey_quota_init(&new_quota.k_i); bkey_quota_init(&new_quota.k_i);
new_quota.k.p = POS(qid.type, from_kqid(&init_user_ns, qid)); new_quota.k.p = POS(qid.type, from_kqid(&init_user_ns, qid));
ret = bch2_trans_do(c, NULL, NULL, 0, ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_set_quota_trans(trans, &new_quota, qdq)) ?: bch2_set_quota_trans(trans, &new_quota, qdq)) ?:
__bch2_quota_set(c, bkey_i_to_s_c(&new_quota.k_i), qdq); __bch2_quota_set(c, bkey_i_to_s_c(&new_quota.k_i), qdq);

View File

@ -70,7 +70,9 @@ err:
int bch2_set_rebalance_needs_scan(struct bch_fs *c, u64 inum) int bch2_set_rebalance_needs_scan(struct bch_fs *c, u64 inum)
{ {
int ret = bch2_trans_do(c, NULL, NULL, BCH_TRANS_COMMIT_no_enospc|BCH_TRANS_COMMIT_lazy_rw, int ret = bch2_trans_commit_do(c, NULL, NULL,
BCH_TRANS_COMMIT_no_enospc|
BCH_TRANS_COMMIT_lazy_rw,
__bch2_set_rebalance_needs_scan(trans, inum)); __bch2_set_rebalance_needs_scan(trans, inum));
rebalance_wakeup(c); rebalance_wakeup(c);
return ret; return ret;

View File

@ -321,7 +321,8 @@ int bch2_journal_replay(struct bch_fs *c)
BCH_TRANS_COMMIT_no_enospc| BCH_TRANS_COMMIT_no_enospc|
BCH_TRANS_COMMIT_journal_reclaim| BCH_TRANS_COMMIT_journal_reclaim|
BCH_TRANS_COMMIT_skip_accounting_apply| BCH_TRANS_COMMIT_skip_accounting_apply|
BCH_TRANS_COMMIT_no_journal_res, BCH_TRANS_COMMIT_no_journal_res|
BCH_WATERMARK_reclaim,
bch2_journal_replay_accounting_key(trans, k)); bch2_journal_replay_accounting_key(trans, k));
if (bch2_fs_fatal_err_on(ret, c, "error replaying accounting; %s", bch2_err_str(ret))) if (bch2_fs_fatal_err_on(ret, c, "error replaying accounting; %s", bch2_err_str(ret)))
goto err; goto err;
@ -1118,7 +1119,7 @@ int bch2_fs_initialize(struct bch_fs *c)
bch2_inode_init_early(c, &lostfound_inode); bch2_inode_init_early(c, &lostfound_inode);
ret = bch2_trans_do(c, NULL, NULL, 0, ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_create_trans(trans, bch2_create_trans(trans,
BCACHEFS_ROOT_SUBVOL_INUM, BCACHEFS_ROOT_SUBVOL_INUM,
&root_inode, &lostfound_inode, &root_inode, &lostfound_inode,

View File

@ -180,6 +180,7 @@ enum bch_fsck_flags {
x(reflink_p_to_missing_reflink_v, 166, 0) \ x(reflink_p_to_missing_reflink_v, 166, 0) \
x(stripe_pos_bad, 167, 0) \ x(stripe_pos_bad, 167, 0) \
x(stripe_val_size_bad, 168, 0) \ x(stripe_val_size_bad, 168, 0) \
x(stripe_csum_granularity_bad, 290, 0) \
x(stripe_sector_count_wrong, 169, 0) \ x(stripe_sector_count_wrong, 169, 0) \
x(snapshot_tree_pos_bad, 170, 0) \ x(snapshot_tree_pos_bad, 170, 0) \
x(snapshot_tree_to_missing_snapshot, 171, 0) \ x(snapshot_tree_to_missing_snapshot, 171, 0) \
@ -266,8 +267,8 @@ enum bch_fsck_flags {
x(journal_entry_dup_same_device, 246, 0) \ x(journal_entry_dup_same_device, 246, 0) \
x(inode_bi_subvol_missing, 247, 0) \ x(inode_bi_subvol_missing, 247, 0) \
x(inode_bi_subvol_wrong, 248, 0) \ x(inode_bi_subvol_wrong, 248, 0) \
x(inode_points_to_missing_dirent, 249, 0) \ x(inode_points_to_missing_dirent, 249, FSCK_AUTOFIX) \
x(inode_points_to_wrong_dirent, 250, 0) \ x(inode_points_to_wrong_dirent, 250, FSCK_AUTOFIX) \
x(inode_bi_parent_nonzero, 251, 0) \ x(inode_bi_parent_nonzero, 251, 0) \
x(dirent_to_missing_parent_subvol, 252, 0) \ x(dirent_to_missing_parent_subvol, 252, 0) \
x(dirent_not_visible_in_parent_subvol, 253, 0) \ x(dirent_not_visible_in_parent_subvol, 253, 0) \
@ -301,7 +302,7 @@ enum bch_fsck_flags {
x(accounting_key_replicas_devs_unsorted, 280, FSCK_AUTOFIX) \ x(accounting_key_replicas_devs_unsorted, 280, FSCK_AUTOFIX) \
x(accounting_key_version_0, 282, FSCK_AUTOFIX) \ x(accounting_key_version_0, 282, FSCK_AUTOFIX) \
x(logged_op_but_clean, 283, FSCK_AUTOFIX) \ x(logged_op_but_clean, 283, FSCK_AUTOFIX) \
x(MAX, 290, 0) x(MAX, 291, 0)
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,

View File

@ -163,6 +163,11 @@ static int validate_member(struct printbuf *err,
return -BCH_ERR_invalid_sb_members; return -BCH_ERR_invalid_sb_members;
} }
if (m.btree_bitmap_shift >= 64) {
prt_printf(err, "device %u: invalid btree_bitmap_shift %u", i, m.btree_bitmap_shift);
return -BCH_ERR_invalid_sb_members;
}
return 0; return 0;
} }

View File

@ -46,8 +46,7 @@ bch2_hash_info_init(struct bch_fs *c, const struct bch_inode_unpacked *bi)
{ {
/* XXX ick */ /* XXX ick */
struct bch_hash_info info = { struct bch_hash_info info = {
.type = (bi->bi_flags >> INODE_STR_HASH_OFFSET) & .type = INODE_STR_HASH(bi),
~(~0U << INODE_STR_HASH_BITS),
.siphash_key = { .k0 = bi->bi_hash_seed } .siphash_key = { .k0 = bi->bi_hash_seed }
}; };
@ -253,19 +252,20 @@ int bch2_hash_needs_whiteout(struct btree_trans *trans,
} }
static __always_inline static __always_inline
int bch2_hash_set_in_snapshot(struct btree_trans *trans, struct bkey_s_c bch2_hash_set_or_get_in_snapshot(struct btree_trans *trans,
struct btree_iter *iter,
const struct bch_hash_desc desc, const struct bch_hash_desc desc,
const struct bch_hash_info *info, const struct bch_hash_info *info,
subvol_inum inum, u32 snapshot, subvol_inum inum, u32 snapshot,
struct bkey_i *insert, struct bkey_i *insert,
enum btree_iter_update_trigger_flags flags) enum btree_iter_update_trigger_flags flags)
{ {
struct btree_iter iter, slot = { NULL }; struct btree_iter slot = {};
struct bkey_s_c k; struct bkey_s_c k;
bool found = false; bool found = false;
int ret; int ret;
for_each_btree_key_upto_norestart(trans, iter, desc.btree_id, for_each_btree_key_upto_norestart(trans, *iter, desc.btree_id,
SPOS(insert->k.p.inode, SPOS(insert->k.p.inode,
desc.hash_bkey(info, bkey_i_to_s_c(insert)), desc.hash_bkey(info, bkey_i_to_s_c(insert)),
snapshot), snapshot),
@ -280,7 +280,7 @@ int bch2_hash_set_in_snapshot(struct btree_trans *trans,
} }
if (!slot.path && !(flags & STR_HASH_must_replace)) if (!slot.path && !(flags & STR_HASH_must_replace))
bch2_trans_copy_iter(&slot, &iter); bch2_trans_copy_iter(&slot, iter);
if (k.k->type != KEY_TYPE_hash_whiteout) if (k.k->type != KEY_TYPE_hash_whiteout)
goto not_found; goto not_found;
@ -290,28 +290,49 @@ int bch2_hash_set_in_snapshot(struct btree_trans *trans,
ret = -BCH_ERR_ENOSPC_str_hash_create; ret = -BCH_ERR_ENOSPC_str_hash_create;
out: out:
bch2_trans_iter_exit(trans, &slot); bch2_trans_iter_exit(trans, &slot);
bch2_trans_iter_exit(trans, &iter); bch2_trans_iter_exit(trans, iter);
return ret ? bkey_s_c_err(ret) : bkey_s_c_null;
return ret;
found: found:
found = true; found = true;
not_found: not_found:
if (found && (flags & STR_HASH_must_create)) {
if (!found && (flags & STR_HASH_must_replace)) { bch2_trans_iter_exit(trans, &slot);
return k;
} else if (!found && (flags & STR_HASH_must_replace)) {
ret = -BCH_ERR_ENOENT_str_hash_set_must_replace; ret = -BCH_ERR_ENOENT_str_hash_set_must_replace;
} else if (found && (flags & STR_HASH_must_create)) {
ret = -BCH_ERR_EEXIST_str_hash_set;
} else { } else {
if (!found && slot.path) if (!found && slot.path)
swap(iter, slot); swap(*iter, slot);
insert->k.p = iter.pos; insert->k.p = iter->pos;
ret = bch2_trans_update(trans, &iter, insert, flags); ret = bch2_trans_update(trans, iter, insert, flags);
} }
goto out; goto out;
} }
static __always_inline
int bch2_hash_set_in_snapshot(struct btree_trans *trans,
const struct bch_hash_desc desc,
const struct bch_hash_info *info,
subvol_inum inum, u32 snapshot,
struct bkey_i *insert,
enum btree_iter_update_trigger_flags flags)
{
struct btree_iter iter;
struct bkey_s_c k = bch2_hash_set_or_get_in_snapshot(trans, &iter, desc, info, inum,
snapshot, insert, flags);
int ret = bkey_err(k);
if (ret)
return ret;
if (k.k) {
bch2_trans_iter_exit(trans, &iter);
return -BCH_ERR_EEXIST_str_hash_set;
}
return 0;
}
static __always_inline static __always_inline
int bch2_hash_set(struct btree_trans *trans, int bch2_hash_set(struct btree_trans *trans,
const struct bch_hash_desc desc, const struct bch_hash_desc desc,
@ -363,8 +384,11 @@ int bch2_hash_delete(struct btree_trans *trans,
struct btree_iter iter; struct btree_iter iter;
struct bkey_s_c k = bch2_hash_lookup(trans, &iter, desc, info, inum, key, struct bkey_s_c k = bch2_hash_lookup(trans, &iter, desc, info, inum, key,
BTREE_ITER_intent); BTREE_ITER_intent);
int ret = bkey_err(k) ?: int ret = bkey_err(k);
bch2_hash_delete_at(trans, desc, info, &iter, 0); if (ret)
return ret;
ret = bch2_hash_delete_at(trans, desc, info, &iter, 0);
bch2_trans_iter_exit(trans, &iter); bch2_trans_iter_exit(trans, &iter);
return ret; return ret;
} }

View File

@ -319,8 +319,7 @@ int bch2_subvol_is_ro_trans(struct btree_trans *trans, u32 subvol)
int bch2_subvol_is_ro(struct bch_fs *c, u32 subvol) int bch2_subvol_is_ro(struct bch_fs *c, u32 subvol)
{ {
return bch2_trans_do(c, NULL, NULL, 0, return bch2_trans_do(c, bch2_subvol_is_ro_trans(trans, subvol));
bch2_subvol_is_ro_trans(trans, subvol));
} }
int bch2_snapshot_get_subvol(struct btree_trans *trans, u32 snapshot, int bch2_snapshot_get_subvol(struct btree_trans *trans, u32 snapshot,
@ -676,8 +675,8 @@ err:
/* set bi_subvol on root inode */ /* set bi_subvol on root inode */
int bch2_fs_upgrade_for_subvolumes(struct bch_fs *c) int bch2_fs_upgrade_for_subvolumes(struct bch_fs *c)
{ {
int ret = bch2_trans_do(c, NULL, NULL, BCH_TRANS_COMMIT_lazy_rw, int ret = bch2_trans_commit_do(c, NULL, NULL, BCH_TRANS_COMMIT_lazy_rw,
__bch2_fs_upgrade_for_subvolumes(trans)); __bch2_fs_upgrade_for_subvolumes(trans));
bch_err_fn(c, ret); bch_err_fn(c, ret);
return ret; return ret;
} }

View File

@ -184,6 +184,7 @@ static DEFINE_MUTEX(bch_fs_list_lock);
DECLARE_WAIT_QUEUE_HEAD(bch2_read_only_wait); DECLARE_WAIT_QUEUE_HEAD(bch2_read_only_wait);
static void bch2_dev_unlink(struct bch_dev *);
static void bch2_dev_free(struct bch_dev *); static void bch2_dev_free(struct bch_dev *);
static int bch2_dev_alloc(struct bch_fs *, unsigned); static int bch2_dev_alloc(struct bch_fs *, unsigned);
static int bch2_dev_sysfs_online(struct bch_fs *, struct bch_dev *); static int bch2_dev_sysfs_online(struct bch_fs *, struct bch_dev *);
@ -620,9 +621,7 @@ void __bch2_fs_stop(struct bch_fs *c)
up_write(&c->state_lock); up_write(&c->state_lock);
for_each_member_device(c, ca) for_each_member_device(c, ca)
if (ca->kobj.state_in_sysfs && bch2_dev_unlink(ca);
ca->disk_sb.bdev)
sysfs_remove_link(bdev_kobj(ca->disk_sb.bdev), "bcachefs");
if (c->kobj.state_in_sysfs) if (c->kobj.state_in_sysfs)
kobject_del(&c->kobj); kobject_del(&c->kobj);
@ -1188,9 +1187,7 @@ static void bch2_dev_free(struct bch_dev *ca)
{ {
cancel_work_sync(&ca->io_error_work); cancel_work_sync(&ca->io_error_work);
if (ca->kobj.state_in_sysfs && bch2_dev_unlink(ca);
ca->disk_sb.bdev)
sysfs_remove_link(bdev_kobj(ca->disk_sb.bdev), "bcachefs");
if (ca->kobj.state_in_sysfs) if (ca->kobj.state_in_sysfs)
kobject_del(&ca->kobj); kobject_del(&ca->kobj);
@ -1227,10 +1224,7 @@ static void __bch2_dev_offline(struct bch_fs *c, struct bch_dev *ca)
percpu_ref_kill(&ca->io_ref); percpu_ref_kill(&ca->io_ref);
wait_for_completion(&ca->io_ref_completion); wait_for_completion(&ca->io_ref_completion);
if (ca->kobj.state_in_sysfs) { bch2_dev_unlink(ca);
sysfs_remove_link(bdev_kobj(ca->disk_sb.bdev), "bcachefs");
sysfs_remove_link(&ca->kobj, "block");
}
bch2_free_super(&ca->disk_sb); bch2_free_super(&ca->disk_sb);
bch2_dev_journal_exit(ca); bch2_dev_journal_exit(ca);
@ -1252,6 +1246,26 @@ static void bch2_dev_io_ref_complete(struct percpu_ref *ref)
complete(&ca->io_ref_completion); complete(&ca->io_ref_completion);
} }
static void bch2_dev_unlink(struct bch_dev *ca)
{
struct kobject *b;
/*
* This is racy w.r.t. the underlying block device being hot-removed,
* which removes it from sysfs.
*
* It'd be lovely if we had a way to handle this race, but the sysfs
* code doesn't appear to provide a good method and block/holder.c is
* susceptible as well:
*/
if (ca->kobj.state_in_sysfs &&
ca->disk_sb.bdev &&
(b = bdev_kobj(ca->disk_sb.bdev))->state_in_sysfs) {
sysfs_remove_link(b, "bcachefs");
sysfs_remove_link(&ca->kobj, "block");
}
}
static int bch2_dev_sysfs_online(struct bch_fs *c, struct bch_dev *ca) static int bch2_dev_sysfs_online(struct bch_fs *c, struct bch_dev *ca)
{ {
int ret; int ret;
@ -1959,7 +1973,7 @@ int bch2_dev_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
}; };
u64 v[3] = { nbuckets - old_nbuckets, 0, 0 }; u64 v[3] = { nbuckets - old_nbuckets, 0, 0 };
ret = bch2_trans_do(ca->fs, NULL, NULL, 0, ret = bch2_trans_commit_do(ca->fs, NULL, NULL, 0,
bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), false)) ?: bch2_disk_accounting_mod(trans, &acc, v, ARRAY_SIZE(v), false)) ?:
bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets); bch2_dev_freespace_init(c, ca, old_nbuckets, nbuckets);
if (ret) if (ret)

View File

@ -450,7 +450,7 @@ static int insert_test_overlapping_extent(struct bch_fs *c, u64 inum, u64 start,
k.k_i.k.p.snapshot = snapid; k.k_i.k.p.snapshot = snapid;
k.k_i.k.size = len; k.k_i.k.size = len;
ret = bch2_trans_do(c, NULL, NULL, 0, ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_btree_insert_nonextent(trans, BTREE_ID_extents, &k.k_i, bch2_btree_insert_nonextent(trans, BTREE_ID_extents, &k.k_i,
BTREE_UPDATE_internal_snapshot_node)); BTREE_UPDATE_internal_snapshot_node));
bch_err_fn(c, ret); bch_err_fn(c, ret);
@ -510,7 +510,7 @@ static int test_snapshots(struct bch_fs *c, u64 nr)
if (ret) if (ret)
return ret; return ret;
ret = bch2_trans_do(c, NULL, NULL, 0, ret = bch2_trans_commit_do(c, NULL, NULL, 0,
bch2_snapshot_node_create(trans, U32_MAX, bch2_snapshot_node_create(trans, U32_MAX,
snapids, snapids,
snapid_subvols, snapid_subvols,

View File

@ -330,7 +330,7 @@ static int bch2_xattr_get_handler(const struct xattr_handler *handler,
{ {
struct bch_inode_info *inode = to_bch_ei(vinode); struct bch_inode_info *inode = to_bch_ei(vinode);
struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch_fs *c = inode->v.i_sb->s_fs_info;
int ret = bch2_trans_do(c, NULL, NULL, 0, int ret = bch2_trans_do(c,
bch2_xattr_get_trans(trans, inode, name, buffer, size, handler->flags)); bch2_xattr_get_trans(trans, inode, name, buffer, size, handler->flags));
if (ret < 0 && bch2_err_matches(ret, ENOENT)) if (ret < 0 && bch2_err_matches(ret, ENOENT))