summaryrefslogtreecommitdiff
path: root/fs/bcachefs/disk_accounting.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/bcachefs/disk_accounting.c')
-rw-r--r--fs/bcachefs/disk_accounting.c185
1 files changed, 95 insertions, 90 deletions
diff --git a/fs/bcachefs/disk_accounting.c b/fs/bcachefs/disk_accounting.c
index 831b4c10b856..a99f821c6a1c 100644
--- a/fs/bcachefs/disk_accounting.c
+++ b/fs/bcachefs/disk_accounting.c
@@ -883,113 +883,118 @@ int bch2_accounting_read(struct bch_fs *c)
*dst++ = *i;
keys->gap = keys->nr = dst - keys->data;
- guard(percpu_write)(&c->mark_lock);
-
- darray_for_each_reverse(acc->k, i) {
- struct disk_accounting_pos acc_k;
- bpos_to_disk_accounting_pos(&acc_k, i->pos);
-
- u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
- memset(v, 0, sizeof(v));
-
- for (unsigned j = 0; j < i->nr_counters; j++)
- v[j] = percpu_u64_get(i->v[0] + j);
+ CLASS(printbuf, underflow_err)();
- /*
- * If the entry counters are zeroed, it should be treated as
- * nonexistent - it might point to an invalid device.
- *
- * Remove it, so that if it's re-added it gets re-marked in the
- * superblock:
- */
- ret = bch2_is_zero(v, sizeof(v[0]) * i->nr_counters)
- ? -BCH_ERR_remove_disk_accounting_entry
- : bch2_disk_accounting_validate_late(trans, &acc_k, v, i->nr_counters);
-
- if (ret == -BCH_ERR_remove_disk_accounting_entry) {
- free_percpu(i->v[0]);
- free_percpu(i->v[1]);
- darray_remove_item(&acc->k, i);
- ret = 0;
- continue;
- }
+ scoped_guard(percpu_write, &c->mark_lock) {
+ darray_for_each_reverse(acc->k, i) {
+ struct disk_accounting_pos acc_k;
+ bpos_to_disk_accounting_pos(&acc_k, i->pos);
- if (ret)
- return ret;
- }
+ u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
+ memset(v, 0, sizeof(v));
- eytzinger0_sort(acc->k.data, acc->k.nr, sizeof(acc->k.data[0]),
- accounting_pos_cmp, NULL);
+ for (unsigned j = 0; j < i->nr_counters; j++)
+ v[j] = percpu_u64_get(i->v[0] + j);
- for (unsigned i = 0; i < acc->k.nr; i++) {
- struct disk_accounting_pos k;
- bpos_to_disk_accounting_pos(&k, acc->k.data[i].pos);
+ /*
+ * If the entry counters are zeroed, it should be treated as
+ * nonexistent - it might point to an invalid device.
+ *
+ * Remove it, so that if it's re-added it gets re-marked in the
+ * superblock:
+ */
+ ret = bch2_is_zero(v, sizeof(v[0]) * i->nr_counters)
+ ? -BCH_ERR_remove_disk_accounting_entry
+ : bch2_disk_accounting_validate_late(trans, &acc_k, v, i->nr_counters);
+
+ if (ret == -BCH_ERR_remove_disk_accounting_entry) {
+ free_percpu(i->v[0]);
+ free_percpu(i->v[1]);
+ darray_remove_item(&acc->k, i);
+ ret = 0;
+ continue;
+ }
- u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
- bch2_accounting_mem_read_counters(acc, i, v, ARRAY_SIZE(v), false);
+ if (ret)
+ return ret;
+ }
- /*
- * Check for underflow, schedule check_allocations
- * necessary:
- *
- * XXX - see if we can factor this out to run on a bkey
- * so we can check everything lazily, right now we don't
- * check the non in-mem counters at all
- */
- bool underflow = false;
- for (unsigned j = 0; j < acc->k.data[i].nr_counters; j++)
- underflow |= (s64) v[j] < 0;
+ eytzinger0_sort(acc->k.data, acc->k.nr, sizeof(acc->k.data[0]),
+ accounting_pos_cmp, NULL);
- if (underflow) {
- CLASS(printbuf, buf)();
- bch2_log_msg_start(c, &buf);
+ for (unsigned i = 0; i < acc->k.nr; i++) {
+ struct disk_accounting_pos k;
+ bpos_to_disk_accounting_pos(&k, acc->k.data[i].pos);
- prt_printf(&buf, "Accounting underflow for\n");
- bch2_accounting_key_to_text(&buf, &k);
+ u64 v[BCH_ACCOUNTING_MAX_COUNTERS];
+ bch2_accounting_mem_read_counters(acc, i, v, ARRAY_SIZE(v), false);
+ /*
+ * Check for underflow, schedule check_allocations
+ * necessary:
+ *
+ * XXX - see if we can factor this out to run on a bkey
+ * so we can check everything lazily, right now we don't
+ * check the non in-mem counters at all
+ */
+ bool underflow = false;
for (unsigned j = 0; j < acc->k.data[i].nr_counters; j++)
- prt_printf(&buf, " %lli", v[j]);
-
- bool print = bch2_count_fsck_err(c, accounting_key_underflow, &buf);
- unsigned pos = buf.pos;
- ret = bch2_run_explicit_recovery_pass(c, &buf,
- BCH_RECOVERY_PASS_check_allocations, 0);
- print |= buf.pos != pos;
+ underflow |= (s64) v[j] < 0;
- if (print)
- bch2_print_str(c, KERN_ERR, buf.buf);
- if (ret)
- return ret;
- }
+ if (underflow) {
+ if (!underflow_err.pos) {
+ bch2_log_msg_start(c, &underflow_err);
+ prt_printf(&underflow_err, "Accounting underflow for\n");
+ }
+ bch2_accounting_key_to_text(&underflow_err, &k);
- guard(preempt)();
- struct bch_fs_usage_base *usage = this_cpu_ptr(c->usage);
+ for (unsigned j = 0; j < acc->k.data[i].nr_counters; j++)
+ prt_printf(&underflow_err, " %lli", v[j]);
+ prt_newline(&underflow_err);
+ }
- switch (k.type) {
- case BCH_DISK_ACCOUNTING_persistent_reserved:
- usage->reserved += v[0] * k.persistent_reserved.nr_replicas;
- break;
- case BCH_DISK_ACCOUNTING_replicas:
- fs_usage_data_type_to_base(usage, k.replicas.data_type, v[0]);
- break;
- case BCH_DISK_ACCOUNTING_dev_data_type: {
- guard(rcu)();
- struct bch_dev *ca = bch2_dev_rcu_noerror(c, k.dev_data_type.dev);
- if (ca) {
- struct bch_dev_usage_type __percpu *d = &ca->usage->d[k.dev_data_type.data_type];
- percpu_u64_set(&d->buckets, v[0]);
- percpu_u64_set(&d->sectors, v[1]);
- percpu_u64_set(&d->fragmented, v[2]);
-
- if (k.dev_data_type.data_type == BCH_DATA_sb ||
- k.dev_data_type.data_type == BCH_DATA_journal)
- usage->hidden += v[0] * ca->mi.bucket_size;
+ guard(preempt)();
+ struct bch_fs_usage_base *usage = this_cpu_ptr(c->usage);
+
+ switch (k.type) {
+ case BCH_DISK_ACCOUNTING_persistent_reserved:
+ usage->reserved += v[0] * k.persistent_reserved.nr_replicas;
+ break;
+ case BCH_DISK_ACCOUNTING_replicas:
+ fs_usage_data_type_to_base(usage, k.replicas.data_type, v[0]);
+ break;
+ case BCH_DISK_ACCOUNTING_dev_data_type: {
+ guard(rcu)();
+ struct bch_dev *ca = bch2_dev_rcu_noerror(c, k.dev_data_type.dev);
+ if (ca) {
+ struct bch_dev_usage_type __percpu *d = &ca->usage->d[k.dev_data_type.data_type];
+ percpu_u64_set(&d->buckets, v[0]);
+ percpu_u64_set(&d->sectors, v[1]);
+ percpu_u64_set(&d->fragmented, v[2]);
+
+ if (k.dev_data_type.data_type == BCH_DATA_sb ||
+ k.dev_data_type.data_type == BCH_DATA_journal)
+ usage->hidden += v[0] * ca->mi.bucket_size;
+ }
+ break;
+ }
}
- break;
- }
}
}
+ if (underflow_err.pos) {
+ bool print = bch2_count_fsck_err(c, accounting_key_underflow, &underflow_err);
+ unsigned pos = underflow_err.pos;
+ ret = bch2_run_explicit_recovery_pass(c, &underflow_err,
+ BCH_RECOVERY_PASS_check_allocations, 0);
+ print |= underflow_err.pos != pos;
+
+ if (print)
+ bch2_print_str(c, KERN_ERR, underflow_err.buf);
+ if (ret)
+ return ret;
+ }
+
return ret;
}