// SPDX-License-Identifier: GPL-2.0
#include "bcachefs.h"
#include "journal_sb.h"
#include "darray.h"
#include <linux/sort.h>
/* BCH_SB_FIELD_journal: */
static int u64_cmp(const void *_l, const void *_r)
{
const u64 *l = _l;
const u64 *r = _r;
return cmp_int(*l, *r);
}
static int bch2_sb_journal_validate(struct bch_sb *sb, struct bch_sb_field *f,
enum bch_validate_flags flags, struct printbuf *err)
{
struct bch_sb_field_journal *journal = field_to_type(f, journal);
struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
int ret = -BCH_ERR_invalid_sb_journal;
unsigned nr;
unsigned i;
u64 *b;
nr = bch2_nr_journal_buckets(journal);
if (!nr)
return 0 ;
b = kmalloc_array(nr, sizeof (u64), GFP_KERNEL);
if (!b)
return -BCH_ERR_ENOMEM_sb_journal_validate;
for (i = 0 ; i < nr; i++)
b[i] = le64_to_cpu(journal->buckets[i]);
sort(b, nr, sizeof (u64), u64_cmp, NULL);
if (!b[0 ]) {
prt_printf(err, "journal bucket at sector 0" );
goto err;
}
if (b[0 ] < le16_to_cpu(m.first_bucket)) {
prt_printf(err, "journal bucket %llu before first bucket %u" ,
b[0 ], le16_to_cpu(m.first_bucket));
goto err;
}
if (b[nr - 1 ] >= le64_to_cpu(m.nbuckets)) {
prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)" ,
b[nr - 1 ], le64_to_cpu(m.nbuckets));
goto err;
}
for (i = 0 ; i + 1 < nr; i++)
if (b[i] == b[i + 1 ]) {
prt_printf(err, "duplicate journal buckets %llu" , b[i]);
goto err;
}
ret = 0 ;
err:
kfree(b);
return ret;
}
static void bch2_sb_journal_to_text(struct printbuf *out, struct bch_sb *sb,
struct bch_sb_field *f)
{
struct bch_sb_field_journal *journal = field_to_type(f, journal);
unsigned i, nr = bch2_nr_journal_buckets(journal);
prt_printf(out, "Buckets: " );
for (i = 0 ; i < nr; i++)
prt_printf(out, " %llu" , le64_to_cpu(journal->buckets[i]));
prt_newline(out);
}
const struct bch_sb_field_ops bch_sb_field_ops_journal = {
.validate = bch2_sb_journal_validate,
.to_text = bch2_sb_journal_to_text,
};
struct u64_range {
u64 start;
u64 end;
};
static int u64_range_cmp(const void *_l, const void *_r)
{
const struct u64_range *l = _l;
const struct u64_range *r = _r;
return cmp_int(l->start, r->start);
}
static int bch2_sb_journal_v2_validate(struct bch_sb *sb, struct bch_sb_field *f,
enum bch_validate_flags flags, struct printbuf *err)
{
struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
int ret = -BCH_ERR_invalid_sb_journal;
u64 sum = 0 ;
unsigned nr;
unsigned i;
struct u64_range *b;
nr = bch2_sb_field_journal_v2_nr_entries(journal);
if (!nr)
return 0 ;
b = kmalloc_array(nr, sizeof (*b), GFP_KERNEL);
if (!b)
return -BCH_ERR_ENOMEM_sb_journal_v2_validate;
for (i = 0 ; i < nr; i++) {
b[i].start = le64_to_cpu(journal->d[i].start);
b[i].end = b[i].start + le64_to_cpu(journal->d[i].nr);
if (b[i].end <= b[i].start) {
prt_printf(err, "journal buckets entry with bad nr: %llu+%llu" ,
le64_to_cpu(journal->d[i].start),
le64_to_cpu(journal->d[i].nr));
goto err;
}
sum += le64_to_cpu(journal->d[i].nr);
}
sort(b, nr, sizeof (*b), u64_range_cmp, NULL);
if (!b[0 ].start) {
prt_printf(err, "journal bucket at sector 0" );
goto err;
}
if (b[0 ].start < le16_to_cpu(m.first_bucket)) {
prt_printf(err, "journal bucket %llu before first bucket %u" ,
b[0 ].start, le16_to_cpu(m.first_bucket));
goto err;
}
if (b[nr - 1 ].end > le64_to_cpu(m.nbuckets)) {
prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)" ,
b[nr - 1 ].end - 1 , le64_to_cpu(m.nbuckets));
goto err;
}
for (i = 0 ; i + 1 < nr; i++) {
if (b[i].end > b[i + 1 ].start) {
prt_printf(err, "duplicate journal buckets in ranges %llu-%llu, %llu-%llu" ,
b[i].start, b[i].end, b[i + 1 ].start, b[i + 1 ].end);
goto err;
}
}
if (sum > UINT_MAX) {
prt_printf(err, "too many journal buckets: %llu > %u" , sum, UINT_MAX);
goto err;
}
ret = 0 ;
err:
kfree(b);
return ret;
}
static void bch2_sb_journal_v2_to_text(struct printbuf *out, struct bch_sb *sb,
struct bch_sb_field *f)
{
struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
unsigned i, nr = bch2_sb_field_journal_v2_nr_entries(journal);
prt_printf(out, "Buckets: " );
for (i = 0 ; i < nr; i++)
prt_printf(out, " %llu-%llu" ,
le64_to_cpu(journal->d[i].start),
le64_to_cpu(journal->d[i].start) + le64_to_cpu(journal->d[i].nr));
prt_newline(out);
}
const struct bch_sb_field_ops bch_sb_field_ops_journal_v2 = {
.validate = bch2_sb_journal_v2_validate,
.to_text = bch2_sb_journal_v2_to_text,
};
int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca,
u64 *buckets, unsigned nr)
{
struct bch_sb_field_journal_v2 *j;
unsigned i, dst = 0 , nr_compacted = 1 ;
if (c)
lockdep_assert_held(&c->sb_lock);
if (!nr) {
bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal_v2);
return 0 ;
}
for (i = 0 ; i + 1 < nr; i++)
if (buckets[i] + 1 != buckets[i + 1 ])
nr_compacted++;
j = bch2_sb_field_resize(&ca->disk_sb, journal_v2,
(sizeof (*j) + sizeof (j->d[0 ]) * nr_compacted) / sizeof (u64));
if (!j)
return bch_err_throw(c, ENOSPC_sb_journal);
bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
j->d[dst].start = cpu_to_le64(buckets[0 ]);
j->d[dst].nr = cpu_to_le64(1 );
for (i = 1 ; i < nr; i++) {
if (buckets[i] == buckets[i - 1 ] + 1 ) {
le64_add_cpu(&j->d[dst].nr, 1 );
} else {
dst++;
j->d[dst].start = cpu_to_le64(buckets[i]);
j->d[dst].nr = cpu_to_le64(1 );
}
}
BUG_ON(dst + 1 != nr_compacted);
return 0 ;
}
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland