summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bcachefs_revision2
-rw-r--r--libbcachefs/btree_cache.c145
-rw-r--r--libbcachefs/btree_cache.h2
-rw-r--r--libbcachefs/btree_io.c2
-rw-r--r--libbcachefs/btree_key_cache.c10
-rw-r--r--libbcachefs/btree_types.h3
-rw-r--r--libbcachefs/btree_update_interior.c125
-rw-r--r--libbcachefs/btree_update_interior.h6
-rw-r--r--libbcachefs/fs.c3
-rw-r--r--libbcachefs/opts.c40
-rw-r--r--libbcachefs/opts.h56
-rw-r--r--libbcachefs/super-io.c190
-rw-r--r--libbcachefs/super.c3
13 files changed, 325 insertions, 262 deletions
diff --git a/.bcachefs_revision b/.bcachefs_revision
index 81c781d7..eaa2d30d 100644
--- a/.bcachefs_revision
+++ b/.bcachefs_revision
@@ -1 +1 @@
-8ffb42b3d09418642680d23401a7a71d6ff87e3a
+f67089dc9b3d98b8c6e8990d5525b4fc7ba770b0
diff --git a/libbcachefs/btree_cache.c b/libbcachefs/btree_cache.c
index 1347b1fc..d91408c2 100644
--- a/libbcachefs/btree_cache.c
+++ b/libbcachefs/btree_cache.c
@@ -42,6 +42,14 @@ static inline unsigned btree_cache_can_free(struct btree_cache *bc)
return max_t(int, 0, bc->used - bc->reserve);
}
+static void btree_node_to_freedlist(struct btree_cache *bc, struct btree *b)
+{
+ if (b->c.lock.readers)
+ list_move(&b->list, &bc->freed_pcpu);
+ else
+ list_move(&b->list, &bc->freed_nonpcpu);
+}
+
static void btree_node_data_free(struct bch_fs *c, struct btree *b)
{
struct btree_cache *bc = &c->btree_cache;
@@ -58,7 +66,8 @@ static void btree_node_data_free(struct bch_fs *c, struct btree *b)
b->aux_data = NULL;
bc->used--;
- list_move(&b->list, &bc->freed);
+
+ btree_node_to_freedlist(bc, b);
}
static int bch2_btree_cache_cmp_fn(struct rhashtable_compare_arg *arg,
@@ -163,11 +172,6 @@ int bch2_btree_node_hash_insert(struct btree_cache *bc, struct btree *b,
b->c.level = level;
b->c.btree_id = id;
- if (level)
- six_lock_pcpu_alloc(&b->c.lock);
- else
- six_lock_pcpu_free_rcu(&b->c.lock);
-
mutex_lock(&bc->lock);
ret = __bch2_btree_node_hash_insert(bc, b);
if (!ret)
@@ -328,17 +332,13 @@ static unsigned long bch2_btree_cache_scan(struct shrinker *shrink,
}
restart:
list_for_each_entry_safe(b, t, &bc->live, list) {
- touched++;
-
- if (touched >= nr) {
- /* Save position */
- if (&t->list != &bc->live)
- list_move_tail(&bc->live, &t->list);
- break;
+ /* tweak this */
+ if (btree_node_accessed(b)) {
+ clear_btree_node_accessed(b);
+ goto touched;
}
- if (!btree_node_accessed(b) &&
- !btree_node_reclaim(c, b)) {
+ if (!btree_node_reclaim(c, b)) {
/* can't call bch2_btree_node_hash_remove under lock */
freed++;
if (&t->list != &bc->live)
@@ -359,8 +359,18 @@ restart:
else if (!mutex_trylock(&bc->lock))
goto out;
goto restart;
- } else
- clear_btree_node_accessed(b);
+ } else {
+ continue;
+ }
+touched:
+ touched++;
+
+ if (touched >= nr) {
+ /* Save position */
+ if (&t->list != &bc->live)
+ list_move_tail(&bc->live, &t->list);
+ break;
+ }
}
mutex_unlock(&bc->lock);
@@ -427,8 +437,10 @@ void bch2_fs_btree_cache_exit(struct bch_fs *c)
BUG_ON(atomic_read(&c->btree_cache.dirty));
- while (!list_empty(&bc->freed)) {
- b = list_first_entry(&bc->freed, struct btree, list);
+ list_splice(&bc->freed_pcpu, &bc->freed_nonpcpu);
+
+ while (!list_empty(&bc->freed_nonpcpu)) {
+ b = list_first_entry(&bc->freed_nonpcpu, struct btree, list);
list_del(&b->list);
six_lock_pcpu_free(&b->c.lock);
kfree(b);
@@ -482,7 +494,8 @@ void bch2_fs_btree_cache_init_early(struct btree_cache *bc)
mutex_init(&bc->lock);
INIT_LIST_HEAD(&bc->live);
INIT_LIST_HEAD(&bc->freeable);
- INIT_LIST_HEAD(&bc->freed);
+ INIT_LIST_HEAD(&bc->freed_pcpu);
+ INIT_LIST_HEAD(&bc->freed_nonpcpu);
}
/*
@@ -557,10 +570,13 @@ static struct btree *btree_node_cannibalize(struct bch_fs *c)
}
}
-struct btree *bch2_btree_node_mem_alloc(struct bch_fs *c)
+struct btree *bch2_btree_node_mem_alloc(struct bch_fs *c, bool pcpu_read_locks)
{
struct btree_cache *bc = &c->btree_cache;
- struct btree *b;
+ struct list_head *freed = pcpu_read_locks
+ ? &bc->freed_pcpu
+ : &bc->freed_nonpcpu;
+ struct btree *b, *b2;
u64 start_time = local_clock();
unsigned flags;
@@ -568,44 +584,49 @@ struct btree *bch2_btree_node_mem_alloc(struct bch_fs *c)
mutex_lock(&bc->lock);
/*
- * btree_free() doesn't free memory; it sticks the node on the end of
- * the list. Check if there's any freed nodes there:
- */
- list_for_each_entry(b, &bc->freeable, list)
- if (!btree_node_reclaim(c, b))
- goto got_node;
-
- /*
* We never free struct btree itself, just the memory that holds the on
* disk node. Check the freed list before allocating a new one:
*/
- list_for_each_entry(b, &bc->freed, list)
- if (!btree_node_reclaim(c, b))
+ list_for_each_entry(b, freed, list)
+ if (!btree_node_reclaim(c, b)) {
+ list_del_init(&b->list);
goto got_node;
+ }
+
+ b = __btree_node_mem_alloc(c);
+ if (!b)
+ goto err_locked;
- b = NULL;
+ if (pcpu_read_locks)
+ six_lock_pcpu_alloc(&b->c.lock);
+
+ BUG_ON(!six_trylock_intent(&b->c.lock));
+ BUG_ON(!six_trylock_write(&b->c.lock));
got_node:
- if (b)
- list_del_init(&b->list);
- mutex_unlock(&bc->lock);
- if (!b) {
- b = __btree_node_mem_alloc(c);
- if (!b)
- goto err;
+ /*
+ * btree_free() doesn't free memory; it sticks the node on the end of
+ * the list. Check if there's any freed nodes there:
+ */
+ list_for_each_entry(b2, &bc->freeable, list)
+ if (!btree_node_reclaim(c, b2)) {
+ swap(b->data, b2->data);
+ swap(b->aux_data, b2->aux_data);
+ btree_node_to_freedlist(bc, b2);
+ six_unlock_write(&b2->c.lock);
+ six_unlock_intent(&b2->c.lock);
+ goto got_mem;
+ }
- BUG_ON(!six_trylock_intent(&b->c.lock));
- BUG_ON(!six_trylock_write(&b->c.lock));
- }
+ mutex_unlock(&bc->lock);
- if (!b->data) {
- if (btree_node_data_alloc(c, b, __GFP_NOWARN|GFP_KERNEL))
- goto err;
+ if (btree_node_data_alloc(c, b, __GFP_NOWARN|GFP_KERNEL))
+ goto err;
- mutex_lock(&bc->lock);
- bc->used++;
- mutex_unlock(&bc->lock);
- }
+ mutex_lock(&bc->lock);
+ bc->used++;
+got_mem:
+ mutex_unlock(&bc->lock);
BUG_ON(btree_node_hashed(b));
BUG_ON(btree_node_dirty(b));
@@ -627,17 +648,21 @@ out:
return b;
err:
mutex_lock(&bc->lock);
-
- if (b) {
- list_add(&b->list, &bc->freed);
- six_unlock_write(&b->c.lock);
- six_unlock_intent(&b->c.lock);
- }
-
+err_locked:
/* Try to cannibalize another cached btree node: */
if (bc->alloc_lock == current) {
- b = btree_node_cannibalize(c);
- list_del_init(&b->list);
+ b2 = btree_node_cannibalize(c);
+ if (b) {
+ swap(b->data, b2->data);
+ swap(b->aux_data, b2->aux_data);
+ btree_node_to_freedlist(bc, b2);
+ six_unlock_write(&b2->c.lock);
+ six_unlock_intent(&b2->c.lock);
+ } else {
+ b = b2;
+ list_del_init(&b->list);
+ }
+
mutex_unlock(&bc->lock);
bch2_btree_node_hash_remove(bc, b);
@@ -677,7 +702,7 @@ static noinline struct btree *bch2_btree_node_fill(struct bch_fs *c,
return ERR_PTR(-EINTR);
}
- b = bch2_btree_node_mem_alloc(c);
+ b = bch2_btree_node_mem_alloc(c, level != 0);
if (trans && b == ERR_PTR(-ENOMEM)) {
trans->memory_allocation_failure = true;
diff --git a/libbcachefs/btree_cache.h b/libbcachefs/btree_cache.h
index 2901f0dc..25906127 100644
--- a/libbcachefs/btree_cache.h
+++ b/libbcachefs/btree_cache.h
@@ -22,7 +22,7 @@ void bch2_btree_cache_cannibalize_unlock(struct bch_fs *);
int bch2_btree_cache_cannibalize_lock(struct bch_fs *, struct closure *);
struct btree *__bch2_btree_node_mem_alloc(struct bch_fs *);
-struct btree *bch2_btree_node_mem_alloc(struct bch_fs *);
+struct btree *bch2_btree_node_mem_alloc(struct bch_fs *, bool);
struct btree *bch2_btree_node_get(struct btree_trans *, struct btree_path *,
const struct bkey_i *, unsigned,
diff --git a/libbcachefs/btree_io.c b/libbcachefs/btree_io.c
index 08f5f6b8..4f0ad06a 100644
--- a/libbcachefs/btree_io.c
+++ b/libbcachefs/btree_io.c
@@ -1546,7 +1546,7 @@ int bch2_btree_root_read(struct bch_fs *c, enum btree_id id,
closure_sync(&cl);
} while (ret);
- b = bch2_btree_node_mem_alloc(c);
+ b = bch2_btree_node_mem_alloc(c, level != 0);
bch2_btree_cache_cannibalize_unlock(c);
BUG_ON(IS_ERR(b));
diff --git a/libbcachefs/btree_key_cache.c b/libbcachefs/btree_key_cache.c
index 167d1771..ee89b650 100644
--- a/libbcachefs/btree_key_cache.c
+++ b/libbcachefs/btree_key_cache.c
@@ -165,13 +165,13 @@ btree_key_cache_create(struct bch_fs *c,
}
was_new = false;
+ } else {
+ if (btree_id == BTREE_ID_subvolumes)
+ six_lock_pcpu_alloc(&ck->c.lock);
+ else
+ six_lock_pcpu_free(&ck->c.lock);
}
- if (btree_id == BTREE_ID_subvolumes)
- six_lock_pcpu_alloc(&ck->c.lock);
- else
- six_lock_pcpu_free(&ck->c.lock);
-
ck->c.level = 0;
ck->c.btree_id = btree_id;
ck->key.btree_id = btree_id;
diff --git a/libbcachefs/btree_types.h b/libbcachefs/btree_types.h
index d87069c5..c18e388b 100644
--- a/libbcachefs/btree_types.h
+++ b/libbcachefs/btree_types.h
@@ -152,7 +152,8 @@ struct btree_cache {
struct mutex lock;
struct list_head live;
struct list_head freeable;
- struct list_head freed;
+ struct list_head freed_pcpu;
+ struct list_head freed_nonpcpu;
/* Number of elements in live + freeable lists */
unsigned used;
diff --git a/libbcachefs/btree_update_interior.c b/libbcachefs/btree_update_interior.c
index e2cf0f58..5834190d 100644
--- a/libbcachefs/btree_update_interior.c
+++ b/libbcachefs/btree_update_interior.c
@@ -181,6 +181,7 @@ static void bch2_btree_node_free_inmem(struct btree_trans *trans,
static struct btree *__bch2_btree_node_alloc(struct bch_fs *c,
struct disk_reservation *res,
struct closure *cl,
+ bool interior_node,
unsigned flags)
{
struct write_point *wp;
@@ -242,7 +243,7 @@ retry:
bch2_open_bucket_get(c, wp, &ob);
bch2_alloc_sectors_done(c, wp);
mem_alloc:
- b = bch2_btree_node_mem_alloc(c);
+ b = bch2_btree_node_mem_alloc(c, interior_node);
six_unlock_write(&b->c.lock);
six_unlock_intent(&b->c.lock);
@@ -260,12 +261,13 @@ static struct btree *bch2_btree_node_alloc(struct btree_update *as, unsigned lev
{
struct bch_fs *c = as->c;
struct btree *b;
+ struct prealloc_nodes *p = &as->prealloc_nodes[!!level];
int ret;
BUG_ON(level >= BTREE_MAX_DEPTH);
- BUG_ON(!as->nr_prealloc_nodes);
+ BUG_ON(!p->nr);
- b = as->prealloc_nodes[--as->nr_prealloc_nodes];
+ b = p->b[--p->nr];
six_lock_intent(&b->c.lock, NULL, NULL);
six_lock_write(&b->c.lock, NULL, NULL);
@@ -377,47 +379,54 @@ static struct btree *__btree_root_alloc(struct btree_update *as, unsigned level)
static void bch2_btree_reserve_put(struct btree_update *as)
{
struct bch_fs *c = as->c;
+ struct prealloc_nodes *p;
mutex_lock(&c->btree_reserve_cache_lock);
- while (as->nr_prealloc_nodes) {
- struct btree *b = as->prealloc_nodes[--as->nr_prealloc_nodes];
+ for (p = as->prealloc_nodes;
+ p < as->prealloc_nodes + ARRAY_SIZE(as->prealloc_nodes);
+ p++) {
+ while (p->nr) {
+ struct btree *b = p->b[--p->nr];
- six_lock_intent(&b->c.lock, NULL, NULL);
- six_lock_write(&b->c.lock, NULL, NULL);
+ six_lock_intent(&b->c.lock, NULL, NULL);
+ six_lock_write(&b->c.lock, NULL, NULL);
- if (c->btree_reserve_cache_nr <
- ARRAY_SIZE(c->btree_reserve_cache)) {
- struct btree_alloc *a =
- &c->btree_reserve_cache[c->btree_reserve_cache_nr++];
+ if (c->btree_reserve_cache_nr <
+ ARRAY_SIZE(c->btree_reserve_cache)) {
+ struct btree_alloc *a =
+ &c->btree_reserve_cache[c->btree_reserve_cache_nr++];
- a->ob = b->ob;
- b->ob.nr = 0;
- bkey_copy(&a->k, &b->key);
- } else {
- bch2_open_buckets_put(c, &b->ob);
- }
+ a->ob = b->ob;
+ b->ob.nr = 0;
+ bkey_copy(&a->k, &b->key);
+ } else {
+ bch2_open_buckets_put(c, &b->ob);
+ }
- __btree_node_free(c, b);
- six_unlock_write(&b->c.lock);
- six_unlock_intent(&b->c.lock);
+ __btree_node_free(c, b);
+ six_unlock_write(&b->c.lock);
+ six_unlock_intent(&b->c.lock);
+ }
}
mutex_unlock(&c->btree_reserve_cache_lock);
}
-static int bch2_btree_reserve_get(struct btree_update *as, unsigned nr_nodes,
+static int bch2_btree_reserve_get(struct btree_update *as,
+ unsigned nr_nodes[2],
unsigned flags)
{
struct bch_fs *c = as->c;
struct closure cl;
struct btree *b;
+ unsigned interior;
int ret;
closure_init_stack(&cl);
retry:
- BUG_ON(nr_nodes > BTREE_RESERVE_MAX);
+ BUG_ON(nr_nodes[0] + nr_nodes[1] > BTREE_RESERVE_MAX);
/*
* Protects reaping from the btree node cache and using the btree node
@@ -430,16 +439,21 @@ retry:
if (ret)
goto err;
- while (as->nr_prealloc_nodes < nr_nodes) {
- b = __bch2_btree_node_alloc(c, &as->disk_res,
- flags & BTREE_INSERT_NOWAIT
- ? NULL : &cl, flags);
- if (IS_ERR(b)) {
- ret = PTR_ERR(b);
- goto err;
- }
+ for (interior = 0; interior < 2; interior++) {
+ struct prealloc_nodes *p = as->prealloc_nodes + interior;
+
+ while (p->nr < nr_nodes[interior]) {
+ b = __bch2_btree_node_alloc(c, &as->disk_res,
+ flags & BTREE_INSERT_NOWAIT
+ ? NULL : &cl,
+ interior, flags);
+ if (IS_ERR(b)) {
+ ret = PTR_ERR(b);
+ goto err;
+ }
- as->prealloc_nodes[as->nr_prealloc_nodes++] = b;
+ p->b[p->nr++] = b;
+ }
}
bch2_btree_cache_cannibalize_unlock(c);
@@ -452,7 +466,7 @@ err:
if (ret == -EAGAIN)
goto retry;
- trace_btree_reserve_get_fail(c, nr_nodes, &cl);
+ trace_btree_reserve_get_fail(c, nr_nodes[0] + nr_nodes[1], &cl);
return ret;
}
@@ -947,13 +961,15 @@ static void bch2_btree_update_done(struct btree_update *as)
static struct btree_update *
bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
- unsigned level, unsigned nr_nodes, unsigned flags)
+ unsigned level, bool split, unsigned flags)
{
struct bch_fs *c = trans->c;
struct btree_update *as;
u64 start_time = local_clock();
int disk_res_flags = (flags & BTREE_INSERT_NOFAIL)
? BCH_DISK_RESERVATION_NOFAIL : 0;
+ unsigned nr_nodes[2] = { 0, 0 };
+ unsigned update_level = level;
int journal_flags = 0;
int ret = 0;
@@ -964,10 +980,24 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
if (flags & BTREE_INSERT_JOURNAL_RECLAIM)
journal_flags |= JOURNAL_RES_GET_NONBLOCK;
- /*
- * XXX: figure out how far we might need to split,
- * instead of locking/reserving all the way to the root:
- */
+ while (1) {
+ nr_nodes[!!update_level] += 1 + split;
+ update_level++;
+
+ if (!btree_path_node(path, update_level))
+ break;
+
+ /*
+ * XXX: figure out how far we might need to split,
+ * instead of locking/reserving all the way to the root:
+ */
+ split = update_level + 1 < BTREE_MAX_DEPTH;
+ }
+
+ /* Might have to allocate a new root: */
+ if (update_level < BTREE_MAX_DEPTH)
+ nr_nodes[1] += 1;
+
if (!bch2_btree_path_upgrade(trans, path, U8_MAX)) {
trace_trans_restart_iter_upgrade(trans->fn, _RET_IP_,
path->btree_id, &path->pos);
@@ -1030,7 +1060,7 @@ bch2_btree_update_start(struct btree_trans *trans, struct btree_path *path,
}
ret = bch2_disk_reservation_get(c, &as->disk_res,
- nr_nodes * btree_sectors(c),
+ (nr_nodes[0] + nr_nodes[1]) * btree_sectors(c),
c->opts.metadata_replicas,
disk_res_flags);
if (ret)
@@ -1551,14 +1581,13 @@ int bch2_btree_split_leaf(struct btree_trans *trans,
struct btree_path *path,
unsigned flags)
{
- struct bch_fs *c = trans->c;
struct btree *b = path_l(path)->b;
struct btree_update *as;
unsigned l;
int ret = 0;
as = bch2_btree_update_start(trans, path, path->level,
- btree_update_reserve_required(c, b), flags);
+ true, flags);
if (IS_ERR(as))
return PTR_ERR(as);
@@ -1669,11 +1698,10 @@ int __bch2_foreground_maybe_merge(struct btree_trans *trans,
goto out;
parent = btree_node_parent(path, b);
- as = bch2_btree_update_start(trans, path, level,
- btree_update_reserve_required(c, parent) + 1,
- flags|
+ as = bch2_btree_update_start(trans, path, level, false,
BTREE_INSERT_NOFAIL|
- BTREE_INSERT_USE_RESERVE);
+ BTREE_INSERT_USE_RESERVE|
+ flags);
ret = PTR_ERR_OR_ZERO(as);
if (ret)
goto err;
@@ -1756,10 +1784,7 @@ int bch2_btree_node_rewrite(struct btree_trans *trans,
parent = btree_node_parent(iter->path, b);
as = bch2_btree_update_start(trans, iter->path, b->c.level,
- (parent
- ? btree_update_reserve_required(c, parent)
- : 0) + 1,
- flags);
+ false, flags);
ret = PTR_ERR_OR_ZERO(as);
if (ret) {
trace_btree_gc_rewrite_node_fail(c, b);
@@ -1996,7 +2021,7 @@ int bch2_btree_node_update_key(struct btree_trans *trans, struct btree_iter *ite
return -EINTR;
}
- new_hash = bch2_btree_node_mem_alloc(c);
+ new_hash = bch2_btree_node_mem_alloc(c, false);
}
path->intent_ref++;
@@ -2072,7 +2097,7 @@ void bch2_btree_root_alloc(struct bch_fs *c, enum btree_id id)
closure_sync(&cl);
} while (ret);
- b = bch2_btree_node_mem_alloc(c);
+ b = bch2_btree_node_mem_alloc(c, false);
bch2_btree_cache_cannibalize_unlock(c);
set_btree_node_fake(b);
diff --git a/libbcachefs/btree_update_interior.h b/libbcachefs/btree_update_interior.h
index 8dc86fa6..e72eb879 100644
--- a/libbcachefs/btree_update_interior.h
+++ b/libbcachefs/btree_update_interior.h
@@ -76,8 +76,10 @@ struct btree_update {
struct journal_entry_pin journal;
/* Preallocated nodes we reserve when we start the update: */
- struct btree *prealloc_nodes[BTREE_UPDATE_NODES_MAX];
- unsigned nr_prealloc_nodes;
+ struct prealloc_nodes {
+ struct btree *b[BTREE_UPDATE_NODES_MAX];
+ unsigned nr;
+ } prealloc_nodes[2];
/* Nodes being freed: */
struct keylist old_keys;
diff --git a/libbcachefs/fs.c b/libbcachefs/fs.c
index 31dbd4c6..f6e96132 100644
--- a/libbcachefs/fs.c
+++ b/libbcachefs/fs.c
@@ -980,6 +980,9 @@ retry:
bch2_btree_iter_set_pos(&iter,
POS(iter.pos.inode, iter.pos.offset + sectors));
+
+ if (btree_trans_too_many_iters(&trans))
+ goto retry;
}
start = iter.pos.offset;
bch2_trans_iter_exit(&trans, &iter);
diff --git a/libbcachefs/opts.c b/libbcachefs/opts.c
index 71bf26eb..15acbabc 100644
--- a/libbcachefs/opts.c
+++ b/libbcachefs/opts.c
@@ -96,6 +96,16 @@ const char * const bch2_d_types[BCH_DT_MAX] = {
[DT_SUBVOL] = "subvol",
};
+u64 BCH2_NO_SB_OPT(const struct bch_sb *sb)
+{
+ BUG();
+}
+
+void SET_BCH2_NO_SB_OPT(struct bch_sb *sb, u64 v)
+{
+ BUG();
+}
+
void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src)
{
#define x(_name, ...) \
@@ -431,6 +441,22 @@ out:
return ret;
}
+u64 bch2_opt_from_sb(struct bch_sb *sb, enum bch_opt_id id)
+{
+ const struct bch_option *opt = bch2_opt_table + id;
+ u64 v;
+
+ v = opt->get_sb(sb);
+
+ if (opt->flags & OPT_SB_FIELD_ILOG2)
+ v = 1ULL << v;
+
+ if (opt->flags & OPT_SB_FIELD_SECTORS)
+ v <<= 9;
+
+ return v;
+}
+
/*
* Initial options from superblock - here we don't want any options undefined,
* any options the superblock doesn't specify are set to 0:
@@ -444,16 +470,10 @@ int bch2_opts_from_sb(struct bch_opts *opts, struct bch_sb *sb)
const struct bch_option *opt = bch2_opt_table + id;
u64 v;
- if (opt->get_sb == NO_SB_OPT)
+ if (opt->get_sb == BCH2_NO_SB_OPT)
continue;
- v = opt->get_sb(sb);
-
- if (opt->flags & OPT_SB_FIELD_ILOG2)
- v = 1ULL << v;
-
- if (opt->flags & OPT_SB_FIELD_SECTORS)
- v <<= 9;
+ v = bch2_opt_from_sb(sb, id);
ret = bch2_opt_validate(opt, "superblock option ", v);
if (ret)
@@ -467,7 +487,7 @@ int bch2_opts_from_sb(struct bch_opts *opts, struct bch_sb *sb)
void __bch2_opt_set_sb(struct bch_sb *sb, const struct bch_option *opt, u64 v)
{
- if (opt->set_sb == SET_NO_SB_OPT)
+ if (opt->set_sb == SET_BCH2_NO_SB_OPT)
return;
if (opt->flags & OPT_SB_FIELD_SECTORS)
@@ -481,7 +501,7 @@ void __bch2_opt_set_sb(struct bch_sb *sb, const struct bch_option *opt, u64 v)
void bch2_opt_set_sb(struct bch_fs *c, const struct bch_option *opt, u64 v)
{
- if (opt->set_sb == SET_NO_SB_OPT)
+ if (opt->set_sb == SET_BCH2_NO_SB_OPT)
return;
mutex_lock(&c->sb_lock);
diff --git a/libbcachefs/opts.h b/libbcachefs/opts.h
index bafacf6b..9e68f100 100644
--- a/libbcachefs/opts.h
+++ b/libbcachefs/opts.h
@@ -42,7 +42,8 @@ static inline const char *bch2_d_type_str(unsigned d_type)
*/
/* dummy option, for options that aren't stored in the superblock */
-LE64_BITMASK(NO_SB_OPT, struct bch_sb, flags[0], 0, 0);
+u64 BCH2_NO_SB_OPT(const struct bch_sb *);
+void SET_BCH2_NO_SB_OPT(struct bch_sb *, u64);
/* When can be set: */
enum opt_flags {
@@ -202,7 +203,7 @@ enum opt_type {
x(btree_node_mem_ptr_optimization, u8, \
OPT_FS|OPT_MOUNT|OPT_RUNTIME, \
OPT_BOOL(), \
- NO_SB_OPT, true, \
+ BCH2_NO_SB_OPT, true, \
NULL, "Stash pointer to in memory btree node in btree ptr")\
x(gc_reserve_percent, u8, \
OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME, \
@@ -229,7 +230,7 @@ enum opt_type {
x(inline_data, u8, \
OPT_FS|OPT_MOUNT|OPT_RUNTIME, \
OPT_BOOL(), \
- NO_SB_OPT, true, \
+ BCH2_NO_SB_OPT, true, \
NULL, "Enable inline data extents") \
x(acl, u8, \
OPT_FS|OPT_FORMAT|OPT_MOUNT, \
@@ -254,22 +255,22 @@ enum opt_type {
x(degraded, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Allow mounting in degraded mode") \
x(very_degraded, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Allow mounting in when data will be missing") \
x(discard, u8, \
OPT_FS|OPT_MOUNT|OPT_DEVICE, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Enable discard/TRIM support") \
x(verbose, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Extra debugging information during mount/recovery")\
x(journal_flush_delay, u32, \
OPT_FS|OPT_MOUNT|OPT_RUNTIME, \
@@ -291,48 +292,48 @@ enum opt_type {
x(fsck, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Run fsck on mount") \
x(fix_errors, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Fix errors during fsck without asking") \
x(ratelimit_errors, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, RATELIMIT_ERRORS_DEFAULT, \
+ BCH2_NO_SB_OPT, RATELIMIT_ERRORS_DEFAULT, \
NULL, "Ratelimit error messages during fsck") \
x(nochanges, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Super read only mode - no writes at all will be issued,\n"\
"even if we have to replay the journal") \
x(norecovery, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Don't replay the journal") \
x(rebuild_replicas, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Rebuild the superblock replicas section") \
x(keep_journal, u8, \
0, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Don't free journal entries/keys after startup")\
x(read_entire_journal, u8, \
0, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Read all journal entries, not just dirty ones")\
x(read_journal_only, u8, \
0, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Only read the journal, skip the rest of recovery")\
x(journal_transaction_names, u8, \
OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME, \
@@ -342,58 +343,58 @@ enum opt_type {
x(noexcl, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Don't open device in exclusive mode") \
x(sb, u64, \
OPT_MOUNT, \
OPT_UINT(0, S64_MAX), \
- NO_SB_OPT, BCH_SB_SECTOR, \
+ BCH2_NO_SB_OPT, BCH_SB_SECTOR, \
"offset", "Sector offset of superblock") \
x(read_only, u8, \
OPT_FS, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, NULL) \
x(nostart, u8, \
0, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Don\'t start filesystem, only open devices") \
x(reconstruct_alloc, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Reconstruct alloc btree") \
x(version_upgrade, u8, \
OPT_FS|OPT_MOUNT, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Set superblock to latest version,\n" \
"allowing any new features to be used") \
x(buckets_nouse, u8, \
0, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, "Allocate the buckets_nouse bitmap") \
x(project, u8, \
OPT_INODE, \
OPT_BOOL(), \
- NO_SB_OPT, false, \
+ BCH2_NO_SB_OPT, false, \
NULL, NULL) \
x(fs_size, u64, \
OPT_DEVICE, \
OPT_UINT(0, S64_MAX), \
- NO_SB_OPT, 0, \
+ BCH2_NO_SB_OPT, 0, \
"size", "Size of filesystem on device") \
x(bucket, u32, \
OPT_DEVICE, \
OPT_UINT(0, S64_MAX), \
- NO_SB_OPT, 0, \
+ BCH2_NO_SB_OPT, 0, \
"size", "Size of filesystem on device") \
x(durability, u8, \
OPT_DEVICE, \
OPT_UINT(0, BCH_REPLICAS_MAX), \
- NO_SB_OPT, 1, \
+ BCH2_NO_SB_OPT, 1, \
"n", "Data written to this device will be considered\n"\
"to have already been replicated n times")
@@ -475,6 +476,7 @@ bool bch2_opt_defined_by_id(const struct bch_opts *, enum bch_opt_id);
u64 bch2_opt_get_by_id(const struct bch_opts *, enum bch_opt_id);
void bch2_opt_set_by_id(struct bch_opts *, enum bch_opt_id, u64);
+u64 bch2_opt_from_sb(struct bch_sb *, enum bch_opt_id);
int bch2_opts_from_sb(struct bch_opts *, struct bch_sb *);
void __bch2_opt_set_sb(struct bch_sb *, const struct bch_option *, u64);
void bch2_opt_set_sb(struct bch_fs *, const struct bch_option *, u64);
diff --git a/libbcachefs/super-io.c b/libbcachefs/super-io.c
index 1a70adae..d87edaad 100644
--- a/libbcachefs/super-io.c
+++ b/libbcachefs/super-io.c
@@ -1045,45 +1045,56 @@ static void bch2_sb_members_to_text(struct printbuf *out, struct bch_sb *sb,
if (!bch2_member_exists(m))
continue;
- pr_buf(out, "Device: %u", i);
+ pr_buf(out, "Device:");
+ pr_tab(out);
+ pr_buf(out, "%u", i);
pr_newline(out);
pr_indent_push(out, 2);
- pr_buf(out, "UUID: ");
+ pr_buf(out, "UUID:");
+ pr_tab(out);
pr_uuid(out, m->uuid.b);
pr_newline(out);
- pr_buf(out, "Size: ");
+ pr_buf(out, "Size:");
+ pr_tab(out);
pr_units(out, device_size, device_size << 9);
pr_newline(out);
- pr_buf(out, "Bucket size: ");
+ pr_buf(out, "Bucket size:");
+ pr_tab(out);
pr_units(out, bucket_size, bucket_size << 9);
pr_newline(out);
- pr_buf(out, "First bucket: %u",
- le16_to_cpu(m->first_bucket));
+ pr_buf(out, "First bucket:");
+ pr_tab(out);
+ pr_buf(out, "%u", le16_to_cpu(m->first_bucket));
pr_newline(out);
- pr_buf(out, "Buckets: %llu",
- le64_to_cpu(m->nbuckets));
+ pr_buf(out, "Buckets:");
+ pr_tab(out);
+ pr_buf(out, "%llu", le64_to_cpu(m->nbuckets));
pr_newline(out);
- pr_buf(out, "Last mount: ");
+ pr_buf(out, "Last mount:");
+ pr_tab(out);
if (m->last_mount)
pr_time(out, le64_to_cpu(m->last_mount));
else
pr_buf(out, "(never)");
pr_newline(out);
- pr_buf(out, "State: %s",
+ pr_buf(out, "State:");
+ pr_tab(out);
+ pr_buf(out, "%s",
BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR
? bch2_member_states[BCH_MEMBER_STATE(m)]
: "unknown");
pr_newline(out);
- pr_buf(out, "Group: ");
+ pr_buf(out, "Group:");
+ pr_tab(out);
if (BCH_MEMBER_GROUP(m)) {
unsigned idx = BCH_MEMBER_GROUP(m) - 1;
@@ -1097,7 +1108,8 @@ static void bch2_sb_members_to_text(struct printbuf *out, struct bch_sb *sb,
}
pr_newline(out);
- pr_buf(out, "Data allowed: ");
+ pr_buf(out, "Data allowed:");
+ pr_tab(out);
if (BCH_MEMBER_DATA_ALLOWED(m))
bch2_flags_to_text(out, bch2_data_types,
BCH_MEMBER_DATA_ALLOWED(m));
@@ -1105,15 +1117,17 @@ static void bch2_sb_members_to_text(struct printbuf *out, struct bch_sb *sb,
pr_buf(out, "(none)");
pr_newline(out);
- pr_buf(out, "Has data: ");
+ pr_buf(out, "Has data:");
+ pr_tab(out);
if (data_have)
bch2_flags_to_text(out, bch2_data_types, data_have);
else
pr_buf(out, "(none)");
pr_newline(out);
- pr_buf(out, "Discard: %llu",
- BCH_MEMBER_DISCARD(m));
+ pr_buf(out, "Discard:");
+ pr_tab(out);
+ pr_buf(out, "%llu", BCH_MEMBER_DISCARD(m));
pr_newline(out);
pr_indent_pop(out, 2);
@@ -1449,6 +1463,9 @@ void bch2_sb_field_to_text(struct printbuf *out, struct bch_sb *sb,
const struct bch_sb_field_ops *ops = type < BCH_SB_FIELD_NR
? bch2_sb_field_ops[type] : NULL;
+ if (!out->tabstops[0])
+ out->tabstops[0] = 32;
+
if (ops)
pr_buf(out, "%s", bch2_sb_fields[type]);
else
@@ -1497,6 +1514,9 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb,
u64 fields_have = 0;
unsigned nr_devices = 0;
+ if (!out->tabstops[0])
+ out->tabstops[0] = 32;
+
mi = bch2_sb_get_members(sb);
if (mi) {
struct bch_member *m;
@@ -1507,137 +1527,105 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb,
nr_devices += bch2_member_exists(m);
}
- pr_buf(out, "External UUID: ");
+ pr_buf(out, "External UUID:");
+ pr_tab(out);
pr_uuid(out, sb->user_uuid.b);
pr_newline(out);
- pr_buf(out, "Internal UUID: ");
+ pr_buf(out, "Internal UUID:");
+ pr_tab(out);
pr_uuid(out, sb->uuid.b);
pr_newline(out);
- pr_buf(out, "Device index: %u", sb->dev_idx);
+ pr_buf(out, "Device index:");
+ pr_tab(out);
+ pr_buf(out, "%u", sb->dev_idx);
pr_newline(out);
- pr_buf(out, "Label: ");
+ pr_buf(out, "Label:");
+ pr_tab(out);
pr_buf(out, "%.*s", (int) sizeof(sb->label), sb->label);
pr_newline(out);
- pr_buf(out, "Version: %u", le16_to_cpu(sb->version));
+ pr_buf(out, "Version:");
+ pr_tab(out);
+ pr_buf(out, "%u", le16_to_cpu(sb->version));
pr_newline(out);
- pr_buf(out, "Oldest version on disk: %u", le16_to_cpu(sb->version_min));
+ pr_buf(out, "Oldest version on disk:");
+ pr_tab(out);
+ pr_buf(out, "%u", le16_to_cpu(sb->version_min));
pr_newline(out);
- pr_buf(out, "Created: ");
+ pr_buf(out, "Created:");
+ pr_tab(out);
if (sb->time_base_lo)
pr_time(out, div_u64(le64_to_cpu(sb->time_base_lo), NSEC_PER_SEC));
else
pr_buf(out, "(not set)");
pr_newline(out);
- pr_buf(out, "Squence number: %llu", le64_to_cpu(sb->seq));
+ pr_buf(out, "Sequence number:");
+ pr_tab(out);
+ pr_buf(out, "%llu", le64_to_cpu(sb->seq));
pr_newline(out);
- pr_buf(out, "Block_size: ");
- pr_units(out, le16_to_cpu(sb->block_size),
- (u32) le16_to_cpu(sb->block_size) << 9);
+ pr_buf(out, "Superblock size:");
+ pr_tab(out);
+ pr_buf(out, "%zu", vstruct_bytes(sb));
pr_newline(out);
- pr_buf(out, "Btree node size: ");
- pr_units(out, BCH_SB_BTREE_NODE_SIZE(sb),
- BCH_SB_BTREE_NODE_SIZE(sb) << 9);
+ pr_buf(out, "Clean:");
+ pr_tab(out);
+ pr_buf(out, "%llu", BCH_SB_CLEAN(sb));
pr_newline(out);
- pr_buf(out, "Error action: %s",
- BCH_SB_ERROR_ACTION(sb) < BCH_ON_ERROR_NR
- ? bch2_error_actions[BCH_SB_ERROR_ACTION(sb)]
- : "unknown");
+ pr_buf(out, "Devices:");
+ pr_tab(out);
+ pr_buf(out, "%u", nr_devices);
pr_newline(out);
- pr_buf(out, "Clean: %llu", BCH_SB_CLEAN(sb));
+ pr_buf(out, "Sections:");
+ vstruct_for_each(sb, f)
+ fields_have |= 1 << le32_to_cpu(f->type);
+ pr_tab(out);
+ bch2_flags_to_text(out, bch2_sb_fields, fields_have);
pr_newline(out);
- pr_buf(out, "Features: ");
+ pr_buf(out, "Features:");
+ pr_tab(out);
bch2_flags_to_text(out, bch2_sb_features,
le64_to_cpu(sb->features[0]));
pr_newline(out);
- pr_buf(out, "Compat features: ");
+ pr_buf(out, "Compat features:");
+ pr_tab(out);
bch2_flags_to_text(out, bch2_sb_compat,
le64_to_cpu(sb->compat[0]));
pr_newline(out);
- pr_buf(out, "Metadata replicas: %llu", BCH_SB_META_REPLICAS_WANT(sb));
pr_newline(out);
-
- pr_buf(out, "Data replicas: %llu", BCH_SB_DATA_REPLICAS_WANT(sb));
- pr_newline(out);
-
- pr_buf(out, "Metadata checksum type: %s (%llu)",
- BCH_SB_META_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
- ? bch2_csum_opts[BCH_SB_META_CSUM_TYPE(sb)]
- : "unknown",
- BCH_SB_META_CSUM_TYPE(sb));
- pr_newline(out);
-
- pr_buf(out, "Data checksum type: %s (%llu)",
- BCH_SB_DATA_CSUM_TYPE(sb) < BCH_CSUM_OPT_NR
- ? bch2_csum_opts[BCH_SB_DATA_CSUM_TYPE(sb)]
- : "unknown",
- BCH_SB_DATA_CSUM_TYPE(sb));
- pr_newline(out);
-
- pr_buf(out, "Compression type: %s (%llu)",
- BCH_SB_COMPRESSION_TYPE(sb) < BCH_COMPRESSION_OPT_NR
- ? bch2_compression_opts[BCH_SB_COMPRESSION_TYPE(sb)]
- : "unknown",
- BCH_SB_COMPRESSION_TYPE(sb));
- pr_newline(out);
-
- pr_buf(out, "Foreground write target: ");
- bch2_sb_target_to_text(out, sb, BCH_SB_FOREGROUND_TARGET(sb));
- pr_newline(out);
-
- pr_buf(out, "Background write target: ");
- bch2_sb_target_to_text(out, sb, BCH_SB_BACKGROUND_TARGET(sb));
- pr_newline(out);
-
- pr_buf(out, "Promote target: ");
- bch2_sb_target_to_text(out, sb, BCH_SB_PROMOTE_TARGET(sb));
- pr_newline(out);
-
- pr_buf(out, "Metadata target: ");
- bch2_sb_target_to_text(out, sb, BCH_SB_METADATA_TARGET(sb));
- pr_newline(out);
-
- pr_buf(out, "String hash type: %s (%llu)",
- BCH_SB_STR_HASH_TYPE(sb) < BCH_STR_HASH_NR
- ? bch2_str_hash_types[BCH_SB_STR_HASH_TYPE(sb)]
- : "unknown",
- BCH_SB_STR_HASH_TYPE(sb));
- pr_newline(out);
-
- pr_buf(out, "32 bit inodes: %llu", BCH_SB_INODE_32BIT(sb));
- pr_newline(out);
-
- pr_buf(out, "GC reserve percentage: %llu%%", BCH_SB_GC_RESERVE(sb));
+ pr_buf(out, "Options:");
pr_newline(out);
+ pr_indent_push(out, 2);
+ {
+ enum bch_opt_id id;
- pr_buf(out, "Root reserve percentage: %llu%%", BCH_SB_ROOT_RESERVE(sb));
- pr_newline(out);
+ for (id = 0; id < bch2_opts_nr; id++) {
+ const struct bch_option *opt = bch2_opt_table + id;
- pr_buf(out, "Devices: %u live, %u total",
- nr_devices, sb->nr_devices);
- pr_newline(out);
+ if (opt->get_sb != BCH2_NO_SB_OPT) {
+ u64 v = bch2_opt_from_sb(sb, id);
- pr_buf(out, "Sections: ");
- vstruct_for_each(sb, f)
- fields_have |= 1 << le32_to_cpu(f->type);
- bch2_flags_to_text(out, bch2_sb_fields, fields_have);
- pr_newline(out);
+ pr_buf(out, "%s:", opt->attr.name);
+ pr_tab(out);
+ bch2_opt_to_text(out, NULL, opt, v, OPT_HUMAN_READABLE|OPT_SHOW_FULL_LIST);
+ pr_newline(out);
+ }
+ }
+ }
- pr_buf(out, "Superblock size: %zu", vstruct_bytes(sb));
- pr_newline(out);
+ pr_indent_pop(out, 2);
if (print_layout) {
pr_newline(out);
diff --git a/libbcachefs/super.c b/libbcachefs/super.c
index 9af8eb35..fb7f8d6d 100644
--- a/libbcachefs/super.c
+++ b/libbcachefs/super.c
@@ -1162,9 +1162,6 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
ca->mi = bch2_mi_to_cpu(member);
ca->uuid = member->uuid;
- if (opt_defined(c->opts, discard))
- ca->mi.discard = opt_get(c->opts, discard);
-
if (percpu_ref_init(&ca->ref, bch2_dev_ref_complete,
0, GFP_KERNEL) ||
percpu_ref_init(&ca->io_ref, bch2_dev_io_ref_complete,