Update bcachefs sources to a8115093df bcachefs: Fix divide by zero in rebalance_work()

This commit is contained in:
Kent Overstreet 2023-08-17 17:08:33 -04:00
parent 4d04fe4262
commit 505c326cbe
43 changed files with 1485 additions and 1252 deletions

View File

@ -1 +1 @@
b0788c47d97935856809bd1357423978dbfcdf9f a8115093df3e08c3e4bf6dab222e1cac132fff69

View File

@ -199,10 +199,6 @@ update-bcachefs-sources:
git add include/linux/xxhash.h git add include/linux/xxhash.h
cp $(LINUX_DIR)/lib/xxhash.c linux/ cp $(LINUX_DIR)/lib/xxhash.c linux/
git add linux/xxhash.c git add linux/xxhash.c
cp $(LINUX_DIR)/kernel/locking/six.c linux/
git add linux/six.c
cp $(LINUX_DIR)/include/linux/six.h include/linux/
git add include/linux/six.h
cp $(LINUX_DIR)/include/linux/list_nulls.h include/linux/ cp $(LINUX_DIR)/include/linux/list_nulls.h include/linux/
git add include/linux/list_nulls.h git add include/linux/list_nulls.h
cp $(LINUX_DIR)/include/linux/poison.h include/linux/ cp $(LINUX_DIR)/include/linux/poison.h include/linux/

View File

@ -107,7 +107,12 @@ extern __thread struct task_struct *current;
#define set_current_state(state_value) \ #define set_current_state(state_value) \
smp_store_mb(current->state, (state_value)) smp_store_mb(current->state, (state_value))
#define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0) static inline struct task_struct *get_task_struct(struct task_struct *task)
{
atomic_inc(&task->usage);
return task;
}
extern void __put_task_struct(struct task_struct *t); extern void __put_task_struct(struct task_struct *t);

View File

@ -989,7 +989,6 @@ retry_blocking:
cl = _cl; cl = _cl;
goto retry_blocking; goto retry_blocking;
} }
} }
return ret; return ret;
@ -1031,6 +1030,16 @@ static int open_bucket_add_buckets(struct btree_trans *trans,
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
} }
/**
* should_drop_bucket - check if this is open_bucket should go away
* @ca: if set, we're killing buckets for a particular device
* @ec: if true, we're shutting down erasure coding and killing all ec
* open_buckets
* otherwise, return true
*
* We're killing open_buckets because we're shutting down a device, erasure
* coding, or the entire filesystem - check if this open_bucket matches:
*/
static bool should_drop_bucket(struct open_bucket *ob, struct bch_fs *c, static bool should_drop_bucket(struct open_bucket *ob, struct bch_fs *c,
struct bch_dev *ca, bool ec) struct bch_dev *ca, bool ec)
{ {
@ -1516,25 +1525,47 @@ static const char * const bch2_write_point_states[] = {
NULL NULL
}; };
static void bch2_write_point_to_text(struct printbuf *out, struct bch_fs *c,
struct write_point *wp)
{
struct open_bucket *ob;
unsigned i;
prt_printf(out, "%lu: ", wp->write_point);
prt_human_readable_u64(out, wp->sectors_allocated);
prt_printf(out, " last wrote: ");
bch2_pr_time_units(out, sched_clock() - wp->last_used);
for (i = 0; i < WRITE_POINT_STATE_NR; i++) {
prt_printf(out, " %s: ", bch2_write_point_states[i]);
bch2_pr_time_units(out, wp->time[i]);
}
prt_newline(out);
printbuf_indent_add(out, 2);
open_bucket_for_each(c, &wp->ptrs, ob, i)
bch2_open_bucket_to_text(out, c, ob);
printbuf_indent_sub(out, 2);
}
void bch2_write_points_to_text(struct printbuf *out, struct bch_fs *c) void bch2_write_points_to_text(struct printbuf *out, struct bch_fs *c)
{ {
struct write_point *wp; struct write_point *wp;
unsigned i;
prt_str(out, "Foreground write points\n");
for (wp = c->write_points; for (wp = c->write_points;
wp < c->write_points + ARRAY_SIZE(c->write_points); wp < c->write_points + ARRAY_SIZE(c->write_points);
wp++) { wp++)
prt_printf(out, "%lu: ", wp->write_point); bch2_write_point_to_text(out, c, wp);
prt_human_readable_u64(out, wp->sectors_allocated);
prt_printf(out, " last wrote: "); prt_str(out, "Copygc write point\n");
bch2_pr_time_units(out, sched_clock() - wp->last_used); bch2_write_point_to_text(out, c, &c->copygc_write_point);
for (i = 0; i < WRITE_POINT_STATE_NR; i++) { prt_str(out, "Rebalance write point\n");
prt_printf(out, " %s: ", bch2_write_point_states[i]); bch2_write_point_to_text(out, c, &c->rebalance_write_point);
bch2_pr_time_units(out, wp->time[i]);
}
prt_newline(out); prt_str(out, "Btree write point\n");
} bch2_write_point_to_text(out, c, &c->btree_write_point);
} }

View File

@ -916,9 +916,7 @@ struct bch_dirent {
#define DT_SUBVOL 16 #define DT_SUBVOL 16
#define BCH_DT_MAX 17 #define BCH_DT_MAX 17
#define BCH_NAME_MAX ((unsigned) (U8_MAX * sizeof(__u64) - \ #define BCH_NAME_MAX 512
sizeof(struct bkey) - \
offsetof(struct bch_dirent, d_name)))
/* Xattrs */ /* Xattrs */
@ -1126,6 +1124,11 @@ struct bch_subvolume {
__le32 flags; __le32 flags;
__le32 snapshot; __le32 snapshot;
__le64 inode; __le64 inode;
/*
* Snapshot subvolumes form a tree, separate from the snapshot nodes
* tree - if this subvolume is a snapshot, this is the ID of the
* subvolume it was created from:
*/
__le32 parent; __le32 parent;
__le32 pad; __le32 pad;
bch_le128 otime; bch_le128 otime;

View File

@ -591,8 +591,10 @@ struct bkey_format bch2_bkey_format_done(struct bkey_format_state *s)
/* allow for extent merging: */ /* allow for extent merging: */
if (ret.bits_per_field[BKEY_FIELD_SIZE]) { if (ret.bits_per_field[BKEY_FIELD_SIZE]) {
ret.bits_per_field[BKEY_FIELD_SIZE] += 4; unsigned b = min(4U, 32U - ret.bits_per_field[BKEY_FIELD_SIZE]);
bits += 4;
ret.bits_per_field[BKEY_FIELD_SIZE] += b;
bits += b;
} }
ret.key_u64s = DIV_ROUND_UP(bits, 64); ret.key_u64s = DIV_ROUND_UP(bits, 64);

View File

@ -13,6 +13,7 @@
#include "lru.h" #include "lru.h"
#include "quota.h" #include "quota.h"
#include "reflink.h" #include "reflink.h"
#include "snapshot.h"
#include "subvolume.h" #include "subvolume.h"
#include "xattr.h" #include "xattr.h"

View File

@ -14,7 +14,7 @@
#include "extents.h" #include "extents.h"
#include "journal.h" #include "journal.h"
#include "replicas.h" #include "replicas.h"
#include "subvolume.h" #include "snapshot.h"
#include "trace.h" #include "trace.h"
#include <linux/random.h> #include <linux/random.h>
@ -2898,12 +2898,14 @@ static void bch2_trans_alloc_paths(struct btree_trans *trans, struct bch_fs *c)
#ifdef __KERNEL__ #ifdef __KERNEL__
p = this_cpu_xchg(c->btree_paths_bufs->path, NULL); p = this_cpu_xchg(c->btree_paths_bufs->path, NULL);
#endif #endif
if (!p) if (!p) {
p = mempool_alloc(&trans->c->btree_paths_pool, GFP_NOFS); p = mempool_alloc(&trans->c->btree_paths_pool, GFP_NOFS);
/* /*
* paths need to be zeroed, bch2_check_for_deadlock looks at paths in * paths need to be zeroed, bch2_check_for_deadlock looks at
* other threads * paths in other threads
*/ */
memset(p, 0, paths_bytes);
}
trans->paths = p; p += paths_bytes; trans->paths = p; p += paths_bytes;
trans->updates = p; p += updates_bytes; trans->updates = p; p += updates_bytes;

View File

@ -10,9 +10,8 @@
* updating the iterator state * updating the iterator state
*/ */
#include <linux/six.h>
#include "btree_iter.h" #include "btree_iter.h"
#include "six.h"
void bch2_btree_lock_init(struct btree_bkey_cached_common *, enum six_lock_init_flags); void bch2_btree_lock_init(struct btree_bkey_cached_common *, enum six_lock_init_flags);

View File

@ -14,7 +14,7 @@
#include "journal.h" #include "journal.h"
#include "journal_reclaim.h" #include "journal_reclaim.h"
#include "replicas.h" #include "replicas.h"
#include "subvolume.h" #include "snapshot.h"
#include <linux/prefetch.h> #include <linux/prefetch.h>

View File

@ -4,7 +4,6 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/rhashtable.h> #include <linux/rhashtable.h>
#include <linux/six.h>
//#include "bkey_methods.h" //#include "bkey_methods.h"
#include "buckets_types.h" #include "buckets_types.h"
@ -12,6 +11,7 @@
#include "errcode.h" #include "errcode.h"
#include "journal_types.h" #include "journal_types.h"
#include "replicas_types.h" #include "replicas_types.h"
#include "six.h"
struct open_bucket; struct open_bucket;
struct btree_update; struct btree_update;

View File

@ -11,7 +11,7 @@
#include "error.h" #include "error.h"
#include "extents.h" #include "extents.h"
#include "keylist.h" #include "keylist.h"
#include "subvolume.h" #include "snapshot.h"
#include "trace.h" #include "trace.h"
static inline int btree_insert_entry_cmp(const struct btree_insert_entry *l, static inline int btree_insert_entry_cmp(const struct btree_insert_entry *l,

View File

@ -2385,7 +2385,7 @@ void bch2_btree_updates_to_text(struct printbuf *out, struct bch_fs *c)
as, as,
as->mode, as->mode,
as->nodes_written, as->nodes_written,
atomic_read(&as->cl.remaining) & CLOSURE_REMAINING_MASK, closure_nr_remaining(&as->cl),
as->journal.seq); as->journal.seq);
mutex_unlock(&c->btree_interior_update_lock); mutex_unlock(&c->btree_interior_update_lock);
} }

View File

@ -415,7 +415,7 @@ void bch2_update_unwritten_extent(struct btree_trans *trans,
break; break;
} }
if ((atomic_read(&cl.remaining) & CLOSURE_REMAINING_MASK) != 1) { if (closure_nr_remaining(&cl) != 1) {
bch2_trans_unlock(trans); bch2_trans_unlock(trans);
closure_sync(&cl); closure_sync(&cl);
} }

View File

