2017-03-17 06:18:50 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Code for manipulating bucket marks for garbage collection.
|
|
|
|
*
|
|
|
|
* Copyright 2014 Datera, Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "bcachefs.h"
|
2018-10-06 04:46:55 +00:00
|
|
|
#include "alloc_background.h"
|
2022-03-18 00:51:27 +00:00
|
|
|
#include "backpointers.h"
|
2018-11-05 07:31:48 +00:00
|
|
|
#include "bset.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "btree_gc.h"
|
2018-11-05 07:31:48 +00:00
|
|
|
#include "btree_update.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "buckets.h"
|
2022-01-05 03:32:09 +00:00
|
|
|
#include "buckets_waiting_for_journal.h"
|
2018-11-01 19:13:19 +00:00
|
|
|
#include "ec.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "error.h"
|
2021-10-30 01:14:23 +00:00
|
|
|
#include "inode.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "movinggc.h"
|
2021-10-19 16:27:47 +00:00
|
|
|
#include "recovery.h"
|
2021-05-23 06:31:33 +00:00
|
|
|
#include "reflink.h"
|
2019-01-21 20:32:13 +00:00
|
|
|
#include "replicas.h"
|
2021-03-16 04:42:25 +00:00
|
|
|
#include "subvolume.h"
|
2017-03-17 06:18:50 +00:00
|
|
|
#include "trace.h"
|
|
|
|
|
|
|
|
#include <linux/preempt.h>
|
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
static inline void fs_usage_data_type_to_base(struct bch_fs_usage_base *fs_usage,
|
2020-07-09 22:31:51 +00:00
|
|
|
enum bch_data_type data_type,
|
|
|
|
s64 sectors)
|
|
|
|
{
|
|
|
|
switch (data_type) {
|
|
|
|
case BCH_DATA_btree:
|
|
|
|
fs_usage->btree += sectors;
|
|
|
|
break;
|
|
|
|
case BCH_DATA_user:
|
|
|
|
case BCH_DATA_parity:
|
|
|
|
fs_usage->data += sectors;
|
|
|
|
break;
|
|
|
|
case BCH_DATA_cached:
|
|
|
|
fs_usage->cached += sectors;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 01:25:40 +00:00
|
|
|
void bch2_fs_usage_initialize(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
percpu_down_write(&c->mark_lock);
|
2023-12-17 04:47:29 +00:00
|
|
|
struct bch_fs_usage *usage = c->usage_base;
|
2019-02-11 00:34:47 +00:00
|
|
|
|
2023-12-17 04:47:29 +00:00
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(c->usage); i++)
|
2019-02-11 00:34:47 +00:00
|
|
|
bch2_fs_usage_acc_to_base(c, i);
|
2019-01-25 01:25:40 +00:00
|
|
|
|
2023-12-17 04:47:29 +00:00
|
|
|
for (unsigned i = 0; i < BCH_REPLICAS_MAX; i++)
|
2023-11-17 05:03:45 +00:00
|
|
|
usage->b.reserved += usage->persistent_reserved[i];
|
2019-02-10 00:20:57 +00:00
|
|
|
|
2023-12-17 04:47:29 +00:00
|
|
|
for (unsigned i = 0; i < c->replicas.nr; i++) {
|
2023-11-09 18:52:35 +00:00
|
|
|
struct bch_replicas_entry_v1 *e =
|
2019-01-25 01:25:40 +00:00
|
|
|
cpu_replicas_entry(&c->replicas, i);
|
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage_data_type_to_base(&usage->b, e->data_type, usage->replicas[i]);
|
2019-01-25 01:25:40 +00:00
|
|
|
}
|
|
|
|
|
2023-12-17 04:47:29 +00:00
|
|
|
for_each_member_device(c, ca) {
|
2021-01-22 02:52:06 +00:00
|
|
|
struct bch_dev_usage dev = bch2_dev_usage_read(ca);
|
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
usage->b.hidden += (dev.d[BCH_DATA_sb].buckets +
|
|
|
|
dev.d[BCH_DATA_journal].buckets) *
|
2021-01-22 02:52:06 +00:00
|
|
|
ca->mi.bucket_size;
|
|
|
|
}
|
|
|
|
|
2019-01-25 01:25:40 +00:00
|
|
|
percpu_up_write(&c->mark_lock);
|
|
|
|
}
|
|
|
|
|
2021-01-22 02:52:06 +00:00
|
|
|
static inline struct bch_dev_usage *dev_usage_ptr(struct bch_dev *ca,
|
|
|
|
unsigned journal_seq,
|
|
|
|
bool gc)
|
|
|
|
{
|
2021-11-15 20:02:13 +00:00
|
|
|
BUG_ON(!gc && !journal_seq);
|
|
|
|
|
2021-01-22 02:52:06 +00:00
|
|
|
return this_cpu_ptr(gc
|
|
|
|
? ca->usage_gc
|
|
|
|
: ca->usage[journal_seq & JOURNAL_BUF_MASK]);
|
|
|
|
}
|
|
|
|
|
2022-10-21 18:01:19 +00:00
|
|
|
void bch2_dev_usage_read_fast(struct bch_dev *ca, struct bch_dev_usage *usage)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-01-22 02:52:06 +00:00
|
|
|
struct bch_fs *c = ca->fs;
|
|
|
|
unsigned seq, i, u64s = dev_usage_u64s();
|
2019-02-14 23:38:52 +00:00
|
|
|
|
2021-01-22 02:52:06 +00:00
|
|
|
do {
|
|
|
|
seq = read_seqcount_begin(&c->usage_lock);
|
2022-10-21 18:01:19 +00:00
|
|
|
memcpy(usage, ca->usage_base, u64s * sizeof(u64));
|
2021-01-22 02:52:06 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(ca->usage); i++)
|
2022-10-21 18:01:19 +00:00
|
|
|
acc_u64s_percpu((u64 *) usage, (u64 __percpu *) ca->usage[i], u64s);
|
2021-01-22 02:52:06 +00:00
|
|
|
} while (read_seqcount_retry(&c->usage_lock, seq));
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 00:34:47 +00:00
|
|
|
u64 bch2_fs_usage_read_one(struct bch_fs *c, u64 *v)
|
|
|
|
{
|
|
|
|
ssize_t offset = v - (u64 *) c->usage_base;
|
2020-11-13 23:36:33 +00:00
|
|
|
unsigned i, seq;
|
2019-02-11 00:34:47 +00:00
|
|
|
u64 ret;
|
|
|
|
|
|
|
|
BUG_ON(offset < 0 || offset >= fs_usage_u64s(c));
|
|
|
|
percpu_rwsem_assert_held(&c->mark_lock);
|
|
|
|
|
|
|
|
do {
|
|
|
|
seq = read_seqcount_begin(&c->usage_lock);
|
2020-11-13 23:36:33 +00:00
|
|
|
ret = *v;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(c->usage); i++)
|
|
|
|
ret += percpu_u64_get((u64 __percpu *) c->usage[i] + offset);
|
2019-02-11 00:34:47 +00:00
|
|
|
} while (read_seqcount_retry(&c->usage_lock, seq));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct bch_fs_usage_online *bch2_fs_usage_read(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
struct bch_fs_usage_online *ret;
|
2023-05-22 18:39:44 +00:00
|
|
|
unsigned nr_replicas = READ_ONCE(c->replicas.nr);
|
|
|
|
unsigned seq, i;
|
2020-11-13 23:36:33 +00:00
|
|
|
retry:
|
2023-05-28 23:23:35 +00:00
|
|
|
ret = kmalloc(__fs_usage_online_u64s(nr_replicas) * sizeof(u64), GFP_KERNEL);
|
2020-11-13 23:36:33 +00:00
|
|
|
if (unlikely(!ret))
|
|
|
|
return NULL;
|
2019-01-21 20:32:13 +00:00
|
|
|
|
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
|
2023-05-22 18:39:44 +00:00
|
|
|
if (nr_replicas != c->replicas.nr) {
|
|
|
|
nr_replicas = c->replicas.nr;
|
2019-01-21 20:32:13 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
2020-11-13 23:36:33 +00:00
|
|
|
kfree(ret);
|
|
|
|
goto retry;
|
2019-01-21 20:32:13 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 00:34:47 +00:00
|
|
|
ret->online_reserved = percpu_u64_get(c->online_reserved);
|
|
|
|
|
|
|
|
do {
|
|
|
|
seq = read_seqcount_begin(&c->usage_lock);
|
2023-05-22 18:39:44 +00:00
|
|
|
unsafe_memcpy(&ret->u, c->usage_base,
|
|
|
|
__fs_usage_u64s(nr_replicas) * sizeof(u64),
|
2022-12-28 20:17:07 +00:00
|
|
|
"embedded variable length struct");
|
2019-02-11 00:34:47 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(c->usage); i++)
|
2023-05-22 18:39:44 +00:00
|
|
|
acc_u64s_percpu((u64 *) &ret->u, (u64 __percpu *) c->usage[i],
|
|
|
|
__fs_usage_u64s(nr_replicas));
|
2019-02-11 00:34:47 +00:00
|
|
|
} while (read_seqcount_retry(&c->usage_lock, seq));
|
2019-01-21 20:32:13 +00:00
|
|
|
|
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2019-02-11 00:34:47 +00:00
|
|
|
void bch2_fs_usage_acc_to_base(struct bch_fs *c, unsigned idx)
|
|
|
|
{
|
2023-12-17 07:34:05 +00:00
|
|
|
unsigned u64s = fs_usage_u64s(c);
|
2019-02-11 00:34:47 +00:00
|
|
|
|
|
|
|
BUG_ON(idx >= ARRAY_SIZE(c->usage));
|
|
|
|
|
|
|
|
preempt_disable();
|
|
|
|
write_seqcount_begin(&c->usage_lock);
|
|
|
|
|
|
|
|
acc_u64s_percpu((u64 *) c->usage_base,
|
|
|
|
(u64 __percpu *) c->usage[idx], u64s);
|
|
|
|
percpu_memset(c->usage[idx], 0, u64s * sizeof(u64));
|
|
|
|
|
2021-01-22 02:52:06 +00:00
|
|
|
rcu_read_lock();
|
2023-12-17 07:34:05 +00:00
|
|
|
for_each_member_device_rcu(c, ca, NULL) {
|
2021-01-22 02:52:06 +00:00
|
|
|
u64s = dev_usage_u64s();
|
|
|
|
|
|
|
|
acc_u64s_percpu((u64 *) ca->usage_base,
|
|
|
|
(u64 __percpu *) ca->usage[idx], u64s);
|
|
|
|
percpu_memset(ca->usage[idx], 0, u64s * sizeof(u64));
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
2019-02-11 00:34:47 +00:00
|
|
|
write_seqcount_end(&c->usage_lock);
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
void bch2_fs_usage_to_text(struct printbuf *out,
|
|
|
|
struct bch_fs *c,
|
|
|
|
struct bch_fs_usage_online *fs_usage)
|
|
|
|
{
|
|
|
|
unsigned i;
|
|
|
|
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "capacity:\t\t\t%llu\n", c->capacity);
|
2019-02-11 00:34:47 +00:00
|
|
|
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "hidden:\t\t\t\t%llu\n",
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->u.b.hidden);
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "data:\t\t\t\t%llu\n",
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->u.b.data);
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "cached:\t\t\t\t%llu\n",
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->u.b.cached);
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "reserved:\t\t\t%llu\n",
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->u.b.reserved);
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "nr_inodes:\t\t\t%llu\n",
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->u.b.nr_inodes);
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "online reserved:\t\t%llu\n",
|
2019-02-11 00:34:47 +00:00
|
|
|
fs_usage->online_reserved);
|
|
|
|
|
|
|
|
for (i = 0;
|
|
|
|
i < ARRAY_SIZE(fs_usage->u.persistent_reserved);
|
|
|
|
i++) {
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "%u replicas:\n", i + 1);
|
|
|
|
prt_printf(out, "\treserved:\t\t%llu\n",
|
2019-02-11 00:34:47 +00:00
|
|
|
fs_usage->u.persistent_reserved[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < c->replicas.nr; i++) {
|
2023-11-09 18:52:35 +00:00
|
|
|
struct bch_replicas_entry_v1 *e =
|
2019-02-11 00:34:47 +00:00
|
|
|
cpu_replicas_entry(&c->replicas, i);
|
|
|
|
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, "\t");
|
2019-02-11 00:34:47 +00:00
|
|
|
bch2_replicas_entry_to_text(out, e);
|
2023-02-04 02:01:40 +00:00
|
|
|
prt_printf(out, ":\t%llu\n", fs_usage->u.replicas[i]);
|
2019-02-11 00:34:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
static u64 reserve_factor(u64 r)
|
|
|
|
{
|
|
|
|
return r + (round_up(r, (1 << RESERVE_FACTOR)) >> RESERVE_FACTOR);
|
|
|
|
}
|
|
|
|
|
2019-02-11 00:34:47 +00:00
|
|
|
u64 bch2_fs_sectors_used(struct bch_fs *c, struct bch_fs_usage_online *fs_usage)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2023-11-17 05:03:45 +00:00
|
|
|
return min(fs_usage->u.b.hidden +
|
|
|
|
fs_usage->u.b.btree +
|
|
|
|
fs_usage->u.b.data +
|
|
|
|
reserve_factor(fs_usage->u.b.reserved +
|
2019-02-14 23:38:52 +00:00
|
|
|
fs_usage->online_reserved),
|
2019-01-21 20:32:13 +00:00
|
|
|
c->capacity);
|
2018-11-27 13:23:22 +00:00
|
|
|
}
|
|
|
|
|
2019-02-14 23:38:52 +00:00
|
|
|
static struct bch_fs_usage_short
|
|
|
|
__bch2_fs_usage_read_short(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
struct bch_fs_usage_short ret;
|
|
|
|
u64 data, reserved;
|
|
|
|
|
|
|
|
ret.capacity = c->capacity -
|
2023-11-17 05:03:45 +00:00
|
|
|
bch2_fs_usage_read_one(c, &c->usage_base->b.hidden);
|
2019-02-14 23:38:52 +00:00
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
data = bch2_fs_usage_read_one(c, &c->usage_base->b.data) +
|
|
|
|
bch2_fs_usage_read_one(c, &c->usage_base->b.btree);
|
|
|
|
reserved = bch2_fs_usage_read_one(c, &c->usage_base->b.reserved) +
|
2019-02-11 00:34:47 +00:00
|
|
|
percpu_u64_get(c->online_reserved);
|
2019-02-14 23:38:52 +00:00
|
|
|
|
|
|
|
ret.used = min(ret.capacity, data + reserve_factor(reserved));
|
|
|
|
ret.free = ret.capacity - ret.used;
|
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
ret.nr_inodes = bch2_fs_usage_read_one(c, &c->usage_base->b.nr_inodes);
|
2019-02-14 23:38:52 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-11-27 13:23:22 +00:00
|
|
|
struct bch_fs_usage_short
|
|
|
|
bch2_fs_usage_read_short(struct bch_fs *c)
|
|
|
|
{
|
|
|
|
struct bch_fs_usage_short ret;
|
|
|
|
|
2019-02-14 23:38:52 +00:00
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
ret = __bch2_fs_usage_read_short(c);
|
|
|
|
percpu_up_read(&c->mark_lock);
|
2018-11-27 13:23:22 +00:00
|
|
|
|
|
|
|
return ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2022-04-01 05:29:59 +00:00
|
|
|
void bch2_dev_usage_init(struct bch_dev *ca)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2022-04-01 05:29:59 +00:00
|
|
|
ca->usage_base->d[BCH_DATA_free].buckets = ca->mi.nbuckets - ca->mi.first_bucket;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2023-11-23 23:25:31 +00:00
|
|
|
void bch2_dev_usage_to_text(struct printbuf *out, struct bch_dev_usage *usage)
|
|
|
|
{
|
2024-04-10 20:08:24 +00:00
|
|
|
prt_printf(out, "\tbuckets\rsectors\rfragmented\r\n");
|
2023-11-23 23:25:31 +00:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < BCH_DATA_NR; i++) {
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_prt_data_type(out, i);
|
2024-04-10 20:08:24 +00:00
|
|
|
prt_printf(out, "\t%llu\r%llu\r%llu\r\n",
|
|
|
|
usage->d[i].buckets,
|
|
|
|
usage->d[i].sectors,
|
|
|
|
usage->d[i].fragmented);
|
2023-11-23 23:25:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 05:59:17 +00:00
|
|
|
void bch2_dev_usage_update(struct bch_fs *c, struct bch_dev *ca,
|
|
|
|
const struct bch_alloc_v4 *old,
|
|
|
|
const struct bch_alloc_v4 *new,
|
|
|
|
u64 journal_seq, bool gc)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2021-06-11 01:44:27 +00:00
|
|
|
struct bch_fs_usage *fs_usage;
|
2020-06-19 01:06:42 +00:00
|
|
|
struct bch_dev_usage *u;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
preempt_disable();
|
2021-06-11 01:44:27 +00:00
|
|
|
fs_usage = fs_usage_ptr(c, journal_seq, gc);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-28 05:59:17 +00:00
|
|
|
if (data_type_is_hidden(old->data_type))
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->b.hidden -= ca->mi.bucket_size;
|
2023-12-28 05:59:17 +00:00
|
|
|
if (data_type_is_hidden(new->data_type))
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->b.hidden += ca->mi.bucket_size;
|
2022-04-01 05:29:59 +00:00
|
|
|
|
|
|
|
u = dev_usage_ptr(ca, journal_seq, gc);
|
2018-11-24 22:09:44 +00:00
|
|
|
|
2023-12-28 05:59:17 +00:00
|
|
|
u->d[old->data_type].buckets--;
|
|
|
|
u->d[new->data_type].buckets++;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2023-12-28 05:59:17 +00:00
|
|
|
u->d[old->data_type].sectors -= bch2_bucket_sectors_dirty(*old);
|
|
|
|
u->d[new->data_type].sectors += bch2_bucket_sectors_dirty(*new);
|
2022-04-01 05:29:59 +00:00
|
|
|
|
2023-12-28 05:59:17 +00:00
|
|
|
u->d[BCH_DATA_cached].sectors += new->cached_sectors;
|
|
|
|
u->d[BCH_DATA_cached].sectors -= old->cached_sectors;
|
2021-01-22 01:51:51 +00:00
|
|
|
|
2023-12-28 05:59:17 +00:00
|
|
|
u->d[old->data_type].fragmented -= bch2_bucket_sectors_fragmented(ca, *old);
|
|
|
|
u->d[new->data_type].fragmented += bch2_bucket_sectors_fragmented(ca, *new);
|
2021-01-22 01:51:51 +00:00
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
2021-06-11 01:44:27 +00:00
|
|
|
static inline int __update_replicas(struct bch_fs *c,
|
|
|
|
struct bch_fs_usage *fs_usage,
|
2023-11-09 18:52:35 +00:00
|
|
|
struct bch_replicas_entry_v1 *r,
|
2021-06-11 01:44:27 +00:00
|
|
|
s64 sectors)
|
|
|
|
{
|
|
|
|
int idx = bch2_replicas_entry_idx(c, r);
|
|
|
|
|
|
|
|
if (idx < 0)
|
|
|
|
return -1;
|
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage_data_type_to_base(&fs_usage->b, r->data_type, sectors);
|
2021-06-11 01:44:27 +00:00
|
|
|
fs_usage->replicas[idx] += sectors;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-12-28 06:31:49 +00:00
|
|
|
int bch2_update_replicas(struct bch_fs *c, struct bkey_s_c k,
|
|
|
|
struct bch_replicas_entry_v1 *r, s64 sectors,
|
|
|
|
unsigned journal_seq, bool gc)
|
2019-01-21 20:32:13 +00:00
|
|
|
{
|
2023-07-07 02:47:42 +00:00
|
|
|
struct bch_fs_usage *fs_usage;
|
2021-11-28 19:31:19 +00:00
|
|
|
int idx, ret = 0;
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2019-01-21 20:32:13 +00:00
|
|
|
|
2021-11-28 19:31:19 +00:00
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
|
|
|
|
idx = bch2_replicas_entry_idx(c, r);
|
2021-11-28 20:13:54 +00:00
|
|
|
if (idx < 0 &&
|
2023-10-25 00:44:36 +00:00
|
|
|
fsck_err(c, ptr_to_missing_replicas_entry,
|
|
|
|
"no replicas entry\n while marking %s",
|
2022-04-13 00:03:19 +00:00
|
|
|
(bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
2021-11-28 20:13:54 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
ret = bch2_mark_replicas(c, r);
|
|
|
|
percpu_down_read(&c->mark_lock);
|
2022-02-25 18:18:19 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
2021-11-28 20:13:54 +00:00
|
|
|
idx = bch2_replicas_entry_idx(c, r);
|
|
|
|
}
|
2021-11-28 19:31:19 +00:00
|
|
|
if (idx < 0) {
|
|
|
|
ret = -1;
|
|
|
|
goto err;
|
|
|
|
}
|
2019-01-21 20:32:13 +00:00
|
|
|
|
2021-06-11 01:44:27 +00:00
|
|
|
preempt_disable();
|
|
|
|
fs_usage = fs_usage_ptr(c, journal_seq, gc);
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage_data_type_to_base(&fs_usage->b, r->data_type, sectors);
|
2019-02-14 23:38:52 +00:00
|
|
|
fs_usage->replicas[idx] += sectors;
|
2021-06-11 01:44:27 +00:00
|
|
|
preempt_enable();
|
2021-11-28 19:31:19 +00:00
|
|
|
err:
|
2021-11-28 20:13:54 +00:00
|
|
|
fsck_err:
|
2021-11-28 19:31:19 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
2021-11-28 19:31:19 +00:00
|
|
|
return ret;
|
2019-01-21 20:32:13 +00:00
|
|
|
}
|
|
|
|
|
2021-04-21 22:08:39 +00:00
|
|
|
static inline int update_cached_sectors(struct bch_fs *c,
|
2021-11-28 20:13:54 +00:00
|
|
|
struct bkey_s_c k,
|
2021-06-11 01:44:27 +00:00
|
|
|
unsigned dev, s64 sectors,
|
|
|
|
unsigned journal_seq, bool gc)
|
2019-01-21 20:32:13 +00:00
|
|
|
{
|
|
|
|
struct bch_replicas_padded r;
|
|
|
|
|
|
|
|
bch2_replicas_entry_cached(&r.e, dev);
|
|
|
|
|
2023-12-28 06:31:49 +00:00
|
|
|
return bch2_update_replicas(c, k, &r.e, sectors, journal_seq, gc);
|
2019-01-21 20:32:13 +00:00
|
|
|
}
|
|
|
|
|
2023-05-28 23:23:35 +00:00
|
|
|
static int __replicas_deltas_realloc(struct btree_trans *trans, unsigned more,
|
|
|
|
gfp_t gfp)
|
2019-05-24 15:56:20 +00:00
|
|
|
{
|
|
|
|
struct replicas_delta_list *d = trans->fs_usage_deltas;
|
|
|
|
unsigned new_size = d ? (d->size + more) * 2 : 128;
|
2021-04-24 04:24:25 +00:00
|
|
|
unsigned alloc_size = sizeof(*d) + new_size;
|
|
|
|
|
|
|
|
WARN_ON_ONCE(alloc_size > REPLICAS_DELTA_LIST_MAX);
|
2019-05-24 15:56:20 +00:00
|
|
|
|
|
|
|
if (!d || d->used + more > d->size) {
|
2023-05-28 23:23:35 +00:00
|
|
|
d = krealloc(d, alloc_size, gfp|__GFP_ZERO);
|
2021-04-24 04:24:25 +00:00
|
|
|
|
2023-05-28 23:23:35 +00:00
|
|
|
if (unlikely(!d)) {
|
|
|
|
if (alloc_size > REPLICAS_DELTA_LIST_MAX)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
d = mempool_alloc(&trans->c->replicas_delta_pool, gfp);
|
|
|
|
if (!d)
|
|
|
|
return -ENOMEM;
|
2021-04-24 04:24:25 +00:00
|
|
|
|
|
|
|
memset(d, 0, REPLICAS_DELTA_LIST_MAX);
|
|
|
|
|
|
|
|
if (trans->fs_usage_deltas)
|
|
|
|
memcpy(d, trans->fs_usage_deltas,
|
|
|
|
trans->fs_usage_deltas->size + sizeof(*d));
|
|
|
|
|
|
|
|
new_size = REPLICAS_DELTA_LIST_MAX - sizeof(*d);
|
|
|
|
kfree(trans->fs_usage_deltas);
|
|
|
|
}
|
2019-05-24 15:56:20 +00:00
|
|
|
|
|
|
|
d->size = new_size;
|
|
|
|
trans->fs_usage_deltas = d;
|
|
|
|
}
|
2023-05-28 23:23:35 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-17 04:41:48 +00:00
|
|
|
int bch2_replicas_deltas_realloc(struct btree_trans *trans, unsigned more)
|
2023-05-28 23:23:35 +00:00
|
|
|
{
|
|
|
|
return allocate_dropping_locks_errcode(trans,
|
|
|
|
__replicas_deltas_realloc(trans, more, _gfp));
|
2019-05-24 15:56:20 +00:00
|
|
|
}
|
|
|
|
|
2023-11-11 22:40:45 +00:00
|
|
|
int bch2_update_replicas_list(struct btree_trans *trans,
|
|
|
|
struct bch_replicas_entry_v1 *r,
|
|
|
|
s64 sectors)
|
2019-05-24 15:56:20 +00:00
|
|
|
{
|
|
|
|
struct replicas_delta_list *d;
|
|
|
|
struct replicas_delta *n;
|
2019-08-22 17:20:38 +00:00
|
|
|
unsigned b;
|
2023-05-28 23:23:35 +00:00
|
|
|
int ret;
|
2019-08-22 17:20:38 +00:00
|
|
|
|
|
|
|
if (!sectors)
|
2023-05-28 23:23:35 +00:00
|
|
|
return 0;
|
2019-05-24 15:56:20 +00:00
|
|
|
|
2019-08-22 17:20:38 +00:00
|
|
|
b = replicas_entry_bytes(r) + 8;
|
2023-07-17 04:41:48 +00:00
|
|
|
ret = bch2_replicas_deltas_realloc(trans, b);
|
2023-05-28 23:23:35 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2019-05-24 15:56:20 +00:00
|
|
|
|
2023-05-28 23:23:35 +00:00
|
|
|
d = trans->fs_usage_deltas;
|
2019-05-24 15:56:20 +00:00
|
|
|
n = (void *) d->d + d->used;
|
|
|
|
n->delta = sectors;
|
2023-10-06 17:47:31 +00:00
|
|
|
unsafe_memcpy((void *) n + offsetof(struct replicas_delta, r),
|
|
|
|
r, replicas_entry_bytes(r),
|
|
|
|
"flexible array member embedded in strcuct with padding");
|
2021-04-03 23:41:09 +00:00
|
|
|
bch2_replicas_entry_sort(&n->r);
|
2019-05-24 15:56:20 +00:00
|
|
|
d->used += b;
|
2023-05-28 23:23:35 +00:00
|
|
|
return 0;
|
2019-05-24 15:56:20 +00:00
|
|
|
}
|
|
|
|
|
2023-11-11 22:40:45 +00:00
|
|
|
int bch2_update_cached_sectors_list(struct btree_trans *trans, unsigned dev, s64 sectors)
|
2019-05-24 15:56:20 +00:00
|
|
|
{
|
|
|
|
struct bch_replicas_padded r;
|
|
|
|
|
|
|
|
bch2_replicas_entry_cached(&r.e, dev);
|
|
|
|
|
2023-11-11 22:40:45 +00:00
|
|
|
return bch2_update_replicas_list(trans, &r.e, sectors);
|
2019-05-24 15:56:20 +00:00
|
|
|
}
|
|
|
|
|
2024-04-07 23:07:09 +00:00
|
|
|
int bch2_check_fix_ptrs(struct btree_trans *trans,
|
|
|
|
enum btree_id btree, unsigned level, struct bkey_s_c k,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct bkey_ptrs_c ptrs_c = bch2_bkey_ptrs_c(k);
|
|
|
|
const union bch_extent_entry *entry_c;
|
|
|
|
struct extent_ptr_decoded p = { 0 };
|
|
|
|
bool do_update = false;
|
|
|
|
struct printbuf buf = PRINTBUF;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
|
|
|
|
bkey_for_each_ptr_decode(k.k, ptrs_c, p, entry_c) {
|
|
|
|
struct bch_dev *ca = bch2_dev_bkey_exists(c, p.ptr.dev);
|
|
|
|
struct bucket *g = PTR_GC_BUCKET(ca, &p.ptr);
|
|
|
|
enum bch_data_type data_type = bch2_bkey_ptr_data_type(k, p, entry_c);
|
|
|
|
|
|
|
|
if (fsck_err_on(!g->gen_valid,
|
|
|
|
c, ptr_to_missing_alloc_key,
|
|
|
|
"bucket %u:%zu data type %s ptr gen %u missing in alloc btree\n"
|
|
|
|
"while marking %s",
|
|
|
|
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
|
|
|
bch2_data_type_str(ptr_data_type(k.k, &p.ptr)),
|
|
|
|
p.ptr.gen,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
|
|
|
if (!p.ptr.cached) {
|
|
|
|
g->gen_valid = true;
|
|
|
|
g->gen = p.ptr.gen;
|
|
|
|
} else {
|
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fsck_err_on(gen_cmp(p.ptr.gen, g->gen) > 0,
|
|
|
|
c, ptr_gen_newer_than_bucket_gen,
|
|
|
|
"bucket %u:%zu data type %s ptr gen in the future: %u > %u\n"
|
|
|
|
"while marking %s",
|
|
|
|
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
|
|
|
bch2_data_type_str(ptr_data_type(k.k, &p.ptr)),
|
|
|
|
p.ptr.gen, g->gen,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
|
|
|
if (!p.ptr.cached &&
|
|
|
|
(g->data_type != BCH_DATA_btree ||
|
|
|
|
data_type == BCH_DATA_btree)) {
|
|
|
|
g->gen_valid = true;
|
|
|
|
g->gen = p.ptr.gen;
|
|
|
|
g->data_type = 0;
|
|
|
|
g->dirty_sectors = 0;
|
|
|
|
g->cached_sectors = 0;
|
|
|
|
} else {
|
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fsck_err_on(gen_cmp(g->gen, p.ptr.gen) > BUCKET_GC_GEN_MAX,
|
|
|
|
c, ptr_gen_newer_than_bucket_gen,
|
|
|
|
"bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n"
|
|
|
|
"while marking %s",
|
|
|
|
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), g->gen,
|
|
|
|
bch2_data_type_str(ptr_data_type(k.k, &p.ptr)),
|
|
|
|
p.ptr.gen,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
|
|
|
do_update = true;
|
|
|
|
|
|
|
|
if (fsck_err_on(!p.ptr.cached && gen_cmp(p.ptr.gen, g->gen) < 0,
|
|
|
|
c, stale_dirty_ptr,
|
|
|
|
"bucket %u:%zu data type %s stale dirty ptr: %u < %u\n"
|
|
|
|
"while marking %s",
|
|
|
|
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr),
|
|
|
|
bch2_data_type_str(ptr_data_type(k.k, &p.ptr)),
|
|
|
|
p.ptr.gen, g->gen,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
|
|
|
do_update = true;
|
|
|
|
|
|
|
|
if (data_type != BCH_DATA_btree && p.ptr.gen != g->gen)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (fsck_err_on(bucket_data_type_mismatch(g->data_type, data_type),
|
|
|
|
c, ptr_bucket_data_type_mismatch,
|
|
|
|
"bucket %u:%zu gen %u different types of data in same bucket: %s, %s\n"
|
|
|
|
"while marking %s",
|
|
|
|
p.ptr.dev, PTR_BUCKET_NR(ca, &p.ptr), g->gen,
|
|
|
|
bch2_data_type_str(g->data_type),
|
|
|
|
bch2_data_type_str(data_type),
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
|
|
|
|
if (data_type == BCH_DATA_btree) {
|
|
|
|
g->gen_valid = true;
|
|
|
|
g->gen = p.ptr.gen;
|
|
|
|
g->data_type = data_type;
|
|
|
|
g->dirty_sectors = 0;
|
|
|
|
g->cached_sectors = 0;
|
|
|
|
} else {
|
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p.has_ec) {
|
|
|
|
struct gc_stripe *m = genradix_ptr(&c->gc_stripes, p.ec.idx);
|
|
|
|
|
|
|
|
if (fsck_err_on(!m || !m->alive, c,
|
|
|
|
ptr_to_missing_stripe,
|
|
|
|
"pointer to nonexistent stripe %llu\n"
|
|
|
|
"while marking %s",
|
|
|
|
(u64) p.ec.idx,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
|
|
|
do_update = true;
|
|
|
|
|
|
|
|
if (fsck_err_on(m && m->alive && !bch2_ptr_matches_stripe_m(m, p), c,
|
|
|
|
ptr_to_incorrect_stripe,
|
|
|
|
"pointer does not match stripe %llu\n"
|
|
|
|
"while marking %s",
|
|
|
|
(u64) p.ec.idx,
|
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
|
|
|
|
do_update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_update) {
|
|
|
|
if (flags & BTREE_TRIGGER_is_root) {
|
|
|
|
bch_err(c, "cannot update btree roots yet");
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct bkey_i *new = bch2_bkey_make_mut_noupdate(trans, k);
|
|
|
|
ret = PTR_ERR_OR_ZERO(new);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (level) {
|
|
|
|
/*
|
|
|
|
* We don't want to drop btree node pointers - if the
|
|
|
|
* btree node isn't there anymore, the read path will
|
|
|
|
* sort it out:
|
|
|
|
*/
|
|
|
|
struct bkey_ptrs ptrs = bch2_bkey_ptrs(bkey_i_to_s(new));
|
|
|
|
bkey_for_each_ptr(ptrs, ptr) {
|
|
|
|
struct bch_dev *ca = bch2_dev_bkey_exists(c, ptr->dev);
|
|
|
|
struct bucket *g = PTR_GC_BUCKET(ca, ptr);
|
|
|
|
|
|
|
|
ptr->gen = g->gen;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct bkey_ptrs ptrs;
|
|
|
|
union bch_extent_entry *entry;
|
|
|
|
restart_drop_ptrs:
|
|
|
|
ptrs = bch2_bkey_ptrs(bkey_i_to_s(new));
|
|
|
|
bkey_for_each_ptr_decode(bkey_i_to_s(new).k, ptrs, p, entry) {
|
|
|
|
struct bch_dev *ca = bch2_dev_bkey_exists(c, p.ptr.dev);
|
|
|
|
struct bucket *g = PTR_GC_BUCKET(ca, &p.ptr);
|
|
|
|
enum bch_data_type data_type = bch2_bkey_ptr_data_type(bkey_i_to_s_c(new), p, entry);
|
|
|
|
|
|
|
|
if ((p.ptr.cached &&
|
|
|
|
(!g->gen_valid || gen_cmp(p.ptr.gen, g->gen) > 0)) ||
|
|
|
|
(!p.ptr.cached &&
|
|
|
|
gen_cmp(p.ptr.gen, g->gen) < 0) ||
|
|
|
|
gen_cmp(g->gen, p.ptr.gen) > BUCKET_GC_GEN_MAX ||
|
|
|
|
(g->data_type &&
|
|
|
|
g->data_type != data_type)) {
|
|
|
|
bch2_bkey_drop_ptr(bkey_i_to_s(new), &entry->ptr);
|
|
|
|
goto restart_drop_ptrs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
again:
|
|
|
|
ptrs = bch2_bkey_ptrs(bkey_i_to_s(new));
|
|
|
|
bkey_extent_entry_for_each(ptrs, entry) {
|
|
|
|
if (extent_entry_type(entry) == BCH_EXTENT_ENTRY_stripe_ptr) {
|
|
|
|
struct gc_stripe *m = genradix_ptr(&c->gc_stripes,
|
|
|
|
entry->stripe_ptr.idx);
|
|
|
|
union bch_extent_entry *next_ptr;
|
|
|
|
|
|
|
|
bkey_extent_entry_for_each_from(ptrs, next_ptr, entry)
|
|
|
|
if (extent_entry_type(next_ptr) == BCH_EXTENT_ENTRY_ptr)
|
|
|
|
goto found;
|
|
|
|
next_ptr = NULL;
|
|
|
|
found:
|
|
|
|
if (!next_ptr) {
|
|
|
|
bch_err(c, "aieee, found stripe ptr with no data ptr");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m || !m->alive ||
|
|
|
|
!__bch2_ptr_matches_stripe(&m->ptrs[entry->stripe_ptr.block],
|
|
|
|
&next_ptr->ptr,
|
|
|
|
m->sectors)) {
|
|
|
|
bch2_bkey_extent_entry_drop(new, entry);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0) {
|
|
|
|
printbuf_reset(&buf);
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k);
|
|
|
|
bch_info(c, "updated %s", buf.buf);
|
|
|
|
|
|
|
|
printbuf_reset(&buf);
|
|
|
|
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(new));
|
|
|
|
bch_info(c, "new key %s", buf.buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
struct btree_iter iter;
|
|
|
|
bch2_trans_node_iter_init(trans, &iter, btree, new->k.p, 0, level,
|
|
|
|
BTREE_ITER_intent|BTREE_ITER_all_snapshots);
|
|
|
|
ret = bch2_btree_iter_traverse(&iter) ?:
|
|
|
|
bch2_trans_update(trans, &iter, new,
|
|
|
|
BTREE_UPDATE_internal_snapshot_node|
|
|
|
|
BTREE_TRIGGER_norun);
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (level)
|
|
|
|
bch2_btree_node_update_key_early(trans, btree, level - 1, k, new);
|
|
|
|
}
|
|
|
|
err:
|
|
|
|
fsck_err:
|
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
printbuf_exit(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-04-19 23:03:58 +00:00
|
|
|
int bch2_bucket_ref_update(struct btree_trans *trans,
|
2023-12-28 06:31:49 +00:00
|
|
|
struct bkey_s_c k,
|
|
|
|
const struct bch_extent_ptr *ptr,
|
|
|
|
s64 sectors, enum bch_data_type ptr_data_type,
|
|
|
|
u8 b_gen, u8 bucket_data_type,
|
2024-04-19 23:03:58 +00:00
|
|
|
u32 *bucket_sectors)
|
2019-03-11 18:59:58 +00:00
|
|
|
{
|
2023-02-12 00:31:03 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2024-04-12 03:31:55 +00:00
|
|
|
struct bch_dev *ca = bch2_dev_bkey_exists(c, ptr->dev);
|
2022-02-13 23:15:35 +00:00
|
|
|
size_t bucket_nr = PTR_BUCKET_NR(ca, ptr);
|
2022-02-25 18:18:19 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
2024-04-19 23:03:58 +00:00
|
|
|
bool inserting = sectors > 0;
|
2022-02-25 18:18:19 +00:00
|
|
|
int ret = 0;
|
2020-06-03 22:27:07 +00:00
|
|
|
|
2024-04-19 23:03:58 +00:00
|
|
|
BUG_ON(!sectors);
|
|
|
|
|
2022-02-13 23:15:35 +00:00
|
|
|
if (gen_after(ptr->gen, b_gen)) {
|
2020-06-03 22:27:07 +00:00
|
|
|
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
2023-10-25 00:44:36 +00:00
|
|
|
BCH_FSCK_ERR_ptr_gen_newer_than_bucket_gen,
|
2020-06-03 22:27:07 +00:00
|
|
|
"bucket %u:%zu gen %u data type %s: ptr gen %u newer than bucket gen\n"
|
|
|
|
"while marking %s",
|
2022-02-13 23:15:35 +00:00
|
|
|
ptr->dev, bucket_nr, b_gen,
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
2020-10-20 02:36:24 +00:00
|
|
|
ptr->gen,
|
2022-02-25 18:18:19 +00:00
|
|
|
(bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
2024-04-19 23:03:58 +00:00
|
|
|
if (inserting)
|
|
|
|
goto err;
|
|
|
|
goto out;
|
2020-06-03 22:27:07 +00:00
|
|
|
}
|
|
|
|
|
2022-02-13 23:15:35 +00:00
|
|
|
if (gen_cmp(b_gen, ptr->gen) > BUCKET_GC_GEN_MAX) {
|
2020-06-03 22:27:07 +00:00
|
|
|
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
2023-10-25 00:44:36 +00:00
|
|
|
BCH_FSCK_ERR_ptr_too_stale,
|
2020-06-03 22:27:07 +00:00
|
|
|
"bucket %u:%zu gen %u data type %s: ptr gen %u too stale\n"
|
|
|
|
"while marking %s",
|
2022-02-13 23:15:35 +00:00
|
|
|
ptr->dev, bucket_nr, b_gen,
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
2020-10-20 02:36:24 +00:00
|
|
|
ptr->gen,
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
2024-04-19 23:03:58 +00:00
|
|
|
if (inserting)
|
|
|
|
goto err;
|
|
|
|
goto out;
|
2020-06-03 22:27:07 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 23:03:58 +00:00
|
|
|
if (b_gen != ptr->gen && ptr->cached) {
|
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (b_gen != ptr->gen) {
|
2020-06-03 22:27:07 +00:00
|
|
|
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
2023-10-25 00:44:36 +00:00
|
|
|
BCH_FSCK_ERR_stale_dirty_ptr,
|
2022-02-13 23:15:35 +00:00
|
|
|
"bucket %u:%zu gen %u (mem gen %u) data type %s: stale dirty ptr (gen %u)\n"
|
2020-06-03 22:27:07 +00:00
|
|
|
"while marking %s",
|
2022-02-13 23:15:35 +00:00
|
|
|
ptr->dev, bucket_nr, b_gen,
|
|
|
|
*bucket_gen(ca, bucket_nr),
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
2020-10-20 02:36:24 +00:00
|
|
|
ptr->gen,
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
2024-04-19 23:03:58 +00:00
|
|
|
if (inserting)
|
|
|
|
goto err;
|
2023-02-12 00:31:03 +00:00
|
|
|
goto out;
|
2022-02-25 18:18:19 +00:00
|
|
|
}
|
2020-06-03 22:27:07 +00:00
|
|
|
|
2024-04-22 21:21:35 +00:00
|
|
|
if (bucket_data_type_mismatch(bucket_data_type, ptr_data_type)) {
|
2020-06-03 22:27:07 +00:00
|
|
|
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
2023-10-25 00:44:36 +00:00
|
|
|
BCH_FSCK_ERR_ptr_bucket_data_type_mismatch,
|
2020-06-03 22:27:07 +00:00
|
|
|
"bucket %u:%zu gen %u different types of data in same bucket: %s, %s\n"
|
|
|
|
"while marking %s",
|
2022-02-13 23:15:35 +00:00
|
|
|
ptr->dev, bucket_nr, b_gen,
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_data_type_str(bucket_data_type),
|
|
|
|
bch2_data_type_str(ptr_data_type),
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
2024-04-19 23:03:58 +00:00
|
|
|
if (inserting)
|
|
|
|
goto err;
|
|
|
|
goto out;
|
2020-06-03 22:27:07 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 23:03:58 +00:00
|
|
|
if ((u64) *bucket_sectors + sectors > U32_MAX) {
|
2020-06-03 22:27:07 +00:00
|
|
|
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
2023-10-25 00:44:36 +00:00
|
|
|
BCH_FSCK_ERR_bucket_sector_count_overflow,
|
2023-09-20 02:18:39 +00:00
|
|
|
"bucket %u:%zu gen %u data type %s sector count overflow: %u + %lli > U32_MAX\n"
|
2020-06-03 22:27:07 +00:00
|
|
|
"while marking %s",
|
2022-02-13 23:15:35 +00:00
|
|
|
ptr->dev, bucket_nr, b_gen,
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_data_type_str(bucket_data_type ?: ptr_data_type),
|
2024-04-19 23:03:58 +00:00
|
|
|
*bucket_sectors, sectors,
|
2022-02-25 18:18:19 +00:00
|
|
|
(printbuf_reset(&buf),
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k), buf.buf));
|
2024-04-19 23:03:58 +00:00
|
|
|
if (inserting)
|
|
|
|
goto err;
|
|
|
|
sectors = -*bucket_sectors;
|
2020-06-03 22:27:07 +00:00
|
|
|
}
|
2024-04-19 23:03:58 +00:00
|
|
|
|
|
|
|
*bucket_sectors += sectors;
|
2023-02-12 00:31:03 +00:00
|
|
|
out:
|
2022-02-25 18:18:19 +00:00
|
|
|
printbuf_exit(&buf);
|
|
|
|
return ret;
|
2023-02-12 00:31:03 +00:00
|
|
|
err:
|
|
|
|
bch2_dump_trans_updates(trans);
|
2024-04-19 23:03:58 +00:00
|
|
|
ret = -EIO;
|
2023-02-12 00:31:03 +00:00
|
|
|
goto out;
|
2020-10-20 02:36:24 +00:00
|
|
|
}
|
|
|
|
|
bcachefs: Btree write buffer
This adds a new method of doing btree updates - a straight write buffer,
implemented as a flat fixed size array.
This is only useful when we don't need to read from the btree in order
to do the update, and when reading is infrequent - perfect for the LRU
btree.
This will make LRU btree updates fast enough that we'll be able to use
it for persistently indexing buckets by fragmentation, which will be a
massive boost to copygc performance.
Changes:
- A new btree_insert_type enum, for btree_insert_entries. Specifies
btree, btree key cache, or btree write buffer.
- bch2_trans_update_buffered(): updates via the btree write buffer
don't need a btree path, so we need a new update path.
- Transaction commit path changes:
The update to the btree write buffer both mutates global, and can
fail if there isn't currently room. Therefore we do all write buffer
updates in the transaction all at once, and also if it fails we have
to revert filesystem usage counter changes.
If there isn't room we flush the write buffer in the transaction
commit error path and retry.
- A new persistent option, for specifying the number of entries in the
write buffer.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-01-04 05:00:50 +00:00
|
|
|
void bch2_trans_fs_usage_revert(struct btree_trans *trans,
|
|
|
|
struct replicas_delta_list *deltas)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct bch_fs_usage *dst;
|
|
|
|
struct replicas_delta *d, *top = (void *) deltas->d + deltas->used;
|
|
|
|
s64 added = 0;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
preempt_disable();
|
|
|
|
dst = fs_usage_ptr(c, trans->journal_res.seq, false);
|
|
|
|
|
|
|
|
/* revert changes: */
|
|
|
|
for (d = deltas->d; d != top; d = replicas_delta_next(d)) {
|
|
|
|
switch (d->r.data_type) {
|
|
|
|
case BCH_DATA_btree:
|
|
|
|
case BCH_DATA_user:
|
|
|
|
case BCH_DATA_parity:
|
|
|
|
added += d->delta;
|
|
|
|
}
|
|
|
|
BUG_ON(__update_replicas(c, dst, &d->r, -d->delta));
|
|
|
|
}
|
|
|
|
|
2023-11-17 05:03:45 +00:00
|
|
|
dst->b.nr_inodes -= deltas->nr_inodes;
|
bcachefs: Btree write buffer
This adds a new method of doing btree updates - a straight write buffer,
implemented as a flat fixed size array.
This is only useful when we don't need to read from the btree in order
to do the update, and when reading is infrequent - perfect for the LRU
btree.
This will make LRU btree updates fast enough that we'll be able to use
it for persistently indexing buckets by fragmentation, which will be a
massive boost to copygc performance.
Changes:
- A new btree_insert_type enum, for btree_insert_entries. Specifies
btree, btree key cache, or btree write buffer.
- bch2_trans_update_buffered(): updates via the btree write buffer
don't need a btree path, so we need a new update path.
- Transaction commit path changes:
The update to the btree write buffer both mutates global, and can
fail if there isn't currently room. Therefore we do all write buffer
updates in the transaction all at once, and also if it fails we have
to revert filesystem usage counter changes.
If there isn't room we flush the write buffer in the transaction
commit error path and retry.
- A new persistent option, for specifying the number of entries in the
write buffer.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-01-04 05:00:50 +00:00
|
|
|
|
|
|
|
for (i = 0; i < BCH_REPLICAS_MAX; i++) {
|
|
|
|
added -= deltas->persistent_reserved[i];
|
2023-11-17 05:03:45 +00:00
|
|
|
dst->b.reserved -= deltas->persistent_reserved[i];
|
bcachefs: Btree write buffer
This adds a new method of doing btree updates - a straight write buffer,
implemented as a flat fixed size array.
This is only useful when we don't need to read from the btree in order
to do the update, and when reading is infrequent - perfect for the LRU
btree.
This will make LRU btree updates fast enough that we'll be able to use
it for persistently indexing buckets by fragmentation, which will be a
massive boost to copygc performance.
Changes:
- A new btree_insert_type enum, for btree_insert_entries. Specifies
btree, btree key cache, or btree write buffer.
- bch2_trans_update_buffered(): updates via the btree write buffer
don't need a btree path, so we need a new update path.
- Transaction commit path changes:
The update to the btree write buffer both mutates global, and can
fail if there isn't currently room. Therefore we do all write buffer
updates in the transaction all at once, and also if it fails we have
to revert filesystem usage counter changes.
If there isn't room we flush the write buffer in the transaction
commit error path and retry.
- A new persistent option, for specifying the number of entries in the
write buffer.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2023-01-04 05:00:50 +00:00
|
|
|
dst->persistent_reserved[i] -= deltas->persistent_reserved[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (added > 0) {
|
|
|
|
trans->disk_res->sectors += added;
|
|
|
|
this_cpu_add(*c->online_reserved, added);
|
|
|
|
}
|
|
|
|
|
|
|
|
preempt_enable();
|
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
}
|
|
|
|
|
2023-11-11 20:08:36 +00:00
|
|
|
void bch2_trans_account_disk_usage_change(struct btree_trans *trans)
|
2021-04-04 00:29:05 +00:00
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2023-11-11 20:08:36 +00:00
|
|
|
u64 disk_res_sectors = trans->disk_res ? trans->disk_res->sectors : 0;
|
2021-04-04 00:29:05 +00:00
|
|
|
static int warned_disk_usage = 0;
|
|
|
|
bool warn = false;
|
|
|
|
|
2021-11-28 19:31:19 +00:00
|
|
|
percpu_down_read(&c->mark_lock);
|
2021-04-04 00:29:05 +00:00
|
|
|
preempt_disable();
|
2023-11-11 20:08:36 +00:00
|
|
|
struct bch_fs_usage_base *dst = &fs_usage_ptr(c, trans->journal_res.seq, false)->b;
|
|
|
|
struct bch_fs_usage_base *src = &trans->fs_usage_delta;
|
2021-04-04 00:29:05 +00:00
|
|
|
|
2023-11-11 20:08:36 +00:00
|
|
|
s64 added = src->btree + src->data + src->reserved;
|
2021-04-04 00:29:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Not allowed to reduce sectors_available except by getting a
|
|
|
|
* reservation:
|
|
|
|
*/
|
2023-11-11 20:08:36 +00:00
|
|
|
s64 should_not_have_added = added - (s64) disk_res_sectors;
|
2021-04-04 00:29:05 +00:00
|
|
|
if (unlikely(should_not_have_added > 0)) {
|
2021-06-11 03:33:27 +00:00
|
|
|
u64 old, new, v = atomic64_read(&c->sectors_available);
|
|
|
|
|
|
|
|
do {
|
|
|
|
old = v;
|
|
|
|
new = max_t(s64, 0, old - should_not_have_added);
|
|
|
|
} while ((v = atomic64_cmpxchg(&c->sectors_available,
|
|
|
|
old, new)) != old);
|
|
|
|
|
2021-04-04 00:29:05 +00:00
|
|
|
added -= should_not_have_added;
|
|
|
|
warn = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (added > 0) {
|
|
|
|
trans->disk_res->sectors -= added;
|
|
|
|
this_cpu_sub(*c->online_reserved, added);
|
|
|
|
}
|
|
|
|
|
2023-11-11 20:08:36 +00:00
|
|
|
dst->hidden += src->hidden;
|
|
|
|
dst->btree += src->btree;
|
|
|
|
dst->data += src->data;
|
|
|
|
dst->cached += src->cached;
|
|
|
|
dst->reserved += src->reserved;
|
|
|
|
dst->nr_inodes += src->nr_inodes;
|
|
|
|
|
2021-04-04 00:29:05 +00:00
|
|
|
preempt_enable();
|
2021-11-28 19:31:19 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
2021-04-04 00:29:05 +00:00
|
|
|
|
|
|
|
if (unlikely(warn) && !xchg(&warned_disk_usage, 1))
|
2023-01-25 15:07:52 +00:00
|
|
|
bch2_trans_inconsistent(trans,
|
2023-10-27 21:19:31 +00:00
|
|
|
"disk usage increased %lli more than %llu sectors reserved)",
|
2023-01-25 15:07:52 +00:00
|
|
|
should_not_have_added, disk_res_sectors);
|
2023-11-11 20:08:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int bch2_trans_fs_usage_apply(struct btree_trans *trans,
|
|
|
|
struct replicas_delta_list *deltas)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct replicas_delta *d, *d2;
|
|
|
|
struct replicas_delta *top = (void *) deltas->d + deltas->used;
|
|
|
|
struct bch_fs_usage *dst;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
preempt_disable();
|
|
|
|
dst = fs_usage_ptr(c, trans->journal_res.seq, false);
|
|
|
|
|
|
|
|
for (d = deltas->d; d != top; d = replicas_delta_next(d))
|
|
|
|
if (__update_replicas(c, dst, &d->r, d->delta))
|
|
|
|
goto need_mark;
|
|
|
|
|
|
|
|
dst->b.nr_inodes += deltas->nr_inodes;
|
|
|
|
|
|
|
|
for (i = 0; i < BCH_REPLICAS_MAX; i++) {
|
|
|
|
dst->b.reserved += deltas->persistent_reserved[i];
|
|
|
|
dst->persistent_reserved[i] += deltas->persistent_reserved[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
preempt_enable();
|
|
|
|
percpu_up_read(&c->mark_lock);
|
2021-11-28 19:08:58 +00:00
|
|
|
return 0;
|
|
|
|
need_mark:
|
|
|
|
/* revert changes: */
|
|
|
|
for (d2 = deltas->d; d2 != d; d2 = replicas_delta_next(d2))
|
|
|
|
BUG_ON(__update_replicas(c, dst, &d2->r, -d2->delta));
|
|
|
|
|
|
|
|
preempt_enable();
|
2021-11-28 19:31:19 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
2021-11-28 19:08:58 +00:00
|
|
|
return -1;
|
2021-04-04 00:29:05 +00:00
|
|
|
}
|
|
|
|
|
2023-12-30 00:02:56 +00:00
|
|
|
/* KEY_TYPE_extent: */
|
2019-03-11 18:59:58 +00:00
|
|
|
|
2023-12-30 00:02:56 +00:00
|
|
|
static int __mark_pointer(struct btree_trans *trans,
|
|
|
|
struct bkey_s_c k,
|
|
|
|
const struct bch_extent_ptr *ptr,
|
|
|
|
s64 sectors, enum bch_data_type ptr_data_type,
|
|
|
|
u8 bucket_gen, u8 *bucket_data_type,
|
|
|
|
u32 *dirty_sectors, u32 *cached_sectors)
|
|
|
|
{
|
|
|
|
u32 *dst_sectors = !ptr->cached
|
|
|
|
? dirty_sectors
|
|
|
|
: cached_sectors;
|
2024-04-19 23:03:58 +00:00
|
|
|
int ret = bch2_bucket_ref_update(trans, k, ptr, sectors, ptr_data_type,
|
|
|
|
bucket_gen, *bucket_data_type, dst_sectors);
|
2023-12-30 00:02:56 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!*dirty_sectors && !*cached_sectors)
|
|
|
|
*bucket_data_type = 0;
|
|
|
|
else if (*bucket_data_type != BCH_DATA_stripe)
|
|
|
|
*bucket_data_type = ptr_data_type;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bch2_trigger_pointer(struct btree_trans *trans,
|
|
|
|
enum btree_id btree_id, unsigned level,
|
|
|
|
struct bkey_s_c k, struct extent_ptr_decoded p,
|
2024-03-25 23:26:05 +00:00
|
|
|
const union bch_extent_entry *entry,
|
2024-04-07 22:05:34 +00:00
|
|
|
s64 *sectors,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2020-10-20 02:36:24 +00:00
|
|
|
{
|
2024-04-07 22:05:34 +00:00
|
|
|
bool insert = !(flags & BTREE_TRIGGER_overwrite);
|
2023-03-31 20:24:45 +00:00
|
|
|
struct bpos bucket;
|
2022-03-18 00:51:27 +00:00
|
|
|
struct bch_backpointer bp;
|
2020-10-20 02:36:24 +00:00
|
|
|
|
2024-03-25 23:26:05 +00:00
|
|
|
bch2_extent_ptr_to_bp(trans->c, btree_id, level, k, p, entry, &bucket, &bp);
|
2023-12-30 00:02:56 +00:00
|
|
|
*sectors = insert ? bp.bucket_len : -((s64) bp.bucket_len);
|
2023-06-24 16:17:57 +00:00
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_transactional) {
|
2023-12-30 00:02:56 +00:00
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_i_alloc_v4 *a = bch2_trans_start_alloc_update(trans, &iter, bucket);
|
|
|
|
int ret = PTR_ERR_OR_ZERO(a);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = __mark_pointer(trans, k, &p.ptr, *sectors, bp.data_type,
|
|
|
|
a->v.gen, &a->v.data_type,
|
|
|
|
&a->v.dirty_sectors, &a->v.cached_sectors) ?:
|
|
|
|
bch2_trans_update(trans, &iter, &a->k_i, 0);
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2022-03-18 00:51:27 +00:00
|
|
|
|
|
|
|
if (ret)
|
2023-06-24 16:17:57 +00:00
|
|
|
return ret;
|
2023-12-30 00:02:56 +00:00
|
|
|
|
|
|
|
if (!p.ptr.cached) {
|
|
|
|
ret = bch2_bucket_backpointer_mod(trans, bucket, bp, k, insert);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_gc) {
|
2023-12-30 00:02:56 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2024-04-12 03:31:55 +00:00
|
|
|
struct bch_dev *ca = bch2_dev_bkey_exists(c, p.ptr.dev);
|
2024-03-25 23:26:05 +00:00
|
|
|
enum bch_data_type data_type = bch2_bkey_ptr_data_type(k, p, entry);
|
2023-12-30 00:02:56 +00:00
|
|
|
|
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
struct bucket *g = PTR_GC_BUCKET(ca, &p.ptr);
|
|
|
|
bucket_lock(g);
|
2024-04-30 04:29:24 +00:00
|
|
|
struct bch_alloc_v4 old = bucket_m_to_alloc(*g);
|
2023-12-30 00:02:56 +00:00
|
|
|
|
|
|
|
u8 bucket_data_type = g->data_type;
|
|
|
|
int ret = __mark_pointer(trans, k, &p.ptr, *sectors,
|
|
|
|
data_type, g->gen,
|
|
|
|
&bucket_data_type,
|
|
|
|
&g->dirty_sectors,
|
|
|
|
&g->cached_sectors);
|
|
|
|
if (ret) {
|
|
|
|
bucket_unlock(g);
|
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
g->data_type = bucket_data_type;
|
2024-04-30 04:29:24 +00:00
|
|
|
struct bch_alloc_v4 new = bucket_m_to_alloc(*g);
|
2023-12-30 00:02:56 +00:00
|
|
|
bucket_unlock(g);
|
2024-04-30 04:29:24 +00:00
|
|
|
bch2_dev_usage_update(c, ca, &old, &new, 0, true);
|
2023-12-30 00:02:56 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
2022-03-18 00:51:27 +00:00
|
|
|
}
|
|
|
|
|
2023-06-24 16:17:57 +00:00
|
|
|
return 0;
|
2019-03-11 18:59:58 +00:00
|
|
|
}
|
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
static int bch2_trigger_stripe_ptr(struct btree_trans *trans,
|
2023-12-30 00:02:56 +00:00
|
|
|
struct bkey_s_c k,
|
2023-12-30 00:08:54 +00:00
|
|
|
struct extent_ptr_decoded p,
|
2023-12-30 00:02:56 +00:00
|
|
|
enum bch_data_type data_type,
|
2024-04-07 22:05:34 +00:00
|
|
|
s64 sectors,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2023-12-30 00:02:56 +00:00
|
|
|
{
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_transactional) {
|
2023-12-30 00:08:54 +00:00
|
|
|
struct btree_iter iter;
|
|
|
|
struct bkey_i_stripe *s = bch2_bkey_get_mut_typed(trans, &iter,
|
|
|
|
BTREE_ID_stripes, POS(0, p.ec.idx),
|
2024-04-07 22:05:34 +00:00
|
|
|
BTREE_ITER_with_updates, stripe);
|
2023-12-30 00:08:54 +00:00
|
|
|
int ret = PTR_ERR_OR_ZERO(s);
|
|
|
|
if (unlikely(ret)) {
|
|
|
|
bch2_trans_inconsistent_on(bch2_err_matches(ret, ENOENT), trans,
|
|
|
|
"pointer to nonexistent stripe %llu",
|
|
|
|
(u64) p.ec.idx);
|
|
|
|
goto err;
|
|
|
|
}
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
if (!bch2_ptr_matches_stripe(&s->v, p)) {
|
|
|
|
bch2_trans_inconsistent(trans,
|
|
|
|
"stripe pointer doesn't match stripe %llu",
|
|
|
|
(u64) p.ec.idx);
|
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
stripe_blockcount_set(&s->v, p.ec.block,
|
|
|
|
stripe_blockcount_get(&s->v, p.ec.block) +
|
|
|
|
sectors);
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
struct bch_replicas_padded r;
|
|
|
|
bch2_bkey_to_replicas(&r.e, bkey_i_to_s_c(&s->k_i));
|
|
|
|
r.e.data_type = data_type;
|
|
|
|
ret = bch2_update_replicas_list(trans, &r.e, sectors);
|
|
|
|
err:
|
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
return ret;
|
2023-12-30 00:02:56 +00:00
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_gc) {
|
2023-12-30 00:08:54 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
BUG_ON(!(flags & BTREE_TRIGGER_gc));
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
struct gc_stripe *m = genradix_ptr_alloc(&c->gc_stripes, p.ec.idx, GFP_KERNEL);
|
|
|
|
if (!m) {
|
|
|
|
bch_err(c, "error allocating memory for gc_stripes, idx %llu",
|
|
|
|
(u64) p.ec.idx);
|
|
|
|
return -BCH_ERR_ENOMEM_mark_stripe_ptr;
|
|
|
|
}
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
mutex_lock(&c->ec_stripes_heap_lock);
|
2023-12-30 00:02:56 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
if (!m || !m->alive) {
|
|
|
|
mutex_unlock(&c->ec_stripes_heap_lock);
|
|
|
|
struct printbuf buf = PRINTBUF;
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k);
|
|
|
|
bch_err_ratelimited(c, "pointer to nonexistent stripe %llu\n while marking %s",
|
|
|
|
(u64) p.ec.idx, buf.buf);
|
|
|
|
printbuf_exit(&buf);
|
|
|
|
bch2_inconsistent_error(c);
|
|
|
|
return -EIO;
|
|
|
|
}
|
2019-03-11 18:59:58 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
m->block_sectors[p.ec.block] += sectors;
|
2021-01-11 18:51:23 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
struct bch_replicas_padded r = m->r;
|
|
|
|
mutex_unlock(&c->ec_stripes_heap_lock);
|
2019-03-11 18:59:58 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
r.e.data_type = data_type;
|
|
|
|
bch2_update_replicas(c, k, &r.e, sectors, trans->journal_res.seq, true);
|
|
|
|
}
|
2021-12-05 04:07:33 +00:00
|
|
|
|
2023-12-30 00:08:54 +00:00
|
|
|
return 0;
|
2019-03-11 18:59:58 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 07:11:00 +00:00
|
|
|
static int __trigger_extent(struct btree_trans *trans,
|
|
|
|
enum btree_id btree_id, unsigned level,
|
2024-04-07 22:05:34 +00:00
|
|
|
struct bkey_s_c k,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2023-12-30 00:02:56 +00:00
|
|
|
{
|
2024-04-07 22:05:34 +00:00
|
|
|
bool gc = flags & BTREE_TRIGGER_gc;
|
2023-12-30 00:02:56 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
|
|
|
|
const union bch_extent_entry *entry;
|
|
|
|
struct extent_ptr_decoded p;
|
|
|
|
struct bch_replicas_padded r;
|
|
|
|
enum bch_data_type data_type = bkey_is_btree_ptr(k.k)
|
|
|
|
? BCH_DATA_btree
|
|
|
|
: BCH_DATA_user;
|
2024-04-30 05:49:59 +00:00
|
|
|
s64 replicas_sectors = 0;
|
2023-12-28 07:11:00 +00:00
|
|
|
int ret = 0;
|
2023-12-30 00:02:56 +00:00
|
|
|
|
|
|
|
r.e.data_type = data_type;
|
|
|
|
r.e.nr_devs = 0;
|
|
|
|
r.e.nr_required = 1;
|
|
|
|
|
|
|
|
bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
|
|
|
|
s64 disk_sectors;
|
2024-03-25 23:26:05 +00:00
|
|
|
ret = bch2_trigger_pointer(trans, btree_id, level, k, p, entry, &disk_sectors, flags);
|
2023-12-30 00:02:56 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
bool stale = ret > 0;
|
|
|
|
|
|
|
|
if (p.ptr.cached) {
|
|
|
|
if (!stale) {
|
2023-12-28 07:11:00 +00:00
|
|
|
ret = !gc
|
|
|
|
? bch2_update_cached_sectors_list(trans, p.ptr.dev, disk_sectors)
|
|
|
|
: update_cached_sectors(c, k, p.ptr.dev, disk_sectors, 0, true);
|
2024-03-18 01:51:19 +00:00
|
|
|
bch2_fs_fatal_err_on(ret && gc, c, "%s: no replicas entry while updating cached sectors",
|
|
|
|
bch2_err_str(ret));
|
2023-12-28 07:11:00 +00:00
|
|
|
if (ret)
|
2023-12-30 00:02:56 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} else if (!p.has_ec) {
|
2024-04-30 05:49:59 +00:00
|
|
|
replicas_sectors += disk_sectors;
|
2023-12-30 00:02:56 +00:00
|
|
|
r.e.devs[r.e.nr_devs++] = p.ptr.dev;
|
|
|
|
} else {
|
2023-12-30 00:08:54 +00:00
|
|
|
ret = bch2_trigger_stripe_ptr(trans, k, p, data_type, disk_sectors, flags);
|
2023-12-30 00:02:56 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There may be other dirty pointers in this extent, but
|
|
|
|
* if so they're not required for mounting if we have an
|
|
|
|
* erasure coded pointer in this extent:
|
|
|
|
*/
|
|
|
|
r.e.nr_required = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r.e.nr_devs) {
|
2023-12-28 07:11:00 +00:00
|
|
|
ret = !gc
|
2024-04-30 05:49:59 +00:00
|
|
|
? bch2_update_replicas_list(trans, &r.e, replicas_sectors)
|
|
|
|
: bch2_update_replicas(c, k, &r.e, replicas_sectors, 0, true);
|
2023-12-28 07:11:00 +00:00
|
|
|
if (unlikely(ret && gc)) {
|
2023-12-30 00:02:56 +00:00
|
|
|
struct printbuf buf = PRINTBUF;
|
|
|
|
|
|
|
|
bch2_bkey_val_to_text(&buf, c, k);
|
2024-03-18 01:51:19 +00:00
|
|
|
bch2_fs_fatal_error(c, ": no replicas entry for %s", buf.buf);
|
2023-12-30 00:02:56 +00:00
|
|
|
printbuf_exit(&buf);
|
|
|
|
}
|
2023-12-28 07:11:00 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2023-12-30 00:02:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-12-28 07:11:00 +00:00
|
|
|
int bch2_trigger_extent(struct btree_trans *trans,
|
2024-04-07 23:07:09 +00:00
|
|
|
enum btree_id btree, unsigned level,
|
2023-12-28 07:11:00 +00:00
|
|
|
struct bkey_s_c old, struct bkey_s new,
|
2024-04-07 22:05:34 +00:00
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2019-03-11 18:59:58 +00:00
|
|
|
{
|
2023-12-28 07:17:18 +00:00
|
|
|
struct bkey_ptrs_c new_ptrs = bch2_bkey_ptrs_c(new.s_c);
|
|
|
|
struct bkey_ptrs_c old_ptrs = bch2_bkey_ptrs_c(old);
|
|
|
|
unsigned new_ptrs_bytes = (void *) new_ptrs.end - (void *) new_ptrs.start;
|
|
|
|
unsigned old_ptrs_bytes = (void *) old_ptrs.end - (void *) old_ptrs.start;
|
|
|
|
|
2024-04-07 23:07:09 +00:00
|
|
|
if (unlikely(flags & BTREE_TRIGGER_check_repair))
|
|
|
|
return bch2_check_fix_ptrs(trans, btree, level, new.s_c, flags);
|
|
|
|
|
2023-12-28 07:17:18 +00:00
|
|
|
/* if pointers aren't changing - nothing to do: */
|
|
|
|
if (new_ptrs_bytes == old_ptrs_bytes &&
|
|
|
|
!memcmp(new_ptrs.start,
|
|
|
|
old_ptrs.start,
|
|
|
|
new_ptrs_bytes))
|
|
|
|
return 0;
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_transactional) {
|
2023-12-28 07:11:00 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
|
|
|
int mod = (int) bch2_bkey_needs_rebalance(c, new.s_c) -
|
|
|
|
(int) bch2_bkey_needs_rebalance(c, old);
|
2019-03-11 18:59:58 +00:00
|
|
|
|
2023-12-28 07:11:00 +00:00
|
|
|
if (mod) {
|
2024-02-09 00:10:19 +00:00
|
|
|
int ret = bch2_btree_bit_mod_buffered(trans, BTREE_ID_rebalance_work,
|
|
|
|
new.k->p, mod > 0);
|
2019-10-08 22:45:29 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2019-03-11 18:59:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & (BTREE_TRIGGER_transactional|BTREE_TRIGGER_gc))
|
2024-04-07 23:07:09 +00:00
|
|
|
return trigger_run_overwrite_then_insert(__trigger_extent, trans, btree, level, old, new, flags);
|
2019-03-11 18:59:58 +00:00
|
|
|
|
2023-12-30 00:02:56 +00:00
|
|
|
return 0;
|
2019-03-11 18:59:58 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 05:50:21 +00:00
|
|
|
/* KEY_TYPE_reservation */
|
|
|
|
|
|
|
|
static int __trigger_reservation(struct btree_trans *trans,
|
2024-04-07 22:05:34 +00:00
|
|
|
enum btree_id btree_id, unsigned level, struct bkey_s_c k,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2021-06-11 01:44:27 +00:00
|
|
|
{
|
2023-12-28 05:50:21 +00:00
|
|
|
struct bch_fs *c = trans->c;
|
2021-06-11 01:44:27 +00:00
|
|
|
unsigned replicas = bkey_s_c_to_reservation(k).v->nr_replicas;
|
2023-12-28 05:50:21 +00:00
|
|
|
s64 sectors = (s64) k.k->size * replicas;
|
2021-06-11 01:44:27 +00:00
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_overwrite)
|
2021-06-11 01:44:27 +00:00
|
|
|
sectors = -sectors;
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_transactional) {
|
2023-12-28 05:50:21 +00:00
|
|
|
int ret = bch2_replicas_deltas_realloc(trans, 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2021-06-11 01:44:27 +00:00
|
|
|
|
2023-12-28 05:50:21 +00:00
|
|
|
struct replicas_delta_list *d = trans->fs_usage_deltas;
|
|
|
|
replicas = min(replicas, ARRAY_SIZE(d->persistent_reserved));
|
|
|
|
|
|
|
|
d->persistent_reserved[replicas - 1] += sectors;
|
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_gc) {
|
2023-12-28 05:50:21 +00:00
|
|
|
percpu_down_read(&c->mark_lock);
|
|
|
|
preempt_disable();
|
|
|
|
|
|
|
|
struct bch_fs_usage *fs_usage = this_cpu_ptr(c->usage_gc);
|
|
|
|
|
|
|
|
replicas = min(replicas, ARRAY_SIZE(fs_usage->persistent_reserved));
|
2023-11-17 05:03:45 +00:00
|
|
|
fs_usage->b.reserved += sectors;
|
2023-12-28 05:50:21 +00:00
|
|
|
fs_usage->persistent_reserved[replicas - 1] += sectors;
|
|
|
|
|
|
|
|
preempt_enable();
|
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
}
|
2021-06-11 01:44:27 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-12-28 05:50:21 +00:00
|
|
|
int bch2_trigger_reservation(struct btree_trans *trans,
|
|
|
|
enum btree_id btree_id, unsigned level,
|
|
|
|
struct bkey_s_c old, struct bkey_s new,
|
2024-04-07 22:05:34 +00:00
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2023-06-23 00:18:12 +00:00
|
|
|
{
|
2023-12-28 05:50:21 +00:00
|
|
|
return trigger_run_overwrite_then_insert(__trigger_reservation, trans, btree_id, level, old, new, flags);
|
2023-06-23 00:18:12 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 05:50:21 +00:00
|
|
|
/* Mark superblocks: */
|
|
|
|
|
2021-01-22 22:56:34 +00:00
|
|
|
static int __bch2_trans_mark_metadata_bucket(struct btree_trans *trans,
|
2024-04-07 06:11:03 +00:00
|
|
|
struct bch_dev *ca, u64 b,
|
2021-01-22 22:56:34 +00:00
|
|
|
enum bch_data_type type,
|
|
|
|
unsigned sectors)
|
|
|
|
{
|
|
|
|
struct bch_fs *c = trans->c;
|
2021-08-30 19:18:31 +00:00
|
|
|
struct btree_iter iter;
|
2021-01-22 22:56:34 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
2024-04-07 06:11:03 +00:00
|
|
|
struct bkey_i_alloc_v4 *a =
|
|
|
|
bch2_trans_start_alloc_update(trans, &iter, POS(ca->dev_idx, b));
|
2022-01-01 01:03:29 +00:00
|
|
|
if (IS_ERR(a))
|
|
|
|
return PTR_ERR(a);
|
2021-01-22 22:56:34 +00:00
|
|
|
|
2023-03-06 10:29:12 +00:00
|
|
|
if (a->v.data_type && type && a->v.data_type != type) {
|
2021-01-22 22:56:34 +00:00
|
|
|
bch2_fsck_err(c, FSCK_CAN_IGNORE|FSCK_NEED_FSCK,
|
2023-10-25 00:44:36 +00:00
|
|
|
BCH_FSCK_ERR_bucket_metadata_type_mismatch,
|
2021-01-22 22:56:34 +00:00
|
|
|
"bucket %llu:%llu gen %u different types of data in same bucket: %s, %s\n"
|
|
|
|
"while marking %s",
|
2022-01-01 01:03:29 +00:00
|
|
|
iter.pos.inode, iter.pos.offset, a->v.gen,
|
2024-01-07 01:57:43 +00:00
|
|
|
bch2_data_type_str(a->v.data_type),
|
|
|
|
bch2_data_type_str(type),
|
|
|
|
bch2_data_type_str(type));
|
2021-01-22 22:56:34 +00:00
|
|
|
ret = -EIO;
|
2023-10-21 17:54:39 +00:00
|
|
|
goto err;
|
2021-01-22 22:56:34 +00:00
|
|
|
}
|
|
|
|
|
2023-10-21 17:54:39 +00:00
|
|
|
if (a->v.data_type != type ||
|
|
|
|
a->v.dirty_sectors != sectors) {
|
|
|
|
a->v.data_type = type;
|
|
|
|
a->v.dirty_sectors = sectors;
|
|
|
|
ret = bch2_trans_update(trans, &iter, &a->k_i, 0);
|
|
|
|
}
|
|
|
|
err:
|
2021-08-30 19:18:31 +00:00
|
|
|
bch2_trans_iter_exit(trans, &iter);
|
2021-01-22 22:56:34 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-04-07 06:11:03 +00:00
|
|
|
static int bch2_mark_metadata_bucket(struct bch_fs *c, struct bch_dev *ca,
|
2024-04-07 22:05:34 +00:00
|
|
|
u64 b, enum bch_data_type data_type, unsigned sectors,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2024-04-07 06:11:03 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
percpu_down_read(&c->mark_lock);
|
2024-04-30 04:29:24 +00:00
|
|
|
struct bucket *g = gc_bucket(ca, b);
|
2024-04-07 06:11:03 +00:00
|
|
|
|
|
|
|
bucket_lock(g);
|
2024-04-30 04:29:24 +00:00
|
|
|
struct bch_alloc_v4 old = bucket_m_to_alloc(*g);
|
2024-04-07 06:11:03 +00:00
|
|
|
|
|
|
|
if (bch2_fs_inconsistent_on(g->data_type &&
|
|
|
|
g->data_type != data_type, c,
|
|
|
|
"different types of data in same bucket: %s, %s",
|
|
|
|
bch2_data_type_str(g->data_type),
|
|
|
|
bch2_data_type_str(data_type))) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bch2_fs_inconsistent_on((u64) g->dirty_sectors + sectors > ca->mi.bucket_size, c,
|
|
|
|
"bucket %u:%llu gen %u data type %s sector count overflow: %u + %u > bucket size",
|
|
|
|
ca->dev_idx, b, g->gen,
|
|
|
|
bch2_data_type_str(g->data_type ?: data_type),
|
|
|
|
g->dirty_sectors, sectors)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
g->data_type = data_type;
|
|
|
|
g->dirty_sectors += sectors;
|
2024-04-30 04:29:24 +00:00
|
|
|
struct bch_alloc_v4 new = bucket_m_to_alloc(*g);
|
2024-04-07 06:11:03 +00:00
|
|
|
err:
|
|
|
|
bucket_unlock(g);
|
|
|
|
if (!ret)
|
2024-04-30 04:29:24 +00:00
|
|
|
bch2_dev_usage_update(c, ca, &old, &new, 0, true);
|
2024-04-07 06:11:03 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-01-22 22:56:34 +00:00
|
|
|
int bch2_trans_mark_metadata_bucket(struct btree_trans *trans,
|
2024-04-07 22:05:34 +00:00
|
|
|
struct bch_dev *ca, u64 b,
|
|
|
|
enum bch_data_type type, unsigned sectors,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2021-01-22 22:56:34 +00:00
|
|
|
{
|
2024-04-07 06:11:03 +00:00
|
|
|
BUG_ON(type != BCH_DATA_free &&
|
|
|
|
type != BCH_DATA_sb &&
|
|
|
|
type != BCH_DATA_journal);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Backup superblock might be past the end of our normal usable space:
|
|
|
|
*/
|
|
|
|
if (b >= ca->mi.nbuckets)
|
|
|
|
return 0;
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
if (flags & BTREE_TRIGGER_gc)
|
2024-04-07 06:11:03 +00:00
|
|
|
return bch2_mark_metadata_bucket(trans->c, ca, b, type, sectors, flags);
|
2024-04-07 22:05:34 +00:00
|
|
|
else if (flags & BTREE_TRIGGER_transactional)
|
2024-04-07 06:11:03 +00:00
|
|
|
return commit_do(trans, NULL, NULL, 0,
|
|
|
|
__bch2_trans_mark_metadata_bucket(trans, ca, b, type, sectors));
|
|
|
|
else
|
|
|
|
BUG();
|
2021-01-22 22:56:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bch2_trans_mark_metadata_sectors(struct btree_trans *trans,
|
2024-04-07 22:05:34 +00:00
|
|
|
struct bch_dev *ca, u64 start, u64 end,
|
|
|
|
enum bch_data_type type, u64 *bucket, unsigned *bucket_sectors,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2021-01-22 22:56:34 +00:00
|
|
|
{
|
|
|
|
do {
|
|
|
|
u64 b = sector_to_bucket(ca, start);
|
|
|
|
unsigned sectors =
|
|
|
|
min_t(u64, bucket_to_sector(ca, b + 1), end) - start;
|
|
|
|
|
2021-04-15 00:25:33 +00:00
|
|
|
if (b != *bucket && *bucket_sectors) {
|
|
|
|
int ret = bch2_trans_mark_metadata_bucket(trans, ca, *bucket,
|
2024-04-07 06:11:03 +00:00
|
|
|
type, *bucket_sectors, flags);
|
2021-04-15 00:25:33 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2021-01-22 22:56:34 +00:00
|
|
|
|
2021-04-15 00:25:33 +00:00
|
|
|
*bucket_sectors = 0;
|
2021-01-22 22:56:34 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 00:25:33 +00:00
|
|
|
*bucket = b;
|
2021-01-22 22:56:34 +00:00
|
|
|
*bucket_sectors += sectors;
|
|
|
|
start += sectors;
|
2021-04-15 00:25:33 +00:00
|
|
|
} while (start < end);
|
2021-01-22 22:56:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
static int __bch2_trans_mark_dev_sb(struct btree_trans *trans, struct bch_dev *ca,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2021-01-22 22:56:34 +00:00
|
|
|
{
|
|
|
|
struct bch_sb_layout *layout = &ca->disk_sb.sb->layout;
|
|
|
|
u64 bucket = 0;
|
|
|
|
unsigned i, bucket_sectors = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for (i = 0; i < layout->nr_superblocks; i++) {
|
|
|
|
u64 offset = le64_to_cpu(layout->sb_offset[i]);
|
|
|
|
|
|
|
|
if (offset == BCH_SB_SECTOR) {
|
2021-04-15 00:25:33 +00:00
|
|
|
ret = bch2_trans_mark_metadata_sectors(trans, ca,
|
2021-01-22 22:56:34 +00:00
|
|
|
0, BCH_SB_SECTOR,
|
2024-04-07 06:11:03 +00:00
|
|
|
BCH_DATA_sb, &bucket, &bucket_sectors, flags);
|
2021-01-22 22:56:34 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-04-15 00:25:33 +00:00
|
|
|
ret = bch2_trans_mark_metadata_sectors(trans, ca, offset,
|
2021-01-22 22:56:34 +00:00
|
|
|
offset + (1 << layout->sb_max_size_bits),
|
2024-04-07 06:11:03 +00:00
|
|
|
BCH_DATA_sb, &bucket, &bucket_sectors, flags);
|
2021-01-22 22:56:34 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bucket_sectors) {
|
2021-04-15 00:25:33 +00:00
|
|
|
ret = bch2_trans_mark_metadata_bucket(trans, ca,
|
2024-04-07 06:11:03 +00:00
|
|
|
bucket, BCH_DATA_sb, bucket_sectors, flags);
|
2021-01-22 22:56:34 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ca->journal.nr; i++) {
|
2021-04-15 00:25:33 +00:00
|
|
|
ret = bch2_trans_mark_metadata_bucket(trans, ca,
|
2021-01-22 22:56:34 +00:00
|
|
|
ca->journal.buckets[i],
|
2024-04-07 06:11:03 +00:00
|
|
|
BCH_DATA_journal, ca->mi.bucket_size, flags);
|
2021-01-22 22:56:34 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
int bch2_trans_mark_dev_sb(struct bch_fs *c, struct bch_dev *ca,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2021-01-22 22:56:34 +00:00
|
|
|
{
|
2024-04-07 06:11:03 +00:00
|
|
|
int ret = bch2_trans_run(c,
|
|
|
|
__bch2_trans_mark_dev_sb(trans, ca, flags));
|
2023-12-17 03:43:41 +00:00
|
|
|
bch_err_fn(c, ret);
|
2023-06-20 17:49:25 +00:00
|
|
|
return ret;
|
2021-01-22 22:56:34 +00:00
|
|
|
}
|
|
|
|
|
2024-04-07 22:05:34 +00:00
|
|
|
int bch2_trans_mark_dev_sbs_flags(struct bch_fs *c,
|
|
|
|
enum btree_iter_update_trigger_flags flags)
|
2023-10-21 17:54:39 +00:00
|
|
|
{
|
2023-12-17 04:47:29 +00:00
|
|
|
for_each_online_member(c, ca) {
|
2024-04-07 06:11:03 +00:00
|
|
|
int ret = bch2_trans_mark_dev_sb(c, ca, flags);
|
2023-10-21 17:54:39 +00:00
|
|
|
if (ret) {
|
|
|
|
percpu_ref_put(&ca->ref);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-07 06:11:03 +00:00
|
|
|
int bch2_trans_mark_dev_sbs(struct bch_fs *c)
|
|
|
|
{
|
2024-04-07 22:05:34 +00:00
|
|
|
return bch2_trans_mark_dev_sbs_flags(c, BTREE_TRIGGER_transactional);
|
2024-04-07 06:11:03 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
/* Disk reservations: */
|
|
|
|
|
|
|
|
#define SECTORS_CACHE 1024
|
|
|
|
|
2022-11-01 02:28:09 +00:00
|
|
|
int __bch2_disk_reservation_add(struct bch_fs *c, struct disk_reservation *res,
|
2021-01-17 18:19:16 +00:00
|
|
|
u64 sectors, int flags)
|
2017-03-17 06:18:50 +00:00
|
|
|
{
|
2018-11-27 13:23:22 +00:00
|
|
|
struct bch_fs_pcpu *pcpu;
|
2017-03-17 06:18:50 +00:00
|
|
|
u64 old, v, get;
|
|
|
|
s64 sectors_available;
|
|
|
|
int ret;
|
|
|
|
|
2018-11-26 05:13:33 +00:00
|
|
|
percpu_down_read(&c->mark_lock);
|
2017-03-17 06:18:50 +00:00
|
|
|
preempt_disable();
|
2018-11-27 13:23:22 +00:00
|
|
|
pcpu = this_cpu_ptr(c->pcpu);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2018-11-27 13:23:22 +00:00
|
|
|
if (sectors <= pcpu->sectors_available)
|
2017-03-17 06:18:50 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
v = atomic64_read(&c->sectors_available);
|
|
|
|
do {
|
|
|
|
old = v;
|
|
|
|
get = min((u64) sectors + SECTORS_CACHE, old);
|
|
|
|
|
|
|
|
if (get < sectors) {
|
|
|
|
preempt_enable();
|
|
|
|
goto recalculate;
|
|
|
|
}
|
|
|
|
} while ((v = atomic64_cmpxchg(&c->sectors_available,
|
|
|
|
old, old - get)) != old);
|
|
|
|
|
2018-11-27 13:23:22 +00:00
|
|
|
pcpu->sectors_available += get;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
out:
|
2018-11-27 13:23:22 +00:00
|
|
|
pcpu->sectors_available -= sectors;
|
2019-02-11 00:34:47 +00:00
|
|
|
this_cpu_add(*c->online_reserved, sectors);
|
2018-11-27 13:23:22 +00:00
|
|
|
res->sectors += sectors;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
preempt_enable();
|
2018-11-26 05:13:33 +00:00
|
|
|
percpu_up_read(&c->mark_lock);
|
2017-03-17 06:18:50 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
recalculate:
|
2020-12-03 19:17:33 +00:00
|
|
|
mutex_lock(&c->sectors_available_lock);
|
2019-02-12 03:08:09 +00:00
|
|
|
|
2020-12-03 19:17:33 +00:00
|
|
|
percpu_u64_set(&c->pcpu->sectors_available, 0);
|
|
|
|
sectors_available = avail_factor(__bch2_fs_usage_read_short(c).free);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
if (sectors <= sectors_available ||
|
|
|
|
(flags & BCH_DISK_RESERVATION_NOFAIL)) {
|
|
|
|
atomic64_set(&c->sectors_available,
|
|
|
|
max_t(s64, 0, sectors_available - sectors));
|
2019-02-11 00:34:47 +00:00
|
|
|
this_cpu_add(*c->online_reserved, sectors);
|
2018-11-27 13:23:22 +00:00
|
|
|
res->sectors += sectors;
|
2017-03-17 06:18:50 +00:00
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
atomic64_set(&c->sectors_available, sectors_available);
|
2022-09-18 21:10:33 +00:00
|
|
|
ret = -BCH_ERR_ENOSPC_disk_reservation;
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2020-12-03 19:17:33 +00:00
|
|
|
mutex_unlock(&c->sectors_available_lock);
|
|
|
|
percpu_up_read(&c->mark_lock);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Startup/shutdown: */
|
|
|
|
|
2021-12-26 00:55:34 +00:00
|
|
|
static void bucket_gens_free_rcu(struct rcu_head *rcu)
|
|
|
|
{
|
|
|
|
struct bucket_gens *buckets =
|
|
|
|
container_of(rcu, struct bucket_gens, rcu);
|
|
|
|
|
2024-02-01 11:35:46 +00:00
|
|
|
kvfree(buckets);
|
2021-12-26 00:55:34 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 06:18:50 +00:00
|
|
|
int bch2_dev_buckets_resize(struct bch_fs *c, struct bch_dev *ca, u64 nbuckets)
|
|
|
|
{
|
2021-12-26 00:55:34 +00:00
|
|
|
struct bucket_gens *bucket_gens = NULL, *old_bucket_gens = NULL;
|
2018-11-19 06:16:07 +00:00
|
|
|
unsigned long *buckets_nouse = NULL;
|
2022-02-11 00:26:55 +00:00
|
|
|
bool resize = ca->bucket_gens != NULL;
|
2023-03-14 19:35:57 +00:00
|
|
|
int ret;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2024-02-01 11:35:46 +00:00
|
|
|
if (!(bucket_gens = kvmalloc(sizeof(struct bucket_gens) + nbuckets,
|
|
|
|
GFP_KERNEL|__GFP_ZERO))) {
|
2023-03-14 19:35:57 +00:00
|
|
|
ret = -BCH_ERR_ENOMEM_bucket_gens;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((c->opts.buckets_nouse &&
|
2024-02-01 11:35:46 +00:00
|
|
|
!(buckets_nouse = kvmalloc(BITS_TO_LONGS(nbuckets) *
|
|
|
|
sizeof(unsigned long),
|
|
|
|
GFP_KERNEL|__GFP_ZERO)))) {
|
2023-03-14 19:35:57 +00:00
|
|
|
ret = -BCH_ERR_ENOMEM_buckets_nouse;
|
2017-03-17 06:18:50 +00:00
|
|
|
goto err;
|
2023-03-14 19:35:57 +00:00
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-12-26 00:55:34 +00:00
|
|
|
bucket_gens->first_bucket = ca->mi.first_bucket;
|
|
|
|
bucket_gens->nbuckets = nbuckets;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
if (resize) {
|
2020-06-17 21:30:38 +00:00
|
|
|
down_write(&c->gc_lock);
|
2017-03-17 06:18:50 +00:00
|
|
|
down_write(&ca->bucket_lock);
|
2018-11-26 05:13:33 +00:00
|
|
|
percpu_down_write(&c->mark_lock);
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-26 00:55:34 +00:00
|
|
|
old_bucket_gens = rcu_dereference_protected(ca->bucket_gens, 1);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
if (resize) {
|
2022-02-11 00:26:55 +00:00
|
|
|
size_t n = min(bucket_gens->nbuckets, old_bucket_gens->nbuckets);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2021-12-26 00:55:34 +00:00
|
|
|
memcpy(bucket_gens->b,
|
|
|
|
old_bucket_gens->b,
|
|
|
|
n);
|
2022-02-14 06:42:31 +00:00
|
|
|
if (buckets_nouse)
|
|
|
|
memcpy(buckets_nouse,
|
|
|
|
ca->buckets_nouse,
|
|
|
|
BITS_TO_LONGS(n) * sizeof(unsigned long));
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
2021-12-26 00:55:34 +00:00
|
|
|
rcu_assign_pointer(ca->bucket_gens, bucket_gens);
|
|
|
|
bucket_gens = old_bucket_gens;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2018-11-19 06:16:07 +00:00
|
|
|
swap(ca->buckets_nouse, buckets_nouse);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2022-02-11 00:26:55 +00:00
|
|
|
nbuckets = ca->mi.nbuckets;
|
|
|
|
|
2020-06-17 21:30:38 +00:00
|
|
|
if (resize) {
|
2018-11-26 05:13:33 +00:00
|
|
|
percpu_up_write(&c->mark_lock);
|
2022-02-11 00:26:55 +00:00
|
|
|
up_write(&ca->bucket_lock);
|
2020-06-17 21:30:38 +00:00
|
|
|
up_write(&c->gc_lock);
|
|
|
|
}
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
err:
|
2024-02-01 11:35:46 +00:00
|
|
|
kvfree(buckets_nouse);
|
2021-12-26 00:55:34 +00:00
|
|
|
if (bucket_gens)
|
2022-02-07 00:20:36 +00:00
|
|
|
call_rcu(&bucket_gens->rcu, bucket_gens_free_rcu);
|
2017-03-17 06:18:50 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bch2_dev_buckets_free(struct bch_dev *ca)
|
|
|
|
{
|
2024-02-01 11:35:46 +00:00
|
|
|
kvfree(ca->buckets_nouse);
|
|
|
|
kvfree(rcu_dereference_protected(ca->bucket_gens, 1));
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2024-02-01 11:35:46 +00:00
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(ca->usage); i++)
|
2021-01-22 02:52:06 +00:00
|
|
|
free_percpu(ca->usage[i]);
|
|
|
|
kfree(ca->usage_base);
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int bch2_dev_buckets_alloc(struct bch_fs *c, struct bch_dev *ca)
|
|
|
|
{
|
2021-01-22 02:52:06 +00:00
|
|
|
ca->usage_base = kzalloc(sizeof(struct bch_dev_usage), GFP_KERNEL);
|
|
|
|
if (!ca->usage_base)
|
2023-03-14 19:35:57 +00:00
|
|
|
return -BCH_ERR_ENOMEM_usage_init;
|
2017-03-17 06:18:50 +00:00
|
|
|
|
2024-02-01 11:35:46 +00:00
|
|
|
for (unsigned i = 0; i < ARRAY_SIZE(ca->usage); i++) {
|
2021-01-22 02:52:06 +00:00
|
|
|
ca->usage[i] = alloc_percpu(struct bch_dev_usage);
|
|
|
|
if (!ca->usage[i])
|
2023-03-14 19:35:57 +00:00
|
|
|
return -BCH_ERR_ENOMEM_usage_init;
|
2021-01-22 02:52:06 +00:00
|
|
|
}
|
|
|
|
|
2022-10-19 22:31:33 +00:00
|
|
|
return bch2_dev_buckets_resize(c, ca, ca->mi.nbuckets);
|
2017-03-17 06:18:50 +00:00
|
|
|
}
|