@ -13,12 +13,25 @@
#include <linux/dcache.h> #include <linux/dcache.h>
unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d) static unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d)
{ {
unsigned len = bkey_val_bytes(d.k) - unsigned bkey_u64s = bkey_val_u64s(d.k);
offsetof(struct bch_dirent, d_name); unsigned bkey_bytes = bkey_u64s * sizeof(u64);
u64 last_u64 = ((u64*)d.v)[bkey_u64s - 1];
#if CPU_BIG_ENDIAN
unsigned trailing_nuls = last_u64 ? __builtin_ctzll(last_u64) / 8 : 64 / 8;
#else
unsigned trailing_nuls = last_u64 ? __builtin_clzll(last_u64) / 8 : 64 / 8;
#endif
return strnlen(d.v->d_name, len); return bkey_bytes -
offsetof(struct bch_dirent, d_name) -
trailing_nuls;
}
struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent d)
{
return (struct qstr) QSTR_INIT(d.v->d_name, bch2_dirent_name_bytes(d));
} }
static u64 bch2_dirent_hash(const struct bch_hash_info *info, static u64 bch2_dirent_hash(const struct bch_hash_info *info,
@ -41,7 +54,7 @@ static u64 dirent_hash_key(const struct bch_hash_info *info, const void *key)
static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k) static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
{ {
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
struct qstr name = QSTR_INIT(d.v->d_name, bch2_dirent_name_bytes(d)); struct qstr name = bch2_dirent_get_name(d);
return bch2_dirent_hash(info, &name); return bch2_dirent_hash(info, &name);
} }
@ -49,20 +62,20 @@ static u64 dirent_hash_bkey(const struct bch_hash_info *info, struct bkey_s_c k)
static bool dirent_cmp_key(struct bkey_s_c _l, const void *_r) static bool dirent_cmp_key(struct bkey_s_c _l, const void *_r)
{ {
struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l); struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
int len = bch2_dirent_name_bytes(l); const struct qstr l_name = bch2_dirent_get_name(l);
const struct qstr *r = _r; const struct qstr *r_name = _r;
return len - r->len ?: memcmp(l.v->d_name, r->name, len); return l_name.len - r_name->len ?: memcmp(l_name.name, r_name->name, l_name.len);
} }
static bool dirent_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r) static bool dirent_cmp_bkey(struct bkey_s_c _l, struct bkey_s_c _r)
{ {
struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l); struct bkey_s_c_dirent l = bkey_s_c_to_dirent(_l);
struct bkey_s_c_dirent r = bkey_s_c_to_dirent(_r); struct bkey_s_c_dirent r = bkey_s_c_to_dirent(_r);
int l_len = bch2_dirent_name_bytes(l); const struct qstr l_name = bch2_dirent_get_name(l);
int r_len = bch2_dirent_name_bytes(r); const struct qstr r_name = bch2_dirent_get_name(r);
return l_len - r_len ?: memcmp(l.v->d_name, r.v->d_name, l_len); return l_name.len - r_name.len ?: memcmp(l_name.name, r_name.name, l_name.len);
} }
static bool dirent_is_visible(subvol_inum inum, struct bkey_s_c k) static bool dirent_is_visible(subvol_inum inum, struct bkey_s_c k)
@ -89,37 +102,45 @@ int bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k,
struct printbuf *err) struct printbuf *err)
{ {
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
unsigned len; struct qstr d_name = bch2_dirent_get_name(d);
len = bch2_dirent_name_bytes(d); if (!d_name.len) {
if (!len) {
prt_printf(err, "empty name"); prt_printf(err, "empty name");
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
if (bkey_val_u64s(k.k) > dirent_val_u64s(len)) { if (bkey_val_u64s(k.k) > dirent_val_u64s(d_name.len)) {
prt_printf(err, "value too big (%zu > %u)", prt_printf(err, "value too big (%zu > %u)",
bkey_val_u64s(k.k), dirent_val_u64s(len)); bkey_val_u64s(k.k), dirent_val_u64s(d_name.len));
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
if (len > BCH_NAME_MAX) { /*
* Check new keys don't exceed the max length
* (older keys may be larger.)
*/
if ((flags & BKEY_INVALID_COMMIT) && d_name.len > BCH_NAME_MAX) {
prt_printf(err, "dirent name too big (%u > %u)", prt_printf(err, "dirent name too big (%u > %u)",
len, BCH_NAME_MAX); d_name.len, BCH_NAME_MAX);
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
if (len == 1 && !memcmp(d.v->d_name, ".", 1)) { if (d_name.len != strnlen(d_name.name, d_name.len)) {
prt_printf(err, "dirent has stray data after name's NUL");
return -BCH_ERR_invalid_bkey;
}
if (d_name.len == 1 && !memcmp(d_name.name, ".", 1)) {
prt_printf(err, "invalid name"); prt_printf(err, "invalid name");
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
if (len == 2 && !memcmp(d.v->d_name, "..", 2)) { if (d_name.len == 2 && !memcmp(d_name.name, "..", 2)) {
prt_printf(err, "invalid name"); prt_printf(err, "invalid name");
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
if (memchr(d.v->d_name, '/', len)) { if (memchr(d_name.name, '/', d_name.len)) {
prt_printf(err, "invalid name"); prt_printf(err, "invalid name");
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
@ -137,10 +158,11 @@ void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k) struct bkey_s_c k)
{ {
struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
struct qstr d_name = bch2_dirent_get_name(d);
prt_printf(out, "%.*s -> %llu type %s", prt_printf(out, "%.*s -> %llu type %s",
bch2_dirent_name_bytes(d), d_name.len,
d.v->d_name, d_name.name,
d.v->d_type != DT_SUBVOL d.v->d_type != DT_SUBVOL
? le64_to_cpu(d.v->d_inum) ? le64_to_cpu(d.v->d_inum)
: le32_to_cpu(d.v->d_child_subvol), : le32_to_cpu(d.v->d_child_subvol),
@ -507,6 +529,7 @@ int bch2_readdir(struct bch_fs *c, subvol_inum inum, struct dir_context *ctx)
subvol_inum target; subvol_inum target;
u32 snapshot; u32 snapshot;
struct bkey_buf sk; struct bkey_buf sk;
struct qstr name;
int ret; int ret;
bch2_bkey_buf_init(&sk); bch2_bkey_buf_init(&sk);
@ -537,9 +560,11 @@ retry:
dirent = bkey_i_to_s_c_dirent(sk.k); dirent = bkey_i_to_s_c_dirent(sk.k);
bch2_trans_unlock(&trans); bch2_trans_unlock(&trans);
name = bch2_dirent_get_name(dirent);
ctx->pos = dirent.k->p.offset; ctx->pos = dirent.k->p.offset;
if (!dir_emit(ctx, dirent.v->d_name, if (!dir_emit(ctx, name.name,
bch2_dirent_name_bytes(dirent), name.len,
target.inum, target.inum,
vfs_d_type(dirent.v->d_type))) vfs_d_type(dirent.v->d_type)))
break; break;

View File

@ -24,7 +24,7 @@ struct bch_fs;
struct bch_hash_info; struct bch_hash_info;
struct bch_inode_info; struct bch_inode_info;
unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent); struct qstr bch2_dirent_get_name(struct bkey_s_c_dirent d);
static inline unsigned dirent_val_u64s(unsigned len) static inline unsigned dirent_val_u64s(unsigned len)
{ {

View File

@ -1059,6 +1059,7 @@ void bch2_bkey_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
static int extent_ptr_invalid(const struct bch_fs *c, static int extent_ptr_invalid(const struct bch_fs *c,
struct bkey_s_c k, struct bkey_s_c k,
enum bkey_invalid_flags flags,
const struct bch_extent_ptr *ptr, const struct bch_extent_ptr *ptr,
unsigned size_ondisk, unsigned size_ondisk,
bool metadata, bool metadata,
@ -1071,6 +1072,14 @@ static int extent_ptr_invalid(const struct bch_fs *c,
struct bch_dev *ca; struct bch_dev *ca;
if (!bch2_dev_exists2(c, ptr->dev)) { if (!bch2_dev_exists2(c, ptr->dev)) {
/*
* If we're in the write path this key might have already been
* overwritten, and we could be seeing a device that doesn't
* exist anymore due to racing with device removal:
*/
if (flags & BKEY_INVALID_WRITE)
return 0;
prt_printf(err, "pointer to invalid device (%u)", ptr->dev); prt_printf(err, "pointer to invalid device (%u)", ptr->dev);
return -BCH_ERR_invalid_bkey; return -BCH_ERR_invalid_bkey;
} }
@ -1136,8 +1145,8 @@ int bch2_bkey_ptrs_invalid(const struct bch_fs *c, struct bkey_s_c k,
switch (extent_entry_type(entry)) { switch (extent_entry_type(entry)) {
case BCH_EXTENT_ENTRY_ptr: case BCH_EXTENT_ENTRY_ptr:
ret = extent_ptr_invalid(c, k, &entry->ptr, size_ondisk, ret = extent_ptr_invalid(c, k, flags, &entry->ptr,
false, err); size_ondisk, false, err);
if (ret) if (ret)
return ret; return ret;

View File

@ -909,6 +909,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
if (!folio_test_uptodate(f) && if (!folio_test_uptodate(f) &&
f_copied != folio_size(f) && f_copied != folio_size(f) &&
pos + copied + f_copied < inode->v.i_size) { pos + copied + f_copied < inode->v.i_size) {
iov_iter_revert(iter, f_copied);
folio_zero_range(f, 0, folio_size(f)); folio_zero_range(f, 0, folio_size(f));
folios_trunc(&folios, fi); folios_trunc(&folios, fi);
break; break;

View File

@ -698,20 +698,26 @@ loff_t bch2_seek_pagecache_data(struct inode *vinode,
return end_offset; return end_offset;
} }
/*
* Search for a hole in a folio.
*
* The filemap layer returns -ENOENT if no folio exists, so reuse the same error
* code to indicate a pagecache hole exists at the returned offset. Otherwise
* return 0 if the folio is filled with data, or an error code. This function
* can return -EAGAIN if nonblock is specified.
*/
static int folio_hole_offset(struct address_space *mapping, loff_t *offset, static int folio_hole_offset(struct address_space *mapping, loff_t *offset,
unsigned min_replicas, bool nonblock) unsigned min_replicas, bool nonblock)
{ {
struct folio *folio; struct folio *folio;
struct bch_folio *s; struct bch_folio *s;
unsigned i, sectors; unsigned i, sectors;
bool ret = true; int ret = -ENOENT;
folio = __filemap_get_folio(mapping, *offset >> PAGE_SHIFT, folio = __filemap_get_folio(mapping, *offset >> PAGE_SHIFT,
FGP_LOCK|(nonblock ? FGP_NOWAIT : 0), 0); FGP_LOCK|(nonblock ? FGP_NOWAIT : 0), 0);
if (folio == ERR_PTR(-EAGAIN)) if (IS_ERR(folio))
return -EAGAIN; return PTR_ERR(folio);
if (IS_ERR_OR_NULL(folio))
return true;
s = bch2_folio(folio); s = bch2_folio(folio);
if (!s) if (!s)
@ -727,7 +733,7 @@ static int folio_hole_offset(struct address_space *mapping, loff_t *offset,
} }
*offset = folio_end_pos(folio); *offset = folio_end_pos(folio);
ret = false; ret = 0;
unlock: unlock:
folio_unlock(folio); folio_unlock(folio);
folio_put(folio); folio_put(folio);
@ -742,11 +748,13 @@ loff_t bch2_seek_pagecache_hole(struct inode *vinode,
{ {
struct address_space *mapping = vinode->i_mapping; struct address_space *mapping = vinode->i_mapping;
loff_t offset = start_offset; loff_t offset = start_offset;
loff_t ret = 0;
while (offset < end_offset && while (!ret && offset < end_offset)
!folio_hole_offset(mapping, &offset, min_replicas, nonblock)) ret = folio_hole_offset(mapping, &offset, min_replicas, nonblock);
;
if (ret && ret != -ENOENT)
return ret;
return min(offset, end_offset); return min(offset, end_offset);
} }

View File

@ -109,7 +109,8 @@ struct inode_new_size {
unsigned fields; unsigned fields;
}; };
static int inode_set_size(struct bch_inode_info *inode, static int inode_set_size(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {
@ -390,7 +391,8 @@ static int bch2_extend(struct mnt_idmap *idmap,
return bch2_setattr_nonsize(idmap, inode, iattr); return bch2_setattr_nonsize(idmap, inode, iattr);
} }
static int bch2_truncate_finish_fn(struct bch_inode_info *inode, static int bch2_truncate_finish_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {
@ -398,7 +400,8 @@ static int bch2_truncate_finish_fn(struct bch_inode_info *inode,
return 0; return 0;
} }
static int bch2_truncate_start_fn(struct bch_inode_info *inode, static int bch2_truncate_start_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, void *p) struct bch_inode_unpacked *bi, void *p)
{ {
u64 *new_i_size = p; u64 *new_i_size = p;
@ -519,7 +522,8 @@ err:
/* fallocate: */ /* fallocate: */
static int inode_update_times_fn(struct bch_inode_info *inode, static int inode_update_times_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, void *p) struct bch_inode_unpacked *bi, void *p)
{ {
struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch_fs *c = inode->v.i_sb->s_fs_info;

View File

@ -108,15 +108,15 @@ static inline int bch2_quota_reservation_add(struct bch_fs *c,
#else #else
static void __bch2_quota_reservation_put(struct bch_fs *c, static inline void __bch2_quota_reservation_put(struct bch_fs *c,
struct bch_inode_info *inode, struct bch_inode_info *inode,
struct quota_res *res) {} struct quota_res *res) {}
static void bch2_quota_reservation_put(struct bch_fs *c, static inline void bch2_quota_reservation_put(struct bch_fs *c,
struct bch_inode_info *inode, struct bch_inode_info *inode,
struct quota_res *res) {} struct quota_res *res) {}
static int bch2_quota_reservation_add(struct bch_fs *c, static inline int bch2_quota_reservation_add(struct bch_fs *c,
struct bch_inode_info *inode, struct bch_inode_info *inode,
struct quota_res *res, struct quota_res *res,
unsigned sectors, unsigned sectors,

View File

@ -31,7 +31,8 @@ struct flags_set {
bool projinherit; bool projinherit;
}; };
static int bch2_inode_flags_set(struct bch_inode_info *inode, static int bch2_inode_flags_set(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {
@ -124,7 +125,8 @@ static int bch2_ioc_fsgetxattr(struct bch_inode_info *inode,
return copy_to_user(arg, &fa, sizeof(fa)); return copy_to_user(arg, &fa, sizeof(fa));
} }
static int fssetxattr_inode_update_fn(struct bch_inode_info *inode, static int fssetxattr_inode_update_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {
@ -135,7 +137,7 @@ static int fssetxattr_inode_update_fn(struct bch_inode_info *inode,
bi->bi_project = s->projid; bi->bi_project = s->projid;
} }
return bch2_inode_flags_set(inode, bi, p); return bch2_inode_flags_set(trans, inode, bi, p);
} }
static int bch2_ioc_fssetxattr(struct bch_fs *c, static int bch2_ioc_fssetxattr(struct bch_fs *c,
@ -192,7 +194,8 @@ err:
return ret; return ret;
} }
static int bch2_reinherit_attrs_fn(struct bch_inode_info *inode, static int bch2_reinherit_attrs_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {

View File

@ -23,6 +23,7 @@
#include "journal.h" #include "journal.h"
#include "keylist.h" #include "keylist.h"
#include "quota.h" #include "quota.h"
#include "snapshot.h"
#include "super.h" #include "super.h"
#include "xattr.h" #include "xattr.h"
@ -92,7 +93,7 @@ retry:
ret = bch2_inode_peek(&trans, &iter, &inode_u, inode_inum(inode), ret = bch2_inode_peek(&trans, &iter, &inode_u, inode_inum(inode),
BTREE_ITER_INTENT) ?: BTREE_ITER_INTENT) ?:
(set ? set(inode, &inode_u, p) : 0) ?: (set ? set(&trans, inode, &inode_u, p) : 0) ?:
bch2_inode_write(&trans, &iter, &inode_u) ?: bch2_inode_write(&trans, &iter, &inode_u) ?:
bch2_trans_commit(&trans, NULL, NULL, BTREE_INSERT_NOFAIL); bch2_trans_commit(&trans, NULL, NULL, BTREE_INSERT_NOFAIL);
@ -1237,7 +1238,8 @@ static int bch2_get_name(struct dentry *parent, char *name, struct dentry *child
struct bch_inode_unpacked inode_u; struct bch_inode_unpacked inode_u;
subvol_inum target; subvol_inum target;
u32 snapshot; u32 snapshot;
unsigned name_len; struct qstr dirent_name;
unsigned name_len = 0;
int ret; int ret;
if (!S_ISDIR(dir->v.i_mode)) if (!S_ISDIR(dir->v.i_mode))
@ -1314,9 +1316,10 @@ retry:
ret = -ENOENT; ret = -ENOENT;
goto err; goto err;
found: found:
name_len = min_t(unsigned, bch2_dirent_name_bytes(d), NAME_MAX); dirent_name = bch2_dirent_get_name(d);
memcpy(name, d.v->d_name, name_len); name_len = min_t(unsigned, dirent_name.len, NAME_MAX);
memcpy(name, dirent_name.name, name_len);
name[name_len] = '\0'; name[name_len] = '\0';
err: err:
if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
@ -1414,7 +1417,8 @@ static void bch2_destroy_inode(struct inode *vinode)
call_rcu(&vinode->i_rcu, bch2_i_callback); call_rcu(&vinode->i_rcu, bch2_i_callback);
} }
static int inode_update_times_fn(struct bch_inode_info *inode, static int inode_update_times_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {

View File

@ -174,7 +174,8 @@ static inline int bch2_set_projid(struct bch_fs *c,
struct inode *bch2_vfs_inode_get(struct bch_fs *, subvol_inum); struct inode *bch2_vfs_inode_get(struct bch_fs *, subvol_inum);
/* returns 0 if we want to do the update, or error is passed up */ /* returns 0 if we want to do the update, or error is passed up */
typedef int (*inode_set_fn)(struct bch_inode_info *, typedef int (*inode_set_fn)(struct btree_trans *,
struct bch_inode_info *,
struct bch_inode_unpacked *, void *); struct bch_inode_unpacked *, void *);
void bch2_inode_update_after_write(struct btree_trans *, void bch2_inode_update_after_write(struct btree_trans *,

View File

@ -12,7 +12,7 @@
#include "inode.h" #include "inode.h"
#include "keylist.h" #include "keylist.h"
#include "recovery.h" #include "recovery.h"
#include "subvolume.h" #include "snapshot.h"
#include "super.h" #include "super.h"
#include "xattr.h" #include "xattr.h"
@ -409,6 +409,28 @@ static inline void snapshots_seen_init(struct snapshots_seen *s)
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
} }
static int snapshots_seen_add_inorder(struct bch_fs *c, struct snapshots_seen *s, u32 id)
{
struct snapshots_seen_entry *i, n = {
.id = id,
.equiv = bch2_snapshot_equiv(c, id),
};
int ret = 0;
darray_for_each(s->ids, i) {
if (i->id == id)
return 0;
if (i->id > id)
break;
}
ret = darray_insert_item(&s->ids, i - s->ids.data, n);
if (ret)
bch_err(c, "error reallocating snapshots_seen table (size %zu)",
s->ids.size);
return ret;
}
static int snapshots_seen_update(struct bch_fs *c, struct snapshots_seen *s, static int snapshots_seen_update(struct bch_fs *c, struct snapshots_seen *s,
enum btree_id btree_id, struct bpos pos) enum btree_id btree_id, struct bpos pos)
{ {
@ -1123,7 +1145,8 @@ static int extent_ends_at(struct bch_fs *c,
static int overlapping_extents_found(struct btree_trans *trans, static int overlapping_extents_found(struct btree_trans *trans,
enum btree_id btree, enum btree_id btree,
struct bpos pos1, struct bkey pos2, struct bpos pos1, struct snapshots_seen *pos1_seen,
struct bkey pos2,
bool *fixed, bool *fixed,
struct extent_end *extent_end) struct extent_end *extent_end)
{ {
@ -1209,10 +1232,24 @@ static int overlapping_extents_found(struct btree_trans *trans,
*fixed = true; *fixed = true;
if (pos1.snapshot == pos2.p.snapshot) if (pos1.snapshot == pos2.p.snapshot) {
/*
* We overwrote the first extent, and did the overwrite
* in the same snapshot:
*/
extent_end->offset = bkey_start_offset(&pos2); extent_end->offset = bkey_start_offset(&pos2);
else } else if (pos1.snapshot > pos2.p.snapshot) {
/*
* We overwrote the first extent in pos2's snapshot:
*/
ret = snapshots_seen_add_inorder(c, pos1_seen, pos2.p.snapshot);
} else {
/*
* We overwrote the second extent - restart
* check_extent() from the top:
*/
ret = -BCH_ERR_transaction_restart_nested; ret = -BCH_ERR_transaction_restart_nested;
}
} }
fsck_err: fsck_err:
err: err:
@ -1254,6 +1291,7 @@ static int check_overlapping_extents(struct btree_trans *trans,
SPOS(iter->pos.inode, SPOS(iter->pos.inode,
i->offset, i->offset,
i->snapshot), i->snapshot),
&i->seen,
*k.k, fixed, i); *k.k, fixed, i);
if (ret) if (ret)
goto err; goto err;

View File

@ -11,6 +11,7 @@
#include "extent_update.h" #include "extent_update.h"
#include "inode.h" #include "inode.h"
#include "str_hash.h" #include "str_hash.h"
#include "snapshot.h"
#include "subvolume.h" #include "subvolume.h"
#include "varint.h" #include "varint.h"
@ -1048,6 +1049,11 @@ static int may_delete_deleted_inode(struct btree_trans *trans, struct bpos pos)
if (ret) if (ret)
goto err; goto err;
if (fsck_err_on(S_ISDIR(inode.bi_mode), c,
"directory %llu:%u in deleted_inodes btree",
pos.offset, pos.snapshot))
goto delete;
if (fsck_err_on(!(inode.bi_flags & BCH_INODE_UNLINKED), c, if (fsck_err_on(!(inode.bi_flags & BCH_INODE_UNLINKED), c,
"non-deleted inode %llu:%u in deleted_inodes btree", "non-deleted inode %llu:%u in deleted_inodes btree",
pos.offset, pos.snapshot)) pos.offset, pos.snapshot))

View File

@ -380,10 +380,10 @@ int bch2_extent_fallocate(struct btree_trans *trans,
struct bch_fs *c = trans->c; struct bch_fs *c = trans->c;
struct disk_reservation disk_res = { 0 }; struct disk_reservation disk_res = { 0 };
struct closure cl; struct closure cl;
struct open_buckets open_buckets; struct open_buckets open_buckets = { 0 };
struct bkey_s_c k; struct bkey_s_c k;
struct bkey_buf old, new; struct bkey_buf old, new;
unsigned sectors_allocated; unsigned sectors_allocated = 0;
bool have_reservation = false; bool have_reservation = false;
bool unwritten = opts.nocow && bool unwritten = opts.nocow &&
c->sb.version >= bcachefs_metadata_version_unwritten_extents; c->sb.version >= bcachefs_metadata_version_unwritten_extents;
@ -392,9 +392,6 @@ int bch2_extent_fallocate(struct btree_trans *trans,
bch2_bkey_buf_init(&old); bch2_bkey_buf_init(&old);
bch2_bkey_buf_init(&new); bch2_bkey_buf_init(&new);
closure_init_stack(&cl); closure_init_stack(&cl);
open_buckets.nr = 0;
retry:
sectors_allocated = 0;
k = bch2_btree_iter_peek_slot(iter); k = bch2_btree_iter_peek_slot(iter);
ret = bkey_err(k); ret = bkey_err(k);
@ -413,14 +410,14 @@ retry:
*/ */
ret = bch2_disk_reservation_get(c, &disk_res, sectors, new_replicas, 0); ret = bch2_disk_reservation_get(c, &disk_res, sectors, new_replicas, 0);
if (unlikely(ret)) if (unlikely(ret))
goto out; goto err;
bch2_bkey_buf_reassemble(&old, c, k); bch2_bkey_buf_reassemble(&old, c, k);
} }
if (have_reservation) { if (have_reservation) {
if (!bch2_extents_match(k, bkey_i_to_s_c(old.k))) if (!bch2_extents_match(k, bkey_i_to_s_c(old.k)))
goto out; goto err;
bch2_key_resize(&new.k->k, sectors); bch2_key_resize(&new.k->k, sectors);
} else if (!unwritten) { } else if (!unwritten) {
@ -452,13 +449,10 @@ retry:
opts.data_replicas, opts.data_replicas,
opts.data_replicas, opts.data_replicas,
BCH_WATERMARK_normal, 0, &cl, &wp); BCH_WATERMARK_normal, 0, &cl, &wp);
if (ret) { if (bch2_err_matches(ret, BCH_ERR_operation_blocked))
bch2_trans_unlock(trans); ret = -BCH_ERR_transaction_restart_nested;
closure_sync(&cl); if (ret)
if (bch2_err_matches(ret, BCH_ERR_operation_blocked)) goto err;
goto retry;
return ret;
}
sectors = min(sectors, wp->sectors_free); sectors = min(sectors, wp->sectors_free);
sectors_allocated = sectors; sectors_allocated = sectors;
@ -477,17 +471,7 @@ retry:
ret = bch2_extent_update(trans, inum, iter, new.k, &disk_res, ret = bch2_extent_update(trans, inum, iter, new.k, &disk_res,
0, i_sectors_delta, true); 0, i_sectors_delta, true);
out: err:
if ((atomic_read(&cl.remaining) & CLOSURE_REMAINING_MASK) != 1) {
bch2_trans_unlock(trans);
closure_sync(&cl);
}
if (bch2_err_matches(ret, BCH_ERR_transaction_restart)) {
bch2_trans_begin(trans);
goto retry;
}
if (!ret && sectors_allocated) if (!ret && sectors_allocated)
bch2_increment_clock(c, sectors_allocated, WRITE); bch2_increment_clock(c, sectors_allocated, WRITE);
@ -496,6 +480,11 @@ out:
bch2_bkey_buf_exit(&new, c); bch2_bkey_buf_exit(&new, c);
bch2_bkey_buf_exit(&old, c); bch2_bkey_buf_exit(&old, c);
if (closure_nr_remaining(&cl) != 1) {
bch2_trans_unlock(trans);
closure_sync(&cl);
}
return ret; return ret;
} }
@ -710,13 +699,15 @@ static void bch2_write_done(struct closure *cl)
struct bch_write_op *op = container_of(cl, struct bch_write_op, cl); struct bch_write_op *op = container_of(cl, struct bch_write_op, cl);
struct bch_fs *c = op->c; struct bch_fs *c = op->c;
EBUG_ON(op->open_buckets.nr);
bch2_time_stats_update(&c->times[BCH_TIME_data_write], op->start_time);
bch2_disk_reservation_put(c, &op->res); bch2_disk_reservation_put(c, &op->res);
if (!(op->flags & BCH_WRITE_MOVE)) if (!(op->flags & BCH_WRITE_MOVE))
bch2_write_ref_put(c, BCH_WRITE_REF_write); bch2_write_ref_put(c, BCH_WRITE_REF_write);
bch2_keylist_free(&op->insert_keys, op->inline_keys); bch2_keylist_free(&op->insert_keys, op->inline_keys);
bch2_time_stats_update(&c->times[BCH_TIME_data_write], op->start_time);
EBUG_ON(cl->parent); EBUG_ON(cl->parent);
closure_debug_destroy(cl); closure_debug_destroy(cl);
if (op->end_io) if (op->end_io)

View File

@ -5,7 +5,7 @@
#include "error.h" #include "error.h"
#include "inode.h" #include "inode.h"
#include "quota.h" #include "quota.h"
#include "subvolume.h" #include "snapshot.h"
#include "super-io.h" #include "super-io.h"
static const char * const bch2_quota_types[] = { static const char * const bch2_quota_types[] = {

View File

@ -113,6 +113,10 @@ static void rebalance_work_accumulate(struct rebalance_work *w,
unsigned percent_full; unsigned percent_full;
u64 work = dev_work + unknown_dev; u64 work = dev_work + unknown_dev;
/* avoid divide by 0 */
if (!capacity)
return;
if (work < dev_work || work < unknown_dev) if (work < dev_work || work < unknown_dev)
work = U64_MAX; work = U64_MAX;
work = min(work, capacity); work = min(work, capacity);

View File

@ -25,6 +25,7 @@
#include "recovery.h" #include "recovery.h"
#include "replicas.h" #include "replicas.h"
#include "sb-clean.h" #include "sb-clean.h"
#include "snapshot.h"
#include "subvolume.h" #include "subvolume.h"
#include "super-io.h" #include "super-io.h"

View File

@ -8,11 +8,13 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/clock.h> #include <linux/sched/clock.h>
#include <linux/sched/rt.h> #include <linux/sched/rt.h>
#include <linux/six.h> #include <linux/sched/task.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <trace/events/lock.h> #include <trace/events/lock.h>
#include "six.h"
#ifdef DEBUG #ifdef DEBUG
#define EBUG_ON(cond) BUG_ON(cond) #define EBUG_ON(cond) BUG_ON(cond)
#else #else
@ -222,15 +224,23 @@ again:
if (ret <= 0) if (ret <= 0)
goto unlock; goto unlock;
__list_del(w->list.prev, w->list.next);
task = w->task;
/* /*
* Do no writes to @w besides setting lock_acquired - otherwise * Similar to percpu_rwsem_wake_function(), we need to guard
* we would need a memory barrier: * against the wakee noticing w->lock_acquired, returning, and
* then exiting before we do the wakeup:
*/ */
barrier(); task = get_task_struct(w->task);
w->lock_acquired = true; __list_del(w->list.prev, w->list.next);
/*
* The release barrier here ensures the ordering of the
* __list_del before setting w->lock_acquired; @w is on the
* stack of the thread doing the waiting and will be reused
* after it sees w->lock_acquired with no other locking:
* pairs with smp_load_acquire() in six_lock_slowpath()
*/
smp_store_release(&w->lock_acquired, true);
wake_up_process(task); wake_up_process(task);
put_task_struct(task);
} }
six_clear_bitmask(lock, SIX_LOCK_WAITING_read << lock_type); six_clear_bitmask(lock, SIX_LOCK_WAITING_read << lock_type);
@ -501,17 +511,32 @@ static int six_lock_slowpath(struct six_lock *lock, enum six_lock_type type,
while (1) { while (1) {
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
if (wait->lock_acquired) /*
* Ensures that writes to the waitlist entry happen after we see
* wait->lock_acquired: pairs with the smp_store_release in
* __six_lock_wakeup
*/
if (smp_load_acquire(&wait->lock_acquired))
break; break;
ret = should_sleep_fn ? should_sleep_fn(lock, p) : 0; ret = should_sleep_fn ? should_sleep_fn(lock, p) : 0;
if (unlikely(ret)) { if (unlikely(ret)) {
bool acquired;
/*
* If should_sleep_fn() returns an error, we are
* required to return that error even if we already
* acquired the lock - should_sleep_fn() might have
* modified external state (e.g. when the deadlock cycle
* detector in bcachefs issued a transaction restart)
*/
raw_spin_lock(&lock->wait_lock); raw_spin_lock(&lock->wait_lock);
if (!wait->lock_acquired) acquired = wait->lock_acquired;
if (!acquired)
list_del(&wait->list); list_del(&wait->list);
raw_spin_unlock(&lock->wait_lock); raw_spin_unlock(&lock->wait_lock);
if (unlikely(wait->lock_acquired)) if (unlikely(acquired))
do_six_unlock_type(lock, type); do_six_unlock_type(lock, type);
break; break;
} }

886
libbcachefs/snapshot.c Normal file
View File

@ -0,0 +1,886 @@
// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "btree_key_cache.h"
#include "btree_update.h"
#include "errcode.h"
#include "error.h"
#include "fs.h"
#include "snapshot.h"
#include <linux/random.h>
/*
* Snapshot trees:
*
* Keys in BTREE_ID_snapshot_trees identify a whole tree of snapshot nodes; they
* exist to provide a stable identifier for the whole lifetime of a snapshot
* tree.
*/
void bch2_snapshot_tree_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k)
{
struct bkey_s_c_snapshot_tree t = bkey_s_c_to_snapshot_tree(k);
prt_printf(out, "subvol %u root snapshot %u",
le32_to_cpu(t.v->master_subvol),
le32_to_cpu(t.v->root_snapshot));
}
int bch2_snapshot_tree_invalid(const struct bch_fs *c, struct bkey_s_c k,
enum bkey_invalid_flags flags,
struct printbuf *err)
{
if (bkey_gt(k.k->p, POS(0, U32_MAX)) ||
bkey_lt(k.k->p, POS(0, 1))) {
prt_printf(err, "bad pos");
return -BCH_ERR_invalid_bkey;
}
return 0;
}
int bch2_snapshot_tree_lookup(struct btree_trans *trans, u32 id,
struct bch_snapshot_tree *s)
{
int ret = bch2_bkey_get_val_typed(trans, BTREE_ID_snapshot_trees, POS(0, id),
BTREE_ITER_WITH_UPDATES, snapshot_tree, s);
if (bch2_err_matches(ret, ENOENT))
ret = -BCH_ERR_ENOENT_snapshot_tree;
return ret;
}
struct bkey_i_snapshot_tree *
__bch2_snapshot_tree_create(struct btree_trans *trans)
{
struct btree_iter iter;
int ret = bch2_bkey_get_empty_slot(trans, &iter,
BTREE_ID_snapshot_trees, POS(0, U32_MAX));
struct bkey_i_snapshot_tree *s_t;
if (ret == -BCH_ERR_ENOSPC_btree_slot)
ret = -BCH_ERR_ENOSPC_snapshot_tree;
if (ret)
return ERR_PTR(ret);
s_t = bch2_bkey_alloc(trans, &iter, 0, snapshot_tree);
ret = PTR_ERR_OR_ZERO(s_t);
bch2_trans_iter_exit(trans, &iter);
return ret ? ERR_PTR(ret) : s_t;
}
static int bch2_snapshot_tree_create(struct btree_trans *trans,
u32 root_id, u32 subvol_id, u32 *tree_id)
{
struct bkey_i_snapshot_tree *n_tree =
__bch2_snapshot_tree_create(trans);
if (IS_ERR(n_tree))
return PTR_ERR(n_tree);
n_tree->v.master_subvol = cpu_to_le32(subvol_id);
n_tree->v.root_snapshot = cpu_to_le32(root_id);
*tree_id = n_tree->k.p.offset;
return 0;
}
/* Snapshot nodes: */
static inline u32 get_ancestor_below(struct snapshot_table *t, u32 id, u32 ancestor)
{
const struct snapshot_t *s = __snapshot_t(t, id);
if (s->skip[2] <= ancestor)
return s->skip[2];
if (s->skip[1] <= ancestor)
return s->skip[1];
if (s->skip[0] <= ancestor)
return s->skip[0];
return s->parent;
}
bool __bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
{
struct snapshot_table *t;
bool ret;
EBUG_ON(c->curr_recovery_pass <= BCH_RECOVERY_PASS_check_snapshots);
rcu_read_lock();
t = rcu_dereference(c->snapshots);
while (id && id < ancestor - IS_ANCESTOR_BITMAP)
id = get_ancestor_below(t, id, ancestor);
ret = id && id < ancestor
? test_bit(ancestor - id - 1, __snapshot_t(t, id)->is_ancestor)
: id == ancestor;
rcu_read_unlock();
return ret;
}
static bool bch2_snapshot_is_ancestor_early(struct bch_fs *c, u32 id, u32 ancestor)
{
struct snapshot_table *t;
rcu_read_lock();
t = rcu_dereference(c->snapshots);
while (id && id < ancestor)
id = __snapshot_t(t, id)->parent;
rcu_read_unlock();
return id == ancestor;
}
static noinline struct snapshot_t *__snapshot_t_mut(struct bch_fs *c, u32 id)
{
size_t idx = U32_MAX - id;
size_t new_size;
struct snapshot_table *new, *old;
new_size = max(16UL, roundup_pow_of_two(idx + 1));
new = kvzalloc(struct_size(new, s, new_size), GFP_KERNEL);
if (!new)
return NULL;
old = rcu_dereference_protected(c->snapshots, true);
if (old)
memcpy(new->s,
rcu_dereference_protected(c->snapshots, true)->s,
sizeof(new->s[0]) * c->snapshot_table_size);
rcu_assign_pointer(c->snapshots, new);
c->snapshot_table_size = new_size;
if (old)
kvfree_rcu(old);
return &rcu_dereference_protected(c->snapshots, true)->s[idx];
}
static inline struct snapshot_t *snapshot_t_mut(struct bch_fs *c, u32 id)
{
size_t idx = U32_MAX - id;
lockdep_assert_held(&c->snapshot_table_lock);
if (likely(idx < c->snapshot_table_size))
return &rcu_dereference_protected(c->snapshots, true)->s[idx];
return __snapshot_t_mut(c, id);
}
void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c,
struct bkey_s_c k)
{
struct bkey_s_c_snapshot s = bkey_s_c_to_snapshot(k);
prt_printf(out, "is_subvol %llu deleted %llu parent %10u children %10u %10u subvol %u tree %u",
BCH_SNAPSHOT_SUBVOL(s.v),
BCH_SNAPSHOT_DELETED(s.v),
le32_to_cpu(s.v->parent),
le32_to_cpu(s.v->children[0]),
le32_to_cpu(s.v->children[1]),
le32_to_cpu(s.v->subvol),
le32_to_cpu(s.v->tree));
if (bkey_val_bytes(k.k) > offsetof(struct bch_snapshot, depth))
prt_printf(out, " depth %u skiplist %u %u %u",
le32_to_cpu(s.v->depth),
le32_to_cpu(s.v->skip[0]),
le32_to_cpu(s.v->skip[1]),
le32_to_cpu(s.v->skip[2]));
}
int bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k,
enum bkey_invalid_flags flags,
struct printbuf *err)
{
struct bkey_s_c_snapshot s;
u32 i, id;
if (bkey_gt(k.k->p, POS(0, U32_MAX)) ||
bkey_lt(k.k->p, POS(0, 1))) {
prt_printf(err, "bad pos");
return -BCH_ERR_invalid_bkey;
}
s = bkey_s_c_to_snapshot(k);
id = le32_to_cpu(s.v->parent);
if (id && id <= k.k->p.offset) {
prt_printf(err, "bad parent node (%u <= %llu)",
id, k.k->p.offset);
return -BCH_ERR_invalid_bkey;
}
if (le32_to_cpu(s.v->children[0]) < le32_to_cpu(s.v->children[1])) {
prt_printf(err, "children not normalized");
return -BCH_ERR_invalid_bkey;
}
if (s.v->children[0] &&
s.v->children[0] == s.v->children[1]) {
prt_printf(err, "duplicate child nodes");
return -BCH_ERR_invalid_bkey;
}
for (i = 0; i < 2; i++) {
id = le32_to_cpu(s.v->children[i]);
if (id >= k.k->p.offset) {
prt_printf(err, "bad child node (%u >= %llu)",
id, k.k->p.offset);
return -BCH_ERR_invalid_bkey;
}
}
if (bkey_val_bytes(k.k) > offsetof(struct bch_snapshot, skip)) {
if (le32_to_cpu(s.v->skip[0]) > le32_to_cpu(s.v->skip[1]) ||
le32_to_cpu(s.v->skip[1]) > le32_to_cpu(s.v->skip[2])) {
prt_printf(err, "skiplist not normalized");
return -BCH_ERR_invalid_bkey;
}
for (i = 0; i < ARRAY_SIZE(s.v->skip); i++) {
id = le32_to_cpu(s.v->skip[i]);
if (!id != !s.v->parent ||
(s.v->parent &&
id <= k.k->p.offset)) {
prt_printf(err, "bad skiplist node %u)", id);
return -BCH_ERR_invalid_bkey;
}
}
}
return 0;
}
int bch2_mark_snapshot(struct btree_trans *trans,
enum btree_id btree, unsigned level,
struct bkey_s_c old, struct bkey_s_c new,
unsigned flags)
{
struct bch_fs *c = trans->c;
struct snapshot_t *t;
u32 id = new.k->p.offset;
int ret = 0;
mutex_lock(&c->snapshot_table_lock);
t = snapshot_t_mut(c, id);
if (!t) {
ret = -BCH_ERR_ENOMEM_mark_snapshot;
goto err;
}
if (new.k->type == KEY_TYPE_snapshot) {
struct bkey_s_c_snapshot s = bkey_s_c_to_snapshot(new);
u32 parent = id;
t->parent = le32_to_cpu(s.v->parent);
t->children[0] = le32_to_cpu(s.v->children[0]);
t->children[1] = le32_to_cpu(s.v->children[1]);
t->subvol = BCH_SNAPSHOT_SUBVOL(s.v) ? le32_to_cpu(s.v->subvol) : 0;
t->tree = le32_to_cpu(s.v->tree);
if (bkey_val_bytes(s.k) > offsetof(struct bch_snapshot, depth)) {
t->depth = le32_to_cpu(s.v->depth);
t->skip[0] = le32_to_cpu(s.v->skip[0]);
t->skip[1] = le32_to_cpu(s.v->skip[1]);
t->skip[2] = le32_to_cpu(s.v->skip[2]);
} else {
t->depth = 0;
t->skip[0] = 0;
t->skip[1] = 0;
t->skip[2] = 0;
}
while ((parent = bch2_snapshot_parent_early(c, parent)) &&
parent - id - 1 < IS_ANCESTOR_BITMAP)
__set_bit(parent - id - 1, t->is_ancestor);
if (BCH_SNAPSHOT_DELETED(s.v)) {
set_bit(BCH_FS_HAVE_DELETED_SNAPSHOTS, &c->flags);
c->recovery_passes_explicit |= BIT_ULL(BCH_RECOVERY_PASS_delete_dead_snapshots);
}
} else {
memset(t, 0, sizeof(*t));
}
err:
mutex_unlock(&c->snapshot_table_lock);
return ret;
}
int bch2_snapshot_lookup(struct btree_trans *trans, u32 id,
struct bch_snapshot *s)
{
return bch2_bkey_get_val_typed(trans, BTREE_ID_snapshots, POS(0, id),
BTREE_ITER_WITH_UPDATES, snapshot, s);
}
int bch2_snapshot_live(struct btree_trans *trans, u32 id)
{
struct bch_snapshot v;
int ret;
if (!id)
return 0;
ret = bch2_snapshot_lookup(trans, id, &v);
if (bch2_err_matches(ret, ENOENT))
bch_err(trans->c, "snapshot node %u not found", id);
if (ret)
return ret;
return !BCH_SNAPSHOT_DELETED(&v);
}
/*
* If @k is a snapshot with just one live child, it's part of a linear chain,
* which we consider to be an equivalence class: and then after snapshot
* deletion cleanup, there should only be a single key at a given position in
* this equivalence class.
*
* This sets the equivalence class of @k to be the child's equivalence class, if
* it's part of such a linear chain: this correctly sets equivalence classes on
* startup if we run leaf to root (i.e. in natural key order).
*/
int bch2_snapshot_set_equiv(struct btree_trans *trans, struct bkey_s_c k)
{
struct bch_fs *c = trans->c;
unsigned i, nr_live = 0, live_idx = 0;
struct bkey_s_c_snapshot snap;
u32 id = k.k->p.offset, child[2];
if (k.k->type != KEY_TYPE_snapshot)
return 0;
snap = bkey_s_c_to_snapshot(k);
child[0] = le32_to_cpu(snap.v->children[0]);
child[1] = le32_to_cpu(snap.v->children[1]);
for (i = 0; i < 2; i++) {
int ret = bch2_snapshot_live(trans, child[i]);
if (ret < 0)
return ret;
if (ret)
live_idx = i;
nr_live += ret;
}
mutex_lock(&c->snapshot_table_lock);
snapshot_t_mut(c, id)->equiv = nr_live == 1
? snapshot_t_mut(c, child[live_idx])->equiv
: id;
mutex_unlock(&c->snapshot_table_lock);
return 0;
}
/* fsck: */
static u32 bch2_snapshot_child(struct bch_fs *c, u32 id, unsigned child)
{
return snapshot_t(c, id)->children[child];
}
static u32 bch2_snapshot_left_child(struct bch_fs *c, u32 id)
{
return bch2_snapshot_child(c, id, 0);
}
static u32 bch2_snapshot_right_child(struct bch_fs *c, u32 id)
{
return bch2_snapshot_child(c, id, 1);
}
static u32 bch2_snapshot_tree_next(struct bch_fs *c, u32 id)
{
u32 n, parent;
n = bch2_snapshot_left_child(c, id);
if (n)
return n;
while ((parent = bch2_snapshot_parent(c, id))) {
n = bch2_snapshot_right_child(c, parent);
if (n && n != id)
return n;
id = parent;
}
return 0;
}
static u32 bch2_snapshot_tree_oldest_subvol(struct bch_fs *c, u32 snapshot_root)
{
u32 id = snapshot_root;
u32 subvol = 0, s;
while (id) {
s = snapshot_t(c, id)->subvol;
if (s && (!subvol || s < subvol))
subvol = s;
id = bch2_snapshot_tree_next(c, id);
}
return subvol;
}
static int bch2_snapshot_tree_master_subvol(struct btree_trans *trans,
u32 snapshot_root, u32 *subvol_id)
{
struct bch_fs *c = trans->c;
struct btree_iter iter;
struct bkey_s_c k;
struct bkey_s_c_subvolume s;
bool found = false;
int ret;
for_each_btree_key_norestart(trans, iter, BTREE_ID_subvolumes, POS_MIN,
0, k, ret) {
if (k.k->type != KEY_TYPE_subvolume)
continue;
s = bkey_s_c_to_subvolume(k);
if (!bch2_snapshot_is_ancestor(c, le32_to_cpu(s.v->snapshot), snapshot_root))
continue;
if (!BCH_SUBVOLUME_SNAP(s.v)) {
*subvol_id = s.k->p.offset;
found = true;
break;
}
}
bch2_trans_iter_exit(trans, &iter);
if (!ret && !found) {
struct bkey_i_subvolume *s;
*subvol_id = bch2_snapshot_tree_oldest_subvol(c, snapshot_root);
s = bch2_bkey_get_mut_typed(trans, &iter,
BTREE_ID_subvolumes, POS(0, *subvol_id),
0, subvolume);
ret = PTR_ERR_OR_ZERO(s);
if (ret)
return ret;
SET_BCH_SUBVOLUME_SNAP(&s->v, false);
}
return ret;
}
static int check_snapshot_tree(struct btree_trans *trans,
struct btree_iter *iter,
struct bkey_s_c k)
{
struct bch_fs *c = trans->c;
struct bkey_s_c_snapshot_tree st;
struct bch_snapshot s;
struct bch_subvolume subvol;
struct printbuf buf = PRINTBUF;
u32 root_id;
int ret;
if (k.k->type != KEY_TYPE_snapshot_tree)
return 0;
st = bkey_s_c_to_snapshot_tree(k);
root_id = le32_to_cpu(st.v->root_snapshot);
ret = bch2_snapshot_lookup(trans, root_id, &s);
if (ret && !bch2_err_matches(ret, ENOENT))
goto err;
if (fsck_err_on(ret ||
root_id != bch2_snapshot_root(c, root_id) ||
st.k->p.offset != le32_to_cpu(s.tree),
c,
"snapshot tree points to missing/incorrect snapshot:\n %s",
(bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) {
ret = bch2_btree_delete_at(trans, iter, 0);
goto err;
}
ret = bch2_subvolume_get(trans, le32_to_cpu(st.v->master_subvol),
false, 0, &subvol);
if (ret && !bch2_err_matches(ret, ENOENT))
goto err;
if (fsck_err_on(ret, c,
"snapshot tree points to missing subvolume:\n %s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) ||
fsck_err_on(!bch2_snapshot_is_ancestor_early(c,
le32_to_cpu(subvol.snapshot),
root_id), c,
"snapshot tree points to subvolume that does not point to snapshot in this tree:\n %s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf)) ||
fsck_err_on(BCH_SUBVOLUME_SNAP(&subvol), c,
"snapshot tree points to snapshot subvolume:\n %s",
(printbuf_reset(&buf),
bch2_bkey_val_to_text(&buf, c, st.s_c), buf.buf))) {
struct bkey_i_snapshot_tree *u;
u32 subvol_id;
ret = bch2_snapshot_tree_master_subvol(trans, root_id, &subvol_id);
if (ret)
goto err;
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot_tree);
ret = PTR_ERR_OR_ZERO(u);
if (ret)
goto err;
u->v.master_subvol = cpu_to_le32(subvol_id);
st = snapshot_tree_i_to_s_c(u);
}
err:
fsck_err:
printbuf_exit(&buf);
return ret;
}
/*
* For each snapshot_tree, make sure it points to the root of a snapshot tree
* and that snapshot entry points back to it, or delete it.
*
* And, make sure it points to a subvolume within that snapshot tree, or correct
* it to point to the oldest subvolume within that snapshot tree.
*/
int bch2_check_snapshot_trees(struct bch_fs *c)
{
struct btree_iter iter;
struct bkey_s_c k;
int ret;
ret = bch2_trans_run(c,
for_each_btree_key_commit(&trans, iter,
BTREE_ID_snapshot_trees, POS_MIN,
BTREE_ITER_PREFETCH, k,
NULL, NULL, BTREE_INSERT_LAZY_RW|BTREE_INSERT_NOFAIL,
check_snapshot_tree(&trans, &iter, k)));
if (ret)
bch_err(c, "error %i checking snapshot trees", ret);
return ret;
}
/*
* Look up snapshot tree for @tree_id and find root,
* make sure @snap_id is a descendent:
*/
static int snapshot_tree_ptr_good(struct btree_trans *trans,
u32 snap_id, u32 tree_id)
{
struct bch_snapshot_tree s_t;
int ret = bch2_snapshot_tree_lookup(trans, tree_id, &s_t);
if (bch2_err_matches(ret, ENOENT))
return 0;
if (ret)
return ret;
return bch2_snapshot_is_ancestor_early(trans->c, snap_id, le32_to_cpu(s_t.root_snapshot));
}
u32 bch2_snapshot_skiplist_get(struct bch_fs *c, u32 id)
{
const struct snapshot_t *s;
if (!id)
return 0;
rcu_read_lock();
s = snapshot_t(c, id);
if (s->parent)
id = bch2_snapshot_nth_parent(c, id, get_random_u32_below(s->depth));
rcu_read_unlock();
return id;
}
static int snapshot_skiplist_good(struct btree_trans *trans, struct bch_snapshot s)
{
struct bch_snapshot a;
unsigned i;
int ret;
for (i = 0; i < 3; i++) {
if (!s.parent != !s.skip[i])
return false;
if (!s.parent)
continue;
ret = bch2_snapshot_lookup(trans, le32_to_cpu(s.skip[i]), &a);
if (bch2_err_matches(ret, ENOENT))
return false;
if (ret)
return ret;
if (a.tree != s.tree)
return false;
}
return true;
}
/*
* snapshot_tree pointer was incorrect: look up root snapshot node, make sure
* its snapshot_tree pointer is correct (allocate new one if necessary), then
* update this node's pointer to root node's pointer:
*/
static int snapshot_tree_ptr_repair(struct btree_trans *trans,
struct btree_iter *iter,
struct bkey_s_c k,
struct bch_snapshot *s)
{
struct bch_fs *c = trans->c;
struct btree_iter root_iter;
struct bch_snapshot_tree s_t;
struct bkey_s_c_snapshot root;
struct bkey_i_snapshot *u;
u32 root_id = bch2_snapshot_root(c, k.k->p.offset), tree_id;
int ret;
root = bch2_bkey_get_iter_typed(trans, &root_iter,
BTREE_ID_snapshots, POS(0, root_id),
BTREE_ITER_WITH_UPDATES, snapshot);
ret = bkey_err(root);
if (ret)
goto err;
tree_id = le32_to_cpu(root.v->tree);
ret = bch2_snapshot_tree_lookup(trans, tree_id, &s_t);
if (ret && !bch2_err_matches(ret, ENOENT))
return ret;
if (ret || le32_to_cpu(s_t.root_snapshot) != root_id) {
u = bch2_bkey_make_mut_typed(trans, &root_iter, &root.s_c, 0, snapshot);
ret = PTR_ERR_OR_ZERO(u) ?:
bch2_snapshot_tree_create(trans, root_id,
bch2_snapshot_tree_oldest_subvol(c, root_id),
&tree_id);
if (ret)
goto err;
u->v.tree = cpu_to_le32(tree_id);
if (k.k->p.offset == root_id)
*s = u->v;
}
if (k.k->p.offset != root_id) {
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
ret = PTR_ERR_OR_ZERO(u);
if (ret)
goto err;
u->v.tree = cpu_to_le32(tree_id);
*s = u->v;
}
err:
bch2_trans_iter_exit(trans, &root_iter);
return ret;
}
static int check_snapshot(struct btree_trans *trans,
struct btree_iter *iter,
struct bkey_s_c k)
{
struct bch_fs *c = trans->c;
struct bch_snapshot s;
struct bch_subvolume subvol;
struct bch_snapshot v;
struct bkey_i_snapshot *u;
u32 parent_id = bch2_snapshot_parent_early(c, k.k->p.offset);
u32 real_depth;
struct printbuf buf = PRINTBUF;
bool should_have_subvol;
u32 i, id;
int ret = 0;
if (k.k->type != KEY_TYPE_snapshot)
return 0;
memset(&s, 0, sizeof(s));
memcpy(&s, k.v, bkey_val_bytes(k.k));
id = le32_to_cpu(s.parent);
if (id) {
ret = bch2_snapshot_lookup(trans, id, &v);
if (bch2_err_matches(ret, ENOENT))
bch_err(c, "snapshot with nonexistent parent:\n %s",
(bch2_bkey_val_to_text(&buf, c, k), buf.buf));
if (ret)
goto err;
if (le32_to_cpu(v.children[0]) != k.k->p.offset &&
le32_to_cpu(v.children[1]) != k.k->p.offset) {
bch_err(c, "snapshot parent %u missing pointer to child %llu",
id, k.k->p.offset);
ret = -EINVAL;
goto err;
}
}
for (i = 0; i < 2 && s.children[i]; i++) {
id = le32_to_cpu(s.children[i]);
ret = bch2_snapshot_lookup(trans, id, &v);
if (bch2_err_matches(ret, ENOENT))
bch_err(c, "snapshot node %llu has nonexistent child %u",
k.k->p.offset, id);
if (ret)
goto err;
if (le32_to_cpu(v.parent) != k.k->p.offset) {
bch_err(c, "snapshot child %u has wrong parent (got %u should be %llu)",
id, le32_to_cpu(v.parent), k.k->p.offset);
ret = -EINVAL;
goto err;
}
}
should_have_subvol = BCH_SNAPSHOT_SUBVOL(&s) &&
!BCH_SNAPSHOT_DELETED(&s);
if (should_have_subvol) {
id = le32_to_cpu(s.subvol);
ret = bch2_subvolume_get(trans, id, 0, false, &subvol);
if (bch2_err_matches(ret, ENOENT))
bch_err(c, "snapshot points to nonexistent subvolume:\n %s",
(bch2_bkey_val_to_text(&buf, c, k), buf.buf));
if (ret)
goto err;
if (BCH_SNAPSHOT_SUBVOL(&s) != (le32_to_cpu(subvol.snapshot) == k.k->p.offset)) {
bch_err(c, "snapshot node %llu has wrong BCH_SNAPSHOT_SUBVOL",
k.k->p.offset);
ret = -EINVAL;
goto err;
}
} else {
if (fsck_err_on(s.subvol, c, "snapshot should not point to subvol:\n %s",
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
ret = PTR_ERR_OR_ZERO(u);
if (ret)
goto err;
u->v.subvol = 0;
s = u->v;
}
}
ret = snapshot_tree_ptr_good(trans, k.k->p.offset, le32_to_cpu(s.tree));
if (ret < 0)
goto err;
if (fsck_err_on(!ret, c, "snapshot points to missing/incorrect tree:\n %s",
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
ret = snapshot_tree_ptr_repair(trans, iter, k, &s);
if (ret)
goto err;
}
ret = 0;
real_depth = bch2_snapshot_depth(c, parent_id);
if (le32_to_cpu(s.depth) != real_depth &&
(c->sb.version_upgrade_complete < bcachefs_metadata_version_snapshot_skiplists ||
fsck_err(c, "snapshot with incorrect depth field, should be %u:\n %s",
real_depth, (bch2_bkey_val_to_text(&buf, c, k), buf.buf)))) {
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
ret = PTR_ERR_OR_ZERO(u);
if (ret)
goto err;
u->v.depth = cpu_to_le32(real_depth);
s = u->v;
}
ret = snapshot_skiplist_good(trans, s);
if (ret < 0)
goto err;
if (!ret &&
(c->sb.version_upgrade_complete < bcachefs_metadata_version_snapshot_skiplists ||
fsck_err(c, "snapshot with bad skiplist field:\n %s",
(bch2_bkey_val_to_text(&buf, c, k), buf.buf)))) {
u = bch2_bkey_make_mut_typed(trans, iter, &k, 0, snapshot);
ret = PTR_ERR_OR_ZERO(u);
if (ret)
goto err;
for (i = 0; i < ARRAY_SIZE(u->v.skip); i++)
u->v.skip[i] = cpu_to_le32(bch2_snapshot_skiplist_get(c, parent_id));
bubble_sort(u->v.skip, ARRAY_SIZE(u->v.skip), cmp_le32);
s = u->v;
}
ret = 0;
err:
fsck_err:
printbuf_exit(&buf);
return ret;
}
int bch2_check_snapshots(struct bch_fs *c)
{
struct btree_iter iter;
struct bkey_s_c k;
int ret;
/*
* We iterate backwards as checking/fixing the depth field requires that
* the parent's depth already be correct:
*/
ret = bch2_trans_run(c,
for_each_btree_key_reverse_commit(&trans, iter,
BTREE_ID_snapshots, POS_MAX,
BTREE_ITER_PREFETCH, k,
NULL, NULL, BTREE_INSERT_LAZY_RW|BTREE_INSERT_NOFAIL,
check_snapshot(&trans, &iter, k)));
if (ret)
bch_err_fn(c, ret);
return ret;
}
int bch2_snapshots_read(struct bch_fs *c)
{
struct btree_iter iter;
struct bkey_s_c k;
int ret = 0;
ret = bch2_trans_run(c,
for_each_btree_key2(&trans, iter, BTREE_ID_snapshots,
POS_MIN, 0, k,
bch2_mark_snapshot(&trans, BTREE_ID_snapshots, 0, bkey_s_c_null, k, 0) ?:
bch2_snapshot_set_equiv(&trans, k)));
if (ret)
bch_err_fn(c, ret);
return ret;
}
void bch2_fs_snapshots_exit(struct bch_fs *c)
{
kfree(rcu_dereference_protected(c->snapshots, true));
}

250
libbcachefs/snapshot.h Normal file
View File

@ -0,0 +1,250 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BCACHEFS_SNAPSHOT_H
#define _BCACHEFS_SNAPSHOT_H
enum bkey_invalid_flags;
void bch2_snapshot_tree_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
int bch2_snapshot_tree_invalid(const struct bch_fs *, struct bkey_s_c,
enum bkey_invalid_flags, struct printbuf *);
#define bch2_bkey_ops_snapshot_tree ((struct bkey_ops) { \
.key_invalid = bch2_snapshot_tree_invalid, \
.val_to_text = bch2_snapshot_tree_to_text, \
.min_val_size = 8, \
})
struct bkey_i_snapshot_tree *__bch2_snapshot_tree_create(struct btree_trans *);
int bch2_snapshot_tree_lookup(struct btree_trans *, u32, struct bch_snapshot_tree *);
void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
int bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c,
enum bkey_invalid_flags, struct printbuf *);
int bch2_mark_snapshot(struct btree_trans *, enum btree_id, unsigned,
struct bkey_s_c, struct bkey_s_c, unsigned);
#define bch2_bkey_ops_snapshot ((struct bkey_ops) { \
.key_invalid = bch2_snapshot_invalid, \
.val_to_text = bch2_snapshot_to_text, \
.atomic_trigger = bch2_mark_snapshot, \
.min_val_size = 24, \
})
static inline struct snapshot_t *__snapshot_t(struct snapshot_table *t, u32 id)
{
return &t->s[U32_MAX - id];
}
static inline const struct snapshot_t *snapshot_t(struct bch_fs *c, u32 id)
{
return __snapshot_t(rcu_dereference(c->snapshots), id);
}
static inline u32 bch2_snapshot_tree(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = snapshot_t(c, id)->tree;
rcu_read_unlock();
return id;
}
static inline u32 __bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
{
return snapshot_t(c, id)->parent;
}
static inline u32 bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = __bch2_snapshot_parent_early(c, id);
rcu_read_unlock();
return id;
}
static inline u32 __bch2_snapshot_parent(struct bch_fs *c, u32 id)
{
#ifdef CONFIG_BCACHEFS_DEBUG
u32 parent = snapshot_t(c, id)->parent;
if (parent &&
snapshot_t(c, id)->depth != snapshot_t(c, parent)->depth + 1)
panic("id %u depth=%u parent %u depth=%u\n",
id, snapshot_t(c, id)->depth,
parent, snapshot_t(c, parent)->depth);
return parent;
#else
return snapshot_t(c, id)->parent;
#endif
}
static inline u32 bch2_snapshot_parent(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = __bch2_snapshot_parent(c, id);
rcu_read_unlock();
return id;
}
static inline u32 bch2_snapshot_nth_parent(struct bch_fs *c, u32 id, u32 n)
{
rcu_read_lock();
while (n--)
id = __bch2_snapshot_parent(c, id);
rcu_read_unlock();
return id;
}
u32 bch2_snapshot_skiplist_get(struct bch_fs *, u32);
static inline u32 bch2_snapshot_root(struct bch_fs *c, u32 id)
{
u32 parent;
rcu_read_lock();
while ((parent = __bch2_snapshot_parent(c, id)))
id = parent;
rcu_read_unlock();
return id;
}
static inline u32 __bch2_snapshot_equiv(struct bch_fs *c, u32 id)
{
return snapshot_t(c, id)->equiv;
}
static inline u32 bch2_snapshot_equiv(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = __bch2_snapshot_equiv(c, id);
rcu_read_unlock();
return id;
}
static inline bool bch2_snapshot_is_equiv(struct bch_fs *c, u32 id)
{
return id == bch2_snapshot_equiv(c, id);
}
static inline bool bch2_snapshot_is_internal_node(struct bch_fs *c, u32 id)
{
const struct snapshot_t *s;
bool ret;
rcu_read_lock();
s = snapshot_t(c, id);
ret = s->children[0];
rcu_read_unlock();
return ret;
}
static inline u32 bch2_snapshot_is_leaf(struct bch_fs *c, u32 id)
{
return !bch2_snapshot_is_internal_node(c, id);
}
static inline u32 bch2_snapshot_sibling(struct bch_fs *c, u32 id)
{
const struct snapshot_t *s;
u32 parent = __bch2_snapshot_parent(c, id);
if (!parent)
return 0;
s = snapshot_t(c, __bch2_snapshot_parent(c, id));
if (id == s->children[0])
return s->children[1];
if (id == s->children[1])
return s->children[0];
return 0;
}
static inline u32 bch2_snapshot_depth(struct bch_fs *c, u32 parent)
{
u32 depth;
rcu_read_lock();
depth = parent ? snapshot_t(c, parent)->depth + 1 : 0;
rcu_read_unlock();
return depth;
}
bool __bch2_snapshot_is_ancestor(struct bch_fs *, u32, u32);
static inline bool bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
{
return id == ancestor
? true
: __bch2_snapshot_is_ancestor(c, id, ancestor);
}
static inline bool bch2_snapshot_has_children(struct bch_fs *c, u32 id)
{
const struct snapshot_t *t;
bool ret;
rcu_read_lock();
t = snapshot_t(c, id);
ret = (t->children[0]|t->children[1]) != 0;
rcu_read_unlock();
return ret;
}
static inline bool snapshot_list_has_id(snapshot_id_list *s, u32 id)
{
u32 *i;
darray_for_each(*s, i)
if (*i == id)
return true;
return false;
}
static inline bool snapshot_list_has_ancestor(struct bch_fs *c, snapshot_id_list *s, u32 id)
{
u32 *i;
darray_for_each(*s, i)
if (bch2_snapshot_is_ancestor(c, id, *i))
return true;
return false;
}
static inline int snapshot_list_add(struct bch_fs *c, snapshot_id_list *s, u32 id)
{
int ret;
BUG_ON(snapshot_list_has_id(s, id));
ret = darray_push(s, id);
if (ret)
bch_err(c, "error reallocating snapshot_id_list (size %zu)", s->size);
return ret;
}
int bch2_snapshot_lookup(struct btree_trans *trans, u32 id,
struct bch_snapshot *s);
int bch2_snapshot_get_subvol(struct btree_trans *, u32,
struct bch_subvolume *);
int bch2_snapshot_live(struct btree_trans *trans, u32 id);
int bch2_snapshot_set_equiv(struct btree_trans *trans, struct bkey_s_c k);
/* only exported for tests: */
int bch2_snapshot_node_create(struct btree_trans *, u32,
u32 *, u32 *, unsigned);
int bch2_check_snapshot_trees(struct bch_fs *);
int bch2_check_snapshots(struct bch_fs *);
int bch2_snapshots_read(struct bch_fs *);
void bch2_fs_snapshots_exit(struct bch_fs *);
#endif /* _BCACHEFS_SNAPSHOT_H */

File diff suppressed because it is too large Load Diff

View File

@ -7,225 +7,8 @@
enum bkey_invalid_flags; enum bkey_invalid_flags;
void bch2_snapshot_tree_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
int bch2_snapshot_tree_invalid(const struct bch_fs *, struct bkey_s_c,
enum bkey_invalid_flags, struct printbuf *);
#define bch2_bkey_ops_snapshot_tree ((struct bkey_ops) { \
.key_invalid = bch2_snapshot_tree_invalid, \
.val_to_text = bch2_snapshot_tree_to_text, \
.min_val_size = 8, \
})
int bch2_snapshot_tree_lookup(struct btree_trans *, u32, struct bch_snapshot_tree *);
void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
int bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c,
enum bkey_invalid_flags, struct printbuf *);
int bch2_mark_snapshot(struct btree_trans *, enum btree_id, unsigned,
struct bkey_s_c, struct bkey_s_c, unsigned);
#define bch2_bkey_ops_snapshot ((struct bkey_ops) { \
.key_invalid = bch2_snapshot_invalid, \
.val_to_text = bch2_snapshot_to_text, \
.atomic_trigger = bch2_mark_snapshot, \
.min_val_size = 24, \
})
static inline struct snapshot_t *__snapshot_t(struct snapshot_table *t, u32 id)
{
return &t->s[U32_MAX - id];
}
static inline const struct snapshot_t *snapshot_t(struct bch_fs *c, u32 id)
{
return __snapshot_t(rcu_dereference(c->snapshots), id);
}
static inline u32 bch2_snapshot_tree(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = snapshot_t(c, id)->tree;
rcu_read_unlock();
return id;
}
static inline u32 __bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
{
return snapshot_t(c, id)->parent;
}
static inline u32 bch2_snapshot_parent_early(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = __bch2_snapshot_parent_early(c, id);
rcu_read_unlock();
return id;
}
static inline u32 __bch2_snapshot_parent(struct bch_fs *c, u32 id)
{
#ifdef CONFIG_BCACHEFS_DEBUG
u32 parent = snapshot_t(c, id)->parent;
if (parent &&
snapshot_t(c, id)->depth != snapshot_t(c, parent)->depth + 1)
panic("id %u depth=%u parent %u depth=%u\n",
id, snapshot_t(c, id)->depth,
parent, snapshot_t(c, parent)->depth);
return parent;
#else
return snapshot_t(c, id)->parent;
#endif
}
static inline u32 bch2_snapshot_parent(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = __bch2_snapshot_parent(c, id);
rcu_read_unlock();
return id;
}
static inline u32 bch2_snapshot_nth_parent(struct bch_fs *c, u32 id, u32 n)
{
rcu_read_lock();
while (n--)
id = __bch2_snapshot_parent(c, id);
rcu_read_unlock();
return id;
}
static inline u32 bch2_snapshot_root(struct bch_fs *c, u32 id)
{
u32 parent;
rcu_read_lock();
while ((parent = __bch2_snapshot_parent(c, id)))
id = parent;
rcu_read_unlock();
return id;
}
static inline u32 __bch2_snapshot_equiv(struct bch_fs *c, u32 id)
{
return snapshot_t(c, id)->equiv;
}
static inline u32 bch2_snapshot_equiv(struct bch_fs *c, u32 id)
{
rcu_read_lock();
id = __bch2_snapshot_equiv(c, id);
rcu_read_unlock();
return id;
}
static inline bool bch2_snapshot_is_equiv(struct bch_fs *c, u32 id)
{
return id == bch2_snapshot_equiv(c, id);
}
static inline bool bch2_snapshot_is_internal_node(struct bch_fs *c, u32 id)
{
const struct snapshot_t *s;
bool ret;
rcu_read_lock();
s = snapshot_t(c, id);
ret = s->children[0];
rcu_read_unlock();
return ret;
}
static inline u32 bch2_snapshot_is_leaf(struct bch_fs *c, u32 id)
{
return !bch2_snapshot_is_internal_node(c, id);
}
static inline u32 bch2_snapshot_sibling(struct bch_fs *c, u32 id)
{
const struct snapshot_t *s;
u32 parent = __bch2_snapshot_parent(c, id);
if (!parent)
return 0;
s = snapshot_t(c, __bch2_snapshot_parent(c, id));
if (id == s->children[0])
return s->children[1];
if (id == s->children[1])
return s->children[0];
return 0;
}
bool __bch2_snapshot_is_ancestor(struct bch_fs *, u32, u32);
static inline bool bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
{
return id == ancestor
? true
: __bch2_snapshot_is_ancestor(c, id, ancestor);
}
static inline bool bch2_snapshot_has_children(struct bch_fs *c, u32 id)
{
const struct snapshot_t *t;
bool ret;
rcu_read_lock();
t = snapshot_t(c, id);
ret = (t->children[0]|t->children[1]) != 0;
rcu_read_unlock();
return ret;
}
static inline bool snapshot_list_has_id(snapshot_id_list *s, u32 id)
{
u32 *i;
darray_for_each(*s, i)
if (*i == id)
return true;
return false;
}
static inline bool snapshot_list_has_ancestor(struct bch_fs *c, snapshot_id_list *s, u32 id)
{
u32 *i;
darray_for_each(*s, i)
if (bch2_snapshot_is_ancestor(c, id, *i))
return true;
return false;
}
static inline int snapshot_list_add(struct bch_fs *c, snapshot_id_list *s, u32 id)
{
int ret;
BUG_ON(snapshot_list_has_id(s, id));
ret = darray_push(s, id);
if (ret)
bch_err(c, "error reallocating snapshot_id_list (size %zu)", s->size);
return ret;
}
int bch2_check_snapshot_trees(struct bch_fs *);
int bch2_check_snapshots(struct bch_fs *);
int bch2_check_subvols(struct bch_fs *); int bch2_check_subvols(struct bch_fs *);
void bch2_fs_snapshots_exit(struct bch_fs *);
int bch2_snapshots_read(struct bch_fs *);
int bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c, int bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c,
unsigned, struct printbuf *); unsigned, struct printbuf *);
void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c); void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
@ -238,14 +21,8 @@ void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c)
int bch2_subvolume_get(struct btree_trans *, unsigned, int bch2_subvolume_get(struct btree_trans *, unsigned,
bool, int, struct bch_subvolume *); bool, int, struct bch_subvolume *);
int bch2_snapshot_get_subvol(struct btree_trans *, u32,
struct bch_subvolume *);
int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *); int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *);
/* only exported for tests: */
int bch2_snapshot_node_create(struct btree_trans *, u32,
u32 *, u32 *, unsigned);
int bch2_delete_dead_snapshots(struct bch_fs *); int bch2_delete_dead_snapshots(struct bch_fs *);
void bch2_delete_dead_snapshots_async(struct bch_fs *); void bch2_delete_dead_snapshots_async(struct bch_fs *);

View File

@ -48,6 +48,7 @@
#include "recovery.h" #include "recovery.h"
#include "replicas.h" #include "replicas.h"
#include "sb-clean.h" #include "sb-clean.h"
#include "snapshot.h"
#include "subvolume.h" #include "subvolume.h"
#include "super.h" #include "super.h"
#include "super-io.h" #include "super-io.h"

View File

@ -4,7 +4,7 @@
#include "bcachefs.h" #include "bcachefs.h"
#include "btree_update.h" #include "btree_update.h"
#include "journal_reclaim.h" #include "journal_reclaim.h"
#include "subvolume.h" #include "snapshot.h"
#include "tests.h" #include "tests.h"
#include "linux/kthread.h" #include "linux/kthread.h"

View File

@ -8,9 +8,9 @@
#include "btree_update_interior.h" #include "btree_update_interior.h"
#include "keylist.h" #include "keylist.h"
#include "opts.h" #include "opts.h"
#include "six.h"
#include <linux/blktrace_api.h> #include <linux/blktrace_api.h>
#include <linux/six.h>
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include "trace.h" #include "trace.h"

View File

@ -269,6 +269,7 @@ void bch2_print_string_as_lines(const char *prefix, const char *lines)
int bch2_save_backtrace(bch_stacktrace *stack, struct task_struct *task) int bch2_save_backtrace(bch_stacktrace *stack, struct task_struct *task)
{ {
#ifdef CONFIG_STACKTRACE
unsigned nr_entries = 0; unsigned nr_entries = 0;
int ret = 0; int ret = 0;
@ -289,6 +290,9 @@ int bch2_save_backtrace(bch_stacktrace *stack, struct task_struct *task)
up_read(&task->signal->exec_update_lock); up_read(&task->signal->exec_update_lock);
return ret; return ret;
#else
return 0;
#endif
} }
void bch2_prt_backtrace(struct printbuf *out, bch_stacktrace *stack) void bch2_prt_backtrace(struct printbuf *out, bch_stacktrace *stack)

View File

@ -845,6 +845,11 @@ static inline int u8_cmp(u8 l, u8 r)
return cmp_int(l, r); return cmp_int(l, r);
} }
static inline int cmp_le32(__le32 l, __le32 r)
{
return cmp_int(le32_to_cpu(l), le32_to_cpu(r));
}
#include <linux/uuid.h> #include <linux/uuid.h>
#endif /* _BCACHEFS_UTIL_H */ #endif /* _BCACHEFS_UTIL_H */

View File

@ -494,7 +494,8 @@ struct inode_opt_set {
bool defined; bool defined;
}; };
static int inode_opt_set_fn(struct bch_inode_info *inode, static int inode_opt_set_fn(struct btree_trans *trans,
struct bch_inode_info *inode,
struct bch_inode_unpacked *bi, struct bch_inode_unpacked *bi,
void *p) void *p)
{ {

View File

@ -17,8 +17,9 @@ static inline void closure_put_after_sub(struct closure *cl, int flags)
{ {
int r = flags & CLOSURE_REMAINING_MASK; int r = flags & CLOSURE_REMAINING_MASK;
BUG_ON(flags & CLOSURE_GUARD_MASK); if ((flags & CLOSURE_GUARD_MASK) ||
BUG_ON(!r && (flags & ~CLOSURE_DESTRUCTOR)); (!r && (flags & ~CLOSURE_DESTRUCTOR)))
panic("closure_put_after_sub: bogus flags %x remaining %i", flags, r);
if (!r) { if (!r) {
if (cl->fn && !(flags & CLOSURE_DESTRUCTOR)) { if (cl->fn && !(flags & CLOSURE_DESTRUCTOR)) {